From a5bba640354756e77ab7f5b69a1b8f1593e127d9 Mon Sep 17 00:00:00 2001 From: sergio Date: Mon, 20 Sep 2021 15:29:41 +0100 Subject: [PATCH] ENH: Adding speciesSorption BC and patchCellsSource fvOption speciesSorption is a zeroGradient BC which absorbs mass given by a first order time derivative, absoprtion rate and an equilibrium value calculated based on internal species values next to the wall. patchCellsSource is a source fvOption which applies to the corresponding species and apply the source calculated on the speciesSorption BC. A new abstract virtual class was created to group BC's which don't introduce a source to the matrix (i.e zeroGradient) but calculate a mass sink/source which should be introduced into the matrix. This is done through the fvOption patchCellsSource. --- src/fvOptions/Make/files | 1 + src/fvOptions/Make/options | 4 +- .../patchCellsSource/patchCellsSource.C | 195 ++++++++ .../patchCellsSource/patchCellsSource.H | 162 +++++++ .../reactionThermo/Make/files | 2 + .../reactionThermo/Make/options | 2 + .../boundarySourcePatch/boundarySourcePatch.C | 38 ++ .../boundarySourcePatch/boundarySourcePatch.H | 78 +++ .../speciesSorptionFvPatchScalarField.C | 459 ++++++++++++++++++ .../speciesSorptionFvPatchScalarField.H | 322 ++++++++++++ 10 files changed, 1262 insertions(+), 1 deletion(-) create mode 100644 src/fvOptions/sources/derived/patchCellsSource/patchCellsSource.C create mode 100644 src/fvOptions/sources/derived/patchCellsSource/patchCellsSource.H create mode 100644 src/thermophysicalModels/reactionThermo/derivedFvPatchFields/boundarySourcePatch/boundarySourcePatch.C create mode 100644 src/thermophysicalModels/reactionThermo/derivedFvPatchFields/boundarySourcePatch/boundarySourcePatch.H create mode 100644 src/thermophysicalModels/reactionThermo/derivedFvPatchFields/speciesSorption/speciesSorptionFvPatchScalarField.C create mode 100644 src/thermophysicalModels/reactionThermo/derivedFvPatchFields/speciesSorption/speciesSorptionFvPatchScalarField.H diff --git a/src/fvOptions/Make/files b/src/fvOptions/Make/files index 534e9cd2d7..7ede376606 100644 --- a/src/fvOptions/Make/files +++ b/src/fvOptions/Make/files @@ -36,6 +36,7 @@ $(derivedSources)/tabulatedAccelerationSource/tabulatedAccelerationSource.C $(derivedSources)/tabulatedAccelerationSource/tabulated6DoFAcceleration/tabulated6DoFAcceleration.C $(derivedSources)/viscousDissipation/viscousDissipation.C $(derivedSources)/buoyancyTurbSource/buoyancyTurbSource.C +$(derivedSources)/patchCellsSource/patchCellsSource.C interRegion = sources/interRegion $(interRegion)/interRegionHeatTransfer/interRegionHeatTransferModel/interRegionHeatTransferModel.C diff --git a/src/fvOptions/Make/options b/src/fvOptions/Make/options index 5da3ef931d..1e51ded765 100644 --- a/src/fvOptions/Make/options +++ b/src/fvOptions/Make/options @@ -8,6 +8,7 @@ EXE_INC = \ -I$(LIB_SRC)/thermophysicalModels/basic/lnInclude \ -I$(LIB_SRC)/thermophysicalModels/specie/lnInclude \ -I$(LIB_SRC)/thermophysicalModels/solidThermo/lnInclude \ + -I$(LIB_SRC)/thermophysicalModels/reactionThermo/lnInclude \ -I$(LIB_SRC)/TurbulenceModels/turbulenceModels/lnInclude \ -I$(LIB_SRC)/TurbulenceModels/compressible/lnInclude \ -I$(LIB_SRC)/TurbulenceModels/incompressible/lnInclude @@ -20,4 +21,5 @@ LIB_LIBS = \ -lsolidThermo \ -lturbulenceModels \ -lincompressibleTurbulenceModels \ - -lcompressibleTurbulenceModels + -lcompressibleTurbulenceModels \ + -lreactionThermophysicalModels diff --git a/src/fvOptions/sources/derived/patchCellsSource/patchCellsSource.C b/src/fvOptions/sources/derived/patchCellsSource/patchCellsSource.C new file mode 100644 index 0000000000..7d5b74f643 --- /dev/null +++ b/src/fvOptions/sources/derived/patchCellsSource/patchCellsSource.C @@ -0,0 +1,195 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2022 OpenCFD Ltd. +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see . + +\*---------------------------------------------------------------------------*/ + +#include "patchCellsSource.H" +#include "fvMatrices.H" +#include "addToRunTimeSelectionTable.H" +#include "boundarySourcePatch.H" + +// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // + +namespace Foam +{ +namespace fv +{ + defineTypeNameAndDebug(patchCellsSource, 0); + addToRunTimeSelectionTable(option, patchCellsSource, dictionary); +} +} + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::fv::patchCellsSource::patchCellsSource +( + const word& sourceName, + const word& modelType, + const dictionary& dict, + const fvMesh& mesh +) +: + fv::option(sourceName, modelType, dict, mesh), + curTimeIndex_(-1), + UName_(coeffs_.getOrDefault("U", "none")), + hName_(coeffs_.getOrDefault("he", "none")), + speciesName_(coeffs_.getOrDefault("species", "none")) +{ + label nFields = 0; + if (UName_ != "none") + { + nFields++; + } + if (hName_ != "none") + { + nFields++; + } + if (speciesName_ != "none") + { + nFields++; + } + + if (nFields > 1) + { + FatalErrorInFunction + << "patchCellsSource : " + << " cannot be used for more than one field." + << exit(FatalError); + } + + fieldNames_.resize(1); + if (speciesName_ != "none") + { + fieldNames_[0] = speciesName_; + } + + if (UName_ != "none") + { + fieldNames_[0] = UName_; + } + + if (hName_ != "none") + { + fieldNames_[0] = hName_; + } + + fv::option::resetApplied(); +} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +void Foam::fv::patchCellsSource::addSup +( + const volScalarField& rho, + fvMatrix& eqn, + const label fieldi +) +{ + if (debug) + { + Info<< type() << ": applying source to " << eqn.psi().name() << endl; + } + + if (curTimeIndex_ == mesh_.time().timeIndex()) + { + return; + } + + volScalarField* psiPtr; + + // If source applied to he, we need to loop over T for BC's + if (hName_ != "none") + { + psiPtr = mesh_.getObjectPtr("T"); + } + else + { + auto* psi = + mesh_.getObjectPtr(eqn.psi().name()); + + psiPtr = psi; + } + + const volScalarField::Boundary& psib = psiPtr->boundaryField(); + + volScalarField mDot + ( + IOobject + ( + "mDot", + mesh_.time().timeName(), + mesh_, + IOobject::NO_READ, + IOobject::NO_WRITE + ), + mesh_, + dimensionedScalar(eqn.dimensions()/dimVolume, Zero) + ); + + forAll(psib, patchi) + { + if (isA(psib[patchi])) + { + const boundarySourcePatch& pp = + refCast(psib[patchi]); + + const labelUList& fc = mesh_.boundary()[patchi].faceCells(); + + tmp tsb = pp.patchSource(); + const scalarField& sb = tsb.cref(); + + forAll(fc, facei) + { + const label celli = fc[facei]; + mDot[celli] += sb[facei]; + } + } + } + eqn += mDot; + + curTimeIndex_ = mesh_.time().timeIndex(); + + if (debug) + { + Info<< " Field source rate min/max : " + << gMin(mDot) << " / " << gMax(mDot) << endl; + } +} + + +bool Foam::fv::patchCellsSource::read(const dictionary& dict) +{ + if (!fv::option::read(dict)) + { + return false; + } + + return true; +} + + +// ************************************************************************* // diff --git a/src/fvOptions/sources/derived/patchCellsSource/patchCellsSource.H b/src/fvOptions/sources/derived/patchCellsSource/patchCellsSource.H new file mode 100644 index 0000000000..6c39b97012 --- /dev/null +++ b/src/fvOptions/sources/derived/patchCellsSource/patchCellsSource.H @@ -0,0 +1,162 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2022 OpenCFD Ltd. +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see . + +Class + Foam::fv::patchCellsSource + +Group + grpFvOptionsSources + +Description + Source defined by a boundary condition applied to cells next to patches. + This fvOption needs to be used with a \c boundarySourcePatch type of + boundary condition (e.g. \c speciesSorption and \c enthalpySorption.) + +Usage + Minimal example by using \c constant/fvOptions: + \verbatim + + { + // Mandatory entries + type patchCellsSource; + + // Optional entries + U ; + he ; + species ; + + // Inherited entries + ... + } + \endverbatim + + where the entries mean: + \table + Property | Description | Type | Reqd | Deflt + type | Type name: patchCellsSource | word | yes | - + U | Name of operand velocity field | word | no | none + he | Name of operand energy field | word | no | none + species | Name of operand species field | word | no | none + \endtable + + The inherited entries are elaborated in: + - \link cellSetOption.H \endlink + +SourceFiles + patchCellsSource.C + +\*---------------------------------------------------------------------------*/ + +#ifndef Foam_fv_patchCellsSource_H +#define Foam_fv_patchCellsSource_H + +#include "fvMesh.H" +#include "volFields.H" +#include "cellSetOption.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ +namespace fv +{ + +/*---------------------------------------------------------------------------*\ + Class patchCellsSource Declaration +\*---------------------------------------------------------------------------*/ + +class patchCellsSource +: + public fv::option +{ + // Private Data + + //- Current time index (used for updating) + label curTimeIndex_; + + //- Name of operand velocity field + word UName_; + + //- Name of operand energy field + word hName_; + + //- Name of operand species field + word speciesName_; + + +public: + + //- Runtime type information + TypeName("patchCellsSource"); + + + // Constructors + + //- Construct from explicit source name and mesh + patchCellsSource + ( + const word& sourceName, + const word& modelType, + const dictionary& dict, + const fvMesh& mesh + ); + + //- No copy construct + patchCellsSource(const patchCellsSource&) = delete; + + //- No copy assignment + void operator=(const patchCellsSource&) = delete; + + + //- Destructor + virtual ~patchCellsSource() = default; + + + // Member Functions + + //- Add explicit contribution to compressible enthalpy equation + virtual void addSup + ( + const volScalarField& rho, + fvMatrix& eqn, + const label fieldi + ); + + //- Read source dictionary + virtual bool read(const dictionary& dict); +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace fv +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + + +#endif + +// ************************************************************************* // diff --git a/src/thermophysicalModels/reactionThermo/Make/files b/src/thermophysicalModels/reactionThermo/Make/files index e31e2076ff..f72e0ff4a6 100644 --- a/src/thermophysicalModels/reactionThermo/Make/files +++ b/src/thermophysicalModels/reactionThermo/Make/files @@ -18,6 +18,8 @@ rhoReactionThermo/rhoReactionThermos.C derivedFvPatchFields/fixedUnburntEnthalpy/fixedUnburntEnthalpyFvPatchScalarField.C derivedFvPatchFields/gradientUnburntEnthalpy/gradientUnburntEnthalpyFvPatchScalarField.C derivedFvPatchFields/mixedUnburntEnthalpy/mixedUnburntEnthalpyFvPatchScalarField.C +derivedFvPatchFields/speciesSorption/speciesSorptionFvPatchScalarField.C +derivedFvPatchFields/boundarySourcePatch/boundarySourcePatch.C functionObjects/moleFractions/moleFractionsFunctionObjects.C diff --git a/src/thermophysicalModels/reactionThermo/Make/options b/src/thermophysicalModels/reactionThermo/Make/options index 9b2471488b..8982d92338 100644 --- a/src/thermophysicalModels/reactionThermo/Make/options +++ b/src/thermophysicalModels/reactionThermo/Make/options @@ -1,5 +1,6 @@ EXE_INC = \ -I$(LIB_SRC)/finiteVolume/lnInclude \ + -I$(LIB_SRC)/meshTools/lnInclude \ -I$(LIB_SRC)/transportModels/compressible/lnInclude \ -I$(LIB_SRC)/thermophysicalModels/basic/lnInclude \ -I$(LIB_SRC)/thermophysicalModels/specie/lnInclude \ @@ -9,6 +10,7 @@ EXE_INC = \ LIB_LIBS = \ -lfiniteVolume \ + -lmeshTools \ -lcompressibleTransportModels \ -lfluidThermophysicalModels \ -lspecie \ diff --git a/src/thermophysicalModels/reactionThermo/derivedFvPatchFields/boundarySourcePatch/boundarySourcePatch.C b/src/thermophysicalModels/reactionThermo/derivedFvPatchFields/boundarySourcePatch/boundarySourcePatch.C new file mode 100644 index 0000000000..4188677dd4 --- /dev/null +++ b/src/thermophysicalModels/reactionThermo/derivedFvPatchFields/boundarySourcePatch/boundarySourcePatch.C @@ -0,0 +1,38 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2022 OpenCFD Ltd. +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see . + +\*---------------------------------------------------------------------------*/ + +#include "boundarySourcePatch.H" + +// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // + +namespace Foam +{ + defineTypeNameAndDebug(boundarySourcePatch, 0); +} + + +// ************************************************************************* // diff --git a/src/thermophysicalModels/reactionThermo/derivedFvPatchFields/boundarySourcePatch/boundarySourcePatch.H b/src/thermophysicalModels/reactionThermo/derivedFvPatchFields/boundarySourcePatch/boundarySourcePatch.H new file mode 100644 index 0000000000..79bf4d3837 --- /dev/null +++ b/src/thermophysicalModels/reactionThermo/derivedFvPatchFields/boundarySourcePatch/boundarySourcePatch.H @@ -0,0 +1,78 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2022 OpenCFD Ltd. +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see . + +Class + Foam::boundarySourcePatch + +Description + Pure virtual class for sources on cells next to patches. + +SourceFiles + boundarySourcePatch.C + +\*---------------------------------------------------------------------------*/ + +#ifndef Foam_boundarySourcePatch_H +#define Foam_boundarySourcePatch_H + +#include "scalarField.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +/*---------------------------------------------------------------------------*\ + Class boundarySourcePatch Declaration +\*---------------------------------------------------------------------------*/ + +class boundarySourcePatch +{ +public: + + //- Runtime type information + TypeName("boundarySourcePatch"); + + + //- Destructor + virtual ~boundarySourcePatch() = default; + + + // Member Functions + + //- Source applied on cells of the patch + virtual tmp patchSource() const = 0; +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/thermophysicalModels/reactionThermo/derivedFvPatchFields/speciesSorption/speciesSorptionFvPatchScalarField.C b/src/thermophysicalModels/reactionThermo/derivedFvPatchFields/speciesSorption/speciesSorptionFvPatchScalarField.C new file mode 100644 index 0000000000..b64b3f2052 --- /dev/null +++ b/src/thermophysicalModels/reactionThermo/derivedFvPatchFields/speciesSorption/speciesSorptionFvPatchScalarField.C @@ -0,0 +1,459 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2022 OpenCFD Ltd. +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see . + +\*---------------------------------------------------------------------------*/ + +#include "speciesSorptionFvPatchScalarField.H" +#include "addToRunTimeSelectionTable.H" +#include "fvPatchFieldMapper.H" +#include "volFields.H" +#include "rhoReactionThermo.H" + +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +const Foam::Enum +< + Foam::speciesSorptionFvPatchScalarField::equilibriumModelType +> +Foam::speciesSorptionFvPatchScalarField::equilibriumModelTypeNames +({ + { equilibriumModelType::LANGMUIR, "Langmuir" } +}); + + +const Foam::Enum +< + Foam::speciesSorptionFvPatchScalarField::kineticModelType +> +Foam::speciesSorptionFvPatchScalarField::kinematicModelTypeNames +({ + { kineticModelType::PseudoFirstOrder, "PseudoFirstOrder" } +}); + + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +Foam::tmp +Foam::speciesSorptionFvPatchScalarField::calcMoleFractions() const +{ + auto tMole = tmp::New(patch().size(), 0); + scalarField& Mole = tMole.ref(); + + if (db().foundObject(basicThermo::dictName)) + { + const auto& thermo = db().lookupObject + ( + basicThermo::dictName + ); + + const PtrList& Y = thermo.composition().Y(); + + const volScalarField W(thermo.W()); + + const labelUList& faceCells = patch().faceCells(); + + const label speicesId = + thermo.composition().species()[this->internalField().name()]; + + const dimensionedScalar Wi + ( + dimMass/dimMoles, + thermo.composition().W(speicesId) + ); + + const volScalarField X(W*Y[speicesId]/Wi); + + forAll(faceCells, i) + { + const label cellId = faceCells[i]; + Mole[i] = X[cellId]; + } + } + else + { + FatalErrorInFunction + << "Thermo type is not 'rhoReactionThermo'. " << nl + << "This BC is designed to operate with a rho based thermo." + << exit(FatalError); + } + + return tMole; +} + + +Foam::volScalarField& +Foam::speciesSorptionFvPatchScalarField::field +( + const word& fieldName, + const dimensionSet& dim +) const +{ + const fvMesh& mesh = this->internalField().mesh(); + auto* ptr = mesh.getObjectPtr(fieldName); + + if (!ptr) + { + ptr = new volScalarField + ( + IOobject + ( + fieldName, + mesh.time().timeName(), + mesh, + IOobject::NO_READ, + IOobject::AUTO_WRITE + ), + mesh, + dimensionedScalar(dim, Zero) + ); + + ptr->store(); + } + + return *ptr; +} + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::speciesSorptionFvPatchScalarField::speciesSorptionFvPatchScalarField +( + const fvPatch& p, + const DimensionedField& iF +) +: + zeroGradientFvPatchScalarField(p, iF), + equilibriumModel_(equilibriumModelType::LANGMUIR), + kinematicModel_(kineticModelType::PseudoFirstOrder), + thicknessPtr_(nullptr), + kabs_(1), + kl_(0), + max_(1), + rhoS_(0), + pName_("p"), + dfldp_(p.size(), 0), + mass_(p.size(), 0) +{} + + +Foam::speciesSorptionFvPatchScalarField::speciesSorptionFvPatchScalarField +( + const fvPatch& p, + const DimensionedField& iF, + const dictionary& dict +) +: + zeroGradientFvPatchScalarField(p, iF, dict), + equilibriumModel_(equilibriumModelTypeNames.get("equilibriumModel", dict)), + kinematicModel_(kinematicModelTypeNames.get("kinematicModel", dict)), + thicknessPtr_(PatchFunction1::New(p.patch(), "thickness", dict)), + kabs_(dict.getCheck("kabs", scalarMinMax::ge(0))), + kl_(dict.getCheck("kl", scalarMinMax::ge(0))), + max_(dict.getCheck("max", scalarMinMax::ge(0))), + rhoS_(dict.get("rhoS")), + pName_(dict.getOrDefault("p", "p")), + dfldp_ + ( + dict.found("dfldp") + ? scalarField("dfldp", dict, p.size()) + : scalarField(p.size(), 0) + ), + mass_ + ( + dict.found("mass") + ? scalarField("mass", dict, p.size()) + : scalarField(p.size(), 0) + ) +{ + if (dict.found("value")) + { + fvPatchScalarField::operator= + ( + scalarField("value", dict, p.size()) + ); + } + else + { + fvPatchField::operator=(Zero); + } +} + + +Foam::speciesSorptionFvPatchScalarField::speciesSorptionFvPatchScalarField +( + const speciesSorptionFvPatchScalarField& ptf, + const fvPatch& p, + const DimensionedField& iF, + const fvPatchFieldMapper& mapper +) +: + zeroGradientFvPatchScalarField(ptf, p, iF, mapper), + equilibriumModel_(ptf.equilibriumModel_), + kinematicModel_(ptf.kinematicModel_), + thicknessPtr_(ptf.thicknessPtr_.clone(patch().patch())), + kabs_(ptf.kabs_), + kl_(ptf.kl_), + max_(ptf.max_), + rhoS_(ptf.rhoS_), + pName_(ptf.pName_), + dfldp_(ptf.dfldp_, mapper), + mass_(ptf.mass_, mapper) +{} + + +Foam::speciesSorptionFvPatchScalarField::speciesSorptionFvPatchScalarField +( + const speciesSorptionFvPatchScalarField& ptf +) +: + zeroGradientFvPatchScalarField(ptf), + equilibriumModel_(ptf.equilibriumModel_), + kinematicModel_(ptf.kinematicModel_), + thicknessPtr_(ptf.thicknessPtr_.clone(patch().patch())), + kabs_(ptf.kabs_), + kl_(ptf.kl_), + max_(ptf.max_), + rhoS_(ptf.rhoS_), + pName_(ptf.pName_), + dfldp_(ptf.dfldp_), + mass_(ptf.mass_) +{} + + +Foam::speciesSorptionFvPatchScalarField::speciesSorptionFvPatchScalarField +( + const speciesSorptionFvPatchScalarField& ptf, + const DimensionedField& iF +) +: + zeroGradientFvPatchScalarField(ptf, iF), + equilibriumModel_(ptf.equilibriumModel_), + kinematicModel_(ptf.kinematicModel_), + thicknessPtr_(ptf.thicknessPtr_.clone(patch().patch())), + kabs_(ptf.kabs_), + kl_(ptf.kl_), + max_(ptf.max_), + rhoS_(ptf.rhoS_), + pName_(ptf.pName_), + dfldp_(ptf.dfldp_), + mass_(ptf.mass_) +{} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +void Foam::speciesSorptionFvPatchScalarField::autoMap +( + const fvPatchFieldMapper& m +) +{ + zeroGradientFvPatchScalarField::autoMap(m); + + dfldp_.autoMap(m); + mass_.autoMap(m); + + if (thicknessPtr_) + { + thicknessPtr_->autoMap(m); + } +} + + +void Foam::speciesSorptionFvPatchScalarField::rmap +( + const fvPatchScalarField& ptf, + const labelList& addr +) +{ + zeroGradientFvPatchScalarField::rmap(ptf, addr); + + const auto& tiptf = refCast(ptf); + + dfldp_.rmap(tiptf.dfldp_, addr); + mass_.rmap(tiptf.mass_, addr); + + if (thicknessPtr_) + { + thicknessPtr_->rmap(tiptf.thicknessPtr_(), addr); + } +} + + +Foam::tmp Foam::speciesSorptionFvPatchScalarField:: +patchSource() const +{ + const auto& thermo = db().lookupObject + ( + basicThermo::dictName + ); + + const label speicesId = + thermo.composition().species()[this->internalField().name()]; + + const scalar Wi(thermo.composition().W(speicesId)); + + const scalar t = db().time().timeOutputValue(); + + const scalarField h(thicknessPtr_->value(t)); + + const scalarField AbyV(this->patch().magSf()); + + // Solid mass [kg] + const scalarField mass(h*AbyV*rhoS_); + + scalarField Vol(this->patch().size()); + + forAll(AbyV, facei) + { + const label faceCelli = this->patch().faceCells()[facei]; + Vol[facei] = this->internalField().mesh().V()[faceCelli]; + } + + // The moles absorbed by the solid + // dfldp[mol/kg/sec]* mass[kg]* Wi[kg/mol] / Vol[m3]= [kg/sec/m3] + const scalarField dfldp(-dfldp_*mass*Wi*1e-3/Vol); + + if (debug) + { + Info<< " Patch mass rate min/max [kg/m3/sec]: " + << gMin(dfldp) << " - " << gMax(dfldp) << endl; + } + + return tmp::New(dfldp); +} + + +Foam::tmp Foam::speciesSorptionFvPatchScalarField:: +mass() const +{ + return tmp::New(mass_); +} + + +void Foam::speciesSorptionFvPatchScalarField::updateCoeffs() +{ + if (updated()) + { + return; + } + + // equilibrium in mol/kg + scalarField cEq(patch().size(), 0); + + switch (equilibriumModel_) + { + case equilibriumModelType::LANGMUIR: + { + // mole fraction + tmp tco = calcMoleFractions(); + + const fvPatchField& pp = + patch().lookupPatchField(pName_); + + cEq = max_*(kl_*tco()*pp/(1 + kl_*tco()*pp)); + break; + } + default: + break; + } + + // source [mol/kg/sec] + dfldp_ = Zero; + + switch (kinematicModel_) + { + case kineticModelType::PseudoFirstOrder: + { + dfldp_ = kabs_*(cEq - mass_); + } + default: + break; + } + + // mass [mol/kg] + const scalar dt = db().time().deltaTValue(); + mass_ += dfldp_*dt; + mass_ = max(mass_, scalar(0)); + + scalarField& pMass = + field + ( + "absorbedMass" + this->internalField().name(), + dimensionSet(dimMoles/dimMass) + ).boundaryFieldRef()[patch().index()]; + + pMass = mass_; + + if (debug) + { + Info<< " Absorption rate min/max [mol/kg/sec]: " + << gMin(dfldp_) << " - " << gMax(dfldp_) << endl; + } + + zeroGradientFvPatchScalarField::updateCoeffs(); +} + + +void Foam::speciesSorptionFvPatchScalarField::write(Ostream& os) const +{ + fvPatchScalarField::write(os); + + os.writeEntry + ( + "equilibriumModel", equilibriumModelTypeNames[equilibriumModel_] + ); + os.writeEntry + ( + "kinematicModel", kinematicModelTypeNames[kinematicModel_] + ); + if (thicknessPtr_) + { + thicknessPtr_->writeData(os); + } + os.writeEntry("kabs", kabs_); + os.writeEntry("kl", kl_); + os.writeEntry("max", max_); + os.writeEntry("rhoS", rhoS_); + + dfldp_.writeEntry("dfldp", os); + mass_.writeEntry("mass", os); + os.writeEntryIfDifferent("p", "p", pName_); + + writeEntry("value", os); +} + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + makePatchTypeField + ( + fvPatchScalarField, + speciesSorptionFvPatchScalarField + ); +} + +// ************************************************************************* // diff --git a/src/thermophysicalModels/reactionThermo/derivedFvPatchFields/speciesSorption/speciesSorptionFvPatchScalarField.H b/src/thermophysicalModels/reactionThermo/derivedFvPatchFields/speciesSorption/speciesSorptionFvPatchScalarField.H new file mode 100644 index 0000000000..140492c752 --- /dev/null +++ b/src/thermophysicalModels/reactionThermo/derivedFvPatchFields/speciesSorption/speciesSorptionFvPatchScalarField.H @@ -0,0 +1,322 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2022 OpenCFD Ltd. +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see . + +Class + Foam::speciesSorptionFvPatchScalarField + +Group + grpGenericBoundaryConditions + +Description + This boundary condition provides a first-order zero-gradient + condition for a given scalar field to model time-dependent + adsorption-desorption processes. + + \f[ + \frac{d c}{d t} = k_{ads} (c_{eq} - c_{abs}) + \f] + + where + \vartable + c_{eq} | Equilibrium concentration + c_{abs} | Absorbed at wall + k_{ads} | Adsorption rate constant [1/s] + \endvartable + + \f[ + c_{eq} = c_{max} \frac{k_l \, c_{int}}{1 + k_l \, c_{int}} + \f] + + where + \vartable + c_{max} | Maximum concentration + k_l | Langmuir constant + c_{int} | Local cell value concentration + \endvartable + +Usage + Example of the boundary condition specification: + \verbatim + + { + // Mandatory entries + type speciesSorption; + equilibriumModel ; + kinematicModel ; + kabs ; + kl ; + max ; + thickness >; + rhoS ; + + // Optional entries + dfldp ; + mass ; + pName ; + + // Inherited entries + ... + } + \endverbatim + + where the entries mean: + \table + Property | Description | Type | Reqd | Deflt + type | Type name: speciesSorption | word | yes | - + equilibriumModel | Equilibrium model | word | yes | - + kinematicModel | Kinematic model | word | yes | - + kabs | Adsorption rate constant [1/s] | scalar | yes | - + kl | Langmuir constant [1/Pa] | scalar | yes | - + max | Maximum concentation at wall [mol/kg] | scalar | yes | - + thickness| Solid thickness along the patch | PatchFunction1\ | yes | - + rhoS | Solid density | scalar | yes | - + dfldp | Source on cells next to patch | scalarField | no | Zero + mass | Absorbed mass per kg of absorbent [mol/kg] | scalarField | no | Zero + pName | Name of operand pressure field | word | no | p + \endtable + + Options for the \c equilibriumModel entry: + \verbatim + Langmuir | Langmuir model + \endverbatim + + Options for the \c kinematicModel entry: + \verbatim + PseudoFirstOrder | Pseudo first-order model + \endverbatim + + The inherited entries are elaborated in: + - \link zeroGradientFvPatchFields.H \endlink + - \link PatchFunction1.H \endlink + +SourceFiles + speciesSorptionFvPatchScalarField.C + +\*---------------------------------------------------------------------------*/ + +#ifndef Foam_speciesSorptionFvPatchScalarField_H +#define Foam_speciesSorptionFvPatchScalarField_H + +#include "boundarySourcePatch.H" +#include "zeroGradientFvPatchFields.H" +#include "PatchFunction1.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +/*---------------------------------------------------------------------------*\ + Class speciesSorptionFvPatchScalarField Declaration +\*---------------------------------------------------------------------------*/ + +class speciesSorptionFvPatchScalarField +: + public zeroGradientFvPatchField, + public boundarySourcePatch +{ +public: + + // Public Enumeration + + //- Options for the equilibrum model + enum equilibriumModelType : char + { + LANGMUIR = 0 + }; + + //- Options for the kinematic model + enum kineticModelType : char + { + PseudoFirstOrder = 0 + }; + + //- Names for equilibriumModelType + static const Enum equilibriumModelTypeNames; + + //- Names for kineticModelType + static const Enum kinematicModelTypeNames; + + +private: + + // Private Data + + //- Equilibrium model + enum equilibriumModelType equilibriumModel_; + + //- Kinematic model + enum kineticModelType kinematicModel_; + + //- Solid thickness along the patch + autoPtr> thicknessPtr_; + + //- Adsorption rate constant [1/sec] + scalar kabs_; + + //- Langmuir adsorption constant [1/Pa] + scalar kl_; + + //- Maximum density on patch [mol/kg] + scalar max_; + + //- Solid density + scalar rhoS_; + + //- Name of operand pressure field + word pName_; + + //- Source on cells next to patch [mol/kg/sec] + scalarField dfldp_; + + //- Absorbed mass per kg of absorbent [mol/kg] + scalarField mass_; + + + // Private Member Functions + + //- Calculate the mole fraction fields + tmp calcMoleFractions() const; + + //- Lookup (or create) field for output + volScalarField& field(const word&, const dimensionSet&) const; + + +public: + + //- Runtime type information + TypeName("speciesSorption"); + + + // Constructors + + //- Construct from patch and internal field + speciesSorptionFvPatchScalarField + ( + const fvPatch&, + const DimensionedField& + ); + + //- Construct from patch, internal field and dictionary + speciesSorptionFvPatchScalarField + ( + const fvPatch&, + const DimensionedField&, + const dictionary& + ); + + //- Construct by mapping given + //- speciesSorptionFvPatchScalarField onto a new patch + speciesSorptionFvPatchScalarField + ( + const speciesSorptionFvPatchScalarField&, + const fvPatch&, + const DimensionedField&, + const fvPatchFieldMapper& + ); + + //- Construct as copy + speciesSorptionFvPatchScalarField + ( + const speciesSorptionFvPatchScalarField& + ); + + //- Construct and return a clone + virtual tmp clone() const + { + return tmp + ( + new speciesSorptionFvPatchScalarField(*this) + ); + } + + //- Construct as copy setting internal field reference + speciesSorptionFvPatchScalarField + ( + const speciesSorptionFvPatchScalarField&, + const DimensionedField& + ); + + //- Construct and return a clone setting internal field reference + virtual tmp clone + ( + const DimensionedField& iF + ) const + { + return tmp + ( + new speciesSorptionFvPatchScalarField(*this, iF) + ); + } + + + // Member Functions + + // Mapping + + //- Map (and resize as needed) from self given a mapping object + virtual void autoMap + ( + const fvPatchFieldMapper& + ); + + //- Reverse map the given fvPatchField onto this fvPatchField + virtual void rmap + ( + const fvPatchScalarField&, + const labelList& + ); + + + // Evaluation + + //- Source of cells next to the patch + virtual tmp patchSource() const; + + //- Access to mass + tmp mass() const; + + //- Update the coefficients associated with the patch field + virtual void updateCoeffs(); + + + // I-O + + //- Write + virtual void write(Ostream&) const; +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* //