solverflow.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/tags/0.9.1/src/solver/solverflow.cpp $
00003   version : $LastChangedRevision: 1656 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2012-03-27 19:05:34 +0200 (Tue, 27 Mar 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 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 }

Documentation generated for frePPLe by  doxygen