sampling: added thresholdCellFaces, sampledThresholdCellFaces

This commit is contained in:
Mark Olesen
2009-03-16 18:49:23 +01:00
parent ba771d1901
commit d3bc5c21b1
6 changed files with 1070 additions and 0 deletions

View File

@ -33,6 +33,8 @@ sampledSurface/sampledCuttingPlane/sampledCuttingPlane.C
sampledSurface/sampledSurface/sampledSurface.C
sampledSurface/sampledSurfaces/sampledSurfaces.C
sampledSurface/sampledSurfacesFunctionObject/sampledSurfacesFunctionObject.C
sampledSurface/thresholdCellFaces/thresholdCellFaces.C
sampledSurface/thresholdCellFaces/sampledThresholdCellFaces.C
surfWriters = sampledSurface/writers

View File

@ -0,0 +1,333 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 1991-2009 OpenCFD Ltd.
\\/ 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 2 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, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
\*---------------------------------------------------------------------------*/
#include "sampledThresholdCellFaces.H"
#include "dictionary.H"
#include "volFields.H"
#include "volPointInterpolation.H"
#include "addToRunTimeSelectionTable.H"
#include "fvMesh.H"
#include "thresholdCellFaces.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(sampledThresholdCellFaces, 0);
addNamedToRunTimeSelectionTable
(
sampledSurface,
sampledThresholdCellFaces,
word,
thresholdCellFaces
);
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
bool Foam::sampledThresholdCellFaces::updateGeometry() const
{
const fvMesh& fvm = static_cast<const fvMesh&>(mesh());
// no update needed
if (fvm.time().timeIndex() == prevTimeIndex_)
{
return false;
}
prevTimeIndex_ = fvm.time().timeIndex();
// Optionally read volScalarField
autoPtr<volScalarField> readFieldPtr_;
// 1. see if field in database
// 2. see if field can be read
const volScalarField* cellFldPtr = NULL;
if (fvm.foundObject<volScalarField>(fieldName_))
{
if (debug)
{
Info<< "sampledThresholdCellFaces::updateGeometry() : lookup "
<< fieldName_ << endl;
}
cellFldPtr = &fvm.lookupObject<volScalarField>(fieldName_);
}
else
{
// Bit of a hack. Read field and store.
if (debug)
{
Info<< "sampledThresholdCellFaces::updateGeometry() : reading "
<< fieldName_ << " from time " << fvm.time().timeName()
<< endl;
}
readFieldPtr_.reset
(
new volScalarField
(
IOobject
(
fieldName_,
fvm.time().timeName(),
fvm,
IOobject::MUST_READ,
IOobject::NO_WRITE,
false
),
fvm
)
);
cellFldPtr = readFieldPtr_.operator->();
}
const volScalarField& cellFld = *cellFldPtr;
thresholdCellFaces surf
(
fvm,
cellFld.internalField(),
lowerThreshold_,
upperThreshold_,
triangulate_
);
const_cast<sampledThresholdCellFaces&>
(
*this
).MeshedSurface<face>::transfer(surf);
meshCells_.transfer(surf.meshCells());
if (debug)
{
Pout<< "sampledThresholdCellFaces::updateGeometry() : constructed"
<< nl
<< " field : " << fieldName_ << nl
<< " lowerLimit : " << lowerThreshold_ << nl
<< " upperLimit : " << upperThreshold_ << nl
<< " point : " << points().size() << nl
<< " faces : " << faces().size() << nl
<< " cut cells : " << meshCells_.size() << endl;
}
return true;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::sampledThresholdCellFaces::sampledThresholdCellFaces
(
const word& name,
const polyMesh& mesh,
const dictionary& dict
)
:
sampledSurface(name, mesh, dict),
fieldName_(dict.lookup("field")),
lowerThreshold_(dict.lookupOrDefault<scalar>("lowerLimit", -VGREAT)),
upperThreshold_(dict.lookupOrDefault<scalar>("upperLimit", VGREAT)),
zoneName_(word::null),
triangulate_(dict.lookupOrDefault("triangulate", false)),
prevTimeIndex_(-1),
meshCells_(0)
{
if (!dict.found("lowerLimit") && !dict.found("upperLimit"))
{
FatalErrorIn
(
"sampledThresholdCellFaces::sampledThresholdCellFaces(..)"
)
<< "require at least one of 'lowerLimit' or 'upperLimit'" << endl
<< abort(FatalError);
}
// dict.readIfPresent("zone", zoneName_);
//
// if (debug && zoneName_.size())
// {
// if (mesh.cellZones().findZoneID(zoneName_) < 0)
// {
// Info<< "cellZone \"" << zoneName_
// << "\" not found - using entire mesh" << endl;
// }
// }
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::sampledThresholdCellFaces::~sampledThresholdCellFaces()
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::sampledThresholdCellFaces::needsUpdate() const
{
const fvMesh& fvm = static_cast<const fvMesh&>(mesh());
return fvm.time().timeIndex() != prevTimeIndex_;
}
bool Foam::sampledThresholdCellFaces::expire()
{
// already marked as expired
if (prevTimeIndex_ == -1)
{
return false;
}
// force update
prevTimeIndex_ = -1;
return true;
}
bool Foam::sampledThresholdCellFaces::update()
{
return updateGeometry();
}
Foam::tmp<Foam::scalarField>
Foam::sampledThresholdCellFaces::sample
(
const volScalarField& vField
) const
{
return sampleField(vField);
}
Foam::tmp<Foam::vectorField>
Foam::sampledThresholdCellFaces::sample
(
const volVectorField& vField
) const
{
return sampleField(vField);
}
Foam::tmp<Foam::sphericalTensorField>
Foam::sampledThresholdCellFaces::sample
(
const volSphericalTensorField& vField
) const
{
return sampleField(vField);
}
Foam::tmp<Foam::symmTensorField>
Foam::sampledThresholdCellFaces::sample
(
const volSymmTensorField& vField
) const
{
return sampleField(vField);
}
Foam::tmp<Foam::tensorField>
Foam::sampledThresholdCellFaces::sample
(
const volTensorField& vField
) const
{
return sampleField(vField);
}
Foam::tmp<Foam::scalarField>
Foam::sampledThresholdCellFaces::interpolate
(
const interpolation<scalar>& interpolator
) const
{
return interpolateField(interpolator);
}
Foam::tmp<Foam::vectorField>
Foam::sampledThresholdCellFaces::interpolate
(
const interpolation<vector>& interpolator
) const
{
return interpolateField(interpolator);
}
Foam::tmp<Foam::sphericalTensorField>
Foam::sampledThresholdCellFaces::interpolate
(
const interpolation<sphericalTensor>& interpolator
) const
{
return interpolateField(interpolator);
}
Foam::tmp<Foam::symmTensorField>
Foam::sampledThresholdCellFaces::interpolate
(
const interpolation<symmTensor>& interpolator
) const
{
return interpolateField(interpolator);
}
Foam::tmp<Foam::tensorField>
Foam::sampledThresholdCellFaces::interpolate
(
const interpolation<tensor>& interpolator
) const
{
return interpolateField(interpolator);
}
void Foam::sampledThresholdCellFaces::print(Ostream& os) const
{
os << "sampledThresholdCellFaces: " << name() << " :"
<< " field:" << fieldName_
<< " lowerLimit:" << lowerThreshold_
<< " upperLimit:" << upperThreshold_;
//<< " faces:" << faces().size() // possibly no geom yet
//<< " points:" << points().size();
}
// ************************************************************************* //

View File

@ -0,0 +1,234 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 1991-2009 OpenCFD Ltd.
\\/ 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 2 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, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Class
Foam::sampledThresholdCellFaces
Description
A sampledSurface defined by the cell faces corresponding to a threshold
value.
SourceFiles
sampledThresholdCellFaces.C
\*---------------------------------------------------------------------------*/
#ifndef sampledThresholdCellFaces_H
#define sampledThresholdCellFaces_H
#include "sampledSurface.H"
#include "MeshedSurface.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
/*---------------------------------------------------------------------------*\
Class sampledThresholdCellFaces Declaration
\*---------------------------------------------------------------------------*/
class sampledThresholdCellFaces
:
public sampledSurface,
public MeshedSurface<face>
{
//- Private typedefs for convenience
typedef MeshedSurface<face> MeshStorage;
// Private data
//- Field to get isoSurface of
const word fieldName_;
//- Threshold value
const scalar lowerThreshold_;
//- Threshold value
const scalar upperThreshold_;
//- zone name (if restricted to zones)
word zoneName_;
//- Triangulated faces or keep faces as is
bool triangulate_;
// Recreated for every time-step
//- Time at last call, also track it surface needs an update
mutable label prevTimeIndex_;
//- For every face the original cell in mesh
mutable labelList meshCells_;
// Private Member Functions
//- Create surface (if time has changed)
// Do nothing (and return false) if no update was needed
bool updateGeometry() const;
//- sample field on faces
template <class Type>
tmp<Field<Type> > sampleField
(
const GeometricField<Type, fvPatchField, volMesh>& vField
) const;
template <class Type>
tmp<Field<Type> >
interpolateField(const interpolation<Type>&) const;
public:
//- Runtime type information
TypeName("sampledThresholdCellFaces");
// Constructors
//- Construct from dictionary
sampledThresholdCellFaces
(
const word& name,
const polyMesh&,
const dictionary&
);
// Destructor
virtual ~sampledThresholdCellFaces();
// Member Functions
//- Does the surface need an update?
virtual bool needsUpdate() const;
//- Mark the surface as needing an update.
// May also free up unneeded data.
// Return false if surface was already marked as expired.
virtual bool expire();
//- Update the surface as required.
// Do nothing (and return false) if no update was needed
virtual bool update();
//- Points of surface
virtual const pointField& points() const
{
return MeshStorage::points();
}
//- Faces of surface
virtual const faceList& faces() const
{
return MeshStorage::faces();
}
//- sample field on surface
virtual tmp<scalarField> sample
(
const volScalarField&
) const;
//- sample field on surface
virtual tmp<vectorField> sample
(
const volVectorField&
) const;
//- sample field on surface
virtual tmp<sphericalTensorField> sample
(
const volSphericalTensorField&
) const;
//- sample field on surface
virtual tmp<symmTensorField> sample
(
const volSymmTensorField&
) const;
//- sample field on surface
virtual tmp<tensorField> sample
(
const volTensorField&
) const;
//- interpolate field on surface
virtual tmp<scalarField> interpolate
(
const interpolation<scalar>&
) const;
//- interpolate field on surface
virtual tmp<vectorField> interpolate
(
const interpolation<vector>&
) const;
//- interpolate field on surface
virtual tmp<sphericalTensorField> interpolate
(
const interpolation<sphericalTensor>&
) const;
//- interpolate field on surface
virtual tmp<symmTensorField> interpolate
(
const interpolation<symmTensor>&
) const;
//- interpolate field on surface
virtual tmp<tensorField> interpolate
(
const interpolation<tensor>&
) const;
//- Write
virtual void print(Ostream&) const;
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#ifdef NoRepository
# include "sampledThresholdCellFacesTemplates.C"
#endif
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,90 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 1991-2009 OpenCFD Ltd.
\\/ 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 2 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, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
\*---------------------------------------------------------------------------*/
#include "sampledThresholdCellFaces.H"
#include "thresholdCellFaces.H"
#include "volFieldsFwd.H"
#include "pointFields.H"
#include "volPointInterpolation.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
template <class Type>
Foam::tmp<Foam::Field<Type> >
Foam::sampledThresholdCellFaces::sampleField
(
const GeometricField<Type, fvPatchField, volMesh>& vField
) const
{
// Recreate geometry if time has changed
updateGeometry();
return tmp<Field<Type> >(new Field<Type>(vField, meshCells_));
}
template <class Type>
Foam::tmp<Foam::Field<Type> >
Foam::sampledThresholdCellFaces::interpolateField
(
const interpolation<Type>& interpolator
) const
{
// Recreate geometry if time has changed
updateGeometry();
// One value per point
tmp<Field<Type> > tvalues(new Field<Type>(points().size()));
Field<Type>& values = tvalues();
boolList pointDone(points().size(), false);
forAll(faces(), cutFaceI)
{
const face& f = faces()[cutFaceI];
forAll(f, faceVertI)
{
label pointI = f[faceVertI];
if (!pointDone[pointI])
{
values[pointI] = interpolator.interpolate
(
points()[pointI],
meshCells_[cutFaceI]
);
pointDone[pointI] = true;
}
}
}
return tvalues;
}
// ************************************************************************* //

View File

@ -0,0 +1,288 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 1991-2009 OpenCFD Ltd.
\\/ 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 2 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, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
\*---------------------------------------------------------------------------*/
#include "thresholdCellFaces.H"
#include "polyMesh.H"
#include "DynamicList.H"
#include "emptyPolyPatch.H"
#include "processorPolyPatch.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
defineTypeNameAndDebug(Foam::thresholdCellFaces, 0);
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::thresholdCellFaces::calculate
(
const scalarField& field,
const scalar lowerThreshold,
const scalar upperThreshold,
const bool triangulate
)
{
const labelList& own = mesh_.faceOwner();
const labelList& nei = mesh_.faceNeighbour();
const faceList& origFaces = mesh_.faces();
const pointField& origPoints = mesh_.points();
const polyBoundaryMesh& bMesh = mesh_.boundaryMesh();
surfZoneList surfZones(bMesh.size()+1);
surfZones[0] = surfZone
(
"internalMesh",
0, // size
0, // start
0 // index
);
forAll(bMesh, patchI)
{
surfZones[patchI+1] = surfZone
(
bMesh[patchI].name(),
0, // size
0, // start
patchI+1 // index
);
}
label nFaces = 0;
label nPoints = 0;
meshCells_.clear();
DynamicList<face> surfFaces(0.5 * mesh_.nFaces());
DynamicList<label> surfCells(surfFaces.size());
labelList oldToNewPoints(origPoints.size(), -1);
// internal faces only
for (label faceI = 0; faceI < mesh_.nInternalFaces(); ++faceI)
{
int side = 0;
// check lowerThreshold
if (field[own[faceI]] > lowerThreshold)
{
if (field[nei[faceI]] < lowerThreshold)
{
side = +1;
}
}
else if (field[nei[faceI]] > lowerThreshold)
{
side = -1;
}
// check upperThreshold
if (field[own[faceI]] < upperThreshold)
{
if (field[nei[faceI]] > upperThreshold)
{
side = +1;
}
}
else if (field[nei[faceI]] < upperThreshold)
{
side = -1;
}
if (side)
{
const face& f = origFaces[faceI];
forAll(f, fp)
{
if (oldToNewPoints[f[fp]] == -1)
{
oldToNewPoints[f[fp]] = nPoints++;
}
}
label cellId;
face surfFace;
if (side > 0)
{
surfFace = f;
cellId = own[faceI];
}
else
{
surfFace = f.reverseFace();
cellId = nei[faceI];
}
if (triangulate)
{
label count = surfFace.triangles(origPoints, surfFaces);
while (count-- > 0)
{
surfCells.append(cellId);
}
}
else
{
surfFaces.append(surfFace);
surfCells.append(cellId);
}
}
}
surfZones[0].size() = surfFaces.size();
// nothing special for processor patches?
forAll(bMesh, patchI)
{
const polyPatch& p = bMesh[patchI];
surfZone& zone = surfZones[patchI+1];
zone.start() = nFaces;
if
(
isType<emptyPolyPatch>(p)
|| (Pstream::parRun() && isType<processorPolyPatch>(p))
)
{
continue;
}
label faceI = p.start();
// patch faces
forAll(p, localFaceI)
{
if
(
field[own[faceI]] > lowerThreshold
&& field[own[faceI]] < upperThreshold
)
{
const face& f = origFaces[faceI];
forAll(f, fp)
{
if (oldToNewPoints[f[fp]] == -1)
{
oldToNewPoints[f[fp]] = nPoints++;
}
}
label cellId = own[faceI];
if (triangulate)
{
label count = f.triangles(origPoints, surfFaces);
while (count-- > 0)
{
surfCells.append(cellId);
}
}
else
{
surfFaces.append(f);
surfCells.append(cellId);
}
}
++faceI;
}
zone.size() = surfFaces.size() - zone.start();
}
surfFaces.shrink();
surfCells.shrink();
// renumber
forAll(surfFaces, faceI)
{
inplaceRenumber(oldToNewPoints, surfFaces[faceI]);
}
pointField surfPoints(nPoints);
nPoints = 0;
forAll(oldToNewPoints, pointI)
{
if (oldToNewPoints[pointI] >= 0)
{
surfPoints[oldToNewPoints[pointI]] = origPoints[pointI];
nPoints++;
}
}
surfPoints.setSize(nPoints);
this->storedPoints().transfer(surfPoints);
this->storedFaces().transfer(surfFaces);
this->storedZones().transfer(surfZones);
meshCells_.transfer(surfCells);
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::thresholdCellFaces::thresholdCellFaces
(
const polyMesh& mesh,
const scalarField& field,
const scalar lowerThreshold,
const scalar upperThreshold,
const bool triangulate
)
:
mesh_(mesh)
{
if (lowerThreshold > upperThreshold)
{
WarningIn("thresholdCellFaces::thresholdCellFaces(...)")
<< "lower > upper limit! "
<< lowerThreshold << " > " << upperThreshold << endl;
}
calculate(field, lowerThreshold, upperThreshold, triangulate);
}
// ************************************************************************* //

View File

@ -0,0 +1,123 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 1991-2009 OpenCFD Ltd.
\\/ 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 2 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, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Class
Foam::thresholdCellFaces
Description
Selects the mesh cell faces specified by a threshold value.
Non-triangulated by default.
SourceFiles
thresholdCellFaces.C
\*---------------------------------------------------------------------------*/
#ifndef thresholdCellFaces_H
#define thresholdCellFaces_H
#include "MeshedSurface.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
class polyMesh;
/*---------------------------------------------------------------------------*\
Class thresholdCellFaces Declaration
\*---------------------------------------------------------------------------*/
class thresholdCellFaces
:
public MeshedSurface<face>
{
//- Private typedefs for convenience
typedef MeshedSurface<face> MeshStorage;
//- Reference to mesh
const polyMesh& mesh_;
//- For every face the original cell in mesh
labelList meshCells_;
// Private Member Functions
void calculate
(
const scalarField&,
const scalar lowerThreshold,
const scalar upperThreshold,
const bool triangulate
);
public:
//- Runtime type information
TypeName("thresholdCellFaces");
// Constructors
//- Construct from mesh, field and threshold value
thresholdCellFaces
(
const polyMesh&,
const scalarField&,
const scalar lowerThreshold,
const scalar upperThreshold,
const bool triangulate = false
);
// Member Functions
//- For every face original cell in mesh
labelList& meshCells()
{
return meshCells_;
}
//- For every face original cell in mesh
const labelList& meshCells() const
{
return meshCells_;
}
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //