CYCAMORE
Loading...
Searching...
No Matches
src/storage.cc
Go to the documentation of this file.
1// storage.cc
2// Implements the Storage class
3#include "storage.h"
4
5namespace cycamore {
6
7// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
8Storage::Storage(cyclus::Context* ctx)
9 : cyclus::Facility(ctx),
10 latitude(0.0),
11 longitude(0.0),
13 inventory_tracker.Init({&inventory, &stocks, &ready, &processing}, cyclus::CY_LARGE_DOUBLE);
14 cyclus::Warn<cyclus::EXPERIMENTAL_WARNING>(
15 "The Storage Facility is experimental.");};
16
17// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
18// pragmas
19
20#pragma cyclus def schema cycamore::Storage
21
22#pragma cyclus def annotations cycamore::Storage
23
24#pragma cyclus def initinv cycamore::Storage
25
26#pragma cyclus def snapshotinv cycamore::Storage
27
28#pragma cyclus def infiletodb cycamore::Storage
29
30#pragma cyclus def snapshot cycamore::Storage
31
32#pragma cyclus def clone cycamore::Storage
33
34//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
35void Storage::InitFrom(Storage* m) {
36#pragma cyclus impl initfromcopy cycamore::Storage
37 cyclus::toolkit::CommodityProducer::Copy(m);
38}
39
40//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
41void Storage::InitFrom(cyclus::QueryableBackend* b) {
42#pragma cyclus impl initfromdb cycamore::Storage
43
44 cyclus::toolkit::Commodity commod = cyclus::toolkit::Commodity(out_commods.front());
45 cyclus::toolkit::CommodityProducer::Add(commod);
46 cyclus::toolkit::CommodityProducer::SetCapacity(commod, throughput);
47}
48
49void Storage::InitBuyPolicyParameters() {
51 if (active_buying_min > active_buying_max) {
52 throw cyclus::ValueError("Active min larger than max.");
53 }
54 if (dormant_buying_min > dormant_buying_max) {
55 throw cyclus::ValueError("Dormant min larger than max.");
56 }
57 if (buying_size_min > buying_size_max) {
58 throw cyclus::ValueError("Buying size min larger than max.");
59 }
60
61 if (active_buying_frequency_type == "Fixed") {
62 active_dist_ = cyclus::FixedIntDist::Ptr (new cyclus::FixedIntDist(active_buying_val));
63 }
64 else if (active_buying_frequency_type == "Uniform") {
65 if ((active_buying_min == -1) || (active_buying_max == -1)) {
66 throw cyclus::ValueError("Invalid active buying frequency range. Please provide both a min and max value.");
67 }
68 active_dist_ = cyclus::UniformIntDist::Ptr (new cyclus::UniformIntDist(active_buying_min, active_buying_max));
69 }
70 else if (active_buying_frequency_type == "Normal") {
71 if ((active_buying_mean == -1) || (active_buying_stddev == -1)) {
72 throw cyclus::ValueError("Invalid active buying frequency range. Please provide both a mean and standard deviation value.");
73 }
74 if (active_buying_min == -1) {active_buying_min = 1;}
75 if (active_buying_max == -1) {
76 active_buying_max = std::numeric_limits<int>::max();}
77
78 active_dist_ = cyclus::NormalIntDist::Ptr (new cyclus::NormalIntDist(active_buying_mean, active_buying_stddev,
79 active_buying_min, active_buying_max));
80 }
81 else if (active_buying_frequency_type == "Binomial") {
82 if (active_buying_end_probability < 0 || active_buying_end_probability > 1) {
83 throw cyclus::ValueError("Active buying end probability must be between 0 and 1");
84 }
85 int success = 1; // only one success is needed to end the active buying period
86 active_dist_ = cyclus::NegativeBinomialIntDist::Ptr (new cyclus::NegativeBinomialIntDist(success, active_buying_end_probability));
87 } else if (active_buying_frequency_type == "FixedWithDisruption") {
88 if (active_buying_disruption < 0) {
89 throw cyclus::ValueError("Disruption must be greater than or equal to 0");
90 }
91 active_dist_ = cyclus::BinaryIntDist::Ptr (
92 new cyclus::BinaryIntDist(active_buying_disruption_probability,
93 active_buying_disruption, active_buying_val));
94 }
95 else {
96 throw cyclus::ValueError("Invalid active buying frequency type");}
97
99 if (dormant_buying_frequency_type == "Fixed") {
100 dormant_dist_ = cyclus::FixedIntDist::Ptr (new cyclus::FixedIntDist(dormant_buying_val));
101 }
102 else if (dormant_buying_frequency_type == "Uniform") {
103 if ((dormant_buying_min == -1) || (dormant_buying_max == -1)) {
104 throw cyclus::ValueError("Invalid dormant buying frequency range. Please provide both a min and max value.");
105 }
106 dormant_dist_ = cyclus::UniformIntDist::Ptr (new cyclus::UniformIntDist(dormant_buying_min, dormant_buying_max));
107 }
108 else if (dormant_buying_frequency_type == "Normal") {
109 if ((dormant_buying_mean == -1) || (dormant_buying_stddev == -1)) {
110 throw cyclus::ValueError("Invalid dormant buying frequency range. Please provide both a mean and standard deviation value.");
111 }
112 if (dormant_buying_min == -1) {dormant_buying_min = 1;}
113 if (dormant_buying_max == -1) {
114 dormant_buying_max = std::numeric_limits<int>::max();}
115 dormant_dist_ = cyclus::NormalIntDist::Ptr (new cyclus::NormalIntDist(dormant_buying_mean, dormant_buying_stddev,
116 dormant_buying_min, dormant_buying_max));
117 }
118 else if (dormant_buying_frequency_type == "Binomial") {
119 if (dormant_buying_end_probability < 0 || dormant_buying_end_probability > 1) {
120 throw cyclus::ValueError("Dormant buying end probability must be between 0 and 1");
121 }
122 int success = 1; // only one success is needed to end the dormant buying period
123 dormant_dist_ = cyclus::NegativeBinomialIntDist::Ptr (new cyclus::NegativeBinomialIntDist(success, dormant_buying_end_probability));
124 } else if (dormant_buying_frequency_type == "FixedWithDisruption") {
125 if (dormant_buying_disruption < 0) {
126 throw cyclus::ValueError("Disruption must be greater than or equal to 0");
127 }
128 dormant_dist_ = cyclus::BinaryIntDist::Ptr (
129 new cyclus::BinaryIntDist(dormant_buying_disruption_probability,
130 dormant_buying_disruption, dormant_buying_val));
131 }
132 else {
133 throw cyclus::ValueError("Invalid dormant buying frequency type");}
134
136 if (buying_size_type == "Fixed") {
137 size_dist_ = cyclus::FixedDoubleDist::Ptr (new cyclus::FixedDoubleDist(buying_size_val));
138 }
139 else if (buying_size_type == "Uniform") {
140 if ((buying_size_min == -1) || (buying_size_max == -1)) {
141 throw cyclus::ValueError("Invalid buying size range. Please provide both a min and max value.");
142 }
143 size_dist_ = cyclus::UniformDoubleDist::Ptr (new cyclus::UniformDoubleDist(buying_size_min, buying_size_max));
144 }
145 else if (buying_size_type == "Normal") {
146 if ((buying_size_mean == -1) || (buying_size_stddev == -1)) {
147 throw cyclus::ValueError("Invalid buying size range. Please provide both a mean and standard deviation value.");
148 }
149 if (buying_size_min == -1) {buying_size_min = 0;}
150 if (buying_size_max == -1) {buying_size_max = 1;}
151 size_dist_ = cyclus::NormalDoubleDist::Ptr (new cyclus::NormalDoubleDist(buying_size_mean, buying_size_stddev,
152 buying_size_min, buying_size_max));
153 }
154 else {
155 throw cyclus::ValueError("Invalid buying size type");}
156}
157
158//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
159void Storage::EnterNotify() {
160 cyclus::Facility::EnterNotify();
161
162 inventory_tracker.set_capacity(max_inv_size);
163 if (reorder_point < 0 && cumulative_cap <= 0) {
164 InitBuyPolicyParameters();
165 buy_policy.Init(this, &inventory, std::string("inventory"),
166 &inventory_tracker, throughput, active_dist_,
167 dormant_dist_, size_dist_);
168 }
169 else if (cumulative_cap > 0) {
170 InitBuyPolicyParameters();
171 buy_policy.Init(this, &inventory, std::string("inventory"),
172 &inventory_tracker, throughput, cumulative_cap,
173 dormant_dist_);
174 }
175 else if (reorder_quantity > 0) {
176 if (reorder_point + reorder_quantity > max_inv_size) {
177 throw cyclus::ValueError(
178 "reorder_point + reorder_quantity must be less than or equal to max_inv_size");
179 }
180 buy_policy.Init(this, &inventory, std::string("inventory"),
181 &inventory_tracker, throughput, "RQ",
182 reorder_quantity, reorder_point);
183 }
184 else {
185 buy_policy.Init(this, &inventory, std::string("inventory"),
186 &inventory_tracker, throughput, "sS",
187 max_inv_size, reorder_point);
188 }
189
190 // dummy comp, use in_recipe if provided
191 cyclus::CompMap v;
192 cyclus::Composition::Ptr comp = cyclus::Composition::CreateFromAtom(v);
193 if (in_recipe != "") {
194 comp = context()->GetRecipe(in_recipe);
195 }
196
197 if (in_commod_prefs.size() == 0) {
198 for (int i = 0; i < in_commods.size(); ++i) {
199 in_commod_prefs.push_back(cyclus::kDefaultPref);
200 }
201 } else if (in_commod_prefs.size() != in_commods.size()) {
202 std::stringstream ss;
203 ss << "in_commod_prefs has " << in_commod_prefs.size()
204 << " values, expected " << in_commods.size();
205 throw cyclus::ValueError(ss.str());
206 }
207
208 for (int i = 0; i != in_commods.size(); ++i) {
209 buy_policy.Set(in_commods[i], comp, in_commod_prefs[i]);
210 }
211 buy_policy.Start();
212
213 std::string package_name_ = context()->GetPackage(package)->name();
214 std::string tu_name_ = context()->GetTransportUnit(transport_unit)->name();
215 if (out_commods.size() == 1) {
216 sell_policy.Init(this, &stocks, std::string("stocks"), cyclus::CY_LARGE_DOUBLE, false,
217 sell_quantity, package_name_, tu_name_)
218 .Set(out_commods.front())
219 .Start();
220
221 } else {
222 std::stringstream ss;
223 ss << "out_commods has " << out_commods.size() << " values, expected 1.";
224 throw cyclus::ValueError(ss.str());
225 }
227}
228
229//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
230std::string Storage::str() {
231 std::stringstream ss;
232 std::string ans, out_str;
233 if (out_commods.size() == 1) {
234 out_str = out_commods.front();
235 } else {
236 out_str = "";
237 }
238 if (cyclus::toolkit::CommodityProducer::Produces(
239 cyclus::toolkit::Commodity(out_str))) {
240 ans = "yes";
241 } else {
242 ans = "no";
243 }
244 ss << cyclus::Facility::str();
245 ss << " has facility parameters {"
246 << "\n"
247 << " Output Commodity = " << out_str << ",\n"
248 << " Residence Time = " << residence_time << ",\n"
249 << " Throughput = " << throughput << ",\n"
250 << " commod producer members: "
251 << " produces " << out_str << "?:" << ans << "'}";
252 return ss.str();
253}
254
255//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
256void Storage::Tick() {
257
258
259 LOG(cyclus::LEV_INFO3, "ComCnv") << prototype() << " is ticking {";
260
261 LOG(cyclus::LEV_INFO5, "ComCnv") << "Processing = " << processing.quantity() << ", ready = " << ready.quantity() << ", stocks = " << stocks.quantity() << " and max inventory = " << max_inv_size;
262
263 LOG(cyclus::LEV_INFO4, "ComCnv") << "current capacity " << max_inv_size << " - " << processing.quantity() << " - " << ready.quantity() << " - " << stocks.quantity() << " = " << current_capacity();
264
265 if (current_capacity() > cyclus::eps_rsrc()) {
266 LOG(cyclus::LEV_INFO4, "ComCnv")
267 << " has capacity for " << current_capacity() << ".";
268 }
269 LOG(cyclus::LEV_INFO3, "ComCnv") << "}";
270}
271
272//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
273void Storage::Tock() {
274 LOG(cyclus::LEV_INFO3, "ComCnv") << prototype() << " is tocking {";
275
276 BeginProcessing_(); // place unprocessed inventory into processing
277
278 LOG(cyclus::LEV_INFO4, "ComCnv") << "processing currently holds " << processing.quantity() << ". ready currently holds " << ready.quantity() << ".";
279
280 if (ready_time() >= 0 || residence_time == 0 && !inventory.empty()) {
281 ReadyMatl_(ready_time()); // place processing into ready
282 }
283
284 LOG(cyclus::LEV_INFO5, "ComCnv") << "Ready now holds " << ready.quantity() << " kg.";
285
286 if (ready.quantity() > throughput) {
287 LOG(cyclus::LEV_INFO5, "ComCnv") << "Up to " << throughput << " kg will be placed in stocks based on throughput limits. ";
288 }
289
290 ProcessMat_(throughput); // place ready into stocks
291
292 std::vector<double>::iterator result;
293 result = std::max_element(in_commod_prefs.begin(), in_commod_prefs.end());
294 int maxindx = std::distance(in_commod_prefs.begin(), result);
295 double demand = 0;
296 demand = current_capacity();
297
298 cyclus::toolkit::RecordTimeSeries<double>("demand"+in_commods[maxindx], this, demand);
299
300 // Multiple commodity tracking is not supported, user can only
301 // provide one value for out_commods, despite it being a vector of strings.
302 cyclus::toolkit::RecordTimeSeries<double>("supply"+out_commods[0], this,
303 stocks.quantity());
304
305 LOG(cyclus::LEV_INFO4, "ComCnv") << "process has "
306 << processing.quantity() << ". Ready has " << ready.quantity() << ". Stocks has " << stocks.quantity() << ".";
307 LOG(cyclus::LEV_INFO3, "ComCnv") << "}";
308}
309
310//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
311void Storage::AddMat_(cyclus::Material::Ptr mat) {
312 LOG(cyclus::LEV_INFO5, "ComCnv") << prototype() << " is initially holding "
313 << inventory.quantity() << " total.";
314
315 try {
316 inventory.Push(mat);
317 } catch (cyclus::Error& e) {
318 e.msg(Agent::InformErrorMsg(e.msg()));
319 throw e;
320 }
321
322 LOG(cyclus::LEV_INFO5, "ComCnv")
323 << prototype() << " added " << mat->quantity()
324 << " of material to its inventory, which is holding "
325 << inventory.quantity() << " total.";
326}
327
328//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
329void Storage::BeginProcessing_() {
330 while (inventory.count() > 0) {
331 try {
332 processing.Push(inventory.Pop());
333 entry_times.push_back(context()->time());
334
335 LOG(cyclus::LEV_DEBUG2, "ComCnv")
336 << "Storage " << prototype()
337 << " added resources to processing at t= " << context()->time();
338 } catch (cyclus::Error& e) {
339 e.msg(Agent::InformErrorMsg(e.msg()));
340 throw e;
341 }
342 }
343}
344
345//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
346void Storage::ProcessMat_(double cap) {
347 if (!ready.empty()) {
348 try {
349 double max_pop = std::min(cap, ready.quantity());
350
351 if (discrete_handling) {
352 if (max_pop == ready.quantity()) {
353 stocks.Push(ready.PopN(ready.count()));
354 } else {
355 double cap_pop = ready.Peek()->quantity();
356 while (cap_pop <= max_pop && !ready.empty()) {
357 stocks.Push(ready.Pop());
358 cap_pop += ready.empty() ? 0 : ready.Peek()->quantity();
359 }
360 }
361 } else {
362 stocks.Push(ready.Pop(max_pop, cyclus::eps_rsrc()));
363 }
364
365 LOG(cyclus::LEV_INFO4, "ComCnv") << "Storage " << prototype()
366 << " moved resources"
367 << " from ready to stocks"
368 << " at t= " << context()->time();
369 } catch (cyclus::Error& e) {
370 e.msg(Agent::InformErrorMsg(e.msg()));
371 throw e;
372 }
373 }
374}
375
376//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
377void Storage::ReadyMatl_(int time) {
378 LOG(cyclus::LEV_INFO5, "ComCnv") << "Placing material into ready";
379
380 int to_ready = 0;
381
382 while (!entry_times.empty() && entry_times.front() <= time) {
383 entry_times.pop_front();
384 ++to_ready;
385 }
386
387 ready.Push(processing.PopN(to_ready));
388}
389
390void Storage::RecordPosition() {
391 std::string specification = this->spec();
392 context()
393 ->NewDatum("AgentPosition")
394 ->AddVal("Spec", specification)
395 ->AddVal("Prototype", this->prototype())
396 ->AddVal("AgentId", id())
397 ->AddVal("Latitude", latitude)
398 ->AddVal("Longitude", longitude)
399 ->Record();
400}
401
402
403// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
404extern "C" cyclus::Agent* ConstructStorage(cyclus::Context* ctx) {
405 return new Storage(ctx);
406}
407
408} // namespace cycamore
Storage(cyclus::Context *ctx)
void RecordPosition()
Records an agent's latitude and longitude to the output db.
cyclus::Agent * ConstructStorage(cyclus::Context *ctx)
cyclus::toolkit::Position coordinates