diff --git a/blast/api/mdaAPI.py b/blast/api/mdaAPI.py new file mode 100644 index 0000000000000000000000000000000000000000..43fc618cf91f0024418d3b763a26f61f3c9d11db --- /dev/null +++ b/blast/api/mdaAPI.py @@ -0,0 +1,294 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Copyright 2024 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. + + +## BLASTER api for openMDAO +# Paul Dechamps + +import numpy as np +import openmdao.api as om +import blast +import blast.utils as viscUtils + +# Solver openMDAO object +class BlasterSolver(om.ExplicitComponent): + """OpenMDAO component for the Blaster solver + + Parameters + ---------- + - cfg {dict} -- Configuration dictionary for the VII coupling + - icfg {dict} -- Configuration dictionary for the inviscid solver interface + - iSolverName {str} -- Inviscid solver name (default: 'DART') + + Inputs + ------ + - aoa {float} -- Angle of attack + - x_aero {np.array} -- Mesh coordinates + + Outputs + ------- + - cl {float} -- Lift coefficient + - cd {float} -- Drag coefficient + """ + + def __init__(self, cfg, icfg, iSolverName='DART'): + super().__init__() + + self.nCpt = 0 + + if iSolverName == 'DART': + from blast.interfaces.dart.DartInterface import DartInterface as interface + else: + raise RuntimeError('Solver '+iSolverName+' currently not implemented') + + for i in range(len(cfg['xtrF'])): + if cfg['xtrF'][i] is None: + cfg['xtrF'][i] = -1 + self.vsol = blast.Driver(cfg['Re'], cfg['Minf'], cfg['CFL0'], cfg['nSections'], xtrF_top=cfg['xtrF'][0], xtrF_bot=cfg['xtrF'][1], _span=cfg['span'], _verbose=cfg['Verb']) + + self.isol = interface(icfg, self.vsol, cfg) + + # Coupler + import blast.coupler as blastCoupler + self.coupler = blastCoupler.Coupler(self.isol, self.vsol, _maxCouplIter=cfg['couplIter'], _couplTol=cfg['couplTol'], _iterPrint=cfg['iterPrint'], _resetInv=cfg['resetInv']) + + # Adjoint + self.adjointSolver = blast.CoupledAdjoint(self.isol.adjointSolver, self.vsol) + + # Single coupler run to initialize viscous sections (which depend on size of upper/lower sides) + self.coupler.run(write=False) + self.coupler.reset() + print('object created') + + def setup(self): + self.add_input('aoa', val=0.0) + self.add_input('x_aero', val=np.zeros(self.isol.iobj['bnd'].nodes.size() * self.isol.getnDim())) + self.add_output('cl', val=0.0) + self.add_output('cd', val=0.0) + + def setup_partials(self): + self.declare_partials('cl', 'aoa', method='exact') + self.declare_partials('cd', 'aoa', method='exact') + self.declare_partials('cl', 'x_aero', method='exact') + self.declare_partials('cd', 'x_aero', method='exact') + + + def compute_partials(self, inputs, partials, discrete_inputs=None): + """Compute partial derivatives using the adjoint solver + """ + # Run adjoint solver + self.isol.iobj['adj'].run() + self.adjointSolver.reset() + self.adjointSolver.run() + + # AoA derivatives + partials['cl', 'aoa'] = self.adjointSolver.tdCl_AoA + partials['cd', 'aoa'] = self.adjointSolver.tdCd_AoA + + # Mesh derivative + nDim = self.isol.getnDim() + # Get mesh matrix + for inod, n in enumerate(self.isol.iobj['bnd'].nodes): + for idim in range(nDim): + partials['cl', 'x_aero'][0][inod*nDim + idim] = self.adjointSolver.tdCl_x[n.row][idim] + partials['cd', 'x_aero'][0][inod*nDim + idim] = self.adjointSolver.tdCd_x[n.row][idim] + + def compute(self, inputs, outputs): + """Update the mesh, aoa and run the solver + """ + nDim = self.isol.getnDim() + # Modify surface and deform mesh + self.isol.iobj['mrf'].savePos() + for inod, n in enumerate(self.isol.iobj['bnd'].nodes): + for idim in range(nDim): + n.pos[idim] = inputs['x_aero'][inod*nDim + idim] + self.isol.iobj['mrf'].deform() + + # Update viscous mesh + self.isol.updateMesh() + + # Update angle of attack + aoa = inputs['aoa'][0] + self.isol.setAoA(aoa*np.pi/180) # aoa is given in deg + + # Reset coupler + self.coupler.reset() + + # Run forward problem + self.coupler.run(write=False) + outputs['cl'] = self.isol.getCl() + outputs['cd'] = self.isol.getCd() + self.vsol.Cdf + + # Write results + self.isol.writeCp(sfx='_'+str(self.nCpt)) + viscUtils.getSolution(self.isol.sec, write=True, toW=['x', 'y', 'cf'], sfx='_'+str(self.nCpt)) + self.nCpt += 1 + + def getBoundaryMesh(self): + """Initial surface mesh coordinates + """ + return BlasterMesh(nDim=self.isol.getnDim(), bnd=self.isol.iobj['bnd']) + + def getBoundaryGeometry(self): + """Airfoil geometry + """ + return BlasterGeometry(nDim=self.isol.getnDim(), nNodes=self.isol.iobj['bnd'].nodes.size(), nodeRows=self.isol.vBnd[0][0].connectListNodes) + +# Surface mesh +class BlasterMesh(om.IndepVarComp): + """Initial 2D surface mesh coordinates + + Parameters + ---------- + - nDim {int} -- Number of dimensions + - bnd {object} -- Boundary object containing the mesh + + Outputs + ------- + - x_aero0 {np.array} -- Initial mesh coordinates + """ + + def initialize(self): + self.options.declare('nDim') + self.options.declare('bnd', recordable=False) + + def setup(self): + bnd = self.options['bnd'] + nDim = self.options['nDim'] + x_aero0 = np.zeros(bnd.nodes.size() * nDim) + for inod, n in enumerate(bnd.nodes): + for idim in range(nDim): + x_aero0[inod*nDim + idim] = n.pos[idim] + self.add_output('x_aero0', val=x_aero0) + +class BlasterGeometry(om.ExplicitComponent): + """Airfoil parametrization as a NACA 4-digit airfoil. + Uses the NACA 4-digit airfoil parametrization to generate an airfoil from a given x + distribution. + Parameters become tau (thickness), eps (max camber) and p (max camber position). + + Parameters + ---------- + - nDim {int} -- Number of dimensions + - nNodes {int} -- Number of nodes + - nodeRows {np.array} -- Nodal indexes + + Inputs + ------ + - x_aero0 {np.array} -- Initial mesh + - tau {float} -- Airfoil thickness + - eps {float} -- Airfoil max camber + - p {float} -- Airfoil max camber position + + Outputs + ------- + - x_aero_out {np.array} -- Airfoil coordinates + """ + + def initialize(self): + self.options.declare('nDim') + self.options.declare('nNodes') + self.options.declare('nodeRows') + + def setup(self): + nNodes = self.options['nNodes'] + nDim = self.options['nDim'] + self.add_input('x_aero0', np.zeros(nNodes*nDim)) # Initial mesh + self.add_input('tau', val = 0.12, desc='Airfoil thickness') + self.add_input('eps', val = 0., desc='Airfoil max camber') + self.add_input('p', val = 0., desc='Airfoil max camber position') + self.add_output('x_aero_out', val = np.zeros(nNodes*nDim)) + + def setup_partials(self): + self.declare_partials('x_aero_out', 'tau', method='fd') + self.declare_partials('x_aero_out', 'eps', method='fd') + self.declare_partials('x_aero_out', 'p', method='fd') + + def compute(self, inputs, outputs): + """Compute airfoil coordinates + Points are sorted in seilig format to identify upper and lower surfaces. + Then the NACA 4-digit airfoil is generated and the points are sorted back + to the original mesh format.""" + + x_aero0 = inputs['x_aero0'] + tau = inputs['tau'][0] + eps = inputs['eps'][0] + p = inputs['p'][0] + nDim = self.options['nDim'] + + # Retreive selig coordinates + x_seilig = np.zeros(len(self.options['nodeRows'])) + for i, val in enumerate(self.options['nodeRows']): + x_seilig[i] = x_aero0[val*nDim] + # Generate airfoil + # Format tau eps and p with 2 digits after coma + print(f"tau: {tau:.10f}, eps: {eps:.10f}, p: {p:.10f}") + xb, yb = self.__generateAirfoil(x_seilig, tau, eps, p) + # Go back to normal mesh + x_aero_out = np.zeros(len(x_aero0)) + for i, val in enumerate(self.options['nodeRows']): + x_aero_out[val*nDim] = xb[i] + x_aero_out[val*nDim+1] = yb[i] + outputs['x_aero_out'] = x_aero_out + + def __generateAirfoil(self, x, tau, eps, p): + """Generate NACA 4-digit airfoil from x coordinates points + + Arguments + --------- + - x {np.array} -- x coordinates sorted in seilig format + - tau {float} -- Airfoil thickness + - eps {float} -- Airfoil max camber + - p {float} -- Airfoil max camber position + + Returns + ------- + - tuple -- x and y coordinates + """ + + T = 10 * tau * ( + 0.2969 * np.sqrt(x) + - 0.1260 * (x) + - 0.3537 * (x)**2 + + 0.2843 * (x)**3 + - 0.1015 * (x)**4) # Thickness + + YBar = np.zeros(len(T)) + dYBar_dx = np.zeros(len(T)) + Y = np.zeros(len(T)) + xB = np.zeros(len(T)) + yB = np.zeros(len(T)) + + for i, xi in enumerate(x): + # Camber + if xi < p: + YBar[i] = ((eps * xi)/(p**2)) * (2 * p - xi) + dYBar_dx[i] = (2 * eps)/(p**2) * (p - xi) + elif xi >= p: + YBar[i] = (eps * (1 - xi)/((1-p)**2)) * (1 + xi - 2 * p) + dYBar_dx[i] = (2 * eps)/((1 - p)**2) * (p - xi) + + # Final x & y coordinates + if i >= len(x)/2 - 1: + # Lower surface + yB[i] = YBar[i] - 0.5 * T[i] * np.cos(np.arctan(dYBar_dx[i])) + xB[i] = x[i] + 0.5 * T[i] * np.sin(np.arctan(dYBar_dx[i])) + else: + # Upper surface + yB[i] = YBar[i] + 0.5 * T[i] * np.cos(np.arctan(dYBar_dx[i])) + xB[i] = x[i] - 0.5 * T[i] * np.sin(np.arctan(dYBar_dx[i])) + return (xB, yB) diff --git a/blast/coupler.py b/blast/coupler.py index b7213a4c4c9ed54404316d8377338bdf45d600cd..2878d67f8d1bb29607c63671310a522894251a5a 100644 --- a/blast/coupler.py +++ b/blast/coupler.py @@ -85,6 +85,7 @@ class Coupler: # Write inviscid Cp distribution. if couplIter == 0: self.isol.initializeViscousSolver() + self.isol.updateStagnation() if write: self.isol.writeCp(sfx='_inviscid'+self.filesfx) @@ -142,20 +143,6 @@ class Coupler: # 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: @@ -166,3 +153,28 @@ class Coupler: for i in range(len(side.blowingVel)): side.blowingVel[i] = 0. self.isol.setBlowingVelocity() + + # Viscous solver + if self.isol.vinit: + for s in self.isol.sec: + for reg in s: + reg.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()) + self.isol.vtExt[iSec][i] = np.zeros(reg.getnNodes()) + reg.setxxExt(self.isol.xxExt[iSec][i]) + reg.setVtExt(self.isol.vtExt[iSec][i]) + reg.setDeltaStarExt(self.isol.deltaStarExt[iSec][i]) + diff --git a/blast/interfaces/DAdjointInterface.py b/blast/interfaces/DAdjointInterface.py deleted file mode 100644 index cf3f60ff13934985b9e8c65a44c291658d1f055e..0000000000000000000000000000000000000000 --- a/blast/interfaces/DAdjointInterface.py +++ /dev/null @@ -1,102 +0,0 @@ -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 4a63dc6302cf5a6839dbdbe6122b9a1c018b0f35..a5049f6ce9f6cef58c64e9b45f064ef73bc90264 100644 --- a/blast/interfaces/DDataStructure.py +++ b/blast/interfaces/DDataStructure.py @@ -69,6 +69,23 @@ class Group: else: raise RuntimeError('Invalid region', reg) + def getNodesRow(self, reg:str): + """Return the row of the nodes + + args: + ---- + reg: str + Region of the boundary layer (upper, lower, wake) + """ + if reg == "wake": + return self.connectListNodes + elif reg == "upper": + return np.flip(self.connectListNodes[:self.stagPoint+1]) + elif reg == "lower": + return self.connectListNodes[self.stagPoint:] + else: + raise RuntimeError('Invalid region', reg) + def getVelocity(self, reg:str, dim:int): """Return the velocity in the direction dim @@ -140,6 +157,23 @@ class Group: return self.Rho[self.stagPoint:] else: raise RuntimeError('Invalid region', reg) + + def getConnectList(self, reg:str): + """Return the connectivity list of the elements + + args: + ---- + reg: str + Region of the boundary layer (upper, lower, wake) + """ + if reg == "wake": + return self.connectListElems + elif reg == "upper": + return np.flip(self.connectListElems[:self.stagPoint]) + elif reg == "lower": + return self.connectListElems[self.stagPoint:] + else: + raise RuntimeError('Invalid region', reg) def setConnectList(self, _connectListNodes, _connectListElems): """Set connectivity lists for viscous structures. diff --git a/blast/interfaces/DSolversInterface.py b/blast/interfaces/DSolversInterface.py index dc85a470676297b4f60acb7850aed6040a3db6f9..b7542bc56ab9b64d9600921f416f5607e88d3053 100644 --- a/blast/interfaces/DSolversInterface.py +++ b/blast/interfaces/DSolversInterface.py @@ -1,7 +1,7 @@ import numpy as np import blast +import tbox from blast.interfaces.DDataStructure import Group -import time class SolversInterface: def __init__(self, _vSolver, _cfg): @@ -16,23 +16,22 @@ class SolversInterface: self.vBnd = [[Group('vAirfoil'+str(k) if iReg == 0 else 'vWake'+str(k), self.getnDim()) for iReg in range(2)] for k in range(self.cfg['nSections'])] # Initialize interpolator and meshes - initialTime = time.time() - self.setMesh() + self.getWing() if self.cfg['interpolator'] == 'Matching': from blast.interfaces.interpolators.DMatchingInterpolator import MatchingInterpolator as interp # Initialize viscous structures for matching computations for iSec in range(len(self.vBnd)): 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 + self.vBnd[iSec][iReg].nodesCoord = self.iBnd[iReg].nodesCoord.copy() + self.vBnd[iSec][iReg].elemsCoord = self.iBnd[iReg].elemsCoord.copy() + self.vBnd[iSec][iReg].connectListNodes = self.iBnd[iReg].connectListNodes.copy() + self.vBnd[iSec][iReg].connectListElems = self.iBnd[iReg].connectListElems.copy() elif self.cfg['interpolator'] == 'Rbf': from blast.interfaces.interpolators.DRbfInterpolator import RbfInterpolator as interp - print('RBF interpolator initialized.') - print('Loading viscous mesh... ', end='') + print('Initializing RBF interpolator.') self.getVWing() - print('done in {:.2f}s.'.format(time.time()-initialTime)) else: raise RuntimeError('Incorrect interpolator specified in config.') @@ -47,7 +46,7 @@ class SolversInterface: for iSec in range(self.cfg['nSections'])] self.vinit = False - def setViscousSolver(self, adjoint=False): + def setViscousSolver(self): """Interpolate solution to viscous mesh and sets the inviscid boundary in the viscous solver. """ for iSec in range(self.cfg['nSections']): @@ -63,11 +62,11 @@ class SolversInterface: reg.setRhoe(self.vBnd[iSec][iReg].getRho(reg.name)) reg.setVt(self.vBnd[iSec][iReg].getVt(reg.name)) - if adjoint == False: - reg.setDeltaStarExt(self.deltaStarExt[iSec][i]) - reg.setxxExt(self.xxExt[iSec][i]) + reg.setDeltaStarExt(self.deltaStarExt[iSec][i]) + reg.setxxExt(self.xxExt[iSec][i]) + reg.setVtExt(self.vtExt[iSec][i]) - def getBlowingBoundary(self, couplIter, adjoint=False): + def getBlowingBoundary(self, couplIter): """Get blowing boundary condition from the viscous solver. args: ---- @@ -82,11 +81,11 @@ 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))) - - 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) + + 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.vtExt[iSec][i] = np.asarray(self.sec[iSec][i].vt) def interpolate(self, dir): if dir == 'i2v': @@ -96,6 +95,102 @@ class SolversInterface: else: raise RuntimeError('Invalid direction', dir) + def updateMesh(self): + """Collect the mesh on blowing boundaries and update the mesh in the viscous solver. + This function does not update connectivity lists + """ + if self.getnDim() != 2: + raise RuntimeError('SolverInterface::updateMesh - Only 2D meshes are supported.') + if self.cfg['interpolator'] != 'Matching': + raise RuntimeError('SolverInterface::updateMesh - Only Matching interpolator is supported.') + + meshList = self.getMesh() + + # Update the mesh in data structures + for ireg, reg in enumerate(self.iBnd): + for i, row in enumerate(reg.connectListNodes): + for idim in range(self.getnDim()): + reg.nodesCoord[i, idim] = meshList[ireg][row, idim] + + # Recompute elements data + for ireg, reg in enumerate(self.iBnd): + for i in range(len(reg.nodesCoord[:,0])-1): + for idim in range(self.getnDim()): + reg.elemsCoord[i, idim] = (reg.nodesCoord[i, idim] + reg.nodesCoord[i+1, idim])/2 + + # Update the mesh in the data structures + for iSec in range(len(self.vBnd)): + for iReg, reg in enumerate(self.vBnd[iSec]): + self.vBnd[iSec][iReg].nodesCoord = self.iBnd[iReg].nodesCoord.copy() + self.vBnd[iSec][iReg].elemsCoord = self.iBnd[iReg].elemsCoord.copy() + + # Set mesh in the viscous solver + for iSec in range(self.cfg['nSections']): + # Compute section chord + xMin = np.min(self.vBnd[iSec][0].nodesCoord[:,0]) + xMax = np.max(self.vBnd[iSec][0].nodesCoord[:,0]) + chord = xMax - xMin + for reg in self.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) + + xIdx = 0 + if self.getnDim() == 2: + yIdx = 1 + zIdx = 2 + elif self.getnDim() == 3: + yIdx = 2 + zIdx = 1 + else: + raise RuntimeError('Incorrect number of dimensions', self.getnDim()) + + nodeRows = tbox.std_vector_int() + if self.cfg['interpolator'] == 'Matching': + for val in self.vBnd[iSec][iReg].getNodesRow(reg.name): + nodeRows.push_back(int(val)) + else: + for i in range(len(self.vBnd[iSec][iReg].getNodesCoord(reg.name, xIdx))): + nodeRows.push_back(int(0)) + + connectElems = tbox.std_vector_int() + if self.cfg['interpolator'] == 'Matching': + for val in self.vBnd[iSec][iReg].getConnectList(reg.name): + connectElems.push_back(int(val)) + else: + for i in range(len(self.vBnd[iSec][iReg].getNodesCoord(reg.name, xIdx))-1): + connectElems.push_back(int(0)) + + # x, y, z + xv = tbox.std_vector_double() + for val in self.vBnd[iSec][iReg].getNodesCoord(reg.name, xIdx): + xv.push_back(val) + yv = tbox.std_vector_double() + for val in self.vBnd[iSec][iReg].getNodesCoord(reg.name, yIdx): + yv.push_back(val) + zv = tbox.std_vector_double() + for val in self.vBnd[iSec][iReg].getNodesCoord(reg.name, zIdx): + zv.push_back(val) + + # Set mesh + reg.setMesh(xv, + yv, + zv, + chord, + xMin, + nodeRows, + connectElems) + + def updateStagnation(self): + """Update the stagnation points in the viscous solver. + """ + for iSec in range(self.cfg['nSections']): + for reg in self.vBnd[iSec]: + reg.computeStagPoint() + def initializeViscousSolver(self): """Initialize the viscous sections in the viscous solver. """ @@ -117,7 +212,6 @@ class SolversInterface: name = "upper" xtrF = self.cfg['xtrF'][j] - self.sec[i].append(blast.BoundaryLayer(xtrF, name)) self.vSolver.addSection(i, self.sec[i][j]) @@ -153,12 +247,41 @@ class SolversInterface: else: raise RuntimeError('Incorrect number of dimensions', self.getnDim()) - reg.setMesh(self.vBnd[iSec][iReg].getNodesCoord(reg.name, xIdx), - self.vBnd[iSec][iReg].getNodesCoord(reg.name, yIdx), - self.vBnd[iSec][iReg].getNodesCoord(reg.name, zIdx), - chord, - xMin) + nodeRows = tbox.std_vector_int() + if self.cfg['interpolator'] == 'Matching': + for val in self.vBnd[iSec][iReg].getNodesRow(reg.name): + nodeRows.push_back(int(val)) + else: + for i in range(len(self.vBnd[iSec][iReg].getNodesCoord(reg.name, xIdx))): + nodeRows.push_back(int(0)) + connectElems = tbox.std_vector_int() + if self.cfg['interpolator'] == 'Matching': + for val in self.vBnd[iSec][iReg].getConnectList(reg.name): + connectElems.push_back(int(val)) + else: + for i in range(len(self.vBnd[iSec][iReg].getNodesCoord(reg.name, xIdx))-1): + connectElems.push_back(int(0)) + + # x, y, z + xv = tbox.std_vector_double() + for val in self.vBnd[iSec][iReg].getNodesCoord(reg.name, xIdx): + xv.push_back(val) + yv = tbox.std_vector_double() + for val in self.vBnd[iSec][iReg].getNodesCoord(reg.name, yIdx): + yv.push_back(val) + zv = tbox.std_vector_double() + for val in self.vBnd[iSec][iReg].getNodesCoord(reg.name, zIdx): + zv.push_back(val) + + # Set mesh + reg.setMesh(xv, + yv, + zv, + chord, + xMin, + nodeRows, + connectElems) # External variables for i, reg in enumerate(self.sec[iSec]): if reg.name == 'wake': @@ -172,7 +295,9 @@ class SolversInterface: loc[iPoint] = reg.loc[iPoint] self.xxExt[iSec][i] = loc self.deltaStarExt[iSec][i] = np.zeros(reg.getnNodes()) + self.vtExt[iSec][i] = np.zeros(reg.getnNodes()) self.vinit = True + return 0 def getVWing(self): """Obtain the nodes' location and row and cg of all elements. @@ -256,4 +381,82 @@ class SolversInterface: self.vBnd[iy][iReg].initStructures(nodesSec.shape[0]) self.vBnd[iy][iReg].nodesCoord = nodesSec - self.vBnd[iy][iReg].elemsCoord = cgSec \ No newline at end of file + self.vBnd[iy][iReg].elemsCoord = cgSec + + # Inviscid solver methods + def run()->int: + """Run inviscid solver + """ + raise NotImplementedError('SolverInterface - run not implemented') + + def reset()->None: + """Reset solution + """ + raise NotImplementedError('SolverInterface - reset not implemented') + + def save(self)->None: + """Save solution + """ + raise NotImplementedError('SolverInterface - save not implemented') + + def writeCp(self)->None: + """Write pressure coefficient + """ + raise NotImplementedError('SolverInterface - writeCp not implemented') + + # Inviscid solver properties + def getVerbose(self)->int: + """Get verbose + """ + raise NotImplementedError('SolverInterface - getVerbose not implemented') + + # Flow properties and aerodynamic coefficients + def getAoA(self)->float: + """Get angle of attack + """ + raise NotImplementedError('SolverInterface - getAoA not implemented') + + def getCl(self)->float: + """Get lift coefficient + """ + raise NotImplementedError('SolverInterface - getCl not implemented') + + def getCd(self)->float: + """Get inviscid drag coefficient + """ + raise NotImplementedError('SolverInterface - getCd not implemented') + + def getCm(self)->float: + """Get moment coefficient + """ + raise NotImplementedError('SolverInterface - getCm not implemented') + + def getnDim(self)->int: + """Get number of dimensions + """ + raise NotImplementedError('SolverInterface - getnDim not implemented') + + def getWing(self)->None: + """Get wing mesh + """ + raise NotImplementedError('SolverInterface - getWing not implemented') + + def getMesh(self)->list: + """Get mesh + """ + raise NotImplementedError('SolverInterface - getMesh not implemented') + + def getInviscidBC(self)->None: + """Get inviscid boundary condition + """ + raise NotImplementedError('SolverInterface - getInviscidBC not implemented') + + def setBlowingVelocity(self)->None: + """Set blowing velocity + """ + raise NotImplementedError('SolverInterface - setBlowingVelocity not implemented') + + def getnNodesDomain(self)->int: + """Get number of nodes in domain + """ + raise NotImplementedError('SolverInterface - getnNodesDomain not implemented') diff --git a/blast/interfaces/dart/DartInterface.py b/blast/interfaces/dart/DartInterface.py index 09e06bead26a75a8cdc60298f0c38a097c67f004..7913ab549b1b745aa332d945590932b7f0633bca 100644 --- a/blast/interfaces/dart/DartInterface.py +++ b/blast/interfaces/dart/DartInterface.py @@ -28,11 +28,11 @@ class DartInterface(SolversInterface): def __init__(self, dartCfg, vSolver, _cfg): try: from modules.dartflo.dart.api.core import init_dart - self.inviscidObjects = init_dart(dartCfg, task=dartCfg['task'], viscous=True) - self.solver = self.inviscidObjects.get('sol') # Solver - self.blw = [self.inviscidObjects.get('blwb'), self.inviscidObjects.get('blww')] + self.iobj = init_dart(dartCfg, task=dartCfg['task'], viscous=True) + self.solver = self.iobj.get('sol') # Solver + self.blw = [self.iobj.get('blwb'), self.iobj.get('blww')] if dartCfg['task'] == 'optimization': - self.adjointSolver = self.inviscidObjects.get('adj') + self.adjointSolver = self.iobj.get('adj') print('Dart successfully loaded for optimization.') else: print('Dart successfully loaded for analysis.') @@ -76,25 +76,27 @@ class DartInterface(SolversInterface): for j in range(size): data[i,3+j] = vData[vElems[i%vElems.size()].nodes[0].row][j] i += 1 - print('writing data file ' + 'Cp' +sfx + '.dat') + if self.getVerbose() > 1: + print('writing data file ' + 'Cp' +sfx + '.dat') np.savetxt('Cp'+sfx+'.dat', data, fmt='%1.6e', delimiter=', ', header='x, y, z, Cp', comments='') elif self.getnDim() == 3: # Save surface cp - data = np.zeros((self.inviscidObjects['bnd'].nodes.size(), 4)) - for i, n in enumerate(self.inviscidObjects['bnd'].nodes): + data = np.zeros((self.iobj['bnd'].nodes.size(), 4)) + for i, n in enumerate(self.iobj['bnd'].nodes): data[i,0] = n.pos[0] data[i,1] = n.pos[1] data[i,2] = n.pos[2] data[i,3] = self.solver.Cp[n.row] - np.savetxt(self.inviscidObjects['msh'].name+'_Cp_surface'+sfx+'.dat', data, fmt='%1.6e', delimiter=', ', header='x, y, z, Cp', comments='') + np.savetxt(self.iobj['msh'].name+'_Cp_surface'+sfx+'.dat', data, fmt='%1.6e', delimiter=', ', header='x, y, z, Cp', comments='') import modules.dartflo.dart.utils as invUtils if self.cfg['Format'] == 'vtk': - print('Saving Cp files in vtk format. Msh {:s}, Tag {:.0f}'.format(self.inviscidObjects['msh'].name, self.cfg['saveTag'])) - invUtils.write_slices(self.inviscidObjects['msh'].name, self.cfg['writeSections'], self.cfg['saveTag'], sfx=sfx) + if self.getVerbose() > 1: + print('Saving Cp files in vtk format. Msh {:s}, Tag {:.0f}'.format(self.iobj['msh'].name, self.cfg['saveTag'])) + invUtils.write_slices(self.iobj['msh'].name, self.cfg['writeSections'], self.cfg['saveTag'], sfx=sfx) def save(self, sfx='inviscid'): - self.solver.save(self.inviscidObjects['wrt'], sfx) + self.solver.save(self.iobj['wrt'], sfx) def getAoA(self): return self.solver.pbl.alpha @@ -118,7 +120,7 @@ class DartInterface(SolversInterface): return self.solver.verbose def getnDim(self): - return self.inviscidObjects['pbl'].nDim + return self.iobj['pbl'].nDim def getnNodesDomain(self): return self.solver.pbl.msh.nodes.size() @@ -154,17 +156,27 @@ class DartInterface(SolversInterface): for i, blw in enumerate(self.iBnd[n].blowingVel): self.blw[n].setU(i, blw) - def setMesh(self): + def getWing(self): if self.getnDim() == 2: - self.getAirfoil() + self.__getWing2D() elif self.getnDim() == 3: - self.getWing() + self.__getWing3D() else: raise RuntimeError('dartInterface::Could not resolve how to set\ the mesh. Either ''nDim'' is incorrect or ''msh'' was not set for\ 3D computations') + + def getMesh(self): + """Return the mesh on blowing boundaries + """ + meshList = [np.zeros((b.nodes.size(), self.getnDim())) for b in self.blw] + for ib, b in enumerate(self.blw): + for inod, n in enumerate(b.nodes): + for idim in range(self.getnDim()): + meshList[ib][inod, idim] = n.pos[idim] + return meshList - def getAirfoil(self): + def __getWing2D(self): """Create data structure for information transfer. """ self.iBnd[0].initStructures(self.blw[0].nodes.size()) @@ -266,9 +278,14 @@ class DartInterface(SolversInterface): data[:,2] = data[connectListNodes,2] data[:,3] = data[connectListNodes,3] data[:,4] = data[connectListNodes,4] + + connect2 = np.zeros((len(connectListElems)), dtype=int) + for i, e in enumerate(self.blw[0].tag.elems): + connect2[connectListElems[i]] = i + self.iBnd[0].nodesCoord = np.column_stack((data[:,1], data[:,2],\ data[:,3], data[:,4])) - self.iBnd[0].setConnectList(connectListNodes, connectListElems) + self.iBnd[0].setConnectList(connectListNodes, connect2) 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]) @@ -277,7 +294,6 @@ class DartInterface(SolversInterface): elemCoord[i,3] = i self.iBnd[0].elemsCoord = elemCoord - # Wake self.iBnd[1].initStructures(self.blw[1].nodes.size()) # Node number @@ -314,7 +330,11 @@ class DartInterface(SolversInterface): dataW[:,4] = dataW[connectListNodes,4] self.iBnd[1].nodesCoord = np.column_stack((dataW[:,1], dataW[:,2], \ dataW[:,3], dataW[:,4])) - self.iBnd[1].setConnectList(connectListNodes, connectListElems) + + connect2_w = np.zeros((len(connectListElems)), dtype=int) + for i, e in enumerate(self.blw[1].tag.elems): + connect2_w[connectListElems[i]] = i + self.iBnd[1].setConnectList(connectListNodes, connect2_w) elemCoordW = np.zeros((len(self.iBnd[1].nodesCoord)-1, 4)) for i in range(len(elemCoordW[:,0])): @@ -324,7 +344,7 @@ class DartInterface(SolversInterface): elemCoordW[i,3] = i self.iBnd[1].elemsCoord = elemCoordW - def getWing(self): + def __getWing3D(self): """Obtain the nodes' location and row and cg of all elements. """ diff --git a/blast/mdao/naca0012_mdao.py b/blast/mdao/naca0012_mdao.py new file mode 100644 index 0000000000000000000000000000000000000000..ec34a6ead36e2ad5799ead7bb2584b3b4f2385ba --- /dev/null +++ b/blast/mdao/naca0012_mdao.py @@ -0,0 +1,201 @@ +import blast.utils as viscUtils +import numpy as np + +from fwk.wutils import parseargs +import fwk +from fwk.testing import * +from fwk.coloring import ccolors + +from blast.api.mdaAPI import BlasterSolver + +import openmdao.api as om + +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.dirname(os.path.abspath(__file__))) + '/models/dart/n0012.geo', # Input file containing the model + 'Pars' : {'xLgt' : 50, 'yLgt' : 50, 'msF' : 30, 'msTe' : 0.01, 'msLe' : 0.0075}, # 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': 50, # Maximum number of iterations + 'couplTol' : 1e-3, # 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' : [0.05, 0.05], # Forced transition location + 'interpolator' : 'Matching',# Interpolator for the coupling + 'nSections' : 1, # Number of sections in the domain + 'span' : 1.0 # Wing span + } + +class groupMDA(om.Group): + def setup(self): + args = parseargs() + icfg = cfgInviscid(args.k, args.verb) + vcfg = cfgBlast(args.verb) + + #cycle = self.add_subsystem('cycle', om.Group()) + + # Add the MDAO components + blaster = BlasterSolver(vcfg, icfg) + + # Design variables box + self.add_subsystem('dvs', om.IndepVarComp(), promotes=['*']) + self.add_subsystem('mesh', blaster.getBoundaryMesh()) + self.add_subsystem('geometry', blaster.getBoundaryGeometry()) + self.add_subsystem('solver', blaster) + + # Add objective function + self.add_subsystem('obj_cmp', om.ExecComp('obj = cd'), promotes=['obj', 'cd']) + + # Connect meshes + self.connect('mesh.x_aero0', 'geometry.x_aero0') + self.connect('geometry.x_aero_out', 'solver.x_aero') + + #nonlinear_solver = om.NewtonSolver(solve_subsystems=False) + self.linear_solver = om.DirectSolver() + + def configure(self): + # Design vars + self.dvs.add_output('aoa', val=0.0) + self.connect('aoa', 'solver.aoa') + #self.add_design_var('aoa', lower=0.0, upper=3.0) + + self.dvs.add_output('tau', val=0.12) + self.connect('tau', 'geometry.tau') + #self.add_design_var('tau', lower=0.10, upper=0.2) + + self.dvs.add_output('eps', val=0.0) + self.connect('eps', 'geometry.eps') + self.add_design_var('eps', lower=0.0, upper=0.05) + + self.dvs.add_output('p', val=0.4) + self.connect('p', 'geometry.p') + self.add_design_var('p', lower=0.35, upper=0.45) + + self.add_constraint('solver.cl', equals=0.4) + + #self.connect('solver.cl', 'cl') + self.connect('solver.cd', 'cd') + self.add_objective('obj') + +def main(): + # Timer. + tms = fwk.Timers() + tms['total'].start() + + prob = om.Problem() + prob.model = groupMDA() + + prob.driver = om.ScipyOptimizeDriver() + prob.driver.options['optimizer'] = 'SLSQP' + prob.driver.options['tol'] = 1e-3 + + recorder = om.SqliteRecorder('reports/BlasterSolver_file.sql') + + prob.driver.add_recorder(recorder) + prob.driver.recording_options['record_objectives'] = True + prob.driver.recording_options['record_desvars'] = True + prob.driver.recording_options['includes'] = ['aoa', 'solver.cl', 'solver.cd', 'mesh.x_aero0', 'geometry.x_aero_out'] + + prob.setup() + #prob.check_partials(includes=['cl', 'aoa'], compact_print=True) + prob.run_driver() + + # Instantiate your CaseReader + cr = om.CaseReader(prob.get_reports_dir() / "../BlasterSolver_file.sql") + # Isolate "problem" as your source + driver_cases = cr.list_cases('driver', out_stream=None) + + # Get evolution of f and x + aoa_evol = [] + cl_evol = [] + cd_evol = [] + shape = [] + shape0 = [] + + for c in driver_cases: + case = cr.get_case(c) + aoa_evol.append(case.get_val('aoa')[0]*180/np.pi) + cl_evol.append(case.get_val('solver.cl')[0]) + cd_evol.append(case.get_val('solver.cd')[0]) + shape0.append(case.get_val('mesh.x_aero0')) + shape.append(case.get_val('geometry.x_aero_out')) + + print('success') + print('aoa:', prob['aoa']*np.pi/180) + print('cl:', prob['solver.cl']) + print('cd:', prob['solver.cd']) + print('obj:', prob['obj']) + + # Recover selig format + shape0_selig = np.array(shape0[0]) + shape1_selig = np.array(shape[-1]) + for i, val in enumerate(prob.model.solver.isol.vBnd[0][0].connectListNodes): + for j in range(prob.model.solver.isol.getnDim()): + shape0_selig[i*prob.model.solver.isol.getnDim()+j] = shape0[0][val*prob.model.solver.isol.getnDim()+j] + shape1_selig[i*prob.model.solver.isol.getnDim()+j] = shape[-1][val*prob.model.solver.isol.getnDim()+j] + from matplotlib import pyplot as plt + + plt.plot(shape0_selig[0::2], shape0_selig[1::2], '--', color='grey', label='Initial shape - cl = {:.2f} - cd = {:.5f}'.format(cl_evol[0], cd_evol[0])) + plt.plot(shape1_selig[0::2], shape1_selig[1::2], '-', color='blue', label='Final shape - cl = {:.2f} - cd = {:.5f}'.format(cl_evol[-1], cd_evol[-1])) + plt.legend() + plt.axis('equal') + plt.show() + quit() + + + + plt.plot(shape0[0][::2], shape0[0][1::2], 'x', color='grey') + for xx in shape: + plt.plot(xx[::2], xx[1::2], 'x', color='blue') + plt.axis('equal') + plt.show() + quit() + + +if __name__ == "__main__": + main() diff --git a/blast/src/DBoundaryLayer.cpp b/blast/src/DBoundaryLayer.cpp index ad18c9951b515213bb3107d8bd1db034b88aeaba..5f84a6996231bd1bd69a902c5118b11fde9606d0 100644 --- a/blast/src/DBoundaryLayer.cpp +++ b/blast/src/DBoundaryLayer.cpp @@ -25,18 +25,51 @@ BoundaryLayer::~BoundaryLayer() delete closSolver; } -void BoundaryLayer::setMesh(std::vector<double> _x, std::vector<double> _y, std::vector<double> _z, double _chord, double xMin) +void BoundaryLayer::setMesh(std::vector<double> _x, std::vector<double> _y, std::vector<double> _z, double _chord, double _xMin, std::vector<int> const _rows, std::vector<int> const _no) { if (_x.size() != _y.size() || _x.size() != _z.size()) throw std::runtime_error("Mesh coordinates are not consistent."); nNodes = _x.size(); nElms = nNodes - 1; + if (_rows.size() == 0) + { + rows.resize(0); + for (size_t i = 0; i < nNodes; ++i) + rows.push_back(static_cast<int>(i)); + } + else if (_rows.size() != nNodes) + throw std::runtime_error("Row vector is not consistent."); + else + { + rows = _rows; + } + // Elements + if (_no.size() == 0) + { + no.resize(0); + for (size_t i = 0; i < nElms; ++i) + no.push_back(static_cast<int>(i)); + } + else if (_no.size() != nElms) + throw std::runtime_error("No vector is not consistent."); + else + { + no = _no; + } + x = _x; y = _y; z = _z; chord = _chord; + xMin = _xMin; // Compute the local coordinate. + this->computeLocalCoordinates(); + this->reset(); +} + +void BoundaryLayer::computeLocalCoordinates() +{ loc.resize(nNodes, 0.); alpha.resize(nElms, 0.); dx.resize(nElms, 0.); @@ -55,8 +88,6 @@ void BoundaryLayer::setMesh(std::vector<double> _x, std::vector<double> _y, std: xoc.resize(nNodes, 0.); for (size_t iPoint = 0; iPoint < nNodes; ++iPoint) xoc[iPoint] = (x[iPoint] - xMin) / chord; - - this->reset(); } void BoundaryLayer::reset() @@ -94,7 +125,7 @@ void BoundaryLayer::reset() */ void BoundaryLayer::setStagnationBC(double Re) { - double dv0 = (vt[1] - vt[0]) / (loc[1] - loc[0]); + double dv0 = std::abs((vt[1] - vt[0]) / (loc[1] - loc[0])); if (dv0 > 0) u[0] = sqrt(0.075 / (Re * dv0)); // Theta else @@ -173,11 +204,9 @@ void BoundaryLayer::setWakeBC(double Re, std::vector<double> UpperCond, std::vec */ void BoundaryLayer::computeBlowingVelocity() { - //deltaStar[0] = u[0] * u[1]; blowingVelocity.resize(nNodes - 1, 0.); for (size_t i = 1; i < nNodes; ++i) { - //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])) { diff --git a/blast/src/DBoundaryLayer.h b/blast/src/DBoundaryLayer.h index 030bb1d2d40e6015c6745b7bbc93a9a60b4aa3a7..5f7adb2b930c5ac2fdd6696da7ab1b975732c8b7 100644 --- a/blast/src/DBoundaryLayer.h +++ b/blast/src/DBoundaryLayer.h @@ -26,16 +26,19 @@ public: std::string name; ///< Name of the region. // Mesh - size_t nNodes; ///< Number of points in the domain. - size_t nElms; ///< Number of cells in the domain. - std::vector<double> x; ///< x coordinate in the global frame of reference. - std::vector<double> y; ///< y coordinate in the global frame of reference. - std::vector<double> z; ///< z coordinate in the global frame of reference. - std::vector<double> xoc; ///< x/c coordinate in the global frame of reference. - std::vector<double> loc; ///< xi coordinate in the local frame of reference. - std::vector<double> dx; ///< Cell size. - std::vector<double> alpha; ///< Angle of the cell wrt the x axis of the global frame of reference. - double chord; ///< Chord of the section. + size_t nNodes; ///< Number of points in the domain. + size_t nElms; ///< Number of cells in the domain. + std::vector<double> x; ///< x coordinate in the global frame of reference. + std::vector<double> y; ///< y coordinate in the global frame of reference. + std::vector<double> z; ///< z coordinate in the global frame of reference. + std::vector<double> xoc; ///< x/c coordinate in the global frame of reference. + std::vector<double> loc; ///< xi coordinate in the local frame of reference. + std::vector<int> rows; ///< Reference numbers of the nodes. + std::vector<int> no; ///< Reference numbers of the nodes. + std::vector<double> dx; ///< Cell size. + std::vector<double> alpha; ///< Angle of the cell wrt the x axis of the global frame of reference. + double chord; ///< Chord of the section. + double xMin; ///< Minimum x coordinate of the mesh (for local coordinates computation). // Boundary layer variables. std::vector<double> u; ///< Solution vector (theta, H, N, ue, Ct). @@ -81,7 +84,8 @@ public: double getMaxMach() const { return *std::max_element(Me.begin(), Me.end()); }; // Setters - void setMesh(std::vector<double> const _x, std::vector<double> const y, std::vector<double> const z, double const _chord, double const xMin); + void setMesh(std::vector<double> const _x, std::vector<double> const y, std::vector<double> const z, double const _chord, double const _xMin, std::vector<int> const _rows=std::vector<int>(0), std::vector<int> const _no=std::vector<int>(0)); + void computeLocalCoordinates(); void setMe(std::vector<double> const _Me) {if (_Me.size() != nNodes) throw std::runtime_error("Mach number vector is not consistent."); Me = _Me;}; void setVt(std::vector<double> const _vt) {if (_vt.size() != nNodes) throw std::runtime_error("Velocity vector is not consistent."); vt = _vt;}; void setRhoe(std::vector<double> const _rhoe) {if (_rhoe.size() != nNodes) throw std::runtime_error("Density vector is not consistent."); rhoe = _rhoe;}; diff --git a/blast/src/DClosures.cpp b/blast/src/DClosures.cpp index c411d1c41c3de7937bfdf85b382e138898481999..2cf9f44bc79ed170eb5a9a32bd3a04bb6875c8e8 100644 --- a/blast/src/DClosures.cpp +++ b/blast/src/DClosures.cpp @@ -169,8 +169,8 @@ void Closures::turbulentClosures(std::vector<double> &closureVars, double theta, if (us > 0.95) us = 0.98; n = 1.0; - cf = (1 / Fc) * (0.3 * std::exp(arg) * std::pow(logRt / 2.3026, (-1.74 - 0.31 * Hk)) + 0.00011 * (std::tanh(4 - (Hk / 0.875)) - 1)); - Hkc = std::max(Hk - 1 - 18 / Ret, 0.01); + cf = (1 / Fc) * (0.3 * std::exp(arg) * std::pow(logRt / 2.3026, (-1.74 - 0.31 * Hk)) + 0.00011 * (std::tanh(4. - (Hk / 0.875)) - 1.)); + Hkc = std::max(Hk - 1. - 18. / Ret, 0.01); // Dissipation coefficient. Hmin = 1 + 2.1 / std::log(Ret); Fl = (Hk - 1) / (Hmin - 1); diff --git a/blast/src/DCoupledAdjoint.cpp b/blast/src/DCoupledAdjoint.cpp index 8139d32f12a89a518de264b9377b4f9cb1522243..8ea9318add45b1a8ec63c746970f578903e819fb 100644 --- a/blast/src/DCoupledAdjoint.cpp +++ b/blast/src/DCoupledAdjoint.cpp @@ -15,6 +15,7 @@ */ #include "DCoupledAdjoint.h" +#include "DDriver.h" #include "wAdjoint.h" #include "wNewton.h" #include "wSolver.h" @@ -23,9 +24,12 @@ #include "wFluid.h" #include "wMshData.h" #include "wNode.h" +#include "wBlowingResidual.h" #include "wLinearSolver.h" #include "wGmres.h" +#include "wCache.h" +#include "wGauss.h" #include <Eigen/Sparse> #include<Eigen/SparseLU> @@ -35,6 +39,7 @@ #include <deque> #include <algorithm> #include <iomanip> +#include <chrono> using namespace blast; @@ -58,53 +63,74 @@ CoupledAdjoint::CoupledAdjoint(std::shared_ptr<dart::Adjoint> _iadjoint, std::sh 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 + size_t nNs = 0; + for (auto bBC : isol->pbl->bBCs) + nNs += bBC->nodes.size(); // Number of surface nodes + nNs += 1; // Duplicate stagnation point + size_t nEs = 0; + for (auto bBC : isol->pbl->bBCs) + nEs += bBC->uE.size(); // Number of blowing elements 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); + dRdStar_phi = Eigen::SparseMatrix<double, Eigen::RowMajor>(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); + dRdStar_M = Eigen::SparseMatrix<double, Eigen::RowMajor>(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); + dRdStar_rho = Eigen::SparseMatrix<double, Eigen::RowMajor>(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); + dRdStar_v = Eigen::SparseMatrix<double, Eigen::RowMajor>(nNs, nDim*nNs); dRblw_v = Eigen::SparseMatrix<double, Eigen::RowMajor>(nEs, nDim*nNs); + dRphi_dStar = Eigen::SparseMatrix<double, Eigen::RowMajor>(nNd, nNs); + dRM_dStar = Eigen::SparseMatrix<double, Eigen::RowMajor>(nNs, nNs); + dRrho_dStar = Eigen::SparseMatrix<double, Eigen::RowMajor>(nNs, nNs); + dRv_dStar = Eigen::SparseMatrix<double, Eigen::RowMajor>(nDim*nNs, nNs); + dRdStar_dStar = Eigen::SparseMatrix<double, Eigen::RowMajor>(nNs, nNs); + dRblw_dStar = Eigen::SparseMatrix<double, Eigen::RowMajor>(nEs, 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); + dRdStar_blw = Eigen::SparseMatrix<double, Eigen::RowMajor>(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); + dRdStar_x = Eigen::SparseMatrix<double, Eigen::RowMajor>(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(); + + // Gradient wrt objective function + dRphi_AoA = Eigen::VectorXd::Zero(nNd); // Objective functions + // Cl = f(phi) & Cd = Cdp + Cdf = f(phi, M, v) dCl_phi = Eigen::VectorXd::Zero(nNd); dCd_phi = Eigen::VectorXd::Zero(nNd); + dCd_M = Eigen::VectorXd::Zero(nNs); + dCd_v = Eigen::VectorXd::Zero(nDim*nNs); + dCd_dStar = Eigen::VectorXd::Zero(nNs); // Angle of attack dCl_AoA = 0.0; @@ -115,7 +141,8 @@ void CoupledAdjoint::reset() // 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); + dCdp_x = Eigen::VectorXd::Zero(nDim*nNd); + dCdf_x = Eigen::VectorXd::Zero(nDim*nNd); tdCl_x.resize(nNd, Eigen::Vector3d::Zero()); tdCd_x.resize(nNd, Eigen::Vector3d::Zero()); @@ -165,22 +192,43 @@ void CoupledAdjoint::buildAdjointMatrix(std::vector<std::vector<Eigen::SparseMat void CoupledAdjoint::run() { + tms["0-adj_total"].start(); + tms["1-adj_inviscid"].start(); gradientswrtInviscidFlow(); + tms["1-adj_inviscid"].stop(); + tms["2-adj_mach"].start(); + gradientswrtMachNumber(); + tms["2-adj_mach"].stop(); + tms["3-adj_density"].start(); + gradientswrtDensity(); + tms["3-adj_density"].stop(); + tms["4-adj_velocity"].start(); + gradientswrtVelocity(); + tms["4-adj_velocity"].stop(); + tms["5-adj_dStar"].start(); + gradientswrtDeltaStar(); + tms["5-adj_dStar"].stop(); + tms["6-adj_blw"].start(); gradientswrtBlowingVelocity(); + tms["6-adj_blw"].stop(); + tms["7-adj_aoa"].start(); gradientwrtAoA(); + tms["7-adj_aoa"].stop(); + tms["8-adj_mesh"].start(); gradientwrtMesh(); + tms["8-adj_mesh"].stop(); transposeMatrices(); - // Adjoint matrix - + tms["9-build"].start(); // 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} + {&dRphi_phi, &dRM_phi, &dRrho_phi, &dRv_phi, &dRdStar_phi, &dRblw_phi}, + {&dRphi_M, &dRM_M, &dRrho_M, &dRv_M, &dRdStar_M, &dRblw_M}, + {&dRphi_rho, &dRM_rho, &dRrho_rho, &dRv_rho, &dRdStar_rho, &dRblw_rho}, + {&dRphi_v, &dRM_v, &dRrho_v, &dRv_v, &dRdStar_v, &dRblw_v}, + {&dRphi_dStar, &dRM_dStar, &dRrho_dStar, &dRv_dStar, &dRdStar_dStar, &dRblw_dStar}, + {&dRphi_blw, &dRM_blw, &dRrho_blw, &dRv_blw, &dRdStar_blw, &dRblw_blw} }; int rows = 0; int cols = 0; @@ -188,60 +236,118 @@ void CoupledAdjoint::run() 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); + tms["9-build"].stop(); + + size_t phiIdx = 0; + size_t machIdx = dRphi_phi.cols(); + size_t rhoIdx = machIdx + dRM_M.cols(); + size_t vIdx = rhoIdx + dRrho_rho.cols(); + size_t dStarIdx = vIdx + dRv_v.cols(); + size_t blwIdx = dStarIdx + dRdStar_dStar.cols(); //***** Gradient wrt angle of attack *****// //****************************************// // CL + tms["10-solveCxAoA"].start(); std::vector<double> rhsCl(A_adjoint.cols(), 0.0); - for (auto i = 0; i<dCl_phi.size(); ++i) - rhsCl[i] = dCl_phi[i]; + for (auto i = phiIdx; i<phiIdx+dCl_phi.size(); ++i) + rhsCl[i] = dCl_phi[i]; // Only dCl_dphi is non-zero 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; + tdCl_AoA = dCl_AoA - dRphi_AoA.transpose()*lambdaCl.segment(phiIdx, dRphi_phi.cols()); // 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); + std::vector<double> rhsCd(A_adjoint.cols(), 0.0); + int jj = 0; + for (auto i = phiIdx; i<phiIdx+dCd_phi.size(); ++i) + { + rhsCd[i] = dCd_phi[jj]; + jj++; + } + jj = 0; + for (auto i = machIdx; i<machIdx+dCd_M.size(); ++i) + { + rhsCd[i] = dCd_M[jj]; + jj++; + } + jj = 0; + for (auto i = vIdx; i<vIdx+dCd_v.size(); ++i) + { + rhsCd[i] = dCd_v[jj]; + jj++; + } + jj = 0; + for (auto i = dStarIdx; i<dStarIdx+dCd_dStar.size(); ++i) + { + rhsCd[i] = dCd_dStar[jj]; + jj++; + } + 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()); - // tdCd_AoA = dCd_AoA - dRphi_AoA.transpose()*lambdaCdPhi; // Total gradient + alinsol->compute(A_adjoint, Eigen::Map<Eigen::VectorXd>(rhsCd_.data(), rhsCd_.size()), lambdaCd_); + + // Total gradient + tdCd_AoA = dCd_AoA - dRphi_AoA.transpose()*lambdaCd.segment(phiIdx, dRphi_phi.cols()); // Total gradient + tms["10-solveCxAoA"].stop(); //***** 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" + // Ck (cL, cd) + // Solution lambdaCl_x of (from augmented Lagrangian, eqn d/dx ) + // "dCk_x + dRphi_x * lambdaCk_phi + dRdStar_x * lambdaCk_x + dRblw_x * lambdaCk_blw + dRx_x * lambdaCk_x = 0" + + tms["12-solveCxMesh"].start(); + Eigen::VectorXd rhsCl_x = dCl_x; + rhsCl_x -= dRphi_x.transpose() * lambdaCl.segment(phiIdx, dRphi_phi.cols()); // Potential contribution + rhsCl_x -= dRM_x.transpose() * lambdaCl.segment(machIdx, dRM_M.cols()); // Mach number contribution + rhsCl_x -= dRrho_x.transpose() * lambdaCl.segment(rhoIdx, dRrho_rho.cols()); // Density contribution + rhsCl_x -= dRv_x.transpose() * lambdaCl.segment(vIdx, dRv_v.cols()); // Velocity contribution + rhsCl_x -= dRdStar_x.transpose() * lambdaCl.segment(dStarIdx, dRdStar_dStar.cols()); // Displacement thickness contribution + rhsCl_x -= dRblw_x.transpose() * lambdaCl.segment(blwIdx, dRblw_blw.cols()); // Blowing contribution + 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_); + Eigen::VectorXd rhsCd_x = dCdp_x; // Pressure drag coefficient contribution + rhsCd_x += dCdf_x; // Friction drag coefficient contribution + rhsCd_x -= dRphi_x.transpose() * lambdaCd.segment(phiIdx, dRphi_phi.cols()); // Potential contribution + rhsCd_x -= dRM_x.transpose() * lambdaCd.segment(machIdx, dRM_M.cols()); // Mach number contribution + rhsCd_x -= dRrho_x.transpose() * lambdaCd.segment(rhoIdx, dRrho_rho.cols()); // Density contribution + rhsCd_x -= dRv_x.transpose() * lambdaCd.segment(vIdx, dRv_v.cols()); // Velocity contribution + rhsCd_x -= dRdStar_x.transpose() * lambdaCd.segment(dStarIdx, dRdStar_dStar.cols()); // Displacement thickness contribution + rhsCd_x -= dRblw_x.transpose() * lambdaCd.segment(blwIdx, dRblw_blw.cols()); // Blowing contribution + + Eigen::VectorXd lambdaCd_x = Eigen::VectorXd::Zero(isol->pbl->nDim * isol->pbl->msh->nodes.size()); + Eigen::Map<Eigen::VectorXd> lambdaCd_x_(lambdaCd_x.data(), lambdaCd_x.size()); + alinsol->compute(dRx_x.transpose(), Eigen::Map<Eigen::VectorXd>(rhsCd_x.data(), rhsCd_x.size()), lambdaCd_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]; + tdCd_x[n->row](m) = lambdaCd_x[isol->pbl->nDim * (n->row) + m]; } } + tms["12-solveCxMesh"].stop(); + //std::cout << tms << std::endl; } +/** + * @brief Compute all the gradients wrt the inviscid flow (phi) in the domain + * Non-zero are: dRphi_phi, dRM_phi, dRrho_phi, dRv_phi + * Zeros are: dRdStar_phi, dRblw_phi, dRx_phi + */ void CoupledAdjoint::gradientswrtInviscidFlow() { //**** dRphi_phi ****// @@ -251,7 +357,7 @@ void CoupledAdjoint::gradientswrtInviscidFlow() auto pbl = isol->pbl; // Finite difference std::vector<double> Phi_save = isol->phi; // Differential of the independent variable (phi) - double deltaPhi = 1e-6; // perturbation + double deltaPhi = 1.e-8; // perturbation std::vector<Eigen::Triplet<double>> tripletsdM; std::vector<Eigen::Triplet<double>> tripletsdrho; @@ -261,30 +367,39 @@ void CoupledAdjoint::gradientswrtInviscidFlow() 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)); + size_t jj = 0; + for (auto sec: vsol->sections[0]){ + int iReg = 0; + if (sec->name == "wake") + iReg = 1; + else if (sec->name == "upper" || sec->name == "lower") + iReg = 0; + else + throw std::runtime_error("gradientswrtInviscidFlow: Unknown section name"); + for (size_t iNode = 0; iNode < sec->nNodes; ++iNode){ + tbox::Node* srfNode = pbl->bBCs[iReg]->nodes[sec->rows[iNode]]; + 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()); - jj++; + // Form triplets + // Minus because M = f(phi) and therefore RM = M - f(phi) = 0 + 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]; @@ -296,111 +411,619 @@ void CoupledAdjoint::gradientswrtInviscidFlow() // Remove zeros dRM_phi.prune(0.); dRrho_phi.prune(0.); dRv_phi.prune(0.); - //**** dCl_phi, dCd_phi ****// + // // Analytical + // std::vector<Eigen::Triplet<double>> tripletsdM_an; + + // size_t jj = 0; + // int iReg = 0; + // for (auto sec : vsol->sections[0]) + // { + // if (sec->name == "wake") + // iReg = 1; + // else if (sec->name == "upper" || sec->name == "lower") + // iReg = 0; + // else + // throw std::runtime_error("gradientswrtInviscidFlow: Unknown section name"); + // for (size_t iNode = 0; iNode < sec->nNodes; ++iNode) + // { + // tbox::Node* srfNode = pbl->bBCs[iReg]->nodes[sec->rows[iNode]]; + // for (auto e: pbl->fluid->neMap[srfNode]) + // { + // // Get pre-computed values + // tbox::Cache &cache = e->getVCache(); + // tbox::Gauss &gauss = cache.getVGauss(); + // Eigen::VectorXd grad = Eigen::VectorXd::Zero(e->nodes.size()); + // for (size_t k = 0; k < gauss.getN(); ++k) + // { + // // Gauss point and determinant + // double wdj = gauss.getW(k) * e->getDetJ(k); + // // Shape functions and solution gradient + // Eigen::MatrixXd const &dSf = e->getJinv(k) * cache.getDsf(k); + // Eigen::VectorXd dPhi = e->computeGradient(isol->phi, k); + // grad += isol->pbl->fluid->mach->evalGrad(*e, isol->phi, k) * dSf.transpose() * dPhi; + // } + // for (size_t k = 0; k < e->nodes.size(); ++k) + // { + // tripletsdM_an.push_back(Eigen::Triplet<double>(jj, e->nodes[k]->row, -grad(k)/e->nodes.size())); + // } + // } + // ++jj; + // } + // } + // Eigen::SparseMatrix<double, Eigen::RowMajor> dRM_phi_an(dRM_phi.rows(), dRM_phi.cols()); + // dRM_phi_an.setFromTriplets(tripletsdM_an.begin(), tripletsdM_an.end()); + // dRM_phi_an.prune(0.); + // writeMatrixToFile(dRM_phi_an, "dRM_phi_an.txt"); + // writeMatrixToFile(dRM_phi, "dRM_phi.txt"); + // writeMatrixToFile((dRM_phi_an - dRM_phi), "dif.txt"); + // std::cout << "written" << std::endl; + // std::cout << dRM_phi_an - dRM_phi << std::endl; + + // throw; + + + //**** partials 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()); +/** + * @brief Compute all the gradients wrt the Mach number on the surface + * Non-zero are: dRM_M, dRdStar_M + * Zeros are: dRphi_M, dRrho_M, dRv_M, dRblw_M, dRx_M + */ +void CoupledAdjoint::gradientswrtMachNumber() +{ + /**** dRM_M ****/ + dRM_M.setIdentity(); + + //**** dRdStar_M ****// + std::deque<Eigen::Triplet<double>> T; + double delta = 1e-6; + size_t i = 0; + double saveM = 0.; + std::vector<Eigen::VectorXd> ddStar(2, Eigen::VectorXd::Zero(dRdStar_M.cols())); + std::vector<double> dCdf(2, 0.); + for (auto sec: vsol->sections[0]) + for (size_t j = 0; j < sec->nNodes; ++j) + { + saveM = sec->Me[j]; + + sec->Me[j] = saveM + delta; + ddStar[0] = this->runViscous(); + dCdf[0] = vsol->Cdf; + sec->Me[j] = saveM - delta; + ddStar[1] = this->runViscous(); + dCdf[1] = vsol->Cdf; + + sec->Me[j] = saveM; + + for (int k = 0; k < ddStar[0].size(); ++k) + T.push_back(Eigen::Triplet<double>(k, i, -(ddStar[0][k] - ddStar[1][k])/(2.*delta))); + // Collect Cd gradients + dCd_M(i) = (dCdf[0] - dCdf[1])/(2.*delta); + ++i; + } + dRdStar_M.setFromTriplets(T.begin(), T.end()); + dRdStar_M.prune(0.); } -void CoupledAdjoint::gradientswrtDensity(){ - // dRphi_dRho = 0 - // dRM_dRho = 0 - // dRrho_dRho = - // dRv_dRho = 0 - // dRblw_dRho = +/** + * @brief Compute all the gradients wrt the density on the surface + * Non-zero are: dRrho_rho, dRblw_rho + * Zeros are: dRphi_rho, dRM_rho, dRv_rho, dRdStar_rho, dRx_rho + */ +void CoupledAdjoint::gradientswrtDensity() +{ + size_t nNs = 0; + for (auto bBC : isol->pbl->bBCs) + nNs += bBC->nodes.size(); // Number of surface nodes + nNs += 1; // Duplicate stagnation point + size_t nEs = 0; + for (auto bBC : isol->pbl->bBCs) + nEs += bBC->uE.size(); // Number of blowing elements + + //**** dRrho_rho ****// + dRrho_rho.setIdentity(); + + //**** dRblw_rho ****// + std::deque<Eigen::Triplet<double>> T_blw; + int offSetElms = 0; + int offSetNodes = 0; + for (const auto& sec: vsol->sections[0]) + { + std::vector<std::vector<double>> grad = sec->evalGradwrtRho(); + for (size_t i = 0; i < grad.size(); ++i) + for (size_t j = 0; j < grad[i].size(); ++j) + T_blw.push_back(Eigen::Triplet<double>(i + offSetElms, j + offSetNodes, -grad[i][j])); + offSetElms += sec->nElms; + offSetNodes += sec->nNodes; + } + dRblw_rho.setFromTriplets(T_blw.begin(), T_blw.end()); + dRblw_rho.prune(0.); +} + +/** + * @brief Compute all the gradients wrt the velocity on the surface + * Non-zeros are: dRv_v, dRdStar_v, dRblw_v + * Zeros are: dRphi_v, dRM_v, dRrho_v, dRx_v + */ +void CoupledAdjoint::gradientswrtVelocity() +{ + size_t nDim = isol->pbl->nDim; // Number of dimensions of the problem + size_t nNs = 0; + for (auto bBC : isol->pbl->bBCs) + nNs += bBC->nodes.size(); // Number of surface nodes + nNs += 1; // Duplicate stagnation point + size_t nEs = 0; + for (auto bBC : isol->pbl->bBCs) + nEs += bBC->uE.size(); // Number of blowing elements + + /**** dRv_v ****/ + dRv_v.setIdentity(); + + //**** Get velocity****/ + Eigen::MatrixXd v = Eigen::MatrixXd::Zero(nNs, nDim); + size_t i = 0; + int iReg = -1; + for (const auto &sec: vsol->sections[0]) + { + if (sec->name == "wake") + iReg = 1; + else if (sec->name == "upper" || sec->name == "lower") + iReg = 0; + else + throw std::runtime_error("Unknown section name"); + for (size_t j = 0; j < sec->nNodes; ++j) + { + for (size_t idim = 0; idim < nDim; ++idim) + v(i, idim) = isol->U[isol->pbl->bBCs[iReg]->nodes[sec->rows[j]]->row](idim); + ++i; + } + } + + //**** dvt_v (analytical) ****// + Eigen::SparseMatrix<double, Eigen::RowMajor> dVt_v(nNs, nNs * nDim); + std::deque<Eigen::Triplet<double>> T_vt; + + i = 0; + for (const auto& sec : vsol->sections[0]) { + for (size_t j = 0; j < sec->nNodes; ++j) { + for (size_t idim = 0; idim < nDim; ++idim) { + T_vt.push_back(Eigen::Triplet<double>(i, i * nDim + idim, 1 / std::sqrt(v(i, 0) * v(i, 0) + v(i, 1) * v(i, 1)) * v(i, idim))); + } + ++i; + } + } + dVt_v.setFromTriplets(T_vt.begin(), T_vt.end()); + dVt_v.prune(0.); + + //**** dRdStar_vt (finite-difference) ****// + std::deque<Eigen::Triplet<double>> T; + double delta = 1e-6; + i = 0; + double saveM = 0.; + std::vector<Eigen::VectorXd> dvt(2, Eigen::VectorXd::Zero(dRdStar_M.cols())); + std::vector<double> dCdf(2, 0.); + Eigen::VectorXd dCd_vt = Eigen::VectorXd::Zero(dRM_M.cols()); + for (auto sec: vsol->sections[0]) + for (size_t j = 0; j < sec->nNodes; ++j) + { + saveM = sec->vt[j]; + + sec->vt[j] = saveM + delta; + dvt[0] = this->runViscous(); + dCdf[0] = vsol->Cdf; + sec->vt[j] = saveM - delta; + dvt[1] = this->runViscous(); + dCdf[1] = vsol->Cdf; - // dCl_dRho = - // dCd_dRho = + sec->vt[j] = saveM; + + for (int k = 0; k < dvt[0].size(); ++k) + T.push_back(Eigen::Triplet<double>(k, i, -(dvt[0][k] - dvt[1][k])/(2.*delta))); + dCd_vt(i) = (dCdf[0] - dCdf[1])/(2.*delta); + ++i; + } + Eigen::SparseMatrix<double, Eigen::RowMajor> dRdStar_vt(dRdStar_M.rows(), dRdStar_M.cols()); + dRdStar_vt.setFromTriplets(T.begin(), T.end()); + dRdStar_vt.prune(0.); + + //**** dRdStar_v ****// + dRdStar_v = dRdStar_vt * dVt_v; + dRdStar_v.prune(0.); + + //**** dRCdf_v ****// + dCd_v = dCd_vt.transpose() * dVt_v; // [1xnNs] x [nNs x nNs*nDim] = [1 x nNs*nDim] + + //**** dRblw_vt (analytical) ****// + std::deque<Eigen::Triplet<double>> T_blw; + int offSetElms = 0; + int offSetNodes = 0; + for (const auto& sec: vsol->sections[0]) + { + std::vector<std::vector<double>> grad = sec->evalGradwrtVt(); + for (size_t i = 0; i < grad.size(); ++i) + for (size_t j = 0; j < grad[i].size(); ++j) + T_blw.push_back(Eigen::Triplet<double>(i + offSetElms, j + offSetNodes, -grad[i][j])); + offSetElms += sec->nElms; + offSetNodes += sec->nNodes; + } + + Eigen::SparseMatrix<double, Eigen::RowMajor> dRblw_vt(nEs, nNs); + dRblw_vt.setFromTriplets(T_blw.begin(), T_blw.end()); + dRblw_vt.prune(0.); + + //**** dRblw_v ****// + dRblw_v = dRblw_vt * dVt_v; + dRblw_v.prune(0.); } -void CoupledAdjoint::gradientswrtVelocity(){ - // dRphi_dV = 0 - // dRM_dV = 0 - // dRrho_dV = 0 - // dRv_dV = - // dRblw_dV = +/** + * @brief Compute all the gradients wrt the displacement thickness on the surface + * Non-zero are: dRdStar_dStar, dRblw_dStar + * Zeros are: dRphi_dStar, dRM_dStar, dRrho_dStar, dRv_dStar, dRx_dStar + */ +void CoupledAdjoint::gradientswrtDeltaStar() +{ + //**** dRdStar_dStar (finite-difference) ****// + std::deque<Eigen::Triplet<double>> T; + double delta = 1e-6; + double saveDStar = 0.; + std::vector<Eigen::VectorXd> ddstar(2, Eigen::VectorXd::Zero(dRdStar_dStar.cols())); + std::vector<double> dCdf(2, 0.); + size_t i = 0; + for (auto sec: vsol->sections[0]) + for (size_t j = 0; j < sec->nNodes; ++j) + { + saveDStar = sec->deltaStarExt[j]; + + sec->deltaStarExt[j] = saveDStar + delta; + ddstar[0] = this->runViscous(); + dCdf[0] = vsol->Cdf; + sec->deltaStarExt[j] = saveDStar - delta; + ddstar[1] = this->runViscous(); + dCdf[1] = vsol->Cdf; + + sec->deltaStarExt[j] = saveDStar; + + for (int k = 0; k < ddstar[0].size(); ++k) + T.push_back(Eigen::Triplet<double>(k, i, (ddstar[0][k] - ddstar[1][k])/(2.*delta))); + dCd_dStar(i) = (dCdf[0] - dCdf[1])/(2.*delta); + ++i; + } + Eigen::SparseMatrix<double, Eigen::RowMajor> dRdStar_dStar_dum(dRdStar_dStar.rows(), dRdStar_dStar.cols()); + // RdStar = dStar - f(dStar) = 0 + // dRdStar_dStar = 1 - df_dStar (minus is 3 lines below and not in the triplets) + dRdStar_dStar_dum.setFromTriplets(T.begin(), T.end()); + dRdStar_dStar.setIdentity(); + dRdStar_dStar -= dRdStar_dStar_dum; + dRdStar_dStar.prune(0.); - // dCl_dV = - // dCd_dV = + //**** dRblw_dStar (analytical) ****// + std::deque<Eigen::Triplet<double>> T_blw; + int offSetElms = 0; + int offSetNodes = 0; + for (const auto& sec: vsol->sections[0]) + { + std::vector<std::vector<double>> grad = sec->evalGradwrtDeltaStar(); + for (size_t i = 0; i < grad.size(); ++i) + for (size_t j = 0; j < grad[i].size(); ++j) + T_blw.push_back(Eigen::Triplet<double>(i + offSetElms, j + offSetNodes, -grad[i][j])); + offSetElms += sec->nElms; + offSetNodes += sec->nNodes; + } + dRblw_dStar.setFromTriplets(T_blw.begin(), T_blw.end()); + dRblw_dStar.prune(0.); } -void CoupledAdjoint::gradientswrtBlowingVelocity(){ - dRphi_blw = iadjoint->getRu_Blw(); +/** + * @brief Compute all the gradients wrt the blowing velocity + * Non-zero are: dRphi_blw, dRblw_blw + * Zeros are: dRM_blw, dRrho_blw, dRv_blw, dRdStar_blw, dRx_blw + */ +void CoupledAdjoint::gradientswrtBlowingVelocity() +{ + /**** dRphi_blw ****/ + std::deque<Eigen::Triplet<double>> T; + + size_t jj = 0; + int iReg = 0; + for (auto sec: vsol->sections[0]){ + if (sec->name == "wake") + iReg = 1; + else if (sec->name == "upper" || sec->name == "lower") + iReg = 0; + else + throw std::runtime_error("Unknown section name"); + for (size_t iElm = 0; iElm < sec->nElms; ++iElm){ + auto blowingElement = isol->pbl->bBCs[iReg]->uE[sec->no[iElm]].first; + Eigen::VectorXd be = dart::BlowingResidual::buildGradientBlowing(*blowingElement); + for (size_t ii = 0; ii < blowingElement->nodes.size(); ii++) + { + tbox::Node *nodi = blowingElement->nodes[ii]; + T.push_back(Eigen::Triplet<double>(isol->rows[nodi->row], jj, be(ii))); + } + ++jj; + } + } + dRphi_blw.setFromTriplets(T.begin(), T.end()); + dRphi_blw.prune(0.); + dRphi_blw.makeCompressed(); - // All others are zero. + /**** dRblw_blw ****/ + dRblw_blw.setIdentity(); } +/** + * @brief Compute all the gradients wrt the angle of attack + * Non-zero are: dRphi_AoA + * Zeros are: dRM_AoA, dRrho_AoA, dRv_AoA, dRdStar_AoA, dRblw_AoA, dRx_AoA + */ 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. } +/** + * @brief Compute all the gradients wrt the mesh coordinates + * Non-zero are: dRphi_x, dRM_x, dRrho_x, dRv_x, dRdStar_x, dRblw_x, dRx_x + * Zeros are: - + */ void CoupledAdjoint::gradientwrtMesh(){ + + /**** dCk_dx ****/ 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()); + dCdp_x = Eigen::Map<Eigen::VectorXd>(dCx_x[1].data(), dCx_x[1].size()); + /**** dRphi_x ****/ dRphi_x = iadjoint->getRu_X(); + /**** dRx_x ****/ dRx_x = iadjoint->getRx_X(); + + /**** dRdStar_x ****/ + std::deque<Eigen::Triplet<double>> T_dStar_x; + double delta = 1e-8; + size_t i = 0; + int iReg = 0; + double saveX = 0.; + std::vector<Eigen::VectorXd> ddStar(2, Eigen::VectorXd::Zero(dRdStar_M.cols())); + std::vector<double> dCdf(2, 0.); + for (auto sec: vsol->sections[0]) + { + if (sec->name == "wake") + iReg = 1; + else if (sec->name == "upper" || sec->name == "lower") + iReg = 0; + else + throw std::runtime_error("Unknown section name"); + for (size_t j = 0; j < sec->nNodes; ++j) + { + int rowj = isol->pbl->bBCs[iReg]->nodes[sec->rows[j]]->row; + // x + saveX = sec->x[j]; + + sec->x[j] = saveX + delta; + sec->computeLocalCoordinates(); + sec->xxExt = sec->loc; + ddStar[0] = this->runViscous(); + dCdf[0] = vsol->Cdf; + sec->x[j] = saveX - delta; + sec->computeLocalCoordinates(); + sec->xxExt = sec->loc; + ddStar[1] = this->runViscous(); + dCdf[1] = vsol->Cdf; + + sec->x[j] = saveX; + + for (int k = 0; k < ddStar[0].size(); ++k) + T_dStar_x.push_back(Eigen::Triplet<double>(k, rowj*isol->pbl->nDim+0, -(ddStar[0][k] - ddStar[1][k])/(2.*delta))); + dCdf_x(rowj*isol->pbl->nDim+0) += (dCdf[0] - dCdf[1])/(2.*delta); + // y + saveX = sec->y[j]; + + sec->y[j] = saveX + delta; + sec->computeLocalCoordinates(); + sec->xxExt = sec->loc; + ddStar[0] = this->runViscous(); + dCdf[0] = vsol->Cdf; + sec->y[j] = saveX - delta; + sec->computeLocalCoordinates(); + sec->xxExt = sec->loc; + ddStar[1] = this->runViscous(); + dCdf[1] = vsol->Cdf; + + sec->y[j] = saveX; + + for (int k = 0; k < ddStar[0].size(); ++k) + T_dStar_x.push_back(Eigen::Triplet<double>(k, rowj*isol->pbl->nDim+1, -(ddStar[0][k] - ddStar[1][k])/(2.*delta))); + dCdf_x(rowj*isol->pbl->nDim+1) += (dCdf[0] - dCdf[1])/(2.*delta); + ++i; + } + } + dRdStar_x.setFromTriplets(T_dStar_x.begin(), T_dStar_x.end()); + dRdStar_x.prune(0.); + + /**** dRblw_x ****/ + std::deque<Eigen::Triplet<double>> T_blw_x; + int offSetElms = 0; + for (const auto& sec: vsol->sections[0]) + { + if (sec->name == "wake") + iReg = 1; + else if (sec->name == "upper" || sec->name == "lower") + iReg = 0; + else + throw std::runtime_error("Unknown section name"); + std::vector<std::vector<double>> gradX = sec->evalGradwrtX(); + std::vector<std::vector<double>> gradY = sec->evalGradwrtY(); + for (size_t i = 0; i < gradX.size(); ++i) + for (size_t j = 0; j < gradX[i].size(); ++j) + { + int rowj = isol->pbl->bBCs[iReg]->nodes[sec->rows[j]]->row; + T_blw_x.push_back(Eigen::Triplet<double>(i + offSetElms, rowj*isol->pbl->nDim+0, -gradX[i][j])); + T_blw_x.push_back(Eigen::Triplet<double>(i + offSetElms, rowj*isol->pbl->nDim+1, -gradY[i][j])); + } + offSetElms += sec->nElms; + } + dRblw_x.setFromTriplets(T_blw_x.begin(), T_blw_x.end()); + dRblw_x.prune(0.); + + /**** dRM_x, dRrho_x, dRv_x ****/ + double delta_x = 1e-8; + auto pbl = isol->pbl; + std::vector<Eigen::Triplet<double>> tripletsdM; + std::vector<Eigen::Triplet<double>> tripletsdrho; + std::vector<Eigen::Triplet<double>> tripletsdv; + + for (auto n : pbl->msh->nodes){ + for (int idim = 0; idim < isol->pbl->nDim; ++idim){ + // Modify node position + double save = n->pos[idim]; + n->pos[idim] = save + delta_x; + for (auto e: pbl->msh->elems) + if (e->type() == tbox::ElType::LINE2 || e->type() == tbox::ElType::TRI3) e->update(); + + // Recompute the quantities on surface nodes. + size_t jj = 0; + for (auto sec: vsol->sections[0]){ + int iReg = 0; + if (sec->name == "wake") + iReg = 1; + else if (sec->name == "upper" || sec->name == "lower") + iReg = 0; + else + throw std::runtime_error("gradientswrtInviscidFlow: Unknown section name"); + for (size_t iNode = 0; iNode < sec->nNodes; ++iNode){ + tbox::Node* srfNode = pbl->bBCs[iReg]->nodes[sec->rows[iNode]]; + 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, isol->phi, 0); + drho += pbl->fluid->rho->eval(*e, isol->phi, 0); + Eigen::VectorXd grad = e->computeGradient(isol->phi, 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 + // Minus because M = f(phi) and therefore RM = M - f(phi) = 0 + tripletsdM.push_back(Eigen::Triplet<double>(jj, n->row*pbl->nDim + idim, -(dM - isol->M[srfNode->row])/delta_x)); + tripletsdrho.push_back(Eigen::Triplet<double>(jj, n->row*pbl->nDim + idim, -(drho - isol->rho[srfNode->row])/delta_x)); + for (int i = 0; i < pbl->nDim; ++i) + tripletsdv.push_back(Eigen::Triplet<double>(jj*pbl->nDim + i, n->row*pbl->nDim + idim, -(dv(i) - isol->U[srfNode->row](i))/delta_x)); + jj++; + } + } + // Reset node pos + n->pos[idim] = save; + } + } + // Fill matrices with gradients + dRM_x.setFromTriplets(tripletsdM.begin(), tripletsdM.end()); + dRrho_x.setFromTriplets(tripletsdrho.begin(), tripletsdrho.end()); + dRv_x.setFromTriplets(tripletsdv.begin(), tripletsdv.end()); + // Remove zeros + dRM_x.prune(0.); dRrho_x.prune(0.); dRv_x.prune(0.); } -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 +void CoupledAdjoint::setdRdStar_M(std::vector<std::vector<double>> _dRdStar_dM){ 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])); + for (size_t i = 0; i < _dRdStar_dM.size(); i++){ + for (size_t j = 0; j < _dRdStar_dM[i].size(); j++){ + triplets.push_back(Eigen::Triplet<double>(i, j, _dRdStar_dM[i][j])); } } // Set matrix - dRblw_M.setFromTriplets(triplets.begin(), triplets.end()); - dRblw_M.prune(0.); + dRdStar_M.setFromTriplets(triplets.begin(), triplets.end()); + dRdStar_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 + +void CoupledAdjoint::setdRdStar_v(std::vector<std::vector<double>> _dRdStar_dv){ 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])); + for (size_t i = 0; i < _dRdStar_dv.size(); i++){ + for (size_t j = 0; j < _dRdStar_dv[i].size(); j++){ + triplets.push_back(Eigen::Triplet<double>(i, j, _dRdStar_dv[i][j])); } } // Set matrix - dRblw_v.setFromTriplets(triplets.begin(), triplets.end()); - dRblw_v.prune(0.); + dRdStar_v.setFromTriplets(triplets.begin(), triplets.end()); + dRdStar_v.prune(0.); +} + +void CoupledAdjoint::setdRdStar_dStar(std::vector<std::vector<double>> _dRdStar_dStar){ + std::vector<Eigen::Triplet<double>> triplets; + for (size_t i = 0; i < _dRdStar_dStar.size(); i++){ + for (size_t j = 0; j < _dRdStar_dStar[i].size(); j++){ + triplets.push_back(Eigen::Triplet<double>(i, j, _dRdStar_dStar[i][j])); + } + } + // Set matrix + dRdStar_dStar.setFromTriplets(triplets.begin(), triplets.end()); + dRdStar_dStar.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 + +void CoupledAdjoint::setdRdStar_x(std::vector<std::vector<double>> _dRdStar_x){ 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])); + for (size_t i = 0; i < _dRdStar_x.size(); i++){ + for (size_t j = 0; j < _dRdStar_x[i].size(); j++){ + triplets.push_back(Eigen::Triplet<double>(i, j, _dRdStar_x[i][j])); + } + } + // Set matrix + dRdStar_x.setFromTriplets(triplets.begin(), triplets.end()); + dRdStar_x.prune(0.); +} + +void CoupledAdjoint::setdRblw_rho(std::vector<std::vector<double>> _dRblw_rho){ + std::vector<Eigen::Triplet<double>> triplets; + for (size_t i = 0; i < _dRblw_rho.size(); i++){ + for (size_t j = 0; j < _dRblw_rho[i].size(); j++){ + triplets.push_back(Eigen::Triplet<double>(i, j, _dRblw_rho[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 + +void CoupledAdjoint::setdRblw_v(std::vector<std::vector<double>> _dRblw_v){ + std::vector<Eigen::Triplet<double>> triplets; + for (size_t i = 0; i < _dRblw_v.size(); i++){ + for (size_t j = 0; j < _dRblw_v[i].size(); j++){ + triplets.push_back(Eigen::Triplet<double>(i, j, _dRblw_v[i][j])); + } + } + // Set matrix + dRblw_v.setFromTriplets(triplets.begin(), triplets.end()); + dRblw_v.prune(0.); +} + +void CoupledAdjoint::setdRblw_dStar(std::vector<std::vector<double>> _dRblw_dStar){ + std::vector<Eigen::Triplet<double>> triplets; + for (size_t i = 0; i < _dRblw_dStar.size(); i++){ + for (size_t j = 0; j < _dRblw_dStar[i].size(); j++){ + triplets.push_back(Eigen::Triplet<double>(i, j, _dRblw_dStar[i][j])); + } + } + // Set matrix + dRblw_dStar.setFromTriplets(triplets.begin(), triplets.end()); + dRblw_dStar.prune(0.); +} + +void CoupledAdjoint::setdRblw_x(std::vector<std::vector<double>> _dRblw_x){ 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])); + for (size_t i = 0; i < _dRblw_x.size(); i++){ + for (size_t j = 0; j < _dRblw_x[i].size(); j++){ + triplets.push_back(Eigen::Triplet<double>(i, j, _dRblw_x[i][j])); } } // Set matrix @@ -408,47 +1031,99 @@ void CoupledAdjoint::setdRblw_x(std::vector<std::vector<double>> _dRblw_dx){ dRblw_x.prune(0.); } +Eigen::VectorXd CoupledAdjoint::runViscous() +{ + int exitCode = vsol->run(); + + // Extract deltaStar + std::vector<Eigen::VectorXd> dStar_p; + + // Extract parts in a loop + for (size_t i = 0; i < vsol->sections[0].size(); ++i) { + std::vector<double> deltaStar_vec = this->vsol->sections[0][i]->deltaStar; + Eigen::VectorXd deltaStar_eigen = Eigen::Map<Eigen::VectorXd>(deltaStar_vec.data(), deltaStar_vec.size()); + dStar_p.push_back(deltaStar_eigen); + } + // Calculate the total size + int nNs = 0; + for (const auto& p : dStar_p) + nNs += p.size(); + + // Concatenate the vectors + Eigen::VectorXd deltaStar(nNs); + int idx = 0; + for (const auto& p : dStar_p) { + deltaStar.segment(idx, p.size()) = p; + idx += p.size(); + } + return deltaStar; +} + /** * @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_dStar = dRphi_dStar.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_dStar = dRM_dStar.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_dStar = dRrho_dStar.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_dStar = dRv_dStar.transpose(); dRv_blw = dRv_blw.transpose(); + dRdStar_phi = dRdStar_phi.transpose(); + dRdStar_M = dRdStar_M.transpose(); + dRdStar_rho = dRdStar_rho.transpose(); + dRdStar_v = dRdStar_v.transpose(); + dRdStar_dStar = dRdStar_dStar.transpose(); + dRdStar_blw = dRdStar_blw.transpose(); + dRblw_phi = dRblw_phi.transpose(); dRblw_M = dRblw_M.transpose(); dRblw_rho = dRblw_rho.transpose(); dRblw_v = dRblw_v.transpose(); + dRblw_dStar = dRblw_dStar.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()) { + // Write the column headers + for (int j = 0; j < matrix.cols(); ++j) { + file << std::setw(15) << j; + if (j != matrix.cols() - 1) { + file << " "; + } + } + file << "\n"; + + // Write the matrix data for (int i = 0; i < matrix.rows(); ++i) { for (int j = 0; j < matrix.cols(); ++j) { - file << std::setprecision(20) << matrix(i, j); + file << std::fixed << std::setprecision(12) << std::setw(15) << matrix(i, j); if (j != matrix.cols() - 1) { file << " "; } diff --git a/blast/src/DCoupledAdjoint.h b/blast/src/DCoupledAdjoint.h index 3ae603da7eb523f8da19ef8243770e714620d478..086a70d31cd853d8508bf4d59e111bd4d8dac390 100644 --- a/blast/src/DCoupledAdjoint.h +++ b/blast/src/DCoupledAdjoint.h @@ -20,6 +20,7 @@ #include "blast.h" #include "wObject.h" #include "dart.h" +#include "wTimers.h" #include <Eigen/Sparse> #include <iostream> @@ -52,6 +53,7 @@ private: 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_dStar; ///< Derivative of the inviscid flow residual with respect to delta* 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 @@ -59,6 +61,7 @@ private: 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_dStar; ///< Derivative of the Mach number residual with respect to delta* 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 @@ -66,6 +69,7 @@ private: 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_dStar; ///< Derivative of the density residual with respect to delta* 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 @@ -73,29 +77,46 @@ private: 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_dStar; ///< Derivative of the velocity residual with respect to delta* 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> dRdStar_phi; ///< Derivative of delta* residual with respect to the inviscid flow + Eigen::SparseMatrix<double, Eigen::RowMajor> dRdStar_M; ///< Derivative of delta* residual with respect to the Mach number + Eigen::SparseMatrix<double, Eigen::RowMajor> dRdStar_v; ///< Derivative of delta* residual with respect to the velocity + Eigen::SparseMatrix<double, Eigen::RowMajor> dRdStar_rho; ///< Derivative of delta* residual with respect to the density + Eigen::SparseMatrix<double, Eigen::RowMajor> dRdStar_dStar; ///< Derivative of delta* residual with respect to delta* + Eigen::SparseMatrix<double, Eigen::RowMajor> dRdStar_blw; ///< Derivative of delta* residual with respect to the blowing velocity + Eigen::SparseMatrix<double, Eigen::RowMajor> dRdStar_x; ///< Derivative of delta* 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_dStar; ///< Derivative of the blowing velocity residual with respect to delta* 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 + Eigen::VectorXd dCd_M; ///< Derivative of the drag coefficient with respect to the Mach number + Eigen::VectorXd dCd_rho; ///< Derivative of the drag coefficient with respect to the density + Eigen::VectorXd dCd_v; ///< Derivative of the drag coefficient with respect to the velocity + Eigen::VectorXd dCd_dStar; ///< Derivative of the drag coefficient with respect to delta* // 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 + double dCd_AoA; ///< Partial derivative of the total 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 + Eigen::VectorXd dCdp_x; ///< Partial derivative of the pressure drag coefficient with respect to the mesh coordinates + Eigen::VectorXd dCdf_x; ///< Partial derivative of the friction drag coefficient with respect to the mesh coordinates + + fwk::Timers tms; //< internal timers public: CoupledAdjoint(std::shared_ptr<dart::Adjoint> iAdjoint, std::shared_ptr<blast::Driver> vSolver); @@ -104,18 +125,26 @@ public: 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); + void setdRdStar_M(std::vector<std::vector<double>> _dRdStar_M); + void setdRdStar_v(std::vector<std::vector<double>> _dRdStar_v); + void setdRdStar_dStar(std::vector<std::vector<double>> _dRdStar_dStar); + void setdRdStar_x(std::vector<std::vector<double>> _dRdStar_x); + + void setdRblw_rho(std::vector<std::vector<double>> _dRblw_rho); + void setdRblw_v(std::vector<std::vector<double>> _dRblw_v); + void setdRblw_dStar(std::vector<std::vector<double>> _dRblw_ddStar); + 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); + Eigen::VectorXd runViscous(); + void gradientswrtInviscidFlow(); void gradientswrtMachNumber(); void gradientswrtDensity(); void gradientswrtVelocity(); + void gradientswrtDeltaStar(); void gradientswrtBlowingVelocity(); void gradientwrtAoA(); diff --git a/blast/src/DDriver.cpp b/blast/src/DDriver.cpp index 2fee439bf1b7c806d1418c4900c24f19a73fb287..8a4fb03d44511956c40cce0a98e8bfcc4cfca715 100644 --- a/blast/src/DDriver.cpp +++ b/blast/src/DDriver.cpp @@ -266,7 +266,7 @@ void Driver::averageTransition(size_t iPoint, BoundaryLayer *bl, bool forced) double avTurb = (bl->x[iPoint] - bl->xtr) / (bl->x[iPoint] - bl->x[iPoint-1]); double avLam = 1. - avTurb; - //std::cout << "avTurb " << avTurb << " avLam " << avLam << std::endl; + // std::cout << "avTurb " << avTurb << " avLam " << avLam << std::endl; if (avTurb < 0. || avTurb > 1. || avLam < 0. || avLam > 1.) { bl->printSolution(iPoint); diff --git a/blast/tests/dart/adjointVII_2D.py b/blast/tests/dart/adjointVII_2D.py index 7341e9836585a889184a840658e0ea09ebb30db0..ea6db8211ea1d332f2aaf6157edf445efe66570b 100644 --- a/blast/tests/dart/adjointVII_2D.py +++ b/blast/tests/dart/adjointVII_2D.py @@ -18,12 +18,11 @@ # @author Paul Dechamps # @date 2024 -# Test the blast adjoint implementation. +# Test the blaster adjoint implementation. # Imports. import blast.utils as viscUtils -from blast.interfaces.DAdjointInterface import AdjointInterface import blast import numpy as np @@ -37,8 +36,6 @@ from fwk.testing import * from fwk.coloring import ccolors import numpy as np -import math - def cfgInviscid(nthrds, verb): import os.path # Parameters @@ -50,7 +47,7 @@ def cfgInviscid(nthrds, verb): '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 + 'Pars' : {'xLgt' : 5, 'yLgt' : 5, 'msF' : 3, 'msTe' : 0.1, 'msLe' : 0.1}, # parameters for input file model 'Dim' : 2, # problem dimension 'Format' : 'gmsh', # save format (vtk or gmsh) # Markers @@ -72,7 +69,7 @@ def cfgInviscid(nthrds, verb): '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) + 'LSolver' : 'SparseLu', # 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 @@ -87,8 +84,8 @@ def cfgBlast(verb): '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 + 'couplIter': 100, # Maximum number of iterations + 'couplTol' : 1e-10, # 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 @@ -108,25 +105,12 @@ def main(): 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 + morpher = isol.iobj['mrf'] # Adjoint objects cAdj = blast.CoupledAdjoint(isol.adjointSolver, vsol) - pyAdjoint = AdjointInterface(cAdj, isol, vsol) # Run coupled case print(ccolors.ANSI_BLUE + 'PySolving...' + ccolors.ANSI_RESET) @@ -140,35 +124,103 @@ def main(): 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() + # Get mesh matrix + dCl_x = np.zeros((isol.iobj['bnd'].nodes.size(), isol.getnDim())) + dCd_x = np.zeros((isol.iobj['bnd'].nodes.size(), isol.getnDim())) + for inod, n in enumerate(isol.blw[0].nodes): + for i in range(isol.getnDim()): + dCl_x[inod, i] = cAdj.tdCl_x[n.row][i] + dCd_x[inod, i] = cAdj.tdCd_x[n.row][i] + + + print(ccolors.ANSI_BLUE + 'PyFiniteDifference...' + ccolors.ANSI_RESET) + # Finite difference on AoA + tms['fd_blaster_aoa'].start() dcl = [] + dcd = [] 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() + dcd.append(isol.getCd()+vsol.Cdf) + dCl_AoA_FD = (dcl[-1] - dcl[-2])/(2.*dalpha) + dCd_AoA_FD = (dcd[-1] - dcd[-2])/(2.*dalpha) + tms['fd_blaster_aoa'].stop() + isol.setAoA(icfg['AoA']*np.pi/180) # Reset AoA after FD + + # Recover grad AoA from grad x + drot = np.array([[-np.sin(alpha), np.cos(alpha)], [-np.cos(alpha), -np.sin(alpha)]]) + + dCl_AoA_fromX = 0. + dCd_AoA_fromX = 0. + for inod, n in enumerate(isol.iobj['bnd'].nodes): + dx = drot.dot([n.pos[0] - 0.25, n.pos[1]]) + dCl_AoA_fromX += np.array([cAdj.tdCl_x[n.row][0], cAdj.tdCl_x[n.row][1]]).dot(dx) + dCd_AoA_fromX += np.array([cAdj.tdCd_x[n.row][0], cAdj.tdCd_x[n.row][1]]).dot(dx) + + ### BLASTER FD MESH ### + tms['fd_blaster_msh'].start() + saveNodes = np.zeros((len(isol.iobj['pbl'].msh.nodes), isol.getnDim())) + for n in isol.iobj['pbl'].msh.nodes: + for i in range(isol.getnDim()): + saveNodes[n.row, i] = n.pos[i] + dCl_x_blaster_fd = np.zeros((len(isol.blw[0].nodes), isol.getnDim())) + dCd_x_blaster_fd = np.zeros((len(isol.blw[0].nodes), isol.getnDim())) + delta = 1e-6 + cmp = 0 + import time + start_time = time.time() + for inod, n in enumerate(isol.blw[0].nodes): + # Safety check + if isol.iobj['bnd'].nodes[inod].row != n.row: + raise RuntimeError('Nodes do not match', n.row, isol.iobj['bnd'].nodes[inod].row) + for idim in range(isol.getnDim()): + cl = [0, 0] + cd = [0, 0] + for isgn, sign in enumerate([-1, 1]): + morpher.savePos() + n.pos[idim] = saveNodes[n.row, idim] + sign * delta + morpher.deform() + isol.updateMesh() + + coupler.reset() + # prevent print + from contextlib import redirect_stdout + with redirect_stdout(None): + coupler.run(write=False) + cmp += 1 + if cmp % 20 == 0: + print('F-D opers', cmp, '/', isol.blw[0].nodes.size() * isol.getnDim() * 2, '(node '+str(n.row)+')', 'time', str(round(time.time() - start_time, 3))+'s') + cl[isgn] = isol.getCl() + cd[isgn] = isol.getCd() + vsol.Cdf + + # Reset mesh + for node in isol.iobj['sol'].pbl.msh.nodes: + for d in range(isol.getnDim()): + node.pos[d] = saveNodes[node.row, d] + dCl_x_blaster_fd[inod, idim] = (cl[1] - cl[0]) / (2. * delta) + dCd_x_blaster_fd[inod, idim] = (cd[1] - cd[0]) / (2. * delta) + tms['fd_blaster_msh'].stop() + + print('Statistics') print(tms) + print(ccolors.ANSI_BLUE + 'PyTesting' + ccolors.ANSI_RESET) + tests = CTests() + tests.add(CTest('dCl_dAoA', cAdj.tdCl_AoA, dCl_AoA_FD, 1e-5)) + tests.add(CTest('dCd_dAoA', cAdj.tdCd_AoA, dCd_AoA_FD, 1e-4)) + tests.add(CTest('norm dCl_dx (adj - fd)', np.linalg.norm(dCl_x), np.linalg.norm(dCl_x_blaster_fd), 1e-6)) + tests.add(CTest('norm dCd_dx (adj - fd)', np.linalg.norm(dCd_x), np.linalg.norm(dCd_x_blaster_fd), 1e-4)) + tests.add(CTest('dCl_dAoA (msh)', dCl_AoA_fromX, cAdj.tdCl_AoA, 1e-2)) # The value gets closer if the mesh if refined + tests.add(CTest('dCd_dAoA (msh)', dCd_AoA_fromX, cAdj.tdCd_AoA, 1e-1)) # The value gets closer if the mesh if refined + tests.run() + + # eof + print('') if __name__ == '__main__': main() \ No newline at end of file diff --git a/blast/utils.py b/blast/utils.py index 93efffcbdd4cc05225ece4500730d58ef26ca91a..db3d5236f2a249159a775f876f8c5728f0b78602 100644 --- a/blast/utils.py +++ b/blast/utils.py @@ -44,7 +44,7 @@ def initBL(Re, Minf, CFL0, nSections, xtrF = [None, None], span=0, verb=None): if xtrF[i] is None: xtrF[i] = -1 if xtrF[i] != -1 and not(0<= xtrF[i] <= 1): - raise RuntimeError('Incorrect forced transition location') + raise RuntimeError('Incorrect forced transition location') solver = blast.Driver(Re, Minf, CFL0, nSections, xtrF_top=xtrF[0], xtrF_bot=xtrF[1], _span=span, _verbose=verbose) return solver @@ -70,7 +70,7 @@ def initBlast(iconfig, vconfig, iSolver='DART'): if 'sfx' not in vconfig: vconfig['sfx'] = '' # Viscous solver - vSolver = initBL(vconfig['Re'], vconfig['Minf'], vconfig['CFL0'], vconfig['nSections'], xtrF=vconfig['xtrF']) + vSolver = initBL(vconfig['Re'], vconfig['Minf'], vconfig['CFL0'], vconfig['nSections'], xtrF=vconfig['xtrF'], verb=vconfig['Verb']) # Inviscid solver if iSolver == 'DART': @@ -123,7 +123,7 @@ def getSolution(sections, write=False, toW=['x', 'xelm', 'theta', 'H', 'deltaSta varKeys = ['theta', 'H', 'N', 'ue', 'Ct'] attrKeys = ['x', 'y', 'z', 'xoc', 'deltaStar', \ 'cd', 'cf', 'Hstar', 'Hstar2', 'Hk', 'ctEq', 'us', 'delta',\ - 'vt', 'rhoe', 'Ret'] + 'vt', 'vtExt', 'deltaStarExt', 'rhoe', 'Ret', 'regime'] elemsKeys = ['xelm', 'yelm', 'zelm'] for iSec, sec in enumerate(sections): diff --git a/blast/validation/raeValidation.py b/blast/validation/raeValidation.py index 2ee1444ccf710a15eeda3c39fde086f3b75477e8..ef462b89c1a17f7710be828104fba0cd4dd64599 100644 --- a/blast/validation/raeValidation.py +++ b/blast/validation/raeValidation.py @@ -132,12 +132,12 @@ def main(): # Test solution print(ccolors.ANSI_BLUE + 'PyTesting...' + ccolors.ANSI_RESET) tests = CTests() - tests.add(CTest('Cl', isol.getCl(), 0.759, 5e-2)) - tests.add(CTest('Cd wake', vsol.Cdt, 0.0093, 1e-3, forceabs=True)) - tests.add(CTest('Cd integral', isol.getCd() + vsol.Cdf, 0.0134, 1e-3, forceabs=True)) + tests.add(CTest('Cl', isol.getCl(), 0.730, 5e-2)) + tests.add(CTest('Cd wake', vsol.Cdt, 0.0095, 1e-3, forceabs=True)) + tests.add(CTest('Cd integral', isol.getCd() + vsol.Cdf, 0.0132, 1e-3, forceabs=True)) tests.add(CTest('Cdf', vsol.Cdf, 0.0068, 1e-3, forceabs=True)) if icfg['LSolver'] == 'SparseLu': - tests.add(CTest('Iterations', len(aeroCoeffs['Cl']), 29, 0, forceabs=True)) + tests.add(CTest('Iterations', len(aeroCoeffs['Cl']), 21, 0, forceabs=True)) tests.run() expResults = np.loadtxt(os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + '/models/references/rae2822_AR138_case6.dat') diff --git a/modules/dartflo b/modules/dartflo index 2cdf73b03d98e1dae2a094fe9f1edb9f4f7e1eab..7bb5ce07ce84f4620798b99c68e2f94181870936 160000 --- a/modules/dartflo +++ b/modules/dartflo @@ -1 +1 @@ -Subproject commit 2cdf73b03d98e1dae2a094fe9f1edb9f4f7e1eab +Subproject commit 7bb5ce07ce84f4620798b99c68e2f94181870936