forecast.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/tags/0.9.1/modules/forecast/forecast.cpp $
00003   version : $LastChangedRevision: 1656 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2012-03-27 19:05:34 +0200 (Tue, 27 Mar 2012) $
00005  ***************************************************************************/
00006 
00007 /***************************************************************************
00008  *                                                                         *
00009  * Copyright (C) 2007-2012 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 Keyword Forecast::tag_total("total");
00034 const Keyword Forecast::tag_net("net");
00035 const Keyword Forecast::tag_consumed("consumed");
00036 const MetaClass *Forecast::metadata;
00037 const MetaClass *ForecastBucket::metadata;
00038 bool ForecastBucket::DueAtEndOfBucket = false;
00039 
00040 
00041 int Forecast::initialize()
00042 {
00043   // Initialize the metadata
00044   metadata = new MetaClass("demand", "demand_forecast",
00045       Object::createString<Forecast>);
00046 
00047   // Get notified when a calendar is deleted
00048   FunctorStatic<Calendar,Forecast>::connect(SIG_REMOVE);
00049 
00050   // Initialize the Python class
00051   FreppleClass<Forecast,Demand>::getType().addMethod("setQuantity", Forecast::setPythonTotalQuantity, METH_VARARGS,
00052       "Update the total quantity in one or more buckets");
00053   FreppleClass<Forecast,Demand>::getType().addMethod("timeseries", Forecast::timeseries, METH_VARARGS,
00054       "Set the future based on the timeseries of historical data");
00055   return FreppleClass<Forecast,Demand>::initialize();
00056 }
00057 
00058 
00059 int ForecastBucket::initialize()
00060 {
00061   // Initialize the metadata
00062   // No factory method for this class
00063   metadata = new MetaClass("demand", "demand_forecastbucket");
00064 
00065   // Initialize the Python class
00066   // No support for creation
00067   PythonType& x = FreppleClass<ForecastBucket,Demand>::getType();
00068   x.setName("demand_forecastbucket");
00069   x.setDoc("frePPLe forecastbucket");
00070   x.supportgetattro();
00071   x.supportsetattro();
00072   x.supportstr();
00073   x.supportcompare();
00074   x.setBase(Demand::metadata->pythonClass);
00075   x.addMethod("toXML", toXML, METH_VARARGS, "return a XML representation");
00076   const_cast<MetaClass*>(metadata)->pythonClass = x.type_object();
00077   return x.typeReady();
00078 }
00079 
00080 
00081 bool Forecast::callback(Calendar* l, const Signal a)
00082 {
00083   // This function is called when a calendar is about to be deleted.
00084   // If that calendar is being used for a forecast we reset the calendar
00085   // pointer to null.
00086   for (MapOfForecasts::iterator x = ForecastDictionary.begin();
00087       x != ForecastDictionary.end(); ++x)
00088     if (x->second->calptr == l)
00089       // Calendar in use for this forecast
00090       x->second->calptr = NULL;
00091   return true;
00092 }
00093 
00094 
00095 Forecast::~Forecast()
00096 {
00097   // Update the dictionary
00098   for (MapOfForecasts::iterator x=
00099       ForecastDictionary.lower_bound(make_pair(&*getItem(),&*getCustomer()));
00100       x != ForecastDictionary.end(); ++x)
00101     if (x->second == this)
00102     {
00103       ForecastDictionary.erase(x);
00104       break;
00105     }
00106 
00107   // Delete all children demands
00108   for(memberIterator i = beginMember(); i != end(); )
00109   {
00110     Demand *tmp = &*i;
00111     ++i;
00112     delete tmp;
00113   }
00114 }
00115 
00116 
00117 void Forecast::instantiate()
00118 {
00119   if (!calptr) throw DataException("Missing forecast calendar");
00120 
00121   // Create a demand for every bucket. The weight value depends on the
00122   // calendar type: double, integer, bool or other
00123   const CalendarDouble* c = dynamic_cast<const CalendarDouble*>(calptr);
00124   ForecastBucket* prev = NULL;
00125   Date prevDate;
00126   double prevValue(0.0);
00127   if (c)
00128     // Double calendar
00129     for (CalendarDouble::EventIterator i(c); i.getDate()<=Date::infiniteFuture; ++i)
00130     {
00131       if ((prevDate || i.getDate() == Date::infiniteFuture) && prevValue > 0.0)
00132       {
00133         prev = new ForecastBucket
00134         (this, prevDate, i.getDate(), prevValue, prev);
00135         Demand::add(prev);
00136       }
00137       if (i.getDate() == Date::infiniteFuture) break;
00138       prevDate = i.getDate();
00139       prevValue = i.getValue();
00140     }
00141   else
00142   {
00143     const CalendarInt* c = dynamic_cast<const CalendarInt*>(calptr);
00144     if (c)
00145       // Integer calendar
00146       for (CalendarInt::EventIterator i(c); i.getDate()<=Date::infiniteFuture; ++i)
00147       {
00148         if ((prevDate || i.getDate() == Date::infiniteFuture) && prevValue > 0)
00149         {
00150           prev = new ForecastBucket
00151           (this, prevDate, i.getDate(), prevValue, prev);
00152           Demand::add(prev);
00153         }
00154         if (i.getDate() == Date::infiniteFuture) break;
00155         prevDate = i.getDate();
00156         prevValue = static_cast<double>(i.getValue());
00157       }
00158     else
00159     {
00160       const CalendarBool* c = dynamic_cast<const CalendarBool*>(calptr);
00161       bool prevValueBool = false;
00162       if (c)
00163         // Boolean calendar
00164         for (CalendarBool::EventIterator i(c); true; ++i)
00165         {
00166           if ((prevDate || i.getDate() == Date::infiniteFuture) && prevValueBool)
00167           {
00168             prev = new ForecastBucket
00169             (this, prevDate, i.getDate(), 1.0, prev);
00170             Demand::add(prev);
00171           }
00172           if (i.getDate() == Date::infiniteFuture) break;
00173           prevDate = i.getDate();
00174           prevValueBool = i.getValue();
00175         }
00176       else
00177       {
00178         // Other calendar
00179         for (Calendar::EventIterator i(calptr); true; ++i)
00180         {
00181           if (prevDate || i.getDate() == Date::infiniteFuture)
00182           {
00183             prev = new ForecastBucket(this, prevDate, i.getDate(), 1.0, prev);
00184             Demand::add(prev);
00185             if (i.getDate() == Date::infiniteFuture) break;
00186           }
00187           prevDate = i.getDate();
00188         }
00189       }
00190     }
00191   }
00192 }
00193 
00194 
00195 void Forecast::setDiscrete(const bool b)
00196 {
00197   // Update the flag
00198   discrete = b;
00199 
00200   // Round down any forecast demands that may already exist.
00201   if (discrete)
00202     for (memberIterator m = beginMember(); m!=end(); ++m)
00203       m->setQuantity(floor(m->getQuantity()));
00204 }
00205 
00206 
00207 void Forecast::setTotalQuantity(const DateRange& d, double f)
00208 {
00209   // Initialize, if not done yet
00210   if (!isGroup()) instantiate();
00211 
00212   // Find all forecast demands, and sum their weights
00213   double weights = 0.0;
00214   for (memberIterator m = beginMember(); m!=end(); ++m)
00215   {
00216     ForecastBucket* x = dynamic_cast<ForecastBucket*>(&*m);
00217     if (!x)
00218       throw DataException("Invalid subdemand of forecast '" + getName() +"'");
00219     if (d.intersect(x->getDueRange()))
00220     {
00221       // Bucket intersects with daterange
00222       if (!d.getDuration())
00223       {
00224         // Single date provided. Update that one bucket.
00225         x->setTotal(f);
00226         return;
00227       }
00228       weights += x->getWeight() * static_cast<long>(x->getDueRange().overlap(d));
00229     }
00230   }
00231 
00232   // Expect to find at least one non-zero weight...
00233   if (!weights)
00234     throw DataException("No valid forecast date in range "
00235         + string(d) + " of forecast '" + getName() +"'");
00236 
00237   // Update the forecast quantity, respecting the weights
00238   f /= weights;
00239   double carryover = 0.0;
00240   for (memberIterator m = beginMember(); m!=end(); ++m)
00241   {
00242     ForecastBucket* x = dynamic_cast<ForecastBucket*>(&*m);
00243     if (d.intersect(x->getDueRange()))
00244     {
00245       // Bucket intersects with daterange
00246       TimePeriod o = x->getDueRange().overlap(d);
00247       double percent = x->getWeight() * static_cast<long>(o);
00248       if (getDiscrete())
00249       {
00250         // Rounding to discrete numbers
00251         carryover += f * percent;
00252         int intdelta = static_cast<int>(ceil(carryover - 0.5));
00253         carryover -= intdelta;
00254         if (o < x->getDueRange().getDuration())
00255           // The bucket is only partially updated
00256           x->incTotal(static_cast<double>(intdelta));
00257         else
00258           // The bucket is completely updated
00259           x->setTotal(static_cast<double>(intdelta));
00260       }
00261       else
00262       {
00263         // No rounding
00264         if (o < x->getDueRange().getDuration())
00265           // The bucket is only partially updated
00266           x->incTotal(f * percent);
00267         else
00268           // The bucket is completely updated
00269           x->setTotal(f * percent);
00270       }
00271     }
00272   }
00273 }
00274 
00275 
00276 void Forecast::setTotalQuantity(const Date d, double f)
00277 {
00278   // Initialize, if not done yet
00279   if (!isGroup()) instantiate();
00280 
00281   // Find the bucket
00282   for (memberIterator m = beginMember(); m!=end(); ++m)
00283   {
00284     ForecastBucket* x = dynamic_cast<ForecastBucket*>(&*m);
00285     if (!x)
00286       throw DataException("Invalid subdemand of forecast '" + getName() +"'");
00287     if (x->getDueRange().within(d))
00288     {
00289       // Update the bucket
00290       x->setTotal(f);
00291       return;
00292     }
00293   }
00294 }
00295 
00296 
00297 void Forecast::writeElement(XMLOutput *o, const Keyword &tag, mode m) const
00298 {
00299   // Writing a reference
00300   if (m == REFERENCE)
00301   {
00302     o->writeElement
00303     (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00304     return;
00305   }
00306 
00307   // Write the complete object
00308   if (m != NOHEADER) o->BeginObject
00309     (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00310 
00311   o->writeElement(Tags::tag_item, &*getItem());
00312   o->writeElement(Tags::tag_operation, &*getOperation());
00313   if (getPriority()) o->writeElement(Tags::tag_priority, getPriority());
00314   o->writeElement(Tags::tag_calendar, calptr);
00315   if (!getDiscrete()) o->writeElement(Tags::tag_discrete, getDiscrete());
00316 
00317   // Write all entries
00318   o->BeginObject (Tags::tag_buckets);
00319   for (memberIterator i = beginMember(); i != end(); ++i)
00320   {
00321     ForecastBucket* f = dynamic_cast<ForecastBucket*>(&*i);
00322     o->BeginObject(Tags::tag_bucket, Tags::tag_start, string(f->getDue()));
00323     o->writeElement(tag_total, f->getTotal());
00324     o->writeElement(Tags::tag_quantity, f->getQuantity());
00325     o->writeElement(tag_consumed, f->getConsumed());
00326     o->EndObject(Tags::tag_bucket);
00327   }
00328   o->EndObject(Tags::tag_buckets);
00329 
00330   o->EndObject(tag);
00331 }
00332 
00333 
00334 void Forecast::endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
00335 {
00336   // While reading forecast buckets, we use the userarea field on the input
00337   // to cache the data. The temporary object is deleted when the bucket
00338   // tag is closed.
00339   if (pAttr.isA(Tags::tag_calendar))
00340   {
00341     Calendar *b = dynamic_cast<Calendar*>(pIn.getPreviousObject());
00342     if (b) setCalendar(b);
00343     else throw LogicException("Incorrect object type during read operation");
00344   }
00345   else if (pAttr.isA(Tags::tag_discrete))
00346     setDiscrete(pElement.getBool());
00347   else if (pAttr.isA(Tags::tag_bucket))
00348   {
00349     pair<DateRange,double> *d =
00350       static_cast< pair<DateRange,double>* >(pIn.getUserArea());
00351     if (d)
00352     {
00353       // Update the forecast quantities
00354       setTotalQuantity(d->first, d->second);
00355       // Clear the read buffer
00356       d->first.setStart(Date());
00357       d->first.setEnd(Date());
00358       d->second = 0;
00359     }
00360   }
00361   else if (pIn.getParentElement().first.isA(Tags::tag_bucket))
00362   {
00363     pair<DateRange,double> *d =
00364       static_cast< pair<DateRange,double>* >(pIn.getUserArea());
00365     if (pAttr.isA(tag_total))
00366     {
00367       if (d) d->second = pElement.getDouble();
00368       else pIn.setUserArea(
00369           new pair<DateRange,double>(DateRange(),pElement.getDouble())
00370         );
00371     }
00372     else if (pAttr.isA(Tags::tag_start))
00373     {
00374       Date x = pElement.getDate();
00375       if (d)
00376       {
00377         if (!d->first.getStart()) d->first.setStartAndEnd(x,x);
00378         else d->first.setStart(x);
00379       }
00380       else pIn.setUserArea(new pair<DateRange,double>(DateRange(x,x),0));
00381     }
00382     else if (pAttr.isA(Tags::tag_end))
00383     {
00384       Date x = pElement.getDate();
00385       if (d)
00386       {
00387         if (!d->first.getStart()) d->first.setStartAndEnd(x,x);
00388         else d->first.setEnd(x);
00389       }
00390       else pIn.setUserArea(new pair<DateRange,double>(DateRange(x,x),0));
00391     }
00392   }
00393   else
00394     Demand::endElement(pIn, pAttr, pElement);
00395 
00396   if (pIn.isObjectEnd())
00397   {
00398     // Delete dynamically allocated temporary read object
00399     if (pIn.getUserArea())
00400       delete static_cast< pair<DateRange,double>* >(pIn.getUserArea());
00401   }
00402 }
00403 
00404 
00405 void Forecast::beginElement(XMLInput& pIn, const Attribute& pAttr)
00406 {
00407   if (pAttr.isA(Tags::tag_calendar))
00408     pIn.readto( Calendar::reader(Calendar::metadata, pIn.getAttributes()) );
00409   else
00410     Demand::beginElement(pIn, pAttr);
00411 }
00412 
00413 
00414 void Forecast::setCalendar(Calendar* c)
00415 {
00416   if (isGroup())
00417     throw DataException(
00418       "Changing the calendar of an initialized forecast isn't allowed");
00419   calptr = c;
00420 }
00421 
00422 
00423 void Forecast::setItem(Item* i)
00424 {
00425   // No change
00426   if (getItem() == i) return;
00427 
00428   // Update the dictionary
00429   for (MapOfForecasts::iterator x =
00430       ForecastDictionary.lower_bound(make_pair(
00431           &*getItem(),&*getCustomer()
00432           ));
00433       x != ForecastDictionary.end(); ++x)
00434     if (x->second == this)
00435     {
00436       ForecastDictionary.erase(x);
00437       break;
00438     }
00439   ForecastDictionary.insert(make_pair(make_pair(i,&*getCustomer()),this));
00440 
00441   // Update data field
00442   Demand::setItem(i);
00443 
00444   // Update the item for all buckets/subdemands
00445   for (memberIterator m = beginMember(); m!=end(); ++m)
00446     m->setItem(i);
00447 }
00448 
00449 
00450 void Forecast::setCustomer(Customer* i)
00451 {
00452   // No change
00453   if (getCustomer() == i) return;
00454 
00455   // Update the dictionary
00456   for (MapOfForecasts::iterator x =
00457       ForecastDictionary.lower_bound(make_pair(
00458           getItem(), getCustomer()
00459           ));
00460       x != ForecastDictionary.end(); ++x)
00461     if (x->second == this)
00462     {
00463       ForecastDictionary.erase(x);
00464       break;
00465     }
00466   ForecastDictionary.insert(make_pair(make_pair(&*getItem(),i),this));
00467 
00468   // Update data field
00469   Demand::setCustomer(i);
00470 
00471   // Update the customer for all buckets/subdemands
00472   for (memberIterator m = beginMember(); m!=end(); ++m)
00473     m->setCustomer(i);
00474 }
00475 
00476 
00477 void Forecast::setMaxLateness(TimePeriod i)
00478 {
00479   Demand::setMaxLateness(i);
00480   // Update the maximum lateness for all buckets/subdemands
00481   for (memberIterator m = beginMember(); m!=end(); ++m)
00482     m->setMaxLateness(i);
00483 }
00484 
00485 
00486 void Forecast::setMinShipment(double i)
00487 {
00488   Demand::setMinShipment(i);
00489   // Update the minimum shipment for all buckets/subdemands
00490   for (memberIterator m = beginMember(); m!=end(); ++m)
00491     m->setMinShipment(i);
00492 }
00493 
00494 
00495 void Forecast::setPriority(int i)
00496 {
00497   Demand::setPriority(i);
00498   // Update the priority for all buckets/subdemands
00499   for (memberIterator m = beginMember(); m!=end(); ++m)
00500     m->setPriority(i);
00501 }
00502 
00503 
00504 void Forecast::setOperation(Operation *o)
00505 {
00506   Demand::setOperation(o);
00507   // Update the priority for all buckets/subdemands
00508   for (memberIterator m = beginMember(); m!=end(); ++m)
00509     m->setOperation(o);
00510 }
00511 
00512 }       // end namespace

Documentation generated for frePPLe by  doxygen