From a941eaf42d632fba4084f550f94a10a8ce99d4ab Mon Sep 17 00:00:00 2001 From: Ping He Date: Thu, 27 Jun 2024 18:02:36 -0500 Subject: [PATCH] Allowed heat source to move with derivs (#656) * Added heat source parameters as DVs and location snapCenter option. * Fixed some bugs and added the derivs for the heatSource pars. * Fixed a bug. --- dafoam/mphys/mphys_dafoam.py | 50 +++ dafoam/pyDAFoam.py | 2 +- src/adjoint/DAFvSource/DAFvSource.C | 96 +++--- src/adjoint/DAFvSource/DAFvSource.H | 22 +- .../DAFvSource/DAFvSourceActuatorDisk.C | 38 +-- src/adjoint/DAFvSource/DAFvSourceHeatSource.C | 147 +++++++-- src/adjoint/DAFvSource/DAFvSourceHeatSource.H | 5 +- src/adjoint/DAObjFunc/DAObjFuncLocation.C | 36 +- src/adjoint/DAObjFunc/DAObjFuncLocation.H | 6 + .../DAResidual/DAResidualHeatTransferFoam.C | 13 + .../DAResidual/DAResidualHeatTransferFoam.H | 2 + .../DAHeatTransferFoam/DAHeatTransferFoam.C | 4 - src/adjoint/DASolver/DASolver.C | 310 ++++++++++++++++++ src/adjoint/DASolver/DASolver.H | 15 + src/pyDASolvers/DASolvers.H | 22 ++ src/pyDASolvers/pyDASolvers.pyx | 8 + .../DAFoam_Test_DAHeatTransferFoamRef.txt | 12 +- .../refs/DAFoam_Test_MphysAeroThermalRef.txt | 12 + tests/runTests_DAHeatTransferFoam.py | 49 ++- tests/runTests_MphysAeroThermal.py | 37 ++- 20 files changed, 774 insertions(+), 112 deletions(-) diff --git a/dafoam/mphys/mphys_dafoam.py b/dafoam/mphys/mphys_dafoam.py index 35c44978..15522ed2 100644 --- a/dafoam/mphys/mphys_dafoam.py +++ b/dafoam/mphys/mphys_dafoam.py @@ -657,6 +657,11 @@ def setup(self): if "comps" in list(designVariables[dvName].keys()): nACTDVars = len(designVariables[dvName]["comps"]) self.add_input(dvName, distributed=False, shape=nACTDVars, tags=["mphys_coupling"]) + elif dvType == "HSC": # add heat source parameter variables + nHSCVars = 9 + if "comps" in list(designVariables[dvName].keys()): + nHSCVars = len(designVariables[dvName]["comps"]) + self.add_input(dvName, distributed=False, shape=nHSCVars, tags=["mphys_coupling"]) elif dvType == "Field": # add field variables # user can prescribe whether the field var is distributed. Default is True distributed = True @@ -888,6 +893,26 @@ def apply_linear(self, inputs, outputs, d_inputs, d_outputs, d_residuals, mode): d_inputs[inputName] += ACTDBarSub else: d_inputs[inputName] += ACTDBar + + # compute [dRdHSC]^T*Psi using reverse mode AD + elif self.dvType[inputName] == "HSC": + prodVec = PETSc.Vec().create(self.comm) + prodVec.setSizes((PETSc.DECIDE, 9), bsize=1) + prodVec.setFromOptions() + DASolver.solverAD.calcdRdHSCTPsiAD( + DASolver.xvVec, DASolver.wVec, resBarVec, inputName.encode(), prodVec + ) + # we will convert the MPI prodVec to seq array for all procs + HSCBar = DASolver.convertMPIVec2SeqArray(prodVec) + if "comps" in list(designVariables[inputName].keys()): + nHSCVars = len(designVariables[inputName]["comps"]) + HSCBarSub = np.zeros(nHSCVars, "d") + for i in range(nHSCVars): + comp = designVariables[inputName]["comps"][i] + HSCBarSub[i] = HSCBar[comp] + d_inputs[inputName] += HSCBarSub + else: + d_inputs[inputName] += HSCBar # compute dRdFieldT*Psi using reverse mode AD elif self.dvType[inputName] == "Field": @@ -1251,6 +1276,11 @@ def setup(self): if "comps" in list(designVariables[dvName].keys()): nACTDVars = len(designVariables[dvName]["comps"]) self.add_input(dvName, distributed=False, shape=nACTDVars, tags=["mphys_coupling"]) + elif dvType == "HSC": # add heat source parameter variables + nHSCVars = 9 + if "comps" in list(designVariables[dvName].keys()): + nHSCVars = len(designVariables[dvName]["comps"]) + self.add_input(dvName, distributed=False, shape=nHSCVars, tags=["mphys_coupling"]) elif dvType == "Field": # add field variables # user can prescribe whether the field var is distributed. Default is True distributed = True @@ -1447,6 +1477,26 @@ def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode): d_inputs[inputName] += ACTDBarSub * fBar else: d_inputs[inputName] += ACTDBar * fBar + + # compute dFdHSC + elif self.dvType[inputName] == "HSC": + dFdHSC = PETSc.Vec().create(self.comm) + dFdHSC.setSizes((PETSc.DECIDE, 9), bsize=1) + dFdHSC.setFromOptions() + DASolver.solverAD.calcdFdHSCAD( + DASolver.xvVec, DASolver.wVec, objFuncName.encode(), inputName.encode(), dFdHSC + ) + # we will convert the MPI dFdHSC to seq array for all procs + HSCBar = DASolver.convertMPIVec2SeqArray(dFdHSC) + if "comps" in list(designVariables[inputName].keys()): + nHSCVars = len(designVariables[inputName]["comps"]) + HSCBarSub = np.zeros(nHSCVars, "d") + for i in range(nHSCVars): + comp = designVariables[inputName]["comps"][i] + HSCBarSub[i] = HSCBar[comp] + d_inputs[inputName] += HSCBarSub * fBar + else: + d_inputs[inputName] += HSCBar * fBar # compute dFdField elif self.dvType[inputName] == "Field": diff --git a/dafoam/pyDAFoam.py b/dafoam/pyDAFoam.py index 0d66923f..84b337f0 100755 --- a/dafoam/pyDAFoam.py +++ b/dafoam/pyDAFoam.py @@ -11,7 +11,7 @@ """ -__version__ = "3.1.2" +__version__ = "3.1.3" import subprocess import os diff --git a/src/adjoint/DAFvSource/DAFvSource.C b/src/adjoint/DAFvSource/DAFvSource.C index a436ebe0..01ea675f 100755 --- a/src/adjoint/DAFvSource/DAFvSource.C +++ b/src/adjoint/DAFvSource/DAFvSource.C @@ -135,49 +135,67 @@ void DAFvSource::syncDAOptionToActuatorDVs() // now we need to initialize actuatorDiskDVs_ dictionary fvSourceSubDict = daOption_.getAllOptions().subDict("fvSource"); - word diskName0 = fvSourceSubDict.toc()[0]; - word type0 = fvSourceSubDict.subDict(diskName0).getWord("type"); - - if (type0 == "actuatorDisk") + forAll(fvSourceSubDict.toc(), idxI) { - word source0 = fvSourceSubDict.subDict(diskName0).getWord("source"); + word diskName = fvSourceSubDict.toc()[idxI]; + // sub dictionary with all parameters for this disk + dictionary diskSubDict = fvSourceSubDict.subDict(diskName); + word type = diskSubDict.getWord("type"); + word source = diskSubDict.getWord("source"); + + if (type == "actuatorDisk" && source == "cylinderAnnulusSmooth") + { - if (source0 == "cylinderAnnulusSmooth") + // now read in all parameters for this actuator disk + scalarList centerList; + diskSubDict.readEntry("center", centerList); + + scalarList dirList; + diskSubDict.readEntry("direction", dirList); + + // we have 13 design variables for each disk + scalarList dvList(13); + dvList[0] = centerList[0]; + dvList[1] = centerList[1]; + dvList[2] = centerList[2]; + dvList[3] = dirList[0]; + dvList[4] = dirList[1]; + dvList[5] = dirList[2]; + dvList[6] = diskSubDict.getScalar("innerRadius"); + dvList[7] = diskSubDict.getScalar("outerRadius"); + dvList[8] = diskSubDict.getScalar("scale"); + dvList[9] = diskSubDict.getScalar("POD"); + dvList[10] = diskSubDict.getScalar("expM"); + dvList[11] = diskSubDict.getScalar("expN"); + dvList[12] = diskSubDict.getScalar("targetThrust"); + + // set actuatorDiskDVs_ + actuatorDiskDVs_.set(diskName, dvList); + } + else if (type == "heatSource" && source == "cylinderSmooth") { - forAll(fvSourceSubDict.toc(), idxI) - { - word diskName = fvSourceSubDict.toc()[idxI]; - - // sub dictionary with all parameters for this disk - dictionary diskSubDict = fvSourceSubDict.subDict(diskName); - - // now read in all parameters for this actuator disk - scalarList centerList; - diskSubDict.readEntry("center", centerList); - - scalarList dirList; - diskSubDict.readEntry("direction", dirList); - - // we have 13 design variables for each disk - scalarList dvList(13); - dvList[0] = centerList[0]; - dvList[1] = centerList[1]; - dvList[2] = centerList[2]; - dvList[3] = dirList[0]; - dvList[4] = dirList[1]; - dvList[5] = dirList[2]; - dvList[6] = diskSubDict.getScalar("innerRadius"); - dvList[7] = diskSubDict.getScalar("outerRadius"); - dvList[8] = diskSubDict.getScalar("scale"); - dvList[9] = diskSubDict.getScalar("POD"); - dvList[10] = diskSubDict.getScalar("expM"); - dvList[11] = diskSubDict.getScalar("expN"); - dvList[12] = diskSubDict.getScalar("targetThrust"); - - // set actuatorDiskDVs_ - actuatorDiskDVs_.set(diskName, dvList); - } + // now read in all parameters for this actuator disk + scalarList centerList; + diskSubDict.readEntry("center", centerList); + + scalarList axisList; + diskSubDict.readEntry("axis", axisList); + + // we have 13 design variables for each disk + scalarList dvList(9); + dvList[0] = centerList[0]; + dvList[1] = centerList[1]; + dvList[2] = centerList[2]; + dvList[3] = axisList[0]; + dvList[4] = axisList[1]; + dvList[5] = axisList[2]; + dvList[6] = diskSubDict.getScalar("radius"); + dvList[7] = diskSubDict.getScalar("length"); + dvList[8] = diskSubDict.getScalar("power"); + + // set actuatorDiskDVs_ + actuatorDiskDVs_.set(diskName, dvList); } } } diff --git a/src/adjoint/DAFvSource/DAFvSource.H b/src/adjoint/DAFvSource/DAFvSource.H index e693f125..296577c0 100755 --- a/src/adjoint/DAFvSource/DAFvSource.H +++ b/src/adjoint/DAFvSource/DAFvSource.H @@ -128,9 +128,25 @@ public: /// calculate fvSource based on the latest actuatorDVs void updateFvSource() { - volVectorField& fvSource = const_cast( - mesh_.thisDb().lookupObject("fvSource")); - this->calcFvSource(fvSource); + if (mesh_.thisDb().foundObject("fvSource")) + { + volVectorField& fvSource = const_cast( + mesh_.thisDb().lookupObject("fvSource")); + + this->calcFvSource(fvSource); + } + else if (mesh_.thisDb().foundObject("fvSource")) + { + volScalarField& fvSource = const_cast( + mesh_.thisDb().lookupObject("fvSource")); + + this->calcFvSource(fvSource); + } + else + { + FatalErrorIn("") << "fvSource not found! " + << abort(FatalError); + } } /// synchronize the values in DAOption and actuatorDiskDVs_ diff --git a/src/adjoint/DAFvSource/DAFvSourceActuatorDisk.C b/src/adjoint/DAFvSource/DAFvSourceActuatorDisk.C index 4b9f43a2..b950667f 100755 --- a/src/adjoint/DAFvSource/DAFvSourceActuatorDisk.C +++ b/src/adjoint/DAFvSource/DAFvSourceActuatorDisk.C @@ -103,21 +103,15 @@ void DAFvSourceActuatorDisk::calcFvSource(volVectorField& fvSource) dictionary fvSourceSubDict = allOptions.subDict("fvSource"); - word diskName0 = fvSourceSubDict.toc()[0]; - word source0 = fvSourceSubDict.subDict(diskName0).getWord("source"); - - if (source0 == "cylinderAnnulusToCell") + forAll(fvSourceSubDict.toc(), idxI) { + word diskName = fvSourceSubDict.toc()[idxI]; + dictionary diskSubDict = fvSourceSubDict.subDict(diskName); + word source = diskSubDict.getWord("source"); - // loop over all the cell indices for all actuator disks - forAll(fvSourceCellIndices_.toc(), idxI) + if (source == "cylinderAnnulusToCell") { - - // name of this disk - word diskName = fvSourceCellIndices_.toc()[idxI]; - - // sub dictionary with all parameters for this disk - dictionary diskSubDict = fvSourceSubDict.subDict(diskName); + // loop over all the cell indices for all actuator disks // now read in all parameters for this actuator disk scalarList point1; @@ -213,14 +207,8 @@ void DAFvSourceActuatorDisk::calcFvSource(volVectorField& fvSource) } } } - } - else if (source0 == "cylinderAnnulusSmooth") - { - - forAll(fvSourceSubDict.toc(), idxI) + else if (source == "cylinderAnnulusSmooth") { - word diskName = fvSourceSubDict.toc()[idxI]; - dictionary diskSubDict = fvSourceSubDict.subDict(diskName); vector center = {actuatorDiskDVs_[diskName][0], actuatorDiskDVs_[diskName][1], actuatorDiskDVs_[diskName][2]}; vector dirNorm = {actuatorDiskDVs_[diskName][3], actuatorDiskDVs_[diskName][4], actuatorDiskDVs_[diskName][5]}; @@ -432,12 +420,12 @@ void DAFvSourceActuatorDisk::calcFvSource(volVectorField& fvSource) } } } - } - else - { - FatalErrorIn("calcFvSourceCells") << "source: " << source0 << " not supported!" - << "Options are: cylinderAnnulusToCell and cylinderAnnulusSmooth!" - << abort(FatalError); + else + { + FatalErrorIn("calcFvSourceCells") << "source: " << source << " not supported!" + << "Options are: cylinderAnnulusToCell and cylinderAnnulusSmooth!" + << abort(FatalError); + } } fvSource.correctBoundaryConditions(); diff --git a/src/adjoint/DAFvSource/DAFvSourceHeatSource.C b/src/adjoint/DAFvSource/DAFvSourceHeatSource.C index 3fff3688..69ba2abf 100755 --- a/src/adjoint/DAFvSource/DAFvSourceHeatSource.C +++ b/src/adjoint/DAFvSource/DAFvSourceHeatSource.C @@ -24,6 +24,8 @@ DAFvSourceHeatSource::DAFvSourceHeatSource( const DAIndex& daIndex) : DAFvSource(modelType, mesh, daOption, daModel, daIndex) { + printInterval_ = daOption.getOption