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 }

Documentation generated for frePPLe by  doxygen