solverplan.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/tags/0.8.0/src/solver/solverplan.cpp $
00003   version : $LastChangedRevision: 1185 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2010-02-21 01:00:40 +0100 (Sun, 21 Feb 2010) $
00005  ***************************************************************************/
00006 
00007 /***************************************************************************
00008  *                                                                         *
00009  * Copyright (C) 2007 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 namespace frepple
00031 {
00032 
00033 DECLARE_EXPORT const MetaClass* SolverMRP::metadata;
00034 
00035 
00036 void LibrarySolver::initialize()
00037 {
00038   // Initialize only once
00039   static bool init = false;
00040   if (init)
00041   {
00042     logger << "Warning: Calling frepple::LibrarySolver::initialize() more "
00043     << "than once." << endl;
00044     return;
00045   }
00046   init = true;
00047 
00048   // Register all classes.
00049   if (SolverMRP::initialize())
00050     throw RuntimeException("Error registering solver_mrp Python type");
00051 }
00052 
00053 
00054 int SolverMRP::initialize()
00055 {
00056   // Initialize the metadata
00057   metadata = new MetaClass
00058     ("solver","solver_mrp",Object::createString<SolverMRP>,true);
00059 
00060   // Initialize the Python class
00061   FreppleClass<SolverMRP,Solver>::getType().addMethod("solve", solve, METH_VARARGS, "run the solver");
00062   FreppleClass<SolverMRP,Solver>::getType().addMethod("commit", commit, METH_NOARGS, "commit the plan changes");
00063   FreppleClass<SolverMRP,Solver>::getType().addMethod("undo", undo, METH_NOARGS, "undo the plan changes");
00064   return FreppleClass<SolverMRP,Solver>::initialize();
00065 }
00066 
00067 
00068 DECLARE_EXPORT bool SolverMRP::demand_comparison(const Demand* l1, const Demand* l2)
00069 {
00070   if (l1->getPriority() != l2->getPriority())
00071     return l1->getPriority() < l2->getPriority();
00072   else if (l1->getDue() != l2->getDue())
00073     return l1->getDue() < l2->getDue();
00074   else
00075     return l1->getQuantity() < l2->getQuantity();
00076 }
00077 
00078 
00079 DECLARE_EXPORT void SolverMRP::SolverMRPdata::execute()
00080 {
00081   // Check
00082   if (!demands || !getSolver())
00083     throw LogicException("Missing demands or solver.");
00084 
00085   // Message
00086   SolverMRP* Solver = getSolver();
00087   if (Solver->getLogLevel()>0)
00088     logger << "Start solving cluster " << cluster << " at " << Date::now() << endl;
00089 
00090   // Solve the planning problem
00091   try
00092   {
00093     // Sort the demands of this problem.
00094     // We use a stable sort to get reproducible results between platforms
00095     // and STL implementations.
00096     stable_sort(demands->begin(), demands->end(), demand_comparison);
00097 
00098     // Loop through the list of all demands in this planning problem
00099     constrainedPlanning = (Solver->getPlanType() == 1);
00100     for (deque<Demand*>::const_iterator i = demands->begin();
00101         i != demands->end(); ++i)
00102     {
00103       Command* topcommand = getLastCommand();
00104       // Plan the demand
00105       try
00106       {
00107         State* mystate = state;
00108         push();
00109         try {(*i)->solve(*Solver,this);}
00110         catch (...)
00111         {
00112           while (state > mystate) pop();
00113           throw;
00114         }
00115         while (state > mystate) pop();
00116       }
00117       catch (...)
00118       {
00119         // Error message
00120         logger << "Error: Caught an exception while solving demand '"
00121           << (*i)->getName() << "':" << endl;
00122         try {throw;}
00123         catch (bad_exception&) {logger << "  bad exception" << endl;}
00124         catch (exception& e) {logger << "  " << e.what() << endl;}
00125         catch (...) {logger << "  Unknown type" << endl;}
00126 
00127         // Cleaning up
00128         undo(topcommand);
00129       }
00130     }
00131 
00132     // Clean the list of demands of this cluster
00133     demands->clear();
00134   }
00135   catch (...)
00136   {
00137     // We come in this exception handling code only if there is a problem with
00138     // with this cluster that goes beyond problems with single orders.
00139     // If the problem is with single orders, the exception handling code above
00140     // will do a proper rollback.
00141 
00142     // Error message
00143     logger << "Error: Caught an exception while solving cluster "
00144     << cluster << ":" << endl;
00145     try {throw;}
00146     catch (bad_exception&){logger << "  bad exception" << endl;}
00147     catch (exception& e) {logger << "  " << e.what() << endl;}
00148     catch (...) {logger << "  Unknown type" << endl;}
00149 
00150     // Clean up the operationplans of this cluster
00151     for (Operation::iterator f=Operation::begin(); f!=Operation::end(); ++f)
00152       if (f->getCluster() == cluster)
00153         f->deleteOperationPlans();
00154 
00155     // Clean the list of demands of this cluster
00156     demands->clear();
00157   }
00158 
00159   // Message
00160   if (Solver->getLogLevel()>0)
00161     logger << "End solving cluster " << cluster << " at " << Date::now() << endl;
00162 }
00163 
00164 
00165 DECLARE_EXPORT void SolverMRP::solve(void *v)
00166 {
00167   // Categorize all demands in their cluster
00168   for (Demand::iterator i = Demand::begin(); i != Demand::end(); ++i)
00169     demands_per_cluster[i->getCluster()].push_back(&*i);
00170 
00171   // Delete of operationplans of the affected clusters
00172   // This deletion is not multi-threaded... But on the other hand we need to
00173   // loop through the operations only once (rather than as many times as there
00174   // are clusters)
00175   // A multi-threaded alternative would be to hash the operations here, and
00176   // then delete in each thread.
00177   if (getLogLevel()>0) logger << "Deleting previous plan" << endl;
00178   for (Operation::iterator e=Operation::begin(); e!=Operation::end(); ++e)
00179     // The next if-condition is actually redundant if we plan everything
00180     if (demands_per_cluster.find(e->getCluster())!=demands_per_cluster.end())
00181       e->deleteOperationPlans();
00182 
00183   // Count how many clusters we have to plan
00184   int cl = demands_per_cluster.size();
00185   if (cl<1) return;
00186 
00187   // Create the command list to control the execution
00188   CommandList threads;
00189 
00190   // Solve in parallel threads.
00191   // When not solving in silent and autocommit mode, we only use a single
00192   // solver thread.
00193   if (getLogLevel()>0 || !getAutocommit())
00194     threads.setMaxParallel(1);
00195   else
00196     threads.setMaxParallel( cl > getMaxParallel() ? getMaxParallel() : cl);
00197 
00198   // Make sure a problem in a single cluster doesn't spoil it all
00199   threads.setAbortOnError(false);
00200   for (classified_demand::iterator j = demands_per_cluster.begin();
00201       j != demands_per_cluster.end(); ++j)
00202     threads.add(new SolverMRPdata(this, j->first, &(j->second)));
00203 
00204   // Run the planning command threads and wait for them to exit
00205   threads.execute();
00206 
00207   // Check the resource setups that were broken yyy xxx todo needs to be removed
00208   for (Resource::iterator gres = Resource::begin(); gres != Resource::end(); ++gres)
00209   {
00210     if (gres->getSetupMatrix()) gres->updateSetups();
00211   }
00212 }
00213 
00214 
00215 DECLARE_EXPORT void SolverMRP::writeElement(XMLOutput *o, const Keyword& tag, mode m) const
00216 {
00217   // Writing a reference
00218   if (m == REFERENCE)
00219   {
00220     o->writeElement
00221     (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00222     return;
00223   }
00224 
00225   // Write the complete object
00226   if (m != NOHEADER) o->BeginObject
00227     (tag, Tags::tag_name, getName(), Tags::tag_type, getType().type);
00228 
00229   // Write the fields
00230   if (constrts) o->writeElement(Tags::tag_constraints, constrts);
00231   if (plantype != 1) o->writeElement(Tags::tag_plantype, plantype);
00232   if (maxparallel) o->writeElement(Tags::tag_maxparallel, maxparallel);
00233   if (!autocommit) o->writeElement(Tags::tag_autocommit, autocommit);
00234   if (userexit_flow) 
00235     o->writeElement(Tags::tag_userexit_flow, static_cast<string>(userexit_flow));
00236   if (userexit_demand) 
00237     o->writeElement(Tags::tag_userexit_demand, static_cast<string>(userexit_demand));
00238   if (userexit_buffer) 
00239     o->writeElement(Tags::tag_userexit_buffer, static_cast<string>(userexit_buffer));
00240   if (userexit_resource) 
00241     o->writeElement(Tags::tag_userexit_resource, static_cast<string>(userexit_resource));
00242   if (userexit_operation) 
00243     o->writeElement(Tags::tag_userexit_operation, static_cast<string>(userexit_operation));
00244 
00245   // Write the parent class
00246   Solver::writeElement(o, tag, NOHEADER);
00247 }
00248 
00249 
00250 DECLARE_EXPORT void SolverMRP::endElement(XMLInput& pIn, const Attribute& pAttr, const DataElement& pElement)
00251 {
00252   if (pAttr.isA(Tags::tag_constraints))
00253     setConstraints(pElement.getInt());
00254   else if (pAttr.isA(Tags::tag_maxparallel))
00255     setMaxParallel(pElement.getInt());
00256   else if (pAttr.isA(Tags::tag_autocommit))
00257     setAutocommit(pElement.getBool());
00258   else if (pAttr.isA(Tags::tag_userexit_flow))
00259     setUserExitFlow(pElement.getString());
00260   else if (pAttr.isA(Tags::tag_userexit_demand))
00261     setUserExitDemand(pElement.getString());
00262   else if (pAttr.isA(Tags::tag_userexit_buffer))
00263     setUserExitBuffer(pElement.getString());
00264   else if (pAttr.isA(Tags::tag_userexit_resource))
00265     setUserExitResource(pElement.getString());
00266   else if (pAttr.isA(Tags::tag_userexit_operation))
00267     setUserExitOperation(pElement.getString());
00268   else if (pAttr.isA(Tags::tag_plantype))
00269     setPlanType(pElement.getInt());
00270   else
00271     Solver::endElement(pIn, pAttr, pElement);
00272 }
00273 
00274 
00275 DECLARE_EXPORT PyObject* SolverMRP::getattro(const Attribute& attr)
00276 {
00277   if (attr.isA(Tags::tag_constraints))
00278     return PythonObject(getConstraints());
00279   if (attr.isA(Tags::tag_maxparallel))
00280     return PythonObject(getMaxParallel());
00281   if (attr.isA(Tags::tag_autocommit))
00282     return PythonObject(getAutocommit());
00283   if (attr.isA(Tags::tag_userexit_flow))
00284     return getUserExitFlow();
00285   if (attr.isA(Tags::tag_userexit_demand))
00286     return getUserExitDemand();
00287   if (attr.isA(Tags::tag_userexit_buffer))
00288     return getUserExitBuffer();
00289   if (attr.isA(Tags::tag_userexit_resource))
00290     return getUserExitResource();
00291   if (attr.isA(Tags::tag_userexit_operation))
00292     return getUserExitOperation();
00293   if (attr.isA(Tags::tag_plantype))
00294     return PythonObject(getPlanType());
00295   return Solver::getattro(attr);
00296 }
00297 
00298 
00299 DECLARE_EXPORT int SolverMRP::setattro(const Attribute& attr, const PythonObject& field)
00300 {
00301   if (attr.isA(Tags::tag_constraints))
00302     setConstraints(field.getInt());
00303   else if (attr.isA(Tags::tag_maxparallel))
00304     setMaxParallel(field.getInt());
00305   else if (attr.isA(Tags::tag_autocommit))
00306     setAutocommit(field.getBool());
00307   else if (attr.isA(Tags::tag_userexit_flow))
00308     setUserExitFlow(field);
00309   else if (attr.isA(Tags::tag_userexit_demand))
00310     setUserExitDemand(field);
00311   else if (attr.isA(Tags::tag_userexit_buffer))
00312     setUserExitBuffer(field);
00313   else if (attr.isA(Tags::tag_userexit_resource))
00314     setUserExitResource(field);
00315   else if (attr.isA(Tags::tag_userexit_operation))
00316     setUserExitOperation(field);
00317   else if (attr.isA(Tags::tag_plantype))
00318     setPlanType(field.getInt());
00319   else
00320     return Solver::setattro(attr, field);
00321   return 0;
00322 }
00323 
00324 
00325 DECLARE_EXPORT PyObject* SolverMRP::solve(PyObject *self, PyObject *args)
00326 {
00327   // Parse the argument
00328   PyObject *dem = NULL;
00329   if (args && !PyArg_ParseTuple(args, "|O:solve", &dem)) return NULL;
00330   if (dem && !PyObject_TypeCheck(dem, Demand::metadata->pythonClass))
00331     throw DataException("solver argument must be a demand");
00332 
00333   Py_BEGIN_ALLOW_THREADS   // Free Python interpreter for other threads
00334   try
00335   {
00336     SolverMRP* sol = static_cast<SolverMRP*>(self);
00337     if (!dem)
00338     {
00339       // Complete replan
00340       sol->setAutocommit(true);
00341       sol->solve();
00342     }
00343     else
00344     {
00345       // Incrementally plan a single demand
00346       sol->setAutocommit(false);
00347       sol->commands.sol = sol;
00348       static_cast<Demand*>(dem)->solve(*sol, &(sol->commands));
00349     }
00350   }
00351   catch(...)
00352   {
00353     Py_BLOCK_THREADS;
00354     PythonType::evalException();
00355     return NULL;
00356   }
00357   Py_END_ALLOW_THREADS   // Reclaim Python interpreter
00358   return Py_BuildValue("");
00359 }
00360 
00361 
00362 DECLARE_EXPORT PyObject* SolverMRP::commit(PyObject *self, PyObject *args)
00363 {
00364   Py_BEGIN_ALLOW_THREADS   // Free Python interpreter for other threads
00365   try
00366   {
00367     static_cast<SolverMRP*>(self)->commands.CommandList::execute();
00368   }
00369   catch(...)
00370   {
00371     Py_BLOCK_THREADS;
00372     PythonType::evalException();
00373     return NULL;
00374   }
00375   Py_END_ALLOW_THREADS   // Reclaim Python interpreter
00376   return Py_BuildValue("");
00377 }
00378 
00379 
00380 DECLARE_EXPORT PyObject* SolverMRP::undo(PyObject *self, PyObject *args)
00381 {
00382   Py_BEGIN_ALLOW_THREADS   // Free Python interpreter for other threads
00383   try
00384   {
00385     static_cast<SolverMRP*>(self)->commands.undo();
00386   }
00387   catch(...)
00388   {
00389     Py_BLOCK_THREADS;
00390     PythonType::evalException();
00391     return NULL;
00392   }
00393   Py_END_ALLOW_THREADS   // Reclaim Python interpreter
00394   return Py_BuildValue("");
00395 }
00396 
00397 } // end namespace

Generated on 16 Apr 2010 for frePPLe by  doxygen 1.6.1