Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #define FREPPLE_CORE
00029 #include "frepple/solver.h"
00030
00031 namespace frepple
00032 {
00033
00034
00035
00036 DECLARE_EXPORT void SolverMRP::solve(const Resource* res, void* v)
00037 {
00038 SolverMRPdata* data = static_cast<SolverMRPdata*>(v);
00039
00040
00041 if (userexit_resource) userexit_resource.call(res, PythonObject(data->constrainedPlanning));
00042
00043
00044 if (data->getSolver()->getLogLevel()>1)
00045 {
00046 if (!data->constrainedPlanning || !data->getSolver()->isConstrained())
00047 logger << indent(res->getLevel()) << " Resource '" << res->getName()
00048 << "' is asked in unconstrained mode: "<< (-data->state->q_qty) << " "
00049 << data->state->q_operationplan->getDates() << endl;
00050 else
00051 logger << indent(res->getLevel()) << " Resource '" << res->getName()
00052 << "' is asked: "<< (-data->state->q_qty) << " "
00053 << data->state->q_operationplan->getDates() << endl;
00054 }
00055
00056
00057 if (!data->constrainedPlanning)
00058 {
00059
00060 data->state->a_qty = data->state->q_qty;
00061 data->state->a_date = data->state->q_date;
00062 data->state->a_cost += data->state->a_qty * res->getCost()
00063 * (data->state->q_operationplan->getDates().getDuration() - data->state->q_operationplan->getUnavailable())
00064 / 3600.0;
00065
00066
00067 if (data->getSolver()->getLogLevel()>1 && data->state->q_qty < 0)
00068 logger << indent(res->getLevel()) << " Resource '" << res << "' answers: "
00069 << (-data->state->a_qty) << " " << data->state->a_date << endl;
00070 }
00071
00072
00073 OperationPlan *setupOpplan = NULL;
00074 DateRange currentSetupOpplanDates;
00075 LoadPlan *setupLdplan = NULL;
00076 if (res->getSetupMatrix() && !data->state->q_loadplan->getLoad()->getSetup().empty())
00077 for (OperationPlan::iterator i(data->state->q_operationplan); i != OperationPlan::end(); ++i)
00078 if (i->getOperation() == OperationSetup::setupoperation)
00079 {
00080 setupOpplan = &*i;
00081 currentSetupOpplanDates = i->getDates();
00082 for (OperationPlan::LoadPlanIterator j = setupOpplan->beginLoadPlans();
00083 j != setupOpplan->endLoadPlans(); ++j)
00084 if (j->getLoad()->getResource() == res && !j->isStart())
00085 {
00086 setupLdplan = &*j;
00087 break;
00088 }
00089 if (!setupLdplan)
00090 throw LogicException("Can't find loadplan on setup operationplan");
00091 break;
00092 }
00093
00094
00095 double orig_q_qty = -data->state->q_qty;
00096 OperationPlanState currentOpplan(data->state->q_operationplan);
00097 Resource::loadplanlist::const_iterator cur = res->getLoadPlans().end();
00098 Date curdate;
00099 double curMax, prevMax;
00100 bool HasOverload;
00101 bool HasSetupOverload;
00102 bool noRestore = data->state->forceLate;
00103
00104
00105 data->state->a_date = data->state->q_date;
00106 data->state->a_qty = orig_q_qty;
00107 Date prevdate;
00108
00109
00110 if (!data->state->forceLate)
00111 do
00112 {
00113
00114 prevdate = data->state->q_operationplan->getDates().getEnd();
00115 noRestore = data->state->forceLate;
00116
00117 if (isLeadtimeConstrained() || isFenceConstrained())
00118
00119 if (data->constrainedPlanning && !checkOperationLeadtime(data->state->q_operationplan,*data,false))
00120 {
00121
00122 noRestore = true;
00123 break;
00124 }
00125
00126
00127 HasOverload = false;
00128 HasSetupOverload = false;
00129 Date earliestdate = data->state->q_operationplan->getDates().getStart();
00130 curdate = data->state->q_loadplan->getDate();
00131 curMax = data->state->q_loadplan->getMax(false);
00132 prevMax = curMax;
00133 for (cur = res->getLoadPlans().begin(data->state->q_loadplan);
00134 cur!=res->getLoadPlans().end() && cur->getDate()>=earliestdate;
00135 --cur)
00136 {
00137
00138 prevMax = curMax;
00139 if (cur->getType() == 4)
00140 curMax = cur->getMax(false);
00141
00142 const LoadPlan* ldplan = dynamic_cast<const LoadPlan*>(&*cur);
00143 if (ldplan && ldplan->getOperationPlan()->getOperation() == OperationSetup::setupoperation
00144 && ldplan->getOperationPlan()->getDates().overlap(data->state->q_operationplan->getDates()) > 0L
00145 && ldplan->getOperationPlan() != setupOpplan)
00146 {
00147
00148 HasOverload = true;
00149 HasSetupOverload = true;
00150 break;
00151 }
00152
00153
00154 if (cur->getDate() == curdate) continue;
00155
00156 if (cur->getOnhand() > prevMax + ROUNDING_ERROR)
00157 {
00158
00159
00160
00161
00162
00163 HasOverload = true;
00164 break;
00165 }
00166 curdate = cur->getDate();
00167 }
00168
00169
00170
00171 if (setupOpplan && !HasOverload)
00172 {
00173 earliestdate = setupOpplan->getDates().getStart();
00174 for (cur = res->getLoadPlans().begin(setupLdplan);
00175 cur!=res->getLoadPlans().end() && cur->getDate()>=earliestdate;
00176 --cur)
00177 {
00178
00179 prevMax = curMax;
00180 if (cur->getType() == 4)
00181 curMax = cur->getMax(false);
00182
00183
00184 const LoadPlan* ldplan = dynamic_cast<const LoadPlan*>(&*cur);
00185 if (ldplan
00186 && ldplan->getOperationPlan()->getDates().overlap(setupOpplan->getDates()) > 0L
00187 && ldplan->getSetup() != setupLdplan->getSetup())
00188 {
00189 HasOverload = true;
00190 HasSetupOverload = true;
00191 break;
00192 }
00193
00194
00195 if (cur->getDate() == curdate) continue;
00196 if (cur->getOnhand() > prevMax + ROUNDING_ERROR)
00197 {
00198
00199
00200
00201
00202
00203 HasOverload = true;
00204 HasSetupOverload = true;
00205 break;
00206 }
00207 curdate = cur->getDate();
00208 }
00209 }
00210
00211
00212
00213
00214
00215 if (HasOverload && !HasSetupOverload
00216 && curdate < data->state->q_loadplan->getDate())
00217 {
00218 Date currentEnd = data->state->q_operationplan->getDates().getEnd();
00219 data->state->q_operationplan->getOperation()->setOperationPlanParameters(
00220 data->state->q_operationplan,
00221 currentOpplan.quantity,
00222 curdate,
00223 currentEnd
00224 );
00225 if (data->state->q_operationplan->getQuantity() > 0
00226 && data->state->q_operationplan->getDates().getEnd() <= currentEnd
00227 && data->state->q_operationplan->getDates().getStart() >= curdate)
00228 {
00229
00230
00231
00232
00233 HasOverload = false;
00234 }
00235 else
00236 {
00237
00238
00239
00240
00241
00242 data->state->q_operationplan->getOperation()->setOperationPlanParameters(
00243 data->state->q_operationplan,
00244 currentOpplan.quantity,
00245 Date::infinitePast,
00246 currentEnd
00247 );
00248 }
00249 }
00250
00251
00252 if (HasOverload)
00253 {
00254
00255 curMax = cur->getMax(false);
00256 prevMax = curMax;
00257 curdate = cur->getDate();
00258 for (; cur!=res->getLoadPlans().end() && curdate > currentOpplan.end - res->getMaxEarly(); --cur)
00259 {
00260
00261 prevMax = curMax;
00262 if (cur->getType() == 4) curMax = cur->getMax(false);
00263
00264
00265 const LoadPlan* ldplan = dynamic_cast<const LoadPlan*>(&*cur);
00266 if (ldplan
00267 && ldplan->getOperationPlan()->getOperation() == OperationSetup::setupoperation
00268 && ldplan->isStart()
00269 && ldplan->getOperationPlan()->getDates().getDuration() > 0L
00270 && ldplan->getOperationPlan() != setupOpplan)
00271 continue;
00272
00273
00274 if (cur->getDate() == curdate) continue;
00275
00276
00277 if (cur->getOnhand() < prevMax + ROUNDING_ERROR && curdate < prevdate)
00278 break;
00279 curdate = cur->getDate();
00280 }
00281 assert (curdate != prevdate);
00282
00283
00284
00285
00286
00287 if (cur != res->getLoadPlans().end() && curdate > currentOpplan.end - res->getMaxEarly())
00288 {
00289
00290 data->state->q_operationplan->setEnd(curdate);
00291
00292
00293 if (data->constrainedPlanning && (isLeadtimeConstrained() || isFenceConstrained()))
00294
00295
00296 checkOperationLeadtime(data->state->q_operationplan,*data,false);
00297 }
00298 else
00299
00300 data->state->a_qty = 0.0;
00301 }
00302 }
00303 while (HasOverload && data->state->a_qty!=0.0);
00304
00305
00306
00307
00308
00309
00310 if (data->constrainedPlanning && (data->state->a_qty == 0.0 || data->state->forceLate))
00311 {
00312
00313 if (!noRestore)
00314 data->state->q_operationplan->restore(currentOpplan);
00315
00316
00317
00318 LoadPlan* old_q_loadplan = data->state->q_loadplan;
00319 data->state->q_loadplan = data->state->q_loadplan->getOtherLoadPlan();
00320
00321
00322 Date newDate;
00323 do
00324 {
00325
00326
00327 HasOverload = false;
00328 newDate = Date::infinitePast;
00329 curMax = data->state->q_loadplan->getMax();
00330 double curOnhand = data->state->q_loadplan->getOnhand();
00331 for (cur=res->getLoadPlans().begin(data->state->q_loadplan);
00332 !(HasOverload && newDate) && cur != res->getLoadPlans().end(); )
00333 {
00334
00335 if (cur->getType() == 4)
00336 curMax = cur->getMax();
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351 const TimeLine<LoadPlan>::Event *loadpl = &*(cur++);
00352 if (cur!=res->getLoadPlans().end() && cur->getDate()==loadpl->getDate())
00353 continue;
00354 curOnhand = loadpl->getOnhand();
00355
00356
00357 if (loadpl->getOnhand() > curMax + ROUNDING_ERROR)
00358
00359 HasOverload = true;
00360 else if (!HasOverload && loadpl->getDate() > data->state->q_operationplan->getDates().getEnd())
00361
00362
00363 break;
00364 else if (!newDate && loadpl->getDate()!=data->state->q_loadplan->getDate() && curMax >= fabs(loadpl->getQuantity()))
00365 {
00366
00367
00368 newDate = loadpl->getDate();
00369 }
00370 }
00371
00372
00373 if (HasOverload && newDate)
00374 {
00375
00376 int parallelOps = static_cast<int>((curMax - curOnhand) / data->state->q_loadplan->getQuantity());
00377 if (parallelOps <= 0) parallelOps = 1;
00378
00379 data->state->q_operationplan->getOperation()->setOperationPlanParameters(
00380 data->state->q_operationplan,
00381 currentOpplan.quantity / parallelOps,
00382 newDate,
00383 Date::infinitePast
00384 );
00385 HasOverload = true;
00386 if (data->state->q_operationplan->getDates().getStart() < newDate)
00387
00388
00389
00390 break;
00391 }
00392 }
00393 while (HasOverload && newDate);
00394 data->state->q_loadplan = old_q_loadplan;
00395
00396
00397 if (HasOverload)
00398
00399 data->state->a_date = Date::infiniteFuture;
00400 else
00401 data->state->a_date = data->state->q_operationplan->getDates().getEnd();
00402
00403
00404 data->state->a_qty = 0.0;
00405 }
00406
00407
00408 if (!data->constrainedPlanning && data->state->a_qty == 0.0)
00409 {
00410 data->state->q_operationplan->restore(currentOpplan);
00411 data->state->a_date = data->state->q_date;
00412 data->state->a_qty = orig_q_qty;
00413 }
00414
00415
00416 if (data->state->a_qty > 0.0)
00417 {
00418
00419 data->state->a_cost += data->state->a_qty * res->getCost()
00420 * (data->state->q_operationplan->getDates().getDuration() - data->state->q_operationplan->getUnavailable()) / 3600.0;
00421
00422 if (setupOpplan)
00423 {
00424 data->state->a_cost += data->state->a_qty * res->getCost()
00425 * (setupOpplan->getDates().getDuration() - setupOpplan->getUnavailable()) / 3600.0;
00426 data->state->a_penalty += setupOpplan->getPenalty();
00427 }
00428
00429 if (currentOpplan.end > data->state->q_operationplan->getDates().getEnd())
00430 data->state->a_penalty +=
00431 (currentOpplan.end - data->state->q_operationplan->getDates().getEnd()) / 86400.0;
00432 }
00433 else if (data->state->q_operationplan->getQuantity() > 0.0)
00434 data->state->q_operationplan->setQuantity(0.0);
00435 if (userexit_resource) userexit_resource.call(res, PythonObject(data->constrainedPlanning));
00436
00437 if (data->getSolver()->getLogLevel()>1)
00438 {
00439 logger << indent(res->getLevel()) << " Resource '" << res << "' answers: "
00440 << data->state->a_qty << " " << data->state->a_date;
00441 if (currentOpplan.end > data->state->q_operationplan->getDates().getEnd())
00442 logger << " using earlier capacity "
00443 << data->state->q_operationplan->getDates().getEnd();
00444 if (data->state->a_qty>0.0 && data->state->q_operationplan->getQuantity() < currentOpplan.quantity)
00445 logger << " with reduced quantity " << data->state->q_operationplan->getQuantity();
00446 logger << endl;
00447 }
00448
00449 }
00450
00451
00452 DECLARE_EXPORT void SolverMRP::solve(const ResourceInfinite* res, void* v)
00453 {
00454 SolverMRPdata* data = static_cast<SolverMRPdata*>(v);
00455
00456
00457 if (userexit_resource) userexit_resource.call(res, PythonObject(data->constrainedPlanning));
00458
00459
00460 if (data->getSolver()->getLogLevel()>1 && data->state->q_qty < 0)
00461 logger << indent(res->getLevel()) << " Infinite resource '" << res << "' is asked: "
00462 << (-data->state->q_qty) << " " << data->state->q_operationplan->getDates() << endl;
00463
00464
00465
00466
00467 data->state->a_qty = data->state->q_qty;
00468 data->state->a_date = data->state->q_date;
00469 data->state->a_cost += data->state->a_qty * res->getCost()
00470 * (data->state->q_operationplan->getDates().getDuration() - data->state->q_operationplan->getUnavailable())
00471 / 3600.0;
00472
00473
00474 if (data->getSolver()->getLogLevel()>1 && data->state->q_qty < 0)
00475 logger << indent(res->getLevel()) << " Infinite resource '" << res << "' answers: "
00476 << (-data->state->a_qty) << " " << data->state->a_date << endl;
00477 }
00478
00479
00480 }