solverflow.cpp
Go to the documentation of this file.
00001 /*************************************************************************** 00002 file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/trunk/src/solver/solverflow.cpp $ 00003 version : $LastChangedRevision: 1505 $ $LastChangedBy: jdetaeye $ 00004 date : $LastChangedDate: 2011-08-26 18:55:08 +0200 (Fri, 26 Aug 2011) $ 00005 ***************************************************************************/ 00006 00007 /*************************************************************************** 00008 * * 00009 * Copyright (C) 2007-2011 by Johan De Taeye, frePPLe bvba * 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 = 0.0; 00069 00070 // Remember the top constraint 00071 bool originalLogConstraints = data->logConstraints; 00072 //Problem* topConstraint = data->planningDemand->getConstraints().top(); 00073 00074 // 4) Loop through the alternates till we find a non-zero reply 00075 Date min_next_date(Date::infiniteFuture); 00076 double ask_qty; 00077 FlowPlan *flplan = data->state->q_flowplan; 00078 for (list<const Flow*>::const_iterator i = thealternates.begin(); 00079 i != thealternates.end();) 00080 { 00081 const Flow *curflow = *i; 00082 data->state->q_flowplan = flplan; // because q_flowplan can change 00083 00084 // 4a) Switch to this flow 00085 if (data->state->q_flowplan->getFlow() != curflow) 00086 data->state->q_flowplan->setFlow(curflow); 00087 00088 // 4b) Call the Python user exit if there is one 00089 if (userexit_flow) 00090 { 00091 PythonObject result = userexit_flow.call(data->state->q_flowplan, PythonObject(data->constrainedPlanning)); 00092 if (!result.getBool()) 00093 { 00094 // Return value is false, alternate rejected 00095 if (data->getSolver()->getLogLevel()>1) 00096 logger << indent(curflow->getOperation()->getLevel()) 00097 << " User exit disallows consumption from '" 00098 << (*i)->getBuffer()->getName() << "'" << endl; 00099 // Move to the next alternate 00100 if (++i != thealternates.end() && data->getSolver()->getLogLevel()>1) 00101 logger << indent(curflow->getOperation()->getLevel()) << " Alternate flow switches from '" 00102 << curflow->getBuffer()->getName() << "' to '" 00103 << (*i)->getBuffer()->getName() << "'" << endl; 00104 continue; 00105 } 00106 } 00107 00108 // Remember the first alternate 00109 if (!firstAlternate) 00110 { 00111 firstAlternate = *i; 00112 firstQuantity = data->state->q_flowplan->getQuantity(); 00113 } 00114 00115 // Constraint tracking 00116 if (*i != firstAlternate) 00117 // Only enabled on first alternate 00118 data->logConstraints = false; 00119 else 00120 // Keep track of constraints, if enabled 00121 data->logConstraints = originalLogConstraints; 00122 00123 // 4c) Ask the buffer 00124 data->state->q_qty = ask_qty = - data->state->q_flowplan->getQuantity(); 00125 data->state->q_date = data->state->q_flowplan->getDate(); 00126 CommandManager::Bookmark* topcommand = data->setBookmark(); 00127 curflow->getBuffer()->solve(*this,data); 00128 00129 // 4d) A positive reply: exit the loop 00130 if (data->state->a_qty > ROUNDING_ERROR) 00131 { 00132 // Update the opplan, which is required to (1) update the flowplans 00133 // and to (2) take care of lot sizing constraints of this operation. 00134 if (data->state->a_qty < ask_qty - ROUNDING_ERROR) 00135 { 00136 flplan->setQuantity(-data->state->a_qty, true); 00137 data->state->a_qty = -flplan->getQuantity(); 00138 } 00139 if (data->state->a_qty > ROUNDING_ERROR) 00140 { 00141 data->constrainedPlanning = originalPlanningMode; 00142 data->logConstraints = originalLogConstraints; 00143 return; 00144 } 00145 } 00146 00147 // 4e) Undo the plan on the alternate 00148 data->rollback(topcommand); 00149 00150 // 4f) Prepare for the next alternate 00151 if (data->state->a_date < min_next_date) 00152 min_next_date = data->state->a_date; 00153 if (++i != thealternates.end() && data->getSolver()->getLogLevel()>1) 00154 logger << indent(curflow->getOperation()->getLevel()) << " Alternate flow switches from '" 00155 << curflow->getBuffer()->getName() << "' to '" 00156 << (*i)->getBuffer()->getName() << "'" << endl; 00157 } 00158 00159 // 5) No reply found, all alternates are infeasible 00160 if (!originalPlanningMode) 00161 { 00162 assert(firstAlternate); 00163 // Unconstrained plan: Plan on the primary alternate 00164 // Switch to this flow 00165 if (flplan->getFlow() != firstAlternate) 00166 flplan->setFlow(firstAlternate); 00167 // Message 00168 if (data->getSolver()->getLogLevel()>1) 00169 logger << indent(fl->getOperation()->getLevel()) 00170 << " Alternate flow plans unconstrained on alternate '" 00171 << firstAlternate->getBuffer()->getName() << "'" << endl; 00172 // Plan unconstrained 00173 data->constrainedPlanning = false; 00174 data->state->q_flowplan = flplan; // because q_flowplan can change 00175 flplan->setQuantity(firstQuantity, true); 00176 data->state->q_qty = ask_qty = - flplan->getQuantity(); 00177 data->state->q_date = flplan->getDate(); 00178 firstAlternate->getBuffer()->solve(*this,data); 00179 data->state->a_qty = -flplan->getQuantity(); 00180 // Restore original planning mode 00181 data->constrainedPlanning = originalPlanningMode; 00182 } 00183 else 00184 { 00185 // Constrained plan: Return 0 00186 data->state->a_date = min_next_date; 00187 data->state->a_qty = 0; 00188 if (data->getSolver()->getLogLevel()>1) 00189 logger << indent(fl->getOperation()->getLevel()) << 00190 " Alternate flow doesn't find supply on any alternate : " 00191 << data->state->a_qty << " " << data->state->a_date << endl; 00192 } 00193 } 00194 else 00195 { 00196 // CASE II: Not an alternate flow. 00197 // In this case, this method is passing control on to the buffer. 00198 data->state->q_qty = - data->state->q_flowplan->getQuantity(); 00199 data->state->q_date = data->state->q_flowplan->getDate(); 00200 if (data->state->q_qty != 0.0) 00201 { 00202 fl->getBuffer()->solve(*this,data); 00203 if (data->state->a_date > fl->getEffective().getEnd()) 00204 { 00205 // The reply date must be less than the effectivity end date: after 00206 // that date the flow in question won't consume any material any more. 00207 if (data->getSolver()->getLogLevel()>1 00208 && data->state->a_qty < ROUNDING_ERROR) 00209 logger << indent(fl->getBuffer()->getLevel()) << " Buffer '" 00210 << fl->getBuffer()->getName() << "' answer date is adjusted to " 00211 << fl->getEffective().getEnd() 00212 << " because of a date effective flow" << endl; 00213 data->state->a_date = fl->getEffective().getEnd(); 00214 } 00215 } 00216 else 00217 { 00218 // It's a zero quantity flowplan. 00219 // E.g. because it is not effective. 00220 data->state->a_date = data->state->q_date; 00221 data->state->a_qty = 0.0; 00222 } 00223 } 00224 } 00225 00226 00227 }