solverflow.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * *
3  * Copyright (C) 2007-2012 by Johan De Taeye, frePPLe bvba *
4  * *
5  * This library is free software; you can redistribute it and/or modify it *
6  * under the terms of the GNU Affero General Public License as published *
7  * by the Free Software Foundation; either version 3 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This library is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU Affero General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU Affero General Public *
16  * License along with this program. *
17  * If not, see <http://www.gnu.org/licenses/>. *
18  * *
19  ***************************************************************************/
20 
21 #define FREPPLE_CORE
22 #include "frepple/solver.h"
23 
24 namespace frepple
25 {
26 
27 bool sortFlow(const Flow* lhs, const Flow* rhs)
28 {
29  return lhs->getPriority() < rhs->getPriority();
30 }
31 
32 
33 DECLARE_EXPORT void SolverMRP::solve(const Flow* fl, void* v) // @todo implement search mode
34 {
35  // Note: This method is only called for consuming flows and for the leading
36  // flow of an alternate group. See SolverMRP::checkOperation
37 
38  SolverMRPdata* data = static_cast<SolverMRPdata*>(v);
39  if (fl->hasAlternates())
40  {
41  // CASE I: It is an alternate flow.
42  // We ask each alternate flow in order of priority till we find a flow
43  // that has a non-zero reply.
44 
45  // 1) collect a list of alternates
46  list<const Flow*> thealternates;
47  const Flow *x = fl->hasAlternates() ? fl : fl->getAlternate();
49  i != fl->getOperation()->getFlows().end(); ++i)
50  if ((i->getAlternate() == x || &*i == x)
51  && i->getEffective().within(data->state->q_flowplan->getDate()))
52  thealternates.push_front(&*i);
53 
54  // 2) Sort the list
55  thealternates.sort(sortFlow);
56 
57  // 3) Control the planning mode
58  bool originalPlanningMode = data->constrainedPlanning;
59  data->constrainedPlanning = true;
60  const Flow *firstAlternate = NULL;
61  double firstQuantity = 0.0;
62 
63  // Remember the top constraint
64  bool originalLogConstraints = data->logConstraints;
65  //Problem* topConstraint = data->planningDemand->getConstraints().top();
66 
67  // 4) Loop through the alternates till we find a non-zero reply
68  Date min_next_date(Date::infiniteFuture);
69  double ask_qty;
70  FlowPlan *flplan = data->state->q_flowplan;
71  for (list<const Flow*>::const_iterator i = thealternates.begin();
72  i != thealternates.end();)
73  {
74  const Flow *curflow = *i;
75  data->state->q_flowplan = flplan; // because q_flowplan can change
76 
77  // 4a) Switch to this flow
78  if (data->state->q_flowplan->getFlow() != curflow)
79  data->state->q_flowplan->setFlow(curflow);
80 
81  // 4b) Call the Python user exit if there is one
82  if (userexit_flow)
83  {
84  PythonObject result = userexit_flow.call(data->state->q_flowplan, PythonObject(data->constrainedPlanning));
85  if (!result.getBool())
86  {
87  // Return value is false, alternate rejected
88  if (data->getSolver()->getLogLevel()>1)
89  logger << indent(curflow->getOperation()->getLevel())
90  << " User exit disallows consumption from '"
91  << (*i)->getBuffer()->getName() << "'" << endl;
92  // Move to the next alternate
93  if (++i != thealternates.end() && data->getSolver()->getLogLevel()>1)
94  logger << indent(curflow->getOperation()->getLevel()) << " Alternate flow switches from '"
95  << curflow->getBuffer()->getName() << "' to '"
96  << (*i)->getBuffer()->getName() << "'" << endl;
97  continue;
98  }
99  }
100 
101  // Remember the first alternate
102  if (!firstAlternate)
103  {
104  firstAlternate = *i;
105  firstQuantity = data->state->q_flowplan->getQuantity();
106  }
107 
108  // Constraint tracking
109  if (*i != firstAlternate)
110  // Only enabled on first alternate
111  data->logConstraints = false;
112  else
113  // Keep track of constraints, if enabled
114  data->logConstraints = originalLogConstraints;
115 
116  // 4c) Ask the buffer
117  data->state->q_qty = ask_qty = - data->state->q_flowplan->getQuantity();
118  data->state->q_date = data->state->q_flowplan->getDate();
119  CommandManager::Bookmark* topcommand = data->setBookmark();
120  curflow->getBuffer()->solve(*this,data);
121 
122  // 4d) A positive reply: exit the loop
123  if (data->state->a_qty > ROUNDING_ERROR)
124  {
125  // Update the opplan, which is required to (1) update the flowplans
126  // and to (2) take care of lot sizing constraints of this operation.
127  if (data->state->a_qty < ask_qty - ROUNDING_ERROR)
128  {
129  flplan->setQuantity(-data->state->a_qty, true);
130  data->state->a_qty = -flplan->getQuantity();
131  }
132  if (data->state->a_qty > ROUNDING_ERROR)
133  {
134  data->constrainedPlanning = originalPlanningMode;
135  data->logConstraints = originalLogConstraints;
136  return;
137  }
138  }
139 
140  // 4e) Undo the plan on the alternate
141  data->rollback(topcommand);
142 
143  // 4f) Prepare for the next alternate
144  if (data->state->a_date < min_next_date)
145  min_next_date = data->state->a_date;
146  if (++i != thealternates.end() && data->getSolver()->getLogLevel()>1)
147  logger << indent(curflow->getOperation()->getLevel()) << " Alternate flow switches from '"
148  << curflow->getBuffer()->getName() << "' to '"
149  << (*i)->getBuffer()->getName() << "'" << endl;
150  }
151 
152  // 5) No reply found, all alternates are infeasible
153  if (!originalPlanningMode)
154  {
155  assert(firstAlternate);
156  // Unconstrained plan: Plan on the primary alternate
157  // Switch to this flow
158  if (flplan->getFlow() != firstAlternate)
159  flplan->setFlow(firstAlternate);
160  // Message
161  if (data->getSolver()->getLogLevel()>1)
162  logger << indent(fl->getOperation()->getLevel())
163  << " Alternate flow plans unconstrained on alternate '"
164  << firstAlternate->getBuffer()->getName() << "'" << endl;
165  // Plan unconstrained
166  data->constrainedPlanning = false;
167  data->state->q_flowplan = flplan; // because q_flowplan can change
168  flplan->setQuantity(firstQuantity, true);
169  data->state->q_qty = ask_qty = - flplan->getQuantity();
170  data->state->q_date = flplan->getDate();
171  firstAlternate->getBuffer()->solve(*this,data);
172  data->state->a_qty = -flplan->getQuantity();
173  // Restore original planning mode
174  data->constrainedPlanning = originalPlanningMode;
175  }
176  else
177  {
178  // Constrained plan: Return 0
179  data->state->a_date = min_next_date;
180  data->state->a_qty = 0;
181  if (data->getSolver()->getLogLevel()>1)
182  logger << indent(fl->getOperation()->getLevel()) <<
183  " Alternate flow doesn't find supply on any alternate : "
184  << data->state->a_qty << " " << data->state->a_date << endl;
185  }
186  }
187  else
188  {
189  // CASE II: Not an alternate flow.
190  // In this case, this method is passing control on to the buffer.
191  data->state->q_qty = - data->state->q_flowplan->getQuantity();
192  data->state->q_date = data->state->q_flowplan->getDate();
193  if (data->state->q_qty != 0.0)
194  {
195  fl->getBuffer()->solve(*this,data);
196  if (data->state->a_date > fl->getEffective().getEnd())
197  {
198  // The reply date must be less than the effectivity end date: after
199  // that date the flow in question won't consume any material any more.
200  if (data->getSolver()->getLogLevel()>1
201  && data->state->a_qty < ROUNDING_ERROR)
202  logger << indent(fl->getBuffer()->getLevel()) << " Buffer '"
203  << fl->getBuffer()->getName() << "' answer date is adjusted to "
204  << fl->getEffective().getEnd()
205  << " because of a date effective flow" << endl;
206  data->state->a_date = fl->getEffective().getEnd();
207  }
208  }
209  else
210  {
211  // It's a zero quantity flowplan.
212  // E.g. because it is not effective.
213  data->state->a_date = data->state->q_date;
214  data->state->a_qty = 0.0;
215  }
216  }
217 }
218 
219 
220 }