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
