pegging.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/model.h"
23 
24 namespace frepple
25 {
26 
28 
29 
31 {
32  // Initialize the pegging metadata
33  PeggingIterator::metadata = new MetaCategory("pegging","peggings");
34 
35  // Initialize the Python type
37  x.setName("peggingIterator");
38  x.setDoc("frePPLe iterator for demand pegging");
39  x.supportgetattro();
40  x.supportiter();
41  const_cast<MetaCategory*>(PeggingIterator::metadata)->pythonClass = x.type_object();
42  return x.typeReady();
43 }
44 
45 
47  : downstream(false), firstIteration(true)
48 {
49  // Loop through all delivery operationplans
50  first = false; // ... because the stack is still empty
51  for (Demand::OperationPlan_list::const_iterator opplaniter = d->getDelivery().begin();
52  opplaniter != d->getDelivery().end(); ++opplaniter)
53  followPegging(*opplaniter, 0, (*opplaniter)->getQuantity(), 1.0);
54 
55  // Initialize Python type information
57 }
58 
59 
61 (short l, double q, double f, const FlowPlan* fc, const FlowPlan* fp, bool p)
62 {
63  // Avoid very small pegging quantities
64  if (q < 0.1) return;
65 
66  if (first)
67  {
68  // We can update the current top element of the stack
69  state& t = states.top();
70  t.cons_flowplan = fc;
71  t.prod_flowplan = fp;
72  t.qty = q;
73  t.factor = f;
74  t.level = l;
75  t.pegged = p;
76  first = false;
77  }
78  else
79  // We need to create a new element on the stack
80  states.push(state(l, q, f, fc, fp, p));
81 }
82 
83 
85 {
86  // Validate
87  if (states.empty())
88  throw LogicException("Incrementing the iterator beyond it's end");
89  if (!downstream)
90  throw LogicException("Incrementing a downstream iterator");
91  state& st = states.top();
92 
93  // Handle unconsumed material entries on the stack
94  if (!st.pegged)
95  {
96  states.pop();
97  return *this;
98  }
99 
100  // Mark the top entry in the stack as invalid, so it can be reused
101  first = true;
102 
103  // Take the consuming flowplan and follow the pegging
104  if (st.cons_flowplan)
105  followPegging(st.cons_flowplan->getOperationPlan()->getTopOwner(),
106  st.level-1, st.qty, st.factor);
107 
108  // Pop invalid entries from the stack
109  if (first) states.pop();
110 
111  return *this;
112 }
113 
114 
116 {
117  // Validate
118  if (states.empty())
119  throw LogicException("Incrementing the iterator beyond it's end");
120  if (downstream)
121  throw LogicException("Decrementing an upstream iterator");
122  state& st = states.top();
123 
124  // Handle unconsumed material entries on the stack
125  if (!st.pegged)
126  {
127  states.pop();
128  return *this;
129  }
130 
131  // Mark the top entry in the stack as invalid, so it can be reused
132  first = true;
133 
134  // Take the producing flowplan and follow the pegging
135  if (st.prod_flowplan)
136  followPegging(st.prod_flowplan->getOperationPlan()->getTopOwner(),
137  st.level+1, st.qty, st.factor);
138 
139  // Pop invalid entries from the stack
140  if (first) states.pop();
141 
142  return *this;
143 }
144 
145 
146 DECLARE_EXPORT void PeggingIterator::followPegging
147 (const OperationPlan* op, short nextlevel, double qty, double factor)
148 {
149  // For each flowplan (producing or consuming depending on whether we go
150  // upstream or downstream) ask the buffer to give us the pegged flowplans.
151  bool noFlowPlans = true;
152  if (downstream)
154  i != op->endFlowPlans(); ++i)
155  {
156  // We're interested in producing flowplans of an operationplan when
157  // walking downstream.
158  if (i->getQuantity()>ROUNDING_ERROR)
159  {
160  i->getFlow()->getBuffer()->followPegging(*this, &*i, nextlevel, qty, factor);
161  noFlowPlans = false;
162  }
163  }
164  else
165  for (OperationPlan::FlowPlanIterator i = op->beginFlowPlans();
166  i != op->endFlowPlans(); ++i)
167  {
168  // We're interested in consuming flowplans of an operationplan when
169  // walking upstream.
170  if (i->getQuantity()<-ROUNDING_ERROR)
171  {
172  i->getFlow()->getBuffer()->followPegging(*this, &*i, nextlevel, qty, factor);
173  noFlowPlans = false;
174  }
175  }
176 
177  // Special case: the operationplan doesn't have flowplans
178  // @todo if (noFlowPlans) updateStack(nextlevel, qty, factor, NULL, NULL);
179 
180  // Recursively call this function for all sub-operationplans.
181  for (OperationPlan::iterator j(op); j != OperationPlan::end(); ++j)
182  followPegging(&*j, nextlevel, qty, factor);
183 }
184 
185 
186 DECLARE_EXPORT PyObject* PeggingIterator::iternext()
187 {
188  if (firstIteration)
189  firstIteration = false;
190  else
191  operator--();
192  if (!operator bool()) return NULL;
193  Py_INCREF(this);
194  return static_cast<PyObject*>(this);
195 }
196 
197 
198 DECLARE_EXPORT PyObject* PeggingIterator::getattro(const Attribute& attr)
199 {
200  if (attr.isA(Tags::tag_level))
201  return PythonObject(getLevel());
202  if (attr.isA(Tags::tag_consuming))
204  if (attr.isA(Tags::tag_producing))
206  if (attr.isA(Tags::tag_buffer))
207  return PythonObject(getBuffer());
208  if (attr.isA(Tags::tag_quantity_demand))
210  if (attr.isA(Tags::tag_quantity_buffer))
212  if (attr.isA(Tags::tag_pegged))
213  return PythonObject(getPegged());
214  if (attr.isA(Tags::tag_consuming_date))
215  return PythonObject(getConsumingDate());
216  if (attr.isA(Tags::tag_producing_date))
217  return PythonObject(getProducingDate());
218  return NULL;
219 }
220 
221 
222 } // End namespace