예제 #1
0
DECLARE_EXPORT PyObject* savePlan(PyObject* self, PyObject* args)
{
  // Pick up arguments
  const char *filename = "plan.out";
  int ok = PyArg_ParseTuple(args, "s:saveplan", &filename);
  if (!ok) return NULL;

  // Free Python interpreter for other threads
  Py_BEGIN_ALLOW_THREADS

  // Execute and catch exceptions
  ofstream textoutput;
  try
  {
    // Open the output file
    textoutput.open(filename, ios::out);

    // Write the buffer summary
    for (Buffer::iterator gbuf = Buffer::begin();
        gbuf != Buffer::end(); ++gbuf)
    {
      if (!gbuf->getHidden())
        for (Buffer::flowplanlist::const_iterator
            oo=gbuf->getFlowPlans().begin();
            oo!=gbuf->getFlowPlans().end();
            ++oo)
          if (oo->getType() == 1 && oo->getQuantity() != 0.0)
          {
            textoutput << "BUFFER\t" << *gbuf << '\t'
                << oo->getDate() << '\t'
                << oo->getQuantity() << '\t'
                << oo->getOnhand() << endl;
          }
    }

    // Write the demand summary
    for (Demand::iterator gdem = Demand::begin();
        gdem != Demand::end(); ++gdem)
    {
      if (!gdem->getHidden())
      {
        for (Demand::OperationPlan_list::const_iterator
            pp = gdem->getDelivery().begin();
            pp != gdem->getDelivery().end();
            ++pp)
          textoutput << "DEMAND\t" << (*gdem) << '\t'
              << (*pp)->getDates().getEnd() << '\t'
              << (*pp)->getQuantity() << endl;
      }
    }

    // Write the resource summary
    for (Resource::iterator gres = Resource::begin();
        gres != Resource::end(); ++gres)
    {
      if (!gres->getHidden())
        for (Resource::loadplanlist::const_iterator
            qq=gres->getLoadPlans().begin();
            qq!=gres->getLoadPlans().end();
            ++qq)
          if (qq->getType() == 1 && qq->getQuantity() != 0.0)
          {
            textoutput << "RESOURCE\t" << *gres << '\t'
                << qq->getDate() << '\t'
                << qq->getQuantity() << '\t'
                << qq->getOnhand() << endl;
          }
    }

    // Write the operationplan summary.
    for (OperationPlan::iterator rr = OperationPlan::begin();
        rr != OperationPlan::end(); ++rr)
    {
      if (rr->getOperation()->getHidden()) continue;
      textoutput << "OPERATION\t" << rr->getOperation() << '\t'
          << rr->getDates().getStart() << '\t'
          << rr->getDates().getEnd() << '\t'
          << rr->getQuantity() << endl;
    }

    // Write the problem summary.
    for (Problem::const_iterator gprob = Problem::begin();
        gprob != Problem::end(); ++gprob)
    {
      textoutput << "PROBLEM\t" << gprob->getType().type << '\t'
          << gprob->getDescription() << '\t'
          << gprob->getDates() << endl;
    }

    // Write the constraint summary
    for (Demand::iterator gdem = Demand::begin();
        gdem != Demand::end(); ++gdem)
    {
      if (!gdem->getHidden())
      {
        for (Problem::const_iterator i = gdem->getConstraints().begin();
            i != gdem->getConstraints().end();
            ++i)
          textoutput << "DEMAND CONSTRAINT\t" << (*gdem) << '\t'
              << i->getDescription() << '\t'
              << i->getDates() << '\t' << endl;
      }
    }

    // Close the output file
    textoutput.close();
  }
  catch (...)
  {
    if (textoutput.is_open())
      textoutput.close();
    Py_BLOCK_THREADS;
    PythonType::evalException();
    return NULL;
  }
  Py_END_ALLOW_THREADS   // Reclaim Python interpreter
  return Py_BuildValue("");
}
예제 #2
0
DECLARE_EXPORT PyObject* printModelSize(PyObject* self, PyObject* args)
{
  // Free Python interpreter for other threads
  Py_BEGIN_ALLOW_THREADS

  // Execute and catch exceptions
  size_t count, memsize;
  try
  {

    // Intro
    logger << endl << "Size information of frePPLe " << PACKAGE_VERSION
        << " (" << __DATE__ << ")" << endl << endl;

    // Print current locale
#if defined(HAVE_SETLOCALE) || defined(_MSC_VER)
    logger << "Locale: " << setlocale(LC_ALL,NULL) << endl << endl;
#else
    logger << endl;
#endif

    // Print loaded modules
    Environment::printModules();

    // Print the number of clusters
    logger << "Clusters: " << HasLevel::getNumberOfClusters()
        << " (hanging: " << HasLevel::getNumberOfHangingClusters() << ")"
        << endl << endl;

    // Header for memory size
    logger << "Memory usage:" << endl;
    logger << "Model        \tNumber\tMemory" << endl;
    logger << "-----        \t------\t------" << endl;

    // Plan
    size_t total = Plan::instance().getSize();
    logger << "Plan         \t1\t"<< Plan::instance().getSize() << endl;

    // Locations
    memsize = 0;
    for (Location::iterator l = Location::begin(); l != Location::end(); ++l)
      memsize += l->getSize();
    logger << "Location     \t" << Location::size() << "\t" << memsize << endl;
    total += memsize;

    // Customers
    memsize = 0;
    for (Customer::iterator c = Customer::begin(); c != Customer::end(); ++c)
      memsize += c->getSize();
    logger << "Customer     \t" << Customer::size() << "\t" << memsize << endl;
    total += memsize;

    // Buffers
    memsize = 0;
    for (Buffer::iterator b = Buffer::begin(); b != Buffer::end(); ++b)
      memsize += b->getSize();
    logger << "Buffer       \t" << Buffer::size() << "\t" << memsize << endl;
    total += memsize;

    // Setup matrices
    memsize = 0;
    for (SetupMatrix::iterator s = SetupMatrix::begin(); s != SetupMatrix::end(); ++s)
      memsize += s->getSize();
    logger << "Setup matrix \t" << SetupMatrix::size() << "\t" << memsize << endl;
    total += memsize;

    // Resources
    memsize = 0;
    for (Resource::iterator r = Resource::begin(); r != Resource::end(); ++r)
      memsize += r->getSize();
    logger << "Resource     \t" << Resource::size() << "\t" << memsize << endl;
    total += memsize;

    // Skills and resourceskills
    size_t countResourceSkills(0), memResourceSkills(0);
    memsize = 0;
    for (Skill::iterator sk = Skill::begin(); sk != Skill::end(); ++sk)
    {
      memsize += sk->getSize();
      for (Skill::resourcelist::const_iterator rs = sk->getResources().begin();
          rs != sk->getResources().end(); ++rs)
      {
        ++countResourceSkills;
        memResourceSkills += rs->getSize();
      }
    }
    logger << "Skill     \t" << Skill::size() << "\t" << memsize << endl;
    logger << "ResourceSkill     \t" << countResourceSkills << "\t" << memResourceSkills << endl;
    total += memsize;

    // Operations, flows and loads
    size_t countFlows(0), memFlows(0), countLoads(0), memLoads(0);
    memsize = 0;
    for (Operation::iterator o = Operation::begin(); o != Operation::end(); ++o)
    {
      memsize += o->getSize();
      for (Operation::flowlist::const_iterator fl = o->getFlows().begin();
          fl != o->getFlows().end(); ++ fl)
      {
        ++countFlows;
        memFlows += fl->getSize();
      }
      for (Operation::loadlist::const_iterator ld = o->getLoads().begin();
          ld != o->getLoads().end(); ++ ld)
      {
        ++countLoads;
        memLoads += ld->getSize();
      }
    }
    logger << "Operation    \t" << Operation::size() << "\t" << memsize << endl;
    logger << "Flow         \t" << countFlows << "\t" << memFlows  << endl;
    logger << "Load         \t" << countLoads << "\t" << memLoads  << endl;
    total += memsize + memFlows + memLoads;

    // Calendars (which includes the buckets)
    memsize = 0;
    for (Calendar::iterator cl = Calendar::begin(); cl != Calendar::end(); ++cl)
      memsize += cl->getSize();
    logger << "Calendar     \t" << Calendar::size() << "\t" << memsize  << endl;
    total += memsize;

    // Items
    memsize = 0;
    for (Item::iterator i = Item::begin(); i != Item::end(); ++i)
      memsize += i->getSize();
    logger << "Item         \t" << Item::size() << "\t" << memsize  << endl;
    total += memsize;

    // Demands
    memsize = 0;
    size_t c_count = 0, c_memsize = 0;
    for (Demand::iterator dm = Demand::begin(); dm != Demand::end(); ++dm)
    {
      memsize += dm->getSize();
      for (Problem::const_iterator cstrnt(dm->getConstraints().begin());
        cstrnt != dm->getConstraints().end(); ++cstrnt)
      {
        ++c_count;
        c_memsize += cstrnt->getSize();
      }
    }
    logger << "Demand       \t" << Demand::size() << "\t" << memsize  << endl;
    logger << "Constraints  \t" << c_count << "\t" << c_memsize  << endl;
    total += memsize + c_memsize;

    // Operationplans
    size_t countloadplans(0), countflowplans(0);
    memsize = count = 0;
    for (OperationPlan::iterator j = OperationPlan::begin();
        j!=OperationPlan::end(); ++j)
    {
      ++count;
      memsize += sizeof(*j);
      countloadplans += j->sizeLoadPlans();
      countflowplans += j->sizeFlowPlans();
    }
    total += memsize;
    logger << "OperationPlan\t" << count << "\t" << memsize << endl;

    // Flowplans
    memsize = countflowplans * sizeof(FlowPlan);
    total +=  memsize;
    logger << "FlowPlan     \t" << countflowplans << "\t" << memsize << endl;

    // Loadplans
    memsize = countloadplans * sizeof(LoadPlan);
    total +=  memsize;
    logger << "LoadPlan     \t" << countloadplans << "\t" << memsize << endl;

    // Problems
    memsize = count = 0;
    for (Problem::const_iterator pr = Problem::begin(); pr!=Problem::end(); ++pr)
    {
      ++count;
      memsize += pr->getSize();
    }
    total += memsize;
    logger << "Problem      \t" << count << "\t" << memsize << endl;

    // TOTAL
    logger << "Total        \t\t" << total << endl << endl;
  }
  catch (...)
  {
    Py_BLOCK_THREADS;
    PythonType::evalException();
    return NULL;
  }
  Py_END_ALLOW_THREADS   // Reclaim Python interpreter
  return Py_BuildValue("");
}
예제 #3
0
DECLARE_EXPORT void SolverMRP::solve(const Demand* l, void* v)
{
  // Set a bookmark at the current command
  SolverMRPdata* data = static_cast<SolverMRPdata*>(v);
  CommandManager::Bookmark* topcommand = data->setBookmark();

  // Create a state stack
  State* mystate = data->state;
  data->push();

  try
  {
    // Call the user exit
    if (userexit_demand) userexit_demand.call(l, PythonData(data->constrainedPlanning));
    short loglevel = data->getSolver()->getLogLevel();

    // Note: This solver method does not push/pop states on the stack.
    // We continue to work on the top element of the stack.

    // Message
    if (loglevel>0)
    {
      logger << "Planning demand '" << l->getName() << "' (" << l->getPriority()
          << ", " << l->getDue() << ", " << l->getQuantity() << ")";
      if (!data->constrainedPlanning || !data->getSolver()->isConstrained())
        logger << " in unconstrained mode";
      logger << endl;
    }

    // Unattach previous delivery operationplans, if required.
    if (data->getSolver()->getErasePreviousFirst())
    {
      // Locked operationplans will NOT be deleted, and a part of the demand can
      // still remain planned.
      const_cast<Demand*>(l)->deleteOperationPlans(false, data);

      // Empty constraint list
      const_cast<Demand*>(l)->getConstraints().clear();
    }

    // Track constraints or not
    data->logConstraints = (getPlanType() == 1);

    // Determine the quantity to be planned and the date for the planning loop
    double plan_qty = l->getQuantity() - l->getPlannedQuantity();
    Date plan_date = l->getDue();
    if (plan_qty < ROUNDING_ERROR || plan_date == Date::infiniteFuture)
    {
      if (loglevel>0) logger << "  Nothing to be planned." << endl;
      data->pop();
      return;
    }
    if (plan_qty < l->getMinShipment()) plan_qty = l->getMinShipment();

    // Temporary values to store the 'best-reply' so far
    double best_q_qty = 0.0, best_a_qty = 0.0;
    Date best_q_date;

    // Select delivery operation
    Operation* deliveryoper = l->getDeliveryOperation();

    // Handle invalid or missing delivery operations
    {
    string problemtext = string("Demand '") + l->getName() + "' has no delivery operation";
    Problem::const_iterator j = Problem::begin(const_cast<Demand*>(l), false);
    while (j!=Problem::end())
    {
      if (&(j->getType()) == ProblemInvalidData::metadata
          && j->getDescription() == problemtext)
        break;
      ++j;
    }
    if (!deliveryoper)
    {
      // Create a problem
      if (j == Problem::end())
      new ProblemInvalidData(const_cast<Demand*>(l), problemtext, "demand",
          l->getDue(), l->getDue(), l->getQuantity());
      // Abort planning of this demand
      throw DataException("Demand '" + l->getName() + "' can't be planned");
    }
    else
      // Remove problem that may already exist
      delete &*j;
    }

    // Planning loop
    do
    {
      // Message
      if (loglevel>0)
        logger << "Demand '" << l << "' asks: "
            << plan_qty << "  " << plan_date << endl;

      // Store the last command in the list, in order to undo the following
      // commands if required.
      CommandManager::Bookmark* topcommand = data->setBookmark();

      // Plan the demand by asking the delivery operation to plan
      double q_qty = plan_qty;
      data->state->curBuffer = NULL;
      data->state->q_qty = plan_qty;
      data->state->q_date = plan_date;
      data->planningDemand = const_cast<Demand*>(l);
      data->state->curDemand = const_cast<Demand*>(l);
      data->state->curOwnerOpplan = NULL;
      deliveryoper->solve(*this,v);
      Date next_date = data->state->a_date;

      if (data->state->a_qty < ROUNDING_ERROR
          && plan_qty > l->getMinShipment() && l->getMinShipment() > 0)
      {
        bool originalLogConstraints = data->logConstraints;
        data->logConstraints = false;
        try
        {
          // The full asked quantity is not possible.
          // Try with the minimum shipment quantity.
          if (loglevel>1)
            logger << "Demand '" << l << "' tries planning minimum quantity " << l->getMinShipment() << endl;
          data->rollback(topcommand);
          data->state->curBuffer = NULL;
          data->state->q_qty = l->getMinShipment();
          data->state->q_date = plan_date;
          data->state->curDemand = const_cast<Demand*>(l);
          deliveryoper->solve(*this,v);
          if (data->state->a_date < next_date)
            next_date = data->state->a_date;
          if (data->state->a_qty > ROUNDING_ERROR)
          {
            // The minimum shipment quantity is feasible.
            // Now try iteratively different quantities to find the best we can do.
            double min_qty = l->getMinShipment();
            double max_qty = plan_qty;
            double delta = fabs(max_qty - min_qty);
            while (delta > data->getSolver()->getIterationAccuracy() * l->getQuantity()
                && delta > data->getSolver()->getIterationThreshold())
            {
              // Note: we're kind of assuming that the demand is an integer value here.
              double new_qty = floor((min_qty + max_qty) / 2);
              if (new_qty == min_qty)
              {
                // Required to avoid an infinite loop on the same value...
                new_qty += 1;
                if (new_qty > max_qty) break;
              }
              if (loglevel>0)
                logger << "Demand '" << l << "' tries planning a different quantity " << new_qty << endl;
              data->rollback(topcommand);
              data->state->curBuffer = NULL;
              data->state->q_qty = new_qty;
              data->state->q_date = plan_date;
              data->state->curDemand = const_cast<Demand*>(l);
              deliveryoper->solve(*this,v);
              if (data->state->a_date < next_date)
                next_date = data->state->a_date;
              if (data->state->a_qty > ROUNDING_ERROR)
                // Too small: new min
                min_qty = new_qty;
              else
                // Too big: new max
                max_qty = new_qty;
              delta = fabs(max_qty - min_qty);
            }
            q_qty = min_qty;  // q_qty is the biggest Q quantity giving a positive reply
            if (data->state->a_qty <= ROUNDING_ERROR)
            {
              if (loglevel>0)
                logger << "Demand '" << l << "' restores plan for quantity " << min_qty << endl;
              // Restore the last feasible plan
              data->rollback(topcommand);
              data->state->curBuffer = NULL;
              data->state->q_qty = min_qty;
              data->state->q_date = plan_date;
              data->state->curDemand = const_cast<Demand*>(l);
              deliveryoper->solve(*this,v);
            }
          }
        }
        catch (...)
        {
          data->logConstraints = originalLogConstraints;
          throw;
        }
        data->logConstraints = originalLogConstraints;
      }

      // Message
      if (loglevel>0)
        logger << "Demand '" << l << "' gets answer: "
            << data->state->a_qty << "  " << next_date << "  "
            << data->state->a_cost << "  " << data->state->a_penalty << endl;

      // Update the date to plan in the next loop
      Date copy_plan_date = plan_date;

      // Compare the planned quantity with the minimum allowed shipment quantity
      // We don't accept the answer in case:
      // 1) Nothing is planned
      // 2) The planned quantity is less than the minimum shipment quantity
      // 3) The remaining quantity after accepting this answer is less than
      //    the minimum quantity.
      if (data->state->a_qty < ROUNDING_ERROR
          || data->state->a_qty + ROUNDING_ERROR < l->getMinShipment()
          || (plan_qty - data->state->a_qty < l->getMinShipment()
              && fabs(plan_qty - data->state->a_qty) > ROUNDING_ERROR))
      {
        if (plan_qty - data->state->a_qty < l->getMinShipment()
            && data->state->a_qty + ROUNDING_ERROR >= l->getMinShipment()
            && data->state->a_qty > best_a_qty )
        {
          // The remaining quantity after accepting this answer is less than
          // the minimum quantity. Therefore, we delay accepting it now, but
          // still keep track of this best offer.
          best_a_qty = data->state->a_qty;
          best_q_qty = q_qty;
          best_q_date = plan_date;
        }

        // Delete operationplans - Undo all changes
        data->rollback(topcommand);

        // Set the ask date for the next pass through the loop
        if (next_date <= copy_plan_date
          || (!data->getSolver()->getAllowSplits() && data->state->a_qty > ROUNDING_ERROR)
          || (data->state->a_qty > ROUNDING_ERROR && plan_qty - data->state->a_qty < l->getMinShipment()
              && plan_qty - data->state->a_qty > ROUNDING_ERROR))
        {
          // Oops, we didn't get a proper answer we can use for the next loop.
          // Print a warning and simply try one day later.
          if (loglevel>0)
            logger << "Warning: Demand '" << l << "': Lazy retry" << endl;
          plan_date = copy_plan_date + data->getSolver()->getLazyDelay();
        }
        else
          // Use the next-date answer from the solver
          plan_date = next_date;
      }
      else
      {
        // Accepting this answer
        if (data->state->a_qty + ROUNDING_ERROR < q_qty)
        {
          // The demand was only partially planned. We need to do a new
          // 'coordinated' planning run.

          // Delete operationplans created in the 'testing round'
          data->rollback(topcommand);

          // Create the correct operationplans
          if (loglevel>=2)
            logger << "Demand '" << l << "' plans coordination." << endl;
          data->getSolver()->setLogLevel(0);
          double tmpresult = 0;
          try
          {
            for(double remainder = data->state->a_qty;
                remainder > ROUNDING_ERROR; remainder -= data->state->a_qty)
            {
              data->state->q_qty = remainder;
              data->state->q_date = copy_plan_date;
              data->state->curDemand = const_cast<Demand*>(l);
              data->state->curBuffer = NULL;
              deliveryoper->solve(*this,v);
              if (data->state->a_qty < ROUNDING_ERROR)
              {
                logger << "Warning: Demand '" << l << "': Failing coordination" << endl;
                break;
              }
              tmpresult += data->state->a_qty;
            }
          }
          catch (...)
          {
            data->getSolver()->setLogLevel(loglevel);
            throw;
          }
          data->getSolver()->setLogLevel(loglevel);
          data->state->a_qty = tmpresult;
          if (tmpresult == 0) break;
        }

        // Register the new operationplans. We need to make sure that the
        // correct execute method is called!
        if (data->getSolver()->getAutocommit())
        {
          data->getSolver()->scanExcess(data);
          data->CommandManager::commit();
        }

        // Update the quantity to plan in the next loop
        plan_qty -= data->state->a_qty;
        best_a_qty = 0.0;  // Reset 'best-answer' remember
      }

    }
    // Repeat while there is still a quantity left to plan and we aren't
    // exceeding the maximum delivery delay.
    while (plan_qty > ROUNDING_ERROR
        && ((data->getSolver()->getPlanType() != 2 && plan_date < l->getDue() + l->getMaxLateness())
            || (data->getSolver()->getPlanType() == 2 && !data->constrainedPlanning && plan_date < l->getDue() + l->getMaxLateness())
            || (data->getSolver()->getPlanType() == 2 && data->constrainedPlanning && plan_date == l->getDue())
           ));

    // Accept the best possible answer.
    // We may have skipped it in the previous loop, awaiting a still better answer
    if (best_a_qty > 0.0 && data->constrainedPlanning)
    {
      if (loglevel>=2) logger << "Demand '" << l << "' accepts a best answer." << endl;
      data->getSolver()->setLogLevel(0);
      try
      {
        for (double remainder = best_q_qty;
          remainder > ROUNDING_ERROR && remainder > l->getMinShipment();
          remainder -= data->state->a_qty)
        {
          data->state->q_qty = remainder;
          data->state->q_date = best_q_date;
          data->state->curDemand = const_cast<Demand*>(l);
          data->state->curBuffer = NULL;
          deliveryoper->solve(*this,v);
          if (data->state->a_qty < ROUNDING_ERROR)
          {
            logger << "Warning: Demand '" << l << "': Failing accepting best answer" << endl;
            break;
          }
        }
        if (data->getSolver()->getAutocommit())
        {
          data->getSolver()->scanExcess(data);
          data->CommandManager::commit();
        }
      }
      catch (...)
      {
        data->getSolver()->setLogLevel(loglevel);
        throw;
      }
      data->getSolver()->setLogLevel(loglevel);
    }

    // Reset the state stack to the position we found it at.
    while (data->state > mystate) data->pop();

  }
  catch (...)
  {
    // Clean up if any exception happened during the planning of the demand
    while (data->state > mystate) data->pop();
    data->rollback(topcommand);
    throw;
  }
}