From 499014582b134f00d3be06272def7605dc95da54 Mon Sep 17 00:00:00 2001 From: Paul Dechamps <paul.dechamps@uliege.be> Date: Mon, 23 Sep 2024 15:02:37 +0200 Subject: [PATCH] (feat) Adjoint 2D Added adjoint for AoA and mesh in 2D. Nodes indices are now passed to the BoundaryLayer object --- blast/coupler.py | 27 +- blast/interfaces/DAdjointInterface.py | 612 ++++++++++++++++++--- blast/interfaces/DDataStructure.py | 34 ++ blast/interfaces/DSolversInterface.py | 65 ++- blast/interfaces/dart/DartInterface.py | 36 +- blast/src/DBoundaryLayer.cpp | 41 +- blast/src/DBoundaryLayer.h | 26 +- blast/src/DClosures.cpp | 4 +- blast/src/DCoupledAdjoint.cpp | 706 +++++++++++++++++++++---- blast/src/DCoupledAdjoint.h | 29 +- blast/src/DDriver.cpp | 2 +- blast/tests/dart/adjointVII_2D.py | 181 +++++-- blast/utils.py | 2 +- blast/validation/raeValidation.py | 8 +- 14 files changed, 1482 insertions(+), 291 deletions(-) diff --git a/blast/coupler.py b/blast/coupler.py index b7213a4..9c3d903 100644 --- a/blast/coupler.py +++ b/blast/coupler.py @@ -142,6 +142,20 @@ class Coupler: # Inviscid solver self.isol.reset() + # Viscous solver + for s in self.isol.sec: + for reg in s: + reg.reset() + + # Blowing velocity + for b in self.isol.iBnd: + for i in range(len(b.blowingVel)): + b.blowingVel[i] = 0. + for sec in self.isol.vBnd: + for side in sec: + for i in range(len(side.blowingVel)): + side.blowingVel[i] = 0. + self.isol.setBlowingVelocity() for iSec in range(self.isol.cfg['nSections']): for i, reg in enumerate(self.isol.sec[iSec]): @@ -156,13 +170,8 @@ class Coupler: 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]) - # Blowing velocity - for b in self.isol.iBnd: - for i in range(len(b.blowingVel)): - b.blowingVel[i] = 0. - for sec in self.isol.vBnd: - for side in sec: - for i in range(len(side.blowingVel)): - side.blowingVel[i] = 0. - self.isol.setBlowingVelocity() diff --git a/blast/interfaces/DAdjointInterface.py b/blast/interfaces/DAdjointInterface.py index cf3f60f..7eee2fe 100644 --- a/blast/interfaces/DAdjointInterface.py +++ b/blast/interfaces/DAdjointInterface.py @@ -10,93 +10,557 @@ class AdjointInterface(): 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)) + + nNs_v = 0 + for isec in self.isol.sec[0]: + nNs_v += isec.nNodes + nEs_v = 0 + for isec in self.isol.sec[0]: + nEs_v += isec.nElms + + dRdStar_dStar = np.zeros((nNs_v, nNs_v)) + dRdStar_M = np.zeros((nNs_v, nNs_v)) + dRdStar_rho = np.zeros((nNs_v, nNs_v)) + dRdStar_v = np.zeros((nNs_v, nNs_v*nDim)) + dRdStar_x = np.zeros((nNs_v, nNd*nDim)) + + dRblw_rho = np.zeros((nEs_v, nNs_v)) + dRblw_v = np.zeros((nEs_v, nNs_v*nDim)) + dRblw_dStar = np.zeros((nEs_v, nNs_v)) + dRblw_x = np.zeros((nEs_v, nNd*nDim)) delta = 1e-6 + delta_x = 1e-8 - 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') + # Compute dVt/dV + dVt_v = np.zeros((nNs_v, nNs_v*nDim)) + v = np.zeros((nNs_v, nDim)) + for d in range(nDim): + v[:self.isol.sec[0][0].nNodes, d] = self.isol.vBnd[0][0].getVelocity("upper", d) + v[self.isol.sec[0][0].nNodes:self.isol.sec[0][0].nNodes+self.isol.sec[0][1].nNodes, d] = self.isol.vBnd[0][0].getVelocity("lower", d) + v[self.isol.sec[0][0].nNodes+self.isol.sec[0][1].nNodes:, d] = self.isol.vBnd[0][1].getVelocity("wake", d) - # 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') + i = 0 + for isec in self.isol.sec[0]: + for j in range(isec.nNodes): + for d in range(nDim): + dVt_v[i, i*nDim+d] = 1/np.sqrt(v[i, 0]**2 + v[i, 1]**2)*v[i, d] + i += 1 + + """# Finite difference + i = 0 + offset = 0 + dVt_v_fd = np.zeros((nNs_v, nNs_v*nDim)) + for isec in self.isol.sec[0]: + for j in range(isec.nNodes): + for d in range(nDim): + save = v[i, d] + v[i, d] = save + delta + vt1 = np.sqrt(v[:,0]**2 + v[:,1]**2) + v[i, d] = save - delta + vt2 = np.sqrt(v[:,0]**2 + v[:,1]**2) + dVt_v_fd[:, i*nDim+d] = (vt1 - vt2)/(2*delta) + v[i, d] = save + i += 1""" + + # dRdStar_M + i = 0 + for isec in self.isol.sec[0]: + for j in range(isec.nNodes): + mach_lo = isec.Me[j] + isec.Me[j] = mach_lo + delta + deltaStar1 = self.__runViscous() + isec.Me[j] = mach_lo - delta + deltaStar2 = self.__runViscous() + isec.Me[j] = mach_lo + dRdStar_M[:, i] = -(deltaStar1 - deltaStar2)/(2.*delta) + i += 1 + + # dRdStar_dStar + dRdStar_dStar_test = np.zeros((nNs_v, nNs_v)) + i = 0 + for isec in self.isol.sec[0]: + for j in range(isec.nNodes): + dStar_lo = isec.deltaStarExt[j] + isec.deltaStarExt[j] = dStar_lo + delta + deltaStar1 = self.__runViscous() + isec.deltaStarExt[j] = dStar_lo - delta + deltaStar2 = self.__runViscous() + isec.deltaStarExt[j] = dStar_lo + dRdStar_dStar_test[:, i] = (deltaStar1 - deltaStar2)/(2.*delta) + i += 1 + + # Set dRdStar_dStar to identity + dRdStar_dStar = np.eye(nNs_v) - dRdStar_dStar_test + + # dRdStar_vt + dRdStar_vt = np.zeros((nNs_v, nNs_v)) + i = 0 + for isec in self.isol.sec[0]: + for j in range(isec.nNodes): + save = isec.vt[j] + isec.vt[j] = save + delta + #isec.vtExt[j] = save + delta + deltaStar1 = self.__runViscous() + isec.vt[j] = save - delta + #isec.vtExt[j] = save - delta + deltaStar2 = self.__runViscous() + isec.vt[j] = save + #isec.vtExt[j] = save + dRdStar_vt[:, i] = -(deltaStar1 - deltaStar2)/(2.*delta) + i += 1 + + dRdStar_v = dRdStar_vt @ dVt_v + + """# dRdStar_v fd + i = 0 + offset = 0 + dRdStar_v_fd = np.zeros((nNs_v, nNs_v*nDim)) + for isec in self.isol.sec[0]: + for j in range(isec.nNodes): + for d in range(nDim): + save = v[i, d] + v[i, d] = save + delta + # Recompute vt + vt = np.sqrt(v[:, 0]**2 + v[:, 1]**2) + # Change vt in c++ object + # Resplit vt in the sections + for nodesSec in range(isec.nNodes): + #print('nodesSec', nodesSec, 'isec.vt', isec.vt[nodesSec], 'vt', vt[offset + nodesSec]) + isec.vt[nodesSec] = vt[offset + nodesSec] + deltaStar1 = self.__runViscous() + v[i, d] = save - delta + # Recompute vt + vt = np.sqrt(v[:, 0]**2 + v[:, 1]**2) + # Change vt in c++ object + for nodesSec in range(isec.nNodes): + isec.vt[nodesSec] = vt[offset + nodesSec] + deltaStar2 = self.__runViscous() + v[i, d] = save + dRdStar_v_fd[:, i*nDim+d] = -(deltaStar1 - deltaStar2)/(2*delta) + i += 1 + offset += isec.nNodes + print('diff', dRdStar_v - dRdStar_v_fd) + print('max diff dRdstar_v', np.max(np.abs(dRdStar_v - dRdStar_v_fd))) + quit()""" + + # dRdStar_x + print('dRdStar_x') + for isec in self.isol.sec[0]: + if isec.name == 'upper' or isec.name == 'lower': + iReg = 0 + elif isec.name == 'wake': + iReg = 1 + for j in range(isec.nNodes): + rowj = self.isol.blw[iReg].nodes[isec.rows[j]].row + save = isec.x[j] + + isec.x[j] = save + delta_x + isec.computeLocalCoordinates() + isec.xxExt = isec.loc - # # 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) + deltaStar1 = self.__runViscous() + + isec.x[j] = save - delta_x + isec.computeLocalCoordinates() + isec.xxExt = isec.loc + + deltaStar2 = self.__runViscous() + + diff = (deltaStar1 - deltaStar2)/(2.*delta_x) + + isec.x[j] = save + isec.computeLocalCoordinates() + isec.xxExt = isec.loc + + dRdStar_x[:, rowj*nDim+0] -= diff + + save = isec.y[j] + isec.y[j] = save + delta_x + isec.computeLocalCoordinates() + isec.xxExt = isec.loc + + deltaStar1 = self.__runViscous() + + isec.y[j] = save - delta_x + isec.computeLocalCoordinates() + isec.xxExt = isec.loc + + deltaStar2 = self.__runViscous() + + diff = (deltaStar1 - deltaStar2)/(2.*delta_x) + + isec.y[j] = save + isec.computeLocalCoordinates() + isec.xxExt = isec.loc + dRdStar_x[:, rowj*nDim+1] -= diff + print('done') + """# Finite difference dRdStar_x_fd_2 + dRdStar_x_fd = np.zeros((nNs_v, nNd*nDim)) + saveNodes = np.zeros((len(self.isol.iobj['pbl'].msh.nodes), self.isol.getnDim())) + for n in self.isol.iobj['pbl'].msh.nodes: + for i in range(self.isol.getnDim()): + saveNodes[n.row, i] = n.pos[i] + for ib, b in enumerate(self.isol.blw): + for inod, n in enumerate(b.nodes): + # Find all the nodes in the viscous mesh corresponding to the current node. We also save the boundary index (0: airfoil, 1: wake) + vidx = [] + try: + vidx.append((np.where(self.isol.vBnd[0][0].nodesCoord[:,3] == n.row)[0][0], 0)) + except IndexError: + pass + try: + vidx.append((np.where(self.isol.vBnd[0][1].nodesCoord[:,3] == n.row)[0][0], 1)) + except IndexError: + pass + for idim in range(self.isol.getnDim()): + ddStar = [np.zeros(nNs_v), np.zeros(nNs_v)] + for isgn, sign in enumerate([-1, 1]): + # Modify all the nodes of the viscous mesh (contained in vidx) + for (ii, boundary) in vidx: + self.isol.vBnd[0][boundary].nodesCoord[ii, idim] = saveNodes[n.row, idim] + sign * delta + self.setvMesh(self.isol.vBnd, self.isol.sec[0]) + + ddStar[isgn] = self.__runViscous() + + # Reset mesh + for vb in self.isol.vBnd[0]: + for jj, jrow in enumerate(vb.nodesCoord[:,3]): + for d in range(self.isol.getnDim()): + vb.nodesCoord[jj, d] = saveNodes[int(jrow), d] + dRdStar_x_fd[:, n.row*self.isol.getnDim()+idim] = -(ddStar[1] - ddStar[0])/(2*delta) + np.savetxt('dRdStar_x_fd.txt', dRdStar_x_fd) + np.savetxt('dRdStar_x.txt', dRdStar_x) + print('max diff', np.max(np.abs(dRdStar_x - dRdStar_x_fd))) + quit()""" + + # dRblw_rho + offsetNodes = 0 + offsetElems = 0 + for isec in self.isol.sec[0]: + test = np.array(isec.evalGradwrtRho()) + dRblw_rho[offsetElems:offsetElems+isec.nElms, offsetNodes:offsetNodes+isec.nNodes] = -test + offsetNodes += isec.nNodes + offsetElems += isec.nElms + + """# Finite difference + start = time.time() + i = 0 + offset = 0 + dRblw_rho_fd = np.zeros((nEs_v, nNs_v)) + for isec in self.isol.sec[0]: + for j in range(isec.nNodes): + save = isec.rhoe[j] + isec.rhoe[j] = save + delta + isec.computeBlowingVelocity() + blowing_part1 = np.asarray(self.isol.sec[0][0].blowingVelocity) + blowing_part2 = np.asarray(self.isol.sec[0][1].blowingVelocity) + blowing_part3 = np.asarray(self.isol.sec[0][2].blowingVelocity) + blowing1 = np.concatenate([blowing_part1, blowing_part2, blowing_part3]) + isec.rhoe[j] = save - delta + isec.computeBlowingVelocity() + blowing_part1 = np.asarray(self.isol.sec[0][0].blowingVelocity) + blowing_part2 = np.asarray(self.isol.sec[0][1].blowingVelocity) + blowing_part3 = np.asarray(self.isol.sec[0][2].blowingVelocity) + blowing2 = np.concatenate([blowing_part1, blowing_part2, blowing_part3]) + isec.rhoe[j] = save + dRblw_rho_fd[:, i] = (blowing1 - blowing2)/(2*delta) + i += 1 + offset += isec.nNodes + end = time.time() + print('done dRblw_rho_fd in', end-start)""" + + # dRblw_vt + offsetNodes = 0 + offsetElems = 0 + dRblw_vt = np.zeros((nEs_v, nNs_v)) + for isec in self.isol.sec[0]: + test = np.array(isec.evalGradwrtVt()) + dRblw_vt[offsetElems:offsetElems+isec.nElms, offsetNodes:offsetNodes+isec.nNodes] = -test + offsetNodes += isec.nNodes + offsetElems += isec.nElms + + #print('dRblw_vt', dRblw_vt) + + # dRblw_v = dRblw_vt*dVt_v + dRblw_v = dRblw_vt @ dVt_v + + # Validate by finite difference + """i = 0 + offset = 0 + dRblw_v_fd = np.zeros((nEs_v, nNs_v*nDim)) + for isec in self.isol.sec[0]: + for j in range(isec.nNodes): + for d in range(nDim): + save = v[i, d] + v[i, d] = save + delta + # Recompute vt + vt = np.sqrt(v[:, 0]**2 + v[:, 1]**2) + # Change vt in c++ object + # Resplit vt in the sections + for nodesSec in range(isec.nNodes): + #print('nodesSec', nodesSec, 'isec.vt', isec.vt[nodesSec], 'vt', vt[offset + nodesSec]) + isec.vt[nodesSec] = vt[offset + nodesSec] + + isec.computeBlowingVelocity() + blowing_part1 = np.asarray(self.isol.sec[0][0].blowingVelocity) + blowing_part2 = np.asarray(self.isol.sec[0][1].blowingVelocity) + blowing_part3 = np.asarray(self.isol.sec[0][2].blowingVelocity) + blowing1 = np.concatenate([blowing_part1, blowing_part2, blowing_part3]) + v[i, d] = save - delta + # Recompute vt + vt = np.sqrt(v[:, 0]**2 + v[:, 1]**2) + # Change vt in c++ object + for nodesSec in range(isec.nNodes): + isec.vt[nodesSec] = vt[offset + nodesSec] + isec.computeBlowingVelocity() + blowing_part1 = np.asarray(self.isol.sec[0][0].blowingVelocity) + blowing_part2 = np.asarray(self.isol.sec[0][1].blowingVelocity) + blowing_part3 = np.asarray(self.isol.sec[0][2].blowingVelocity) + blowing2 = np.concatenate([blowing_part1, blowing_part2, blowing_part3]) + v[i, d] = save + dRblw_v_fd[:, i*nDim+d] = (blowing1 - blowing2)/(2*delta) + i += 1 + offset += isec.nNodes""" + + # dRblw_dStar + offsetNodes = 0 + offsetElems = 0 + for isec in self.isol.sec[0]: + test = np.array(isec.evalGradwrtDeltaStar()) + dRblw_dStar[offsetElems:offsetElems+isec.nElms, offsetNodes:offsetNodes+isec.nNodes] = -test + offsetNodes += isec.nNodes + offsetElems += isec.nElms + + """# Finite difference + dRblw_dStar_fd = np.zeros((nEs_v, nNs_v)) + i = 0 + for isec in self.isol.sec[0]: + for j in range(isec.nNodes): + save = isec.rhoe[j] + isec.deltaStar[j] = save + delta + isec.computeBlowingVelocity() + blowing_part1 = np.asarray(self.isol.sec[0][0].blowingVelocity) + blowing_part2 = np.asarray(self.isol.sec[0][1].blowingVelocity) + blowing_part3 = np.asarray(self.isol.sec[0][2].blowingVelocity) + blowing1 = np.concatenate([blowing_part1, blowing_part2, blowing_part3]) + isec.deltaStar[j] = save - delta + isec.computeBlowingVelocity() + blowing_part1 = np.asarray(self.isol.sec[0][0].blowingVelocity) + blowing_part2 = np.asarray(self.isol.sec[0][1].blowingVelocity) + blowing_part3 = np.asarray(self.isol.sec[0][2].blowingVelocity) + blowing2 = np.concatenate([blowing_part1, blowing_part2, blowing_part3]) + isec.deltaStar[j] = save + dRblw_dStar_fd[:, i] = -(blowing1 - blowing2)/(2*delta) + i += 1 + print('diff', dRblw_dStar - dRblw_dStar_fd) + print('max diff', np.max(np.abs(dRblw_dStar - dRblw_dStar_fd))) + quit()""" + + + """print('Max error dRblw_v', np.max(np.abs(dRblw_v - dRblw_v_fd))) + print('Max error dRblw_rho', np.max(np.abs(dRblw_rho - dRblw_rho_fd))) + print('Max error dRblw_dStar', np.max(np.abs(dRblw_dStar - dRblw_dStar_fd)))""" + + # dRblw_x + offsetElems = 0 + dRblw_x = np.zeros((nEs_v, nNd*nDim)) + for isec in self.isol.sec[0]: + if isec.name == 'upper' or isec.name == 'lower': + iReg = 0 + elif isec.name == 'wake': + iReg = 1 + test_x = -np.array(isec.evalGradwrtX()) + test_y = -np.array(isec.evalGradwrtY()) + for j in range(isec.nNodes): + rowj = self.isol.blw[iReg].nodes[isec.rows[j]].row + dRblw_x[offsetElems:offsetElems+isec.nElms, rowj*nDim+0] = test_x[:, j] + dRblw_x[offsetElems:offsetElems+isec.nElms, rowj*nDim+1] = test_y[:, j] + offsetElems += isec.nElms + + + """# Finite difference dRblw_x_fd_2 + dRblw_x_fd = np.zeros((nEs_v, nNd*nDim)) + saveNodes = np.zeros((len(self.isol.iobj['pbl'].msh.nodes), self.isol.getnDim())) + for n in self.isol.iobj['pbl'].msh.nodes: + for i in range(self.isol.getnDim()): + saveNodes[n.row, i] = n.pos[i] + for ib, b in enumerate(self.isol.blw): + for inod, n in enumerate(b.nodes): + # Find all the nodes in the viscous mesh corresponding to the current node. We also save the boundary index (0: airfoil, 1: wake) + vidx = [] + try: + vidx.append((np.where(self.isol.vBnd[0][0].nodesCoord[:,3] == n.row)[0][0], 0)) + except IndexError: + pass + try: + vidx.append((np.where(self.isol.vBnd[0][1].nodesCoord[:,3] == n.row)[0][0], 1)) + except IndexError: + pass + for idim in range(self.isol.getnDim()): + dblw = [np.zeros(nEs_v), np.zeros(nEs_v)] + for isgn, sign in enumerate([-1, 1]): + # Modify all the nodes of the viscous mesh (contained in vidx) + for (ii, boundary) in vidx: + self.isol.vBnd[0][boundary].nodesCoord[ii, idim] = saveNodes[n.row, idim] + sign * delta + self.setvMesh(self.isol.vBnd, self.isol.sec[0]) + for reg in self.isol.sec[0]: + reg.computeBlowingVelocity() + + blowing1_part1 = np.asarray(self.isol.sec[0][0].blowingVelocity) + blowing1_part2 = np.asarray(self.isol.sec[0][1].blowingVelocity) + blowing1_part3 = np.asarray(self.isol.sec[0][2].blowingVelocity) + dblw[isgn] = np.concatenate([blowing1_part1, blowing1_part2, blowing1_part3]) + + # Reset mesh + for vb in self.isol.vBnd[0]: + for jj, jrow in enumerate(vb.nodesCoord[:,3]): + for d in range(self.isol.getnDim()): + vb.nodesCoord[jj, d] = saveNodes[int(jrow), d] + dRblw_x_fd[:, n.row*self.isol.getnDim()+idim] = -(dblw[1] - dblw[0])/(2*delta) + # Write to file + np.savetxt('dRblw_x_fd.txt', dRblw_x_fd) + np.savetxt('dRblw_x.txt', dRblw_x) + print('max diff', np.max(np.abs(dRblw_x - dRblw_x_fd))) + quit()""" + + """# Finite difference dRblw_x_fd + offsetElems = 0 + dRblw_x_fd = np.zeros((nEs_v, nNd*nDim)) + for isec in self.isol.sec[0]: + if isec.name == 'upper' or isec.name == 'lower': + iReg = 0 + elif isec.name == 'wake': + iReg = 1 + for j in range(isec.nNodes): + rowj = self.isol.blw[iReg].nodes[isec.rows[j]].row + save = isec.x[j] + isec.x[j] = save + delta + isec.computeLocalCoordinates() + isec.computeBlowingVelocity() + + blowing1_part1 = np.asarray(self.isol.sec[0][0].blowingVelocity) + blowing1_part2 = np.asarray(self.isol.sec[0][1].blowingVelocity) + blowing1_part3 = np.asarray(self.isol.sec[0][2].blowingVelocity) + blowing1 = np.concatenate([blowing1_part1, blowing1_part2, blowing1_part3]) + + isec.x[j] = save - delta + isec.computeLocalCoordinates() + isec.computeBlowingVelocity() + + blowing2_part1 = np.asarray(self.isol.sec[0][0].blowingVelocity) + blowing2_part2 = np.asarray(self.isol.sec[0][1].blowingVelocity) + blowing2_part3 = np.asarray(self.isol.sec[0][2].blowingVelocity) + blowing2 = np.concatenate([blowing2_part1, blowing2_part2, blowing2_part3]) + bb = (blowing1 - blowing2)/(2*delta) + + isec.x[j] = save + dRblw_x_fd[offsetElems:offsetElems+isec.nElms, rowj*nDim+0] = -bb[offsetElems:offsetElems+isec.nElms] + + if rowj == 0: + print(isec.name, j, -(blowing1 - blowing2)/(2*delta)) + + save = isec.y[j] + isec.y[j] = save + delta + isec.computeLocalCoordinates() + isec.computeBlowingVelocity() + + blowing1_part1 = np.asarray(self.isol.sec[0][0].blowingVelocity) + blowing1_part2 = np.asarray(self.isol.sec[0][1].blowingVelocity) + blowing1_part3 = np.asarray(self.isol.sec[0][2].blowingVelocity) + blowing1 = np.concatenate([blowing1_part1, blowing1_part2, blowing1_part3]) + + isec.y[j] = save - delta + isec.computeLocalCoordinates() + isec.computeBlowingVelocity() + + blowing2_part1 = np.asarray(self.isol.sec[0][0].blowingVelocity) + blowing2_part2 = np.asarray(self.isol.sec[0][1].blowingVelocity) + blowing2_part3 = np.asarray(self.isol.sec[0][2].blowingVelocity) + blowing2 = np.concatenate([blowing2_part1, blowing2_part2, blowing2_part3]) + bb = (blowing1 - blowing2)/(2*delta) + + isec.y[j] = save + dRblw_x_fd[offsetElems:offsetElems+isec.nElms, rowj*nDim+1] = -bb[offsetElems:offsetElems+isec.nElms] + offsetElems += isec.nElms + for row in range(len(dRblw_x[:,0])): + print(dRblw_x[row,:] - dRblw_x_fd[row,:]) + print( + 'max diff', np.max(np.abs(dRblw_x - dRblw_x_fd))) + quit()""" + + print('Imposing matrix') + self.coupledAdjointSolver.setdRdStar_M(dRdStar_M) + print('dRdStar_M done') + self.coupledAdjointSolver.setdRdStar_v(dRdStar_v) + print('dRdStar_v done') + self.coupledAdjointSolver.setdRdStar_dStar(dRdStar_dStar) + print('dRdStar_dStar done') + self.coupledAdjointSolver.setdRdStar_x(dRdStar_x) + print('dRdStar_x 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) + print('dRblw_rho done') self.coupledAdjointSolver.setdRblw_v(dRblw_v) + print('dRblw_v done') + self.coupledAdjointSolver.setdRblw_dStar(dRblw_dStar) + print('dRblw_dStar done') self.coupledAdjointSolver.setdRblw_x(dRblw_x) + print('dRblw_x done') + + def __getDeltaStar(self): + deltaStar_part1 = np.asarray(self.isol.sec[0][0].deltaStar) + deltaStar_part2 = np.asarray(self.isol.sec[0][1].deltaStar) + deltaStar_part3 = np.asarray(self.isol.sec[0][2].deltaStar) + deltaStar = np.concatenate([deltaStar_part1, deltaStar_part2, deltaStar_part3]) + #deltaStar = np.concatenate([deltaStar_part1, deltaStar_part2]) + return deltaStar + + def setvMesh(self, vBnd, secs): + import tbox + # Compute section chord + xMin = np.min(vBnd[0][0].nodesCoord[:,0]) + xMax = np.max(vBnd[0][0].nodesCoord[:,0]) + chord = xMax - xMin + for reg in secs: + if reg.name == 'wake': + iReg = 1 + elif reg.name == 'lower' or reg.name == 'upper': + iReg = 0 + else: + raise RuntimeError('Invalid region', reg.name) + + nodeRows = tbox.std_vector_int() + for val in vBnd[0][iReg].getNodesRow(reg.name): + nodeRows.push_back(int(val)) + connectElems = tbox.std_vector_int() + for val in vBnd[0][iReg].getConnectList(reg.name): + connectElems.push_back(int(val)) + # x, y, z + xv = tbox.std_vector_double() + for val in vBnd[0][iReg].getNodesCoord(reg.name, 0): + xv.push_back(val) + yv = tbox.std_vector_double() + for val in vBnd[0][iReg].getNodesCoord(reg.name, 1): + yv.push_back(val) + zv = tbox.std_vector_double() + for val in vBnd[0][iReg].getNodesCoord(reg.name, 2): + zv.push_back(val) + + # Set mesh + reg.setMesh(xv, + yv, + zv, + chord, + xMin, + nodeRows, + connectElems) 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 + deltaStar = self.__getDeltaStar() + return deltaStar diff --git a/blast/interfaces/DDataStructure.py b/blast/interfaces/DDataStructure.py index 4a63dc6..a5049f6 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 dc85a47..50eae4b 100644 --- a/blast/interfaces/DSolversInterface.py +++ b/blast/interfaces/DSolversInterface.py @@ -1,5 +1,6 @@ import numpy as np import blast +import tbox from blast.interfaces.DDataStructure import Group import time @@ -26,6 +27,8 @@ class SolversInterface: 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].connectListNodes = self.iBnd[iReg].connectListNodes + self.vBnd[iSec][iReg].connectListElems = self.iBnd[iReg].connectListElems elif self.cfg['interpolator'] == 'Rbf': from blast.interfaces.interpolators.DRbfInterpolator import RbfInterpolator as interp @@ -47,7 +50,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 +66,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 +85,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': @@ -117,7 +120,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 +155,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 +203,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. diff --git a/blast/interfaces/dart/DartInterface.py b/blast/interfaces/dart/DartInterface.py index 09e06be..0334541 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.') @@ -81,20 +81,20 @@ class DartInterface(SolversInterface): 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) + 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 +118,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() @@ -266,9 +266,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 +282,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 +318,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])): diff --git a/blast/src/DBoundaryLayer.cpp b/blast/src/DBoundaryLayer.cpp index ad18c99..5f84a69 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 030bb1d..5f7adb2 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 c411d1c..2cf9f44 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 8139d32..e712037 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,49 +63,71 @@ 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 + + std::cout << "nDim " << nDim << std::endl; + std::cout << "nNd " << nNd << std::endl; + std::cout << "nNs " << nNs << std::endl; + std::cout << "nEs " << nEs << std::endl; 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 dCl_phi = Eigen::VectorXd::Zero(nNd); @@ -166,21 +193,24 @@ void CoupledAdjoint::buildAdjointMatrix(std::vector<std::vector<Eigen::SparseMat void CoupledAdjoint::run() { gradientswrtInviscidFlow(); + gradientswrtMachNumber(); + gradientswrtDensity(); + gradientswrtVelocity(); + gradientswrtDeltaStar(); gradientswrtBlowingVelocity(); gradientwrtAoA(); gradientwrtMesh(); transposeMatrices(); - // Adjoint matrix - // Store pointers to the matrices in a 2D vector std::vector<std::vector<Eigen::SparseMatrix<double, Eigen::RowMajor>*>> matrices = { - {&dRphi_phi, &dRM_phi, &dRv_phi, &dRrho_phi, &dRblw_phi}, - {&dRphi_M, &dRM_M, &dRv_M, &dRrho_M, &dRblw_M}, - {&dRphi_v, &dRM_v, &dRv_v, &dRrho_v, &dRblw_v}, - {&dRphi_rho, &dRM_rho, &dRv_rho, &dRrho_rho, &dRblw_rho}, - {&dRphi_blw, &dRM_blw, &dRv_blw, &dRrho_blw, &dRblw_blw} + {&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; @@ -189,59 +219,87 @@ void CoupledAdjoint::run() Eigen::SparseMatrix<double, Eigen::RowMajor> A_adjoint(rows, cols); buildAdjointMatrix(matrices, A_adjoint); + 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 std::vector<double> rhsCl(A_adjoint.cols(), 0.0); for (auto i = 0; i<dCl_phi.size(); ++i) - rhsCl[i] = dCl_phi[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); - - // tdCd_AoA = dCd_AoA - dRphi_AoA.transpose()*lambdaCdPhi; // Total gradient + 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()); + // Solve linear system + 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 //***** 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" + + 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 = dCd_x; + 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]; } } } +/** + * @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 +309,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 +319,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); + 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()); + + // 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++; } - dM /= static_cast<double>(pbl->fluid->neMap[srfNode].size()); - drho /= static_cast<double>(pbl->fluid->neMap[srfNode].size()); - dv /= static_cast<double>(pbl->fluid->neMap[srfNode].size()); - - // Form triplets - tripletsdM.push_back(Eigen::Triplet<double>(jj, n->row, -(dM - isol->M[srfNode->row])/deltaPhi)); - tripletsdrho.push_back(Eigen::Triplet<double>(jj, n->row, -(drho - isol->rho[srfNode->row])/deltaPhi)); - for (int i = 0; i < pbl->nDim; ++i) - tripletsdv.push_back(Eigen::Triplet<double>(jj*pbl->nDim + i, n->row, -(dv(i) - isol->U[srfNode->row](i))/deltaPhi)); - - jj++; } // Reset phi Phi_save[n->row] = isol->phi[n->row]; @@ -296,111 +363,464 @@ void CoupledAdjoint::gradientswrtInviscidFlow() // Remove zeros dRM_phi.prune(0.); dRrho_phi.prune(0.); dRv_phi.prune(0.); - //**** dCl_phi, dCd_phi ****// + //**** 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())); + 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(); + sec->Me[j] = saveM - delta; + ddStar[1] = this->runViscous(); + + 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))); + ++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(); - // dCl_dRho = - // dCd_dRho = + //**** 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.); } -void CoupledAdjoint::gradientswrtVelocity(){ - // dRphi_dV = 0 - // dRM_dV = 0 - // dRrho_dV = 0 - // dRv_dV = - // dRblw_dV = +/** + * @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())); + 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(); + sec->vt[j] = saveM - delta; + dvt[1] = this->runViscous(); - // dCl_dV = - // dCd_dV = + 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))); + ++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.); + + + //**** 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::gradientswrtBlowingVelocity(){ - dRphi_blw = iadjoint->getRu_Blw(); +/** + * @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())); + size_t i = 0; + for (auto sec: vsol->sections[0]) + for (size_t j = 0; j < sec->nNodes; ++j) + { + saveDStar = sec->deltaStarExt[j]; - // All others are zero. + sec->deltaStarExt[j] = saveDStar + delta; + ddstar[0] = this->runViscous(); + sec->deltaStarExt[j] = saveDStar - delta; + ddstar[1] = this->runViscous(); + + 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))); + ++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.); + + //**** 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.); } +/** + * @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(); + + /**** 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()); + /**** dRphi_x ****/ dRphi_x = iadjoint->getRu_X(); + /**** dRx_x ****/ dRx_x = iadjoint->getRx_X(); + + /**** 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::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_dStar(std::vector<std::vector<double>> _dRdStar_dStar){ 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_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::setdRdStar_x(std::vector<std::vector<double>> _dRdStar_x){ + std::vector<Eigen::Triplet<double>> triplets; + 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 +828,101 @@ void CoupledAdjoint::setdRblw_x(std::vector<std::vector<double>> _dRblw_dx){ dRblw_x.prune(0.); } +Eigen::VectorXd CoupledAdjoint::runViscous() +{ + int exitCode = vsol->run(); + if (exitCode != 0) + throw std::runtime_error("CoupledAdjoint::runViscous: Solver did not converge"); + + // 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 3ae603d..0993698 100644 --- a/blast/src/DCoupledAdjoint.h +++ b/blast/src/DCoupledAdjoint.h @@ -52,6 +52,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 +60,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 +68,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,13 +76,23 @@ 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 @@ -104,18 +117,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 2fee439..8a4fb03 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 7341e98..4440196 100644 --- a/blast/tests/dart/adjointVII_2D.py +++ b/blast/tests/dart/adjointVII_2D.py @@ -24,6 +24,7 @@ import blast.utils as viscUtils from blast.interfaces.DAdjointInterface import AdjointInterface +import modules.dartflo.dart.default as floD import blast import numpy as np @@ -37,7 +38,8 @@ from fwk.testing import * from fwk.coloring import ccolors import numpy as np -import math +import os +import sys def cfgInviscid(nthrds, verb): import os.path @@ -50,7 +52,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 +74,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 +89,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 @@ -109,20 +111,10 @@ def main(): dalpha = 1e-6 * np.pi/180 daoa = [alpha - dalpha, alpha + dalpha] dcl = [] + dcd = [] 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) @@ -132,7 +124,10 @@ def main(): print(ccolors.ANSI_BLUE + 'PySolving...' + ccolors.ANSI_RESET) tms['forward'].start() aeroCoeffs = coupler.run() - tms['forward'].stop() + tms['forward'].stop() + + # Print with precision of 1e-12 + print(f"{isol.solver.Cl:.12f}", f"{isol.solver.Cd:.12f}") print(ccolors.ANSI_BLUE + 'PyDartAdjoint...' + ccolors.ANSI_RESET) tms['adj_dart'].start() @@ -147,28 +142,148 @@ def main(): 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()) + 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): + vidx = np.where(isol.vBnd[0][0].nodesCoord[:,3] == n.row)[0][0] + # 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.vBnd[0][0].nodesCoord[vidx, idim] = saveNodes[n.row, idim] + sign * delta + setvMesh(isol.vBnd, isol.sec[0]) + + coupler.reset() + # prevent print + from contextlib import redirect_stdout + with redirect_stdout(None): + aeroCoeffs = 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() + + # Reset mesh + for node in isol.iobj['sol'].pbl.msh.nodes: + for d in range(isol.getnDim()): + node.pos[d] = saveNodes[node.row, d] + for jj, jrow in enumerate(isol.vBnd[0][0].nodesCoord[:,3]): + for d in range(isol.getnDim()): + isol.vBnd[0][0].nodesCoord[jj, d] = saveNodes[int(jrow), 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-4)) + tests.add(CTest('norm dCd_dx (adj - fd)', np.linalg.norm(dCd_x), np.linalg.norm(dCd_x_blaster_fd), 1e-3)) + 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('') + +def setvMesh(vBnd, secs): + import tbox + # Compute section chord + xMin = np.min(vBnd[0][0].nodesCoord[:,0]) + xMax = np.max(vBnd[0][0].nodesCoord[:,0]) + chord = xMax - xMin + for reg in secs: + if reg.name == 'wake': + iReg = 1 + elif reg.name == 'lower' or reg.name == 'upper': + iReg = 0 + else: + raise RuntimeError('Invalid region', reg.name) + + nodeRows = tbox.std_vector_int() + for val in vBnd[0][iReg].getNodesRow(reg.name): + nodeRows.push_back(int(val)) + connectElems = tbox.std_vector_int() + for val in vBnd[0][iReg].getConnectList(reg.name): + connectElems.push_back(int(val)) + # x, y, z + xv = tbox.std_vector_double() + for val in vBnd[0][iReg].getNodesCoord(reg.name, 0): + xv.push_back(val) + yv = tbox.std_vector_double() + for val in vBnd[0][iReg].getNodesCoord(reg.name, 1): + yv.push_back(val) + zv = tbox.std_vector_double() + for val in vBnd[0][iReg].getNodesCoord(reg.name, 2): + zv.push_back(val) + + # Set mesh + reg.setMesh(xv, + yv, + zv, + chord, + xMin, + nodeRows, + connectElems) + + reg.xxExt = reg.loc + + if __name__ == '__main__': main() \ No newline at end of file diff --git a/blast/utils.py b/blast/utils.py index 93efffc..4da3ce6 100644 --- a/blast/utils.py +++ b/blast/utils.py @@ -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 2ee1444..ef462b8 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') -- GitLab