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: 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 00028 #include "forecast.h" 00029 00030 namespace module_forecast 00031 { 00032 00033 00034 bool ForecastSolver::callback(Demand* l, const Signal a) 00035 { 00036 // Call the netting function 00037 solve(l, NULL); 00038 00039 // Always return 'okay' 00040 return true; 00041 } 00042 00043 00044 void ForecastSolver::writeElement(XMLOutput *o, const Keyword& tag, mode m) const 00045 { 00046 // Writing a reference 00047 if (m == REFERENCE) 00048 { 00049 o->writeElement 00050 (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type); 00051 return; 00052 } 00053 00054 // Write the complete object 00055 if (m != NOHEADER) o->BeginObject 00056 (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type); 00057 00058 // Write the parent class 00059 Solver::writeElement(o, tag, NOHEADER); 00060 } 00061 00062 00063 void ForecastSolver::solve(const Demand* l, void* v) 00064 { 00065 // Forecast don't net themselves, and hidden demands either... 00066 if (!l || dynamic_cast<const Forecast*>(l) || l->getHidden()) return; 00067 00068 const Demand* x(l); 00069 00070 // Message 00071 if (getLogLevel()>0) 00072 logger << " Netting of demand '" << l << "' ('" << l->getCustomer() 00073 << "','" << l->getItem() << "', '" << l->getDeliveryOperation() 00074 << "'): " << l->getDue() << ", " << l->getQuantity() << endl; 00075 00076 // Find a matching forecast 00077 Forecast *fcst = matchDemandToForecast(l); 00078 00079 if (!fcst) 00080 { 00081 // Message 00082 if (getLogLevel()>0) 00083 logger << " No matching forecast available" << endl; 00084 return; 00085 } 00086 else if (getLogLevel()>0) 00087 logger << " Matching forecast: " << fcst << endl; 00088 00089 // Netting the order from the forecast 00090 netDemandFromForecast(l,fcst); 00091 } 00092 00093 00094 void ForecastSolver::solve(void *v) 00095 { 00096 // Sort the demands using the same sort function as used for planning. 00097 // Note: the memory consumption of the sorted list can be significant 00098 sortedDemandList l; 00099 for (Demand::iterator i = Demand::begin(); i != Demand::end(); ++i) 00100 // Only sort non-forecast demand. 00101 if (!dynamic_cast<Forecast*>(&*i) 00102 && !dynamic_cast<Forecast::ForecastBucket*>(&*i)) 00103 l.insert(&*i); 00104 00105 // Netting loop 00106 for(sortedDemandList::iterator i = l.begin(); i != l.end(); ++i) 00107 try {solve(*i, NULL);} 00108 catch (...) 00109 { 00110 // Error message 00111 logger << "Error: Caught an exception while netting demand '" 00112 << (*i)->getName() << "':" << endl; 00113 try { throw; } 00114 catch (bad_exception&) {logger << " bad exception" << endl;} 00115 catch (exception& e) {logger << " " << e.what() << endl;} 00116 catch (...) {logger << " Unknown type" << endl;} 00117 } 00118 } 00119 00120 00121 Forecast* ForecastSolver::matchDemandToForecast(const Demand* l) 00122 { 00123 pair<const Item*, const Customer*> key 00124 = make_pair(&*(l->getItem()), &*(l->getCustomer())); 00125 00126 do // Loop through second dimension 00127 { 00128 do // Loop through first dimension 00129 { 00130 Forecast::MapOfForecasts::iterator x = Forecast::ForecastDictionary.lower_bound(key); 00131 00132 // Loop through all matching keys 00133 while (x != Forecast::ForecastDictionary.end() && x->first == key) 00134 { 00135 if (!Forecast::getMatchUsingDeliveryOperation() 00136 || x->second->getDeliveryOperation() == l->getDeliveryOperation()) 00137 // Bingo! Found a matching key, if required plus matching delivery operation 00138 return x->second; 00139 else 00140 ++ x; 00141 } 00142 // Not found: try a higher level match in first dimension 00143 if (Forecast::Customer_Then_Item_Hierarchy) 00144 { 00145 // First customer hierarchy 00146 if (key.second) key.second = key.second->getOwner(); 00147 else break; 00148 } 00149 else 00150 { 00151 // First item hierarchy 00152 if (key.first) key.first = key.first->getOwner(); 00153 else break; 00154 } 00155 } 00156 while (true); 00157 00158 // Not found at any level in the first dimension 00159 00160 // Try a new level in the second dimension 00161 if (Forecast::Customer_Then_Item_Hierarchy) 00162 { 00163 // Second is item 00164 if (key.first) key.first = key.first->getOwner(); 00165 else return NULL; 00166 // Reset to lowest level in the first dimension again 00167 key.second = &*(l->getCustomer()); 00168 } 00169 else 00170 { 00171 // Second is customer 00172 if (key.second) key.second = key.second->getOwner(); 00173 else return NULL; 00174 // Reset to lowest level in the first dimension again 00175 key.first = &*(l->getItem()); 00176 } 00177 } 00178 while (true); 00179 } 00180 00181 00182 void ForecastSolver::netDemandFromForecast(const Demand* dmd, Forecast* fcst) 00183 { 00184 // Find the bucket with the due date 00185 Forecast::ForecastBucket* zerobucket = NULL; 00186 for (Forecast::memberIterator i = fcst->beginMember(); i != fcst->endMember(); ++i) 00187 { 00188 zerobucket = dynamic_cast<Forecast::ForecastBucket*>(&*i); 00189 if (zerobucket && zerobucket->timebucket.within(dmd->getDue())) break; 00190 } 00191 if (!zerobucket) 00192 throw LogicException("Can't find forecast bucket for " 00193 + string(dmd->getDue()) + " in forecast '" + fcst->getName() + "'"); 00194 00195 // Netting - looking for time buckets with net forecast 00196 double remaining = dmd->getQuantity(); 00197 Forecast::ForecastBucket* curbucket = zerobucket; 00198 bool backward = true; 00199 while ( remaining > 0 && curbucket 00200 && (dmd->getDue()-Forecast::getNetEarly() < curbucket->timebucket.getEnd()) 00201 && (dmd->getDue()+Forecast::getNetLate() >= curbucket->timebucket.getStart()) 00202 ) 00203 { 00204 // Net from the current bucket 00205 double available = curbucket->getQuantity(); 00206 if (available > 0) 00207 { 00208 if (available >= remaining) 00209 { 00210 // Partially consume a bucket 00211 if (getLogLevel()>=2) 00212 logger << " Consuming " << remaining << " from bucket " 00213 << curbucket->timebucket << " (" << available 00214 << " available)" << endl; 00215 curbucket->setQuantity(available - remaining); 00216 curbucket->consumed += remaining; 00217 remaining = 0; 00218 } 00219 else 00220 { 00221 // Completely consume a bucket 00222 if (getLogLevel()>=2) 00223 logger << " Consuming " << available << " from bucket " 00224 << curbucket->timebucket << " (" << available 00225 << " available)" << endl; 00226 remaining -= available; 00227 curbucket->consumed += available; 00228 curbucket->setQuantity(0); 00229 } 00230 } 00231 else if (getLogLevel()>=2) 00232 logger << " Nothing available in bucket " 00233 << curbucket->timebucket << endl; 00234 00235 // Find the next forecast bucket 00236 if (backward) 00237 { 00238 // Moving to earlier buckets 00239 curbucket = curbucket->prev; 00240 if (!curbucket) 00241 { 00242 backward = false; 00243 curbucket = zerobucket->next; 00244 } 00245 } 00246 else 00247 // Moving to later buckets 00248 curbucket = curbucket->next; 00249 } 00250 00251 // Quantity for which no bucket is found 00252 if (remaining > 0 && getLogLevel()>=2) 00253 logger << " Remains " << remaining << " that can't be netted" << endl; 00254 00255 } 00256 00257 } // end namespace
Documentation generated by
