Merge branch 'master' of ssh://dm/home/dm4/OpenFOAM/OpenFOAM-dev

This commit is contained in:
Henry
2013-01-17 09:54:19 +00:00
126 changed files with 3832 additions and 487 deletions

View File

@ -30,7 +30,7 @@ Description
Sub-models include:
- turbulence modelling, i.e. laminar, RAS or LES
- run-time selectable fvOptions, e.g. MRF, explicit porosity
- run-time selectable finitie volume options, e.g. MRF, explicit porosity
\*---------------------------------------------------------------------------*/

View File

@ -3,6 +3,7 @@ cd ${0%/*} || exit 1 # run from this directory
set -x
wmake
wmake simpleReactingParcelFoam
wmake LTSReactingParcelFoam
# ----------------------------------------------------------------- end-of-file

View File

@ -28,7 +28,7 @@ Description
Local time stepping (LTS) solver for steady, compressible, laminar or
turbulent reacting and non-reacting flow with multiphase Lagrangian
parcels and porous media, including run-time selectable finitite volume
options, e.g. fvOptions, constraints
options, e.g. sources, constraints
Note: ddtPhiCorr not used here when porous zones are active
- not well defined for porous calculations

View File

@ -22,12 +22,12 @@ License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Application
porousExplicitSourceReactingParcelFoam
reactingParcelFoam
Description
Transient PIMPLE solver for compressible, laminar or turbulent flow with
reacting multiphase Lagrangian parcels, including run-time selectable
finite volume options, e.g. fvOptions, constraints
finite volume options, e.g. sources, constraints
\*---------------------------------------------------------------------------*/

View File

@ -0,0 +1,32 @@
{
volScalarField& he = thermo.he();
fvScalarMatrix EEqn
(
mvConvection->fvmDiv(phi, he)
+ (
he.name() == "e"
? fvc::div(phi, volScalarField("Ekp", 0.5*magSqr(U) + p/rho))
: fvc::div(phi, volScalarField("K", 0.5*magSqr(U)))
)
- fvm::laplacian(turbulence->alphaEff(), he)
==
parcels.Sh(he)
+ radiation->Sh(thermo)
+ combustion->Sh()
+ fvOptions(rho, he)
);
EEqn.relax();
fvOptions.constrain(EEqn);
EEqn.solve();
fvOptions.correct(he);
thermo.correct();
radiation->correct();
Info<< "T gas min/max = " << min(T).value() << ", "
<< max(T).value() << endl;
}

View File

@ -0,0 +1,3 @@
simpleReactingParcelFoam.C
EXE = $(FOAM_APPBIN)/simpleReactingParcelFoam

View File

@ -0,0 +1,53 @@
EXE_INC = \
-I$(LIB_SRC)/finiteVolume/lnInclude \
-I${LIB_SRC}/meshTools/lnInclude \
-I$(LIB_SRC)/turbulenceModels/compressible/turbulenceModel \
-I$(LIB_SRC)/lagrangian/basic/lnInclude \
-I$(LIB_SRC)/lagrangian/intermediate/lnInclude \
-I$(LIB_SRC)/lagrangian/coalCombustion/lnInclude \
-I$(LIB_SRC)/lagrangian/distributionModels/lnInclude \
-I$(LIB_SRC)/thermophysicalModels/specie/lnInclude \
-I$(LIB_SRC)/thermophysicalModels/basic/lnInclude \
-I$(LIB_SRC)/thermophysicalModels/properties/liquidProperties/lnInclude \
-I$(LIB_SRC)/thermophysicalModels/properties/liquidMixtureProperties/lnInclude \
-I$(LIB_SRC)/thermophysicalModels/properties/solidProperties/lnInclude \
-I$(LIB_SRC)/thermophysicalModels/properties/solidMixtureProperties/lnInclude \
-I$(LIB_SRC)/thermophysicalModels/thermophysicalFunctions/lnInclude \
-I$(LIB_SRC)/thermophysicalModels/reactionThermo/lnInclude \
-I$(LIB_SRC)/thermophysicalModels/SLGThermo/lnInclude \
-I$(LIB_SRC)/thermophysicalModels/chemistryModel/lnInclude \
-I$(LIB_SRC)/thermophysicalModels/radiationModels/lnInclude \
-I$(LIB_SRC)/ODE/lnInclude \
-I$(LIB_SRC)/regionModels/regionModel/lnInclude \
-I$(LIB_SRC)/regionModels/surfaceFilmModels/lnInclude \
-I$(LIB_SRC)/combustionModels/lnInclude \
-I$(LIB_SRC)/fvOptions/lnInclude \
-I$(LIB_SRC)/sampling/lnInclude \
-I$(FOAM_SOLVERS)/combustion/reactingFoam
EXE_LIBS = \
-lfiniteVolume \
-lmeshTools \
-lcompressibleTurbulenceModel \
-lcompressibleRASModels \
-lcompressibleLESModels \
-llagrangian \
-llagrangianIntermediate \
-lspecie \
-lfluidThermophysicalModels \
-lliquidProperties \
-lliquidMixtureProperties \
-lsolidProperties \
-lsolidMixtureProperties \
-lthermophysicalFunctions \
-lreactionThermophysicalModels \
-lSLGThermo \
-lchemistryModel \
-lradiationModels \
-lODE \
-lregionModels \
-lsurfaceFilmModels \
-lcombustionModels \
-lfvOptions \
-lsampling

View File

@ -0,0 +1,17 @@
tmp<fvVectorMatrix> UEqn
(
fvm::div(phi, U)
+ turbulence->divDevRhoReff(U)
==
rho.dimensionedInternalField()*g
+ parcels.SU(U)
+ fvOptions(rho, U)
);
UEqn().relax();
fvOptions.constrain(UEqn());
solve(UEqn() == -fvc::grad(p));
fvOptions.correct(U);

View File

@ -0,0 +1,53 @@
tmp<fv::convectionScheme<scalar> > mvConvection
(
fv::convectionScheme<scalar>::New
(
mesh,
fields,
phi,
mesh.divScheme("div(phi,Yi_h)")
)
);
{
combustion->correct();
dQ = combustion->dQ();
label inertIndex = -1;
volScalarField Yt(0.0*Y[0]);
forAll(Y, i)
{
if (Y[i].name() != inertSpecie)
{
volScalarField& Yi = Y[i];
fvScalarMatrix YEqn
(
mvConvection->fvmDiv(phi, Yi)
- fvm::laplacian(turbulence->muEff(), Yi)
==
parcels.SYi(i, Yi)
+ combustion->R(Yi)
+ fvOptions(rho, Yi)
);
YEqn.relax();
fvOptions.constrain(YEqn);
YEqn.solve(mesh.solver("Yi"));
fvOptions.correct(Yi);
Yi.max(0.0);
Yt += Yi;
}
else
{
inertIndex = i;
}
}
Y[inertIndex] = scalar(1) - Yt;
Y[inertIndex].max(0.0);
}

View File

@ -0,0 +1,9 @@
Info<< "\nConstructing reacting cloud" << endl;
basicReactingMultiphaseCloud parcels
(
"reactingCloud1",
rho,
U,
g,
slgThermo
);

View File

@ -0,0 +1,98 @@
Info<< "Creating combustion model\n" << endl;
autoPtr<combustionModels::rhoCombustionModel> combustion
(
combustionModels::rhoCombustionModel::New(mesh)
);
rhoReactionThermo& thermo = combustion->thermo();
thermo.validate(args.executable(), "h", "e");
SLGThermo slgThermo(mesh, thermo);
basicMultiComponentMixture& composition = thermo.composition();
PtrList<volScalarField>& Y = composition.Y();
const word inertSpecie(thermo.lookup("inertSpecie"));
if (!composition.contains(inertSpecie))
{
FatalErrorIn(args.executable())
<< "Specified inert specie '" << inertSpecie << "' not found in "
<< "species list. Available species:" << composition.species()
<< exit(FatalError);
}
volScalarField& p = thermo.p();
const volScalarField& T = thermo.T();
const volScalarField& psi = thermo.psi();
volScalarField rho
(
IOobject
(
"rho",
runTime.timeName(),
mesh,
IOobject::NO_READ,
IOobject::AUTO_WRITE
),
thermo.rho()
);
Info<< "\nReading field U\n" << endl;
volVectorField U
(
IOobject
(
"U",
runTime.timeName(),
mesh,
IOobject::MUST_READ,
IOobject::AUTO_WRITE
),
mesh
);
#include "compressibleCreatePhi.H"
dimensionedScalar rhoMax(simple.dict().lookup("rhoMax"));
dimensionedScalar rhoMin(simple.dict().lookup("rhoMin"));
Info<< "Creating turbulence model\n" << endl;
autoPtr<compressible::turbulenceModel> turbulence
(
compressible::turbulenceModel::New
(
rho,
U,
phi,
thermo
)
);
// Set the turbulence into the combustion model
combustion->setTurbulence(turbulence());
Info<< "Creating multi-variate interpolation scheme\n" << endl;
multivariateSurfaceInterpolationScheme<scalar>::fieldTable fields;
forAll(Y, i)
{
fields.add(Y[i]);
}
fields.add(thermo.he());
volScalarField dQ
(
IOobject
(
"dQ",
runTime.timeName(),
mesh,
IOobject::NO_READ,
IOobject::AUTO_WRITE
),
mesh,
dimensionedScalar("dQ", dimEnergy/dimTime, 0.0)
);

View File

@ -0,0 +1,59 @@
{
rho = thermo.rho();
// Thermodynamic density needs to be updated by psi*d(p) after the
// pressure solution - done in 2 parts. Part 1:
thermo.rho() -= psi*p;
volScalarField rAU(1.0/UEqn().A());
volVectorField HbyA("HbyA", U);
HbyA = rAU*UEqn().H();
UEqn.clear();
surfaceScalarField phiHbyA
(
"phiHbyA",
fvc::interpolate(rho)*(fvc::interpolate(HbyA) & mesh.Sf())
);
fvOptions.relativeFlux(fvc::interpolate(rho), phiHbyA);
while (simple.correctNonOrthogonal())
{
fvScalarMatrix pEqn
(
fvc::div(phiHbyA)
- fvm::laplacian(rho*rAU, p)
==
parcels.Srho()
+ fvOptions(psi, p, rho.name())
);
fvOptions.constrain(pEqn);
pEqn.solve();
if (simple.finalNonOrthogonalIter())
{
phi = phiHbyA + pEqn.flux();
}
}
p.relax();
// Second part of thermodynamic density update
thermo.rho() += psi*p;
#include "compressibleContinuityErrs.H"
U = HbyA - rAU*fvc::grad(p);
U.correctBoundaryConditions();
fvOptions.correct(U);
rho = thermo.rho();
rho = max(rho, rhoMin);
rho = min(rho, rhoMax);
Info<< "p min/max = " << min(p).value() << ", " << max(p).value() << endl;
}

View File

@ -0,0 +1,95 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2013 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/>.
Application
simpleReactingParcelFoam
Description
Steady state SIMPLE solver for compressible, laminar or turbulent flow with
reacting multiphase Lagrangian parcels, including run-time selectable
finite volume options, e.g. sources, constraints
\*---------------------------------------------------------------------------*/
#include "fvCFD.H"
#include "turbulenceModel.H"
#include "basicReactingMultiphaseCloud.H"
#include "rhoCombustionModel.H"
#include "radiationModel.H"
#include "IOporosityModelList.H"
#include "fvIOoptionList.H"
#include "SLGThermo.H"
#include "simpleControl.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
#include "setRootCase.H"
#include "createTime.H"
#include "createMesh.H"
#include "readGravitationalAcceleration.H"
simpleControl simple(mesh);
#include "createFields.H"
#include "createRadiationModel.H"
#include "createClouds.H"
#include "createFvOptions.H"
#include "initContinuityErrs.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
Info<< "\nStarting time loop\n" << endl;
while (simple.loop())
{
Info<< "Time = " << runTime.timeName() << nl << endl;
parcels.evolve();
// --- Pressure-velocity SIMPLE corrector loop
{
#include "UEqn.H"
#include "YEqn.H"
#include "EEqn.H"
#include "pEqn.H"
}
turbulence->correct();
runTime.write();
Info<< "ExecutionTime = " << runTime.elapsedCpuTime() << " s"
<< " ClockTime = " << runTime.elapsedClockTime() << " s"
<< nl << endl;
}
Info<< "End\n" << endl;
return(0);
}
// ************************************************************************* //

View File

@ -38,94 +38,6 @@ Description
using namespace Foam;
// return
// 0: no match
// +1: identical
// -1: same face, but different orientation
label compare(const face& a, const face& b)
{
// Basic rule: we assume that the sequence of labels in each list
// will be circular in the same order (but not necessarily in the
// same direction or from the same starting point).
// Trivial reject: faces are different size
label sizeA = a.size();
label sizeB = b.size();
if (sizeA != sizeB || sizeA == 0)
{
return 0;
}
const_circulator<face> aCirc(a);
const_circulator<face> bCirc(b);
// Rotate face b until its element matches the starting element of face a.
do
{
if (aCirc() == bCirc())
{
// Set bCirc fulcrum to its iterator and increment the iterators
bCirc.setFulcrumToIterator();
++aCirc;
++bCirc;
break;
}
} while (bCirc.circulate(CirculatorBase::CLOCKWISE));
// Look forwards around the faces for a match
do
{
if (aCirc() != bCirc())
{
break;
}
}
while
(
aCirc.circulate(CirculatorBase::CLOCKWISE),
bCirc.circulate(CirculatorBase::CLOCKWISE)
);
// If the circulator has stopped then faces a and b matched.
if (!aCirc.circulate())
{
return 1;
}
else
{
// Reset the circulators back to their fulcrum
aCirc.setIteratorToFulcrum();
bCirc.setIteratorToFulcrum();
++aCirc;
--bCirc;
}
// Look backwards around the faces for a match
do
{
if (aCirc() != bCirc())
{
break;
}
}
while
(
aCirc.circulate(CirculatorBase::CLOCKWISE),
bCirc.circulate(CirculatorBase::ANTICLOCKWISE)
);
// If the circulator has stopped then faces a and b matched.
if (!aCirc.circulate())
{
return -1;
}
return 0;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Main program:
@ -184,40 +96,44 @@ int main(int argc, char *argv[])
Info<< nl << nl << "Compare two faces: " << endl;
face a(identity(5));
Info<< "Compare " << a << " and " << a << " Match = " << compare(a, a)
Info<< "Compare " << a << " and " << a << " Match = " << face::compare(a, a)
<< endl;
face b(reverseList(a));
Info<< "Compare " << a << " and " << b << " Match = " << compare(a, b)
Info<< "Compare " << a << " and " << b << " Match = " << face::compare(a, b)
<< endl;
face c(a);
c[4] = 3;
Info<< "Compare " << a << " and " << c << " Match = " << compare(a, c)
Info<< "Compare " << a << " and " << c << " Match = " << face::compare(a, c)
<< endl;
face d(rotateList(a, 2));
Info<< "Compare " << a << " and " << d << " Match = " << compare(a, d)
Info<< "Compare " << a << " and " << d << " Match = " << face::compare(a, d)
<< endl;
face g(labelList(5, 1));
face h(g);
Info<< "Compare " << g << " and " << h << " Match = " << compare(g, h)
Info<< "Compare " << g << " and " << h << " Match = " << face::compare(g, h)
<< endl;
g[0] = 2;
h[3] = 2;
Info<< "Compare " << g << " and " << h << " Match = " << compare(g, h)
Info<< "Compare " << g << " and " << h << " Match = " << face::compare(g, h)
<< endl;
g[4] = 3;
h[4] = 3;
Info<< "Compare " << g << " and " << h << " Match = " << compare(g, h)
Info<< "Compare " << g << " and " << h << " Match = " << face::compare(g, h)
<< endl;
face face1(identity(1));
Info<< "Compare " << face1 << " and " << face1
<< " Match = " << compare(face1, face1) << endl;
<< " Match = " << face::compare(face1, face1) << endl;
face face2(identity(1)+1);
Info<< "Compare " << face1 << " and " << face2
<< " Match = " << face::compare(face1, face2) << endl;
Info<< nl << nl << "Zero face" << nl << endl;

View File

@ -17,7 +17,7 @@ FoamFile
collapseEdgesCoeffs
{
// Edges shorter than this absolute value will be merged
minimumEdgeLength 1e-8;
minimumEdgeLength 1e-6;
// The maximum angle between two edges that share a point attached to
// no other edges
@ -25,7 +25,7 @@ collapseEdgesCoeffs
// The amount that minimumEdgeLength will be reduced by for each
// edge if that edge's collapse generates a poor quality face
reductionFactor 0.5;
reductionFactor 0.5;
}
@ -67,14 +67,17 @@ meshQualityCoeffs
{
// Name of the dictionary that has the mesh quality coefficients used
// by motionSmoother::checkMesh
meshQualityCoeffDict meshQualityDict;
#include "meshQualityDict";
// Maximum number of smoothing iterations for the reductionFactors
maximumSmoothingIterations 2;
// Maximum number of outer iterations is mesh quality checking is enabled
maximumIterations 10;
maximumIterations 10;
// Maximum number of iterations deletion of a point can cause a bad face
// to be constructed before it is forced to not be deleted
maxPointErrorCount 5;
maxPointErrorCount 5;
}

View File

@ -1,5 +1,4 @@
EXE_INC = \
/* -DFULLDEBUG -g -O0 */ \
-I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/dynamicMesh/lnInclude \
-I$(LIB_SRC)/finiteVolume/lnInclude \

View File

@ -26,12 +26,6 @@ License
#include "patchToPoly2DMesh.H"
#include "PatchTools.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::patchToPoly2DMesh::flipFaceOrder()
@ -275,12 +269,9 @@ void Foam::patchToPoly2DMesh::createPolyMeshComponents()
addPatchFacesToOwner();
}
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
//- Construct from a primitivePatch
Foam::patchToPoly2DMesh::patchToPoly2DMesh
(
const MeshedSurface<face>& patch,
@ -321,13 +312,5 @@ void Foam::patchToPoly2DMesh::createMesh()
}
}
// * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * * //
// * * * * * * * * * * * * * * Friend Functions * * * * * * * * * * * * * * //
// * * * * * * * * * * * * * * Friend Operators * * * * * * * * * * * * * * //
// ************************************************************************* //

View File

@ -51,6 +51,7 @@ class patchToPoly2DMesh
{
// Private data
// Reference to the meshed surface
const MeshedSurface<face>& patch_;
const wordList& patchNames_;
@ -62,11 +63,16 @@ class patchToPoly2DMesh
const EdgeMap<label>& mapEdgesRegion_;
pointField points_;
faceList faces_;
labelList owner_;
labelList neighbour_;
//- Description of data_
// Private Member Functions
void flipFaceOrder();
void createNeighbours();
@ -79,9 +85,6 @@ class patchToPoly2DMesh
void createPolyMeshComponents();
// Private Member Functions
//- Disallow default bitwise copy construct
patchToPoly2DMesh(const patchToPoly2DMesh&);
@ -110,47 +113,47 @@ public:
// Member Functions
// Access
pointField& points()
{
return points_;
}
faceList& faces()
{
return faces_;
}
pointField& points()
{
return points_;
}
labelList& owner()
{
return owner_;
}
faceList& faces()
{
return faces_;
}
labelList& neighbour()
{
return neighbour_;
}
labelList& owner()
{
return owner_;
}
const wordList& patchNames() const
{
return patchNames_;
}
labelList& neighbour()
{
return neighbour_;
}
const labelList& patchSizes() const
{
return patchSizes_;
}
const wordList& patchNames() const
{
return patchNames_;
}
const labelList& patchSizes() const
{
return patchSizes_;
}
const labelList& patchStarts() const
{
return patchStarts_;
}
const labelList& patchStarts() const
{
return patchStarts_;
}
// Check
// Edit
void createMesh();
// Write
//- Create the mesh
void createMesh();
};

View File

@ -317,12 +317,12 @@ int main(int argc, char *argv[])
}
// Take over refinement levels and write to new time directory.
Pout<< "\nWriting extruded mesh to time = " << runTimeExtruded.timeName()
Info<< "\nWriting extruded mesh to time = " << runTimeExtruded.timeName()
<< nl << endl;
mesh().write();
Pout<< "End\n" << endl;
Info<< "End\n" << endl;
return 0;
}

View File

@ -2,31 +2,31 @@
#define CGAL_PSURF_RINGS_H_
// This file adapted from
// CGAL-3.7/examples/Jet_fitting_3//PolyhedralSurf_rings.h
// Licensed under CGAL-3.7/LICENSE.FREE_USE
// CGAL-4.0/examples/Jet_fitting_3/PolyhedralSurf_rings.h
// Licensed under CGAL-4.0/LICENSE.FREE_USE
// Copyright (c) 1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007
// Utrecht University (The Netherlands), ETH Zurich (Switzerland), Freie
// Universitaet Berlin (Germany), INRIA Sophia-Antipolis (France),
// Martin-Luther-University Halle-Wittenberg (Germany), Max-Planck-Institute
// Saarbruecken (Germany), RISC Linz (Austria), and Tel-Aviv University
// (Israel). All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// Utrecht University (The Netherlands),
// ETH Zurich (Switzerland),
// INRIA Sophia-Antipolis (France),
// Max-Planck-Institute Saarbruecken (Germany),
// and Tel-Aviv University (Israel). All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <cassert>