From 3bf01e74ece0b90b2fabc9c1e4beee0819c73fdc Mon Sep 17 00:00:00 2001 From: Paul Dechamps <paul.dechamps@uliege.be> Date: Tue, 7 May 2024 17:15:24 +0200 Subject: [PATCH] (feat) Added adjoint Adjoint is not working because the delta star box is missing --- blast/_src/blastw.h | 3 +- blast/_src/blastw.i | 10 +- blast/coupler.py | 56 ++- blast/interfaces/DAdjointInterface.py | 102 ++++++ blast/interfaces/DDataStructure.py | 1 - blast/interfaces/DSolversInterface.py | 29 +- blast/interfaces/dart/DartInterface.py | 31 +- blast/src/CMakeLists.txt | 7 +- blast/src/DBoundaryLayer.cpp | 168 ++++++++- blast/src/DBoundaryLayer.h | 6 + blast/src/DCoupledAdjoint.cpp | 460 +++++++++++++++++++++++++ blast/src/DCoupledAdjoint.h | 128 +++++++ blast/src/DDriver.cpp | 25 +- blast/src/DDriver.h | 3 + blast/src/blast.h | 3 + blast/tests/dart/adjointVII_2D.py | 174 ++++++++++ blast/tests/dart/naca0012_2D.py | 22 +- 17 files changed, 1168 insertions(+), 60 deletions(-) create mode 100644 blast/interfaces/DAdjointInterface.py create mode 100644 blast/src/DCoupledAdjoint.cpp create mode 100644 blast/src/DCoupledAdjoint.h create mode 100644 blast/tests/dart/adjointVII_2D.py diff --git a/blast/_src/blastw.h b/blast/_src/blastw.h index f0b51a2..dd883e5 100644 --- a/blast/_src/blastw.h +++ b/blast/_src/blastw.h @@ -15,4 +15,5 @@ */ #include "DDriver.h" -#include "DBoundaryLayer.h" \ No newline at end of file +#include "DBoundaryLayer.h" +#include "DCoupledAdjoint.h" \ No newline at end of file diff --git a/blast/_src/blastw.i b/blast/_src/blastw.i index a28e208..3528a3b 100644 --- a/blast/_src/blastw.i +++ b/blast/_src/blastw.i @@ -43,11 +43,10 @@ threads="1" // ----------- blast CLASSES ---------------- %include "blast.h" -%shared_ptr(blast::Driver); %shared_ptr(blast::BoundaryLayer); %include "DBoundaryLayer.h" - +%shared_ptr(blast::Driver); %immutable blast::Driver::Re; // read-only variable (no setter) %immutable blast::Driver::Cdt; %immutable blast::Driver::Cdt_sec; @@ -57,4 +56,9 @@ threads="1" %immutable blast::Driver::Cdp_sec; %immutable blast::Driver::CFL0; %immutable blast::Driver::verbose; -%include "DDriver.h" \ No newline at end of file +%include "DDriver.h" + +%shared_ptr(blast::CoupledAdjoint); +%immutable blast::CoupledAdjoint::tdCl_AoA; +%immutable blast::CoupledAdjoint::tdCd_AoA; +%include "DCoupledAdjoint.h" diff --git a/blast/coupler.py b/blast/coupler.py index 91ecd93..b7213a4 100644 --- a/blast/coupler.py +++ b/blast/coupler.py @@ -33,8 +33,6 @@ class Coupler: self.iterPrint = _iterPrint if self.isol.getVerbose() == 0 and self.vsol.verbose == 0 else 1 self.tms = fwk.Timers() self.filesfx = sfx - - def run(self): print('') print(' ######################################################## ') print('| ___ __ ___ _________________ |') @@ -54,13 +52,14 @@ class Coupler: print('Interpolator:', self.isol.cfg['interpolator']) print('') print('-- Coupler parameters --') - print('MaxIter:{:>3.0f}'.format(self.maxIter)) - print('Tolerance:{:>6.2f}'.format(np.log10(self.tol))) - print('IterPrint:{:>2.0f}'.format(self.iterPrint)) - print('ResetInv:{:>5s}'.format(str(self.resetInviscid))) - print('Verb:{:>2.0f}'.format(self.isol.getVerbose())) + print('MaxIter: {:>3.0f}'.format(self.maxIter)) + print('Tolerance: {:>6.2f}'.format(np.log10(self.tol))) + print('IterPrint: {:>2.0f}'.format(self.iterPrint)) + print('ResetInv: {:>5s}'.format(str(self.resetInviscid))) + print('Verb: {:>2.0f}'.format(self.isol.getVerbose())) print('') + def run(self, write=True): # Aerodynamic coefficients. aeroCoeffs = {'Cl':[], 'Cd': [], 'Cdwake': []} @@ -72,6 +71,7 @@ class Coupler: # Impose blowing boundary condition in the inviscid solver. self.tms['processing'].start() self.isol.getBlowingBoundary(couplIter) + self.isol.interpolate('v2i') self.isol.setBlowingVelocity() self.tms['processing'].stop() @@ -85,11 +85,13 @@ class Coupler: # Write inviscid Cp distribution. if couplIter == 0: self.isol.initializeViscousSolver() - self.isol.writeCp(sfx='_inviscid'+self.filesfx) + if write: + self.isol.writeCp(sfx='_inviscid'+self.filesfx) # Impose inviscid boundary in the viscous solver. self.tms['processing'].start() self.isol.getInviscidBC() + self.isol.interpolate('i2v') self.isol.setViscousSolver() self.tms['processing'].stop() @@ -109,7 +111,8 @@ class Coupler: if error <= self.tol: print(ccolors.ANSI_GREEN, '{:>4.0f}| {:>7.5f} {:>7.5f} {:>7.5f} | {:>6.4f} {:>6.4f} | {:>6.3f}\n'.format(couplIter, self.isol.getCl(), self.isol.getCd()+self.vsol.Cdf, self.vsol.Cdt, self.vsol.getAverageTransition(0), self.vsol.getAverageTransition(1), np.log10(error)), ccolors.ANSI_RESET) - self.isol.writeCp(sfx='_viscous'+self.filesfx) + if write: + self.isol.writeCp(sfx='_viscous'+self.filesfx) return aeroCoeffs cdPrev = cd @@ -128,5 +131,38 @@ class Coupler: print('{:>5s}| {:>7s} {:>7s} {:>7s} | {:>6s} {:>8s} | {:>6s}'.format('It', 'Cl', 'Cd', 'Cdwake', 'xtrT', 'xtrB', 'Error')) print(' ----------------------------------------------------------') print(ccolors.ANSI_RED, '{:>4.0f}| {:>7.5f} {:>7.5f} {:>7.5f} | {:>6.4f} {:>7.4f} | {:>6.3f}\n'.format(couplIter-1, self.isol.getCl(), self.isol.getCd()+self.vsol.Cdf, self.vsol.Cdt, self.vsol.getAverageTransition(0), self.vsol.getAverageTransition(1), np.log10(error)), ccolors.ANSI_RESET) - self.isol.writeCp(sfx='_viscous'+self.filesfx) + if write: + self.isol.writeCp(sfx='_viscous'+self.filesfx) return aeroCoeffs + + def reset(self): + """Reset the entire VII coupling; inviscid solver, transfer quantities + and blowing velocity. Viscous section objects are not deleted + """ + # Inviscid solver + + self.isol.reset() + + for iSec in range(self.isol.cfg['nSections']): + for i, reg in enumerate(self.isol.sec[iSec]): + if reg.name == 'wake': + iReg = 1 + elif reg.name == 'lower' or reg.name == 'upper': + iReg = 0 + else: + raise RuntimeError('Invalid region', reg.name) + loc = np.zeros(reg.getnNodes()) + for iPoint in range(reg.getnNodes()): + loc[iPoint] = reg.loc[iPoint] + self.isol.xxExt[iSec][i] = loc + self.isol.deltaStarExt[iSec][i] = np.zeros(reg.getnNodes()) + + # Blowing velocity + for b in self.isol.iBnd: + for i in range(len(b.blowingVel)): + b.blowingVel[i] = 0. + for sec in self.isol.vBnd: + for side in sec: + for i in range(len(side.blowingVel)): + side.blowingVel[i] = 0. + self.isol.setBlowingVelocity() diff --git a/blast/interfaces/DAdjointInterface.py b/blast/interfaces/DAdjointInterface.py new file mode 100644 index 0000000..cf3f60f --- /dev/null +++ b/blast/interfaces/DAdjointInterface.py @@ -0,0 +1,102 @@ +import numpy as np +from matplotlib import pyplot as plt + +class AdjointInterface(): + def __init__(self, _coupledAdjointSolver, _iSolverAPI, _vSolver): + self.coupledAdjointSolver = _coupledAdjointSolver + self.isol = _iSolverAPI + self.vsol = _vSolver + + def build(self): + nDim = self.isol.solver.pbl.nDim + nNd = self.isol.solver.pbl.msh.nodes.size() + nEs = len(self.isol.iBnd[0].elemsCoord[:,0]) + nNs = len(self.isol.blw[0].nodes) + dRblw_M = np.zeros((nEs, nNs)) + dRblw_rho = np.zeros((nEs, nNs)) + dRblw_v = np.zeros((nEs, nDim*nNs)) + dRblw_x = np.zeros((nEs, nDim*nNd)) + + delta = 1e-6 + + saveBlw = np.zeros(nEs) + for i in range(nEs): + saveBlw[i] = self.isol.blw[0].getU(i) + + # Mach + print('Mach', end='...') + for i, n in enumerate(self.isol.blw[0].nodes): + savemach = self.isol.solver.M[n.row] + self.isol.solver.M[n.row] = savemach + delta + blowing1 = self.__runViscous() + + self.isol.solver.M[n.row] = savemach - delta + blowing2 = self.__runViscous() + self.isol.solver.M[n.row] = savemach + dRblw_M[:,i] = -(blowing1 - blowing2)/(2*delta) + print('done') + + # Rho + print('Density', end='...') + for i, n in enumerate(self.isol.blw[0].nodes): + saverho = self.isol.solver.rho[n.row] + self.isol.solver.rho[n.row] = saverho + delta + blowing1 = self.__runViscous() + + self.isol.solver.rho[n.row] = saverho - delta + blowing2 = self.__runViscous() + self.isol.solver.rho[n.row] = saverho + dRblw_rho[:,i] = -(blowing1 - blowing2)/(2*delta) + print('done') + + # # V + print('Velocity', end='...') + for i, n in enumerate(self.isol.blw[0].nodes): + for jj in range(nDim): + savev = self.isol.solver.U[n.row][jj] + self.isol.solver.U[n.row][jj] = savev + delta + blowing1 = self.__runViscous() + + self.isol.solver.U[n.row][jj] = savev - delta + blowing2 = self.__runViscous() + self.isol.solver.U[n.row][jj] = savev + dRblw_v[:,i*nDim + jj] = -(blowing1 - blowing2)/(2*delta) + print('done') + + """# # Mesh coordinates + print('Mesh') + for i, n in enumerate(self.iSolverAPI.blw[0].nodes): + vidx = np.where(self.iSolverAPI.iBnd[0].nodesCoord[:,3] == n.row)[0][0] + for jj in range(nDim): + if self.iSolverAPI.iBnd[0].nodesCoord[vidx,jj] != n.pos[jj]: + print('WRONG NODE MESH') + quit() + savemesh = n.pos[jj] + self.iSolverAPI.iBnd[0].nodesCoord[vidx, jj] = savemesh + delta + blowing1 = self.__runViscous() + + self.iSolverAPI.iBnd[0].nodesCoord[vidx, jj] = savemesh - delta + blowing2 = self.__runViscous() + self.iSolverAPI.iBnd[0].nodesCoord[vidx, jj] = savemesh + dRblw_x[:, n.row*nDim+jj] = -(blowing1 - blowing2)/(2*delta)""" + + self.coupledAdjointSolver.setdRblw_M(dRblw_M) + self.coupledAdjointSolver.setdRblw_rho(dRblw_rho) + self.coupledAdjointSolver.setdRblw_v(dRblw_v) + self.coupledAdjointSolver.setdRblw_x(dRblw_x) + + def __runViscous(self): + self.isol.getInviscidBC() + self.isol.interpolate('i2v') + self.isol.setViscousSolver(adjoint=True) + ext = self.vsol.run() + if ext != 0: + raise ValueError('Viscous solver did not converge') + self.isol.getBlowingBoundary(1, adjoint=True) + self.isol.interpolate('v2i') + self.isol.setBlowingVelocity() + + blowing = np.zeros(len(self.isol.iBnd[0].elemsCoord[:,0])) + for i in range(len(self.isol.iBnd[0].elemsCoord[:,0])): + blowing[i] = self.isol.blw[0].getU(i) + return blowing diff --git a/blast/interfaces/DDataStructure.py b/blast/interfaces/DDataStructure.py index 8fa91c0..4a63dc6 100644 --- a/blast/interfaces/DDataStructure.py +++ b/blast/interfaces/DDataStructure.py @@ -25,7 +25,6 @@ class Group: self.nPoints = nPoints self.nElems = nPoints - 1 self.nodesCoord = np.zeros((self.nPoints,4)) - self.elemsCoord = np.zeros((self.nPoints,4)) self.V = np.zeros((self.nPoints,3)) self.M = np.zeros(self.nPoints) diff --git a/blast/interfaces/DSolversInterface.py b/blast/interfaces/DSolversInterface.py index c1f9ed1..dc85a47 100644 --- a/blast/interfaces/DSolversInterface.py +++ b/blast/interfaces/DSolversInterface.py @@ -25,6 +25,7 @@ class SolversInterface: for iReg, reg in enumerate(self.vBnd[iSec]): reg.initStructures(self.iBnd[iReg].nPoints) self.vBnd[iSec][iReg].nodesCoord = self.iBnd[iReg].nodesCoord + self.vBnd[iSec][iReg].elemsCoord = self.iBnd[iReg].elemsCoord elif self.cfg['interpolator'] == 'Rbf': from blast.interfaces.interpolators.DRbfInterpolator import RbfInterpolator as interp @@ -44,12 +45,11 @@ class SolversInterface: for iSec in range(self.cfg['nSections'])] self.vtExt = [[np.zeros(0) for iReg in range(3)]\ for iSec in range(self.cfg['nSections'])] + self.vinit = False def setViscousSolver(self, adjoint=False): """Interpolate solution to viscous mesh and sets the inviscid boundary in the viscous solver. """ - self.interpolator.inviscidToViscous(self.iBnd, self.vBnd) - for iSec in range(self.cfg['nSections']): for i, reg in enumerate(self.sec[iSec]): if reg.name == 'wake': @@ -67,8 +67,8 @@ class SolversInterface: reg.setDeltaStarExt(self.deltaStarExt[iSec][i]) reg.setxxExt(self.xxExt[iSec][i]) - def getBlowingBoundary(self, couplIter): - """ Get blowing boundary condition from the viscous solver. + def getBlowingBoundary(self, couplIter, adjoint=False): + """Get blowing boundary condition from the viscous solver. args: ---- couplIter: int @@ -82,15 +82,26 @@ class SolversInterface: elif 'vAirfoil' in reg.name: reg.blowingVel = np.concatenate((np.flip(np.asarray(self.sec[iSec][0].blowingVelocity)), np.asarray(self.sec[iSec][1].blowingVelocity))) - for i in range(len(self.sec[iSec])): - self.xxExt[iSec][i] = np.asarray(self.sec[iSec][i].loc) - self.deltaStarExt[iSec][i] = np.asarray(self.sec[iSec][i].deltaStar) - self.interpolator.viscousToInviscid(self.iBnd, self.vBnd) + if adjoint == False: + for i in range(len(self.sec[iSec])): + self.xxExt[iSec][i] = np.asarray(self.sec[iSec][i].loc) + self.deltaStarExt[iSec][i] = np.asarray(self.sec[iSec][i].deltaStar) + + def interpolate(self, dir): + if dir == 'i2v': + self.interpolator.inviscidToViscous(self.iBnd, self.vBnd) + elif dir == 'v2i': + self.interpolator.viscousToInviscid(self.iBnd, self.vBnd) + else: + raise RuntimeError('Invalid direction', dir) def initializeViscousSolver(self): """Initialize the viscous sections in the viscous solver. """ + if self.vinit == True: + return None + self.sec = [[] for _ in range(self.cfg['nSections'])] self.vSolver.reset() for i in range(self.cfg['nSections']): @@ -161,6 +172,7 @@ class SolversInterface: loc[iPoint] = reg.loc[iPoint] self.xxExt[iSec][i] = loc self.deltaStarExt[iSec][i] = np.zeros(reg.getnNodes()) + self.vinit = True def getVWing(self): """Obtain the nodes' location and row and cg of all elements. @@ -169,7 +181,6 @@ class SolversInterface: If not, the nodes are pseudo-duplicated (without changing the row). They therefore exist only for the interpolation but not for the solution. """ - data = [np.zeros((0,4)) for _ in range(2)] lwRows = [] for e in self.cfg['vMsh'].elems: diff --git a/blast/interfaces/dart/DartInterface.py b/blast/interfaces/dart/DartInterface.py index 9c6f040..c505ec0 100644 --- a/blast/interfaces/dart/DartInterface.py +++ b/blast/interfaces/dart/DartInterface.py @@ -28,10 +28,14 @@ class DartInterface(SolversInterface): def __init__(self, dartCfg, vSolver, _cfg): try: from modules.dartflo.dart.api.core import initDart - self.inviscidObjects = initDart(dartCfg, viscous=True) + self.inviscidObjects = initDart(dartCfg, task=dartCfg['task'], viscous=True) self.solver = self.inviscidObjects.get('sol') # Solver self.blw = [self.inviscidObjects.get('blwb'), self.inviscidObjects.get('blww')] - print('Dart successfully loaded.') + if dartCfg['task'] == 'optimization': + self.adjointSolver = self.inviscidObjects.get('adj') + print('Dart successfully loaded for optimization.') + else: + print('Dart successfully loaded for analysis.') except: print(ccolors.ANSI_RED, 'Could not load DART. Make sure it is installed.', ccolors.ANSI_RESET) raise RuntimeError('Import error') @@ -95,6 +99,9 @@ class DartInterface(SolversInterface): def getAoA(self): return self.solver.pbl.alpha + def setAoA(self, aoa): + self.solver.pbl.update(aoa) + def getMinf(self): return self.solver.pbl.M_inf @@ -113,6 +120,9 @@ class DartInterface(SolversInterface): def getnDim(self): return self.inviscidObjects['pbl'].nDim + def getnNodesDomain(self): + return self.solver.pbl.msh.nodes.size() + def reset(self): self.solver.reset() @@ -137,7 +147,6 @@ class DartInterface(SolversInterface): """ Extract the solution of the viscous calculation (blowing velocity) and use it as a boundary condition in the inviscid solver. """ - # Impose blowing velocities. for n in range(len(self.iBnd)): if self.getnDim() == 2: @@ -260,6 +269,14 @@ class DartInterface(SolversInterface): self.iBnd[0].nodesCoord = np.column_stack((data[:,1], data[:,2],\ data[:,3], data[:,4])) self.iBnd[0].setConnectList(connectListNodes, connectListElems) + elemCoord = np.zeros((len(self.iBnd[0].nodesCoord)-1, 4)) + for i in range(len(elemCoord[:,0])): + elemCoord[i,0] = 0.5 * (self.iBnd[0].nodesCoord[i,0] + self.iBnd[0].nodesCoord[i+1,0]) + elemCoord[i,1] = 0.5 * (self.iBnd[0].nodesCoord[i,1] + self.iBnd[0].nodesCoord[i+1,1]) + elemCoord[i,2] = 0.5 * (self.iBnd[0].nodesCoord[i,2] + self.iBnd[0].nodesCoord[i+1,2]) + elemCoord[i,3] = i + self.iBnd[0].elemsCoord = elemCoord + # Wake self.iBnd[1].initStructures(self.blw[1].nodes.size()) @@ -299,6 +316,14 @@ class DartInterface(SolversInterface): dataW[:,3], dataW[:,4])) self.iBnd[1].setConnectList(connectListNodes, connectListElems) + elemCoordW = np.zeros((len(self.iBnd[1].nodesCoord)-1, 4)) + for i in range(len(elemCoordW[:,0])): + elemCoordW[i,0] = 0.5 * (self.iBnd[1].nodesCoord[i,0] + self.iBnd[1].nodesCoord[i+1,0]) + elemCoordW[i,1] = 0.5 * (self.iBnd[1].nodesCoord[i,1] + self.iBnd[1].nodesCoord[i+1,1]) + elemCoordW[i,2] = 0.5 * (self.iBnd[1].nodesCoord[i,2] + self.iBnd[1].nodesCoord[i+1,2]) + elemCoordW[i,3] = i + self.iBnd[1].elemsCoord = elemCoordW + def getWing(self): """Obtain the nodes' location and row and cg of all elements. """ diff --git a/blast/src/CMakeLists.txt b/blast/src/CMakeLists.txt index 96caacb..cd3f09b 100644 --- a/blast/src/CMakeLists.txt +++ b/blast/src/CMakeLists.txt @@ -18,8 +18,11 @@ FILE(GLOB SRCS *.h *.cpp *.inl *.hpp) ADD_LIBRARY(blast SHARED ${SRCS}) MACRO_DebugPostfix(blast) -TARGET_INCLUDE_DIRECTORIES(blast PUBLIC ${PROJECT_SOURCE_DIR}/blast/src) - +TARGET_INCLUDE_DIRECTORIES(blast PUBLIC ${PROJECT_SOURCE_DIR}/blast/src + ${PROJECT_SOURCE_DIR}/modules/dartflo/ext/amfe/tbox/src + ${PROJECT_SOURCE_DIR}/modules/dartflo/ext/amfe/fwk/src + ${PROJECT_SOURCE_DIR}/modules/dartflo/dart/src + ${PYTHON_INCLUDE_PATH}) TARGET_LINK_LIBRARIES(blast dart fwk tbox) INSTALL(TARGETS blast DESTINATION ${CMAKE_INSTALL_PREFIX}) diff --git a/blast/src/DBoundaryLayer.cpp b/blast/src/DBoundaryLayer.cpp index 86fc294..ad18c99 100644 --- a/blast/src/DBoundaryLayer.cpp +++ b/blast/src/DBoundaryLayer.cpp @@ -173,27 +173,169 @@ void BoundaryLayer::setWakeBC(double Re, std::vector<double> UpperCond, std::vec */ void BoundaryLayer::computeBlowingVelocity() { - deltaStar[0] = u[0] * u[1]; + //deltaStar[0] = u[0] * u[1]; blowingVelocity.resize(nNodes - 1, 0.); - for (size_t iPoint = 1; iPoint < nNodes; ++iPoint) + for (size_t i = 1; i < nNodes; ++i) { - deltaStar[iPoint] = u[iPoint * nVar + 0] * u[iPoint * nVar + 1]; - blowingVelocity[iPoint - 1] = (rhoe[iPoint] * vt[iPoint] * deltaStar[iPoint] - rhoe[iPoint-1] * vt[iPoint-1] * deltaStar[iPoint - 1]) / (rhoe[iPoint] * (loc[iPoint] - loc[iPoint-1])); - if (std::isnan(blowingVelocity[iPoint - 1])) + //deltaStar[i] = u[i * nVar + 0] * u[i * nVar + 1]; + blowingVelocity[i - 1] = (rhoe[i] * vt[i] * deltaStar[i] - rhoe[i-1] * vt[i-1] * deltaStar[i - 1]) / (rhoe[i] * (loc[i] - loc[i-1])); + if (std::isnan(blowingVelocity[i - 1])) { - if (rhoe[iPoint] == 0.) - std::cout << "Density is zero at point " << iPoint << std::endl; - if ((loc[iPoint] - loc[iPoint-1]) == 0.) - std::cout << "Points " << iPoint -1 << " and " << iPoint << " are at the same position " << loc[iPoint] << ", resulting in an infinite gradient." << std::endl; - if (std::isnan(deltaStar[iPoint])) - std::cout << "Displacement thickness is nan at position " << iPoint << std::endl; - if (std::isnan(deltaStar[iPoint - 1])) - std::cout << "Displacement thickness is nan at position " << iPoint - 1 << std::endl; + if (rhoe[i] == 0.) + std::cout << "Density is zero at point " << i << std::endl; + if ((loc[i] - loc[i-1]) == 0.) + std::cout << "Points " << i -1 << " and " << i << " are at the same position " << loc[i] << ", resulting in an infinite gradient." << std::endl; + if (std::isnan(deltaStar[i])) + std::cout << "Displacement thickness is nan at position " << i << std::endl; + if (std::isnan(deltaStar[i - 1])) + std::cout << "Displacement thickness is nan at position " << i - 1 << std::endl; throw std::runtime_error("NaN detected in blowing velocity computation (see reason above)"); } } } +std::vector<std::vector<double>> BoundaryLayer::evalGradwrtDeltaStar() +{ + std::vector<std::vector<double>> gradVe = std::vector<std::vector<double>>(nElms); + for (auto &vec : gradVe) + vec.resize(nNodes, 0.); + + for (size_t i = 1; i < nNodes; ++i) + { + gradVe[i-1][i] = vt[i] / (loc[i] - loc[i-1]); + gradVe[i-1][i-1] = -1/rhoe[i] * rhoe[i-1] * vt[i-1] / (loc[i] - loc[i-1]); + } + return gradVe; +} + +std::vector<std::vector<double>> BoundaryLayer::evalGradwrtVt() +{ + std::vector<std::vector<double>> gradVe = std::vector<std::vector<double>>(nElms); + for (auto &vec : gradVe) + vec.resize(nNodes, 0.); + + for (size_t i = 1; i < nNodes; ++i) + { + gradVe[i-1][i] = deltaStar[i] / (loc[i] - loc[i-1]); + gradVe[i-1][i - 1] = - rhoe[i-1] / rhoe[i] * deltaStar[i-1] / (loc[i] - loc[i-1]); + } + return gradVe; +} + +std::vector<std::vector<double>> BoundaryLayer::evalGradwrtRho() +{ + std::vector<std::vector<double>> gradVe = std::vector<std::vector<double>>(nElms); + for (auto &vec : gradVe) + vec.resize(nNodes, 0.); + + for (size_t i = 1; i < nNodes; ++i) + { + gradVe[i-1][i] = -1/(rhoe[i]*rhoe[i]) * ((rhoe[i] * vt[i] * deltaStar[i] - rhoe[i-1] * vt[i-1] * deltaStar[i-1])/(loc[i] - loc[i-1])) + + 1/rhoe[i] * vt[i] * deltaStar[i] / (loc[i] - loc[i-1]); + gradVe[i-1][i-1] = -vt[i-1] * deltaStar[i-1] / (rhoe[i] * (loc[i] - loc[i-1])); + } + return gradVe; +} + +/** + * @brief Compute the gradient of the velocity with respect to the x coordinate. + * + * dVe_dx = dVe_dloc * dloc_dx + * + * @return std::vector<double> Gradient of the velocity with respect to the x coordinate. + */ +std::vector<std::vector<double>> BoundaryLayer::evalGradwrtX() +{ + std::vector<std::vector<double>> gradVe = std::vector<std::vector<double>>(nElms); + for (auto &vec : gradVe) + vec.resize(nNodes, 0.); + + // Compute dVe/dloc + std::vector<std::vector<double>> gradVe_loc = std::vector<std::vector<double>>(nElms); + for (auto &vec : gradVe_loc) + vec.resize(nNodes, 0.); + + for (size_t i = 1; i < nNodes; ++i) + { + double val = 1/rhoe[i] * (rhoe[i] * vt[i] * deltaStar[i] - rhoe[i-1] * vt[i-1] * deltaStar[i-1]) / ((loc[i] - loc[i-1]) * (loc[i] - loc[i-1])); + gradVe_loc[i-1][i] = -val; + gradVe_loc[i-1][i-1] = val; + } + + // Compute dloc/dx + // Derivative wrt x_j = (x_j - x_j-1) / sqrt((x_j - x_j-1)^2 + (y_j - y_j-1)^2) + // Derivative wrt x_j-1 = - (x_j - x_j-1) / sqrt((x_j - x_j-1)^2 + (y_j - y_j-1)^2) = - dloc/dx_j + // For loc_i, there are two contributions of x_j except if j = i. + // loc_n = sqrt((xn-xn-1)^2) + sqrt((xn-1-xn-2)^2) + ... + sqrt((x1-x0)^2) + std::vector<std::vector<double>> gradloc_x = std::vector<std::vector<double>>(nNodes); + for (auto &vec : gradloc_x) + vec.resize(nNodes, 0.); + + for (size_t j = 1; j < nNodes; ++j) + { + gradloc_x[j][j] = (x[j] - x[j-1]) / std::sqrt((x[j] - x[j-1]) * (x[j] - x[j-1]) + (y[j] - y[j-1]) * (y[j] - y[j-1])); + for (size_t i = 0; i < nNodes - j; ++i) + gradloc_x[j+i][j-1] = gradloc_x[j-1][j-1] - gradloc_x[j][j]; + } + + // Matrix product of gradVe_loc and gradloc_x + for (size_t i = 0; i < nElms; ++i) + { + for (size_t j = 0; j < nNodes; ++j) + { + for (size_t k = 0; k < nNodes; ++k) + gradVe[i][j] += gradVe_loc[i][k] * gradloc_x[k][j]; + } + } + return gradVe; +} + +std::vector<std::vector<double>> BoundaryLayer::evalGradwrtY() +{ + std::vector<std::vector<double>> gradVe = std::vector<std::vector<double>>(nElms); + for (auto &vec : gradVe) + vec.resize(nNodes, 0.); + + // Compute dVe/dloc + std::vector<std::vector<double>> gradVe_loc = std::vector<std::vector<double>>(nElms); + for (auto &vec : gradVe_loc) + vec.resize(nNodes, 0.); + + for (size_t i = 1; i < nNodes; ++i) + { + double val = 1/rhoe[i] * (rhoe[i] * vt[i] * deltaStar[i] - rhoe[i-1] * vt[i-1] * deltaStar[i-1]) / ((loc[i] - loc[i-1]) * (loc[i] - loc[i-1])); + gradVe_loc[i-1][i] = -val; + gradVe_loc[i-1][i-1] = val; + } + + // Compute dloc/dy + // Derivative wrt y_j = (y_j - y_j-1) / sqrt((y_j - y_j-1)^2 + (y_j - y_j-1)^2) + // Derivative wrt y_j-1 = - (y_j - y_j-1) / sqrt((y_j - y_j-1)^2 + (y_j - y_j-1)^2) = - dloc/dy_j + // For loc_i, there are two contributions of y_j except if j = i. + // loc_n = sqrt((yn-yn-1)^2) + sqrt((yn-1-yn-2)^2) + ... + sqrt((y1-y0)^2) + std::vector<std::vector<double>> gradloc_y = std::vector<std::vector<double>>(nNodes); + for (auto &vec : gradloc_y) + vec.resize(nNodes, 0.); + + for (size_t j = 1; j < nNodes; ++j) + { + gradloc_y[j][j] = (y[j] - y[j-1]) / std::sqrt((x[j] - x[j-1]) * (x[j] - x[j-1]) + (y[j] - y[j-1]) * (y[j] - y[j-1])); + for (size_t i = 0; i < nNodes - j; ++i) + gradloc_y[j+i][j-1] = gradloc_y[j-1][j-1] - gradloc_y[j][j]; + } + + // Matrix product of gradVe_loc and gradloc_x + for (size_t i = 0; i < nElms; ++i) + { + for (size_t j = 0; j < nNodes; ++j) + { + for (size_t k = 0; k < nNodes; ++k) + gradVe[i][j] += gradVe_loc[i][k] * gradloc_y[k][j]; + } + } + return gradVe; +} + /** * @brief Print solution at a given station. * diff --git a/blast/src/DBoundaryLayer.h b/blast/src/DBoundaryLayer.h index ec04200..030bb1d 100644 --- a/blast/src/DBoundaryLayer.h +++ b/blast/src/DBoundaryLayer.h @@ -88,6 +88,7 @@ public: void setxxExt(std::vector<double> const _xxExt) {if (_xxExt.size() != nNodes) throw std::runtime_error("External mesh vector is not consistent."); xxExt = _xxExt;}; void setDeltaStarExt(std::vector<double> const _deltaStarExt) {if (_deltaStarExt.size() != nNodes) throw std::runtime_error("Displacement thickness vector is not consistent."); deltaStarExt = _deltaStarExt;}; void setVtExt(std::vector<double> const _vtExt) {if (_vtExt.size() != nNodes) throw std::runtime_error("Velocity vector is not consistent."); vtExt = _vtExt;}; + void setxtrF(double const _xtrF) {xtrF = _xtrF;}; // Boundary conditions. void setStagnationBC(double Re); @@ -101,6 +102,11 @@ public: // Others void printSolution(size_t iPoint) const; void computeBlowingVelocity(); + std::vector<std::vector<double>> evalGradwrtRho(); + std::vector<std::vector<double>> evalGradwrtVt(); + std::vector<std::vector<double>> evalGradwrtDeltaStar(); + std::vector<std::vector<double>> evalGradwrtX(); + std::vector<std::vector<double>> evalGradwrtY(); }; } // namespace blast #endif // DBOUNDARYLAYER_H diff --git a/blast/src/DCoupledAdjoint.cpp b/blast/src/DCoupledAdjoint.cpp new file mode 100644 index 0000000..8139d32 --- /dev/null +++ b/blast/src/DCoupledAdjoint.cpp @@ -0,0 +1,460 @@ +/* + * Copyright 2020 University of Liège + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DCoupledAdjoint.h" +#include "wAdjoint.h" +#include "wNewton.h" +#include "wSolver.h" +#include "wProblem.h" +#include "wBlowing.h" +#include "wFluid.h" +#include "wMshData.h" +#include "wNode.h" + +#include "wLinearSolver.h" +#include "wGmres.h" + +#include <Eigen/Sparse> +#include<Eigen/SparseLU> +#include <Eigen/Dense> +#include<Eigen/SparseCholesky> +#include <fstream> +#include <deque> +#include <algorithm> +#include <iomanip> + +using namespace blast; + +CoupledAdjoint::CoupledAdjoint(std::shared_ptr<dart::Adjoint> _iadjoint, std::shared_ptr<blast::Driver> vSolver) : iadjoint(_iadjoint), vsol(vSolver) +{ + isol = iadjoint->sol; + // Linear solvers (if GMRES, use the same, but with a thighter tolerance) + if (isol->linsol->type() == tbox::LSolType::GMRES_ILUT) + { + std::shared_ptr<tbox::Gmres> gmres = std::make_shared<tbox::Gmres>(*dynamic_cast<tbox::Gmres *>(isol->linsol.get())); + gmres->setTolerance(1e-8); + alinsol = gmres; + } + else + alinsol = isol->linsol; + + // Initalize all derivatives + this->reset(); +} + +void CoupledAdjoint::reset() +{ + size_t nDim = isol->pbl->nDim; // Number of dimensions of the problem + size_t nNs = isol->pbl->bBCs[0]->nodes.size(); // Number of surface nodes + size_t nNd = isol->pbl->msh->nodes.size(); // Number of domain nodes + size_t nEs = isol->pbl->bBCs[0]->uE.size(); // Number of elements on the surface + + dRphi_phi = Eigen::SparseMatrix<double, Eigen::RowMajor>(nNd, nNd); + dRM_phi = Eigen::SparseMatrix<double, Eigen::RowMajor>(nNs, nNd); + dRrho_phi = Eigen::SparseMatrix<double, Eigen::RowMajor>(nNs, nNd); + dRv_phi = Eigen::SparseMatrix<double, Eigen::RowMajor>(nDim*nNs, nNd); + dRblw_phi = Eigen::SparseMatrix<double, Eigen::RowMajor>(nEs, nNd); + + dRphi_M = Eigen::SparseMatrix<double, Eigen::RowMajor>(nNd, nNs); + dRM_M = Eigen::SparseMatrix<double, Eigen::RowMajor>(nNs, nNs); + dRrho_M = Eigen::SparseMatrix<double, Eigen::RowMajor>(nNs, nNs); + dRv_M = Eigen::SparseMatrix<double, Eigen::RowMajor>(nDim*nNs, nNs); + dRblw_M = Eigen::SparseMatrix<double, Eigen::RowMajor>(nEs, nNs); + + dRphi_rho = Eigen::SparseMatrix<double, Eigen::RowMajor>(nNd, nNs); + dRM_rho = Eigen::SparseMatrix<double, Eigen::RowMajor>(nNs, nNs); + dRrho_rho = Eigen::SparseMatrix<double, Eigen::RowMajor>(nNs, nNs); + dRv_rho = Eigen::SparseMatrix<double, Eigen::RowMajor>(nDim*nNs, nNs); + dRblw_rho = Eigen::SparseMatrix<double, Eigen::RowMajor>(nEs, nNs); + + dRphi_v = Eigen::SparseMatrix<double, Eigen::RowMajor>(nNd, nDim*nNs); + dRM_v = Eigen::SparseMatrix<double, Eigen::RowMajor>(nNs, nDim*nNs); + dRrho_v = Eigen::SparseMatrix<double, Eigen::RowMajor>(nNs, nDim*nNs); + dRv_v = Eigen::SparseMatrix<double, Eigen::RowMajor>(nDim*nNs, nDim*nNs); + dRblw_v = Eigen::SparseMatrix<double, Eigen::RowMajor>(nEs, nDim*nNs); + + dRphi_blw = Eigen::SparseMatrix<double, Eigen::RowMajor>(nNd, nEs); + dRM_blw = Eigen::SparseMatrix<double, Eigen::RowMajor>(nNs, nEs); + dRrho_blw = Eigen::SparseMatrix<double, Eigen::RowMajor>(nNs, nEs); + dRv_blw = Eigen::SparseMatrix<double, Eigen::RowMajor>(nDim*nNs, nEs); + dRblw_blw = Eigen::SparseMatrix<double, Eigen::RowMajor>(nEs, nEs); + + dRphi_x = Eigen::SparseMatrix<double, Eigen::RowMajor>(nNd, nDim*nNd); + dRM_x = Eigen::SparseMatrix<double, Eigen::RowMajor>(nNs, nDim*nNd); + dRrho_x = Eigen::SparseMatrix<double, Eigen::RowMajor>(nNs, nDim*nNd); + dRv_x = Eigen::SparseMatrix<double, Eigen::RowMajor>(nDim*nNs, nDim*nNd); + dRblw_x = Eigen::SparseMatrix<double, Eigen::RowMajor>(nEs, nDim*nNd); + dRM_M.setIdentity(); + dRrho_rho.setIdentity(); + dRv_v.setIdentity(); + dRblw_blw.setIdentity(); + + // Objective functions + dCl_phi = Eigen::VectorXd::Zero(nNd); + dCd_phi = Eigen::VectorXd::Zero(nNd); + + // Angle of attack + dCl_AoA = 0.0; + dCd_AoA = 0.0; + tdCl_AoA = 0.0; + tdCd_AoA = 0.0; + + // Mesh + dRx_x = Eigen::SparseMatrix<double, Eigen::RowMajor>(nDim*nNd, nDim*nNd); + dCl_x = Eigen::VectorXd::Zero(nDim*nNd); + dCd_x = Eigen::VectorXd::Zero(nDim*nNd); + + tdCl_x.resize(nNd, Eigen::Vector3d::Zero()); + tdCd_x.resize(nNd, Eigen::Vector3d::Zero()); +} + +void CoupledAdjoint::buildAdjointMatrix(std::vector<std::vector<Eigen::SparseMatrix<double, Eigen::RowMajor>*>> &matrices, Eigen::SparseMatrix<double, Eigen::RowMajor> &matrixAdjoint) +{ + // Create a list of triplets for the larger matrix + std::vector<Eigen::Triplet<double>> triplets; + + // Sanity check + for (size_t irow = 0; irow < matrices.size(); ++irow) + { + if (irow > 0) + if (matrices[irow].size() != matrices[irow-1].size()){ + std::cout << "WRONG ROW SIZE " << std::endl; + throw; + } + for (size_t icol = 0; icol < matrices[irow].size(); ++icol) + if (icol > 0) + if (matrices[irow][icol]->rows() != matrices[irow][icol-1]->rows()){ + std::cout << "WRONG COL SIZE " << std::endl; + throw; + } + } + + // Iterate over the rows and columns of the vector + int rowOffset = 0; + for (const auto& row : matrices) { + int colOffset = 0; + for (const auto& matrix : row) { + // Add the triplets of the matrix to the list of triplets for the larger matrix + for (int k = 0; k < matrix->outerSize(); ++k) { + for (Eigen::SparseMatrix<double, Eigen::RowMajor>::InnerIterator it(*matrix, k); it; ++it) { + triplets.push_back(Eigen::Triplet<double>(it.row() + rowOffset, it.col() + colOffset, it.value())); + } + } + colOffset += matrix->cols(); + } + rowOffset += row[0]->rows(); + } + // Create a new matrix from the deque of triplets + matrixAdjoint.setFromTriplets(triplets.begin(), triplets.end()); + matrixAdjoint.prune(0.); + matrixAdjoint.makeCompressed(); +} + +void CoupledAdjoint::run() +{ + gradientswrtInviscidFlow(); + gradientswrtBlowingVelocity(); + gradientwrtAoA(); + gradientwrtMesh(); + + transposeMatrices(); + + // Adjoint matrix + + // Store pointers to the matrices in a 2D vector + std::vector<std::vector<Eigen::SparseMatrix<double, Eigen::RowMajor>*>> matrices = { + {&dRphi_phi, &dRM_phi, &dRv_phi, &dRrho_phi, &dRblw_phi}, + {&dRphi_M, &dRM_M, &dRv_M, &dRrho_M, &dRblw_M}, + {&dRphi_v, &dRM_v, &dRv_v, &dRrho_v, &dRblw_v}, + {&dRphi_rho, &dRM_rho, &dRv_rho, &dRrho_rho, &dRblw_rho}, + {&dRphi_blw, &dRM_blw, &dRv_blw, &dRrho_blw, &dRblw_blw} + }; + + int rows = 0; int cols = 0; + for (const auto& row : matrices) rows += row[0]->rows(); // All matrices in a row have the same number of rows + for (const auto& mat1 : matrices[0]) cols += mat1->cols(); // All matrices in a column have the same number of columns + Eigen::SparseMatrix<double, Eigen::RowMajor> A_adjoint(rows, cols); + buildAdjointMatrix(matrices, A_adjoint); + + //***** Gradient wrt angle of attack *****// + //****************************************// + // CL + std::vector<double> rhsCl(A_adjoint.cols(), 0.0); + for (auto i = 0; i<dCl_phi.size(); ++i) + rhsCl[i] = dCl_phi[i]; + Eigen::VectorXd rhsCl_ = Eigen::Map<const Eigen::VectorXd>(rhsCl.data(), rhsCl.size()); + + Eigen::VectorXd lambdaCl(A_adjoint.cols()); // Solution vector for lambdasCl + Eigen::Map<Eigen::VectorXd> lambdaCl_(lambdaCl.data(), lambdaCl.size()); + alinsol->compute(A_adjoint, Eigen::Map<Eigen::VectorXd>(rhsCl_.data(), rhsCl_.size()), lambdaCl_); + Eigen::VectorXd lambdaClPhi = lambdaCl.segment(0, isol->pbl->msh->nodes.size()); + Eigen::VectorXd lambdaClBlw = lambdaCl.segment(lambdaCl.size() - isol->pbl->bBCs[0]->uE.size(), isol->pbl->bBCs[0]->uE.size()); + + // Total gradient + tdCl_AoA = dCl_AoA - dRphi_AoA.transpose()*lambdaClPhi; + + // CD + // std::vector<double> rhsCd(A_adjoint.cols(), 0.0); + // for (auto i = 0; i<dCd_phi.size(); ++i) + // rhsCd[i] = dCd_phi[i]; + // Eigen::VectorXd rhsCd_ = Eigen::Map<const Eigen::VectorXd>(rhsCd.data(), rhsCd.size()); + + // Eigen::VectorXd lambdaCd(A_adjoint.cols()); // Solution vector for lambdasCd + // Eigen::Map<Eigen::VectorXd> lambdaCd_(lambdaCd.data(), lambdaCd.size()); + // alinsol->compute(A_adjoint, Eigen::Map<Eigen::VectorXd>(rhsCd_.data(), rhsCd_.size()), lambdaCd_); + // Eigen::VectorXd lambdaCdPhi = lambdaCd.block(0, 0, isol->pbl->msh->nodes.size(), 1); + + // tdCd_AoA = dCd_AoA - dRphi_AoA.transpose()*lambdaCdPhi; // Total gradient + + //***** Gradient wrt mesh coordinates *****// + //*****************************************// + // CL + // Solution of (from augmented Lagrangian, eqn d/dx ) + // "dCl_x + dRphi_x * lambdaCl_phi + dRblw_x * lambdaCl_blw + dRx_x * lambdaCl_x" + Eigen::VectorXd lambdaCl_x = Eigen::VectorXd::Zero(isol->pbl->nDim * isol->pbl->msh->nodes.size()); + Eigen::Map<Eigen::VectorXd> lambdaCl_x_(lambdaCl_x.data(), lambdaCl_x.size()); + + Eigen::VectorXd rhsCl_x = dCl_x - dRphi_x.transpose() * lambdaClPhi - dRblw_x.transpose() * lambdaClBlw; + dRx_x.setIdentity(); + alinsol->compute(dRx_x.transpose(), Eigen::Map<Eigen::VectorXd>(rhsCl_x.data(), rhsCl_x.size()), lambdaCl_x_); + + for (auto n : isol->pbl->msh->nodes) + { + for (int m = 0; m < isol->pbl->nDim; m++) + { + tdCl_x[n->row](m) = lambdaCl_x[isol->pbl->nDim * (n->row) + m]; + // TODO: CHANGE FOR CD + tdCd_x[n->row](m) = lambdaCl_x[isol->pbl->nDim * (n->row) + m]; + } + } +} + +void CoupledAdjoint::gradientswrtInviscidFlow() +{ + //**** dRphi_phi ****// + dRphi_phi = iadjoint->getRu_U(); + + //**** dRM_phi, dRrho_phi, dRv_phi ****// + auto pbl = isol->pbl; + // Finite difference + std::vector<double> Phi_save = isol->phi; // Differential of the independent variable (phi) + double deltaPhi = 1e-6; // perturbation + + std::vector<Eigen::Triplet<double>> tripletsdM; + std::vector<Eigen::Triplet<double>> tripletsdrho; + std::vector<Eigen::Triplet<double>> tripletsdv; + + for (auto n : pbl->msh->nodes){ + Phi_save[n->row] = isol->phi[n->row] + deltaPhi; + + // Recompute the quantities on surface nodes. + int jj = 0; + for (auto srfNode : pbl->bBCs[0]->nodes){ + + double dM = 0.; double drho = 0.; + Eigen::Vector3d dv = Eigen::Vector3d::Zero(); + // Average of the quantity on the elements adjacent to the considered node. + for (auto e : pbl->fluid->neMap[srfNode]){ + dM += pbl->fluid->mach->eval(*e, Phi_save, 0); + drho += pbl->fluid->rho->eval(*e, Phi_save, 0); + Eigen::VectorXd grad = e->computeGradient(Phi_save, 0); + for (int i = 0; i < grad.size(); ++i) + dv(i) += grad(i); + } + dM /= static_cast<double>(pbl->fluid->neMap[srfNode].size()); + drho /= static_cast<double>(pbl->fluid->neMap[srfNode].size()); + dv /= static_cast<double>(pbl->fluid->neMap[srfNode].size()); + + // Form triplets + tripletsdM.push_back(Eigen::Triplet<double>(jj, n->row, -(dM - isol->M[srfNode->row])/deltaPhi)); + tripletsdrho.push_back(Eigen::Triplet<double>(jj, n->row, -(drho - isol->rho[srfNode->row])/deltaPhi)); + for (int i = 0; i < pbl->nDim; ++i) + tripletsdv.push_back(Eigen::Triplet<double>(jj*pbl->nDim + i, n->row, -(dv(i) - isol->U[srfNode->row](i))/deltaPhi)); + + jj++; + } + // Reset phi + Phi_save[n->row] = isol->phi[n->row]; + } + // Fill matrices with gradients + dRM_phi.setFromTriplets(tripletsdM.begin(), tripletsdM.end()); + dRrho_phi.setFromTriplets(tripletsdrho.begin(), tripletsdrho.end()); + dRv_phi.setFromTriplets(tripletsdv.begin(), tripletsdv.end()); + // Remove zeros + dRM_phi.prune(0.); dRrho_phi.prune(0.); dRv_phi.prune(0.); + + //**** dCl_phi, dCd_phi ****// + std::vector<std::vector<double>> dCx_phi = iadjoint->computeGradientCoefficientsFlow(); + dCl_phi = Eigen::VectorXd::Map(dCx_phi[0].data(), dCx_phi[0].size()); + dCd_phi = Eigen::VectorXd::Map(dCx_phi[1].data(), dCx_phi[1].size()); +} + +void CoupledAdjoint::gradientswrtMachNumber(){ + // dRphi_M = Eigen::SparseMatrix<double, Eigen::RowMajor>(isol->pbl->msh->nodes.size(), isol->pbl->bBCs[0]->nodes.size()); + // dRrho_M = Eigen::SparseMatrix<double, Eigen::RowMajor>(isol->pbl->bBCs[0]->nodes.size(), isol->pbl->bBCs[0]->nodes.size()); + // dRv_M = Eigen::SparseMatrix<double, Eigen::RowMajor>(isol->pbl->nDim*isol->pbl->bBCs[0]->nodes.size(), isol->pbl->bBCs[0]->nodes.size()); + // dRblw_M = Eigen::SparseMatrix<double, Eigen::RowMajor>(isol->pbl->bBCs[0]->uE.size(), isol->pbl->bBCs[0]->nodes.size()); +} + +void CoupledAdjoint::gradientswrtDensity(){ + // dRphi_dRho = 0 + // dRM_dRho = 0 + // dRrho_dRho = + // dRv_dRho = 0 + // dRblw_dRho = + + // dCl_dRho = + // dCd_dRho = +} + +void CoupledAdjoint::gradientswrtVelocity(){ + // dRphi_dV = 0 + // dRM_dV = 0 + // dRrho_dV = 0 + // dRv_dV = + // dRblw_dV = + + // dCl_dV = + // dCd_dV = +} + +void CoupledAdjoint::gradientswrtBlowingVelocity(){ + dRphi_blw = iadjoint->getRu_Blw(); + + // All others are zero. +} + +void CoupledAdjoint::gradientwrtAoA(){ + std::vector<double> dCx_AoA = iadjoint->computeGradientCoefficientsAoa(); + dCl_AoA = dCx_AoA[0]; dCd_AoA = dCx_AoA[1]; + + dRphi_AoA = iadjoint->getRu_A(); + + // All others are zero. +} + +void CoupledAdjoint::gradientwrtMesh(){ + std::vector<std::vector<double>> dCx_x = iadjoint->computeGradientCoefficientsMesh(); + dCl_x = Eigen::Map<Eigen::VectorXd>(dCx_x[0].data(), dCx_x[0].size()); + dCd_x = Eigen::Map<Eigen::VectorXd>(dCx_x[1].data(), dCx_x[1].size()); + + dRphi_x = iadjoint->getRu_X(); + dRx_x = iadjoint->getRx_X(); +} + +void CoupledAdjoint::setdRblw_M(std::vector<std::vector<double>> _dRblw_dM){ + //dRblw_M = Eigen::SparseMatrix<double, Eigen::RowMajor>(_dRblw_dM.size(), _dRblw_dM[0].size()); + // Convert std::vector<double> to Eigen::Triplets + std::vector<Eigen::Triplet<double>> triplets; + for (size_t i = 0; i < _dRblw_dM.size(); i++){ + for (size_t j = 0; j < _dRblw_dM[i].size(); j++){ + triplets.push_back(Eigen::Triplet<double>(i, j, _dRblw_dM[i][j])); + } + } + // Set matrix + dRblw_M.setFromTriplets(triplets.begin(), triplets.end()); + dRblw_M.prune(0.); +} +void CoupledAdjoint::setdRblw_v(std::vector<std::vector<double>> _dRblw_dv){ + //dRblw_v = Eigen::SparseMatrix<double, Eigen::RowMajor>(_dRblw_dv.size(), _dRblw_dv[0].size()); + // Convert std::vector<double> to Eigen::Triplets + std::vector<Eigen::Triplet<double>> triplets; + for (size_t i = 0; i < _dRblw_dv.size(); i++){ + for (size_t j = 0; j < _dRblw_dv[i].size(); j++){ + triplets.push_back(Eigen::Triplet<double>(i, j, _dRblw_dv[i][j])); + } + } + // Set matrix + dRblw_v.setFromTriplets(triplets.begin(), triplets.end()); + dRblw_v.prune(0.); +} +void CoupledAdjoint::setdRblw_rho(std::vector<std::vector<double>> _dRblw_drho){ + //dRblw_rho = Eigen::SparseMatrix<double, Eigen::RowMajor>(_dRblw_drho.size(), _dRblw_drho[0].size()); + // Convert std::vector<double> to Eigen::Triplets + std::vector<Eigen::Triplet<double>> triplets; + for (size_t i = 0; i < _dRblw_drho.size(); i++){ + for (size_t j = 0; j < _dRblw_drho[i].size(); j++){ + triplets.push_back(Eigen::Triplet<double>(i, j, _dRblw_drho[i][j])); + } + } + // Set matrix + dRblw_rho.setFromTriplets(triplets.begin(), triplets.end()); + dRblw_rho.prune(0.); +} +void CoupledAdjoint::setdRblw_x(std::vector<std::vector<double>> _dRblw_dx){ + //dRblw_x = Eigen::SparseMatrix<double, Eigen::RowMajor>(_dRblw_dx.size(), _dRblw_dx[0].size()); + // Convert std::vector<double> to Eigen::Triplets + std::vector<Eigen::Triplet<double>> triplets; + for (size_t i = 0; i < _dRblw_dx.size(); i++){ + for (size_t j = 0; j < _dRblw_dx[i].size(); j++){ + triplets.push_back(Eigen::Triplet<double>(i, j, _dRblw_dx[i][j])); + } + } + // Set matrix + dRblw_x.setFromTriplets(triplets.begin(), triplets.end()); + dRblw_x.prune(0.); +} + +/** + * @brief Transpose all matrices of the adjoint problem included in the system. Not the others. + */ +void CoupledAdjoint::transposeMatrices(){ + dRphi_phi = dRphi_phi.transpose(); + dRphi_M = dRphi_M.transpose(); + dRphi_rho = dRphi_rho.transpose(); + dRphi_v = dRphi_v.transpose(); + dRphi_blw = dRphi_blw.transpose(); + + dRM_phi = dRM_phi.transpose(); + dRM_M = dRM_M.transpose(); + dRM_rho = dRM_rho.transpose(); + dRM_v = dRM_v.transpose(); + dRM_blw = dRM_blw.transpose(); + + dRrho_phi = dRrho_phi.transpose(); + dRrho_M = dRrho_M.transpose(); + dRrho_rho = dRrho_rho.transpose(); + dRrho_v = dRrho_v.transpose(); + dRrho_blw = dRrho_blw.transpose(); + + dRv_phi = dRv_phi.transpose(); + dRv_M = dRv_M.transpose(); + dRv_rho = dRv_rho.transpose(); + dRv_v = dRv_v.transpose(); + dRv_blw = dRv_blw.transpose(); + + dRblw_phi = dRblw_phi.transpose(); + dRblw_M = dRblw_M.transpose(); + dRblw_rho = dRblw_rho.transpose(); + dRblw_v = dRblw_v.transpose(); + dRblw_blw = dRblw_blw.transpose(); +} + +void CoupledAdjoint::writeMatrixToFile(const Eigen::MatrixXd& matrix, const std::string& filename) { + std::ofstream file(filename); + if (file.is_open()) { + for (int i = 0; i < matrix.rows(); ++i) { + for (int j = 0; j < matrix.cols(); ++j) { + file << std::setprecision(20) << matrix(i, j); + if (j != matrix.cols() - 1) { + file << " "; + } + } + file << "\n"; + } + file.close(); + } +} diff --git a/blast/src/DCoupledAdjoint.h b/blast/src/DCoupledAdjoint.h new file mode 100644 index 0000000..3ae603d --- /dev/null +++ b/blast/src/DCoupledAdjoint.h @@ -0,0 +1,128 @@ +/* + * Copyright 2020 University of Liège + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DCOUPLEDADJOINT_H +#define DCOUPLEDADJOINT_H + +#include "blast.h" +#include "wObject.h" +#include "dart.h" +#include <Eigen/Sparse> + +#include <iostream> +#include <memory> + +namespace blast +{ + +/** + * @brief Adjoint of the coupled system. + * @author Paul Dechamps + */ +class BLAST_API CoupledAdjoint : public fwk::wSharedObject +{ + +public: + std::shared_ptr<dart::Adjoint> iadjoint; ///< Adjoint solver + std::shared_ptr<dart::Newton> isol; ///< Inviscid Newton solver + std::shared_ptr<blast::Driver> vsol; ///< Viscous solver + std::shared_ptr<tbox::LinearSolver> alinsol; ///< linear solver (adjoint aero) + + double tdCl_AoA; ///< Total derivative of the lift coefficient with respect to the angle of attack + double tdCd_AoA; ///< Total erivative of the drag coefficient with respect to the angle of attack + + std::vector<Eigen::Vector3d> tdCl_x; ///< Total derivative of the lift coefficient with respect to the mesh coordinates + std::vector<Eigen::Vector3d> tdCd_x; ///< Total derivative of the drag coefficient with respect to the mesh coordinates + +private: + Eigen::SparseMatrix<double, Eigen::RowMajor> dRphi_phi; ///< Inviscid flow Jacobian + Eigen::SparseMatrix<double, Eigen::RowMajor> dRphi_M; ///< Derivative of the inviscid flow residual with respect to the Mach number + Eigen::SparseMatrix<double, Eigen::RowMajor> dRphi_v; ///< Derivative of the inviscid flow residual with respect to the velocity + Eigen::SparseMatrix<double, Eigen::RowMajor> dRphi_rho; ///< Derivative of the inviscid flow residual with respect to the density + Eigen::SparseMatrix<double, Eigen::RowMajor> dRphi_blw; ///< Derivative of the inviscid flow residual with respect to the blowing velocity + Eigen::SparseMatrix<double, Eigen::RowMajor> dRphi_x; ///< Derivative of the potential residual with respect to the mesh coordinates + + Eigen::SparseMatrix<double, Eigen::RowMajor> dRM_phi; ///< Derivative of the Mach number residual with respect to the inviscid flow + Eigen::SparseMatrix<double, Eigen::RowMajor> dRM_M; ///< Derivative of the Mach number residual with respect to the Mach number + Eigen::SparseMatrix<double, Eigen::RowMajor> dRM_v; ///< Derivative of the Mach number residual with respect to the velocity + Eigen::SparseMatrix<double, Eigen::RowMajor> dRM_rho; ///< Derivative of the Mach number residual with respect to the density + Eigen::SparseMatrix<double, Eigen::RowMajor> dRM_blw; ///< Derivative of the Mach number residual with respect to the blowing velocity + Eigen::SparseMatrix<double, Eigen::RowMajor> dRM_x; ///< Derivative of the Mach number residual with respect to the mesh coordinates + + Eigen::SparseMatrix<double, Eigen::RowMajor> dRrho_phi; ///< Derivative of the density residual with respect to the inviscid flow + Eigen::SparseMatrix<double, Eigen::RowMajor> dRrho_M; ///< Derivative of the density residual with respect to the Mach number + Eigen::SparseMatrix<double, Eigen::RowMajor> dRrho_v; ///< Derivative of the density residual with respect to the velocity + Eigen::SparseMatrix<double, Eigen::RowMajor> dRrho_rho; ///< Derivative of the density residual with respect to the density + Eigen::SparseMatrix<double, Eigen::RowMajor> dRrho_blw; ///< Derivative of the density residual with respect to the blowing velocity + Eigen::SparseMatrix<double, Eigen::RowMajor> dRrho_x; ///< Derivative of the density residual with respect to the mesh coordinates + + Eigen::SparseMatrix<double, Eigen::RowMajor> dRv_phi; ///< Derivative of the velocity residual with respect to the inviscid flow + Eigen::SparseMatrix<double, Eigen::RowMajor> dRv_M; ///< Derivative of the velocity residual with respect to the Mach number + Eigen::SparseMatrix<double, Eigen::RowMajor> dRv_v; ///< Derivative of the velocity residual with respect to the velocity + Eigen::SparseMatrix<double, Eigen::RowMajor> dRv_rho; ///< Derivative of the velocity residual with respect to the density + Eigen::SparseMatrix<double, Eigen::RowMajor> dRv_blw; ///< Derivative of the velocity residual with respect to the blowing velocity + Eigen::SparseMatrix<double, Eigen::RowMajor> dRv_x; ///< Derivative of the velocity residual with respect to the mesh coordinates + + Eigen::SparseMatrix<double, Eigen::RowMajor> dRblw_phi; ///< Derivative of the blowing velocity residual with respect to the inviscid flow + Eigen::SparseMatrix<double, Eigen::RowMajor> dRblw_M; ///< Derivative of the blowing velocity residual with respect to the Mach number + Eigen::SparseMatrix<double, Eigen::RowMajor> dRblw_v; ///< Derivative of the blowing velocity residual with respect to the velocity + Eigen::SparseMatrix<double, Eigen::RowMajor> dRblw_rho; ///< Derivative of the blowing velocity residual with respect to the density + Eigen::SparseMatrix<double, Eigen::RowMajor> dRblw_blw; ///< Derivative of the blowing velocity residual with respect to the blowing velocity + Eigen::SparseMatrix<double, Eigen::RowMajor> dRblw_x; ///< Derivative of the blowing velocity residual with respect to the mesh coordinates + + // Objective functions + Eigen::VectorXd dCl_phi; ///< Derivative of the lift coefficient with respect to the inviscid flow + Eigen::VectorXd dCd_phi; ///< Derivative of the drag coefficient with respect to the inviscid flow + + // Angle of attack + Eigen::VectorXd dRphi_AoA; ///< Derivative of the inviscid flow residual with respect to the angle of attack + double dCl_AoA; ///< Partial derivative of the lift coefficient with respect to the angle of attack + double dCd_AoA; ///< Partial erivative of the drag coefficient with respect to the angle of attack + + // Mesh + Eigen::SparseMatrix<double, Eigen::RowMajor> dRx_x; ///< Derivative of the mesh residual with respect to the mesh coordinates + Eigen::VectorXd dCl_x; ///< Partial derivative of the lift coefficient with respect to the mesh coordinates + Eigen::VectorXd dCd_x; ///< Partial derivative of the drag coefficient with respect to the mesh coordinates + +public: + CoupledAdjoint(std::shared_ptr<dart::Adjoint> iAdjoint, std::shared_ptr<blast::Driver> vSolver); + virtual ~CoupledAdjoint () {std::cout << "~blast::CoupledAdjoint()\n";}; + + void run(); + void reset(); + + void setdRblw_M(std::vector<std::vector<double>> dRblw_dM); + void setdRblw_v(std::vector<std::vector<double>> dRblw_dv); + void setdRblw_rho(std::vector<std::vector<double>> dRblw_drho); + void setdRblw_x(std::vector<std::vector<double>> dRblw_dx); + +private: + void buildAdjointMatrix(std::vector<std::vector<Eigen::SparseMatrix<double, Eigen::RowMajor>*>> &matrices, Eigen::SparseMatrix<double, Eigen::RowMajor> &matrixAdjoint); + + void gradientswrtInviscidFlow(); + void gradientswrtMachNumber(); + void gradientswrtDensity(); + void gradientswrtVelocity(); + void gradientswrtBlowingVelocity(); + + void gradientwrtAoA(); + void gradientwrtMesh(); + + void transposeMatrices(); + void writeMatrixToFile(const Eigen::MatrixXd& matrix, const std::string& filename); +}; +} // namespace blast +#endif // DDARTADJOINT diff --git a/blast/src/DDriver.cpp b/blast/src/DDriver.cpp index ecaf966..2fee439 100644 --- a/blast/src/DDriver.cpp +++ b/blast/src/DDriver.cpp @@ -104,7 +104,7 @@ int Driver::run() // Reset transition if (reg->name == "wake") reg->xtr = 0.; - else if (reg-> name == "upper" || reg->name == "lower") + else if (reg->name == "upper" || reg->name == "lower") reg->xtr = 1.; else { @@ -222,15 +222,15 @@ int Driver::run() void Driver::checkWaves(size_t iPoint, BoundaryLayer *bl) { - double logRet_crit = 2.492 * std::pow(1 / (bl->Hk[iPoint] - 1), 0.43) + 0.7 * (std::tanh(14 * (1 / (bl->Hk[iPoint] - 1)) - 9.24) + 1.0); + double logRet_crit = 2.492 * std::pow(1. / (bl->Hk[iPoint] - 1), 0.43) + 0.7 * (std::tanh(14 * (1. / (bl->Hk[iPoint] - 1)) - 9.24) + 1.0); if (bl->Ret[iPoint] > 0) // Avoid log of negative number. { if (std::log10(bl->Ret[iPoint]) <= logRet_crit - 0.08) - bl->u[iPoint * bl->getnVar() + 2] = 0; // Set N to 0 at that point if waves do not grow. + bl->u[iPoint * bl->getnVar() + 2] = 0.; // Set N to 0 at that point if waves do not grow. } else - bl->u[iPoint * bl->getnVar() + 2] = 0; // Set N to 0 at that point if waves do not grow. + bl->u[iPoint * bl->getnVar() + 2] = 0.; // Set N to 0 at that point if waves do not grow. } /** @@ -340,7 +340,6 @@ void Driver::averageTransition(size_t iPoint, BoundaryLayer *bl, bool forced) void Driver::computeSectionalDrag(std::vector<BoundaryLayer *> const &sec, size_t i) { size_t nVar = sec[0]->getnVar(); - size_t lastWkPt = (sec[2]->nNodes - 1) * nVar; // Normalize friction and dissipation coefficients. std::vector<double> frictioncoeff_upper(sec[0]->nNodes, 0.0); @@ -361,8 +360,20 @@ void Driver::computeSectionalDrag(std::vector<BoundaryLayer *> const &sec, size_ // Friction drag coefficient. Cdf_sec[i] = Cdf_upper + Cdf_lower; // No friction in the wake - // Total drag coefficient. - Cdt_sec[i] = 2 * sec[2]->u[lastWkPt + 0] * pow(sec[2]->u[lastWkPt + 3], (sec[2]->u[lastWkPt + 1] + 5) / 2); + + // Total drag coefficient. Compute from upper and lower TE if there is no wake. + if (sec.size() > 2 && sec[2]->getName() == "wake") + { + size_t lastWkPt = (sec[2]->nNodes - 1) * nVar; + Cdt_sec[i] = 2 * sec[2]->u[lastWkPt + 0] * pow(sec[2]->u[lastWkPt + 3], (sec[2]->u[lastWkPt + 1] + 5) / 2); + } + else + { + size_t lastUpPt = (sec[0]->nNodes - 1) * nVar; + size_t lastLwPt = (sec[1]->nNodes - 1) * nVar; + Cdt_sec[i] = sec[0]->u[lastUpPt + 0] * pow(sec[0]->u[lastUpPt + 3], (sec[0]->u[lastUpPt + 1] + 5) / 2) + + sec[1]->u[lastLwPt + 0] * pow(sec[1]->u[lastLwPt + 3], (sec[1]->u[lastLwPt + 1] + 5) / 2); + } // Pressure drag coefficient. Cdp_sec[i] = Cdt_sec[i] - Cdf_sec[i]; } diff --git a/blast/src/DDriver.h b/blast/src/DDriver.h index ed598aa..c04b1e8 100644 --- a/blast/src/DDriver.h +++ b/blast/src/DDriver.h @@ -47,6 +47,9 @@ public: double getRe() const { return Re; }; std::vector<std::vector<double>> getSolution(size_t iSec); + // Setters. + void setVerbose(unsigned int _verbose) { verbose = _verbose; }; + void addSection(size_t iSec, BoundaryLayer * &bl){sections[iSec].push_back(bl);}; private: diff --git a/blast/src/blast.h b/blast/src/blast.h index 382e040..0fcf795 100644 --- a/blast/src/blast.h +++ b/blast/src/blast.h @@ -41,6 +41,9 @@ namespace blast class Driver; class Solver; +// Adjoint +class CoupledAdjoint; + // Data structures class BoundaryLayer; diff --git a/blast/tests/dart/adjointVII_2D.py b/blast/tests/dart/adjointVII_2D.py new file mode 100644 index 0000000..7341e98 --- /dev/null +++ b/blast/tests/dart/adjointVII_2D.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Copyright 2022 University of Liège +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# @author Paul Dechamps +# @date 2024 +# Test the blast adjoint implementation. + +# Imports. + +import blast.utils as viscUtils +from blast.interfaces.DAdjointInterface import AdjointInterface +import blast +import numpy as np + +from fwk.wutils import parseargs +import fwk +from fwk.testing import * +from fwk.coloring import ccolors + +import fwk +from fwk.testing import * +from fwk.coloring import ccolors +import numpy as np + +import math + +def cfgInviscid(nthrds, verb): + import os.path + # Parameters + return { + # Options + 'scenario' : 'aerodynamic', + 'task' : 'optimization', + 'Threads' : nthrds, # number of threads + 'Verb' : verb, # verbosity + # Model (geometry or mesh) + 'File' : os.path.dirname(os.path.abspath(__file__)) + '/../../models/dart/n0012.geo', # Input file containing the model + 'Pars' : {'xLgt' : 50, 'yLgt' : 50, 'msF' : 20, 'msTe' : 0.01, 'msLe' : 0.01}, # parameters for input file model + 'Dim' : 2, # problem dimension + 'Format' : 'gmsh', # save format (vtk or gmsh) + # Markers + 'Fluid' : 'field', # name of physical group containing the fluid + 'Farfield' : ['upstream', 'farfield', 'downstream'], # LIST of names of physical groups containing the farfield boundaries (upstream/downstream should be first/last element) + 'Wing' : 'airfoil', # LIST of names of physical groups containing the lifting surface boundary + 'Wake' : 'wake', # LIST of names of physical group containing the wake + 'WakeTip' : 'wakeTip', # LIST of names of physical group containing the edge of the wake + 'Te' : 'te', # LIST of names of physical group containing the trailing edge + 'dbc' : False, + 'Upstream' : 'upstream', + # Freestream + 'M_inf' : 0.2, # freestream Mach number + 'AoA' : 2., # freestream angle of attack + # Geometry + 'S_ref' : 1., # reference surface length + 'c_ref' : 1., # reference chord length + 'x_ref' : .25, # reference point for moment computation (x) + 'y_ref' : 0.0, # reference point for moment computation (y) + 'z_ref' : 0.0, # reference point for moment computation (z) + # Numerical + 'LSolver' : 'GMRES', # inner solver (Pardiso, MUMPS or GMRES) + 'G_fill' : 2, # fill-in factor for GMRES preconditioner + 'G_tol' : 1e-5, # tolerance for GMRES + 'G_restart' : 50, # restart for GMRES + 'Rel_tol' : 1e-6, # relative tolerance on solver residual + 'Abs_tol' : 1e-8, # absolute tolerance on solver residual + 'Max_it' : 20, # solver maximum number of iterations + } + +def cfgBlast(verb): + return { + 'Re' : 1e7, # Freestream Reynolds number + 'Minf' : 0.2, # Freestream Mach number (used for the computation of the time step only) + 'CFL0' : 1, # Inital CFL number of the calculation + 'Verb': verb, # Verbosity level of the solver + 'couplIter': 200, # Maximum number of iterations + 'couplTol' : 1e-8, # Tolerance of the VII methodology + 'iterPrint': 20, # int, number of iterations between outputs + 'resetInv' : True, # bool, flag to reset the inviscid calculation at every iteration. + 'sections' : [0], # List of sections for boundary layer calculation + 'xtrF' : [0.2, 0.2], # Forced transition location + 'interpolator' : 'Matching', # Interpolator for the coupling + } + +def main(): + # Timer. + tms = fwk.Timers() + tms['total'].start() + + args = parseargs() + icfg = cfgInviscid(args.k, args.verb) + vcfg = cfgBlast(args.verb) + + alpha = icfg['AoA']*np.pi/180 + dalpha = 1e-6 * np.pi/180 + daoa = [alpha - dalpha, alpha + dalpha] + dcl = [] + + coupler, isol, vsol = viscUtils.initBlast(icfg, vcfg) + + print(ccolors.ANSI_BLUE + 'PyInviscid...' + ccolors.ANSI_RESET) + isol.run() + # Inviscid finite difference + for aa in daoa: + isol.setAoA(aa) + isol.reset() + isol.run() + dcl.append(isol.getCl()) + dcl_daoa_dart_fd = (dcl[-1] - dcl[-2])/(2*dalpha) + isol.adjointSolver.run() + dcl_daoa_dart_ad = isol.adjointSolver.dClAoa + + # Adjoint objects + cAdj = blast.CoupledAdjoint(isol.adjointSolver, vsol) + pyAdjoint = AdjointInterface(cAdj, isol, vsol) + + # Run coupled case + print(ccolors.ANSI_BLUE + 'PySolving...' + ccolors.ANSI_RESET) + tms['forward'].start() + aeroCoeffs = coupler.run() + tms['forward'].stop() + + print(ccolors.ANSI_BLUE + 'PyDartAdjoint...' + ccolors.ANSI_RESET) + tms['adj_dart'].start() + isol.adjointSolver.run() + tms['adj_dart'].stop() + + print(ccolors.ANSI_BLUE + 'PyBlasterAdjoint...' + ccolors.ANSI_RESET) + tms['adj_py'].start() + pyAdjoint.build() + tms['adj_py'].stop() + tms['adj_blast'].start() + cAdj.run() + tms['adj_blast'].stop() + + # Coupled finite difference + tms['fd_blast'].start() + dcl = [] + for aa in daoa: + isol.setAoA(aa) + coupler.reset() + aeroCoeffs = coupler.run(write=False) + dcl.append(isol.getCl()) + dcl_daoa_blast_fd = (dcl[-1] - dcl[-2])/(2*dalpha) + tms['fd_blast'].stop() + + print(ccolors.ANSI_BLUE + 'PyResults' + ccolors.ANSI_RESET) + print('--------- Dart ---------') + print('AD: ' + str(dcl_daoa_dart_ad)) + print('FD: ' + str(dcl_daoa_dart_fd)) + print('Error: ' + str(abs(dcl_daoa_dart_ad - dcl_daoa_dart_fd)/dcl_daoa_dart_ad)) + print('--------- Blaster ---------') + print('AD: ' + str(cAdj.tdCl_AoA)) + print('FD: ' + str(dcl_daoa_blast_fd)) + print('Error: ' + str(abs(cAdj.tdCl_AoA - dcl_daoa_blast_fd)/cAdj.tdCl_AoA)) + tms['total'].stop() + print(tms) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/blast/tests/dart/naca0012_2D.py b/blast/tests/dart/naca0012_2D.py index 361b760..04ab790 100644 --- a/blast/tests/dart/naca0012_2D.py +++ b/blast/tests/dart/naca0012_2D.py @@ -88,17 +88,17 @@ def cfgInviscid(nthrds, verb): def cfgBlast(verb): return { - 'Re' : 1e7, # Freestream Reynolds number - 'Minf' : 0.2, # Freestream Mach number (used for the computation of the time step only) - 'CFL0' : 1, # Inital CFL number of the calculation - 'Verb': verb, # Verbosity level of the solver - 'couplIter': 50, # Maximum number of iterations - 'couplTol' : 1e-4, # Tolerance of the VII methodology - 'iterPrint': 5, # int, number of iterations between outputs - 'resetInv' : True, # bool, flag to reset the inviscid calculation at every iteration. - 'sections' : [0], - 'xtrF' : [None, 0.4],# Forced transition location - 'interpolator' : 'Matching' + 'Re' : 1e7, # Freestream Reynolds number + 'Minf' : 0.2, # Freestream Mach number (used for the computation of the time step only) + 'CFL0' : 1, # Inital CFL number of the calculation + 'Verb': verb, # Verbosity level of the solver + 'couplIter': 50, # Maximum number of iterations + 'couplTol' : 1e-4, # Tolerance of the VII methodology + 'iterPrint': 5, # int, number of iterations between outputs + 'resetInv' : True, # bool, flag to reset the inviscid calculation at every iteration. + 'sections' : [0], # List of sections for boundary layer calculation + 'xtrF' : [None, 0.4], # Forced transition location + 'interpolator' : 'Matching' # Interpolator for the coupling } def main(): -- GitLab