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