InterfaceCompositionPhaseChangePhaseSystem: Fixes to species transfer

The handling of species transfer within the interface-composition phase
change system has been sigificantly altered. The explicit-implicit
caching of the mass transfer has been removed and been replaced with
storage of an Su-Sp coefficient pair. The mass transfer is now generated
on the fly from these coefficients.

These fixes resolve a number of issues involving multiple species for
which the pimple loop did not converge to a conservative solution. It
also removes the requirement for a second evaluation of the mass
transfer after solution of the species fraction equations.

This work was supported by Zhen Li, at Evonik
This commit is contained in:
Will Bainbridge
2018-05-25 14:02:12 +01:00
parent c259eac3e7
commit 6701c09d21
9 changed files with 272 additions and 195 deletions

View File

@ -37,14 +37,50 @@ Foam::InterfaceCompositionPhaseChangePhaseSystem<BasePhaseSystem>::iDmdt
const phasePairKey& key const phasePairKey& key
) const ) const
{ {
if (!iDmdt_.found(key)) tmp<volScalarField> tIDmdt = phaseSystem::dmdt(key);
const phasePair unorderedPair
(
this->phases()[key.first()],
this->phases()[key.second()]
);
forAllConstIter(phasePair, unorderedPair, iter)
{ {
return phaseSystem::dmdt(key); const phaseModel& phase = iter();
const phaseModel& otherPhase = iter.otherPhase();
const phasePair pair(phase, otherPhase, true);
if (interfaceCompositionModels_.found(pair))
{
const scalar iDmdtSign = Pair<word>::compare(pair, key);
forAllConstIter
(
hashedWordList,
interfaceCompositionModels_[pair]->species(),
memberIter
)
{
const word& member = *memberIter;
const word name(IOobject::groupName(member, phase.name()));
const word otherName
(
IOobject::groupName(member, otherPhase.name())
);
tIDmdt.ref() +=
iDmdtSign
*(
*(*iDmdtSu_[pair])[member]
+ *(*iDmdtSp_[pair])[member]*phase.Y(member)
);
}
}
} }
const scalar dmdtSign(Pair<word>::compare(iDmdt_.find(key).key(), key)); return tIDmdt;
return dmdtSign**iDmdt_[key];
} }
@ -58,7 +94,11 @@ InterfaceCompositionPhaseChangePhaseSystem
const fvMesh& mesh const fvMesh& mesh
) )
: :
BasePhaseSystem(mesh) BasePhaseSystem(mesh),
nInterfaceCorrectors_
(
this->template lookupOrDefault<label>("nInterfaceCorrectors", 1)
)
{ {
this->generatePairsAndSubModels this->generatePairsAndSubModels
( (
@ -127,48 +167,36 @@ InterfaceCompositionPhaseChangePhaseSystem
} }
} }
// Generate mass transfer fields, set to zero // Generate mass transfer fields, initially assumed to be zero
forAllConstIter forAllConstIter
( (
phaseSystem::phasePairTable, interfaceCompositionModelTable,
this->phasePairs_, interfaceCompositionModels_,
phasePairIter interfaceCompositionModelIter
) )
{ {
const phasePair& pair(phasePairIter()); const interfaceCompositionModel& compositionModel =
interfaceCompositionModelIter();
if (pair.ordered()) const phasePair& pair =
this->phasePairs_[interfaceCompositionModelIter.key()];
const phasePair unorderedPair(pair.phase1(), pair.phase2());
iDmdtSu_.insert(pair, new HashPtrTable<volScalarField>());
iDmdtSp_.insert(pair, new HashPtrTable<volScalarField>());
forAllConstIter(hashedWordList, compositionModel.species(), memberIter)
{ {
continue; const word& member = *memberIter;
}
// Initially assume no mass transfer iDmdtSu_[pair]->insert
iDmdt_.insert
( (
pair, member,
new volScalarField new volScalarField
( (
IOobject IOobject
( (
IOobject::groupName("iDmdt", pair.name()), IOobject::groupName("iDmdtSu", pair.name()),
this->mesh().time().timeName(),
this->mesh(),
IOobject::READ_IF_PRESENT,
IOobject::AUTO_WRITE
),
this->mesh(),
dimensionedScalar("zero", dimDensity/dimTime, 0)
)
);
iDmdtExplicit_.insert
(
pair,
new volScalarField
(
IOobject
(
IOobject::groupName("iDmdtExplicit", pair.name()),
this->mesh().time().timeName(), this->mesh().time().timeName(),
this->mesh() this->mesh()
), ),
@ -176,6 +204,23 @@ InterfaceCompositionPhaseChangePhaseSystem
dimensionedScalar("zero", dimDensity/dimTime, 0) dimensionedScalar("zero", dimDensity/dimTime, 0)
) )
); );
iDmdtSp_[pair]->insert
(
member,
new volScalarField
(
IOobject
(
IOobject::groupName("iDmdtSp", pair.name()),
this->mesh().time().timeName(),
this->mesh()
),
this->mesh(),
dimensionedScalar("zero", dimDensity/dimTime, 0)
)
);
}
} }
} }
@ -207,13 +252,40 @@ Foam::InterfaceCompositionPhaseChangePhaseSystem<BasePhaseSystem>::dmdts() const
{ {
PtrList<volScalarField> dmdts(BasePhaseSystem::dmdts()); PtrList<volScalarField> dmdts(BasePhaseSystem::dmdts());
forAllConstIter(iDmdtTable, iDmdt_, iDmdtIter) forAllConstIter
(
interfaceCompositionModelTable,
interfaceCompositionModels_,
interfaceCompositionModelIter
)
{ {
const phasePair& pair = this->phasePairs_[iDmdtIter.key()]; const interfaceCompositionModel& compositionModel =
const volScalarField& iDmdt = *iDmdtIter(); interfaceCompositionModelIter();
this->addField(pair.phase1(), "dmdt", iDmdt, dmdts); const phasePair& pair =
this->addField(pair.phase2(), "dmdt", - iDmdt, dmdts); this->phasePairs_[interfaceCompositionModelIter.key()];
const phaseModel& phase = pair.phase1();
const phaseModel& otherPhase = pair.phase2();
forAllConstIter(hashedWordList, compositionModel.species(), memberIter)
{
const word& member = *memberIter;
const word name(IOobject::groupName(member, phase.name()));
const word otherName
(
IOobject::groupName(member, otherPhase.name())
);
const volScalarField iDmdt
(
*(*iDmdtSu_[pair])[member]
+ *(*iDmdtSp_[pair])[member]*phase.Y(member)
);
this->addField(phase, "dmdt", iDmdt, dmdts);
this->addField(otherPhase, "dmdt", - iDmdt, dmdts);
}
} }
return dmdts.xfer(); return dmdts.xfer();
@ -230,28 +302,6 @@ massTransfer() const
phaseSystem::massTransferTable& eqns = eqnsPtr(); phaseSystem::massTransferTable& eqns = eqnsPtr();
// Reset the interfacial mass flow rates
forAllConstIter
(
phaseSystem::phasePairTable,
this->phasePairs_,
phasePairIter
)
{
const phasePair& pair(phasePairIter());
if (pair.ordered())
{
continue;
}
*this->iDmdt_[pair] =
*this->iDmdtExplicit_[pair];
*this->iDmdtExplicit_[pair] =
dimensionedScalar("zero", dimDensity/dimTime, 0);
}
// Sum up the contribution from each interface composition model // Sum up the contribution from each interface composition model
forAllConstIter forAllConstIter
( (
@ -260,72 +310,58 @@ massTransfer() const
interfaceCompositionModelIter interfaceCompositionModelIter
) )
{ {
const interfaceCompositionModel& compositionModel const interfaceCompositionModel& compositionModel =
( interfaceCompositionModelIter();
interfaceCompositionModelIter()
);
const phasePair& pair = const phasePair& pair =
this->phasePairs_[interfaceCompositionModelIter.key()]; this->phasePairs_[interfaceCompositionModelIter.key()];
const phaseModel& phase = pair.phase1(); const phaseModel& phase = pair.phase1();
const phaseModel& otherPhase = pair.phase2(); const phaseModel& otherPhase = pair.phase2();
const phasePairKey key(phase.name(), otherPhase.name()); const phasePair unorderedPair(phase, otherPhase);
const volScalarField& Tf(*this->Tf_[key]); const volScalarField& Tf(*this->Tf_[unorderedPair]);
volScalarField& iDmdtExplicit_(*this->iDmdtExplicit_[key]);
volScalarField& iDmdt_(*this->iDmdt_[key]);
scalar dmdtSign(Pair<word>::compare(this->iDmdt_.find(key).key(), key));
const volScalarField K const volScalarField K
( (
massTransferModels_[key][pair.index(phase)]->K() massTransferModels_[unorderedPair][pair.index(phase)]->K()
); );
forAllConstIter forAllConstIter(hashedWordList, compositionModel.species(), memberIter)
(
hashedWordList,
compositionModel.species(),
memberIter
)
{ {
const word& member = *memberIter; const word& member = *memberIter;
const word name const word name(IOobject::groupName(member, phase.name()));
(
IOobject::groupName(member, phase.name())
);
const word otherName const word otherName
( (
IOobject::groupName(member, otherPhase.name()) IOobject::groupName(member, otherPhase.name())
); );
const volScalarField KD const volScalarField KD(K*compositionModel.D(member));
const volScalarField Yf(compositionModel.Yf(member, Tf));
*(*iDmdtSu_[pair])[member] = phase.rho()*KD*Yf;
*(*iDmdtSp_[pair])[member] = - phase.rho()*KD;
const fvScalarMatrix eqn
( (
K*compositionModel.D(member) *(*iDmdtSu_[pair])[member]
+ fvm::Sp(*(*iDmdtSp_[pair])[member], phase.Y(member))
); );
const volScalarField Yf const volScalarField iDmdt
( (
compositionModel.Yf(member, Tf) *(*iDmdtSu_[pair])[member]
+ *(*iDmdtSp_[pair])[member]*phase.Y(member)
); );
// Implicit transport through the phase // Implicit transport through this phase
*eqns[name] += *eqns[name] += eqn;
phase.rho()*KD*Yf
- fvm::Sp(phase.rho()*KD, eqns[name]->psi());
// Sum the mass transfer rate
iDmdtExplicit_ += dmdtSign*phase.rho()*KD*Yf;
iDmdt_ -= dmdtSign*phase.rho()*KD*eqns[name]->psi();
// Explicit transport out of the other phase // Explicit transport out of the other phase
if (eqns.found(otherName)) if (eqns.found(otherName))
{ {
*eqns[otherName] -= *eqns[otherName] -= iDmdt;
otherPhase.rho()*KD*compositionModel.dY(member, Tf);
} }
} }
} }
@ -363,10 +399,14 @@ correctInterfaceThermo()
const phasePairKey key12(pair.first(), pair.second(), true); const phasePairKey key12(pair.first(), pair.second(), true);
const phasePairKey key21(pair.second(), pair.first(), true); const phasePairKey key21(pair.second(), pair.first(), true);
volScalarField H1(heatTransferModelIter().first()->K()); const volScalarField H1(heatTransferModelIter().first()->K());
volScalarField H2(heatTransferModelIter().second()->K()); const volScalarField H2(heatTransferModelIter().second()->K());
dimensionedScalar HSmall("small", heatTransferModel::dimK, small); const dimensionedScalar HSmall("small", heatTransferModel::dimK, small);
volScalarField& Tf = *this->Tf_[pair];
for (label i = 0; i < nInterfaceCorrectors_; ++ i)
{
volScalarField mDotL volScalarField mDotL
( (
IOobject IOobject
@ -390,8 +430,6 @@ correctInterfaceThermo()
dimensionedScalar("zero", mDotL.dimensions()/dimTemperature, 0) dimensionedScalar("zero", mDotL.dimensions()/dimTemperature, 0)
); );
volScalarField& Tf = *this->Tf_[pair];
// Add latent heats from forward and backward models // Add latent heats from forward and backward models
if (this->interfaceCompositionModels_.found(key12)) if (this->interfaceCompositionModels_.found(key12))
{ {
@ -444,6 +482,7 @@ correctInterfaceThermo()
this->interfaceCompositionModels_[key21]->update(Tf); this->interfaceCompositionModels_[key21]->update(Tf);
} }
} }
}
} }

View File

@ -77,6 +77,14 @@ protected:
phasePairKey::hash phasePairKey::hash
> massTransferModelTable; > massTransferModelTable;
typedef HashPtrTable
<
HashPtrTable<volScalarField>,
phasePairKey,
phasePairKey::hash
>
iDmdtSuSpTable;
typedef HashPtrTable<volScalarField, phasePairKey, phasePairKey::hash> typedef HashPtrTable<volScalarField, phasePairKey, phasePairKey::hash>
iDmdtTable; iDmdtTable;
@ -85,17 +93,20 @@ protected:
// Sub Models // Sub Models
//- The number of interface correctors
const label nInterfaceCorrectors_;
//- Mass transfer models //- Mass transfer models
massTransferModelTable massTransferModels_; massTransferModelTable massTransferModels_;
//- Interface composition models //- Interface composition models
interfaceCompositionModelTable interfaceCompositionModels_; interfaceCompositionModelTable interfaceCompositionModels_;
//- Interfacial Mass transfer rate //- The explicit part of the interfacial mass transfer rates
iDmdtTable iDmdt_; iDmdtSuSpTable iDmdtSu_;
//- Explicit part of the mass transfer rate //- The implicit part of the interfacial mass transfer rates
iDmdtTable iDmdtExplicit_; iDmdtSuSpTable iDmdtSp_;
// Protected member functions // Protected member functions
@ -127,7 +138,7 @@ public:
//- Return the mass transfer matrices //- Return the mass transfer matrices
virtual autoPtr<phaseSystem::massTransferTable> massTransfer() const; virtual autoPtr<phaseSystem::massTransferTable> massTransfer() const;
//- Correct the thermodynamics //- Correct the interface temperatures
virtual void correctInterfaceThermo(); virtual void correctInterfaceThermo();
//- Read base phaseProperties dictionary //- Read base phaseProperties dictionary

View File

@ -179,6 +179,14 @@ Foam::MultiComponentPhaseModel<BasePhaseModel>::Y() const
} }
template<class BasePhaseModel>
const Foam::volScalarField&
Foam::MultiComponentPhaseModel<BasePhaseModel>::Y(const word& name) const
{
return this->thermo_->composition().Y(name);
}
template<class BasePhaseModel> template<class BasePhaseModel>
Foam::PtrList<Foam::volScalarField>& Foam::PtrList<Foam::volScalarField>&
Foam::MultiComponentPhaseModel<BasePhaseModel>::YRef() Foam::MultiComponentPhaseModel<BasePhaseModel>::YRef()

View File

@ -90,6 +90,8 @@ public:
//- Correct the thermodynamics //- Correct the thermodynamics
virtual void correctThermo(); virtual void correctThermo();
// Species
//- Return whether the phase is pure (i.e., not multi-component) //- Return whether the phase is pure (i.e., not multi-component)
virtual bool pure() const; virtual bool pure() const;
@ -99,6 +101,9 @@ public:
//- Return the species mass fractions //- Return the species mass fractions
virtual const PtrList<volScalarField>& Y() const; virtual const PtrList<volScalarField>& Y() const;
//- Return a species mass fraction by name
virtual const volScalarField& Y(const word& name) const;
//- Access the species mass fractions //- Access the species mass fractions
virtual PtrList<volScalarField>& YRef(); virtual PtrList<volScalarField>& YRef();

View File

@ -78,6 +78,18 @@ Foam::PurePhaseModel<BasePhaseModel>::Y() const
} }
template<class BasePhaseModel>
const Foam::volScalarField&
Foam::PurePhaseModel<BasePhaseModel>::Y(const word& name) const
{
FatalErrorInFunction
<< "Cannot get a species fraction by name from a pure phase"
<< exit(FatalError);
return NullObjectRef<volScalarField>();
}
template<class BasePhaseModel> template<class BasePhaseModel>
Foam::PtrList<Foam::volScalarField>& Foam::PtrList<Foam::volScalarField>&
Foam::PurePhaseModel<BasePhaseModel>::YRef() Foam::PurePhaseModel<BasePhaseModel>::YRef()

View File

@ -89,6 +89,9 @@ public:
//- Return the species mass fractions //- Return the species mass fractions
virtual const PtrList<volScalarField>& Y() const; virtual const PtrList<volScalarField>& Y() const;
//- Return a species mass fraction by name
virtual const volScalarField& Y(const word& name) const;
//- Access the species mass fractions //- Access the species mass fractions
virtual PtrList<volScalarField>& YRef(); virtual PtrList<volScalarField>& YRef();

View File

@ -246,6 +246,9 @@ public:
//- Return the species mass fractions //- Return the species mass fractions
virtual const PtrList<volScalarField>& Y() const = 0; virtual const PtrList<volScalarField>& Y() const = 0;
//- Return a species mass fraction by name
virtual const volScalarField& Y(const word& name) const = 0;
//- Access the species mass fractions //- Access the species mass fractions
virtual PtrList<volScalarField>& YRef() = 0; virtual PtrList<volScalarField>& YRef() = 0;

View File

@ -27,6 +27,4 @@
YiEqn.solve(mesh.solver("Yi")); YiEqn.solve(mesh.solver("Yi"));
} }
} }
fluid.massTransfer(); // updates interfacial mass flow rates
} }

View File

@ -42,6 +42,4 @@
Y2iEqn.solve(mesh.solver("Yi")); Y2iEqn.solve(mesh.solver("Yi"));
} }
} }
fluid.massTransfer(); // updates interfacial mass flow rates
} }