From 2e2e93bd6e56353dc4bcf7b925f698381de1be80 Mon Sep 17 00:00:00 2001
From: Paul Dechamps <paul.dechamps@uliege.be>
Date: Mon, 4 Dec 2023 21:51:28 +0100
Subject: [PATCH] (feat) Added vtk support

Added vtk support in Dart api. Modified writing of boundary layer slices and introduced new argument of blast - writeSlices - which indicates vtk where to extract cps. The feature is not yet available for the boundary layer slices as this would require interpolation in the viscous solver.
---
 blast/interfaces/dart/DartInterface.py | 66 +++++++++++++++-----------
 blast/utils.py                         |  7 ++-
 blast/validation/oneraValidation.py    | 34 ++++++-------
 3 files changed, 62 insertions(+), 45 deletions(-)

diff --git a/blast/interfaces/dart/DartInterface.py b/blast/interfaces/dart/DartInterface.py
index 92c7919..7b3752c 100644
--- a/blast/interfaces/dart/DartInterface.py
+++ b/blast/interfaces/dart/DartInterface.py
@@ -28,15 +28,15 @@ class DartInterface(SolversInterface):
     def __init__(self, dartCfg, vSolver, _cfg):
         try:
             from modules.dartflo.dart.api.core import initDart
-            argOut = initDart(dartCfg, viscous=True)
-            self.solver = argOut.get('sol') # Solver
-            self.wrt = argOut.get('wrt') # Writer
-            self.blw = [argOut.get('blwb'), argOut.get('blww')]
+            self.argOut = initDart(dartCfg, viscous=True)
+            self.solver = self.argOut.get('sol') # Solver
+            self.blw = [self.argOut.get('blwb'), self.argOut.get('blww')]
             print('Dart successfully loaded.')
         except:
             print(ccolors.ANSI_RED, 'Could not load DART. Make sure it is installed.', ccolors.ANSI_RESET)
             raise RuntimeError('Import error')
 
+        _cfg['Format'] = dartCfg['Format']
         if 'iMsh' not in _cfg:
             _cfg['iMsh'] = self.blw
         SolversInterface.__init__(self, vSolver, _cfg)
@@ -48,32 +48,44 @@ class DartInterface(SolversInterface):
         """ Write Cp distribution around the airfoil on file.
         """
         # Extract Cp on elements
-        vElems = self.blw[0].tag.elems
-        vData = self.solver.Cp
-        if isinstance(vData[0], float):
-            size = 1
-        elif vData[0].size() == 3:
-            size = 3
-        else:
-            raise RuntimeError('unrecognized format for vData!')
-        # store data (not efficient, but OK since only meant for small 2D cases)
-        data = np.zeros((vElems.size()+1,3+size))
-        i = 0
-        while i < vElems.size()+1:
-            data[i,0] = vElems[i%vElems.size()].nodes[0].pos[0]
-            data[i,1] = vElems[i%vElems.size()].nodes[0].pos[1]
-            data[i,2] = vElems[i%vElems.size()].nodes[0].pos[2]
-            if size == 1:
-                data[i,3] = vData[vElems[i%vElems.size()].nodes[0].row]
+        self.save(sfx=sfx)
+        # 2D Case
+        if self.cfg['nDim'] == 2:
+            vElems = self.blw[0].tag.elems
+            vData = self.solver.Cp
+            if isinstance(vData[0], float):
+                size = 1
+            elif vData[0].size() == 3:
+                size = 3
             else:
-                for j in range(size):
-                    data[i,3+j] = vData[vElems[i%vElems.size()].nodes[0].row][j]
-            i += 1
-        print('writing data file ' + 'Cp' +sfx + '.dat')
-        np.savetxt('Cp'+sfx+'.dat', data, fmt='%1.6e', delimiter=', ', header='x, y, z, Cp', comments='')
+                raise RuntimeError('unrecognized format for vData!')
+            # store data (not efficient, but OK since only meant for small 2D cases)
+            data = np.zeros((vElems.size()+1,3+size))
+            i = 0
+            while i < vElems.size()+1:
+                data[i,0] = vElems[i%vElems.size()].nodes[0].pos[0]
+                data[i,1] = vElems[i%vElems.size()].nodes[0].pos[1]
+                data[i,2] = vElems[i%vElems.size()].nodes[0].pos[2]
+                if size == 1:
+                    data[i,3] = vData[vElems[i%vElems.size()].nodes[0].row]
+                else:
+                    for j in range(size):
+                        data[i,3+j] = vData[vElems[i%vElems.size()].nodes[0].row][j]
+                i += 1
+            print('writing data file ' + 'Cp' +sfx + '.dat')
+            np.savetxt('Cp'+sfx+'.dat', data, fmt='%1.6e', delimiter=', ', header='x, y, z, Cp', comments='')
+        
+        elif self.cfg['nDim'] == 3:
+                import modules.dartflo.dart.utils as invUtils
+                if self.cfg['Format'] == 'vtk':
+                    import os
+                    if not os.path.exists('cpSlices'+sfx):
+                        os.makedirs('cpSlices'+sfx)
+                    print('Saving Cp files in vtk format. Msh {:s}, Tag {:.0f}'.format(self.argOut['msh'].name, self.cfg['saveTag']))
+                    invUtils.writeSlices(self.argOut['msh'].name, self.cfg['writeSections'], self.cfg['saveTag'], sfx=sfx)
     
     def save(self, sfx='inviscid'):
-        self.solver.save(self.wrt, sfx)
+        self.solver.save(self.argOut['wrt'], sfx)
 
     def getAoA(self):
         return self.solver.pbl.alpha
diff --git a/blast/utils.py b/blast/utils.py
index b07f83c..80ac5bb 100644
--- a/blast/utils.py
+++ b/blast/utils.py
@@ -142,9 +142,12 @@ def getSolution(vSolver, iSec=0):
 def write(wData, Re, toW=['deltaStar', 'H', 'Hstar', 'Cf', 'Ct', 'ue', 'ueInv', 'delta'], sfx=''):
     """Write the results in bl files
     """
+    import os
+    if not os.path.exists('blSlices'):
+        os.makedirs('blSlices')
     # Write
-    print('Writing file: bl_'+sfx+'.dat...', end = '')
-    f = open('bl'+sfx+'.dat', 'w+')
+    print('Writing file: /blSlices/bl'+sfx+'.dat...', end = '')
+    f = open('blSlices/bl'+sfx+'.dat', 'w+')
 
     f.write('$Sectional aerodynamic coefficients\n')
     f.write('             Re             Cdw             Cdp             Cdf         xtr_top         xtr_bot\n')
diff --git a/blast/validation/oneraValidation.py b/blast/validation/oneraValidation.py
index 1fb9785..3c0d239 100644
--- a/blast/validation/oneraValidation.py
+++ b/blast/validation/oneraValidation.py
@@ -57,7 +57,7 @@ def cfgInviscid(nthrds, verb):
     'File' : os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + '/models/dart/onera_lfs.msh', # Input file containing the model
     'Pars' : {}, # parameters for input file model
     'Dim' : 3, # problem dimension
-    'Format' : 'gmsh', # save format (vtk or gmsh)
+    'Format' : 'vtk', # save format (vtk or gmsh)
     # Markers
     'Fluid' : 'field', # name of physical group containing the fluid
     'Farfield' : ['upstream', 'farfield', 'downstream'], # LIST of names of physical groups containing the farfield boundaries (upstream/downstream should be first/last element)
@@ -68,31 +68,32 @@ def cfgInviscid(nthrds, verb):
     'dbc' : True,
     'Upstream' : 'upstream',
     # Freestream
-    'M_inf' : 0.839, # freestream Mach number
-    'AoA' : 3.06, # freestream angle of attack
+    'M_inf' : 0.839,     # freestream Mach number
+    'AoA' : 3.06,        # freestream angle of attack
     # Geometry
-    'S_ref' : 0.7528, # reference surface length
-    'c_ref' : 0.64, # reference chord length
-    'x_ref' : 0.0, # reference point for moment computation (x)
-    'y_ref' : 0.0, # reference point for moment computation (y)
-    'z_ref' : 0.0, # reference point for moment computation (z)
+    'S_ref' : 0.7528,    # reference surface length
+    'c_ref' : 0.64,      # reference chord length
+    'x_ref' : 0.0,       # reference point for moment computation (x)
+    'y_ref' : 0.0,       # reference point for moment computation (y)
+    'z_ref' : 0.0,       # reference point for moment computation (z)
     # Numerical
     'LSolver' : 'GMRES', # inner solver (Pardiso, MUMPS or GMRES)
-    'G_fill' : 2, # fill-in factor for GMRES preconditioner
-    'G_tol' : 1e-5, # tolerance for GMRES
-    'G_restart' : 50, # restart for GMRES
-    'Rel_tol' : 1e-6, # relative tolerance on solver residual
-    'Abs_tol' : 1e-8, # absolute tolerance on solver residual
-    'Max_it' : 50 # solver maximum number of iterations
+    'G_fill' : 2,        # fill-in factor for GMRES preconditioner
+    'G_tol' : 1e-5,      # tolerance for GMRES
+    'G_restart' : 50,    # restart for GMRES
+    'Rel_tol' : 1e-6,    # relative tolerance on solver residual
+    'Abs_tol' : 1e-8,    # absolute tolerance on solver residual
+    'Max_it' : 50        # solver maximum number of iterations
     }
 
 def cfgBlast(verb):
     return {
-        'Re' : 1e7,       # Freestream Reynolds number
-        'Minf' : 0.2,     # Freestream Mach number (used for the computation of the time step only)
+        'Re' : 11.72*10e6,  # Freestream Reynolds number
+        'Minf' : 0.839,     # Freestream Mach number (used for the computation of the time step only)
         'CFL0' : 1,         # Inital CFL number of the calculation
 
         'sections' : np.linspace(0.026, 1.15, 20),
+        'writeSections': [0.20, 0.44, 0.80],
         'Sym':[0.],
         'span':1.196,
         'interpolator': 'Rbf',
@@ -100,6 +101,7 @@ def cfgBlast(verb):
         'smoothing': 1e-8,
         'degree': 0,
         'neighbors': 10,
+        'saveTag': 5,
 
         'Verb': verb,       # Verbosity level of the solver
         'couplIter': 50,    # Maximum number of iterations
-- 
GitLab