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
00030 namespace frepple
00031 {
00032
00033
00034 double suggestQuantity(const BufferProcure* b, double f)
00035 {
00036
00037 double order_qty = f;
00038
00039
00040 if (b->getSizeMultiple()>0.0)
00041 {
00042 int mult = static_cast<int>(order_qty / b->getSizeMultiple() + 0.99999999);
00043 order_qty = mult * b->getSizeMultiple();
00044 }
00045
00046
00047 if (order_qty < b->getSizeMinimum())
00048 {
00049 order_qty = b->getSizeMinimum();
00050
00051 if (b->getSizeMultiple()>0.0)
00052 {
00053 int mult = static_cast<int>(order_qty / b->getSizeMultiple() + 0.99999999);
00054 order_qty = mult * b->getSizeMultiple();
00055 }
00056
00057 if (order_qty > b->getSizeMaximum())
00058 throw DataException("Inconsistent procurement parameters on buffer '"
00059 + b->getName() + "'");
00060 }
00061
00062
00063 if (order_qty > b->getSizeMaximum())
00064 {
00065 order_qty = b->getSizeMaximum();
00066
00067 if (b->getSizeMultiple()>0.0)
00068 {
00069 int mult = static_cast<int>(order_qty / b->getSizeMultiple());
00070 order_qty = mult * b->getSizeMultiple();
00071 }
00072
00073 if (order_qty < b->getSizeMinimum())
00074 throw DataException("Inconsistent procurement parameters on buffer '"
00075 + b->getName() + "'");
00076 }
00077
00078
00079 return order_qty;
00080 }
00081
00082
00083 DECLARE_EXPORT void SolverMRP::solve(const BufferProcure* b, void* v)
00084 {
00085 SolverMRPdata* data = static_cast<SolverMRPdata*>(v);
00086
00087
00088 if (data->getSolver()->getLogLevel()>1)
00089 logger << indent(b->getLevel()) << " Procurement buffer '" << b->getName()
00090 << "' is asked: " << data->state->q_qty << " " << data->state->q_date << endl;
00091
00092
00093 data->state->a_date = Date::infiniteFuture;
00094
00095
00096 OperationPlan *last_operationplan = NULL;
00097 OperationPlan::iterator curProcure(b->getOperation());
00098 while (curProcure != OperationPlan::iterator(NULL)
00099 && curProcure->getLocked())
00100 ++curProcure;
00101 set<OperationPlan*> moved;
00102
00103
00104
00105 Date earliest_next;
00106 for (OperationPlan::iterator procs(b->getOperation());
00107 procs != OperationPlan::iterator(NULL); ++procs)
00108 if (procs->getLocked())
00109 earliest_next = procs->getDates().getEnd();
00110 Date latest_next = Date::infiniteFuture;
00111
00112
00113 if (earliest_next && b->getMaximumInterval())
00114 latest_next = earliest_next + b->getMaximumInterval();
00115 if (earliest_next && b->getMinimumInterval())
00116 earliest_next += b->getMinimumInterval();
00117 if (data->getSolver()->isLeadtimeConstrained()
00118 && earliest_next < Plan::instance().getCurrent() + b->getLeadtime())
00119 earliest_next = Plan::instance().getCurrent() + b->getLeadtime();
00120 if (data->getSolver()->isFenceConstrained()
00121 && earliest_next < Plan::instance().getCurrent() + b->getFence())
00122 earliest_next = Plan::instance().getCurrent() + b->getFence();
00123
00124
00125 Date current_date;
00126 double produced = 0.0;
00127 double consumed = 0.0;
00128 double current_inventory = 0.0;
00129 const FlowPlan* current_flowplan = NULL;
00130 for (Buffer::flowplanlist::const_iterator cur=b->getFlowPlans().begin();
00131 latest_next != Date::infiniteFuture || cur != b->getFlowPlans().end(); )
00132 {
00133 if (cur==b->getFlowPlans().end() || latest_next < cur->getDate())
00134 {
00135
00136 current_date = latest_next;
00137 current_flowplan = NULL;
00138 }
00139 else if (earliest_next && earliest_next < cur->getDate())
00140 {
00141
00142 current_date = earliest_next;
00143 current_flowplan = NULL;
00144 }
00145 else
00146 {
00147
00148 if (current_date && current_date >= cur->getDate())
00149 {
00150
00151
00152 cur++;
00153 continue;
00154 }
00155 current_date = cur->getDate();
00156 bool noConsumers = true;
00157 do
00158 {
00159 if (cur->getType() != 1)
00160 {
00161 cur++;
00162 continue;
00163 }
00164 current_flowplan = static_cast<const FlowPlan*>(&*(cur++));
00165 if (current_flowplan->getQuantity() < 0)
00166 {
00167 consumed -= current_flowplan->getQuantity();
00168 noConsumers = false;
00169 }
00170 else if (current_flowplan->getOperationPlan()->getLocked())
00171 produced += current_flowplan->getQuantity();
00172 }
00173
00174 while (cur != b->getFlowPlans().end() && cur->getDate() == current_date);
00175
00176 if (noConsumers) continue;
00177 }
00178
00179
00180
00181 current_inventory = produced - consumed;
00182
00183
00184 if (current_date < earliest_next)
00185 {
00186 if (current_inventory < -ROUNDING_ERROR
00187 && current_date >= data->state->q_date
00188 && b->getMinimumInterval()
00189 && data->state->a_date > earliest_next
00190 && data->getSolver()->isMaterialConstrained())
00191
00192
00193 data->state->a_date = earliest_next;
00194 continue;
00195 }
00196
00197
00198 if (current_inventory >= b->getMinimumInventory()
00199 && current_date < latest_next)
00200 {
00201 if (current_date == earliest_next) earliest_next = Date::infinitePast;
00202 continue;
00203 }
00204
00205
00206
00207 if (current_date == earliest_next
00208 && last_operationplan
00209 && current_inventory < b->getMinimumInventory())
00210 {
00211 double origqty = last_operationplan->getQuantity();
00212 last_operationplan->setQuantity(suggestQuantity(b,
00213 last_operationplan->getQuantity()
00214 + b->getMinimumInventory() - current_inventory));
00215 produced += last_operationplan->getQuantity() - origqty;
00216 current_inventory = produced - consumed;
00217 if (current_inventory < -ROUNDING_ERROR
00218 && data->state->a_date > earliest_next + b->getMinimumInterval()
00219 && earliest_next + b->getMinimumInterval() > data->state->q_date
00220 && data->getSolver()->isMaterialConstrained())
00221
00222 data->state->a_date = earliest_next + b->getMinimumInterval();
00223 }
00224
00225
00226 earliest_next = Date::infinitePast;
00227 double order_qty = suggestQuantity(b,
00228 b->getMaximumInventory() - current_inventory);
00229 if (order_qty > 0)
00230 {
00231
00232 if (curProcure == OperationPlan::iterator(NULL))
00233 {
00234
00235 CommandCreateOperationPlan *a =
00236 new CommandCreateOperationPlan(b->getOperation(), order_qty,
00237 Date::infinitePast, current_date, data->state->curDemand);
00238 last_operationplan = a->getOperationPlan();
00239 produced += last_operationplan->getQuantity();
00240 data->add(a);
00241 }
00242 else if (curProcure->getDates().getEnd() == current_date
00243 && curProcure->getQuantity() == order_qty)
00244 {
00245
00246 produced += order_qty;
00247 last_operationplan = &*curProcure;
00248 moved.insert(last_operationplan);
00249 do
00250 ++curProcure;
00251 while (curProcure != OperationPlan::iterator(NULL)
00252 && curProcure->getLocked() && moved.find(&*curProcure)!=moved.end());
00253 }
00254 else
00255 {
00256
00257 CommandMoveOperationPlan *a =
00258 new CommandMoveOperationPlan(&*curProcure, current_date, true, order_qty);
00259 last_operationplan = a->getOperationPlan();
00260 moved.insert(last_operationplan);
00261 data->add(a);
00262 produced += last_operationplan->getQuantity();
00263 do
00264 ++curProcure;
00265 while (curProcure != OperationPlan::iterator(NULL)
00266 && curProcure->getLocked() && moved.find(&*curProcure)!=moved.end());
00267 }
00268 if (b->getMinimumInterval())
00269 earliest_next = current_date + b->getMinimumInterval();
00270 }
00271 if (b->getMaximumInterval())
00272 {
00273 current_inventory = produced - consumed;
00274 if (current_inventory >= b->getMaximumInventory()
00275 && cur == b->getFlowPlans().end())
00276
00277
00278 latest_next = Date::infiniteFuture;
00279 else
00280 latest_next = current_date + b->getMaximumInterval();
00281 }
00282 }
00283
00284
00285 while (curProcure != OperationPlan::iterator(NULL))
00286 {
00287 OperationPlan *opplan = &*(curProcure++);
00288 if (!opplan->getLocked() && moved.find(opplan)!=moved.end())
00289 data->add(new CommandDeleteOperationPlan(opplan));
00290 }
00291
00292
00293 if (data->getSolver()->isFenceConstrained()
00294 || data->getSolver()->isLeadtimeConstrained()
00295 || data->getSolver()->isMaterialConstrained())
00296 {
00297
00298 double shortage = 0;
00299 for (Buffer::flowplanlist::const_iterator cur=b->getFlowPlans().begin();
00300 cur != b->getFlowPlans().end(); ++cur)
00301 if (cur->getDate() >= data->state->q_date
00302 && cur->getOnhand() < -ROUNDING_ERROR
00303 && cur->getOnhand() < shortage)
00304 {
00305 shortage = cur->getOnhand();
00306 if (-shortage >= data->state->q_qty) break;
00307 }
00308 if (shortage < 0)
00309 {
00310
00311 data->state->a_qty = data->state->q_qty + shortage;
00312
00313 if (data->state->a_qty < 0) data->state->a_qty = 0;
00314
00315 if (data->getSolver()->isFenceConstrained()
00316 && data->state->q_date < Plan::instance().getCurrent() + b->getFence()
00317 && data->state->a_date > Plan::instance().getCurrent() + b->getFence())
00318 data->state->a_date = Plan::instance().getCurrent() + b->getFence();
00319 if (data->getSolver()->isLeadtimeConstrained()
00320 && data->state->q_date < Plan::instance().getCurrent() + b->getLeadtime()
00321 && data->state->a_date > Plan::instance().getCurrent() + b->getLeadtime())
00322 data->state->a_date = Plan::instance().getCurrent() + b->getLeadtime();
00323 }
00324 else
00325
00326 data->state->a_qty = data->state->q_qty;
00327 }
00328 else
00329
00330 data->state->a_qty = data->state->q_qty;
00331
00332
00333 if (b->getItem() && data->state->a_qty > 0.0)
00334 data->state->a_cost += data->state->a_qty * b->getItem()->getPrice();
00335
00336
00337 if (data->getSolver()->getLogLevel()>1)
00338 logger << indent(b->getLevel()) << " Procurement buffer '" << b
00339 << "' answers: " << data->state->a_qty << " " << data->state->a_date
00340 << " " << data->state->a_cost << " " << data->state->a_penalty << endl;
00341 }
00342
00343
00344 }