forecastsolver.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/trunk/modules/forecast/forecastsolver.cpp $
00003   version : $LastChangedRevision: 1505 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2011-08-26 18:55:08 +0200 (Fri, 26 Aug 2011) $
00005  ***************************************************************************/
00006 
00007 /***************************************************************************
00008  *                                                                         *
00009  * Copyright (C) 2007-2011 by Johan De Taeye, frePPLe bvba                 *
00010  *                                                                         *
00011  * This library is free software; you can redistribute it and/or modify it *
00012  * under the terms of the GNU Lesser General Public License as published   *
00013  * by the Free Software Foundation; either version 2.1 of the License, or  *
00014  * (at your option) any later version.                                     *
00015  *                                                                         *
00016  * This library is distributed in the hope that it will be useful,         *
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser *
00019  * General Public License for more details.                                *
00020  *                                                                         *
00021  * You should have received a copy of the GNU Lesser General Public        *
00022  * License along with this library; if not, write to the Free Software     *
00023  * Foundation Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,*
00024  * USA                                                                     *
00025  *                                                                         *
00026  ***************************************************************************/
00027 
00028 #include "forecast.h"
00029 
00030 namespace module_forecast
00031 {
00032 
00033 const MetaClass *ForecastSolver::metadata;
00034 
00035 int ForecastSolver::initialize()
00036 {
00037   // Initialize the metadata
00038   metadata = new MetaClass("solver", "solver_forecast",
00039     Object::createString<ForecastSolver>);
00040 
00041   // Initialize the Python class
00042   return FreppleClass<ForecastSolver,Solver>::initialize();
00043 }
00044 
00045 
00046 bool ForecastSolver::callback(Demand* l, const Signal a)
00047 {
00048   // Call the netting function
00049   solve(l, NULL);
00050 
00051   // Always return 'okay'
00052   return true;
00053 }
00054 
00055 
00056 void ForecastSolver::writeElement(XMLOutput *o, const Keyword& tag, mode m) const
00057 {
00058   // Writing a reference
00059   if (m == REFERENCE)
00060   {
00061     o->writeElement
00062     (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00063     return;
00064   }
00065 
00066   // Write the complete object
00067   if (m != NOHEADER) o->BeginObject
00068     (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00069 
00070   // Write the parent class
00071   Solver::writeElement(o, tag, NOHEADER);
00072 }
00073 
00074 
00075 void ForecastSolver::solve(const Demand* l, void* v)
00076 {
00077   // Forecast don't net themselves, and hidden demands either...
00078   if (!l || dynamic_cast<const Forecast*>(l) || l->getHidden()) return;
00079 
00080   // Message
00081   if (getLogLevel()>0)
00082     logger << "  Netting of demand '" << l << "'  ('" << l->getCustomer()
00083       << "','" << l->getItem() << "', '" << l->getDeliveryOperation()
00084       << "'): " << l->getDue() << ", " << l->getQuantity() << endl;
00085 
00086   // Find a matching forecast
00087   Forecast *fcst = matchDemandToForecast(l);
00088 
00089   if (!fcst)
00090   {
00091     // Message
00092     if (getLogLevel()>0)
00093       logger << "    No matching forecast available" << endl;
00094     return;
00095   }
00096   else if (getLogLevel()>0)
00097     logger << "    Matching forecast: " << fcst << endl;
00098 
00099   // Netting the order from the forecast
00100   netDemandFromForecast(l,fcst);
00101 }
00102 
00103 
00104 void ForecastSolver::solve(void *v)
00105 {
00106   // Sort the demands using the same sort function as used for planning.
00107   // Note: the memory consumption of the sorted list can be significant
00108   sortedDemandList l;
00109   for (Demand::iterator i = Demand::begin(); i != Demand::end(); ++i)
00110     // Only sort non-forecast demand.
00111     if (!dynamic_cast<Forecast*>(&*i)
00112       && !dynamic_cast<ForecastBucket*>(&*i))
00113         l.insert(&*i);
00114 
00115   // Netting loop
00116   for(sortedDemandList::iterator i = l.begin(); i != l.end(); ++i)
00117     try {solve(*i, NULL);}
00118     catch (...)
00119     {
00120       // Error message
00121       logger << "Error: Caught an exception while netting demand '"
00122         << (*i)->getName() << "':" << endl;
00123       try {throw;}
00124       catch (bad_exception&) {logger << "  bad exception" << endl;}
00125       catch (exception& e) {logger << "  " << e.what() << endl;}
00126       catch (...) {logger << "  Unknown type" << endl;}
00127     }
00128 }
00129 
00130 
00131 Forecast* ForecastSolver::matchDemandToForecast(const Demand* l)
00132 {
00133   pair<const Item*, const Customer*> key
00134     = make_pair(&*(l->getItem()), &*(l->getCustomer()));
00135 
00136   do  // Loop through second dimension
00137   {
00138     do // Loop through first dimension
00139     {
00140       Forecast::MapOfForecasts::iterator x = Forecast::ForecastDictionary.lower_bound(key);
00141 
00142       // Loop through all matching keys
00143       while (x != Forecast::ForecastDictionary.end() && x->first == key)
00144       {
00145         if (!Forecast::getMatchUsingDeliveryOperation()
00146           || x->second->getDeliveryOperation() == l->getDeliveryOperation())
00147           // Bingo! Found a matching key, if required plus matching delivery operation
00148           return x->second;
00149         else
00150           ++ x;
00151       }
00152       // Not found: try a higher level match in first dimension
00153       if (Forecast::Customer_Then_Item_Hierarchy)
00154       {
00155         // First customer hierarchy
00156         if (key.second) key.second = key.second->getOwner();
00157         else break;
00158       }
00159       else
00160       {
00161         // First item hierarchy
00162         if (key.first) key.first = key.first->getOwner();
00163         else break;
00164       }
00165     }
00166     while (true);
00167 
00168     // Not found at any level in the first dimension
00169 
00170     // Try a new level in the second dimension
00171     if (Forecast::Customer_Then_Item_Hierarchy)
00172     {
00173       // Second is item
00174       if (key.first) key.first = key.first->getOwner();
00175       else return NULL;
00176       // Reset to lowest level in the first dimension again
00177       key.second = &*(l->getCustomer());
00178     }
00179     else
00180     {
00181       // Second is customer
00182       if (key.second) key.second = key.second->getOwner();
00183       else return NULL;
00184       // Reset to lowest level in the first dimension again
00185       key.first = &*(l->getItem());
00186     }
00187   }
00188   while (true);
00189 }
00190 
00191 
00192 void ForecastSolver::netDemandFromForecast(const Demand* dmd, Forecast* fcst)
00193 {
00194   // Find the bucket with the due date
00195   ForecastBucket* zerobucket = NULL;
00196   for (Forecast::memberIterator i = fcst->beginMember(); i != fcst->end(); ++i)
00197   {
00198     zerobucket = dynamic_cast<ForecastBucket*>(&*i);
00199     if (zerobucket && zerobucket->getDueRange().within(dmd->getDue())) break;
00200   }
00201   if (!zerobucket)
00202     throw LogicException("Can't find forecast bucket for "
00203       + string(dmd->getDue()) + " in forecast '" + fcst->getName() + "'");
00204 
00205   // Netting - looking for time buckets with net forecast
00206   double remaining = dmd->getQuantity();
00207   ForecastBucket* curbucket = zerobucket;
00208   bool backward = true;
00209   while ( remaining > 0 && curbucket
00210     && (dmd->getDue()-Forecast::getNetEarly() < curbucket->getDueRange().getEnd())
00211     && (dmd->getDue()+Forecast::getNetLate() >= curbucket->getDueRange().getStart())
00212     )
00213   {
00214     // Net from the current bucket
00215     double available = curbucket->getQuantity();
00216     if (available > 0)
00217     {
00218       if (available >= remaining)
00219       {
00220         // Partially consume a bucket
00221         if (getLogLevel()>=2)
00222           logger << "    Consuming " << remaining << " from bucket "
00223             << curbucket->getDueRange() << " (" << available
00224             << " available)" << endl;
00225         curbucket->incConsumed(remaining);
00226         remaining = 0;
00227       }
00228       else
00229       {
00230         // Completely consume a bucket
00231         if (getLogLevel()>=2)
00232           logger << "    Consuming " << available << " from bucket "
00233             << curbucket->getDueRange() << " (" << available
00234             << " available)" << endl;
00235         remaining -= available;
00236         curbucket->incConsumed(available);
00237       }
00238     }
00239     else if (getLogLevel()>=2)
00240       logger << "    Nothing available in bucket "
00241         << curbucket->getDueRange() << endl;
00242 
00243     // Find the next forecast bucket
00244     if (backward)
00245     {
00246       // Moving to earlier buckets
00247       curbucket = curbucket->getPreviousBucket();
00248       if (!curbucket)
00249       {
00250         backward = false;
00251         curbucket = zerobucket->getNextBucket();
00252       }
00253     }
00254     else
00255       // Moving to later buckets
00256       curbucket = curbucket->getNextBucket();
00257   }
00258 
00259   // Quantity for which no bucket is found
00260   if (remaining > 0 && getLogLevel()>=2)
00261     logger << "    Remains " << remaining << " that can't be netted" << endl;
00262 
00263 }
00264 
00265 }       // end namespace

Documentation generated for frePPLe by  doxygen