fixed writing out entries in advective bc
[OpenFOAM-1.6-ext.git] / src / lagrangian / molecularDynamics / molecularMeasurements / distribution / distribution.C
blob1e1c1bdd9a14f3a5952020e201e8af127d231e5a
1 /*---------------------------------------------------------------------------*\
2   =========                 |
3   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
4    \\    /   O peration     |
5     \\  /    A nd           | Copyright held by original author
6      \\/     M anipulation  |
7 -------------------------------------------------------------------------------
8 License
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
19     for more details.
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"
29 namespace Foam
32 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
34 distribution::distribution()
36     Map<label>(),
37     binWidth_(1)
41 distribution::distribution(const scalar binWidth)
43     Map<label>(),
44     binWidth_(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)
68     {
69         sumOfEntries += iter();
71         if (sumOfEntries < 0)
72         {
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;
81             sumOfEntries = -1;
83             break;
84         }
85     }
87     return sumOfEntries;
91 scalar distribution::approxTotalEntries() const
93     scalar sumOfEntries = 0;
95     forAllConstIter(Map<label>, *this, iter)
96     {
97         sumOfEntries += scalar(iter());
98     }
100     return sumOfEntries;
104 scalar distribution::mean() const
106     scalar runningSum = 0;
108     scalar totEnt = approxTotalEntries();
110     List<label> keys = toc();
112     forAll(keys,k)
113     {
114         label key = keys[k];
116         runningSum +=
117             (0.5 + scalar(key))
118            *binWidth_
119            *scalar((*this)[key])
120            /totEnt;
121     }
123     return runningSum;
127 scalar distribution::median()
129     // From:
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.
134     scalar median = 0.0;
136     scalar runningSum = 0.0;
138     List<Pair<scalar> > normDist(normalised());
140     if (normDist.size())
141     {
142         if (normDist.size() == 1)
143         {
144             median = normDist[0].first();
145         }
146         else if
147         (
148             normDist.size() > 1
149          && normDist[0].second()*binWidth_ > 0.5
150         )
151         {
152             scalar xk = normDist[1].first();
153             scalar xkm1 = normDist[0].first();
154             scalar Sk =
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;
159         }
160         else
161         {
162             label lastNonZeroIndex = 0;
164             forAll(normDist,nD)
165             {
166                 if (runningSum + (normDist[nD].second()*binWidth_) > 0.5)
167                 {
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;
175                     break;
176                 }
177                 else if (normDist[nD].second() > 0.0)
178                 {
179                     runningSum += normDist[nD].second()*binWidth_;
181                     lastNonZeroIndex = nD;
182                 }
183             }
184         }
185     }
187     return median;
191 void distribution::add(const scalar valueToAdd)
193     iterator iter(this->begin());
195     label n = label(valueToAdd/binWidth_) - label(neg(valueToAdd/binWidth_));
197     iter = find(n);
199     if (iter == this->end())
200     {
201         this->insert(n,1);
202     }
203     else
204     {
205         (*this)[n]++;
206     }
208     if ((*this)[n] < 0)
209     {
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);
217     }
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();
233     sort(keys);
235     if (keys.size())
236     {
237         for (label k = keys[0]; k < keys[keys.size()-1]; k++)
238         {
239             iter = find(k);
241             if (iter == this->end())
242             {
243                 this->insert(k,0);
244             }
245         }
246     }
250 List< Pair<scalar> > distribution::normalised()
252     scalar totEnt = approxTotalEntries();
254     insertMissingKeys();
256     List<label> keys = toc();
258     sort(keys);
260     List<Pair<scalar> > normDist(size());
262     forAll(keys,k)
263     {
264         label key = keys[k];
266         normDist[k].first() = (0.5 + scalar(key))*binWidth_;
268         normDist[k].second() = scalar((*this)[key])/totEnt/binWidth_;
269     }
271     return normDist;
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());
287     forAll(oldDist,u)
288     {
289         oldDist[u].first() -= shiftValue;
290     }
292     scalar lowestOldBin = oldDist[0].first()/binWidth_ - 0.5;
294     label lowestNewKey = label
295     (
296         lowestOldBin + 0.5*sign(lowestOldBin)
297     );
299     scalar interpolationStartDirection =
300         sign(scalar(lowestNewKey) - lowestOldBin);
302     label newKey = lowestNewKey;
304 //     Info << shiftValue
305 //         << nl << lowestOldBin
306 //         << nl << lowestNewKey
307 //         << nl << interpolationStartDirection
308 //         << endl;
310 //     scalar checkNormalisation = 0;
312 //     forAll (oldDist, oD)
313 //     {
314 //         checkNormalisation += oldDist[oD].second()*binWidth_;
315 //     }
317 //     Info << "Initial normalisation = " << checkNormalisation << endl;
319     forAll(oldDist,u)
320     {
321         newDist[u].first() = (0.5 + scalar(newKey)) * binWidth_;
323         if (interpolationStartDirection < 0)
324         {
325             if (u == 0)
326             {
327                 newDist[u].second() =
328                     (0.5 + scalar(newKey))*oldDist[u].second()
329                   - oldDist[u].second()
330                         *(oldDist[u].first() - binWidth_)/ binWidth_;
331             }
332             else
333             {
334                 newDist[u].second() =
335                     (0.5 + scalar(newKey))
336                    *(oldDist[u].second() - oldDist[u-1].second())
337                   +
338                     (
339                         oldDist[u-1].second()*oldDist[u].first()
340                       - oldDist[u].second()*oldDist[u-1].first()
341                     )
342                     /binWidth_;
343             }
344         }
345         else
346         {
347             if (u == oldDist.size() - 1)
348             {
349                 newDist[u].second() =
350                     (0.5 + scalar(newKey))*-oldDist[u].second()
351                   + oldDist[u].second()*(oldDist[u].first() + binWidth_)
352                    /binWidth_;
353             }
354             else
355             {
356                 newDist[u].second() =
357                     (0.5 + scalar(newKey))
358                    *(oldDist[u+1].second() - oldDist[u].second())
359                   +
360                     (
361                         oldDist[u].second()*oldDist[u+1].first()
362                       - oldDist[u+1].second()*oldDist[u].first()
363                     )
364                    /binWidth_;
365             }
366         }
368         newKey++;
369     }
371 //     checkNormalisation = 0;
373 //     forAll (newDist, nD)
374 //     {
375 //         checkNormalisation += newDist[nD].second()*binWidth_;
376 //     }
378 //     Info << "Shifted normalisation = " << checkNormalisation << endl;
380     return newDist;
384 List<Pair<scalar> > distribution::raw()
386     insertMissingKeys();
388     List<label> keys = toc();
390     sort(keys);
392     List<Pair<scalar> > rawDist(size());
394     forAll(keys,k)
395     {
396         label key = keys[k];
398         rawDist[k].first() = (0.5 + scalar(key))*binWidth_;
400         rawDist[k].second() = scalar((*this)[key]);
401     }
403     return rawDist;
407 // * * * * * * * * * * * * * * * Member Operators  * * * * * * * * * * * * * //
409 void distribution::operator=(const distribution& rhs)
411     // Check for assignment to self
412     if (this == &rhs)
413     {
414         FatalErrorIn("distribution::operator=(const distribution&)")
415             << "Attempted assignment to self"
416             << abort(FatalError);
417     }
419     Map<label>::operator=(rhs);
421     binWidth_ = rhs.binWidth();
425 // * * * * * * * * * * * * * * * Friend Operators  * * * * * * * * * * * * * //
427 Ostream& operator<<(Ostream& os, const distribution& d)
429     os  << d.binWidth_
430         << static_cast<const Map<label>&>(d);
432     // Check state of Ostream
433     os.check
434     (
435         "Ostream& operator<<(Ostream&, "
436         "const distribution&)"
437     );
439     return os;
443 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
445 } // End namespace Foam
447 // ************************************************************************* //