1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2008-2010 OpenCFD Ltd.
7 -------------------------------------------------------------------------------
9 This file is part of OpenFOAM.
11 OpenFOAM is free software: you can redistribute it and/or modify it
12 under the terms of the GNU General Public License as published by
13 the Free Software Foundation, either version 3 of the License, or
14 (at your option) any later version.
16 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 You should have received a copy of the GNU General Public License
22 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
24 \*----------------------------------------------------------------------------*/
26 #include "distribution.H"
29 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
33 defineTypeNameAndDebug(distribution, 0);
36 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
38 void Foam::distribution::write
41 const List<Pair<scalar> >& pairs
48 os << pairs[i].first() << ' ' << pairs[i].second() << nl;
53 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
55 Foam::distribution::distribution()
62 Foam::distribution::distribution(const scalar binWidth)
69 Foam::distribution::distribution(const distribution& d)
71 Map<label>(static_cast< Map<label> >(d)),
72 binWidth_(d.binWidth())
76 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
78 Foam::distribution::~distribution()
82 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
84 Foam::label Foam::distribution::totalEntries() const
86 label sumOfEntries = 0;
88 forAllConstIter(Map<label>, *this, iter)
90 sumOfEntries += iter();
94 WarningIn("label distribution::totalEntries()")
95 << "Accumulated distribution values total has become negative: "
96 << "sumOfEntries = " << sumOfEntries
97 << ". This is most likely to be because too many samples "
98 << "have been added to the bins and the label has 'rolled "
99 << "round'. Try distribution::approxTotalEntries which "
100 << "returns a scalar." << endl;
112 Foam::scalar Foam::distribution::approxTotalEntries() const
114 scalar sumOfEntries = 0;
116 forAllConstIter(Map<label>, *this, iter)
118 sumOfEntries += scalar(iter());
125 Foam::scalar Foam::distribution::mean() const
127 scalar runningSum = 0;
129 scalar totEnt = approxTotalEntries();
131 List<label> keys = toc();
140 *scalar((*this)[key])
148 Foam::scalar Foam::distribution::median()
151 // http://mathworld.wolfram.com/StatisticalMedian.html
152 // The statistical median is the value of the distribution variable
153 // where the cumulative distribution = 0.5.
157 scalar runningSum = 0.0;
159 List<Pair<scalar> > normDist(normalised());
163 if (normDist.size() == 1)
165 median = normDist[0].first();
170 && normDist[0].second()*binWidth_ > 0.5
173 scalar xk = normDist[1].first();
174 scalar xkm1 = normDist[0].first();
176 (normDist[0].second() + normDist[1].second())*binWidth_;
177 scalar Skm1 = normDist[0].second()*binWidth_;
179 median = (0.5 - Skm1)*(xk - xkm1)/(Sk - Skm1) + xkm1;
183 label lastNonZeroIndex = 0;
187 if (runningSum + (normDist[nD].second()*binWidth_) > 0.5)
189 scalar xk = normDist[nD].first();
190 scalar xkm1 = normDist[lastNonZeroIndex].first();
191 scalar Sk = runningSum + (normDist[nD].second()*binWidth_);
192 scalar Skm1 = runningSum;
194 median = (0.5 - Skm1)*(xk - xkm1)/(Sk - Skm1) + xkm1;
198 else if (normDist[nD].second() > 0.0)
200 runningSum += normDist[nD].second()*binWidth_;
202 lastNonZeroIndex = nD;
212 void Foam::distribution::add(const scalar valueToAdd)
214 iterator iter(this->begin());
216 label n = label(valueToAdd/binWidth_) - label(neg(valueToAdd/binWidth_));
220 if (iter == this->end())
231 FatalErrorIn("distribution::add(const scalar valueToAdd)")
232 << "Accumulated distribution value has become negative: "
233 << "bin = " << (0.5 + scalar(n)) * binWidth_
234 << ", value = " << (*this)[n]
235 << ". This is most likely to be because too many samples "
236 << "have been added to a bin and the label has 'rolled round'"
237 << abort(FatalError);
242 void Foam::distribution::add(const label valueToAdd)
244 add(scalar(valueToAdd));
248 void Foam::distribution::insertMissingKeys()
250 iterator iter(this->begin());
252 List<label> keys = toc();
258 for (label k = keys[0]; k < keys.last(); k++)
262 if (iter == this->end())
271 Foam::List<Foam::Pair<Foam::scalar> > Foam::distribution::normalised()
273 scalar totEnt = approxTotalEntries();
277 List<label> keys = toc();
281 List<Pair<scalar> > normDist(size());
287 normDist[k].first() = (0.5 + scalar(key))*binWidth_;
289 normDist[k].second() = scalar((*this)[key])/totEnt/binWidth_;
294 Info<< "totEnt: " << totEnt << endl;
301 Foam::List<Foam::Pair<Foam::scalar> > Foam::distribution::normalisedMinusMean()
303 return normalisedShifted(mean());
307 Foam::List<Foam::Pair<Foam::scalar> > Foam::distribution::normalisedShifted
312 List<Pair<scalar> > oldDist(normalised());
314 List<Pair<scalar> > newDist(oldDist.size());
318 oldDist[u].first() -= shiftValue;
321 scalar lowestOldBin = oldDist[0].first()/binWidth_ - 0.5;
323 label lowestNewKey = label
325 lowestOldBin + 0.5*sign(lowestOldBin)
328 scalar interpolationStartDirection =
329 sign(scalar(lowestNewKey) - lowestOldBin);
331 label newKey = lowestNewKey;
336 << nl << lowestOldBin
337 << nl << lowestNewKey
338 << nl << interpolationStartDirection
341 scalar checkNormalisation = 0;
345 checkNormalisation += oldDist[oD].second()*binWidth_;
348 Info<< "Initial normalisation = " << checkNormalisation << endl;
353 newDist[u].first() = (0.5 + scalar(newKey)) * binWidth_;
355 if (interpolationStartDirection < 0)
359 newDist[u].second() =
360 (0.5 + scalar(newKey))*oldDist[u].second()
361 - oldDist[u].second()
362 *(oldDist[u].first() - binWidth_)/ binWidth_;
366 newDist[u].second() =
367 (0.5 + scalar(newKey))
368 *(oldDist[u].second() - oldDist[u-1].second())
371 oldDist[u-1].second()*oldDist[u].first()
372 - oldDist[u].second()*oldDist[u-1].first()
379 if (u == oldDist.size() - 1)
381 newDist[u].second() =
382 (0.5 + scalar(newKey))*-oldDist[u].second()
383 + oldDist[u].second()*(oldDist[u].first() + binWidth_)
388 newDist[u].second() =
389 (0.5 + scalar(newKey))
390 *(oldDist[u+1].second() - oldDist[u].second())
393 oldDist[u].second()*oldDist[u+1].first()
394 - oldDist[u+1].second()*oldDist[u].first()
405 scalar checkNormalisation = 0;
409 checkNormalisation += newDist[nD].second()*binWidth_;
412 Info<< "Shifted normalisation = " << checkNormalisation << endl;
419 Foam::List<Foam::Pair<Foam::scalar> > Foam::distribution::raw()
423 List<label> keys = toc();
427 List<Pair<scalar> > rawDist(size());
433 rawDist[k].first() = (0.5 + scalar(key))*binWidth_;
435 rawDist[k].second() = scalar((*this)[key]);
442 // * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
444 void Foam::distribution::operator=(const distribution& rhs)
446 // Check for assignment to self
449 FatalErrorIn("distribution::operator=(const distribution&)")
450 << "Attempted assignment to self"
451 << abort(FatalError);
454 Map<label>::operator=(rhs);
456 binWidth_ = rhs.binWidth();
460 // * * * * * * * * * * * * * * * Friend Operators * * * * * * * * * * * * * //
462 Foam::Ostream& Foam::operator<<(Ostream& os, const distribution& d)
465 << static_cast<const Map<label>&>(d);
467 // Check state of Ostream
470 "Ostream& operator<<(Ostream&, "
471 "const distribution&)"
478 // ************************************************************************* //