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