1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright held by original author
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 the
13 Free Software Foundation; either version 2 of the License, or (at your
14 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, write to the Free Software Foundation,
23 Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 \*----------------------------------------------------------------------------*/
27 #include "distribution.H"
32 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
34 distribution::distribution()
41 distribution::distribution(const scalar binWidth)
48 distribution::distribution(const distribution& d)
50 Map<label>(static_cast< Map<label> >(d)),
51 binWidth_(d.binWidth())
55 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
57 distribution::~distribution()
61 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
63 label distribution::totalEntries() const
65 label sumOfEntries = 0;
67 forAllConstIter(Map<label>, *this, iter)
69 sumOfEntries += iter();
73 WarningIn("label distribution::totalEntries()")
74 << "Accumulated distribution values total has become negative: "
75 << "sumOfEntries = " << sumOfEntries
76 << ". This is most likely to be because too many samples "
77 << "have been added to the bins and the label has 'rolled "
78 << "round'. Try distribution::approxTotalEntries which "
79 << "returns a scalar." << endl;
91 scalar distribution::approxTotalEntries() const
93 scalar sumOfEntries = 0;
95 forAllConstIter(Map<label>, *this, iter)
97 sumOfEntries += scalar(iter());
104 scalar distribution::mean() const
106 scalar runningSum = 0;
108 scalar totEnt = approxTotalEntries();
110 List<label> keys = toc();
119 *scalar((*this)[key])
127 scalar distribution::median()
130 // http://mathworld.wolfram.com/StatisticalMedian.html
131 // The statistical median is the value of the distribution variable
132 // where the cumulative distribution = 0.5.
136 scalar runningSum = 0.0;
138 List<Pair<scalar> > normDist(normalised());
142 if (normDist.size() == 1)
144 median = normDist[0].first();
149 && normDist[0].second()*binWidth_ > 0.5
152 scalar xk = normDist[1].first();
153 scalar xkm1 = normDist[0].first();
155 (normDist[0].second() + normDist[1].second())*binWidth_;
156 scalar Skm1 = normDist[0].second()*binWidth_;
158 median = (0.5 - Skm1)*(xk - xkm1)/(Sk - Skm1) + xkm1;
162 label lastNonZeroIndex = 0;
166 if (runningSum + (normDist[nD].second()*binWidth_) > 0.5)
168 scalar xk = normDist[nD].first();
169 scalar xkm1 = normDist[lastNonZeroIndex].first();
170 scalar Sk = runningSum + (normDist[nD].second()*binWidth_);
171 scalar Skm1 = runningSum;
173 median = (0.5 - Skm1)*(xk - xkm1)/(Sk - Skm1) + xkm1;
177 else if (normDist[nD].second() > 0.0)
179 runningSum += normDist[nD].second()*binWidth_;
181 lastNonZeroIndex = nD;
191 void distribution::add(const scalar valueToAdd)
193 iterator iter(this->begin());
195 label n = label(valueToAdd/binWidth_) - label(neg(valueToAdd/binWidth_));
199 if (iter == this->end())
210 FatalErrorIn("distribution::add(const scalar valueToAdd)")
211 << "Accumulated distribution value has become negative: "
212 << "bin = " << (0.5 + scalar(n)) * binWidth_
213 << ", value = " << (*this)[n]
214 << ". This is most likely to be because too many samples "
215 << "have been added to a bin and the label has 'rolled round'"
216 << abort(FatalError);
221 void distribution::add(const label valueToAdd)
223 add(scalar(valueToAdd));
227 void distribution::insertMissingKeys()
229 iterator iter(this->begin());
231 List<label> keys = toc();
237 for (label k = keys[0]; k < keys[keys.size()-1]; k++)
241 if (iter == this->end())
250 List< Pair<scalar> > distribution::normalised()
252 scalar totEnt = approxTotalEntries();
256 List<label> keys = toc();
260 List<Pair<scalar> > normDist(size());
266 normDist[k].first() = (0.5 + scalar(key))*binWidth_;
268 normDist[k].second() = scalar((*this)[key])/totEnt/binWidth_;
275 List< Pair<scalar> > distribution::normalisedMinusMean()
277 return normalisedShifted(mean());
281 List< Pair<scalar> > distribution::normalisedShifted(const scalar shiftValue)
283 List<Pair<scalar> > oldDist(normalised());
285 List<Pair<scalar> > newDist(oldDist.size());
289 oldDist[u].first() -= shiftValue;
292 scalar lowestOldBin = oldDist[0].first()/binWidth_ - 0.5;
294 label lowestNewKey = label
296 lowestOldBin + 0.5*sign(lowestOldBin)
299 scalar interpolationStartDirection =
300 sign(scalar(lowestNewKey) - lowestOldBin);
302 label newKey = lowestNewKey;
304 // Info << shiftValue
305 // << nl << lowestOldBin
306 // << nl << lowestNewKey
307 // << nl << interpolationStartDirection
310 // scalar checkNormalisation = 0;
312 // forAll (oldDist, oD)
314 // checkNormalisation += oldDist[oD].second()*binWidth_;
317 // Info << "Initial normalisation = " << checkNormalisation << endl;
321 newDist[u].first() = (0.5 + scalar(newKey)) * binWidth_;
323 if (interpolationStartDirection < 0)
327 newDist[u].second() =
328 (0.5 + scalar(newKey))*oldDist[u].second()
329 - oldDist[u].second()
330 *(oldDist[u].first() - binWidth_)/ binWidth_;
334 newDist[u].second() =
335 (0.5 + scalar(newKey))
336 *(oldDist[u].second() - oldDist[u-1].second())
339 oldDist[u-1].second()*oldDist[u].first()
340 - oldDist[u].second()*oldDist[u-1].first()
347 if (u == oldDist.size() - 1)
349 newDist[u].second() =
350 (0.5 + scalar(newKey))*-oldDist[u].second()
351 + oldDist[u].second()*(oldDist[u].first() + binWidth_)
356 newDist[u].second() =
357 (0.5 + scalar(newKey))
358 *(oldDist[u+1].second() - oldDist[u].second())
361 oldDist[u].second()*oldDist[u+1].first()
362 - oldDist[u+1].second()*oldDist[u].first()
371 // checkNormalisation = 0;
373 // forAll (newDist, nD)
375 // checkNormalisation += newDist[nD].second()*binWidth_;
378 // Info << "Shifted normalisation = " << checkNormalisation << endl;
384 List<Pair<scalar> > distribution::raw()
388 List<label> keys = toc();
392 List<Pair<scalar> > rawDist(size());
398 rawDist[k].first() = (0.5 + scalar(key))*binWidth_;
400 rawDist[k].second() = scalar((*this)[key]);
407 // * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
409 void distribution::operator=(const distribution& rhs)
411 // Check for assignment to self
414 FatalErrorIn("distribution::operator=(const distribution&)")
415 << "Attempted assignment to self"
416 << abort(FatalError);
419 Map<label>::operator=(rhs);
421 binWidth_ = rhs.binWidth();
425 // * * * * * * * * * * * * * * * Friend Operators * * * * * * * * * * * * * //
427 Ostream& operator<<(Ostream& os, const distribution& d)
430 << static_cast<const Map<label>&>(d);
432 // Check state of Ostream
435 "Ostream& operator<<(Ostream&, "
436 "const distribution&)"
443 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
445 } // End namespace Foam
447 // ************************************************************************* //