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 #define FREPPLE_CORE
00028 #include "frepple/solver.h"
00029 namespace frepple
00030 {
00031
00032
00033 DECLARE_EXPORT void SolverMRP::checkOperationCapacity
00034 (OperationPlan* opplan, SolverMRP::SolverMRPdata& data)
00035 {
00036 bool hasMultipleLoads(opplan->sizeLoadPlans() > 2);
00037 DateRange orig;
00038
00039
00040
00041 do
00042 {
00043 orig = opplan->getDates();
00044 for (OperationPlan::LoadPlanIterator h=opplan->beginLoadPlans();
00045 h!=opplan->endLoadPlans() && opplan->getDates()==orig; ++h)
00046 {
00047 data.state->q_operationplan = opplan;
00048 data.state->q_loadplan = &*h;
00049 data.state->q_qty = h->getQuantity();
00050 data.state->q_date = h->getDate();
00051
00052 h->getLoad()->solve(*this,&data);
00053 }
00054 }
00055
00056
00057
00058
00059 while (hasMultipleLoads && opplan->getDates()!=orig && (data.state->a_qty!=0.0 || data.state->forceLate));
00060 }
00061
00062
00063 DECLARE_EXPORT bool SolverMRP::checkOperation
00064 (OperationPlan* opplan, SolverMRP::SolverMRPdata& data)
00065 {
00066
00067 data.state->a_date = Date::infiniteFuture;
00068 data.state->a_qty = data.state->q_qty;
00069
00070
00071
00072
00073 if (opplan->getQuantity() == 0.0)
00074 {
00075
00076
00077
00078
00079
00080 opplan->setQuantity(0.0001,false);
00081
00082 opplan->setStart(Plan::instance().getCurrent());
00083
00084 data.state->a_date = opplan->getDates().getEnd();
00085 data.state->a_qty = 0.0;
00086 return false;
00087 }
00088
00089
00090
00091 Command* topcommand = data.getLastCommand();
00092
00093
00094 if (!checkOperationLeadtime(opplan,data,true))
00095
00096
00097 return false;
00098
00099
00100
00101
00102 DateRange orig_dates = opplan->getDates();
00103 bool okay = true;
00104 Date a_date;
00105 Date prev_a_date;
00106 double a_qty;
00107 Date orig_q_date = data.state->q_date;
00108 double orig_opplan_qty = data.state->q_qty;
00109 double q_qty_Flow;
00110 Date q_date_Flow;
00111 TimePeriod delay;
00112 bool incomplete;
00113 bool tmp_forceLate = data.state->forceLate;
00114 data.state->forceLate = false;
00115 bool isPlannedEarly;
00116 do
00117 {
00118 if (isCapacityConstrained())
00119 {
00120
00121 checkOperationCapacity(opplan,data);
00122
00123 if (data.state->a_qty==0.0) return false;
00124 }
00125
00126
00127 data.state->q_qty = opplan->getQuantity();
00128 data.state->q_date = opplan->getDates().getEnd();
00129 a_qty = opplan->getQuantity();
00130 a_date = data.state->q_date;
00131 incomplete = false;
00132 delay = 0L;
00133
00134
00135 for (OperationPlan::FlowPlanIterator g=opplan->beginFlowPlans();
00136 g!=opplan->endFlowPlans(); ++g)
00137 if (g->getFlow()->isConsumer())
00138 {
00139
00140 data.state->q_flowplan = &*g;
00141 q_qty_Flow = - data.state->q_flowplan->getQuantity();
00142 q_date_Flow = data.state->q_flowplan->getDate();
00143 g->getFlow()->solve(*this,&data);
00144
00145
00146 if (data.state->a_qty < q_qty_Flow)
00147 {
00148
00149
00150 g->setQuantity(-data.state->a_qty, true);
00151 a_qty = opplan->getQuantity();
00152 incomplete = true;
00153
00154
00155
00156
00157
00158 delay = data.state->a_date - q_date_Flow;
00159
00160
00161
00162 if (a_qty <= ROUNDING_ERROR) break;
00163 }
00164 else if (data.state->a_qty >+ q_qty_Flow + ROUNDING_ERROR)
00165
00166
00167 a_qty = - q_qty_Flow / g->getFlow()->getQuantity();
00168 }
00169
00170 isPlannedEarly = opplan->getDates().getEnd() < orig_dates.getEnd();
00171
00172 if (delay>0L && a_qty <= ROUNDING_ERROR
00173 && a_date + delay <= data.state->q_date_max && a_date + delay > orig_q_date)
00174 {
00175
00176
00177
00178 data.state->q_date = a_date + delay;
00179 data.state->q_qty = orig_opplan_qty;
00180 data.state->a_date = Date::infiniteFuture;
00181 data.state->a_qty = data.state->q_qty;
00182 opplan->getOperation()->setOperationPlanParameters(
00183 opplan, orig_opplan_qty, Date::infinitePast, a_date + delay
00184 );
00185 okay = false;
00186
00187 data.undo(topcommand);
00188
00189 if (data.getSolver()->getLogLevel()>1)
00190 logger << indent(opplan->getOperation()->getLevel())
00191 << " Retrying new date." << endl;
00192 }
00193 else if (delay>0L && a_qty <= ROUNDING_ERROR
00194 && delay < orig_dates.getDuration())
00195 {
00196
00197
00198
00199
00200 opplan->getOperation()->setOperationPlanParameters(
00201 opplan, orig_opplan_qty, orig_dates.getStart() + delay,
00202 orig_dates.getEnd()
00203 );
00204 if (opplan->getDates().getStart() >= orig_dates.getStart() + delay
00205 && opplan->getDates().getEnd() <= orig_dates.getEnd()
00206 && opplan->getQuantity() > ROUNDING_ERROR)
00207 {
00208
00209 orig_dates = opplan->getDates();
00210 data.state->q_date = a_date;
00211 data.state->q_qty = opplan->getQuantity();
00212 data.state->a_date = Date::infiniteFuture;
00213 data.state->a_qty = data.state->q_qty;
00214 okay = false;
00215
00216 data.undo(topcommand);
00217
00218 if (data.getSolver()->getLogLevel()>1)
00219 logger << indent(opplan->getOperation()->getLevel())
00220 << " Retrying with a smaller quantity: "
00221 << opplan->getQuantity() << endl;
00222 }
00223 else
00224 {
00225
00226 opplan->setQuantity(0);
00227 okay = true;
00228 }
00229 }
00230 else
00231 okay = true;
00232 }
00233 while (!okay);
00234
00235
00236 if (a_qty <= ROUNDING_ERROR && !data.state->forceLate
00237 && isPlannedEarly
00238 && a_date != Date::infiniteFuture && isCapacityConstrained())
00239 {
00240
00241
00242
00243
00244
00245 opplan->getOperation()->setOperationPlanParameters
00246 (opplan, orig_opplan_qty, a_date + delay, Date::infinitePast);
00247
00248
00249 data.state->forceLate = true;
00250 checkOperationCapacity(opplan,data);
00251
00252
00253 a_qty = 0.0;
00254 delay = 0L;
00255 a_date = opplan->getDates().getEnd();
00256 }
00257
00258
00259 data.state->a_date = incomplete ? (a_date + delay) : Date::infiniteFuture;
00260 data.state->a_qty = a_qty;
00261 data.state->forceLate = tmp_forceLate;
00262 if (a_qty > ROUNDING_ERROR)
00263 return true;
00264 else
00265 {
00266
00267 data.undo(topcommand);
00268 return false;
00269 }
00270 }
00271
00272
00273 DECLARE_EXPORT bool SolverMRP::checkOperationLeadtime
00274 (OperationPlan* opplan, SolverMRP::SolverMRPdata& data, bool extra)
00275 {
00276
00277 if (!isFenceConstrained() && !isLeadtimeConstrained()) return true;
00278
00279
00280
00281
00282
00283 TimePeriod delta;
00284 if (isFenceConstrained())
00285 {
00286 delta = opplan->getOperation()->getFence();
00287
00288
00289 if (isLeadtimeConstrained() && delta<0L) delta = 0L;
00290 }
00291
00292
00293 if (opplan->getDates().getStart() >= Plan::instance().getCurrent() + delta)
00294
00295 return true;
00296
00297
00298
00299
00300
00301 if (extra)
00302
00303 opplan->getOperation()->setOperationPlanParameters(
00304 opplan, opplan->getQuantity(),
00305 Plan::instance().getCurrent() + delta,
00306 opplan->getDates().getEnd() + opplan->getOperation()->getPostTime(),
00307 false
00308 );
00309 else
00310
00311 opplan->getOperation()->setOperationPlanParameters(
00312 opplan, opplan->getQuantity(),
00313 Plan::instance().getCurrent() + delta,
00314 opplan->getDates().getEnd(),
00315 true
00316 );
00317
00318
00319 if (opplan->getDates().getStart() >= Plan::instance().getCurrent() + delta
00320 && (!extra || opplan->getDates().getEnd() <= data.state->q_date_max)
00321 && opplan->getQuantity() > ROUNDING_ERROR)
00322 {
00323
00324 data.state->a_qty = opplan->getQuantity();
00325 data.state->a_date = opplan->getDates().getEnd();
00326
00327 return true;
00328 }
00329 else
00330 {
00331
00332 data.state->a_qty = 0.0;
00333
00334 if (opplan->getQuantity() + ROUNDING_ERROR < opplan->getOperation()->getSizeMinimum())
00335 opplan->setQuantity(0.0001,false);
00336
00337 opplan->setStart(Plan::instance().getCurrent() + delta);
00338
00339 data.state->a_date = opplan->getDates().getEnd();
00340
00341 opplan->setQuantity(0.0);
00342
00343 return false;
00344 }
00345 }
00346
00347
00348 DECLARE_EXPORT void SolverMRP::solve(const Operation* oper, void* v)
00349 {
00350
00351 assert(oper);
00352
00353 SolverMRPdata* data = static_cast<SolverMRPdata*>(v);
00354 OperationPlan *z;
00355
00356
00357
00358 double flow_qty_per = 1.0;
00359 if (data->state->curBuffer)
00360 {
00361 Flow* f = oper->findFlow(data->state->curBuffer, data->state->q_date);
00362 if (f && f->getQuantity()>0.0)
00363 flow_qty_per = f->getQuantity();
00364 else
00365
00366
00367 throw DataException("Invalid producing operation '" + oper->getName()
00368 + "' for buffer '" + data->state->curBuffer->getName() + "'");
00369 }
00370
00371
00372 if (data->getSolver()->getLogLevel()>1)
00373 logger << indent(oper->getLevel()) << " Operation '" << oper->getName()
00374 << "' is asked: " << data->state->q_qty << " " << data->state->q_date << endl;
00375
00376
00377 Date prev_q_date_max = data->state->q_date_max;
00378 data->state->q_date_max = data->state->q_date;
00379 data->state->q_date -= oper->getPostTime();
00380
00381
00382 if (data->state->curOwnerOpplan)
00383 {
00384
00385 assert(!data->state->curDemand);
00386 z = oper->createOperationPlan(
00387 data->state->q_qty / flow_qty_per,
00388 Date::infinitePast, data->state->q_date, data->state->curDemand,
00389 data->state->curOwnerOpplan, 0
00390 );
00391 }
00392 else
00393 {
00394
00395 CommandCreateOperationPlan *a =
00396 new CommandCreateOperationPlan(
00397 oper, data->state->q_qty / flow_qty_per,
00398 Date::infinitePast, data->state->q_date, data->state->curDemand,
00399 data->state->curOwnerOpplan
00400 );
00401 data->state->curDemand = NULL;
00402 z = a->getOperationPlan();
00403 data->add(a);
00404 }
00405 assert(z);
00406
00407
00408 data->getSolver()->checkOperation(z,*data);
00409 data->state->q_date_max = prev_q_date_max;
00410
00411
00412 if (data->state->curBuffer) data->state->a_qty *= flow_qty_per;
00413
00414
00415 assert(data->state->a_qty >= 0);
00416
00417
00418 if (data->state->a_qty > 0.0)
00419 data->state->a_cost += z->getQuantity() * oper->getCost();
00420
00421
00422 if (data->getSolver()->getLogLevel()>1)
00423 logger << indent(oper->getLevel()) << " Operation '" << oper->getName()
00424 << "' answers: " << data->state->a_qty << " " << data->state->a_date
00425 << " " << data->state->a_cost << " " << data->state->a_penalty << endl;
00426 }
00427
00428
00429
00430 DECLARE_EXPORT void SolverMRP::solve(const OperationRouting* oper, void* v)
00431 {
00432 SolverMRPdata* data = static_cast<SolverMRPdata*>(v);
00433
00434
00435 if (data->getSolver()->getLogLevel()>1)
00436 logger << indent(oper->getLevel()) << " Operation '" << oper->getName()
00437 << "' is asked: " << data->state->q_qty << " " << data->state->q_date << endl;
00438
00439
00440
00441 double flow_qty = 1.0;
00442 if (data->state->curBuffer)
00443 {
00444 flow_qty = 0.0;
00445 Flow *f = oper->findFlow(data->state->curBuffer, data->state->q_date);
00446 if (f) flow_qty += f->getQuantity();
00447 for (Operation::Operationlist::const_iterator
00448 e = oper->getSubOperations().begin();
00449 e != oper->getSubOperations().end();
00450 ++e)
00451 {
00452 f = (*e)->findFlow(data->state->curBuffer, data->state->q_date);
00453 if (f) flow_qty += f->getQuantity();
00454 }
00455 if (flow_qty <= 0.0)
00456 throw DataException("Invalid producing operation '" + oper->getName()
00457 + "' for buffer '" + data->state->curBuffer->getName() + "'");
00458 }
00459
00460 data->state->curBuffer = NULL;
00461 double a_qty(data->state->q_qty / flow_qty);
00462
00463
00464 CommandCreateOperationPlan *a = new CommandCreateOperationPlan(
00465 oper, a_qty, Date::infinitePast,
00466 data->state->q_date, data->state->curDemand, data->state->curOwnerOpplan, false
00467 );
00468 data->state->curDemand = NULL;
00469
00470
00471 OperationPlan *prev_owner_opplan = data->state->curOwnerOpplan;
00472 data->state->curOwnerOpplan = a->getOperationPlan();
00473
00474
00475 Date max_Date;
00476 for (Operation::Operationlist::const_reverse_iterator
00477 e = oper->getSubOperations().rbegin();
00478 e != oper->getSubOperations().rend() && a_qty > 0.0;
00479 ++e)
00480 {
00481
00482 data->state->q_qty = a_qty;
00483 data->state->q_date = data->state->curOwnerOpplan->getDates().getStart();
00484 (*e)->solve(*this,v);
00485 a_qty = data->state->a_qty;
00486
00487 data->state->curOwnerOpplan->setQuantity(a_qty,true);
00488
00489 if (data->state->a_date > max_Date && data->state->a_date != Date::infiniteFuture)
00490 max_Date = data->state->a_date;
00491 }
00492
00493
00494
00495 data->state->a_qty = a_qty * flow_qty;
00496
00497
00498
00499
00500
00501
00502
00503 data->state->curOwnerOpplan->createFlowLoads();
00504 if (data->state->curOwnerOpplan->getQuantity() > 0.0)
00505 {
00506 data->getSolver()->checkOperation(data->state->curOwnerOpplan,*data);
00507
00508
00509 if (data->state->a_date > max_Date && data->state->a_date != Date::infiniteFuture)
00510 max_Date = data->state->a_date;
00511 }
00512 data->state->a_date = (max_Date ? max_Date : Date::infiniteFuture);
00513 if (data->state->a_date < data->state->q_date)
00514 data->state->a_date = data->state->q_date;
00515
00516
00517 data->add(a);
00518
00519
00520 if (data->state->a_qty > 0.0)
00521 data->state->a_cost += data->state->curOwnerOpplan->getQuantity() * oper->getCost();
00522
00523
00524
00525 data->state->curOwnerOpplan = prev_owner_opplan;
00526
00527
00528 assert(data->state->a_qty >= 0);
00529
00530
00531 assert(data->state->a_date >= data->state->q_date);
00532
00533
00534 if (data->getSolver()->getLogLevel()>1)
00535 logger << indent(oper->getLevel()) << " Operation '" << oper->getName()
00536 << "' answers: " << data->state->a_qty << " " << data->state->a_date << " "
00537 << data->state->a_cost << " " << data->state->a_penalty << endl;
00538 }
00539
00540
00541
00542 DECLARE_EXPORT void SolverMRP::solve(const OperationAlternate* oper, void* v)
00543 {
00544 SolverMRPdata *data = static_cast<SolverMRPdata*>(v);
00545 Date origQDate = data->state->q_date;
00546 double origQqty = data->state->q_qty;
00547 const Buffer *buf = data->state->curBuffer;
00548
00549
00550 if (data->getSolver()->getLogLevel()>1)
00551 logger << indent(oper->getLevel()) << " Operation '" << oper->getName()
00552 << "' is asked: " << data->state->q_qty << " " << data->state->q_date << endl;
00553
00554
00555 OperationPlan *prev_owner_opplan = data->state->curOwnerOpplan;
00556 const Demand *d = data->state->curDemand;
00557
00558
00559 double top_flow_qty_per = 0.0;
00560 bool top_flow_exists = false;
00561 if (buf)
00562 {
00563 Flow* f = oper->findFlow(buf, data->state->q_date);
00564 if (f && f->getQuantity() > 0.0)
00565 {
00566 top_flow_qty_per = f->getQuantity();
00567 top_flow_exists = true;
00568 }
00569 }
00570
00571
00572
00573
00574
00575
00576 double a_qty = data->state->q_qty;
00577 bool effectiveOnly = true;
00578 Date a_date = Date::infiniteFuture;
00579 Date ask_date;
00580 for (Operation::Operationlist::const_iterator altIter
00581 = oper->getSubOperations().begin();
00582 altIter != oper->getSubOperations().end(); )
00583 {
00584
00585 const OperationAlternate::alternateProperty& props
00586 = oper->getProperties(*altIter);
00587
00588
00589 if (props.first == 0.0
00590 || (effectiveOnly && !props.second.within(data->state->q_date))
00591 || (!effectiveOnly && props.second.getEnd() > data->state->q_date)
00592 )
00593 {
00594 ++altIter;
00595 if (altIter == oper->getSubOperations().end() && effectiveOnly)
00596 {
00597
00598 effectiveOnly = false;
00599 altIter = oper->getSubOperations().begin();
00600 }
00601 continue;
00602 }
00603
00604
00605 ask_date = effectiveOnly ? origQDate : props.second.getEnd();
00606
00607
00608
00609 double sub_flow_qty_per = 0.0;
00610 if (buf)
00611 {
00612 Flow* f = (*altIter)->findFlow(buf, ask_date);
00613 if (f && f->getQuantity() > 0.0)
00614 sub_flow_qty_per = f->getQuantity();
00615 else if (!top_flow_exists)
00616
00617
00618 throw DataException("Invalid producing operation '" + oper->getName()
00619 + "' for buffer '" + buf->getName() + "'");
00620 }
00621 else
00622
00623 sub_flow_qty_per = 1.0;
00624
00625
00626
00627
00628 data->state->q_qty = a_qty / (sub_flow_qty_per + top_flow_qty_per);
00629 data->state->q_date = ask_date;
00630 data->state->curDemand = const_cast<Demand*>(d);
00631 CommandCreateOperationPlan *a = new CommandCreateOperationPlan(
00632 oper, a_qty, Date::infinitePast, ask_date,
00633 data->state->curDemand, prev_owner_opplan, false
00634 );
00635 data->add(a);
00636 data->state->curDemand = NULL;
00637 data->state->curOwnerOpplan = a->getOperationPlan();
00638
00639
00640 data->state->curBuffer = NULL;
00641 data->state->q_qty = a_qty / (sub_flow_qty_per + top_flow_qty_per);
00642
00643
00644 (*altIter)->solve(*this,v);
00645
00646
00647 if (effectiveOnly && data->state->a_date < a_date && data->state->a_date > ask_date)
00648 a_date = data->state->a_date;
00649
00650
00651 if (data->state->a_qty < ROUNDING_ERROR)
00652
00653 data->undo(a);
00654 else
00655 {
00656
00657
00658 data->state->a_qty *= (sub_flow_qty_per + top_flow_qty_per);
00659
00660
00661 a_qty -= data->state->a_qty;
00662
00663
00664
00665 data->state->q_qty = data->state->a_qty;
00666 data->state->q_date = origQDate;
00667 data->state->curOwnerOpplan->createFlowLoads();
00668 data->getSolver()->checkOperation(data->state->curOwnerOpplan,*data);
00669
00670
00671
00672 if (data->state->a_date < a_date && data->state->a_date > ask_date)
00673 a_date = data->state->a_date;
00674
00675
00676 if (a_qty < ROUNDING_ERROR)
00677 {
00678 a_qty = 0.0;
00679 break;
00680 }
00681 }
00682
00683
00684 ++altIter;
00685 if (altIter == oper->getSubOperations().end() && effectiveOnly)
00686 {
00687
00688 effectiveOnly = false;
00689 altIter = oper->getSubOperations().begin();
00690 }
00691
00692 }
00693 data->state->a_qty = origQqty - a_qty;
00694 data->state->a_date = a_date;
00695
00696
00697 if (data->state->a_qty > 0.0)
00698 data->state->a_cost += data->state->curOwnerOpplan->getQuantity() * oper->getCost();
00699
00700
00701
00702 data->state->curOwnerOpplan = prev_owner_opplan;
00703
00704
00705 assert(data->state->a_qty >= 0);
00706
00707
00708 assert(data->state->a_date >= data->state->q_date);
00709
00710
00711 if (data->getSolver()->getLogLevel()>1)
00712 logger << indent(oper->getLevel()) << " Operation '" << oper->getName()
00713 << "' answers: " << data->state->a_qty << " " << data->state->a_date
00714 << " " << data->state->a_cost << " " << data->state->a_penalty << endl;
00715 }
00716
00717
00718 }