From 44c0abd084b0f52978125e3d76a43d3532f3b656 Mon Sep 17 00:00:00 2001
From: Bilocq Amaury <amaury.bilocq@student.uliege.be>
Date: Wed, 11 Nov 2020 13:38:57 +0100
Subject: [PATCH] Squashed - Quasi-simultaneous coupler for VII with test cases

---
 .gitlab-ci.yml          |  2 +-
 flow/tests/bli.py       | 30 ++++++++++++------------
 flow/viscous/airfoil.py | 32 +++++++++++++++-----------
 flow/viscous/coupler.py |  2 +-
 flow/viscous/solver.py  | 51 ++++++++++++++++-------------------------
 flow/viscous/wake.py    |  4 ++--
 6 files changed, 57 insertions(+), 64 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 451ca6ee..f4cf7d46 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -10,7 +10,7 @@ default:
 
 .global_tag: &global_tag_def
     tags:
-        - mn2l
+#        - mn2l
 #        - warson   # you can choose a set of runners here
 
 stages:
diff --git a/flow/tests/bli.py b/flow/tests/bli.py
index 3a89ec2e..c6e8cbc2 100644
--- a/flow/tests/bli.py
+++ b/flow/tests/bli.py
@@ -21,13 +21,13 @@
 # Amaury Bilocq
 # Test the viscous-inviscid interaction scheme
 # Reference to the master's thesis: http://hdl.handle.net/2268/252195
-# Reference test cases with Naca0012: 
+# Reference test cases with Naca0012 (different from master's thesis): 
 # 1) Incompressible: Re = 1e7, M_inf = 0, alpha = 5°, p = 2, m = 7, tol = 10^-4, msTE = 0.01, msLE = 0.001
-#    -> nIt = 41, Cl = 0.58, Cd = 0.0062, xtrTop = 0.0555, xtrBot = 0.7397
+#    -> nIt = 32, Cl = 0.55, Cd = 0.0061, xtrTop = 0.0611, xtrBot = 0.7392
 # 2) Compressible: Re = 1e7, M_inf = 5, alpha = 5°, p = 2, m = 7, tol = 10^-4, msTE = 0.01, msLE = 0.001
-#    -> nIt = , Cl = 0.69, Cd = 0.0067, xtrTop = 0.0384, xtrBot = 0.7364
-# 3) Separated: Re = 1e7, M_inf = 0, alpha = 12°, p = 2, m = 11, tol = 5.2*10^-3, msTE = 0.01, msLE = 0.00075
-#    -> nIt = , Cl = 1.39 , Cd = 0.011, xtrTop = 0.008, xtrBot = 1
+#    -> nIt = 45, Cl = 0.64, Cd = 0.0065, xtrTop = 0.0497, xtrBot = 0.7338
+# 3) Separated: Re = 1e7, M_inf = 0, alpha = 12°, p = 2, m = 5, tol = 2.5*10^-3, msTE = 0.01, msLE = 0.00075
+#    -> nIt = 39, Cl = 1.30 , Cd = 0.011, xtrTop = 0.010, xtrBot = 1
 #
 # CAUTION
 # This test is provided to ensure that the solver works properly.
@@ -60,7 +60,7 @@ def main():
 
     # define filter parameters and tolerance of the VII 
     p = 2
-    m = 7
+    m = 5
     tol = 1e-4
 
     # mesh the geometry
@@ -75,7 +75,7 @@ def main():
     pbl, _, _, bnd, blw = floD.problem(msh, dim, alpha, 0., M_inf, M_crit, c_ref, c_ref, 0.25, 0., 0., 'airfoil', te = 'te', vsc = True)
     tms['pre'].stop()
 
-    # solve inviscid problem
+    # solve viscous problem
     print(ccolors.ANSI_BLUE + 'PySolving...' + ccolors.ANSI_RESET)
     tms['solver'].start()
     isolver = floD.newton(pbl)
@@ -106,22 +106,22 @@ def main():
     print(ccolors.ANSI_BLUE + 'PyTesting...' + ccolors.ANSI_RESET)
     tests = CTests()
     if Re == 1e7 and M_inf == 0 and alpha == 5*math.pi/180:
-        tests.add(CTest('Cl', isolver.Cl, 0.58, 5e-2))
+        tests.add(CTest('Cl', isolver.Cl, 0.55, 5e-2)) # Xfoil 0.58
         tests.add(CTest('Cd', vsolver.Cd, 0.0062, 0.01))
         tests.add(CTest('Cdp', vsolver.Cdp, 0.0018, 0.01))
-        tests.add(CTest('xtr_top', vsolver.xtr[0], 0.056, 0.05))
+        tests.add(CTest('xtr_top', vsolver.xtr[0], 0.061, 0.05)) # Xfoil 0.056
         tests.add(CTest('xtr_bot', vsolver.xtr[1], 0.740, 0.05))
     elif Re == 1e7 and M_inf == 0.5 and alpha == 5*math.pi/180:
-        tests.add(CTest('Cl', isolver.Cl, 0.69, 5e-2))
+        tests.add(CTest('Cl', isolver.Cl, 0.65, 5e-2)) # Xfoil 0.69
         tests.add(CTest('Cd', vsolver.Cd, 0.0067, 0.01))
         tests.add(CTest('Cdp', vsolver.Cdp, 0.0025, 0.01))
-        tests.add(CTest('xtr_top', vsolver.xtr[0], 0.038, 0.05))
+        tests.add(CTest('xtr_top', vsolver.xtr[0], 0.049, 0.05)) # Xfoil 0.038
         tests.add(CTest('xtr_bot', vsolver.xtr[1], 0.736, 0.05))
     elif Re == 1e7 and M_inf == 0 and alpha == 12*math.pi/180:
-        tests.add(CTest('Cl', isolver.Cl, 1.39, 5e-2))
+        tests.add(CTest('Cl', isolver.Cl, 1.30, 5e-2)) # Xfoil 1.39
         tests.add(CTest('Cd', vsolver.Cd, 0.0011, 0.01))
-        tests.add(CTest('Cdp', vsolver.Cdp, 0.0025, 0.01))
-        tests.add(CTest('xtr_top', vsolver.xtr[0], 0.008, 0.05))
+        tests.add(CTest('Cdp', vsolver.Cdp, 0.0064, 0.01))
+        tests.add(CTest('xtr_top', vsolver.xtr[0], 0.010, 0.05)) # Xfoil 0.008
         tests.add(CTest('xtr_bot', vsolver.xtr[1], 1.000, 0.05))
     else:
         raise Exception('Test not defined for this flow')
@@ -132,4 +132,4 @@ def main():
     print('')
 
 if __name__ == "__main__":
-    main()
+    main()
\ No newline at end of file
diff --git a/flow/viscous/airfoil.py b/flow/viscous/airfoil.py
index 0dd0ccd1..15fc43c7 100644
--- a/flow/viscous/airfoil.py
+++ b/flow/viscous/airfoil.py
@@ -72,30 +72,34 @@ class Airfoil(Boundary):
             eData[i,0] = self.boundary.tag.elems[i].no
             eData[i,1] = self.boundary.tag.elems[i].nodes[0].no
             eData[i,2] = self.boundary.tag.elems[i].nodes[1].no
-        # Defining TE/LE nodes number 
+        # Find the stagnation point
         idxStag = np.argmin(np.sqrt(data[:,4]**2+data[:,5]**2))
         globStag = int(data[idxStag,0]) # position of the stagnation point in boundary.nodes
-        idxTE = np.where(data[:,1] == 1.0)[0]
-        if idxTE[0] < idxTE[1]:
-            upperIdxTE = idxTE[0]
-            lowerIdxTE = idxTE[1]
+        # Find TE nodes (assuming suction side element is above pressure side element in the y direction)
+        idxTE = np.where(data[:,1] == np.max(data[:,1]))[0] # TE nodes
+        idxTEe0 = np.where(np.logical_or(eData[:,1] == data[idxTE[0],0], eData[:,2] == data[idxTE[0],0]))[0] # TE element 1
+        idxTEe1 = np.where(np.logical_or(eData[:,1] == data[idxTE[1],0], eData[:,2] == data[idxTE[1],0]))[0] # TE element 2
+        ye0 = 0.5 * (data[np.where(data[:,0] == eData[idxTEe0[0],1])[0],2] + data[np.where(data[:,0] == eData[idxTEe0[0],2])[0],2]) # y coordinates of TE element 1 CG
+        ye1 = 0.5 * (data[np.where(data[:,0] == eData[idxTEe1[0],1])[0],2] + data[np.where(data[:,0] == eData[idxTEe1[0],2])[0],2]) # y coordinates of TE element 2 CG
+        if ye0 - ye1 > 0: # element 1 containing TE node 1 is above element 2
+            upperGlobTE = int(data[idxTE[0],0])
+            lowerGlobTE = int(data[idxTE[1],0])
         else:
-            upperIdxTE = idxTE[1]
-            lowerIdxTE = idxTE[0]
-        upperGlobTE = data[upperIdxTE,0] # Number of the upper TE node
-        lowerGlobTE = data[lowerIdxTE,0] # Number of the lower TE node   
+             upperGlobTE = int(data[idxTE[1],0])
+             lowerGlobTE = int(data[idxTE[0],0])
+        # Connectivity
         connectListElems[0] = np.where(eData[:,1] == globStag)[0]
-        N1[0] = eData[connectListElems[0],1] # number of the stag node elems.nodes
+        N1[0] = eData[connectListElems[0],1] # number of the stag node elems.nodes -> globStag
         connectListNodes[0] = np.where(data[:,0] == N1[0])[0]
         i = 1
         upperTE = 0 
         lowerTE = 0
-        # Sort the suction part 
+        # Sort the suction part
         while upperTE == 0:
             N1[i] = eData[connectListElems[i-1],2] # Second node of the element
             connectListElems[i] = np.where(eData[:,1] == N1[i])[0] # # Index of the first node of the next element in elems.nodes 
             connectListNodes[i] = np.where(data[:,0] == N1[i])[0] # Index of the node in boundary.nodes
-            if eData[connectListElems[i],2] == int(upperGlobTE): 
+            if eData[connectListElems[i],2] == upperGlobTE: 
                 upperTE = 1
             i += 1
         # Sort the pressure side
@@ -106,7 +110,7 @@ class Airfoil(Boundary):
             N1[i+1]  = eData[connectListElems[i],1] # First node of the element
             connectListElems[i+1] = np.where(eData[:,2] == N1[i+1])[0] # # Index of the second node of the next element in elems.nodes 
             connectListNodes[i+1] = np.where(data[:,0] == N1[i+1])[0] # Index of the node in boundary.nodes
-            if eData[connectListElems[i+1],1] == int(lowerGlobTE): 
+            if eData[connectListElems[i+1],1] == lowerGlobTE: 
                 lowerTE = 1
             i += 1
         connectListNodes[i+1] = np.where(data[:,0] == lowerGlobTE)[0]
@@ -125,4 +129,4 @@ class Airfoil(Boundary):
         uData = data[0:np.argmax(data[:,0])+1]
         lData = data[np.argmax(data[:,0])+1:None]
         lData = np.insert(lData, 0, uData[0,:], axis = 0) #double the stagnation point
-        return connectListNodes, connectListElems,[uData, lData]
+        return connectListNodes, connectListElems,[uData, lData]
\ No newline at end of file
diff --git a/flow/viscous/coupler.py b/flow/viscous/coupler.py
index 727a9a73..4a672c20 100644
--- a/flow/viscous/coupler.py
+++ b/flow/viscous/coupler.py
@@ -82,4 +82,4 @@ class Coupler:
         # save results
         self.isolver.save(0, self.writer)
         self.vsolver.writeFile()
-        print('\n')
+        print('\n')
\ No newline at end of file
diff --git a/flow/viscous/solver.py b/flow/viscous/solver.py
index 6af0f757..54f97a45 100644
--- a/flow/viscous/solver.py
+++ b/flow/viscous/solver.py
@@ -29,7 +29,6 @@
 # - Implement quasi-2D method for 3D capability
 # - User can define the transition location (l 303)
 # - Write the empirical relation between the amplification factor and the turbulence intensity (l 303)
-# - Check why the computation crash if the first point of the wake is computed with the turbulent closure (l 309)
 
 from flow.viscous.boundary import Boundary
 from flow.viscous.wake import Wake
@@ -69,6 +68,7 @@ class Solver:
         wData['Cf'] = self.blVectors[6]
         wData['Cdissip'] = self.blVectors[7]
         wData['Ctau'] = self.blVectors[8]
+        wData['delta'] = self.blVectors[9]
         wData['xtrTop'] = self.xtr[0]
         wData['xtrBot'] = self.xtr[1]
         return wData
@@ -193,8 +193,8 @@ class Solver:
             Cd = ( -0.0016*HkCd/denCd + 0.207) * (Hstar/(2*Ret))
         if name == 'wake':
             Cd = 2*(1.10*(1.0-1.0/Hk)**2/Hk)* (Hstar/(2*Ret))  
-            Cf = 0          
-        return Cd, Cf, Hstar, Hstar2, Hk, delta, H
+            Cf = 0   
+        return Cd, Cf, Hstar, Hstar2, Hk, delta
 
     def __turbulentClosure(self, theta, H, Ct, Ret, Me, name):
         ''' Turbulent closure derived from Nishida and Drela (1996)
@@ -256,7 +256,7 @@ class Solver:
         Cteq = np.sqrt(n*Hstar*Ctcon*((Hk-1)*Hkc**2)/((1-Us)*(Hk**2)*H)) #Here it is Cteq^0.5
         Cd = n*(Cdw+ Cdd + Cdl)         
         Ueq = 1.25/Hk*(Cf/2-((Hk-1)/(6.432*Hk))**2)   
-        return Cd, Cf, Hstar, Hstar2, Hk, Cteq, delta, Ueq, Ct
+        return Cd, Cf, Hstar, Hstar2, Hk, Cteq, delta
 
 
     def __boundaryLayer(self, data, group): 
@@ -269,6 +269,8 @@ class Solver:
         #Filter the initial conditions (Me and rho can be filtered too)
         if group.name == 'airfoil':
             vtOld = savgol_filter(vtOld, self.m, self.p, mode = 'interp')
+            Me = savgol_filter(Me, self.m, self.p, mode = 'interp')
+            rhoe = savgol_filter(rhoe, self.m, self.p, mode = 'interp')
         vtInv = vtOld
         rhoInv = rhoe                           
         if self.it == 0:
@@ -291,7 +293,6 @@ class Solver:
         Ax = np.zeros(nN) # Amplification factor 
         Ct = np.zeros(nN) # Shear stress coefficient 
         Cteq = np.zeros(nN) # Equilibrium shear stress coefficient 
-        Ueq = np.zeros(nN) # Equilibrium velocity gradient
         delta = np.zeros(nN) # BL thickness
         # Boundary conditions           
         if group.name == 'airfoil':
@@ -309,27 +310,26 @@ class Solver:
         deltaStar[0] = theta[0]*H[0]
         # Define transition location ( N = 9) and compute stagnation point
         ntr = 9  
-        if n[0] < 9:
+        Cd[0], Cf[0],  Hstar[0], Hstar2[0], Hk[0], delta[0] = self.__laminarClosure( theta[0], H[0], Ret[0],Me[0], group.name)
+        if n[0] < ntr:
             turb =0 # we begin with laminar flow 
-            Cd[0], Cf[0],  Hstar[0], Hstar2[0], Hk[0], delta[0], H[0] = self.__laminarClosure( theta[0], H[0], Ret[0],Me[0], group.name)
         else : 
             turb = 1 # typically for the wake 
-            Cd[0], Cf[0],  Hstar[0], Hstar2[0], Hk[0], delta[0], H[0] = self.__laminarClosure( theta[0], H[0], Ret[0],Me[0], group.name)
         xtr = 0 # if only turbulent flow
         itTurb = 0
         # Solve the boundary layer equations
         for i in range(1,nN):
             # x[0] = theta; x[1] = H; x[2] = n for laminar/Ct for turbulent; x[3] = vt from interaction law
             def f_lam(x):
-                f = np.zeros(len(x))                
+                f = np.zeros(len(x))               
                 Ret[i] = np.maximum(x[3]*x[0]*self.Re, 1)
-                Cd[i], Cf[i], Hstar[i], Hstar2[i], Hk[i], delta[i], x[1] = self.__laminarClosure(x[0], x[1], Ret[i],Me[i], group.name)
+                Cd[i], Cf[i], Hstar[i], Hstar2[i], Hk[i], delta[i] = self.__laminarClosure(x[0], x[1], Ret[i],Me[i], group.name)
                 Ta = upw*x[0]+(1-upw)*theta[i-1]
                 Ha = upw*x[1]+(1-upw)*H[i-1]
                 uea = upw*x[3]+(1-upw)*vt[i-1]
                 Reta = upw*Ret[i] + (1-upw)*Ret[i-1]
                 Mea = upw*Me[i]+(1-upw)*Me[i-1]
-                Cda, Cfa, Hstara, Hstar2a, Hka, deltaa, Ha = self.__laminarClosure(Ta , Ha, Reta, Mea, group.name)
+                Cda, Cfa, Hstara, Hstar2a, Hka, deltaa = self.__laminarClosure(Ta , Ha, Reta, Mea, group.name)
                 dx = xx[i] - xx[i-1]
                 Axa = self.__amplRoutine(Hka, Reta, Ta)
                 dTheta = x[0]-theta[i-1]
@@ -357,8 +357,8 @@ class Solver:
                 uea = upw*x[3]+(1-upw)*vt[i-1]
                 Reta = np.maximum(upw*(x[3]*x[0]*self.Re)+(1-upw)*(vt[i-1]*theta[i-1]*self.Re), 1)
                 Mea = upw*Me[i]+(1-upw)*Me[i-1]
-                Cd[i], Cf[i], Hstar[i], Hstar2[i],Hk[i],Cteq[i], delta[i], Ueq[i], x[2] = self.__turbulentClosure(x[0], x[1],x[2], Ret[i],Me[i], group.name)
-                Cda, Cfa, Hstara, Hstar2a, Hka,Cteqa, deltaa, Ueqa, Cta = self.__turbulentClosure(Ta, Ha,Cta, Reta,Mea, group.name)
+                Cd[i], Cf[i], Hstar[i], Hstar2[i],Hk[i],Cteq[i], delta[i] = self.__turbulentClosure(x[0], x[1],x[2], Ret[i],Me[i], group.name)
+                Cda, Cfa, Hstara, Hstar2a, Hka,Cteqa, deltaa = self.__turbulentClosure(Ta, Ha,Cta, Reta,Mea, group.name)
                 dx = xx[i]-xx[i-1]
                 dTheta = x[0]-theta[i-1]
                 due = x[3]-vt[i-1]
@@ -401,7 +401,7 @@ class Solver:
                 Ct[i] = sol[2]  
             theta[i] = sol[0]
             H[i] = sol[1]
-            vt[i] = sol[3]
+            vt[i] = sol[3]  
            # Transition solver
             if n[i] >= ntr and turb ==0:
                 turb = 1
@@ -421,10 +421,10 @@ class Solver:
                 else:
                     Ct[i] =  max(avTurb*sol_turb[2],0.05)
                 vt[i] = avLam*sol[3]+avTurb*sol_turb[3]
-                Cd[i-1], Cf[i-1], Hstar[i-1], Hstar2[i-1], Hk[i-1], delta[i-1], _ = self.__laminarClosure(theta[i-1], H[i-1], Ret[i-1],Me[i-1], group.name)
-                Cd[i], Cf[i], Hstar[i], Hstar2[i],Hk[i],Cteq[i], delta[i], Ueq[i], _ = self.__turbulentClosure(theta[i], H[i],Ct[i], Ret[i],Me[i], group.name)
+                Cd[i-1], Cf[i-1], Hstar[i-1], Hstar2[i-1], Hk[i-1], delta[i-1] = self.__laminarClosure(theta[i-1], H[i-1], Ret[i-1],Me[i-1], group.name)
+                Cd[i], Cf[i], Hstar[i], Hstar2[i],Hk[i],Cteq[i], delta[i] = self.__turbulentClosure(theta[i], H[i],Ct[i], Ret[i],Me[i], group.name)
             deltaStar[i] = H[i]*theta[i]              
-            blwVel[i-1] = -1*(rhoe[i]*vt[i]*deltaStar[i]-rhoe[i-1]*vt[i-1]*deltaStar[i-1])/(rhoe[i]*(xx[i]-xx[i-1]))   # TO DO: need to divide by rhoe or not?
+            blwVel[i-1] = -1*(rhoe[i]*vt[i]*deltaStar[i]-rhoe[i-1]*vt[i-1]*deltaStar[i-1])/(rhoe[i]*(xx[i]-xx[i-1]))   
         # Normalize the friction and dissipation coefficients by the freestream velocity
         Cf = vt**2*Cf 
         Cd = vt**2*Cd
@@ -434,7 +434,7 @@ class Solver:
         Cdp = Cdtot-Cdf 
         # Outputs
         blScalars = [Cdtot, Cdf, Cdp]
-        blVectors = [deltaStar, theta, H, Hk, Hstar, Hstar2, Cf, Cd, Ct]
+        blVectors = [deltaStar, theta, H, Hk, Hstar, Hstar2, Cf, Cd, Ct, delta]
         return blwVel, xtr, xx, blScalars, blVectors
 
 
@@ -451,7 +451,7 @@ class Solver:
             self.blScalars = np.add(ublScalars, lblScalars)
             self.blVectors = np.hstack((ublVectors, lblVectors))
             blwVel = np.concatenate((ublwVel, lblwVel))
-            blwVel = savgol_filter(blwVel, self.m, self.p, mode = 'interp')
+            blwVel = savgol_filter(blwVel, self.m, self.p, mode = 'interp') 
             self.xtr = [xtrTop, xtrBot]
             # Boundary conditions for the wake
             group.TEnd = [ublVectors[1][-1], lblVectors[1][-1]]
@@ -475,15 +475,4 @@ class Solver:
         # Sort the following results in reference frame
         group.deltaStar = group.deltaStar[group.connectListNodes.argsort()] 
         group.xx = group.xx[group.connectListNodes.argsort()]
-        group.u = blwVel[group.connectListElems]  
-
-
-
-        
-
-
-
-
-
-            
-        
+        group.u = blwVel[group.connectListElems.argsort()]
\ No newline at end of file
diff --git a/flow/viscous/wake.py b/flow/viscous/wake.py
index 0e8da24a..0f3cd5e5 100644
--- a/flow/viscous/wake.py
+++ b/flow/viscous/wake.py
@@ -61,7 +61,7 @@ class Wake(Boundary):
             eData[i,1] = self.boundary.tag.elems[i].nodes[0].no
             eData[i,2] = self.boundary.tag.elems[i].nodes[1].no
         connectListNodes = data[:,1].argsort()
-        connectListElems[0] = np.where(eData[:,1] == 1.0)[0]
+        connectListElems[0] = np.where(eData[:,1] == min(data[:,1]))[0]
         for i in range(1, len(eData[:,0])):
             connectListElems[i] = np.where(eData[:,1] == eData[connectListElems[i-1],2])[0]
         data[:,0] = data[connectListNodes,0]
@@ -76,4 +76,4 @@ class Wake(Boundary):
         data[:,9] = data[connectListNodes,9]
         # Separated upper and lower part 
         data = np.delete(data,0,1)
-        return connectListNodes, connectListElems, data
+        return connectListNodes, connectListElems, data
\ No newline at end of file
-- 
GitLab