FreeFOAM The Cross-Platform CFD Toolkit
Hosted by SourceForge:
Get FreeFOAM at SourceForge.net.
            Fast, secure and Free Open Source software downloads

distribution.C

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------------*\
00002   =========                 |
00003   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
00004    \\    /   O peration     |
00005     \\  /    A nd           | Copyright (C) 2008-2010 OpenCFD Ltd.
00006      \\/     M anipulation  |
00007 -------------------------------------------------------------------------------
00008 License
00009     This file is part of OpenFOAM.
00010 
00011     OpenFOAM is free software: you can redistribute it and/or modify it
00012     under the terms of the GNU General Public License as published by
00013     the Free Software Foundation, either version 3 of the License, or
00014     (at your option) any later version.
00015 
00016     OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
00017     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00018     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00019     for more details.
00020 
00021     You should have received a copy of the GNU General Public License
00022     along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
00023 
00024 \*----------------------------------------------------------------------------*/
00025 
00026 #include "distribution.H"
00027 
00028 namespace Foam
00029 {
00030 
00031 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
00032 
00033 distribution::distribution()
00034 :
00035     Map<label>(),
00036     binWidth_(1)
00037 {}
00038 
00039 
00040 distribution::distribution(const scalar binWidth)
00041 :
00042     Map<label>(),
00043     binWidth_(binWidth)
00044 {}
00045 
00046 
00047 distribution::distribution(const distribution& d)
00048 :
00049     Map<label>(static_cast< Map<label> >(d)),
00050     binWidth_(d.binWidth())
00051 {}
00052 
00053 
00054 // * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
00055 
00056 distribution::~distribution()
00057 {}
00058 
00059 
00060 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
00061 
00062 label distribution::totalEntries() const
00063 {
00064     label sumOfEntries = 0;
00065 
00066     forAllConstIter(Map<label>, *this, iter)
00067     {
00068         sumOfEntries += iter();
00069 
00070         if (sumOfEntries < 0)
00071         {
00072             WarningIn("label distribution::totalEntries()")
00073                 << "Accumulated distribution values total has become negative: "
00074                 << "sumOfEntries = " << sumOfEntries
00075                 << ". This is most likely to be because too many samples "
00076                 << "have been added to the bins and the label has 'rolled "
00077                 << "round'. Try distribution::approxTotalEntries which "
00078                 << "returns a scalar." << endl;
00079 
00080             sumOfEntries = -1;
00081 
00082             break;
00083         }
00084     }
00085 
00086     return sumOfEntries;
00087 }
00088 
00089 
00090 scalar distribution::approxTotalEntries() const
00091 {
00092     scalar sumOfEntries = 0;
00093 
00094     forAllConstIter(Map<label>, *this, iter)
00095     {
00096         sumOfEntries += scalar(iter());
00097     }
00098 
00099     return sumOfEntries;
00100 }
00101 
00102 
00103 scalar distribution::mean() const
00104 {
00105     scalar runningSum = 0;
00106 
00107     scalar totEnt = approxTotalEntries();
00108 
00109     List<label> keys = toc();
00110 
00111     forAll(keys,k)
00112     {
00113         label key = keys[k];
00114 
00115         runningSum +=
00116             (0.5 + scalar(key))
00117            *binWidth_
00118            *scalar((*this)[key])
00119            /totEnt;
00120     }
00121 
00122     return runningSum;
00123 }
00124 
00125 
00126 scalar distribution::median()
00127 {
00128     // From:
00129     // http://mathworld.wolfram.com/StatisticalMedian.html
00130     // The statistical median is the value of the distribution variable
00131     // where the cumulative distribution = 0.5.
00132 
00133     scalar median = 0.0;
00134 
00135     scalar runningSum = 0.0;
00136 
00137     List<Pair<scalar> > normDist(normalised());
00138 
00139     if (normDist.size())
00140     {
00141         if (normDist.size() == 1)
00142         {
00143             median = normDist[0].first();
00144         }
00145         else if
00146         (
00147             normDist.size() > 1
00148          && normDist[0].second()*binWidth_ > 0.5
00149         )
00150         {
00151             scalar xk = normDist[1].first();
00152             scalar xkm1 = normDist[0].first();
00153             scalar Sk =
00154                 (normDist[0].second() + normDist[1].second())*binWidth_;
00155             scalar Skm1 = normDist[0].second()*binWidth_;
00156 
00157             median = (0.5 - Skm1)*(xk - xkm1)/(Sk - Skm1) + xkm1;
00158         }
00159         else
00160         {
00161             label lastNonZeroIndex = 0;
00162 
00163             forAll(normDist,nD)
00164             {
00165                 if (runningSum + (normDist[nD].second()*binWidth_) > 0.5)
00166                 {
00167                     scalar xk = normDist[nD].first();
00168                     scalar xkm1 = normDist[lastNonZeroIndex].first();
00169                     scalar Sk = runningSum + (normDist[nD].second()*binWidth_);
00170                     scalar Skm1 = runningSum;
00171 
00172                     median = (0.5 - Skm1)*(xk - xkm1)/(Sk - Skm1) + xkm1;
00173 
00174                     break;
00175                 }
00176                 else if (normDist[nD].second() > 0.0)
00177                 {
00178                     runningSum += normDist[nD].second()*binWidth_;
00179 
00180                     lastNonZeroIndex = nD;
00181                 }
00182             }
00183         }
00184     }
00185 
00186     return median;
00187 }
00188 
00189 
00190 void distribution::add(const scalar valueToAdd)
00191 {
00192     iterator iter(this->begin());
00193 
00194     label n = label(valueToAdd/binWidth_) - label(neg(valueToAdd/binWidth_));
00195 
00196     iter = find(n);
00197 
00198     if (iter == this->end())
00199     {
00200         this->insert(n,1);
00201     }
00202     else
00203     {
00204         (*this)[n]++;
00205     }
00206 
00207     if ((*this)[n] < 0)
00208     {
00209         FatalErrorIn("distribution::add(const scalar valueToAdd)")
00210             << "Accumulated distribution value has become negative: "
00211             << "bin = " << (0.5 + scalar(n)) * binWidth_
00212             << ", value = " << (*this)[n]
00213             << ". This is most likely to be because too many samples "
00214             << "have been added to a bin and the label has 'rolled round'"
00215             << abort(FatalError);
00216     }
00217 }
00218 
00219 
00220 void distribution::add(const label valueToAdd)
00221 {
00222     add(scalar(valueToAdd));
00223 }
00224 
00225 
00226 void distribution::insertMissingKeys()
00227 {
00228     iterator iter(this->begin());
00229 
00230     List<label> keys = toc();
00231 
00232     sort(keys);
00233 
00234     if (keys.size())
00235     {
00236         for (label k = keys[0]; k < keys[keys.size()-1]; k++)
00237         {
00238             iter = find(k);
00239 
00240             if (iter == this->end())
00241             {
00242                 this->insert(k,0);
00243             }
00244         }
00245     }
00246 }
00247 
00248 
00249 List< Pair<scalar> > distribution::normalised()
00250 {
00251     scalar totEnt = approxTotalEntries();
00252 
00253     insertMissingKeys();
00254 
00255     List<label> keys = toc();
00256 
00257     sort(keys);
00258 
00259     List<Pair<scalar> > normDist(size());
00260 
00261     forAll(keys,k)
00262     {
00263         label key = keys[k];
00264 
00265         normDist[k].first() = (0.5 + scalar(key))*binWidth_;
00266 
00267         normDist[k].second() = scalar((*this)[key])/totEnt/binWidth_;
00268     }
00269 
00270     return normDist;
00271 }
00272 
00273 
00274 List< Pair<scalar> > distribution::normalisedMinusMean()
00275 {
00276     return normalisedShifted(mean());
00277 }
00278 
00279 
00280 List< Pair<scalar> > distribution::normalisedShifted(const scalar shiftValue)
00281 {
00282     List<Pair<scalar> > oldDist(normalised());
00283 
00284     List<Pair<scalar> > newDist(oldDist.size());
00285 
00286     forAll(oldDist,u)
00287     {
00288         oldDist[u].first() -= shiftValue;
00289     }
00290 
00291     scalar lowestOldBin = oldDist[0].first()/binWidth_ - 0.5;
00292 
00293     label lowestNewKey = label
00294     (
00295         lowestOldBin + 0.5*sign(lowestOldBin)
00296     );
00297 
00298     scalar interpolationStartDirection =
00299         sign(scalar(lowestNewKey) - lowestOldBin);
00300 
00301     label newKey = lowestNewKey;
00302 
00303 //     Info << shiftValue
00304 //         << nl << lowestOldBin
00305 //         << nl << lowestNewKey
00306 //         << nl << interpolationStartDirection
00307 //         << endl;
00308 
00309 //     scalar checkNormalisation = 0;
00310 
00311 //     forAll (oldDist, oD)
00312 //     {
00313 //         checkNormalisation += oldDist[oD].second()*binWidth_;
00314 //     }
00315 
00316 //     Info << "Initial normalisation = " << checkNormalisation << endl;
00317 
00318     forAll(oldDist,u)
00319     {
00320         newDist[u].first() = (0.5 + scalar(newKey)) * binWidth_;
00321 
00322         if (interpolationStartDirection < 0)
00323         {
00324             if (u == 0)
00325             {
00326                 newDist[u].second() =
00327                     (0.5 + scalar(newKey))*oldDist[u].second()
00328                   - oldDist[u].second()
00329                         *(oldDist[u].first() - binWidth_)/ binWidth_;
00330             }
00331             else
00332             {
00333                 newDist[u].second() =
00334                     (0.5 + scalar(newKey))
00335                    *(oldDist[u].second() - oldDist[u-1].second())
00336                   +
00337                     (
00338                         oldDist[u-1].second()*oldDist[u].first()
00339                       - oldDist[u].second()*oldDist[u-1].first()
00340                     )
00341                     /binWidth_;
00342             }
00343         }
00344         else
00345         {
00346             if (u == oldDist.size() - 1)
00347             {
00348                 newDist[u].second() =
00349                     (0.5 + scalar(newKey))*-oldDist[u].second()
00350                   + oldDist[u].second()*(oldDist[u].first() + binWidth_)
00351                    /binWidth_;
00352             }
00353             else
00354             {
00355                 newDist[u].second() =
00356                     (0.5 + scalar(newKey))
00357                    *(oldDist[u+1].second() - oldDist[u].second())
00358                   +
00359                     (
00360                         oldDist[u].second()*oldDist[u+1].first()
00361                       - oldDist[u+1].second()*oldDist[u].first()
00362                     )
00363                    /binWidth_;
00364             }
00365         }
00366 
00367         newKey++;
00368     }
00369 
00370 //     checkNormalisation = 0;
00371 
00372 //     forAll (newDist, nD)
00373 //     {
00374 //         checkNormalisation += newDist[nD].second()*binWidth_;
00375 //     }
00376 
00377 //     Info << "Shifted normalisation = " << checkNormalisation << endl;
00378 
00379     return newDist;
00380 }
00381 
00382 
00383 List<Pair<scalar> > distribution::raw()
00384 {
00385     insertMissingKeys();
00386 
00387     List<label> keys = toc();
00388 
00389     sort(keys);
00390 
00391     List<Pair<scalar> > rawDist(size());
00392 
00393     forAll(keys,k)
00394     {
00395         label key = keys[k];
00396 
00397         rawDist[k].first() = (0.5 + scalar(key))*binWidth_;
00398 
00399         rawDist[k].second() = scalar((*this)[key]);
00400     }
00401 
00402     return rawDist;
00403 }
00404 
00405 
00406 // * * * * * * * * * * * * * * * Member Operators  * * * * * * * * * * * * * //
00407 
00408 void distribution::operator=(const distribution& rhs)
00409 {
00410     // Check for assignment to self
00411     if (this == &rhs)
00412     {
00413         FatalErrorIn("distribution::operator=(const distribution&)")
00414             << "Attempted assignment to self"
00415             << abort(FatalError);
00416     }
00417 
00418     Map<label>::operator=(rhs);
00419 
00420     binWidth_ = rhs.binWidth();
00421 }
00422 
00423 
00424 // * * * * * * * * * * * * * * * Friend Operators  * * * * * * * * * * * * * //
00425 
00426 Ostream& operator<<(Ostream& os, const distribution& d)
00427 {
00428     os  << d.binWidth_
00429         << static_cast<const Map<label>&>(d);
00430 
00431     // Check state of Ostream
00432     os.check
00433     (
00434         "Ostream& operator<<(Ostream&, "
00435         "const distribution&)"
00436     );
00437 
00438     return os;
00439 }
00440 
00441 
00442 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
00443 
00444 } // End namespace Foam
00445 
00446 // ************************ vim: set sw=4 sts=4 et: ************************ //
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines