functionObjects: Simplification of moleFractions, and new massFractions function

The moleFractions function has been simplified and generalised. It no
longer needs to execute on construction, as function objects now have
the ability to execute at the start of a simulation. It can also now
construct a thermo model if none exists, simplifying its use as a post
processing operation. A packaged function has been provided, so that all
that is needed to execute the function is the following setting in the
functions section of the system/controlDict:

    #includeFunc moleFractions

Alternatively, it can be executed on the command line as follows:

    foamPostProcess -func moleFractions

A new massFractions function has also been added which converts mole
fraction fields (e.g., X_CH4, X_O2, etc...), or moles fields (n_CH4,
n_O2, etc...) to the corresponding mass fraction fields. This function,
by contrast to the moleFractions function described above, should not be
used at run-time. It should only be used to initialise a simulation in
which molar data is known and needs converting to mass-fractions. If at
the point of execution a thermo model exists, or mass-fraction fields
are found on disk, then this function will exit with an error rather
than invalidating the existing mass-fraction data. Packaging is provided
that allows the function to be executed to initialise a case as follows:

    foamPostProcess -func massFractions
This commit is contained in:
Will Bainbridge
2023-01-31 14:38:39 +00:00
parent 7c684b925d
commit 1b80fd35e4
9 changed files with 504 additions and 249 deletions

View File

@ -0,0 +1,20 @@
/*--------------------------------*- C++ -*----------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Version: dev
\\/ M anipulation |
-------------------------------------------------------------------------------
Description
Calculates mass-fraction fields from mole-fraction fields, or moles fields,
and a multicomponent thermo.
\*---------------------------------------------------------------------------*/
type massFractions;
libs ("libmulticomponentThermophysicalModels.so");
executeControl writeTime;
writeControl writeTime;
// ************************************************************************* //

View File

@ -0,0 +1,20 @@
/*--------------------------------*- C++ -*----------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Version: dev
\\/ M anipulation |
-------------------------------------------------------------------------------
Description
Calculates mole-fraction fields from the mass-fraction fields of a
multicomponent thermo.
\*---------------------------------------------------------------------------*/
type moleFractions;
libs ("libmulticomponentThermophysicalModels.so");
executeControl writeTime;
writeControl writeTime;
// ************************************************************************* //

View File

@ -16,6 +16,7 @@ derivedFvPatchFields/fixedUnburntEnthalpy/fixedUnburntEnthalpyFvPatchScalarField
derivedFvPatchFields/gradientUnburntEnthalpy/gradientUnburntEnthalpyFvPatchScalarField.C
derivedFvPatchFields/mixedUnburntEnthalpy/mixedUnburntEnthalpyFvPatchScalarField.C
functionObjects/moleFractions/moleFractionsFunctionObjects.C
functionObjects/moleFractions/moleFractions.C
functionObjects/massFractions/massFractions.C
LIB = $(FOAM_LIBBIN)/libmulticomponentThermophysicalModels

View File

@ -0,0 +1,251 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2023 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "massFractions.H"
#include "fluidMulticomponentThermo.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
namespace functionObjects
{
defineTypeNameAndDebug(massFractions, 0);
addToRunTimeSelectionTable(functionObject, massFractions, dictionary);
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::functionObjects::massFractions::massFractions
(
const word& name,
const Time& runTime,
const dictionary& dict
)
:
fvMeshFunctionObject(name, runTime, dict),
phaseName_(dict.lookupOrDefault<word>("phase", word::null)),
Y_()
{}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::functionObjects::massFractions::~massFractions()
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::functionObjects::massFractions::read(const dictionary& dict)
{
return true;
}
bool Foam::functionObjects::massFractions::execute()
{
// A multicomponent thermo should not exist for this context, as the
// following would create a different, inconsistent definition of the
// composition of an existing thermo model
const word thermoName =
IOobject::groupName(physicalProperties::typeName, phaseName_);
if (mesh_.foundObject<fluidMulticomponentThermo>(thermoName))
{
FatalErrorInFunction
<< "Cannot create mass fractions. Mass fractions already exist "
<< "within the multicomponent thermo model, \"" << thermoName
<< "\"." << exit(FatalError);
}
// Read Ydefault if it exists
typeIOobject<volScalarField> YdefaultIo
(
"Ydefault",
time_.name(),
mesh_,
IOobject::READ_IF_PRESENT,
IOobject::NO_WRITE,
false
);
const bool YdefaultIoExists = YdefaultIo.headerOk();
const volScalarField Ydefault
(
YdefaultIo,
mesh_,
dimensionedScalar(dimless, 0)
);
// Back up Ydefault if it exists
if (YdefaultIoExists)
{
fileHandler().cp
(
YdefaultIo.filePath(),
YdefaultIo.path(false)/"molesToMassFractions:" + YdefaultIo.name()
);
}
// Write Ydefault out again, but limited to a value of small. This prevents
// errors in construction of the thermo if no non-default fractions exist
{
volScalarField YdefaultLim(Ydefault);
YdefaultLim.max(small);
YdefaultLim.write();
}
// Construct a multicomponent thermo
autoPtr<fluidMulticomponentThermo> thermoPtr =
fluidMulticomponentThermo::New(mesh_);
fluidMulticomponentThermo& thermo = thermoPtr();
const PtrList<volScalarField>& Y = thermo.composition().Y();
// Restore the original Ydefault if it exists, and create a new Ydefault if
// it does not
if (YdefaultIoExists)
{
fileHandler().mv
(
YdefaultIo.path(false)/"molesToMassFractions:" + YdefaultIo.name(),
YdefaultIo.filePath()
);
}
else
{
Ydefault.write();
}
// One-mole constant for conversions
static const dimensionedScalar oneMole(dimMoles, 1);
// Construct lists of specie molar mass, fields of specie mass, and a field
// of total mass
List<dimensionedScalar> W(Y.size());
PtrList<volScalarField> m(Y.size());
volScalarField mTotal
(
IOobject("mTotal", time_.name(), mesh_),
mesh_,
dimensionedScalar(dimMass, 0)
);
bool fromMoleFractions = false, fromMoles = false;
forAll(Y, i)
{
W[i].dimensions().reset(dimMass/dimMoles);
W[i].value() = thermo.composition().Wi(i);
typeIOobject<volScalarField> YIo
(
Y[i].name(),
time_.name(),
mesh_,
IOobject::MUST_READ
);
typeIOobject<volScalarField> XIo
(
"X_" + Y[i].name(),
time_.name(),
mesh_,
IOobject::MUST_READ
);
typeIOobject<volScalarField> nIo
(
"n_" + Y[i].name(),
time_.name(),
mesh_,
IOobject::MUST_READ
);
// Check consistency of input
if (YIo.headerOk())
{
FatalErrorInFunction
<< "Mass fraction field " << YIo.name()
<< " already exists on disk" << exit(FatalError);
}
fromMoleFractions = fromMoleFractions || XIo.headerOk();
fromMoles = fromMoles || nIo.headerOk();
if (fromMoleFractions && fromMoles)
{
FatalErrorInFunction
<< "Mole fraction fields and moles fields "
<< " both found on disk"
<< exit(FatalError);
}
// Sum the contributions
if (XIo.headerOk())
{
m.set(i, W[i]*oneMole*volScalarField(XIo, mesh_));
}
if (nIo.headerOk())
{
m.set(i, W[i]*volScalarField(nIo, mesh_));
}
if (XIo.headerOk() || nIo.headerOk())
{
mTotal += m[i];
}
}
// Steal the thermo's mass fraction fields and delete the thermo
Y_.transfer(thermo.composition().Y());
thermoPtr.clear();
// Divide the specie masses by the total mass to get the mass fractions
forAll(Y_, i)
{
if (m.set(i))
{
Y_[i] == m[i]/mTotal;
}
else
{
Y_.set(i, nullptr);
}
}
return true;
}
bool Foam::functionObjects::massFractions::write()
{
forAll(Y_, i)
{
if (Y_.set(i))
{
Y_[i].write();
}
}
return true;
}
// ************************************************************************* //

View File

@ -0,0 +1,138 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2023 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Class
Foam::functionObjects::massFractions
Description
This function object calculates mass-fraction fields from mole-fraction or
moles fields present on disk. This is intended to be used for
initialisation where mole-fractions are known. If any mass fraction fields
are found (other than Ydefault) then an error will be generated and the
fields will not be overwritten. The names of the mole-fraction fields are
obtained from the corresponding mass-fraction fields prepended by "X_", and
the moles fields are prepended by "n_". Either mole-fraction fields or
moles fields should be present, not both.
Example of function object specification:
\verbatim
massFractions
{
type massFractions;
}
\endverbatim
Optionally, the name of the phase can be specified for multiphase cases.
See also
Foam::functionObjects::fvMeshFunctionObject
SourceFiles
massFractions.C
\*---------------------------------------------------------------------------*/
#ifndef functionObjects_massFractions_H
#define functionObjects_massFractions_H
#include "fvMeshFunctionObject.H"
#include "volFieldsFwd.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
namespace functionObjects
{
/*---------------------------------------------------------------------------*\
Class massFractions Declaration
\*---------------------------------------------------------------------------*/
class massFractions
:
public functionObjects::fvMeshFunctionObject
{
// Private Data
//- Optional phase name
word phaseName_;
//- Species mass fractions
PtrList<volScalarField> Y_;
public:
//- Runtime type information
TypeName("massFractions");
// Constructors
//- Construct from Time and dictionary
massFractions(const word& name, const Time& t, const dictionary& dict);
//- Disallow default bitwise copy construction
massFractions(const massFractions&) = delete;
//- Destructor
virtual ~massFractions();
// Member Functions
//- Read the massFractions data
virtual bool read(const dictionary&);
//- Return the list of fields required
virtual wordList fields() const
{
return wordList::null();
}
//- Calculate the mass-fraction fields
virtual bool execute();
//- The mass-fraction fields auto-write
virtual bool write();
// Member Operators
//- Disallow default bitwise assignment
void operator=(const massFractions&) = delete;
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace functionObjects
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -2,7 +2,7 @@
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2016-2022 OpenFOAM Foundation
\\ / A nd | Copyright (C) 2016-2023 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
License
@ -24,41 +24,24 @@ License
\*---------------------------------------------------------------------------*/
#include "moleFractions.H"
#include "basicThermo.H"
#include "fluidMulticomponentThermo.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
template<class ThermoType>
void Foam::moleFractions<ThermoType>::calculateMoleFractions()
namespace Foam
{
const ThermoType& thermo =
mesh_.lookupObject<ThermoType>
(
IOobject::groupName(physicalProperties::typeName, phaseName_)
);
const PtrList<volScalarField>& Y = thermo.composition().Y();
const volScalarField W(thermo.W());
forAll(Y, i)
{
const dimensionedScalar Wi
(
"Wi",
dimMass/dimMoles,
thermo.composition().Wi(i)
);
X_[i] = W*Y[i]/Wi;
}
namespace functionObjects
{
defineTypeNameAndDebug(moleFractions, 0);
addToRunTimeSelectionTable(functionObject, moleFractions, dictionary);
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template<class ThermoType>
Foam::moleFractions<ThermoType>::moleFractions
Foam::functionObjects::moleFractions::moleFractions
(
const word& name,
const Time& runTime,
@ -66,19 +49,45 @@ Foam::moleFractions<ThermoType>::moleFractions
)
:
fvMeshFunctionObject(name, runTime, dict),
phaseName_(dict.lookupOrDefault<word>("phase", word::null))
phaseName_(dict.lookupOrDefault<word>("phase", word::null)),
X_()
{}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::functionObjects::moleFractions::~moleFractions()
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::functionObjects::moleFractions::read(const dictionary& dict)
{
const word dictName
return true;
}
bool Foam::functionObjects::moleFractions::execute()
{
// Lookup or construct a multicomponent thermo. Lookup is likely to be
// appropriate if this is being used at run-time. Construction if this is
// being called as a one-off post-process.
const word thermoName =
IOobject::groupName(physicalProperties::typeName, phaseName_);
autoPtr<fluidMulticomponentThermo> thermoPtr
(
IOobject::groupName(physicalProperties::typeName, phaseName_)
mesh_.foundObject<fluidMulticomponentThermo>(thermoName)
? autoPtr<fluidMulticomponentThermo>(nullptr)
: fluidMulticomponentThermo::New(mesh_)
);
const fluidMulticomponentThermo& thermo =
mesh_.lookupObject<fluidMulticomponentThermo>(thermoName);
if (mesh_.foundObject<ThermoType>(dictName))
{
const ThermoType& thermo = mesh_.lookupObject<ThermoType>(dictName);
// Construct mole fraction fields corresponding to the mass fraction fields
const PtrList<volScalarField>& Y = thermo.composition().Y();
if (X_.empty())
{
X_.setSize(Y.size());
forAll(Y, i)
@ -99,60 +108,29 @@ Foam::moleFractions<ThermoType>::moleFractions
)
);
}
calculateMoleFractions();
}
else
// Get the mixture molar mass
const volScalarField W(thermo.W());
// Calculate the mole fractions
forAll(Y, i)
{
if (phaseName_ != word::null)
{
FatalErrorInFunction
<< "Cannot find thermodynamics model of type "
<< ThermoType::typeName
<< " for phase "
<< phaseName_
<< exit(FatalError);
const dimensionedScalar Wi
(
"Wi",
dimMass/dimMoles,
thermo.composition().Wi(i)
);
X_[i] = Y[i]*W/Wi;
}
else
{
FatalErrorInFunction
<< "Cannot find thermodynamics model of type "
<< ThermoType::typeName
<< exit(FatalError);
}
}
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
template<class ThermoType>
Foam::moleFractions<ThermoType>::~moleFractions()
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
template<class ThermoType>
bool Foam::moleFractions<ThermoType>::read
(
const dictionary& dict
)
{
return true;
}
template<class ThermoType>
bool Foam::moleFractions<ThermoType>::execute()
{
calculateMoleFractions();
return true;
}
template<class ThermoType>
bool Foam::moleFractions<ThermoType>::write()
bool Foam::functionObjects::moleFractions::write()
{
forAll(X_, i)
{

View File

@ -2,7 +2,7 @@
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2016-2022 OpenFOAM Foundation
\\ / A nd | Copyright (C) 2016-2023 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
License
@ -22,31 +22,20 @@ License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class
Foam::moleFractions
Foam::functionObjects::moleFractions
Description
This function object calculates mole-fraction fields from the mass-fraction
fields of the psi/rhoMulticomponentThermo and caches them for output and
further post-processing.
The names of the mole-fraction fields are obtained from the corresponding
mass-fraction fields prepended by "X_"
fields of the multicomponent thermo. The names of the mole-fraction fields
are obtained from the corresponding mass-fraction fields prepended by "X_".
Example of function object specification:
\verbatim
moleFractions
{
type psiMulticomponentThermoMoleFractions;
type moleFractions;
}
\endverbatim
or
\verbatim
moleFractions
{
type rhoMulticomponentThermoMoleFractions;
}
\endverbatim
depending on the thermodynamics package used in the solver.
Optionally, the name of the phase can be specified for multiphase cases.
@ -58,8 +47,8 @@ SourceFiles
\*---------------------------------------------------------------------------*/
#ifndef moleFractions_H
#define moleFractions_H
#ifndef functionObjects_moleFractions_H
#define functionObjects_moleFractions_H
#include "fvMeshFunctionObject.H"
#include "volFieldsFwd.H"
@ -68,29 +57,24 @@ SourceFiles
namespace Foam
{
namespace functionObjects
{
/*---------------------------------------------------------------------------*\
Class moleFractions Declaration
\*---------------------------------------------------------------------------*/
template<class ThermoType>
class moleFractions
:
public functionObjects::fvMeshFunctionObject
{
// Private Data
//- Species mole fractions
PtrList<volScalarField> X_;
//- Optional phase name
word phaseName_;
// Private Member Functions
//- Calculate the mole fraction fields
virtual void calculateMoleFractions();
//- Species mole fractions
PtrList<volScalarField> X_;
public:
@ -102,12 +86,7 @@ public:
// Constructors
//- Construct from Time and dictionary
moleFractions
(
const word& name,
const Time& t,
const dictionary& dict
);
moleFractions(const word& name, const Time& t, const dictionary& dict);
//- Disallow default bitwise copy construction
moleFractions(const moleFractions&) = delete;
@ -144,16 +123,11 @@ public:
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace functionObjects
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#ifdef NoRepository
#include "moleFractions.C"
#endif
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -1,63 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2016-2022 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "moleFractionsFunctionObjects.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTemplateTypeNameAndDebugWithName
(
psiMulticomponentThermoMoleFractionsFunctionObject,
"psiMulticomponentThermoMoleFractions",
0
);
addToRunTimeSelectionTable
(
functionObject,
psiMulticomponentThermoMoleFractionsFunctionObject,
dictionary
);
defineTemplateTypeNameAndDebugWithName
(
rhoMulticomponentThermoMoleFractionsFunctionObject,
"rhoMulticomponentThermoMoleFractions",
0
);
addToRunTimeSelectionTable
(
functionObject,
rhoMulticomponentThermoMoleFractionsFunctionObject,
dictionary
);
}
// ************************************************************************* //

View File

@ -1,64 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2016-2022 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Typedef
Foam::psiMulticomponentThermoMoleFractionsFunctionObject
Description
Instantiate the moleFractions functionObject for psiMulticomponentThermo
Typedef
Foam::rhoMulticomponentThermoMoleFractionsFunctionObject
Description
Instantiate the moleFractions functionObject for rhoMulticomponentThermo
SourceFiles
moleFractionsFunctionObjects.C
\*---------------------------------------------------------------------------*/
#ifndef moleFractionsFunctionObjects_H
#define moleFractionsFunctionObjects_H
#include "moleFractions.H"
#include "psiMulticomponentThermo.H"
#include "rhoMulticomponentThermo.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
typedef moleFractions<psiMulticomponentThermo>
psiMulticomponentThermoMoleFractionsFunctionObject;
typedef moleFractions<rhoMulticomponentThermo>
rhoMulticomponentThermoMoleFractionsFunctionObject;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //