forecast.h

Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/trunk/modules/forecast/forecast.h $
00003   version : $LastChangedRevision: 751 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2008-04-20 18:49:53 +0200 (Sun, 20 Apr 2008) $
00005  ***************************************************************************/
00006 
00007 /***************************************************************************
00008  *                                                                         *
00009  * Copyright (C) 2007 by Johan De Taeye                                    *
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA *
00024  *                                                                         *
00025  ***************************************************************************/
00026 
00027 /** @file forecast.h
00028   * @brief Header file for the module forecast.
00029   *
00030   * @namespace module_forecast
00031   * @brief Module for representing forecast.
00032   *
00033   * The forecast module provides the following functionality:
00034   *
00035   *  - A <b>new demand type</b> to model forecasts.<br>
00036   *    A forecast demand is bucketized. A demand is automatically
00037   *    created for each time bucket.<br>
00038   *    A calendar is used to define the time buckets to be used.
00039   *
00040   *  - Functionality for <b>distributing / profiling</b> forecast numbers
00041   *    into time buckets used for planning.<br>
00042   *    This functionality is typically used to translate between the time
00043   *    granularity of the sales department (which creates a sales forecast
00044   *    per e.g. calendar month) and the manufacturing department (which
00045   *    creates manufacturing and procurement plans in weekly or daily buckets
00046   *    ).<br>
00047   *    Another usage is to model a delivery date profile of the customers.
00048   *    Each bucket has a weight that is used to model situations where the
00049   *    demand is not evenly spread across buckets: e.g. when more orders are
00050   *    expected due on a monday than on a friday, or when a peak of orders is
00051   *    expected for delivery near the end of a month.
00052   *
00053   *  - A solver for <b>netting orders from the forecast</b>.<br>
00054   *    As customer orders are being received they need to be deducted from
00055   *    the forecast to avoid double-counting demand.<br>
00056   *    The netting solver will for each order search for a matching forecast
00057   *    and reduce the remaining net quantity of the forecast.
00058   *
00059   *  - Techniques to predict/forecast the future demand based on the demand
00060   *    history are NOT available in this module (yet).
00061   *
00062   * The XML schema extension enabled by this module is (see mod_forecast.xsd):
00063   * <PRE>
00064   * <!-- Define the forecast type -->
00065   * <xsd:complexType name="demand_forecast">
00066   *   <xsd:complexContent>
00067   *     <xsd:extension base="demand">
00068   *       <xsd:choice minOccurs="0" maxOccurs="unbounded">
00069   *         <xsd:element name="calendar" type="calendar" />
00070   *         <xsd:element name="discrete" type="xsd:boolean" />
00071   *         <xsd:element name="buckets">
00072   *           <xsd:complexType>
00073   *             <xsd:choice minOccurs="0" maxOccurs="unbounded">
00074   *               <xsd:element name="bucket">
00075   *                 <xsd:complexType>
00076   *                   <xsd:all>
00077   *                     <xsd:element name="total" type="positiveDouble"
00078   *                       minOccurs="0" />
00079   *                     <xsd:element name="net" type="positiveDouble"
00080   *                       minOccurs="0" />
00081   *                     <xsd:element name="consumed" type="positiveDouble"
00082   *                       minOccurs="0" />
00083   *                     <xsd:element name="start" type="xsd:dateTime"
00084   *                       minOccurs="0"/>
00085   *                     <xsd:element name="end" type="xsd:dateTime"
00086   *                       minOccurs="0"/>
00087   *                   </xsd:all>
00088   *                   <xsd:attribute name="total" type="positiveDouble" />
00089   *                   <xsd:attribute name="net" type="positiveDouble" />
00090   *                   <xsd:attribute name="consumed" type="positiveDouble" />
00091   *                   <xsd:attribute name="start" type="xsd:dateTime" />
00092   *                   <xsd:attribute name="end" type="xsd:dateTime" />
00093   *                 </xsd:complexType>
00094   *               </xsd:element>
00095   *             </xsd:choice>
00096   *           </xsd:complexType>
00097   *         </xsd:element>
00098   *       </xsd:choice>
00099   *       <xsd:attribute name="discrete" type="xsd:boolean" />
00100   *     </xsd:extension>
00101   *   </xsd:complexContent>
00102   * </xsd:complexType>
00103   *
00104   * <!-- Define the netting solver. -->
00105   * <xsd:complexType name="solver_forecast">
00106   * <xsd:complexContent>
00107   *   <xsd:extension base="solver">
00108   *     <xsd:choice minOccurs="0" maxOccurs="unbounded">
00109   *       <xsd:element name="loglevel" type="loglevel" />
00110   *     </xsd:choice>
00111   *   </xsd:extension>
00112   * </xsd:complexContent>
00113   * </xsd:complexType>
00114   * </PRE>
00115   *
00116   * The module support the following configuration parameters:
00117   *
00118   *   - <b>Customer_Then_Item_Hierarchy</b>:<br>
00119   *     As part of the forecast netting a demand is assiociated with a certain
00120   *     forecast. When no matching forecast is found for the customer and item
00121   *     of the demand, frePPLe looks for forecast at higher level customers
00122   *     and items.<br>
00123   *     This flag allows us to control whether we first search the customer
00124   *     hierarchy and then the item hierarchy, or the other way around.<br>
00125   *     The default value is true, ie search higher customer levels before
00126   *     searching higher levels of the item.
00127   *
00128   *   - <b>Match_Using_Delivery_Operation</b>:<br>
00129   *     Specifies whether or not a demand and a forecast require to have the
00130   *     same delivery operation to be a match.<br>
00131   *     The default value is true.
00132   *
00133   *   - <b>Net_Early</b>:<br>
00134   *     Defines how much time before the due date of an order we are allowed
00135   *     to search for a forecast bucket to net from.<br>
00136   *     The default value is 0, meaning that we can net only from the bucket
00137   *     where the demand is due.
00138   *
00139   *   - <b>Net_Late</b>:<br>
00140   *     Defines how much time after the due date of an order we are allowed
00141   *     to search for a forecast bucket to net from.<br>
00142   *     The default value is 0, meaning that we can net only from the bucket
00143   *     where the demand is due.
00144   */
00145 
00146 #ifndef FORECAST_H
00147 #define FORECAST_H
00148 
00149 #include "frepple.h"
00150 using namespace frepple;
00151 
00152 namespace module_forecast
00153 {
00154 
00155 
00156 /** Initialization routine for the library. */
00157 MODULE_EXPORT const char* initialize(const CommandLoadLibrary::ParameterList&);
00158 
00159 
00160 /** Initializes python extensions enabled by the module. */
00161 void initializePython();
00162 
00163 struct PythonForecastBucket;
00164 
00165 /** @brief This class represents a bucketized demand signal.
00166   *
00167   * The forecast object defines the item and priority of the demands.<br>
00168   * A calendar (of type void, double, integer or boolean) divides the time horizon
00169   * in individual time buckets. The calendar value is used to assign priorities
00170   * to the time buckets.<br>
00171   * The class basically works as an interface for a hierarchy of demands, where the
00172   * lower level demands represent forecasting time buckets.
00173   */
00174 class Forecast : public Demand
00175 {
00176     friend class ForecastSolver;
00177     friend struct PythonForecastBucket;
00178   private:
00179     /** @brief This class represents a forecast value in a time bucket.
00180       *
00181       * A forecast bucket is never manipulated or created directly. Instead,
00182       * the owning forecast manages the buckets.
00183       */
00184     class ForecastBucket : public Demand
00185     {
00186 
00187       public:
00188         ForecastBucket(Forecast* f, Date d, Date e, double w, ForecastBucket* p)
00189           : Demand(f->getName() + " - " + string(d)), weight(w), consumed(0.0),
00190             total(0.0), timebucket(d,e), prev(p), next(NULL)
00191         {
00192           if (p) p->next = this;
00193           setOwner(f);
00194           setHidden(true);  // Avoid the subdemands show up in the output
00195           setItem(&*(f->getItem()));
00196           setDue(d);
00197           setPriority(f->getPriority());
00198           setMaxLateness(f->getMaxLateness());
00199           setMinShipment(f->getMinShipment());
00200           setOperation(&*(f->getOperation()));
00201         }
00202         double weight;
00203         double consumed;
00204         double total;
00205         DateRange timebucket;
00206         ForecastBucket* prev;
00207         ForecastBucket* next;
00208         virtual size_t getSize() const 
00209         {
00210           return sizeof(ForecastBucket) + Demand::extrasize();
00211         }
00212     };
00213 
00214   public:
00215     /** Constructor. */
00216     explicit Forecast(const string& nm)
00217       : Demand(nm), calptr(NULL), discrete(true) {}
00218 
00219     /** Destructor. */
00220     ~Forecast();
00221 
00222     /** Updates the quantity of the forecast. This method is empty. */
00223     virtual void setQuantity(double f)
00224       {throw DataException("Can't set quantity of a forecast");}
00225 
00226     /** Update the forecast quantity.<br>
00227       * The forecast quantity will be distributed equally among the buckets
00228       * available between the two dates, taking into account also the bucket
00229       * weights.<br>
00230       * The logic applied is briefly summarized as follows:
00231       *  - If the daterange has its start and end dates equal, we find the
00232       *    matching forecast bucket and update the quantity.
00233       *  - Otherwise the quantity is distributed among all intersecting
00234       *    forecast buckets. This distribution is considering the weigth of
00235       *    the bucket and the time duration of the bucket.<br>
00236       *    The bucket weight is the value specified on the calendar.<br>
00237       *    If a forecast bucket only partially overlaps with the daterange
00238       *    only the overlapping time is used as the duration.
00239       *  - If only buckets with zero weigth are found in the daterange a
00240       *    dataexception is thrown. It indicates a situation where forecast
00241       *    is specified for a date where no values are allowed.
00242       */
00243     virtual void setTotalQuantity(const DateRange& , double);
00244 
00245     void writeElement(XMLOutput*, const Keyword&, mode=DEFAULT) const;
00246     void endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement);
00247     void beginElement(XMLInput& pIn, const Attribute& pAttr);
00248 
00249     /** Returns whether fractional forecasts are allowed or not.<br>
00250       * The default is true.
00251       */
00252     bool getDiscrete() const {return discrete;}
00253 
00254     /** Updates forecast discreteness flag. */
00255     void setDiscrete(const bool b);
00256 
00257     /** Update the item to be planned. */
00258     virtual void setItem(Item*);
00259 
00260     /** Update the customer. */
00261     virtual void setCustomer(Customer*);
00262 
00263     /* Update the maximum allowed lateness for planning. */
00264     void setMaxLateness(TimePeriod);
00265 
00266     /* Update the minumum allowed shipment quantity for planning. */
00267     void setMinShipment(double);
00268 
00269     /** Specify a bucket calendar for the forecast. Once forecasted
00270       * quantities have been entered for the forecast, the calendar
00271       * can't be updated any more. */
00272     virtual void setCalendar(Calendar* c);
00273 
00274     /** Returns a reference to the calendar used for this forecast. */
00275     Calendar* getCalendar() const {return calptr;}
00276 
00277     /** Updates the due date of the demand. Lower numbers indicate a
00278       * higher priority level. The method also updates the priority
00279       * in all buckets.
00280       */
00281     virtual void setPriority(int);
00282 
00283     /** Updates the operation being used to plan the demands. */
00284     virtual void setOperation(Operation *);
00285 
00286     /** Updates the due date of the demand. */
00287     virtual void setDue(const Date& d)
00288     {throw DataException("Can't set due date of a forecast");}
00289 
00290     virtual const MetaClass& getType() const {return metadata;}
00291     static const MetaClass metadata;
00292     virtual size_t getSize() const
00293     {
00294       return sizeof(Forecast) + Demand::extrasize() 
00295         + 6 * sizeof(void*); // Approx. size of an entry in forecast dictionary
00296     }
00297 
00298     /** Updates the value of the Customer_Then_Item_Hierarchy module
00299       * parameter. */
00300     static void setCustomerThenItemHierarchy(bool b)
00301       {Customer_Then_Item_Hierarchy = b;}
00302 
00303     /** Returns the value of the Customer_Then_Item_Hierarchy module
00304       * parameter. */
00305     bool getCustomerThenItemHierarchy()
00306       {return Customer_Then_Item_Hierarchy;}
00307 
00308     /** Updates the value of the Match_Using_Delivery_Operation module
00309       * parameter. */
00310     static void setMatchUsingDeliveryOperation(bool b)
00311       {Match_Using_Delivery_Operation = b;}
00312 
00313     /** Returns the value of the Match_Using_Delivery_Operation module
00314       * parameter. */
00315     static bool getMatchUsingDeliveryOperation()
00316       {return Match_Using_Delivery_Operation;}
00317 
00318     /** Updates the value of the Net_Early module parameter. */
00319     static void setNetEarly(TimePeriod t) {Net_Early = t;}
00320 
00321     /** Returns the value of the Net_Early module parameter. */
00322     static TimePeriod getNetEarly() {return Net_Early;}
00323 
00324     /** Updates the value of the Net_Late module parameter. */
00325     static void setNetLate(TimePeriod t) {Net_Late = t;}
00326 
00327     /** Returns the value of the Net_Late module parameter. */
00328     static TimePeriod getNetLate() {return Net_Late;}
00329 
00330     /** A data type to maintain a dictionary of all forecasts. */
00331     typedef multimap < pair<const Item*, const Customer*>, Forecast* > MapOfForecasts;
00332 
00333     /** Callback function, used for prevent a calendar from being deleted when it
00334       * is used for an uninitialized forecast. */
00335     static bool callback(Calendar*, const Signal);
00336 
00337     /** Return a reference to a dictionary with all forecast objects. */
00338     static const MapOfForecasts& getForecasts() {return ForecastDictionary;}
00339 
00340   private:
00341     /** Initializion of a forecast.<br>
00342       * It creates demands for each bucket of the calendar.
00343       */
00344     void initialize();
00345 
00346     /** A void calendar to define the time buckets. */
00347     Calendar* calptr;
00348 
00349     /** Flags whether fractional forecasts are allowed. */
00350     bool discrete;
00351 
00352     /** A dictionary of all forecasts. */
00353     static MapOfForecasts ForecastDictionary;
00354 
00355     /** Controls how we search the customer and item levels when looking for a
00356       * matching forecast for a demand.
00357       */
00358     static bool Customer_Then_Item_Hierarchy;
00359 
00360     /** Controls whether or not a matching delivery operation is required
00361       * between a matching order and its forecast.
00362       */
00363     static bool Match_Using_Delivery_Operation;
00364 
00365     /** Store the maximum time difference between an order due date and a
00366       * forecast bucket to net from.<br>
00367       * The default value is 0, meaning that only netting from the due
00368       * bucket is allowed.
00369       */
00370     static TimePeriod Net_Late;
00371 
00372     /** Store the maximum time difference between an order due date and a
00373       * forecast bucket to net from.<br>
00374       * The default value is 0, meaning that only netting from the due
00375       * bucket is allowed.
00376       */
00377     static TimePeriod Net_Early;
00378 };
00379 
00380 
00381 /** @brief Implementation of a forecast netting algorithm.
00382   *
00383   * As customer orders are being received they need to be deducted from
00384   * the forecast to avoid double-counting demand.
00385   *
00386   * The netting solver will process each order as follows:
00387   * - <b>First search for a matching forecast.</b><br>
00388   *   A matching forecast has the same item and customer as the order.<br>
00389   *   If no match is found at this level, a match is tried at higher levels
00390   *   of the customer and item.<br>
00391   *   Ultimately a match is tried with a empty customer or item field.
00392   * - <b>Next, the remaining net quantity of the forecast is decreased.</b><br>
00393   *   The forecast bucket to be reduced is the one where the order is due.<br>
00394   *   If the net quantity is already completely depleted in that bucket
00395   *   the solver will look in earlier and later buckets. The parameters
00396   *   Net_Early and Net_Late control the limits for the search in the
00397   *   time dimension.
00398   *
00399   * The logging levels have the following meaning:
00400   * - 0: Silent operation. Default logging level.
00401   * - 1: Log demands being netted and the matching forecast.
00402   * - 2: Same as 1, plus details on forecast buckets being netted.
00403   */
00404 class ForecastSolver : public Solver
00405 {
00406     friend class Forecast;
00407   public:
00408     /** Constructor. */
00409     ForecastSolver(const string& n) : Solver(n) {}
00410 
00411     /** This method handles the search for a matching forecast, followed
00412       * by decreasing the net forecast.
00413       */
00414     void solve(const Demand*, void* = NULL);
00415 
00416     /** This is the main solver method that will appropriately call the other
00417       * solve methods.<br>
00418       */
00419     void solve(void *v = NULL);
00420 
00421     virtual const MetaClass& getType() const {return metadata;}
00422     static const MetaClass metadata;
00423     virtual size_t getSize() const {return sizeof(ForecastSolver);}
00424     void writeElement(XMLOutput*, const Keyword&, mode) const;
00425 
00426     /** Callback function, used for netting orders against the forecast. */
00427     bool callback(Demand* l, const Signal a);
00428 
00429   private:
00430     /** Given a demand, this function will identify the forecast model it
00431       * links to.
00432       */
00433     Forecast* matchDemandToForecast(const Demand* l);
00434 
00435     /** Implements the netting of a customer order from a matching forecast
00436       * (and its delivery plan).
00437       */
00438     void netDemandFromForecast(const Demand*, Forecast*);
00439 
00440     /** Used for sorting demands during netting. */
00441     struct sorter
00442     {
00443       bool operator()(const Demand* x, const Demand* y) const
00444         { return SolverMRP::demand_comparison(x,y); }
00445     };
00446 
00447     /** Used for sorting demands during netting. */
00448     typedef multiset < Demand*, sorter > sortedDemandList;
00449 };
00450 
00451 
00452 #if defined(Py_PYTHON_H) || defined(DOXYGEN)
00453 
00454 extern "C"
00455 {
00456 
00457   /** @brief This struct exports forecast information to Python. */
00458   struct PythonForecast
00459   {
00460     private:
00461       PyObject_HEAD
00462       Forecast::MapOfForecasts::const_iterator iter;
00463     public:
00464       static PyTypeObject InfoType;
00465       static PyObject* next(PythonForecast*);
00466       static PyObject* create(PyTypeObject*, PyObject*, PyObject*);
00467       static void destroy(PythonForecast* obj) {PyObject_Del(obj);}
00468   };
00469 
00470 
00471   /** @brief This struct exports forecast bucket information to Python. */
00472   struct PythonForecastBucket
00473   {
00474     private:
00475       PyObject_HEAD
00476       Forecast::ForecastBucket *iter;
00477     public:
00478       static PyTypeObject InfoType;
00479       static PyObject* next(PythonForecastBucket*);
00480       static PyObject* create(PyTypeObject*, PyObject*, PyObject*) {return NULL;}
00481       static void destroy(PythonForecastBucket* obj) {PyObject_Del(obj);}
00482       static PyObject* createFromForecast(Forecast*);
00483   };
00484 
00485 }
00486 
00487 #endif
00488 
00489 }   // End namespace
00490 
00491 #endif
00492 
00493 

Documentation generated by  doxygen