From f6ca79ce1619a4dba93eb58bca7769eb3d29ece5 Mon Sep 17 00:00:00 2001
From: Dechamps Paul <paul.dechamps@student.uliege.be>
Date: Mon, 22 Nov 2021 02:04:46 +0100
Subject: [PATCH] Fixes residuals and jacobian computation. Adds mesh mapping
 feature.

---
 dart/tests/bliHighLift.py                 |   2 +-
 dart/tests/bliNonLift.py                  |   2 +-
 dart/viscous/Drivers/VDriver.py           | 178 +++++-----
 dart/viscous/Drivers/VGroupConstructor.py | 172 +++++++---
 dart/viscous/Drivers/VPostProcess.py      | 100 +++++-
 dart/viscous/Physics/VBIConditions.py     | 142 +++++---
 dart/viscous/Physics/VVariables.py        |  44 +--
 dart/viscous/README.md                    |   2 +-
 dart/viscous/Solvers/Steady/StdSolver.py  |   7 +-
 dart/viscous/Solvers/VSolver.py           | 100 ++----
 dart/viscous/Solvers/VTimeSolver.py       | 383 ++++++++++++----------
 11 files changed, 645 insertions(+), 487 deletions(-)

diff --git a/dart/tests/bliHighLift.py b/dart/tests/bliHighLift.py
index f7a15ed..c2029fa 100755
--- a/dart/tests/bliHighLift.py
+++ b/dart/tests/bliHighLift.py
@@ -70,7 +70,7 @@ def main():
 
     # Time solver parameters
     Time_Domain=True # True for unsteady solver, False for steady solver
-    Cfl = 1.5
+    Cfl = 1
     spaceMarching=True
 
     if Time_Domain is True:
diff --git a/dart/tests/bliNonLift.py b/dart/tests/bliNonLift.py
index a67cd04..23ac27a 100755
--- a/dart/tests/bliNonLift.py
+++ b/dart/tests/bliNonLift.py
@@ -72,7 +72,7 @@ def main():
 
     # Time solver parameters
     Time_Domain = True # True for unsteady solver, False for steady solver
-    Cfl = 2
+    Cfl = 3
     spaceMarching=True
 
     if Time_Domain is True:
diff --git a/dart/viscous/Drivers/VDriver.py b/dart/viscous/Drivers/VDriver.py
index e0821fe..76c926e 100644
--- a/dart/viscous/Drivers/VDriver.py
+++ b/dart/viscous/Drivers/VDriver.py
@@ -31,17 +31,10 @@ from fwk.coloring import ccolors
 import numpy as np
 import matplotlib.pyplot as plt
 
-# ------------------------------------------------------------------------------
-#  Driver : Input:  - Data with the inviscid solution, geometry and user input for
-#                  time integration
-#
-#           Output: - Corrected data ready to be used for viscous inviscd coupling.
-#
-#  Sorts the data, allows the whole computational domain to be treated at the same
-#  time (equations are solved on the three groups simultaneously) and computes
-#  required data for the inviscid solver (blowing velocity aerodynamic drag,...)
-# ------------------------------------------------------------------------------
 class Driver:
+    """Driver of the time-dependent boundary layer equation solver (viscous solver)
+    """
+
     def __init__(self,_Re, user_xtr, _timeParams, _Minfty, _case, _mapMesh):
         self.Minfty = _Minfty
         self.Re=_Re
@@ -60,8 +53,9 @@ class Driver:
         pass
 
     def dictionary(self):
-        '''Create dictionary with the coordinates and the boundary layer parameters
-        '''
+        """Create dictionary with the coordinates and the boundary layer parameters
+        """
+
         wData = {}
         wData['x'] = self.data[:,0]
         wData['y'] = self.data[:,1]
@@ -92,78 +86,64 @@ class Driver:
         return wData
 
     def writeFile(self):
-        '''Write the results in airfoil_viscous.dat
-        '''
+        """Write the results in airfoil_viscous.dat
+        """
+        
         wData = self.dictionary()
         toW = ['delta*', 'H', 'Cf', 'Ctau'] # list of variable to be written (should be a user input)
         # Write
-        print('writing file: airfoil_viscous.dat...', end = '')
+        print('Writing file: airfoil_viscous.dat...', end = '')
         f = open('airfoil_viscous.dat', 'w+')
+
         f.write('$Aerodynamic coefficients\n')
         f.write('             Re              Cd             Cdp             Cdf         xtr_top         xtr_bot\n')
-        f.write('{0:15.6f} {1:15.6f} {2:15.6f} {3:15.6f} {4:15.6f} {5:15.6f}\n'.format(self.Re, wData['Cd'], wData['Cdp'], wData['Cdf'], wData['xtrTop'], wData['xtrBot']))
+        f.write('{0:15.6f} {1:15.6f} {2:15.6f} {3:15.6f} {4:15.6f} {5:15.6f}\n'.format(self.Re, wData['Cd'], wData['Cdp'],
+                                                                                        wData['Cdf'], wData['xtrTop'],
+                                                                                        wData['xtrBot']))
         f.write('$Boundary layer variables\n')
         f.write('              x               y               z             x/c')
+
         for s in toW:
             f.write(' {0:>15s}'.format(s))
         f.write('\n')
+
         for i in range(len(self.data[:,0])):
             f.write('{0:15.6f} {1:15.6f} {2:15.6f} {3:15.6f}'.format(wData['x'][i], wData['y'][i], wData['z'][i], wData['x/c'][i]))
             for s in toW:
                 f.write(' {0:15.6f}'.format(wData[s][i]))
             f.write('\n')
+
         f.close()
-        print('done!')
-
-
-    def run(self, group):
-        '''Primary component of the Diver. Receives 2 groups : 'airfoil' and 'wake'.
-        Sorts data, initialize code components and calls the time tintegrator. Then sorts the data again,
-        communicating it to the coupler.'''
-
-        # ###################################### Driver.run() ######################################
-        # ------------------------------------------------------------------------------------------
-        #   Input:   -'group':       - List of 2 elements (airfoil/wake) containing parameters
-        #                              from the inviscid solver.
-        #
-        #   Tasks:   - Initializing: - var:          Data container used throughout the code.
-        #                                            Contains the mesh, the solution, boundary 
-        #                                            layer parameters etc.
-        #                            - DiricheletBC: Used to compute the boudary conditions
-        #                                            of the airfoil (Twaithes) and the wake.
-        #                            - gradComp:     Gradient computation using various spacial
-        #                                            schemes.
-        #                            - cSolver:      Evaluates the boundary layer laws at one point
-        #                                            of the computational domain given a prescibed
-        #                                            solution or not. Uses the closures models.
-        #                            - initializer:  Provides the initial condition at the first
-        #                                            iteration
-        #                            - tSolver:      Time integration given a model, CFL and tolerences.
-        #                                            Used the same way for explicit/implicit time
-        #                                            marching.
-        #             
-        #            - Running:     Runs the time integration.
-        #                           Tries with lower CFL if the solver crashes
-        #            - Sorting:     (Post Process): Calls the PostProcess class to sort the value and,
-        #                           from the solution, compute/correct parameters that must
-        #                           sent to the inviscid solver
-        #   Output:  - Void.
-        #              Directly modifes the group that were initialy sent.
-        # ------------------------------------------------------------------------------------------
-        nFactor = 3
+        print('done.')
+
+
+    def run(self, groups):
+        """Runs viscous solver driver.
+        - Data preprocessing and initialization of the different components.
+        - Runs the pseudo-time solver
+        - Data postprocessing to ensure proper communication between the solvers.
+        
+        Args
+        ----
+        
+        - groups (data structure): Structures data containing the necessary information
+        related to each group (Airfoil upper surface, airfoil lower surface and wake).
+        """
+
+        nFactor = 2
 
         # Merge upper, lower sides and wake
-        dataIn=[None,None]
-        for n in range(0, len(group)):
-            group[n].connectListNodes, group[n].connectListElems, dataIn[n]  = group[n].connectList()
-        paramDict, bNodes, data, dx = GroupConstructor().mergeVectors(dataIn)
+        dataIn = [None,None]
+        for n in range(0, len(groups)):
+            groups[n].connectListNodes, groups[n].connectListElems, dataIn[n]  = groups[n].connectList()
+        inMesh, paramDict, data, bNodes, inMeshbNodes, xxInit = GroupConstructor().mergeVectors(dataIn, self.mapMesh, nFactor)
 
-        # Initialize variable container.
-        var = Variables(data, paramDict, bNodes, self.xtrF, self.Ti, self.Re)
+        # Initialize data container.
+        var = Variables(data, paramDict, bNodes, self.xtrF, self.Ti, self.Re)       
 
         if self.it!=0:
             # Extenal flow local markers. 
-            var.extPar[4::var.nExtPar]=data[:,8]
+            var.extPar[4::var.nExtPar] = data[:,8]
 
         # Boundary condition (Airfoil/Wake).
         DirichletBC=Conditions.Boundaries(self.Re)
@@ -185,7 +165,7 @@ class Driver:
         if self.it == 0:
             print('+------------------------------- Solver setup ---------------------------------+')
             if self.mapMesh:
-                print('| - Mesh mapped from {:<4.0f} to {:<4.0f} elements.{:>38s}'.format(len(initMesh), var.nN, '|'))
+                print('| - Mesh mapped from {:>5.0f} to {:>5.0f} elements.{:>35s}'.format(len(inMesh), var.nN, '|'))
             print('| - Number of elements: {:<.0f}. {:>51s}'.format(var.nN,'|'))
             if var.xtrF[0] is None:
                 print('| - Free transition on the upper side. {:>41}'.format('|'))
@@ -213,7 +193,7 @@ class Driver:
 
         # Initial condition: 'Driver' stores the solution form the previous coupling iteration.
 
-        if self.uPrevious is None or self.it < 6:
+        if self.uPrevious is None or self.it < 1:
             # Typically for the first iteration.
 
             # Initalize initial condition builder.
@@ -222,19 +202,15 @@ class Driver:
             # Compute initial condition based on Twaithes solution.
             initializer.initialConditions(var) # Set boundary condition 
 
-            # Default values of the time marching procedure if we start from an
-            # initial solution that is far from the steady state.
-            #tSolver.setCFL0(0.5)
-            #tSolver.setmaxIt(30000)
-
             print('| - Using Twaithes solution as initial guess. {:>34}'.format('|'))
 
         else:
             # If we use a solution previously obtained. 
-            var.u=self.uPrevious.copy()
-
-            # Reset amplification factors
-            var.u[2::var.nVar] = 0
+            
+            var.u=self.uPrevious.copy()                         # Use previous solution.
+            DirichletBC.airfoilBC(var)                          # Reset boundary condition.
+            var.u[2::var.nVar] = 0                              # Reset amplification factors.
+            var.u[3::var.nVar] = var.extPar[2::var.nExtPar]     # Reset inviscid velocity.
 
             print('| - Using previous solution as initial guess. {:>34}'.format('|'))
 
@@ -263,36 +239,36 @@ class Driver:
         self.blParPrevious=var.blPar.copy()
 
         # Compute blowing velocities and sort the boundary layer parameters.
-        self.blScalars, AFblwVel, WKblwVel, AFblVectors, WKblVectors = PostProcess().sortParam(var)
-        self.xtr=var.xtr.copy()
-        self.blVectors=AFblVectors
+        self.blScalars, AFblwVel, WKblwVel, AFblVectors, WKblVectors, AFdeltaStarOut, WKdeltaStarOut = PostProcess().sortParam(var, self.mapMesh, inMesh, inMeshbNodes, xxInit)
+        self.xtr = var.xtr.copy()
+        self.blVectors = AFblVectors
 
         # Group modification to ensure communication between the solvers.
 
-        group[0].TEnd = [AFblVectors[1][var.bNodes[1]-1], AFblVectors[1][var.bNodes[2]-1]]
-        group[0].HEnd = [AFblVectors[2][var.bNodes[1]-1], AFblVectors[2][var.bNodes[2]-1]]
-        group[0].CtEnd = [AFblVectors[8][var.bNodes[1]-1], AFblVectors[8][var.bNodes[2]-1]]
-        # Delete the stagnation point doublon for variables in the VII loop
-        group[0].deltaStar = AFblVectors[0]
-        group[0].xx = np.concatenate((var.xx[:var.bNodes[1]],var.xx[var.bNodes[1]:var.bNodes[2]]))
-
-        # Sort the following results in reference frame
-        group[0].deltaStar = group[0].deltaStar[group[0].connectListNodes.argsort()]
-        group[0].xx = group[0].xx[group[0].connectListNodes.argsort()]
-        group[0].u = AFblwVel[group[0].connectListElems.argsort()]
-        self.data=data[:var.bNodes[2],:]
-
-        group[1].deltaStar = WKblVectors[0]
-        # The drag is measured at the end of the wake
-        self.Cd = self.blScalars[0]
-        self.Cdf = self.blScalars[1]
-        self.Cdp = self.blScalars[2] # Cdf comes from the airfoil as there is no friction in the wake
-
-        group[1].xx=var.xx[var.bNodes[2]:]
-        # Sort the following results in reference frame
-        group[1].deltaStar = group[1].deltaStar[group[1].connectListNodes.argsort()]
-        group[1].xx = group[1].xx[group[1].connectListNodes.argsort()]
-        group[1].u = WKblwVel[group[1].connectListElems.argsort()]
-        
+        groups[0].TEnd      = [AFblVectors[1][var.bNodes[1]-1], AFblVectors[1][var.bNodes[2]-1]]
+        groups[0].HEnd      = [AFblVectors[2][var.bNodes[1]-1], AFblVectors[2][var.bNodes[2]-1]]
+        groups[0].CtEnd     = [AFblVectors[8][var.bNodes[1]-1], AFblVectors[8][var.bNodes[2]-1]]
+        groups[0].deltaStar = AFdeltaStarOut
+        groups[0].xx        = xxInit[:inMeshbNodes[2]]
+
+        # Sort the following results in reference frame.
+        groups[0].deltaStar = groups[0].deltaStar[groups[0].connectListNodes.argsort()]
+        groups[0].xx        = groups[0].xx[groups[0].connectListNodes.argsort()]
+        groups[0].u         = AFblwVel[groups[0].connectListElems.argsort()]
+        self.data           = data[:var.bNodes[2],:]
+
+        # Drag is measured at the end of the wake.
+        self.Cd             = self.blScalars[0]
+        self.Cdf            = self.blScalars[1]
+        self.Cdp            = self.blScalars[2] # Cdf comes from the airfoil as there is no friction in the wake.
+
+        groups[1].deltaStar = WKdeltaStarOut
+        groups[1].xx        = xxInit[inMeshbNodes[2]:]
+
+        # Sort the following results in reference frame.
+        groups[1].deltaStar = groups[1].deltaStar[groups[1].connectListNodes.argsort()]
+        groups[1].xx        = groups[1].xx[groups[1].connectListNodes.argsort()]
+        groups[1].u         = WKblwVel[groups[1].connectListElems.argsort()]
+
         """self.writeFile()
-        Validation().main(1,self.wData, WKblVectors, var.xGlobal[var.bNodes[2]:])"""
\ No newline at end of file
+        Validation().main(1,self.wData, WKblVectors, var.xGlobal[var.bNodes[2]:]) """
\ No newline at end of file
diff --git a/dart/viscous/Drivers/VGroupConstructor.py b/dart/viscous/Drivers/VGroupConstructor.py
index f93410e..bca6618 100755
--- a/dart/viscous/Drivers/VGroupConstructor.py
+++ b/dart/viscous/Drivers/VGroupConstructor.py
@@ -17,6 +17,7 @@
 import numpy as np
 import math
 from matplotlib import pyplot as plt
+from scipy import interpolate
 
 class GroupConstructor():
     """ ---------- Group Constructor ----------
@@ -26,63 +27,132 @@ class GroupConstructor():
     def __init__(self) -> None:
         pass
 
-    def mergeVectors(self,dataIn):
-        '''Merges 3 groups upper/lower sides and wake.'''
+    def mergeVectors(self,dataIn, mapMesh, nFactor):
+        """Merges 3 groups upper/lower sides and wake.
+        """
+
+        # Saves the initial mesh. 
+        inMesh = np.concatenate((dataIn[0][0][:,0], dataIn[0][1][1:,0], dataIn[1][:,0]))
+        inMeshbNodes = [0, len(dataIn[0][0][:,0]), len(dataIn[0][0][:,0]) + len(dataIn[0][1][1:,0])]
+
+        xxInit_up, _, _, _, _ = self.__getBLcoordinates(dataIn[0][0])
+        xxInit_lw, _, _, _, _ = self.__getBLcoordinates(dataIn[0][1])
+        xxInit_lw = np.delete(xxInit_lw, 0) # Delete stagnation point doublon.
+        xxInit_wk, _, _, _, _ = self.__getBLcoordinates(dataIn[1])
+        xxInit = np.concatenate((xxInit_up, xxInit_lw, xxInit_wk))
+
         for k in range(len(dataIn)):
-            if len(dataIn[k])==2: # Airfoil case
-                xx_up, dv_up, vtExt_up, _, alpha_up= self.__getBLcoordinates(dataIn[k][0])
-                xx_lw, dv_lw, vtExt_lw, _, alpha_lw= self.__getBLcoordinates(dataIn[k][1])
+
+            if len(dataIn[k]) == 2: # Airfoil case.
+                
+                if mapMesh:
+                    dataUpper = self.interpolateToFineMesh(dataIn[k][0], nFactor)
+                    dataLower = self.interpolateToFineMesh(dataIn[k][1], nFactor)
+                else:
+                    dataUpper = dataIn[k][0]
+                    dataLower = dataIn[k][1]
+
+                xx_up, dv_up, vtExt_up, _, alpha_up = self.__getBLcoordinates(dataUpper)
+                xx_lw, dv_lw, vtExt_lw, _, alpha_lw = self.__getBLcoordinates(dataLower)
+
             else: # Wake case
-                xx_wk, dv_wk, vtExt_wk, _, alpha_wk= self.__getBLcoordinates(dataIn[k])
-        
+                if mapMesh:
+                    dataWake = self.interpolateToFineMesh(dataIn[k], nFactor)
+                else:
+                    dataWake = dataIn[k]
+
+                xx_wk, dv_wk, vtExt_wk, _, alpha_wk = self.__getBLcoordinates(dataWake)
+
         # Delete stagnation point doublon
-        xx_lw=np.delete(xx_lw,0)
-        dv_lw=np.delete(dv_lw,0)
-        vtExt_lw=np.delete(vtExt_lw,0)
-        alpha_lw=np.delete(alpha_lw,0)
-
-        # Nodes at the boundary of the computational domain: [Stagnation point, First lower side point, First wake point]
-        boundaryNodes=np.zeros(3,dtype=int)
-        boundaryNodes[0]=0
-        boundaryNodes[1]=len(xx_up)
-        boundaryNodes[2]=len(xx_up)+len(xx_lw)
-        
-        param={'xx':None,'dv':None,'vtExt':None,'alpha':None}
-        param['xx']=np.concatenate((xx_up,xx_lw,xx_wk))
-        param['dv']=np.concatenate((dv_up,dv_lw,dv_wk))
-        param['vtExt']=np.concatenate((vtExt_up,vtExt_lw,vtExt_wk))
-        param['alpha']=np.concatenate((alpha_up,alpha_lw,alpha_wk))
-
-        data=np.zeros((len(param['xx']),9))
-        data=np.concatenate((dataIn[0][0],dataIn[0][1],dataIn[1]),axis=0)
-        data=np.delete(data,boundaryNodes[1],0) # Delete stagnation point doublon
-
-        dx=np.zeros(len(param['xx']))
-        for i in range(1,len(param['xx'])):
-            if i==boundaryNodes[1]:
-                dx[i]=param['xx'][i]-param['xx'][0]
-            elif i==boundaryNodes[2]:
-                dx[i]=0
+        xx_lw    = np.delete(xx_lw,0)
+        dv_lw    = np.delete(dv_lw,0)
+        vtExt_lw = np.delete(vtExt_lw,0)
+        alpha_lw = np.delete(alpha_lw,0)
+
+        # Nodes at the boundary of the computational domain:
+        # [Stagnation point, First lower side point, First wake point]
+        boundaryNodes    = np.zeros(3, dtype=int)
+        boundaryNodes[0] = 0
+        boundaryNodes[1] = len(xx_up)
+        boundaryNodes[2] = len(xx_up) + len(xx_lw)
+
+        param={'xx':None, 'dv':None, 'vtExt':None, 'alpha':None}
+        param['xx']    = np.concatenate((xx_up,xx_lw,xx_wk))
+        param['dv']    = np.concatenate((dv_up,dv_lw,dv_wk))
+        param['vtExt'] = np.concatenate((vtExt_up,vtExt_lw,vtExt_wk))
+        param['alpha'] = np.concatenate((alpha_up,alpha_lw,alpha_wk))
+
+        data = np.zeros((len(param['xx']),9))
+        data = np.concatenate((dataUpper, dataLower, dataWake), axis=0)
+        data = np.delete(data,boundaryNodes[1], axis = 0) # Delete stagnation point doublon
+
+        dx = np.zeros(len(param['xx']))
+        for i in range(1, len(param['xx'])):
+
+            if i == boundaryNodes[1]:
+
+                dx[i] = param['xx'][i] - param['xx'][0]
+
+            elif i == boundaryNodes[2]:
+
+                dx[i] = 0
+
             else:
-                dx[i]=param['xx'][i]-param['xx'][i-1]
-        return param, boundaryNodes, data, dx
 
+                dx[i] = param['xx'][i] - param['xx'][i-1]
+
+        return inMesh, param, data, boundaryNodes, inMeshbNodes, xxInit
 
     def __getBLcoordinates(self,data):
         """Transform the reference coordinates into airfoil coordinates
         """
-        nN = len(data[:,0])
-        nE = nN-1 # Nbr of element along the surface
-        vt = np.zeros(nN)
-        xx = np.zeros(nN) # Position along the surface of the airfoil
-        dx = np.zeros(nE) # Distance along two nodes
-        dv = np.zeros(nE) # Speed difference according to element
-        alpha = np.zeros(nE) # Angle of the element for the rotation matrix
-        for i in range(0,nE):
-            alpha[i] = np.arctan2((data[i+1,1]-data[i,1]),(data[i+1,0]-data[i,0]))
-            dx[i] = np.sqrt((data[i+1,0]-data[i,0])**2+(data[i+1,1]-data[i,1])**2)
-            xx[i+1] = dx[i]+xx[i]
-            vt[i] = (data[i,3]*np.cos(alpha[i]) + data[i,4]*np.sin(alpha[i])) # Tangential speed at node 1 element i
-            vt[i+1] = (data[i+1,3]*np.cos(alpha[i]) + data[i+1,4]*np.sin(alpha[i])) # Tangential speed at node 2 element i
-            dv[i] = (vt[i+1] - vt[i])/dx[i] # Velocity gradient according to element i. central scheme with half point
-        return xx, dv, vt, nN, alpha
\ No newline at end of file
+        nN    = len(data[:,0])
+        nE    = nN-1            # Nbr of element along the surface.
+        vt    = np.zeros(nN)    # Outer flow velocity. 
+        xx    = np.zeros(nN)    # Position along the surface of the airfoil.
+        dx    = np.zeros(nE)    # Distance along two nodes.
+        dv    = np.zeros(nE)    # Speed difference according to element.
+        alpha = np.zeros(nE)    # Angle of the element for the rotation matrix.
+
+        for i in range(0, nE):
+
+            alpha[i] = np.arctan2((data[i+1, 1] - data[i, 1]), (data[i+1, 0]-data[i, 0]))
+            dx[i]    = np.sqrt((data[i+1, 0] - data[i,0])**2 + (data[i+1, 1]-data[i, 1])**2)
+            xx[i+1]  = dx[i] + xx[i]
+            vt[i]    = (data[i, 3] * np.cos(alpha[i]) + data[i ,4] * np.sin(alpha[i]))          # Tangential speed at node 1 element i
+            vt[i+1]  = (data[i+1, 3] * np.cos(alpha[i]) + data[i+1, 4] * np.sin(alpha[i]))      # Tangential speed at node 2 element i
+            dv[i]    = (vt[i+1] - vt[i]) / dx[i]                                                # Velocity gradient according to element i.
+                                                                                                #             central scheme with half point
+        
+        return xx, dv, vt, nN, alpha
+
+    def interpolateToFineMesh(self, iData, nFactor):
+
+        inMesh = iData[:,0]
+
+        outMesh = []
+        for iPoint in range(len(inMesh) - 1):
+
+            dx = inMesh[iPoint + 1] - inMesh[iPoint]
+
+            for iRefinedPoint in range(nFactor):
+
+                outMesh = np.append(outMesh, inMesh[iPoint] + iRefinedPoint * dx / nFactor)
+
+        outMesh = np.append(outMesh, inMesh[-1])
+        outData = np.empty((len(outMesh), len(iData[0,:])))
+        outData[:,0] = outMesh
+
+        for iParam in range(1, len(iData[0, :])):
+
+            # Create interpolation function between the initial mesh
+            # and the function to evaluate.
+
+            f_interp = interpolate.interp1d(inMesh, iData[:,iParam])
+
+            # Map the function to the new mesh. 
+            outData[:, iParam] = f_interp(outData[:, 0])
+
+        return outData
+
+
diff --git a/dart/viscous/Drivers/VPostProcess.py b/dart/viscous/Drivers/VPostProcess.py
index 0cdfd3b..3d844db 100644
--- a/dart/viscous/Drivers/VPostProcess.py
+++ b/dart/viscous/Drivers/VPostProcess.py
@@ -1,31 +1,63 @@
 from matplotlib import pyplot as plt
 import numpy as np
+from scipy import interpolate
 
 class PostProcess:
     def __init__(self):
         pass
 
-    def sortParam(self,var):
+    def sortParam(self,var, mapMesh, inMesh, inMeshbNodes, xxInit):
+
 
         # Recompute deltaStar.
 
         var.blPar[9::var.nBlPar] = var.u[0::var.nVar] * var.u[1::var.nVar]
 
-        # Compute blowing velocity on each cell.
-        blwVel=np.zeros(var.nN-1)
+        if mapMesh:
+
+            rho, invVel, deltaStar = self.inverseMeshMapping(var, inMesh, inMeshbNodes)
+
+            AFdeltaStarOut = deltaStar[:inMeshbNodes[2]]
+            WKdeltaStarOut = deltaStar[inMeshbNodes[2]:]
+
+            # Compute blowing velocity on each cell.
+            blwVel=np.zeros(len(xxInit)-1)
+
+            for iPoint in range(1, len(xxInit)):
+
+                iPrev=0 if iPoint == inMeshbNodes[1] else iPoint - 1
+
+                # blwVel[i-1] = (rhoe[i]*vt[i]*deltaStar[i]-rhoe[i-1]*vt[i-1]*deltaStar[i-1])/(rhoe[i]*(xx[i]-xx[i-1])) 
+                blwVel[iPoint-1] = (rho[iPoint] * invVel[iPoint] * deltaStar[iPoint]
+                                - rho[iPrev] * invVel[iPrev] * deltaStar[iPrev]) / (rho[iPoint] * (xxInit[iPoint] - xxInit[iPrev]))
+            
+            blwVel = np.delete(blwVel, inMeshbNodes[2])
+            # Separate airfoil and wake blowing velocities.
+            AFblwVel=blwVel[:inMeshbNodes[2]-1]
+            WKblwVel=blwVel[inMeshbNodes[2]-1:]
+
+        else:
+            
+            AFdeltaStarOut = var.blPar[9:var.bNodes[2]*var.nBlPar:var.nBlPar]
+            WKdeltaStarOut = var.blPar[var.bNodes[2]*var.nBlPar + 9::var.nBlPar]
+
+            # Compute blowing velocity on each cell.
+            blwVel=np.zeros(var.nN-1)
 
-        for i in range(1, var.nN):
+            for iPoint in range(1, var.nN):
 
-            iPrev=0 if i==var.bNodes[1] else i-1
+                iPrev=0 if iPoint == var.bNodes[1] else iPoint - 1
 
-            # blwVel[i-1] = (rhoe[i]*vt[i]*deltaStar[i]-rhoe[i-1]*vt[i-1]*deltaStar[i-1])/(rhoe[i]*(xx[i]-xx[i-1])) 
-            blwVel[i-1] = (var.extPar[i*var.nExtPar+1]*var.u[i*var.nVar+3] * var.blPar[i*var.nBlPar+9]
-                            - var.extPar[iPrev*var.nExtPar+1] * var.u[iPrev*var.nVar+3] * var.blPar[iPrev*var.nBlPar+9]) / (var.extPar[i*var.nExtPar+1] * (var.xx[i] - var.xx[iPrev]))
-        
-        blwVel = np.delete(blwVel, var.bNodes[2])
-        # Separate airfoil and wake blowing velocities.
-        AFblwVel=blwVel[:var.bNodes[2]-1]
-        WKblwVel=blwVel[var.bNodes[2]-1:]
+                # blwVel[i-1] = (rhoe[i]*vt[i]*deltaStar[i]-rhoe[i-1]*vt[i-1]*deltaStar[i-1])/(rhoe[i]*(xx[i]-xx[i-1])) 
+                blwVel[iPoint-1] = (var.extPar[iPoint*var.nExtPar+1] * var.u[iPoint*var.nVar+3] * var.blPar[iPoint*var.nBlPar+9]
+                                - var.extPar[iPrev*var.nExtPar+1] * var.u[iPrev*var.nVar+3] * var.blPar[iPrev*var.nBlPar+9]) / (var.extPar[iPoint*var.nExtPar+1] * (var.xx[iPoint] - var.xx[iPrev]))
+            
+            print('blwVel bnodes2', blwVel[var.bNodes[2]])
+            print('blwVel bnodes2-1', blwVel[var.bNodes[2]-1])
+            blwVel = np.delete(blwVel, var.bNodes[2])
+            # Separate airfoil and wake blowing velocities.
+            AFblwVel=blwVel[:var.bNodes[2]-1]
+            WKblwVel=blwVel[var.bNodes[2]-1:]
 
         # Normalize Friction and dissipation coefficients.
         frictionCoeff=var.u[3::var.nVar]**2 * var.blPar[2::var.nBlPar]
@@ -67,4 +99,44 @@ class PostProcess:
                         var.u[var.bNodes[2]*var.nVar+4::var.nVar],
                         var.blPar[var.bNodes[2]*var.nBlPar+3::var.nBlPar]]
 
-        return blScalars, AFblwVel, WKblwVel, blVectors_airfoil, blVectors_wake
+        return blScalars, AFblwVel, WKblwVel, blVectors_airfoil, blVectors_wake, AFdeltaStarOut, WKdeltaStarOut
+
+
+    def inverseMeshMapping(self, var, inMesh, inMeshbNodes):
+        """Maps the variables required for blowing velocity computation from
+        the fine mesh to the coarse mesh. Group separation is performed
+        to avoid confusion between the points at the same place on the mesh
+        but with different physical meaning (e.g First wake point).
+        """
+
+        fMapRhoUpper         = interpolate.interp1d(var.xGlobal[:var.bNodes[1]].copy(), var.extPar[1:var.bNodes[1]*var.nExtPar:var.nExtPar].copy())
+        rhoUpper             = fMapRhoUpper(inMesh[:inMeshbNodes[1]])
+        fMapRhoLower         = interpolate.interp1d(var.xGlobal[var.bNodes[1]:var.bNodes[2]].copy(),
+                                                     var.extPar[var.bNodes[1]*var.nExtPar + 1:var.bNodes[2]*var.nExtPar:var.nExtPar].copy())
+        rhoLower             = fMapRhoLower(inMesh[inMeshbNodes[1]:inMeshbNodes[2]])
+        fMapRhoWake          = interpolate.interp1d(var.xGlobal[var.bNodes[2]:].copy(), var.extPar[var.bNodes[2]*var.nExtPar + 1::var.nExtPar].copy())
+        rhoWake              = fMapRhoWake(inMesh[inMeshbNodes[2]:])
+
+        rho = np.concatenate((rhoUpper, rhoLower, rhoWake))
+
+        fMapinvVelUpper      = interpolate.interp1d(var.xGlobal[:var.bNodes[1]].copy(), var.u[3:var.bNodes[1]*var.nVar:var.nVar].copy())
+        invVelUpper          = fMapinvVelUpper(inMesh[:inMeshbNodes[1]])
+        fMapinvVelLower      = interpolate.interp1d(var.xGlobal[var.bNodes[1]:var.bNodes[2]].copy(),
+                                                     var.u[var.bNodes[1]*var.nVar + 3:var.bNodes[2]*var.nVar:var.nVar].copy())
+        invVelLower          = fMapinvVelLower(inMesh[inMeshbNodes[1]:inMeshbNodes[2]])
+        fMapinvVelWake       = interpolate.interp1d(var.xGlobal[var.bNodes[2]:].copy(), var.u[var.bNodes[2]*var.nVar + 3::var.nVar].copy())
+        invVelWake           = fMapinvVelWake(inMesh[inMeshbNodes[2]:])
+
+        invVel = np.concatenate((invVelUpper, invVelLower, invVelWake))
+
+        fMapdeltaStarUpper   = interpolate.interp1d(var.xGlobal[:var.bNodes[1]].copy(), var.blPar[9:var.bNodes[1]*var.nBlPar:var.nBlPar].copy())
+        deltaStarUpper       = fMapdeltaStarUpper(inMesh[:inMeshbNodes[1]])
+        fMapdeltaStarLower   = interpolate.interp1d(var.xGlobal[var.bNodes[1]:var.bNodes[2]].copy(),
+                                                     var.blPar[var.bNodes[1]*var.nBlPar + 9:var.bNodes[2]*var.nBlPar:var.nBlPar].copy())
+        deltaStarLower       = fMapdeltaStarLower(inMesh[inMeshbNodes[1]:inMeshbNodes[2]])
+        fMapdeltaStarWake    = interpolate.interp1d(var.xGlobal[var.bNodes[2]:].copy(), var.blPar[var.bNodes[2]*var.nBlPar + 9::var.nBlPar].copy())
+        deltaStarWake        = fMapdeltaStarWake(inMesh[inMeshbNodes[2]:])
+
+        deltaStar = np.concatenate((deltaStarUpper, deltaStarLower, deltaStarWake))
+
+        return rho, invVel, deltaStar
diff --git a/dart/viscous/Physics/VBIConditions.py b/dart/viscous/Physics/VBIConditions.py
index 4f8de9c..c4cbf59 100755
--- a/dart/viscous/Physics/VBIConditions.py
+++ b/dart/viscous/Physics/VBIConditions.py
@@ -44,48 +44,69 @@ class Initial:
         regime prescibed by the inviscid solver. 
         - Define boundary conditions at the stagnation point and the first wake point 
         """
-        u0=np.zeros(var.nN*var.nVar)
 
-        for n in range(3): # 3 components (upper, lower sides, wake)
+        
+        u0=np.zeros(var.nN*var.nVar)        # Vector containing the intial solution
+        
+        # Loop over the three groups (upper, lower sides, wake)
+        for n in range(3):
+
             if n==2:
-                xx=var.xx[var.bNodes[n]:]
-                vt=var.extPar[var.bNodes[n]*var.nExtPar+2::var.nExtPar]
-                rho=var.extPar[var.bNodes[n]*var.nExtPar+1::var.nExtPar]
+
+                xx=var.xx[var.bNodes[n]:]                                         # Airfoil local coordinates.
+                vt=var.extPar[var.bNodes[n]*var.nExtPar+2::var.nExtPar]           # Inviscid velocities.
+                rho=var.extPar[var.bNodes[n]*var.nExtPar+1::var.nExtPar]          # Air density in each cell. 
+
             else:
+
                 xx=var.xx[var.bNodes[n]:var.bNodes[n+1]]
                 vt=var.extPar[var.bNodes[n]*var.nExtPar+2:var.bNodes[n+1]*var.nExtPar:var.nExtPar]
                 rho=var.extPar[var.bNodes[n]*var.nExtPar+1:var.bNodes[n+1]*var.nExtPar:var.nExtPar]
 
+            # Reynolds number based on the arc length.
             Rex=rho*vt*xx/self.mu_air
             Rex[Rex==0]=1 # Stagnation point
-            ThetaTw=xx/np.sqrt(Rex)
-            # lambda : Dimensionless pressure-gradient parameter 
-            lambdaTw=np.zeros(len(ThetaTw))
-            HTw=np.zeros(len(ThetaTw))
-            for idx in range(0,len(lambdaTw)):
-                idxPrev=idx-1
-                lambdaTw[idx]=(rho[idx]*ThetaTw[idx]**2)/self.mu_air*(vt[idx]-vt[idxPrev])#/(xx[idx]-xx[idxPrev])
+
+            ThetaTw=xx/np.sqrt(Rex)             # Momentum thickness.
+            lambdaTw=np.zeros(len(ThetaTw))     # Dimensionless pressure gradient parameter.
+            HTw=np.zeros(len(ThetaTw))          # Shape factor of the boundary layer.
+
+            for iMarker in range(len(lambdaTw)):
+
+                idxPrev = iMarker-1
+                lambdaTw[iMarker] = (rho[iMarker] * ThetaTw[iMarker]**2) / self.mu_air * (vt[iMarker] - vt[idxPrev])#/(xx[idx]-xx[idxPrev])
+
                 # Empirical correlations for H=f(lambda)
-                if 0<=lambdaTw[idx]<0.1:
-                    HTw[idx]=2.61-3.75*lambdaTw[idx]+5.24*lambdaTw[idx]**2
-                elif -0.1<lambdaTw[idx]<0:
-                    HTw[idx]=2.088+0.0731/(lambdaTw[idx]+0.14)
+                if 0 <= lambdaTw[iMarker] < 0.1:
+
+                    HTw[iMarker] = 2.61 - 3.75*lambdaTw[iMarker] + 5.24*lambdaTw[iMarker]**2
+
+                elif -0.1<lambdaTw[iMarker]<0:
+
+                    HTw[iMarker] = 2.088 + 0.0731 / (lambdaTw[iMarker] + 0.14)
             
             if n==2:
-                u0[var.bNodes[n]*var.nVar+0::var.nVar]=ThetaTw # Theta
-                u0[var.bNodes[n]*var.nVar+1::var.nVar]=HTw # H
+
+                u0[var.bNodes[n]*var.nVar+0::var.nVar]=ThetaTw      # Theta
+                u0[var.bNodes[n]*var.nVar+1::var.nVar]=HTw          # H
+
             else:
-                u0[var.bNodes[n]*var.nVar+0:var.bNodes[n+1]*var.nVar:var.nVar]=ThetaTw # Theta
-                u0[var.bNodes[n]*var.nVar+1:var.bNodes[n+1]*var.nVar:var.nVar]=HTw # H
 
-        u0[1::var.nVar]=2 # H
-        u0[2::var.nVar]=0 # N
-        u0[3::var.nVar]=var.extPar[2::var.nExtPar] # Vt
-        u0[4::var.nVar]=0.001 # Ct
+                u0[var.bNodes[n]*var.nVar+0:var.bNodes[n+1]*var.nVar:var.nVar]=ThetaTw      # Theta
+                u0[var.bNodes[n]*var.nVar+1:var.bNodes[n+1]*var.nVar:var.nVar]=HTw          # H
 
+        u0[1::var.nVar]=2                           # H
+        u0[2::var.nVar]=0                           # N
+        u0[3::var.nVar]=var.extPar[2::var.nExtPar]  # Vt
+        u0[4::var.nVar]=0.001                       # Ct
+        
+        # Set initial condition. 
         var.u=u0
+
+        # Impose boudary conditions.
         self.boundCond.airfoilBC(var)
         self.boundCond.wakeBC(var)
+        pass
 
 
 class Boundaries:
@@ -97,33 +118,46 @@ class Boundaries:
         ----------
         Re: Flow Reynolds number.
         """
+
     def __init__(self, _Re):
-        self.Re=_Re
+
+        self.Re = _Re
         
     # Airfoil boundary conditions
     def airfoilBC(self, var):
         """Boundary conditions at the stagnation point.
-        Twaithes solution method values used for the SP.
+        Twaithes solution method values used for the
+        stagnation point.
         """
+
         if var.dv[0] > 0:
-            T0 = np.sqrt(0.075/(self.Re*var.dv[0]))
+
+            T0 = np.sqrt(0.075 / (self.Re * var.dv[0]))
+
         else:
+
             T0 = 1e-6
-        H0 = 2.23 # One parameter family Falkner Skan
-        n0 = 0
-        ue0=var.extPar[2]
-        Ct0 = 0
-
-        # Stagnation point
-        var.blPar[var.bNodes[0]*var.nBlPar+0] = ue0*T0*var.Re # Ret
-        if var.blPar[var.bNodes[0]*var.nBlPar+0]<1:
-            var.blPar[var.bNodes[0]*var.nBlPar+0]=1
-            ue0=var.blPar[var.bNodes[0]*var.nBlPar+0]/(T0*var.Re)
-        var.blPar[var.bNodes[0]*var.nBlPar+9]=T0*H0 # deltaStar
-        var.blPar[(var.bNodes[0]*var.nBlPar+1):(var.bNodes[0]*var.nBlPar+7)]=Closures.laminarClosure(T0, H0, var.blPar[var.bNodes[0]*var.nBlPar+0],var.extPar[var.bNodes[0]*var.nExtPar+0], 'airfoil')
-        # Cteq stays = 0
+
+        H0 = 2.23           # One parameter family Falkner Skan.
+        n0 = 0              # Initial log of amplification factor.
+        ue0=var.extPar[2]   # Inviscid flow velocity.
+        Ct0 = 0             # Stagnation point is laminar. 
+
+        # Compute Reynolds number based on the momentum thickness
+        var.blPar[var.bNodes[0]*var.nBlPar + 0] = ue0*T0*var.Re
+
+        if var.blPar[var.bNodes[0]*var.nBlPar+0] < 1:
+            var.blPar[var.bNodes[0]*var.nBlPar+0] = 1
+            ue0 = var.blPar[var.bNodes[0]*var.nBlPar + 0] / (T0*var.Re)
+
+        var.blPar[var.bNodes[0]*var.nBlPar + 9] = T0 * H0 # deltaStar
+        var.blPar[(var.bNodes[0]*var.nBlPar + 1):(var.bNodes[0]*var.nBlPar+7)]=Closures.laminarClosure(T0, H0,
+                                                                                                        var.blPar[var.bNodes[0]*var.nBlPar+0],
+                                                                                                        var.extPar[var.bNodes[0]*var.nExtPar+0],
+                                                                                                         'airfoil')
         #var.blPar[var.bNodes[0]*var.nBlPar+8]=(var.blPar[var.bNodes[0]*var.nBlPar+4]/2)*(1-4*(var.blPar[var.bNodes[0]*var.nBlPar+6]-1)/(3*H0)) # Equivalent normalized wall slip velocity
         
+        # Set boundary condition.
         var.u[var.bNodes[0]*var.nVar:(var.bNodes[0]+1)*var.nVar]=[T0, H0, n0, ue0, Ct0]
         pass
 
@@ -133,23 +167,33 @@ class Boundaries:
         based on the parameters at the last point on the upper and lower sides.
         Drela (1989).
         """
+
         xEnd_up=var.u[(var.bNodes[1]-1)*var.nVar:var.bNodes[1]*var.nVar]
         xEnd_lw=var.u[(var.bNodes[2]-1)*var.nVar:var.bNodes[2]*var.nVar]
-        T0=xEnd_up[0]+xEnd_lw[0] # (Theta_up+Theta_low)
-        H0=(xEnd_up[0]*xEnd_up[1]+xEnd_lw[0]*xEnd_lw[1])/T0 # ((Theta*H)_up+(Theta*H)_low)/(Theta_up+Theta_low)
+
+        T0=xEnd_up[0]+xEnd_lw[0]                                # (Theta_up+Theta_low).
+        H0=(xEnd_up[0]*xEnd_up[1]+xEnd_lw[0]*xEnd_lw[1])/T0     # ((Theta*H)_up+(Theta*H)_low)/(Theta_up+Theta_low).
         n0=9
-        ue0=var.extPar[var.bNodes[2]*var.nExtPar+2]
-        Ct0=(xEnd_up[0]*xEnd_up[4]+xEnd_lw[0]*xEnd_lw[4])/T0 # ((Ct*Theta)_up+(Theta)_low)/(Ct*Theta_up+Theta_low)
+        ue0=var.extPar[var.bNodes[2]*var.nExtPar+2]             # Inviscid velocity.
+        Ct0=(xEnd_up[0]*xEnd_up[4]+xEnd_lw[0]*xEnd_lw[4])/T0    # ((Ct*Theta)_up+(Theta)_low)/(Ct*Theta_up+Theta_low).
 
+        # Reynolds number based on the momentum thickness.
         var.blPar[var.bNodes[2]*var.nBlPar+0] = ue0*T0*var.Re
-        if var.blPar[var.bNodes[2]*var.nBlPar+0]<1:
-            var.blPar[var.bNodes[2]*var.nBlPar+0]=1
-            ue0=var.blPar[var.bNodes[2]*var.nBlPar+0]/(T0*var.Re)
+        
+        if var.blPar[var.bNodes[2]*var.nBlPar+0] < 1:
+            var.blPar[var.bNodes[2]*var.nBlPar+0] = 1
+            ue0 = var.blPar[var.bNodes[2]*var.nBlPar+0] / (T0*var.Re)
+
         var.blPar[var.bNodes[2]*var.nBlPar+9]=T0*H0 # deltaStar
+
         # Laminar reset
-        var.blPar[(var.bNodes[2]*var.nBlPar+1):(var.bNodes[2]*var.nBlPar+7)]=Closures.laminarClosure(T0, H0, var.blPar[var.bNodes[2]*var.nBlPar+0],var.extPar[var.bNodes[2]*var.nExtPar+0], 'wake')
+        var.blPar[(var.bNodes[2]*var.nBlPar+1):(var.bNodes[2]*var.nBlPar+7)]=Closures.laminarClosure(T0, H0,
+                                                                                                     var.blPar[var.bNodes[2]*var.nBlPar+0],
+                                                                                                     var.extPar[var.bNodes[2]*var.nExtPar+0],
+                                                                                                     'wake')
         #var.blPar[var.bNodes[2]*var.nBlPar+7]=0 # Cteq stays = 0
         #var.blPar[var.bNodes[2]*var.nBlPar+8]=(var.blPar[var.bNodes[2]*var.nBlPar+4]/2)*(1-4*(var.blPar[var.bNodes[2]*var.nBlPar+6]-1)/(3*H0)) if H0!=0 else 0 # Equivalent normalized wall slip velocity
         
-        var.u[var.bNodes[2]*var.nVar:(var.bNodes[2]+1)*var.nVar]=[T0, H0, n0, ue0, Ct0]
+        # Set boundary condition. 
+        var.u[var.bNodes[2]*var.nVar:(var.bNodes[2]+1)*var.nVar] = [T0, H0, n0, ue0, Ct0]
         pass
diff --git a/dart/viscous/Physics/VVariables.py b/dart/viscous/Physics/VVariables.py
index a5a9f62..094e74d 100755
--- a/dart/viscous/Physics/VVariables.py
+++ b/dart/viscous/Physics/VVariables.py
@@ -16,6 +16,7 @@
 # ------------------------------------------------------------------------------
 import numpy as np 
 from matplotlib import pyplot as plt 
+from fwk.coloring import ccolors
 
 class Variables:
     """Data container for viscous solver.
@@ -66,38 +67,39 @@ class Variables:
 
     """
     def __init__(self, data, paramDict, bNodes, xtrF, Ti, Re):
-        self.__Ti=Ti
-        self.Re=Re
-        self.xx=paramDict['xx']
-        self.nN=len(self.xx)
+        self.__Ti = Ti
+        self.Re   = Re
+        self.xx   = paramDict['xx']
+        self.nN   = len(self.xx)
 
-        self.bNodes=bNodes
+        self.bNodes = bNodes
         # Solution 
-        self.nVar=5
-        self.u=np.zeros(self.nVar*self.nN)
+        self.nVar = 5
+        self.u = np.zeros(self.nVar*self.nN)
 
         # Parameters of the boundary layers
         # [ 0 ,  1,  2,     3,     4,      5,  6,    7,  8,         9]
         # [Ret, Cd, Cf, delta, Hstar, Hstar2, Hk, Cteq, Us, deltaStar]
-        self.nBlPar=10
-        self.blPar=np.zeros(self.nBlPar*self.nN)
-        self.Ret_previous=self.blPar[0::self.nVar]
+        self.nBlPar = 10
+        self.blPar        = np.zeros(self.nBlPar*self.nN)
+        self.Ret_previous = self.blPar[0::self.nVar]
 
-        self.dueOld_dx=np.zeros(self.nN)
-        self.ddeltaStarOld_dx=np.zeros(self.nN)
+        self.dueOld_dx = np.zeros(self.nN)
+        self.ddeltaStarOld_dx = np.zeros(self.nN)
 
         # Parameters of the external (inviscid) flow
         # [ 0,    1,     2,            3,     4,    5]
         # [Me, rhoe, vtExt, deltaStarExt, xxExt ReExt]
-        self.nExtPar=6
-        self.extPar=np.zeros(self.nExtPar*self.nN)
-        self.extPar[0::self.nExtPar]=data[:,5]
-        self.extPar[1::self.nExtPar]=data[:,6]
-        self.extPar[2::self.nExtPar]=paramDict['vtExt']
-        self.extPar[3::self.nExtPar]=data[:,7]
-        self.extPar[4::self.nExtPar]=paramDict['xx']
-        self.dv=paramDict['dv']
-        self.xGlobal=data[:,0]
+        self.nExtPar = 6
+
+        self.extPar                  = np.zeros(self.nExtPar*self.nN)
+        self.extPar[0::self.nExtPar] = data[:,5]
+        self.extPar[1::self.nExtPar] = data[:,6]
+        self.extPar[2::self.nExtPar] = paramDict['vtExt']
+        self.extPar[3::self.nExtPar] = data[:,7]
+        self.extPar[4::self.nExtPar] = paramDict['xx']
+        self.dv                      = paramDict['dv']
+        self.xGlobal                 = data[:,0]
 
         self.flowRegime = np.zeros(self.nN, dtype=int)
 
diff --git a/dart/viscous/README.md b/dart/viscous/README.md
index fe02415..71ba0a1 100644
--- a/dart/viscous/README.md
+++ b/dart/viscous/README.md
@@ -13,7 +13,7 @@ The two flow domains are coupled through a quasi-simulataneous interaction law t
   - Time-dependant equations solution through a pseudo-time marching procedure.
 
 ## Solver operation map
-The viscous inviscid interaction can be launched from /dart/tests/bli_xxx.py. The class in Master/Coupler.py is first called with the required arguments and manages the two solvers to work togheter. The steady-state equations solver is called from /Solvers/Steady/StdSolver.py. The time-dependent equations solver is called from /Drivers/VDriver.py which initializes the different components of the code and starts the viscous flow computation using a implicit Euler pseudo-time marching procedures. 
+The viscous inviscid interaction can be launched from /dart/tests/bli_xxx.py. The class in Master/Coupler.py is first called with the required arguments and manages the two solvers to work togheter. The steady-state equations solver is called from /Solvers/Steady/StdSolver.py. The time-dependent equations solver is called from /Drivers/VDriver.py which initializes the different components of the code and starts the viscous flow computation using an implicit Euler pseudo-time marching procedure. 
 
 ## References
 Crovato Adrien, [Steady Transonic Aerodynamic and Aeroelastic Modeling for Preliminary Aircraft Design](http://hdl.handle.net/2268/251906), PhD thesis, University of Liège, 2020.
diff --git a/dart/viscous/Solvers/Steady/StdSolver.py b/dart/viscous/Solvers/Steady/StdSolver.py
index 9185fa4..5ae44a3 100755
--- a/dart/viscous/Solvers/Steady/StdSolver.py
+++ b/dart/viscous/Solvers/Steady/StdSolver.py
@@ -465,7 +465,7 @@ class Solver:
             self.blScalars = np.add(ublScalars, lblScalars)
             self.blVectors = np.hstack((ublVectors, lblVectors))
             blwVel = np.concatenate((ublwVel, lblwVel))
-            print('AF',len(blwVel))
+            print('AFblwVel',len(blwVel))
             self.xtr = [xtrTop, xtrBot]
             # Boundary conditions for the wake
             group.TEnd = [ublVectors[1][-1], lblVectors[1][-1]]
@@ -474,12 +474,14 @@ class Solver:
             # Delete the stagnation point doublon for variables in the VII loop
             lblVectors[0] = np.delete(lblVectors[0],0,0) #deltastar
             lxx = np.delete(lxx,0,0) # airfoil coordinates
+            print('AFdeltaStarOut', len(np.concatenate((ublVectors[0], lblVectors[0]))))
             group.deltaStar = np.concatenate((ublVectors[0], lblVectors[0]))
             group.xx = np.concatenate((uxx, lxx))
         # Compute BLE for the wake
         else:
             blwVel, _, group.xx, blScalars, blVectors = self.__boundaryLayer(data, group)
-            print('WK',len(blwVel))
+            print('WKblwVel',len(blwVel))
+            print('WKdeltaStarOut', len(blVectors[0]))
             group.deltaStar = blVectors[0]
             # The drag is measured at the end of the wake
             self.Cd = blScalars[0]
@@ -489,5 +491,6 @@ class Solver:
             self.Cdf = self.blScalars[1]
         # Sort the following results in reference frame
         group.deltaStar = group.deltaStar[group.connectListNodes.argsort()]
+        print('xxInit',len(group.xx))
         group.xx = group.xx[group.connectListNodes.argsort()]
         group.u = blwVel[group.connectListElems.argsort()]
diff --git a/dart/viscous/Solvers/VSolver.py b/dart/viscous/Solvers/VSolver.py
index 5c3a0f2..91bf0b9 100644
--- a/dart/viscous/Solvers/VSolver.py
+++ b/dart/viscous/Solvers/VSolver.py
@@ -44,12 +44,12 @@ class flowEquations:
         if iMarker == dataCont.bNodes[2]:
             return np.zeros(dataCont.nVar)
 
-        timeMatrix=np.empty((dataCont.nVar, dataCont.nVar))
-        spaceMatrix=np.empty(dataCont.nVar)
+        timeMatrix  = np.empty((dataCont.nVar, dataCont.nVar))
+        spaceMatrix = np.empty(dataCont.nVar)
         
         if x is None: x = dataCont.u[iMarker*dataCont.nVar : (iMarker+1)*dataCont.nVar]
         
-        iMarkerPrev2, iMarkerPrev, iMarkerNext, name, xtr, xtrF=self.__evalPoint(iMarker, dataCont.bNodes, dataCont.xtr, dataCont.xtrF, dataCont.nN)
+        iMarkerPrev2, iMarkerPrev, iMarkerNext, name, xtr, xtrF = self.__evalPoint(iMarker, dataCont.bNodes, dataCont.xtr, dataCont.xtrF, dataCont.nN)
         
         dissipRatio = 0.9 if name == 'wake' else 1 # Wall/wake dissipation length ratio
 
@@ -62,14 +62,14 @@ class flowEquations:
         if dataCont.flowRegime[iMarker]==0: 
 
             # Laminar closure relations
-            dataCont.blPar[iMarker*dataCont.nBlPar+1:iMarker*dataCont.nBlPar+7]=Closures.laminarClosure(x[0], x[1],
+            dataCont.blPar[iMarker*dataCont.nBlPar+1:iMarker*dataCont.nBlPar+7] = Closures.laminarClosure(x[0], x[1],
                                                                                                          dataCont.blPar[iMarker*dataCont.nBlPar+0],
                                                                                                          dataCont.extPar[iMarker*dataCont.nExtPar+0], name)
 
         elif dataCont.flowRegime[iMarker]==1:
 
             # Turbulent closures relations 
-            dataCont.blPar[iMarker*dataCont.nBlPar+1:iMarker*dataCont.nBlPar+9]=Closures.turbulentClosure(x[0], x[1],
+            dataCont.blPar[iMarker*dataCont.nBlPar+1:iMarker*dataCont.nBlPar+9] = Closures.turbulentClosure(x[0], x[1],
                                                                                                           x[4], dataCont.blPar[iMarker*dataCont.nBlPar+0],
                                                                                                           dataCont.extPar[iMarker*dataCont.nExtPar+0], name)
 
@@ -118,7 +118,7 @@ class flowEquations:
             # Interaction law
             # ----------------
             #
-            # due/dt - c*H*dTheta/dt - c*Theta*dH/dt + due/dx-c*(H*dTheta/dx+Theta*dH/dx) + (-due/dx + c*deltaStar)_Old
+            # due/dt - c*H*dTheta/dt - c*Theta*dH/dt + due/dx-c*(H*dTheta/dx+Theta*dH/dx) + (-due/dx + c*deltaStar)_Old = 0
             #
             # Unsteady shear lag equation (ignored in the laminar case)
             # ---------------------------------------------------------
@@ -156,7 +156,7 @@ class flowEquations:
 
         if np.linalg.det(timeMatrix) == 0:
             raise RuntimeError('Singular time matrix.')
-        sysRes = np.linalg.solve(timeMatrix,-spaceMatrix).flatten()
+        sysRes = np.linalg.solve(timeMatrix, spaceMatrix).flatten()
         return sysRes
 
 
@@ -220,18 +220,7 @@ class sysMatrix(flowEquations):
         self._nMarkers = _nMarkers
         pass
 
-    class Jacobian:
-
-        def _initialize(nVar, nMarkers) -> np.ndarray:
-            return np.zeros((nVar * nMarkers, nVar * nMarkers))
-
-    class linSysRes:
-
-        def _initialize(nVar, nMarkers) -> np.ndarray:
-            return np.zeros(nVar * nMarkers)
-
-
-    def buildJacobian(self, dataCont, marker = -1, order = 1, linSysRes = None, eps = 1e-10) -> np.ndarray:
+    def buildJacobian(self, dataCont, marker, sysRes, order = 1, eps = 1e-10) -> np.ndarray:
         """Contruct the Jacobian used for solving the liner system.
         
             Args:
@@ -254,61 +243,42 @@ class sysMatrix(flowEquations):
 
             """
         
-        # Build Jacobian for whole domain
-        if marker == -1:
-            sysMatSize = self._nMarker * self._nMarkers
-            Jacobian = np.zeros((sysMatSize, sysMatSize))
-
-            for iMarker in range(1,dataCont.nMarkers):
-                xUp = dataCont.u[iMarker*dataCont.nMarkers : (iMarker+1)*dataCont.nMarkers].copy()
-                xDwn = dataCont.u[iMarker*dataCont.nMarkers : (iMarker+1)*dataCont.nMarkers].copy()
-
-                for iVar in range(dataCont.nVar):
-                    xSave = xUp
-                    xUp += eps
-                    xDwn -= eps
-
         # Build Jacobian for one point 
-        else:
+        if order == 1 and sysRes is not None:
             
-            if order == 1 and linSysRes is not None:
-                
-                JacMatrix = np.zeros((dataCont.nVar, dataCont.nVar))
-                xUp = dataCont.u[marker*dataCont.nVar : (marker+1)*dataCont.nVar].copy()
+            JacMatrix = np.zeros((dataCont.nVar, dataCont.nVar))
+            xUp = dataCont.u[marker*dataCont.nVar : (marker+1)*dataCont.nVar].copy()
 
-                for iVar in range(dataCont.nVar):
-                    varSave = xUp[iVar]
-                    xUp[iVar] += eps
+            for iVar in range(dataCont.nVar):
+                varSave = xUp[iVar]
+                xUp[iVar] += eps
 
-                    JacMatrix[:,iVar] = (self._blLaws(dataCont, marker, x=xUp) - linSysRes) / eps
+                JacMatrix[:,iVar] = (self._blLaws(dataCont, marker, x=xUp) - sysRes) / eps
 
-                    xUp[iVar] = varSave
-                return JacMatrix
-            
-            elif order == 1:
-                raise RuntimeError('First order Jacobian determination requires the linear system residuals')
+                xUp[iVar] = varSave
+            return JacMatrix
 
-            elif order == 2:
-                JacMatrix = np.zeros((dataCont.nVar, dataCont.nVar))
-                xUp = dataCont.u[marker*dataCont.nVar : (marker+1)*dataCont.nVar].copy()
-                xDwn = dataCont.u[marker*dataCont.nVar : (marker+1)*dataCont.nVar].copy()
+        elif order == 2:
+            JacMatrix = np.zeros((dataCont.nVar, dataCont.nVar))
+            xUp = dataCont.u[marker*dataCont.nVar : (marker+1)*dataCont.nVar].copy()
+            xDwn = dataCont.u[marker*dataCont.nVar : (marker+1)*dataCont.nVar].copy()
 
-                for iVar in range(dataCont.nVar):
-                    varSave = xUp[iVar]
-                    xUp[iVar] += eps
-                    xDwn[iVar] -= eps
+            for iVar in range(dataCont.nVar):
+                varSave = xUp[iVar]
+                xUp[iVar] += eps
+                xDwn[iVar] -= eps
 
-                    JacMatrix[:,iVar] = (self._blLaws(dataCont, marker, x=xUp) - self._blLaws(dataCont, marker, x=xDwn)) / (2*eps)
+                JacMatrix[:,iVar] = (self._blLaws(dataCont, marker, x = xUp) - self._blLaws(dataCont, marker, x = xDwn)) / (2*eps)
 
-                    xUp[iVar] = varSave
-                    xDwn[iVar] = varSave
-                return JacMatrix
+                xUp[iVar] = varSave
+                xDwn[iVar] = varSave
+            return JacMatrix
 
-            else:
-                raise RuntimeError('Only first and second orders Jacobian can be computed.')
+        else:
+            raise RuntimeError('Only first and second orders Jacobian can be computed.')
 
 
-    def buildResiduals(self, dataCont, marker = -1) -> np.ndarray:
+    def buildResiduals(self, dataCont, marker) -> np.ndarray:
         """Construct residuals of the linear system.
         
             Args:
@@ -319,9 +289,5 @@ class sysMatrix(flowEquations):
             - marker (int, optional): Cell id on the computational domain. default: -1 for whole domain.
             """
 
-        # Build Residual for the whole domain
-        if marker == -1:
-            a=1
-
         # Build Residual for one point
-        else: return self._blLaws(dataCont, marker, x=None)
\ No newline at end of file
+        return self._blLaws(dataCont, marker, x=None)
\ No newline at end of file
diff --git a/dart/viscous/Solvers/VTimeSolver.py b/dart/viscous/Solvers/VTimeSolver.py
index b14541d..1b4fe23 100644
--- a/dart/viscous/Solvers/VTimeSolver.py
+++ b/dart/viscous/Solvers/VTimeSolver.py
@@ -14,7 +14,6 @@
 # ------------------------------------------------------------------------------
 #  Imports
 # ------------------------------------------------------------------------------
-from typing import SupportsRound
 from dart.viscous.Physics.VClosures import Closures
 
 from matplotlib import pyplot as plt
@@ -124,7 +123,7 @@ class implicitEuler(timeConfig):
 
         self.__CFL0=_cfl0
         self.__maxIt = 20000
-        self.__tol = 1e-6
+        self.__tol = 1e-8
         self.solver=_solver
         self.transSolver = transition
         self.wakeBC = wakeBC
@@ -141,83 +140,92 @@ class implicitEuler(timeConfig):
     def setmaxIt(self, _maxIt): self.__maxIt = _maxIt
 
     def resetSolution(self, iMarker, var, u0):
+
         if iMarker == var.bNodes[1]:
+
             var.u[iMarker*var.nVar : (iMarker + 1)*var.nVar] = var.u[0*var.nVar : 1*var.nVar]
+
         elif iMarker != var.transMarkers[0] and iMarker != var.transMarkers[1]:
+
             var.u[iMarker*var.nVar : (iMarker + 1)*var.nVar] = var.u[(iMarker-1)*var.nVar : (iMarker)*var.nVar]
+
         else:
+            
             var.u[iMarker*var.nVar : (iMarker + 1)*var.nVar] = u0[iMarker*var.nVar : (iMarker + 1)*var.nVar].copy()
 
     def timeSolve(self, var):
-      """ ---------- Newton iteration to solve the boundary layer equations ----------
-
-          Damped Newton method in Pseudo-Time. Linear algebraic system (obtained by Taylor linearis.)
-          is solved with a direct method. 
-
-          Equations
-          ---------
-          dU/dt = F(U)
-          <=> (U_(n+1)-U_n)/dt = F(U_(n+1)) (Time disc.)
-          <=> (J-I)dU = -F(U_n), J=dF_n/dU_n (Linearis.)
-
-          Parameters
-          ----------
-          - var: class
-              Data container: - Solution
-                              - Boundary layer parameters
-                              - External flow parameters
-                              - Mesh
-                              - Transition information
-          
-          Tasks
-          ------
-          - Updates the solution 'var.u' through Newton iterations until steady state.
+        """ ---------- Newton iteration to solve the boundary layer equations ----------
+
+            Damped Newton method in Pseudo-Time. Linear algebraic system (obtained by Taylor linearis.)
+            is solved with a direct method. 
+
+            Equations
+            ---------
+            ∂U/∂t = F(U)
+            <=> (U_(n+1)-U_n)/∆t = - F(U_(n+1)) (Time disc.)
+            <=> ∆U/∆t = -(F(U_n) + ∂F/∂U*∆u), where ∂F/∂U = J(U_n)
+            <=> (I+J)dU = -F(U_n), J=dF_n/dU_n (Linearis.)
+
+            Parameters
+            ----------
+            - var: class
+                Data container: - Solution
+                                - Boundary layer parameters
+                                - External flow parameters
+                                - Mesh
+                                - Transition information
+            
+            Tasks
+            ------
+            - Updates the solution 'var.u' through Newton iterations until steady state.
 
-          - Asks 'solver' for spacial operator F and Jacobian J and uses a 
-          direct linear solver (LU decomposition).
+            - Asks 'solver' for spacial operator F and Jacobian J and uses a 
+            direct linear solver (LU decomposition).
 
-          - Corrects undesirable results.
+            - Corrects undesirable results.
 
-          - Asks the solver for transtion position/BC and wake BC to be updated at each iteration.
-          """
-      
-      # Flag to diplay the simulation evolution.
-      convergedPoints = np.zeros(var.nN)
-      convergedPoints[0] = 1 # Boundary condition
+            - Asks the solver for transtion position/BC and wake BC to be updated at each iteration.
+            """
+
+        
+        nN = var.nN                                     # Number of points in the computational domain.
+        nBlPar = var.nBlPar                             # Number of boundary layer parameters used for vector indexing.
+        nExtPar = var.nExtPar                           # Number of boundary layer parameters used for vector indexing.
+        bNodes = var.bNodes                             # Boundary nodes of the computational domain.
+        convergedPoints = np.zeros(var.nN)              # Convergence control of each point.
+        convergedPoints[0] = 1                          # Boundary condition.
       
-      # Initialize the flow regime on each cell.
-      self.transSolver.initTransition(var)
+        # Initialize the flow regime on each cell.
+        self.transSolver.initTransition(var)
 
-      # Transition is locked when it is found on one side. We do not look at the value of N
-      # for the remaining points on this side. 
-      lockTrans = 0
+        lockTrans = 0                                 # Flag to remember if the transition is already found or not.
 
-      for iMarker in range(1, var.nN):
-          displayState = 0
+        for iMarker in range(1, nN):
+            displayState = 0                          # Flag to output simulation state.
 
-          if not lockTrans and 1 < iMarker < var.bNodes[2]:
+            if not lockTrans and 1 < iMarker < bNodes[2]:
 
-              # Determine if the amplification waves start growing or not. 
-              logRet_crit = 2.492*(1/(var.blPar[(iMarker-1)*var.nBlPar + 6]-1))**0.43
-              + 0.7*(np.tanh(14*(1/(var.blPar[(iMarker-1)*var.nBlPar + 6]-1))-9.24)+1.0)
+                # Determine if the amplification waves start growing or not. 
+                logRet_crit = 2.492*(1/(var.blPar[(iMarker-1)*nBlPar + 6]-1))**0.43
+                + 0.7*(np.tanh(14*(1/(var.blPar[(iMarker-1)*nBlPar + 6]-1))-9.24)+1.0)
 
-              if var.blPar[(iMarker-1)*var.nBlPar + 0] > 0: # Avoid log of something <= 0
+                if var.blPar[(iMarker-1)*nBlPar + 0] > 0: # Avoid log of something <= 0
 
-                  if np.log10(var.blPar[(iMarker-1)*var.nBlPar + 0]) <= logRet_crit - 0.08:
+                    if np.log10(var.blPar[(iMarker-1)*nBlPar + 0]) <= logRet_crit - 0.08:
 
-                      var.u[iMarker*var.nVar + 2] = 0
+                        var.u[iMarker*var.nVar + 2] = 0
 
-          # Upper side start.
-          if iMarker == var.bNodes[0]+1:
-              print('| - Computing upper side. {:>54}'.format('|'))
+            # Upper side start.
+            if iMarker == bNodes[0]+1:
+                 print('| - Computing upper side. {:>54}'.format('|'))
           
-          # Lower side start.
-          if iMarker == var.bNodes[1]:
-              lockTrans = 0
-              print('| - Computing lower side. {:>54}'.format('|'))
+            # Lower side start.
+            if iMarker == bNodes[1]:
+                lockTrans = 0
+                print('| - Computing lower side. {:>54}'.format('|'))
 
-          # Wake start
-          if iMarker == var.bNodes[2]: # First wake point
+             # Wake start
+            if iMarker == bNodes[2]: # First wake point
 
               # Wake boundary condition.
               self.wakeBC(var)
@@ -228,104 +236,123 @@ class implicitEuler(timeConfig):
               # Ignore remaining instructions for the first wake point. 
               continue
 
-          if not lockTrans and iMarker-1 < var.bNodes[2] and var.u[(iMarker-1) * var.nVar +2 ] >= var.Ncrit:
-            # Transition
-            print('| Transition located near x/c = {:<2.3f}. Marker: {:<4.0f} {:>28s}'.format(var.xGlobal[iMarker-1],iMarker-1, '|'))
-
-            avTurb = self.transSolver.solveTransition(var, iMarker - 1)
-            # Save laminar solution
-            lamSol = var.u[(iMarker-1)*var.nVar : (iMarker)* var.nVar].copy()
-
-            # Set up turbulent portion boundary condition
-            avTurb = self.transSolver.solveTransition(var, iMarker - 1)
-
-            # Compute turbulent solution
-            var.flowRegime[iMarker - 1] = 1
-            self.newtonSolver(var, iMarker-1, displayState)
-
-            # Average solutions
-            var.u[(iMarker-1)*var.nVar : (iMarker)*var.nVar] = (1-avTurb) * lamSol + avTurb * var.u[(iMarker-1)*var.nVar : (iMarker)*var.nVar]
-
-            # Recompute closures
-            var.blPar[(iMarker-1)*var.nBlPar+1:(iMarker-1)*var.nBlPar+9]=Closures.turbulentClosure(var.u[(iMarker-1)*var.nVar+0],
-                                                                                                var.u[(iMarker-1)*var.nVar+1],
-                                                                                                var.u[(iMarker-1)*var.nVar+4],
-                                                                                                var.blPar[(iMarker-1)*var.nBlPar+0],
-                                                                                                var.extPar[(iMarker-1)*var.nExtPar+0],
-                                                                                                'airfoil')
-
-            lockTrans = 1
-
-          # Forced transition 
-          if not lockTrans and var.xtrF[0] is not None and (var.flowRegime[iMarker] == 1 and var.flowRegime[iMarker-1] == 0):
-              print('| Transition near {:<1.4f}. Marker: {:<5.0f} {:>40}'.format(var.xGlobal[iMarker-1],iMarker-1, '|'))
-              lamSol = var.u[(iMarker-1)*var.nVar : (iMarker)* var.nVar].copy()
-              avTurb = self.transSolver.transitionBC(var, iMarker - 1)
-              # Compute turbulent solution
-              var.flowRegime[iMarker - 1] = 1
-              self.newtonSolver(var, iMarker-1, displayState)
-              # Average solutions
-              var.u[(iMarker-1)*var.nVar : (iMarker)*var.nVar] = (1-avTurb) * lamSol + avTurb * var.u[(iMarker-1)*var.nVar : (iMarker)*var.nVar]
-              # Recompute closures
-              var.blPar[(iMarker-1)*var.nBlPar+1:(iMarker-1)*var.nBlPar+9]=Closures.turbulentClosure(var.u[(iMarker-1)*var.nVar+0],
-                                                                                                  var.u[(iMarker-1)*var.nVar+1],
-                                                                                                  var.u[(iMarker-1)*var.nVar+4],
-                                                                                                  var.blPar[(iMarker-1)*var.nBlPar+0],
-                                                                                                  var.extPar[(iMarker-1)*var.nExtPar+0],
-                                                                                                  'airfoil')
-
-              lockTrans = 1
-          if not lockTrans and var.xtrF[1] is not None and (var.flowRegime[iMarker]== 1 and var.flowRegime[iMarker-1] == 0):
-              print('| Transition near {:<1.4f}. Marker: {:<5.0f} {:>40}'.format(var.xGlobal[iMarker-1],iMarker-1, '|'))
-              lamSol = var.u[(iMarker-1)*var.nVar : (iMarker)* var.nVar].copy()
-              avTurb = self.transSolver.transitionBC(var, iMarker - 1)
-              # Compute turbulent solution
-              var.flowRegime[iMarker - 1] = 1
-              self.newtonSolver(var, iMarker-1, displayState)
-              # Average solutions
-              var.u[(iMarker-1)*var.nVar : (iMarker)*var.nVar] = (1-avTurb) * lamSol + avTurb * var.u[(iMarker-1)*var.nVar : (iMarker)*var.nVar]
-              # Recompute closures
-              var.blPar[(iMarker-1)*var.nBlPar+1:(iMarker-1)*var.nBlPar+9]=Closures.turbulentClosure(var.u[(iMarker-1)*var.nVar+0],
-                                                                                                  var.u[(iMarker-1)*var.nVar+1],
-                                                                                                  var.u[(iMarker-1)*var.nVar+4],
-                                                                                                  var.blPar[(iMarker-1)*var.nBlPar+0],
-                                                                                                  var.extPar[(iMarker-1)*var.nExtPar+0],
-                                                                                                  'airfoil')
-
-          # Call Newton solver for the point
-          convergedPoints[iMarker] = self.newtonSolver(var, iMarker, displayState)
-      
-      return convergedPoints
+            if not lockTrans and iMarker-1 < bNodes[2] and var.u[(iMarker-1) * var.nVar +2 ] >= var.Ncrit:
+                # Transition
+                print('| Transition located near x/c = {:<2.3f}. Marker: {:<4.0f} {:>28s}'.format(var.xGlobal[iMarker-1],iMarker-1, '|'))
+
+                avTurb = self.transSolver.solveTransition(var, iMarker - 1)
+                # Save laminar solution
+                lamSol = var.u[(iMarker-1)*var.nVar : (iMarker)* var.nVar].copy()
+
+                # Set up turbulent portion boundary condition
+                avTurb = self.transSolver.solveTransition(var, iMarker - 1)
+
+                # Compute turbulent solution
+                var.flowRegime[iMarker - 1] = 1
+                self.newtonSolver(var, iMarker-1, displayState)
+
+                # Average solutions
+                var.u[(iMarker-1)*var.nVar : (iMarker)*var.nVar] = (1-avTurb) * lamSol + avTurb * var.u[(iMarker-1)*var.nVar : (iMarker)*var.nVar]
+
+                # Recompute closures
+                var.blPar[(iMarker-1)*nBlPar+1:(iMarker-1)*nBlPar+9]=Closures.turbulentClosure(var.u[(iMarker-1)*var.nVar+0],
+                                                                                                    var.u[(iMarker-1)*var.nVar+1],
+                                                                                                    var.u[(iMarker-1)*var.nVar+4],
+                                                                                                    var.blPar[(iMarker-1)*nBlPar+0],
+                                                                                                    var.extPar[(iMarker-1)*nExtPar+0],
+                                                                                                    'airfoil')
+
+                lockTrans = 1
+
+            # Forced transition 
+            if not lockTrans and var.xtrF[0] is not None and (var.flowRegime[iMarker] == 1 and var.flowRegime[iMarker-1] == 0):
+                print('| Transition near {:<1.4f}. Marker: {:<5.0f} {:>40}'.format(var.xGlobal[iMarker-1],iMarker-1, '|'))
+                lamSol = var.u[(iMarker-1)*var.nVar : (iMarker)* var.nVar].copy()
+                avTurb = self.transSolver.transitionBC(var, iMarker - 1)
+                # Compute turbulent solution
+                var.flowRegime[iMarker - 1] = 1
+                self.newtonSolver(var, iMarker-1, displayState)
+                # Average solutions
+                var.u[(iMarker-1)*var.nVar : (iMarker)*var.nVar] = (1-avTurb) * lamSol + avTurb * var.u[(iMarker-1)*var.nVar : (iMarker)*var.nVar]
+                # Recompute closures
+                var.blPar[(iMarker-1)*nBlPar+1:(iMarker-1)*nBlPar+9]=Closures.turbulentClosure(var.u[(iMarker-1)*var.nVar+0],
+                                                                                                    var.u[(iMarker-1)*var.nVar+1],
+                                                                                                    var.u[(iMarker-1)*var.nVar+4],
+                                                                                                    var.blPar[(iMarker-1)*nBlPar+0],
+                                                                                                    var.extPar[(iMarker-1)*nExtPar+0],
+                                                                                                    'airfoil')
+
+                lockTrans = 1
+            if not lockTrans and var.xtrF[1] is not None and (var.flowRegime[iMarker]== 1 and var.flowRegime[iMarker-1] == 0):
+                print('| Transition near {:<1.4f}. Marker: {:<5.0f} {:>40}'.format(var.xGlobal[iMarker-1],iMarker-1, '|'))
+                lamSol = var.u[(iMarker-1)*var.nVar : (iMarker)* var.nVar].copy()
+                avTurb = self.transSolver.transitionBC(var, iMarker - 1)
+                # Compute turbulent solution
+                var.flowRegime[iMarker - 1] = 1
+                self.newtonSolver(var, iMarker-1, displayState)
+                # Average solutions
+                var.u[(iMarker-1)*var.nVar : (iMarker)*var.nVar] = (1-avTurb) * lamSol + avTurb * var.u[(iMarker-1)*var.nVar : (iMarker)*var.nVar]
+                # Recompute closures
+                var.blPar[(iMarker-1)*nBlPar+1:(iMarker-1)*nBlPar+9]=Closures.turbulentClosure(var.u[(iMarker-1)*var.nVar+0],
+                                                                                                    var.u[(iMarker-1)*var.nVar+1],
+                                                                                                    var.u[(iMarker-1)*var.nVar+4],
+                                                                                                    var.blPar[(iMarker-1)*nBlPar+0],
+                                                                                                    var.extPar[(iMarker-1)*nExtPar+0],
+                                                                                                    'airfoil')
+
+            # Call Newton solver for the point.
+            convergedPoints[iMarker] = self.newtonSolver(var, iMarker, displayState)
+        
+        return convergedPoints
 
 
     def newtonSolver (self, var, iMarker, displayState):
         
-        # Save initial condition in case a point diverges. 
-        u0=var.u.copy()
-
-        sysRes = self.solver.buildResiduals(var, iMarker)
-        normSysRes0 = np.linalg.norm(sysRes)
-
-        # Initial CFL and time step.
-        CFL = self.__CFL0
-        CFLAdapt = 1
-        jacEval = 3
-        iMarkerPrev = 0 if iMarker == var.bNodes[1] else iMarker -1
-        dx = var.xx[iMarker] - var.xx[iMarkerPrev]
-
-        dt = self.computeTimeStep(CFL, dx, var.extPar[iMarker*var.nExtPar + 2])
-        IMatrix=np.identity(var.nVar) / dt
+        # Save initial condition in case a point diverges.
+        u0=var.u.copy()                                                 # Initial solution.
+        
+        nVar = var.nVar                                                 # Number of variables of the differential problem.
+        nBlPar = var.nBlPar                                             # Number of boundary layer parameters used for vector indexing.
+        nExtPar = var.nExtPar                                           # Number of boundary layer parameters used for vector indexing.
+        bNodes = var.bNodes                                             # Boundary nodes of the computational domain.
+        iInvVel = var.extPar[iMarker*nExtPar + 2]                       # Inviscid velocity on the considered cell.
+        iMarkerPrev = 0 if iMarker == bNodes[1] else iMarker - 1        # Point upstream of the considered point.
+        dx = var.xx[iMarker] - var.xx[iMarkerPrev]                      # Cell size.
+
+        sysRes = self.solver.buildResiduals(var, iMarker)               # System initial residuals.
+        normSysRes0 = np.linalg.norm(sysRes)                            # Initial norm of the residuals.
+
+        CFL = self.__CFL0                                               # Initialized CFL number.
+        CFLAdapt = 1                                                    # Flag for CFL adaptation.
+        jacEval = 3                                                     # Number of iterations for which Jacobian is frozen.
+        dt = self.computeTimeStep(CFL, dx, iInvVel)                     # Initial time step.
+        IMatrix=np.identity(nVar) / dt                                  # Identity matrix used to construct the linear system.
 
         # Main loop.
-        converged = 0
-        innerIters = 0
+        converged = 0                                                   # Flag to output convergence or divergence of the point.
+        nErrors = 0                                                     # Counter for the number of errors identified at that point.
+        innerIters = 0                                                  # Number of inner iterations to solver the non-linear system.
         while innerIters <= self.__maxIt:
             
-            try:                 
+            try:        
+
+                # Jacobian computation.
+
+                if innerIters % jacEval == 0:
+                    Jacobian=self.solver.buildJacobian(var, iMarker, sysRes, order = 1, eps = 1e-8)
+
+                # Linear solver.
+
+                slnIncr = np.linalg.solve((Jacobian + IMatrix), - sysRes)
+
+                # Increment solution and correct absurd transcient results.
+
+                var.u[iMarker*nVar : (iMarker+1)*nVar] += slnIncr
+                # var.u[iMarker*nVar + 1] = max(var.u[iMarker*nVar + 1], 1.0005)
 
                 # Compute Residuals.
                 sysRes = self.solver.buildResiduals(var, iMarker)
-                normSysRes = np.linalg.norm(sysRes)
+                normSysRes = np.linalg.norm(slnIncr/dt - sysRes)
 
                 if displayState and innerIters % 1000 == 0:
                     self.outputState(var, iMarker, innerIters, normSysRes, normSysRes0, CFL, 'yellow')
@@ -336,39 +363,35 @@ class implicitEuler(timeConfig):
                         self.outputState(var, iMarker, innerIters, normSysRes, normSysRes0, CFL)
                     break
 
-                # Jacobian computation.
-
-                if innerIters % jacEval == 0:
-                    Jacobian=self.solver.buildJacobian(var, iMarker, linSysRes = sysRes, eps = 1e-10)
-
-                # Linear solver.
-
-                slnIncr = np.linalg.solve((IMatrix - Jacobian), sysRes)
-
-                # Increment solution and correct absurd transcient results.
-
-                var.u[iMarker*var.nVar : (iMarker+1)*var.nVar] += slnIncr
-                var.u[iMarker*var.nVar + 1] = max(var.u[iMarker*var.nVar + 1], 1.0005)
-
             except KeyboardInterrupt:
                 quit()
-            except Exception as e:
+            except Exception as error:
+
+                nErrors += 1
+                if nErrors >= 5:
+                    print('|', ccolors.ANSI_RED + 'Too many errors identified at position {:<1.4f}. Marker: {:<4.0f}. {:>17s}'
+                                                                .format(var.xGlobal[iMarker], iMarker,'|'), ccolors.ANSI_RESET)
+                    quit()
                 
-                print('|', ccolors.ANSI_YELLOW + 'Newton solver failed at position {:<.4f}. Marker: {:<.0f} {:>30s}'.format(var.xGlobal[iMarker],                                                                                                             iMarker,'|'), ccolors.ANSI_RESET)
-                print(e)
+                print('|', ccolors.ANSI_YELLOW + 'Newton solver failed at position {:<.4f}. Marker: {:<.0f} {:>25s}'
+                                                    .format(var.xGlobal[iMarker], iMarker,'|'), ccolors.ANSI_RESET)
+
+                print(error)
 
                 #self.resetSolution(iMarker, var, u0)
-                var.u[iMarker*var.nVar : (iMarker+1)*var.nVar] = var.u[(iMarker-1)*var.nVar : (iMarker)*var.nVar]
+                var.u[iMarker*nVar : (iMarker+1)*nVar] = var.u[(iMarker-1)*nVar : (iMarker)*nVar]
 
                 # Lower CFL and recompute time step
                 print('Current CFL', CFL)
                 if math.isnan(CFL):
                     CFL = self.__CFL0
-                CFL = min(max(CFL / 3,0.01),1)
+                CFL = min(max(CFL / 2,0.01),1)
                 print('Lowering CFL: {:<.3f}'.format(CFL))
-                CFLAdapt = 0
-                dt = self.computeTimeStep(CFL, dx, var.extPar[iMarker*var.nExtPar + 2])
-                IMatrix = np.identity(var.nVar) / dt
+                CFLAdapt = 1
+                dt = self.computeTimeStep(CFL, dx, iInvVel)
+                IMatrix = np.identity(nVar) / dt
+
+                sysRes = self.solver.buildResiduals(var, iMarker)
 
                 jacEval = 1
                 displayState = 1
@@ -386,8 +409,8 @@ class implicitEuler(timeConfig):
                 CFL = self.__CFL0* (normSysRes0/normSysRes)**0.7
 
                 CFL = max(CFL, 0.1)
-                dt = self.computeTimeStep(CFL, dx, var.extPar[iMarker*var.nExtPar + 2])
-                IMatrix=np.identity(var.nVar) / dt
+                dt = self.computeTimeStep(CFL, dx, iInvVel)
+                IMatrix=np.identity(nVar) / dt
 
 
             innerIters+=1
@@ -395,29 +418,31 @@ class implicitEuler(timeConfig):
         else:
             # Maximum number of iterations reached.
             if normSysRes >= 1e-3 * normSysRes0:
-                print('|', ccolors.ANSI_RED + 'Too many iteration at position {:<.4f}. normLinSysRes = {:<.3f}. {:>30s}'.format(var.xGlobal[iMarker], 
-                                                                                                                                np.log10(normSysRes/normSysRes0),'|'),
+                print('|', ccolors.ANSI_RED + 'Too many iteration at position {:<.4f}. normLinSysRes = {:<.3f}. {:>30s}'
+                                                                                            .format(var.xGlobal[iMarker], 
+                                                                                                    np.log10(normSysRes/normSysRes0),'|'),
                 ccolors.ANSI_RESET)
-                var.u[iMarker*var.nVar:(iMarker+1)*var.nVar] = u0[(iMarker)*var.nVar: (iMarker+1)*var.nVar].copy()
+                var.u[iMarker*nVar:(iMarker+1)*nVar] = u0[(iMarker)*nVar: (iMarker+1)*nVar].copy()
                 name = 'airfoil' if iMarker <= var.bNodes[2] else 'wake'
 
                 if var.flowRegime[iMarker]==0: 
 
                     # Laminar closure relations
-                    var.blPar[iMarker*var.nBlPar+1:iMarker*var.nBlPar+7]=Closures.laminarClosure(var.u[iMarker*var.nVar + 0], var.u[iMarker*var.nVar + 1],
-                                                                                                  var.blPar[iMarker*var.nBlPar+0], var.extPar[iMarker*var.nExtPar+0],
+                    var.blPar[iMarker*nBlPar+1:iMarker*nBlPar+7] = Closures.laminarClosure(var.u[iMarker*nVar + 0], var.u[iMarker*nVar + 1],
+                                                                                                  var.blPar[iMarker*nBlPar+0], var.extPar[iMarker*nExtPar+0],
                                                                                                   name)
 
                 elif var.flowRegime[iMarker]==1:
 
                     # Turbulent closures relations 
-                    var.blPar[iMarker*var.nBlPar+1:iMarker*var.nBlPar+9]=Closures.turbulentClosure(var.u[iMarker*var.nVar + 0], var.u[iMarker*var.nVar + 1],
-                                                                                                    var.u[iMarker*var.nVar + 4], var.blPar[iMarker*var.nBlPar+0],
-                                                                                                    var.extPar[iMarker*var.nExtPar+0], name)
+                    var.blPar[iMarker*nBlPar+1:iMarker*nBlPar+9] = Closures.turbulentClosure(var.u[iMarker*nVar + 0], var.u[iMarker*nVar + 1],
+                                                                                                    var.u[iMarker*nVar + 4], var.blPar[iMarker*nBlPar+0],
+                                                                                                    var.extPar[iMarker*nExtPar+0], name)
 
             else:
-                print('|', ccolors.ANSI_YELLOW+ 'Too many iteration at position {:<.4f}. normLinSysRes = {:<.3f}. {:>30s}'.format(var.xGlobal[iMarker],
-                                                                                                                                  np.log10(normSysRes/normSysRes0),'|'),
-                ccolors.ANSI_RESET)
+                print('|', ccolors.ANSI_YELLOW+ 'Too many iteration at position {:<.4f}. normLinSysRes = {:<.3f}. {:>30s}'
+                                                                                                .format(var.xGlobal[iMarker],
+                                                                                                np.log10(normSysRes/normSysRes0),'|'),
+                                                                                                ccolors.ANSI_RESET)
 
         return converged
\ No newline at end of file
-- 
GitLab