00001 /*************************************************************************** 00002 file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/tags/0.8.0/src/solver/solverflow.cpp $ 00003 version : $LastChangedRevision: 1187 $ $LastChangedBy: jdetaeye $ 00004 date : $LastChangedDate: 2010-02-21 12:45:06 +0100 (Sun, 21 Feb 2010) $ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * 00024 * USA * 00025 * * 00026 ***************************************************************************/ 00027 00028 #define FREPPLE_CORE 00029 #include "frepple/solver.h" 00030 00031 namespace frepple 00032 { 00033 00034 bool sortFlow(const Flow* lhs, const Flow* rhs) 00035 { 00036 return lhs->getPriority() < rhs->getPriority(); 00037 } 00038 00039 00040 DECLARE_EXPORT void SolverMRP::solve(const Flow* fl, void* v) // @todo implement search mode 00041 { 00042 // Note: This method is only called for consuming flows and for the leading 00043 // flow of an alternate group. See SolverMRP::checkOperation 00044 00045 SolverMRPdata* data = static_cast<SolverMRPdata*>(v); 00046 if (fl->hasAlternates()) 00047 { 00048 // CASE I: It is an alternate flow. 00049 // We ask each alternate flow in order of priority till we find a flow 00050 // that has a non-zero reply. 00051 00052 // 1) collect a list of alternates 00053 list<const Flow*> thealternates; 00054 const Flow *x = fl->hasAlternates() ? fl : fl->getAlternate(); 00055 for (Operation::flowlist::const_iterator i = fl->getOperation()->getFlows().begin(); 00056 i != fl->getOperation()->getFlows().end(); ++i) 00057 if ((i->getAlternate() == x || &*i == x) 00058 && i->getEffective().within(data->state->q_flowplan->getDate())) 00059 thealternates.push_front(&*i); 00060 00061 // 2) Sort the list 00062 thealternates.sort(sortFlow); 00063 00064 // 3) Control the planning mode 00065 bool originalPlanningMode = data->constrainedPlanning; 00066 data->constrainedPlanning = true; 00067 const Flow *firstAlternate = NULL; 00068 double firstQuantity; 00069 00070 // 4) Loop through the alternates till we find a non-zero reply 00071 Date min_next_date(Date::infiniteFuture); 00072 double ask_qty; 00073 FlowPlan *flplan = data->state->q_flowplan; 00074 for (list<const Flow*>::const_iterator i = thealternates.begin(); 00075 i != thealternates.end();) 00076 { 00077 const Flow *curflow = *i; 00078 data->state->q_flowplan = flplan; // because q_flowplan can change 00079 00080 // 4a) Switch to this flow 00081 if (data->state->q_flowplan->getFlow() != curflow) 00082 data->state->q_flowplan->setFlow(curflow); 00083 00084 // 4b) Call the Python user exit if there is one 00085 if (userexit_flow) 00086 { 00087 PythonObject result = userexit_flow.call(data->state->q_flowplan, PythonObject(data->constrainedPlanning)); 00088 if (!result.getBool()) 00089 { 00090 // Return value is false, alternate rejected 00091 if (data->getSolver()->getLogLevel()>1) 00092 logger << indent(curflow->getOperation()->getLevel()) 00093 << " User exit disallows consumption from '" 00094 << (*i)->getBuffer()->getName() << "'" << endl; 00095 // Move to the next alternate 00096 if (++i != thealternates.end() && data->getSolver()->getLogLevel()>1) 00097 logger << indent(curflow->getOperation()->getLevel()) << " Alternate flow switches from '" 00098 << curflow->getBuffer()->getName() << "' to '" 00099 << (*i)->getBuffer()->getName() << "'" << endl; 00100 continue; 00101 } 00102 } 00103 00104 // Remember the first alternate 00105 if (!firstAlternate) 00106 { 00107 firstAlternate = *i; 00108 firstQuantity = data->state->q_flowplan->getQuantity(); 00109 } 00110 00111 // 4c) Ask the buffer 00112 data->state->q_qty = ask_qty = - data->state->q_flowplan->getQuantity(); 00113 data->state->q_date = data->state->q_flowplan->getDate(); 00114 Command* topcommand = data->getLastCommand(); 00115 curflow->getBuffer()->solve(*this,data); 00116 00117 // 4d) A positive reply: exit the loop 00118 if (data->state->a_qty > ROUNDING_ERROR) 00119 { 00120 // Update the opplan, which is required to (1) update the flowplans 00121 // and to (2) take care of lot sizing constraints of this operation. 00122 if (data->state->a_qty < ask_qty - ROUNDING_ERROR) 00123 { 00124 flplan->setQuantity(-data->state->a_qty, true); 00125 data->state->a_qty = -flplan->getQuantity(); 00126 } 00127 if (data->state->a_qty > ROUNDING_ERROR) 00128 { 00129 data->constrainedPlanning = originalPlanningMode; 00130 return; 00131 } 00132 } 00133 00134 // 4e) Undo the plan on the alternate 00135 data->undo(topcommand); 00136 00137 // 4f) Prepare for the next alternate 00138 if (data->state->a_date < min_next_date) 00139 min_next_date = data->state->a_date; 00140 if (++i != thealternates.end() && data->getSolver()->getLogLevel()>1) 00141 logger << indent(curflow->getOperation()->getLevel()) << " Alternate flow switches from '" 00142 << curflow->getBuffer()->getName() << "' to '" 00143 << (*i)->getBuffer()->getName() << "'" << endl; 00144 } 00145 00146 // 5) No reply found, all alternates are infeasible 00147 if (!originalPlanningMode) 00148 { 00149 assert(firstAlternate); 00150 // Unconstrained plan: Plan on the primary alternate 00151 // Switch to this flow 00152 if (flplan->getFlow() != firstAlternate) 00153 flplan->setFlow(firstAlternate); 00154 // Message 00155 if (data->getSolver()->getLogLevel()>1) 00156 logger << indent(fl->getOperation()->getLevel()) 00157 << " Alternate flow plans unconstrained on alternate '" 00158 << firstAlternate->getBuffer()->getName() << "'" << endl; 00159 // Plan unconstrained 00160 data->constrainedPlanning = false; 00161 data->state->q_flowplan = flplan; // because q_flowplan can change 00162 flplan->setQuantity(firstQuantity, true); 00163 data->state->q_qty = ask_qty = - flplan->getQuantity(); 00164 data->state->q_date = flplan->getDate(); 00165 Command* topcommand = data->getLastCommand(); 00166 firstAlternate->getBuffer()->solve(*this,data); 00167 data->state->a_qty = -flplan->getQuantity(); 00168 // Restore original planning mode 00169 data->constrainedPlanning = originalPlanningMode; 00170 } 00171 else 00172 { 00173 // Constrained plan: Return 0 00174 data->state->a_date = min_next_date; 00175 data->state->a_qty = 0; 00176 if (data->getSolver()->getLogLevel()>1) 00177 logger << indent(fl->getOperation()->getLevel()) << 00178 " Alternate flow doesn't find supply on any alternate : " 00179 << data->state->a_qty << " " << data->state->a_date << endl; 00180 } 00181 } 00182 else 00183 { 00184 // CASE II: Not an alternate flow. 00185 // In this case, this method is passing control on to the buffer. 00186 data->state->q_qty = - data->state->q_flowplan->getQuantity(); 00187 data->state->q_date = data->state->q_flowplan->getDate(); 00188 if (data->state->q_qty != 0.0) 00189 { 00190 fl->getBuffer()->solve(*this,data); 00191 if (data->state->a_date > fl->getEffective().getEnd()) 00192 { 00193 // The reply date must be less than the effectivity end date: after 00194 // that date the flow in question won't consume any material any more. 00195 if (data->getSolver()->getLogLevel()>1 00196 && data->state->a_qty < ROUNDING_ERROR) 00197 logger << indent(fl->getBuffer()->getLevel()) << " Buffer '" 00198 << fl->getBuffer()->getName() << "' answer date is adjusted to " 00199 << fl->getEffective().getEnd() 00200 << " because of a date effective flow" << endl; 00201 data->state->a_date = fl->getEffective().getEnd(); 00202 } 00203 } 00204 else 00205 { 00206 // It's a zero quantity flowplan. 00207 // E.g. because it is not effective. 00208 data->state->a_date = data->state->q_date; 00209 data->state->a_qty = 0.0; 00210 } 00211 } 00212 } 00213 00214 00215 }