From 64e26ad527ade27f86f213a21e25acb4f0816e1e Mon Sep 17 00:00:00 2001
From: pdechamps <paul.dechamps@uliege.be>
Date: Mon, 24 Feb 2025 02:56:42 +0100
Subject: [PATCH] (feat) Few modifs of su2 interface

---
 blast/interfaces/su2/SU2Interface.py | 151 +++++++++++++++++++--------
 1 file changed, 110 insertions(+), 41 deletions(-)

diff --git a/blast/interfaces/su2/SU2Interface.py b/blast/interfaces/su2/SU2Interface.py
index 2c89b10..c4c9832 100644
--- a/blast/interfaces/su2/SU2Interface.py
+++ b/blast/interfaces/su2/SU2Interface.py
@@ -18,6 +18,8 @@
 # SU2 interface
 # Paul Dechamps
 import numpy as np
+import os
+import sys
 
 from blast.interfaces.blSolversInterface import SolversInterface
 import pysu2
@@ -25,6 +27,7 @@ import pysu2
 class SU2Interface(SolversInterface):
     def __init__(self, su2config, vconfig, task='analysis'):
         self.filename = su2config.get('filename')
+        self.verbose = su2config.get('Verb', 0)
         self.have_mpi, self.comm, self.status, self.myid = self.__setup_mpi()
         self.solver = pysu2.CSinglezoneDriver(self.filename, 1, self.comm)
 
@@ -50,6 +53,10 @@ class SU2Interface(SolversInterface):
         return have_mpi, comm, status, myid
     
     def run(self):
+        print('SU2Interface::Running SU2 solver.')
+        #Remove output in the terminal
+        if self.getVerbose() == 0:
+            sys.stdout = open(os.devnull, 'w')
         self.comm.barrier()
         # run solver
         self.solver.Preprocess(0)
@@ -59,6 +66,10 @@ class SU2Interface(SolversInterface):
         self.solver.Monitor(0) 
         self.solver.Output(0)
         self.comm.barrier()
+        #restore stdout
+        if self.getVerbose() == 0:
+            sys.stdout = sys.__stdout__
+        print(f'SU2Interface::SU2 solver finished in {self.solver.GetOutputValue("INNER_ITER"):.0f} iterations. RMS_DENSITY: {self.solver.GetOutputValue("RMS_DENSITY"):.2f}')
         return 1
     
     def writeCp(self, sfx=''):
@@ -117,7 +128,7 @@ class SU2Interface(SolversInterface):
         return self.solver.Get_Mx()
     
     def getVerbose(self):
-        return 2
+        return self.verbose
     
     def reset(self):
         print('SU2Interface::Warning: Cannot reset solver.')
@@ -161,6 +172,18 @@ class SU2Interface(SolversInterface):
                     for idim in range(self.getnDim()):
                         vel_norm += reg.V[inode,idim]**2
                     reg.M[inode] = np.sqrt(vel_norm) / self.solver.Primitives().Get(nodeIndex, soundSpeedIdx)
+        self.iBnd[0][0].V[0,:] = self.iBnd[0][0].V[-2,:]
+        self.iBnd[0][0].V[-1,:] = self.iBnd[0][0].V[-2,:]
+        self.iBnd[0][0].Rho[0] = self.iBnd[0][0].Rho[-2]
+        self.iBnd[0][0].Rho[-1] = self.iBnd[0][0].Rho[-2]
+        self.iBnd[0][0].M[0] = self.iBnd[0][0].M[-2]
+        self.iBnd[0][0].M[-1] = self.iBnd[0][0].M[-2]
+        print('vel first', self.iBnd[0][0].V[0,:])
+        print('Rho first', self.iBnd[0][0].Rho[0])
+        print('M first', self.iBnd[0][0].M[0])
+        print('vel last', self.iBnd[0][0].V[-1,:])
+        print('Rho last', self.iBnd[0][0].Rho[-1])
+        print('M last', self.iBnd[0][0].M[-1])
         import matplotlib
         matplotlib.use('Agg')  # Use the Agg backend for non-interactive plotting
         from matplotlib import pyplot as plt
@@ -200,22 +223,44 @@ class SU2Interface(SolversInterface):
 
         # interpolate blowing velocity from elements to nodes
         from scipy.interpolate import interp1d
-        for body in self.iBnd:
-            for reg in body:
-                blowingNodes = interp1d(reg.elemsCoord[:,0], reg.blowingVel, kind='linear', fill_value='extrapolate')(reg.nodesCoord[:,0])
+        blowingNodes = interp1d(self.iBnd[0][0].elemsCoord[:,0], self.iBnd[0][0].blowingVel, kind='linear', fill_value='extrapolate')(self.iBnd[0][0].nodesCoord[:,0])
                 
-                #Plot blowing velocity
-                import matplotlib
-                matplotlib.use('Agg')  # Use the Agg backend for non-interactive plotting
-                from matplotlib import pyplot as plt
-                plt.figure()
-                plt.plot(reg.elemsCoord[:, 0], reg.blowingVel, 'o', label='Original')
-                plt.plot(reg.nodesCoord[:, 0], blowingNodes, 'x--', label='Interpolated')
-                plt.legend()
-                plt.xlabel('$x$')
-                plt.ylabel('Blowing velocity')
-                plt.savefig('Blowing_velocity.png')  # Save the plot as an image file
-                print('Plot saved as Blowing_velocity.png')
+        
+        #Plot blowing velocity
+        import matplotlib
+        matplotlib.use('Agg')  # Use the Agg backend for non-interactive plotting
+        from matplotlib import pyplot as plt
+        plt.figure()
+        plt.plot(self.iBnd[0][0].elemsCoord[:, 0], self.iBnd[0][0].blowingVel, 'o', label='Original')
+        plt.plot(self.iBnd[0][0].nodesCoord[:, 0], blowingNodes, 'x--', label='Interpolated')
+        plt.legend()
+        plt.xlabel('$x$')
+        plt.ylabel('Blowing velocity')
+        plt.savefig('Blowing_velocity.png')  # Save the plot as an image file
+        print('Plot saved as Blowing_velocity.png')
+
+        if self.getnDim() == 2:
+            velComponents = ['VELOCITY_X', 'VELOCITY_Y']
+        elif self.getnDim() == 3:
+            velComponents = ['VELOCITY_X', 'VELOCITY_Y', 'VELOCITY_Z']
+        else:
+            raise RuntimeError('su2Interface::Incorrect number of dimensions.')
+        
+        velIdx = np.zeros(self.getnDim(), dtype=int)
+        for i, velComp in enumerate(velComponents):
+            velIdx[i] = self.solver.GetPrimitiveIndices()[velComp]
+        # Get all the tags with the CHT option
+        for arfTag in self.wingTags:
+            for ivertex in range(self.solver.GetNumberMarkerNodes(self.wingMarkersToID[arfTag])):
+                nodeIndex = self.solver.GetMarkerNode(self.wingMarkersToID[arfTag], ivertex)
+                blasterIndex = np.where(self.iBnd[0][0].nodesCoord[:, -1] == nodeIndex)[0][0]
+                normal = self.solver.GetMarkerVertexNormals(self.wingMarkersToID[arfTag], ivertex)
+                blw = np.zeros(3)
+                for idim in range(self.getnDim()):
+                    blw += normal[idim] * blowingNodes[blasterIndex]
+                for idim in range(self.getnDim()):
+                    print('Setting blowing velocity on node', nodeIndex, 'blasterIndex', blasterIndex, blw[idim])
+                    self.solver.Primitives().Set(int(nodeIndex), int(velIdx[idim]), blw[idim])
 
     def getWing(self):
         if self.getnDim() == 2:
@@ -229,37 +274,52 @@ class SU2Interface(SolversInterface):
         """ Retreives the mesh used for SU2 Euler calculation.
         Airfoil is already in seilig format (Upper TE -> Lower TE).
         """
-        elemsCoord = np.zeros((0,4))
-        nodesCoord = np.zeros((0,4))
+        # elemsCoord = np.zeros((0,4))
+        # nodesCoord = np.zeros((0,4))
+        # for arfTag in self.wingTags:
+        #     nodesOnSide = np.zeros((self.solver.GetNumberMarkerNodes(self.wingMarkersToID[arfTag]), 4))
+        #     elemsCoordOnSide = np.zeros((self.solver.GetNumberMarkerElements(self.wingMarkersToID[arfTag]), 4))
+        #     inod = 0
+        #     for ielm in range(self.solver.GetNumberMarkerElements(self.wingMarkersToID[arfTag])):
+        #         nodesonelm = self.solver.GetMarkerElementNodes(self.wingMarkersToID[arfTag], ielm)
+        #         for n in nodesonelm:
+        #             if n not in nodesOnSide[:,-1]:
+        #                 nodesOnSide[inod, :self.getnDim()] = self.solver.Coordinates().Get(n)
+        #                 nodesOnSide[inod, -1] = n
+        #                 inod += 1
+        #             elemsCoordOnSide[ielm, :self.getnDim()] += self.solver.Coordinates().Get(n)
+        #         elemsCoordOnSide[ielm, :self.getnDim()] /= len(nodesonelm)
+        #         elemsCoordOnSide[ielm, -1] = self.solver.GetMarkerElementGlobalIndex(self.wingMarkersToID[arfTag], ielm)
+
+            # # Sort in ascending order of first column
+            # nodesOnSide = nodesOnSide[nodesOnSide[:,0].argsort()]
+            # elemsCoordOnSide = elemsCoordOnSide[elemsCoordOnSide[:,0].argsort()]
+            # if arfTag == 'airfoil':
+            #     nodesOnSide = np.flip(nodesOnSide, axis=0)
+            #     elemsCoordOnSide = np.flip(elemsCoordOnSide, axis=0)
+            # #elemsCoordOnSide = np.flip(elemsCoordOnSide, axis=0)
+            # nodesCoord = np.row_stack((nodesCoord, nodesOnSide))
+            # elemsCoord = np.row_stack((elemsCoord, elemsCoordOnSide))
+            
+        nodesCoord = np.zeros((0, 4))
         for arfTag in self.wingTags:
             nodesOnSide = np.zeros((self.solver.GetNumberMarkerNodes(self.wingMarkersToID[arfTag]), 4))
-            elemsCoordOnSide = np.zeros((self.solver.GetNumberMarkerElements(self.wingMarkersToID[arfTag]), 4))
-            inod = 0
-            for ielm in range(self.solver.GetNumberMarkerElements(self.wingMarkersToID[arfTag])):
-                nodesonelm = self.solver.GetMarkerElementNodes(self.wingMarkersToID[arfTag], ielm)
-                for n in nodesonelm:
-                    if n not in nodesOnSide[:,-1]:
-                        nodesOnSide[inod, :self.getnDim()] = self.solver.Coordinates().Get(n)
-                        nodesOnSide[inod, -1] = n
-                        inod += 1
-                    elemsCoordOnSide[ielm, :self.getnDim()] += self.solver.Coordinates().Get(n)
-                elemsCoordOnSide[ielm, :self.getnDim()] /= len(nodesonelm)
-                elemsCoordOnSide[ielm, -1] = self.solver.GetMarkerElementGlobalIndex(self.wingMarkersToID[arfTag], ielm)
-            
+            for i in range(self.solver.GetNumberMarkerNodes(self.wingMarkersToID[arfTag])):
+                nodeIndex = self.solver.GetMarkerNode(self.wingMarkersToID[arfTag], i)
+                nodesOnSide[i, :self.getnDim()] = self.solver.Coordinates().Get(nodeIndex)
+                nodesOnSide[i, -1] = nodeIndex
             # Sort in ascending order of first column
             nodesOnSide = nodesOnSide[nodesOnSide[:,0].argsort()]
-            elemsCoordOnSide = elemsCoordOnSide[elemsCoordOnSide[:,0].argsort()]
             if arfTag == 'airfoil':
                 nodesOnSide = np.flip(nodesOnSide, axis=0)
-                elemsCoordOnSide = np.flip(elemsCoordOnSide, axis=0)
-            #elemsCoordOnSide = np.flip(elemsCoordOnSide, axis=0)
+            elif arfTag == 'airfoil_':
+                nodesOnSide = np.delete(nodesOnSide, 0, axis=0)
             nodesCoord = np.row_stack((nodesCoord, nodesOnSide))
-            elemsCoord = np.row_stack((elemsCoord, elemsCoordOnSide))
-
-        # Avoid duplicated leading edge point
-        if len(np.where(nodesCoord[:,0] == np.min(nodesCoord[:,0]))[0]) > 1:
-            for delindex in np.where(nodesCoord[:,0] == np.min(nodesCoord[:,0]))[0][1::-1]:
-                nodesCoord = np.delete(nodesCoord, delindex, axis=0)
+        
+        elemsCoord = np.zeros((nodesCoord.shape[0]-1, 4))
+        for i in range(nodesCoord.shape[0]-1):
+            elemsCoord[i, :self.getnDim()] = (nodesCoord[i, :self.getnDim()] + nodesCoord[i+1, :self.getnDim()]) / 2
+            elemsCoord[i, -1] = i
         
         import matplotlib
         matplotlib.use('Agg')  # Use the Agg backend for non-interactive plotting
@@ -267,7 +327,8 @@ class SU2Interface(SolversInterface):
         plt.figure()
         plt.plot(nodesCoord[:, 0], nodesCoord[:, 1], 'x--')
         plt.plot(elemsCoord[:, 0], elemsCoord[:, 1], 'o')
-        plt.xlim([0.95, 1.0])
+        #plt.xlim([-0.001, 0.002])
+        #plt.ylim([-0.02, 0.02])
         plt.xlabel('X Coordinate')
         plt.ylabel('Y Coordinate')
         plt.title('Airfoil Nodes')
@@ -287,6 +348,14 @@ class SU2Interface(SolversInterface):
         else:
             print(len(np.where(nodesCoord[:,0] == np.min(nodesCoord[:,0]))[0]))
             print('Airfoil has one point at the leading edge.')
+        
+        # Check for duplicated elements
+        if len(np.unique(elemsCoord[:,-1])) != elemsCoord.shape[0]:
+            raise RuntimeError('su2Interface::Duplicated elements in airfoil mesh.')
+        else:
+            print('No duplicated elements in airfoil mesh.')
+
+        print('nNodes:', nodesCoord.shape[0], 'nElems:', elemsCoord.shape[0])
 
         self.iBnd[ibody][0].initStructures(nodesCoord.shape[0], elemsCoord.shape[0])
         self.iBnd[ibody][0].nodesCoord = nodesCoord
-- 
GitLab