Files
OpenFOAM-12/src/OpenFOAM/distributions/multiNormal/multiNormal.C
Will Bainbridge e1e0e258c8 distributions: Generalise usage
The distributions have been extended in various ways to permit usage in
a greater variety of situations...

The distributions now have write methods which allow a distribution to
be written into a field file for restart, therby permitting their usage
in boundary conditions and similar. Their read methods now also support
dimension-checked unit conversions for all their parameters.

An additional selector has been added that allows a distribution to be
re-constructed with a different sample size exponent.

The distributions now own their random generator, thereby simplifying
their usage and preventing the need for a (potentially dangling)
reference member. This makes sense now as the random generators do not
rely on global state; individual sub-models can and should own their own
random generator and manage its initialisation and restart. This
principle should be extended to other parts of the code in future.
2024-06-11 10:47:23 +01:00

292 lines
7.1 KiB
C++

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2011-2024 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 "multiNormal.H"
#include "mathematicalConstants.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
namespace distributions
{
defineTypeNameAndDebug(multiNormal, 0);
addToRunTimeSelectionTable(distribution, multiNormal, dictionary);
}
}
// * * * * * * * * * * * Private Static Member Functions * * * * * * * * * * //
Foam::scalarList Foam::distributions::multiNormal::readCumulativeStrengths
(
const dictionary& dict
)
{
const scalarList s(dict.lookup<scalarList>("strength"));
const scalarList sHat(s/sum(s));
scalarList cumSHat(s.size() + 1, scalar(0));
forAll(sHat, i)
{
cumSHat[i + 1] = cumSHat[i] + sHat[i];
}
return cumSHat;
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::tmp<Foam::scalarField> Foam::distributions::multiNormal::phi
(
const label q,
const scalarField& x
) const
{
scalarField phiQ0(x.size(), 0);
forAll(distributions_, i)
{
phiQ0 +=
(cumulativeStrengths_[i + 1] - cumulativeStrengths_[i])
*distributions_[i].phi(0, x);
}
return integerPow(x, q)*phiQ0;
}
Foam::tmp<Foam::scalarField> Foam::distributions::multiNormal::Phi
(
const label q,
const scalarField& x
) const
{
if (q == 0)
{
tmp<scalarField> tPhiQ0(new scalarField(x.size(), 0));
scalarField& PhiQ0 = tPhiQ0.ref();
forAll(distributions_, i)
{
PhiQ0 +=
(cumulativeStrengths_[i + 1] - cumulativeStrengths_[i])
*distributions_[i].Phi(0, x);
}
return tPhiQ0;
}
else
{
return unintegrableForNonZeroQ::Phi(q, x);
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::distributions::multiNormal::multiNormal
(
const unitConversion& units,
const dictionary& dict,
const label sampleQ,
randomGenerator&& rndGen
)
:
FieldDistribution<unintegrableForNonZeroQ, multiNormal>
(
typeName,
units,
dict,
sampleQ,
std::move(rndGen)
),
cumulativeStrengths_(readCumulativeStrengths(dict))
{
const scalar min
(
dict.lookupBackwardsCompatible<scalar>({"min", "minValue"}, units)
);
const scalar max
(
dict.lookupBackwardsCompatible<scalar>({"max", "maxValue"}, units)
);
const scalarList mu
(
dict.lookupBackwardsCompatible<scalarList>({"mu", "expectation"}, units)
);
const scalarList sigma
(
dict.lookup<scalarList>("sigma", units)
);
if
(
mu.size() != cumulativeStrengths_.size() - 1
|| sigma.size() != cumulativeStrengths_.size() - 1
)
{
FatalIOErrorInFunction(dict)
<< type() << ": Differing numbers of means, standard deviations "
<< "and strengths were given:" << nl << " mu = " << mu
<< ", sigma = " << sigma << ", strength = "
<< dict.lookup<scalarList>("strength") << abort(FatalIOError);
}
distributions_.resize(mu.size());
forAll(mu, i)
{
distributions_.set
(
i,
new normal
(
0,
0,
rndGen_.generator(),
-1,
min,
max,
mu[i],
sigma[i]
)
);
}
validateBounds(dict);
if (q() != 0) validatePositive(dict);
mean();
}
Foam::distributions::multiNormal::multiNormal
(
const multiNormal& d,
const label sampleQ
)
:
FieldDistribution<unintegrableForNonZeroQ, multiNormal>(d, sampleQ),
cumulativeStrengths_(d.cumulativeStrengths_),
distributions_(d.distributions_.size())
{
forAll(d.distributions_, i)
{
distributions_.set
(
i,
new normal
(
0,
0,
randomGenerator(d.distributions_[i].rndGen_),
-1,
d.distributions_[i].min_,
d.distributions_[i].max_,
d.distributions_[i].mu_,
d.distributions_[i].sigma_
)
);
}
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::distributions::multiNormal::~multiNormal()
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::scalar Foam::distributions::multiNormal::sample() const
{
if (q() == 0)
{
const scalar S = rndGen_.sample01<scalar>();
const label n = cumulativeStrengths_.size() - 1;
label i = 0;
for (; i < n && cumulativeStrengths_[i + 1] < S; ++ i);
const scalar s =
(S - cumulativeStrengths_[i])
/(cumulativeStrengths_[i + 1] - cumulativeStrengths_[i]);
return distributions_[i].sampleForZeroQ(s);
}
else
{
return unintegrableForNonZeroQ::sample();
}
}
Foam::scalar Foam::distributions::multiNormal::min() const
{
return distributions_[0].min();
}
Foam::scalar Foam::distributions::multiNormal::max() const
{
return distributions_[0].max();
}
void Foam::distributions::multiNormal::write
(
Ostream& os,
const unitConversion& units
) const
{
FieldDistribution<unintegrableForNonZeroQ, multiNormal>::write(os, units);
writeEntry(os, "min", units, distributions_[0].min());
writeEntry(os, "max", units, distributions_[0].max());
scalarList mu(distributions_.size()), sigma(distributions_.size());
forAll(distributions_, i)
{
mu[i] = distributions_[i].mu();
sigma[i] = distributions_[i].sigma();
}
writeEntry(os, "mu", units, mu);
writeEntry(os, "sigma", units, sigma);
}
Foam::tmp<Foam::scalarField>
Foam::distributions::multiNormal::x(const label n) const
{
return distributions_[0].x(n);
}
// ************************************************************************* //