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: 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 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->endMember(); ++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
