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.
292 lines
7.1 KiB
C++
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);
|
|
}
|
|
|
|
|
|
// ************************************************************************* //
|