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: 1315 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2010-07-17 18:08:53 +0200 (Sat, 17 Jul 2010) $
00005  ***************************************************************************/
00006 
00007 /***************************************************************************
00008  *                                                                         *
00009  * Copyright (C) 2007-2010 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 = 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       Command* topcommand = data->getLastCommand();
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->undo(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       Command* topcommand = data->getLastCommand();
00179       firstAlternate->getBuffer()->solve(*this,data);
00180       data->state->a_qty = -flplan->getQuantity();
00181       // Restore original planning mode
00182       data->constrainedPlanning = originalPlanningMode;
00183     }
00184     else
00185     {
00186       // Constrained plan: Return 0
00187       data->state->a_date = min_next_date;
00188       data->state->a_qty = 0;
00189       if (data->getSolver()->getLogLevel()>1)
00190         logger << indent(fl->getOperation()->getLevel()) <<
00191           "   Alternate flow doesn't find supply on any alternate : "
00192           << data->state->a_qty << "  " << data->state->a_date << endl;
00193     }
00194   }
00195   else
00196   {
00197     // CASE II: Not an alternate flow.
00198     // In this case, this method is passing control on to the buffer.
00199     data->state->q_qty = - data->state->q_flowplan->getQuantity();
00200     data->state->q_date = data->state->q_flowplan->getDate();
00201     if (data->state->q_qty != 0.0)
00202     {
00203       fl->getBuffer()->solve(*this,data);
00204       if (data->state->a_date > fl->getEffective().getEnd())
00205       {
00206         // The reply date must be less than the effectivity end date: after
00207         // that date the flow in question won't consume any material any more.
00208         if (data->getSolver()->getLogLevel()>1
00209           && data->state->a_qty < ROUNDING_ERROR)
00210           logger << indent(fl->getBuffer()->getLevel()) << "  Buffer '"
00211             << fl->getBuffer()->getName() << "' answer date is adjusted to "
00212             << fl->getEffective().getEnd()
00213             << " because of a date effective flow" << endl;
00214         data->state->a_date = fl->getEffective().getEnd();
00215       }
00216     }
00217     else
00218     {
00219       // It's a zero quantity flowplan.
00220       // E.g. because it is not effective.
00221       data->state->a_date = data->state->q_date;
00222       data->state->a_qty = 0.0;
00223     }
00224   }
00225 }
00226 
00227 
00228 }

Documentation generated for frePPLe by  doxygen