CYCAMORE
Loading...
Searching...
No Matches
src/fuel_fab_tests.cc
Go to the documentation of this file.
1#include "fuel_fab.h"
2
3#include <gtest/gtest.h>
4#include <sstream>
5#include "cyclus.h"
6
7using pyne::nucname::id;
8using cyclus::Composition;
9using cyclus::CompMap;
10using cyclus::Material;
11using cyclus::QueryResult;
12using cyclus::Cond;
13
14namespace cycamore {
15namespace fuelfabtests {
16
17Composition::Ptr c_uox() {
18 CompMap m;
19 m[id("u235")] = 0.04;
20 m[id("u238")] = 0.96;
21 return Composition::CreateFromMass(m);
22};
23
24Composition::Ptr c_mox() {
25 CompMap m;
26 m[id("u235")] = .7;
27 m[id("u238")] = 100;
28 m[id("pu239")] = 3.3;
29 return Composition::CreateFromMass(m);
30};
31
32Composition::Ptr c_natu() {
33 CompMap m;
34 m[id("u235")] = .007;
35 m[id("u238")] = .993;
36 return Composition::CreateFromMass(m);
37};
38
39Composition::Ptr c_pustream() {
40 CompMap m;
41 m[id("pu239")] = 100;
42 m[id("pu240")] = 10;
43 m[id("pu241")] = 1;
44 m[id("pu242")] = 1;
45 return Composition::CreateFromMass(m);
46};
47
48Composition::Ptr c_pustreamlow() {
49 CompMap m;
50 m[id("pu239")] = 80;
51 m[id("pu240")] = 10;
52 m[id("pu241")] = 1;
53 m[id("pu242")] = 1;
54 return Composition::CreateFromMass(m);
55};
56
57Composition::Ptr c_pustreambad() {
58 CompMap m;
59 m[id("pu239")] = 1;
60 m[id("pu240")] = 10;
61 m[id("pu241")] = 1;
62 m[id("pu242")] = 1;
63 return Composition::CreateFromMass(m);
64};
65
66Composition::Ptr c_water() {
67 CompMap m;
68 m[id("O16")] = 1;
69 m[id("H1")] = 2;
70 return Composition::CreateFromAtom(m);
71};
72
73TEST(FuelFabTests, CosiWeight) {
74 cyclus::Env::SetNucDataPath();
75 CompMap m;
76 m[942390000] = 1;
77 Composition::Ptr c = Composition::CreateFromMass(m);
78 double w = CosiWeight(c, "thermal");
79 EXPECT_DOUBLE_EQ(1.0, w);
80
81 m.clear();
82 m[922380000] = 1;
83 c = Composition::CreateFromMass(m);
84 w = CosiWeight(c, "thermal");
85 EXPECT_DOUBLE_EQ(0.0, w);
86
87 m.clear();
88 m[942390000] = 1;
89 c = Composition::CreateFromMass(m);
90 w = CosiWeight(c, "fission_spectrum_ave");
91 EXPECT_DOUBLE_EQ(1.0, w);
92
93 m.clear();
94 m[922380000] = 1;
95 c = Composition::CreateFromMass(m);
96 w = CosiWeight(c, "fission_spectrum_ave");
97 EXPECT_DOUBLE_EQ(0.0, w);
98
99 m.clear();
100 m[922380000] = 1;
101 m[942390000] = 1;
102 c = Composition::CreateFromAtom(m);
103 w = CosiWeight(c, "thermal");
104 EXPECT_DOUBLE_EQ(0.5, w) << "might be using mass-fractions instead of atom";
105
106 m.clear();
107 m[922380000] = 1;
108 m[942390000] = 1;
109 c = Composition::CreateFromAtom(m);
110 w = CosiWeight(c, "fission_spectrum_ave");
111 EXPECT_DOUBLE_EQ(0.5, w) << "might be using mass-fractions instead of atom";
112
113 m.clear();
114 m[922380000] = 1;
115 m[942390000] = 1;
116 m[922350000] = 1;
117 c = Composition::CreateFromAtom(m);
118 double w_therm = CosiWeight(c, "thermal");
119 double w_fast = CosiWeight(c, "fission_spectrum_ave");
120 EXPECT_GT(w_therm, w_fast);
121}
122
123TEST(FuelFabTests, CosiWeight_Mixed) {
124 cyclus::Env::SetNucDataPath();
125 double w_fill = CosiWeight(c_natu(), "thermal");
126 double w_fiss = CosiWeight(c_pustream(), "thermal");
127 double w_target = CosiWeight(c_uox(), "thermal");
128
129 double fiss_frac = HighFrac(w_fill, w_target, w_fiss);
130 double fill_frac = LowFrac(w_fill, w_target, w_fiss);
131
132 // correct frac's from atom-based into mass-based for mixing
133 fiss_frac = AtomToMassFrac(fiss_frac, c_pustream(), c_natu());
134 fill_frac = AtomToMassFrac(fill_frac, c_natu(), c_pustream());
135
136 Material::Ptr m1 = Material::CreateUntracked(fiss_frac, c_pustream());
137 Material::Ptr m2 = Material::CreateUntracked(fill_frac, c_natu());
138 m1->Absorb(m2);
139 double got = CosiWeight(m1->comp(), "thermal");
140 EXPECT_LT(std::abs((w_target-got)/w_target), 0.00001) << "mixed composition not within 0.001% of target";
141}
142
143TEST(FuelFabTests, HighFrac) {
144 cyclus::Env::SetNucDataPath();
145 double w_fill = CosiWeight(c_natu(), "thermal");
146 double w_fiss = CosiWeight(c_pustream(), "thermal");
147 double w_target = CosiWeight(c_uox(), "thermal");
148
149 EXPECT_THROW(HighFrac(w_fiss, w_target, w_fill), cyclus::ValueError);
150 EXPECT_THROW(HighFrac(w_target, w_fill, w_fiss), cyclus::ValueError);
151 EXPECT_THROW(HighFrac(w_target, w_fiss, w_fill), cyclus::ValueError);
152
153 double f = HighFrac(w_fill, w_target, w_fiss);
154 EXPECT_DOUBLE_EQ((w_target-w_fill)/(w_fiss-w_fill), f);
155}
156
157TEST(FuelFabTests, LowFrac) {
158 cyclus::Env::SetNucDataPath();
159 double w_fill = CosiWeight(c_natu(), "thermal");
160 double w_fiss = CosiWeight(c_pustream(), "thermal");
161 double w_target = CosiWeight(c_uox(), "thermal");
162
163 EXPECT_THROW(LowFrac(w_fiss, w_target, w_fill), cyclus::ValueError);
164 EXPECT_THROW(LowFrac(w_target, w_fill, w_fiss), cyclus::ValueError);
165 EXPECT_THROW(LowFrac(w_target, w_fiss, w_fill), cyclus::ValueError);
166
167 double f = LowFrac(w_fill, w_target, w_fiss);
168 EXPECT_DOUBLE_EQ((w_fiss-w_target)/(w_fiss-w_fill), f);
169}
170
171TEST(FuelFabTests, ValidWeights) {
172 cyclus::Env::SetNucDataPath();
173 double w_fill = CosiWeight(c_natu(), "thermal");
174 double w_fiss = CosiWeight(c_pustream(), "thermal");
175 double w_target = CosiWeight(c_uox(), "thermal");
176
177 EXPECT_EQ(true, ValidWeights(w_fill, w_target, w_fiss));
178 EXPECT_EQ(false, ValidWeights(w_fiss, w_target, w_fill));
179 EXPECT_EQ(false, ValidWeights(w_target, w_fill, w_fiss));
180 EXPECT_EQ(false, ValidWeights(w_fiss, w_fill, w_target));
181 EXPECT_EQ(false, ValidWeights(w_target, w_fiss, w_fill));
182 EXPECT_EQ(false, ValidWeights(w_fill, w_fiss, w_target));
183}
184
185// request (and receive) a specific recipe for fissile stream correctly.
186TEST(FuelFabTests, FissRecipe) {
187 std::string config =
188 "<fill_commods> <val>dummy</val> </fill_commods>"
189 "<fill_recipe>natu</fill_recipe>"
190 "<fill_size>1</fill_size>"
191 ""
192 "<fiss_commods> <val>stream1</val> <val>stream2</val> <val>stream3</val> </fiss_commods>"
193 "<fiss_size>2.5</fiss_size>"
194 "<fiss_recipe>spentuox</fiss_recipe>"
195 ""
196 "<outcommod>dummyout</outcommod>"
197 "<spectrum>thermal</spectrum>"
198 "<throughput>0</throughput>"
199 ;
200
201 int simdur = 1;
202 cyclus::MockSim sim(cyclus::AgentSpec(":cycamore:FuelFab"), config, simdur);
203 sim.AddSource("stream1").Finalize();
204 sim.AddRecipe("spentuox", c_pustream());
205 sim.AddRecipe("natu", c_natu());
206 int id = sim.Run();
207
208 int resid = sim.db().Query("Transactions", NULL).GetVal<int>("ResourceId");
209 CompMap got = sim.GetMaterial(resid)->comp()->mass();
210 CompMap want = c_pustream()->mass();
211 cyclus::compmath::Normalize(&got);
212 cyclus::compmath::Normalize(&want);
213 CompMap::iterator it;
214 for (it = want.begin(); it != want.end(); ++it) {
215 EXPECT_DOUBLE_EQ(it->second, got[it->first]) << "nuclide qty off: " << pyne::nucname::name(it->first);
216 }
217}
218
219// multiple fissile streams can be correctly requested and used as
220// fissile material inventory.
221TEST(FuelFabTests, MultipleFissStreams) {
222 std::string config =
223 "<fill_commods> <val>dummy</val> </fill_commods>"
224 "<fill_recipe>natu</fill_recipe>"
225 "<fill_size>1</fill_size>"
226 ""
227 "<fiss_commods> <val>stream1</val> <val>stream2</val> <val>stream3</val> </fiss_commods>"
228 "<fiss_size>2.5</fiss_size>"
229 ""
230 "<outcommod>dummyout</outcommod>"
231 "<spectrum>thermal</spectrum>"
232 "<throughput>0</throughput>"
233 ;
234
235 int simdur = 1;
236 cyclus::MockSim sim(cyclus::AgentSpec(":cycamore:FuelFab"), config, simdur);
237 sim.AddSource("stream1").recipe("spentuox").capacity(1).Finalize();
238 sim.AddSource("stream2").recipe("spentuox").capacity(1).Finalize();
239 sim.AddSource("stream3").recipe("spentuox").capacity(1).Finalize();
240 sim.AddRecipe("spentuox", c_pustream());
241 sim.AddRecipe("natu", c_natu());
242 int id = sim.Run();
243
244 QueryResult qr = sim.db().Query("Transactions", NULL);
245 EXPECT_EQ(3, qr.rows.size());
246
247 std::vector<Cond> conds;
248 conds.push_back(Cond("Commodity", "==", std::string("stream1")));
249 qr = sim.db().Query("Transactions", &conds);
250 EXPECT_EQ(1, qr.rows.size());
251
252 conds[0] = Cond("Commodity", "==", std::string("stream2"));
253 qr = sim.db().Query("Transactions", &conds);
254 EXPECT_EQ(1, qr.rows.size());
255
256 conds[0] = Cond("Commodity", "==", std::string("stream3"));
257 qr = sim.db().Query("Transactions", &conds);
258 EXPECT_EQ(1, qr.rows.size());
259}
260
261// fissile stream preferences can be specified.
262TEST(FuelFabTests, FissStreamPrefs) {
263 std::string config =
264 "<fill_commods> <val>dummy</val> </fill_commods>"
265 "<fill_recipe>natu</fill_recipe>"
266 "<fill_size>1</fill_size>"
267 ""
268 "<fiss_commods> <val>stream1</val> <val>stream2</val> <val>stream3</val> </fiss_commods>"
269 "<fiss_commod_prefs> <val>1.0</val> <val>0.1</val> <val>2.0</val> </fiss_commod_prefs>"
270 "<fiss_size>1.5</fiss_size>"
271 ""
272 "<outcommod>dummyout</outcommod>"
273 "<spectrum>thermal</spectrum>"
274 "<throughput>0</throughput>"
275 ;
276
277 int simdur = 1;
278 cyclus::MockSim sim(cyclus::AgentSpec(":cycamore:FuelFab"), config, simdur);
279 sim.AddSource("stream1").recipe("spentuox").capacity(1).Finalize();
280 sim.AddSource("stream2").recipe("spentuox").capacity(1).Finalize();
281 sim.AddSource("stream3").recipe("spentuox").capacity(1).Finalize();
282 sim.AddRecipe("spentuox", c_pustream());
283 sim.AddRecipe("natu", c_natu());
284 int id = sim.Run();
285
286 std::vector<Cond> conds;
287 conds.push_back(Cond("Commodity", "==", std::string("stream1")));
288 QueryResult qr = sim.db().Query("Transactions", &conds);
289 Material::Ptr m = sim.GetMaterial(qr.GetVal<int>("ResourceId"));
290 EXPECT_DOUBLE_EQ(0.5, m->quantity());
291
292 conds[0] = Cond("Commodity", "==", std::string("stream2"));
293 qr = sim.db().Query("Transactions", &conds);
294 EXPECT_EQ(0, qr.rows.size());
295
296 conds[0] = Cond("Commodity", "==", std::string("stream3"));
297 qr = sim.db().Query("Transactions", &conds);
298 m = sim.GetMaterial(qr.GetVal<int>("ResourceId"));
299 EXPECT_DOUBLE_EQ(1.0, m->quantity());
300}
301
302// zero throughput must not result in a zero capacity constraint excception.
303TEST(FuelFabTests, ZeroThroughput) {
304 std::string config =
305 "<fill_commods> <val>natu</val> </fill_commods>"
306 "<fill_recipe>natu</fill_recipe>"
307 "<fill_size>3.9</fill_size>"
308 ""
309 "<fiss_commods> <val>spentuox</val> </fiss_commods>"
310 "<fiss_recipe>spentuox</fiss_recipe>"
311 "<fiss_size>3.5</fiss_size>"
312 ""
313 "<topup_commod>uox</topup_commod>"
314 "<topup_recipe>uox</topup_recipe>"
315 "<topup_size>3.3</topup_size>"
316 ""
317 "<outcommod>dummyout</outcommod>"
318 "<spectrum>thermal</spectrum>"
319 "<throughput>0</throughput>"
320 ;
321
322 int simdur = 10;
323 cyclus::MockSim sim(cyclus::AgentSpec(":cycamore:FuelFab"), config, simdur);
324 sim.AddSource("uox").capacity(1).Finalize();
325 sim.AddSource("spentuox").capacity(1).Finalize();
326 sim.AddSource("natu").capacity(1).Finalize();
327 sim.AddRecipe("uox", c_uox());
328 sim.AddRecipe("spentuox", c_pustream());
329 sim.AddRecipe("natu", c_natu());
330 EXPECT_NO_THROW(sim.Run());
331}
332
333// fill, fiss, and topup inventories are all requested for and
334// filled as expected. Inventory size constraints are properly
335// enforced after they are full.
336TEST(FuelFabTests, FillAllInventories) {
337 std::string config =
338 "<fill_commods> <val>natu</val> </fill_commods>"
339 "<fill_recipe>natu</fill_recipe>"
340 "<fill_size>3.9</fill_size>"
341 ""
342 "<fiss_commods> <val>spentuox</val> </fiss_commods>"
343 "<fiss_recipe>spentuox</fiss_recipe>"
344 "<fiss_size>3.5</fiss_size>"
345 ""
346 "<topup_commod>uox</topup_commod>"
347 "<topup_recipe>uox</topup_recipe>"
348 "<topup_size>3.3</topup_size>"
349 ""
350 "<outcommod>dummyout</outcommod>"
351 "<spectrum>thermal</spectrum>"
352 "<throughput>1</throughput>"
353 ;
354
355 int simdur = 10;
356 cyclus::MockSim sim(cyclus::AgentSpec(":cycamore:FuelFab"), config, simdur);
357 sim.AddSource("uox").capacity(1).Finalize();
358 sim.AddSource("spentuox").capacity(1).Finalize();
359 sim.AddSource("natu").capacity(1).Finalize();
360 sim.AddRecipe("uox", c_uox());
361 sim.AddRecipe("spentuox", c_pustream());
362 sim.AddRecipe("natu", c_natu());
363 int id = sim.Run();
364
365 cyclus::SqlStatement::Ptr stmt = sim.db().db().Prepare(
366 "SELECT SUM(r.Quantity) FROM Transactions AS t"
367 " INNER JOIN Resources AS r ON r.ResourceId = t.ResourceId"
368 " WHERE t.Commodity = ?;"
369 );
370
371 stmt->BindText(1, "natu");
372 stmt->Step();
373 EXPECT_DOUBLE_EQ(3.9, stmt->GetDouble(0));
374 stmt->Reset();
375 stmt->BindText(1, "spentuox");
376 stmt->Step();
377 EXPECT_DOUBLE_EQ(3.5, stmt->GetDouble(0));
378 stmt->Reset();
379 stmt->BindText(1, "uox");
380 stmt->Step();
381 EXPECT_DOUBLE_EQ(3.3, stmt->GetDouble(0));
382}
383
384// Meet a request requiring zero fill inventory when we have zero fill
385// inventory quantity.
386TEST(FuelFabTests, ProvideStraightFiss_WithZeroFill) {
387 std::string config =
388 "<fill_commods> <val>nothing</val> </fill_commods>"
389 "<fill_recipe>natu</fill_recipe>"
390 "<fill_size>100</fill_size>"
391 ""
392 "<fiss_commods> <val>anything</val> </fiss_commods>"
393 "<fiss_recipe>spentuox</fiss_recipe>"
394 "<fiss_size>100</fiss_size>"
395 ""
396 "<outcommod>recyclefuel</outcommod>"
397 "<spectrum>thermal</spectrum>"
398 "<throughput>100</throughput>"
399 ;
400 int simdur = 6;
401 cyclus::MockSim sim(cyclus::AgentSpec(":cycamore:FuelFab"), config, simdur);
402 sim.AddSource("anything").Finalize();
403 sim.AddSink("recyclefuel").recipe("spentuox").capacity(100).Finalize();
404 sim.AddRecipe("uox", c_uox());
405 sim.AddRecipe("spentuox", c_pustream());
406 sim.AddRecipe("natu", c_natu());
407 int id = sim.Run();
408
409 std::vector<Cond> conds;
410 conds.push_back(Cond("Commodity", "==", std::string("recyclefuel")));
411 QueryResult qr = sim.db().Query("Transactions", NULL);
412 // 6 = 3 receives of inventory, 3 sells of recycled fuel
413 EXPECT_EQ(6, qr.rows.size());
414}
415
416TEST(FuelFabTests, ProvideStraightFill_ZeroFiss) {
417 std::string config =
418 "<fill_commods> <val>anything</val> </fill_commods>"
419 "<fill_recipe>natu</fill_recipe>"
420 "<fill_size>100</fill_size>"
421 ""
422 "<fiss_commods> <val>nothing</val> </fiss_commods>"
423 "<fiss_recipe>spentuox</fiss_recipe>"
424 "<fiss_size>100</fiss_size>"
425 ""
426 "<outcommod>recyclefuel</outcommod>"
427 "<spectrum>thermal</spectrum>"
428 "<throughput>100</throughput>"
429 ;
430 int simdur = 6;
431 cyclus::MockSim sim(cyclus::AgentSpec(":cycamore:FuelFab"), config, simdur);
432 sim.AddSource("anything").Finalize();
433 sim.AddSink("recyclefuel").recipe("natu").capacity(100).Finalize();
434 sim.AddRecipe("uox", c_uox());
435 sim.AddRecipe("spentuox", c_pustream());
436 sim.AddRecipe("natu", c_natu());
437 int id = sim.Run();
438
439 std::vector<Cond> conds;
440 conds.push_back(Cond("Commodity", "==", std::string("recyclefuel")));
441 QueryResult qr = sim.db().Query("Transactions", NULL);
442 // 6 = 3 receives of inventory, 3 sells of recycled fuel
443 EXPECT_EQ(6, qr.rows.size());
444}
445
446// throughput is properly restricted when faced with many fuel
447// requests and with ample material inventory.
448TEST(FuelFabTests, ThroughputLimit) {
449 std::string config =
450 "<fill_commods> <val>anything</val> </fill_commods>"
451 "<fill_recipe>natu</fill_recipe>"
452 "<fill_size>100</fill_size>"
453 ""
454 "<fiss_commods> <val>anything</val> </fiss_commods>"
455 "<fiss_recipe>pustream</fiss_recipe>"
456 "<fiss_size>100</fiss_size>"
457 ""
458 "<outcommod>recyclefuel</outcommod>"
459 "<spectrum>thermal</spectrum>"
460 "<throughput>3</throughput>"
461 ;
462 double throughput = 3;
463
464 int simdur = 5;
465 cyclus::MockSim sim(cyclus::AgentSpec(":cycamore:FuelFab"), config, simdur);
466 sim.AddSource("anything").lifetime(1).Finalize();
467 sim.AddSink("recyclefuel").recipe("uox").capacity(2*throughput).Finalize();
468 sim.AddRecipe("uox", c_uox());
469 sim.AddRecipe("pustream", c_pustream());
470 sim.AddRecipe("natu", c_natu());
471 int id = sim.Run();
472
473 QueryResult qr = sim.db().Query("Transactions", NULL);
474 EXPECT_LT(2, qr.rows.size());
475
476 cyclus::SqlStatement::Ptr stmt = sim.db().db().Prepare(
477 "SELECT SUM(r.Quantity) FROM Transactions AS t"
478 " INNER JOIN Resources AS r ON r.ResourceId = t.ResourceId"
479 " WHERE t.Commodity = 'recyclefuel';"
480 );
481
482 stmt->Step();
483 EXPECT_DOUBLE_EQ(throughput * (simdur-1), stmt->GetDouble(0));
484
485 stmt = sim.db().db().Prepare(
486 "SELECT COUNT(*) FROM Transactions WHERE Commodity = 'recyclefuel';"
487 );
488
489 stmt->Step();
490 EXPECT_DOUBLE_EQ(simdur-1, stmt->GetDouble(0));
491}
492
493// supplied fuel has proper equivalence weights as requested.
494TEST(FuelFabTests, CorrectMixing) {
495 std::string config =
496 "<fill_commods> <val>natu</val> </fill_commods>"
497 "<fill_recipe>natu</fill_recipe>"
498 "<fill_size>100</fill_size>"
499 ""
500 "<fiss_commods> <val>pustream</val> </fiss_commods>"
501 "<fiss_recipe>pustream</fiss_recipe>"
502 "<fiss_size>100</fiss_size>"
503 ""
504 "<outcommod>recyclefuel</outcommod>"
505 "<spectrum>thermal</spectrum>"
506 "<throughput>100</throughput>"
507 ;
508 int simdur = 3;
509 cyclus::MockSim sim(cyclus::AgentSpec(":cycamore:FuelFab"), config, simdur);
510 sim.AddSource("pustream").Finalize();
511 sim.AddSource("natu").Finalize();
512 sim.AddSink("recyclefuel").recipe("uox").capacity(10).lifetime(2).Finalize();
513 sim.AddRecipe("uox", c_uox());
514 sim.AddRecipe("pustream", c_pustream());
515 sim.AddRecipe("natu", c_natu());
516 int id = sim.Run();
517
518 std::vector<Cond> conds;
519 conds.push_back(Cond("Commodity", "==", std::string("recyclefuel")));
520 QueryResult qr = sim.db().Query("Transactions", &conds);
521 EXPECT_EQ(1, qr.rows.size());
522
523 Material::Ptr m = sim.GetMaterial(qr.GetVal<int>("ResourceId"));
524 double got = CosiWeight(m->comp(), "thermal");
525 double w_target = CosiWeight(c_uox(), "thermal");
526 EXPECT_LT(std::abs((w_target-got)/w_target), 0.00001) << "mixed composition not within 0.001% of target";
527
528 conds.push_back(Cond("Time", "==", 2));
529
530 // TODO: do hand calcs to verify expected vals below - they are just regression values currently.
531 conds[0] = Cond("Commodity", "==", std::string("natu"));
532 qr = sim.db().Query("Transactions", &conds);
533 m = sim.GetMaterial(qr.GetVal<int>("ResourceId"));
534 EXPECT_NEAR(9.7463873197, m->quantity(), cyclus::CY_NEAR_ZERO) << "mixed wrong amount of Nat. U stream";
535
536 conds[0] = Cond("Commodity", "==", std::string("pustream"));
537 qr = sim.db().Query("Transactions", &conds);
538 m = sim.GetMaterial(qr.GetVal<int>("ResourceId"));
539 EXPECT_NEAR(0.25361268029, m->quantity(), cyclus::CY_NEAR_ZERO) << "mixed wrong amount of Pu stream";
540}
541
542// fuel is requested requiring more filler than is available with plenty of
543// fissile.
544TEST(FuelFabTests, FillConstrained) {
545 cyclus::Env::SetNucDataPath();
546 std::string config =
547 "<fill_commods> <val>natu</val> </fill_commods>"
548 "<fill_recipe>natu</fill_recipe>"
549 "<fill_size>1</fill_size>"
550 ""
551 "<fiss_commods> <val>pustream</val> </fiss_commods>"
552 "<fiss_recipe>pustream</fiss_recipe>"
553 "<fiss_size>10000</fiss_size>"
554 ""
555 "<outcommod>recyclefuel</outcommod>"
556 "<spectrum>thermal</spectrum>"
557 "<throughput>10000</throughput>"
558 ;
559 double fillinv = 1;
560 int simdur = 2;
561
562 double w_fill = CosiWeight(c_natu(), "thermal");
563 double w_fiss = CosiWeight(c_pustream(), "thermal");
564 double w_target = CosiWeight(c_uox(), "thermal");
565 double fiss_frac = HighFrac(w_fill, w_target, w_fiss);
566 double fill_frac = LowFrac(w_fill, w_target, w_fiss);
567 fiss_frac = AtomToMassFrac(fiss_frac, c_pustream(), c_natu());
568 fill_frac = AtomToMassFrac(fill_frac, c_natu(), c_pustream());
569 double max_provide = fillinv / fill_frac;
570
571 cyclus::MockSim sim(cyclus::AgentSpec(":cycamore:FuelFab"), config, simdur);
572 sim.AddSource("pustream").lifetime(1).Finalize();
573 sim.AddSource("natu").lifetime(1).Finalize();
574 sim.AddSink("recyclefuel").recipe("uox").capacity(2 * max_provide).Finalize();
575 sim.AddRecipe("uox", c_uox());
576 sim.AddRecipe("pustream", c_pustream());
577 sim.AddRecipe("natu", c_natu());
578 int id = sim.Run();
579
580 std::vector<Cond> conds;
581 conds.push_back(Cond("Commodity", "==", std::string("recyclefuel")));
582 QueryResult qr = sim.db().Query("Transactions", &conds);
583 Material::Ptr m = sim.GetMaterial(qr.GetVal<int>("ResourceId"));
584
585 EXPECT_NEAR(max_provide, m->quantity(), cyclus::CY_NEAR_ZERO) << "matched trade uses more fill than available";
586}
587
588// fuel is requested requiring more fissile material than is available with
589// plenty of filler.
590TEST(FuelFabTests, FissConstrained) {
591 cyclus::Env::SetNucDataPath();
592 std::string config =
593 "<fill_commods> <val>natu</val> </fill_commods>"
594 "<fill_recipe>natu</fill_recipe>"
595 "<fill_size>10000</fill_size>"
596 ""
597 "<fiss_commods> <val>pustream</val> </fiss_commods>"
598 "<fiss_recipe>pustream</fiss_recipe>"
599 "<fiss_size>1</fiss_size>"
600 ""
601 "<outcommod>recyclefuel</outcommod>"
602 "<spectrum>thermal</spectrum>"
603 "<throughput>10000</throughput>"
604 ;
605 double fissinv = 1;
606 int simdur = 2;
607
608 double w_fill = CosiWeight(c_natu(), "thermal");
609 double w_fiss = CosiWeight(c_pustream(), "thermal");
610 double w_target = CosiWeight(c_uox(), "thermal");
611 double fiss_frac = HighFrac(w_fill, w_target, w_fiss);
612 double fill_frac = LowFrac(w_fill, w_target, w_fiss);
613 fiss_frac = AtomToMassFrac(fiss_frac, c_pustream(), c_natu());
614 fill_frac = AtomToMassFrac(fill_frac, c_natu(), c_pustream());
615 double max_provide = fissinv / fiss_frac;
616
617 cyclus::MockSim sim(cyclus::AgentSpec(":cycamore:FuelFab"), config, simdur);
618 sim.AddSource("pustream").lifetime(1).Finalize();
619 sim.AddSource("natu").lifetime(1).Finalize();
620 sim.AddSink("recyclefuel").recipe("uox").capacity(2 * max_provide).Finalize();
621 sim.AddRecipe("uox", c_uox());
622 sim.AddRecipe("pustream", c_pustream());
623 sim.AddRecipe("natu", c_natu());
624 int id = sim.Run();
625
626 std::vector<Cond> conds;
627 conds.push_back(Cond("Commodity", "==", std::string("recyclefuel")));
628 QueryResult qr = sim.db().Query("Transactions", &conds);
629 Material::Ptr m = sim.GetMaterial(qr.GetVal<int>("ResourceId"));
630
631 EXPECT_NEAR(max_provide, m->quantity(), cyclus::CY_NEAR_ZERO) << "matched trade uses more fill than available";
632}
633
634// swap to topup inventory because fissile has too low reactivity.
635TEST(FuelFabTests, SwapTopup) {
636 std::string config =
637 "<fill_commods> <val>natu</val> </fill_commods>"
638 "<fill_recipe>natu</fill_recipe>"
639 "<fill_size>10000</fill_size>"
640 ""
641 "<fiss_commods> <val>pustreambad</val> </fiss_commods>"
642 "<fiss_recipe>pustreambad</fiss_recipe>"
643 "<fiss_size>10000</fiss_size>"
644 ""
645 "<topup_commod>pustream</topup_commod>"
646 "<topup_recipe>pustream</topup_recipe>"
647 "<topup_size>10000</topup_size>"
648 ""
649 "<outcommod>recyclefuel</outcommod>"
650 "<spectrum>thermal</spectrum>"
651 "<throughput>10000</throughput>"
652 ;
653 int simdur = 3;
654 double sink_cap = 10;
655
656 cyclus::MockSim sim(cyclus::AgentSpec(":cycamore:FuelFab"), config, simdur);
657 sim.AddSource("pustream").Finalize();
658 sim.AddSource("pustreambad").Finalize();
659 sim.AddSource("natu").Finalize();
660 sim.AddSink("recyclefuel").recipe("uox").capacity(sink_cap).lifetime(2).Finalize();
661 sim.AddRecipe("uox", c_uox());
662 sim.AddRecipe("pustream", c_pustream());
663 sim.AddRecipe("pustreambad", c_pustreambad());
664 sim.AddRecipe("natu", c_natu());
665 int id = sim.Run();
666
667 std::vector<Cond> conds;
668 conds.push_back(Cond("Commodity", "==", std::string("recyclefuel")));
669 QueryResult qr = sim.db().Query("Transactions", &conds);
670 ASSERT_EQ(1, qr.rows.size()) << "failed to meet fuel request";
671 Material::Ptr m = sim.GetMaterial(qr.GetVal<int>("ResourceId"));
672 EXPECT_NEAR(sink_cap, m->quantity(), cyclus::CY_NEAR_ZERO) << "supplied fuel was constrained too much";
673
674 conds[0] = Cond("Commodity", "==", std::string("natu"));
675 conds.push_back(Cond("Time", "==", 2));
676 qr = sim.db().Query("Transactions", &conds);
677 ASSERT_EQ(0, qr.rows.size()) << "failed to swith to topup - used fill - uh oh";
678
679 conds[0] = Cond("Commodity", "==", std::string("pustream"));
680 conds.push_back(Cond("Time", "==", 2));
681 qr = sim.db().Query("Transactions", &conds);
682 ASSERT_EQ(1, qr.rows.size()) << "didn't get more topup after supposedly using it - why?";
683}
684
685TEST(FuelFabTests, SwapTopup_ZeroFill) {
686 std::string config =
687 "<fill_commods> <val>natu</val> </fill_commods>"
688 "<fill_recipe>natu</fill_recipe>"
689 "<fill_size>0</fill_size>"
690 ""
691 "<fiss_commods> <val>pustreambad</val> </fiss_commods>"
692 "<fiss_recipe>pustreambad</fiss_recipe>"
693 "<fiss_size>10000</fiss_size>"
694 ""
695 "<topup_commod>pustream</topup_commod>"
696 "<topup_recipe>pustream</topup_recipe>"
697 "<topup_size>10000</topup_size>"
698 ""
699 "<outcommod>recyclefuel</outcommod>"
700 "<spectrum>thermal</spectrum>"
701 "<throughput>10000</throughput>"
702 ;
703 int simdur = 3;
704 double sink_cap = 10;
705
706 cyclus::MockSim sim(cyclus::AgentSpec(":cycamore:FuelFab"), config, simdur);
707 sim.AddSource("pustream").Finalize();
708 sim.AddSource("pustreambad").Finalize();
709 sim.AddSource("natu").Finalize();
710 sim.AddSink("recyclefuel").recipe("uox").capacity(sink_cap).lifetime(2).Finalize();
711 sim.AddRecipe("uox", c_uox());
712 sim.AddRecipe("pustream", c_pustream());
713 sim.AddRecipe("pustreambad", c_pustreambad());
714 sim.AddRecipe("natu", c_natu());
715 int id = sim.Run();
716
717 std::vector<Cond> conds;
718 conds.push_back(Cond("Commodity", "==", std::string("recyclefuel")));
719 QueryResult qr = sim.db().Query("Transactions", &conds);
720 ASSERT_EQ(1, qr.rows.size()) << "failed to meet fuel request";
721 Material::Ptr m = sim.GetMaterial(qr.GetVal<int>("ResourceId"));
722 EXPECT_NEAR(sink_cap, m->quantity(), cyclus::CY_NEAR_ZERO) << "supplied fuel was constrained too much";
723
724 conds[0] = Cond("Commodity", "==", std::string("pustream"));
725 conds.push_back(Cond("Time", "==", 2));
726 qr = sim.db().Query("Transactions", &conds);
727 ASSERT_EQ(1, qr.rows.size()) << "failed to use topup - why?";
728
729 conds[0] = Cond("Commodity", "==", std::string("pustreambad"));
730 conds.push_back(Cond("Time", "==", 2));
731 qr = sim.db().Query("Transactions", &conds);
732 ASSERT_EQ(1, qr.rows.size()) << "failed to use fiss - why?";
733}
734
735// swap to topup inventory but are limited by topup inventory quantity. This
736// test makes sure the provided fuel is much less than requested due to a
737// small topup inventory despite a surplus of fill that can't be used due to
738// the fiss stream not having a high enough weight (so we must use topup with
739// fiss).
740TEST(FuelFabTests, SwapTopup_TopupConstrained) {
741 cyclus::Env::SetNucDataPath();
742 std::string config =
743 "<fill_commods> <val>natu</val> </fill_commods>"
744 "<fill_recipe>natu</fill_recipe>"
745 "<fill_size>10000</fill_size>"
746 ""
747 "<fiss_commods> <val>pustreambad</val> </fiss_commods>"
748 "<fiss_recipe>pustreambad</fiss_recipe>"
749 "<fiss_size>10000</fiss_size>"
750 ""
751 "<topup_commod>pustream</topup_commod>"
752 "<topup_recipe>pustream</topup_recipe>"
753 "<topup_size>1</topup_size>"
754 ""
755 "<outcommod>recyclefuel</outcommod>"
756 "<spectrum>thermal</spectrum>"
757 "<throughput>10000</throughput>"
758 ;
759 double topupinv = 1;
760 int simdur = 2;
761
762 // compute max fuel mass providable in a single time step
763 double w_fiss = CosiWeight(c_pustreambad(), "thermal");
764 double w_topup = CosiWeight(c_pustream(), "thermal");
765 double w_target = CosiWeight(c_uox(), "thermal");
766 double topup_frac = HighFrac(w_fiss, w_target, w_topup);
767 double fiss_frac = LowFrac(w_fiss, w_target, w_topup);
768 fiss_frac = AtomToMassFrac(fiss_frac, c_pustreambad(), c_pustream());
769 topup_frac = AtomToMassFrac(topup_frac, c_pustream(), c_pustreambad());
770 double max_provide = topupinv / topup_frac;
771
772 cyclus::MockSim sim(cyclus::AgentSpec(":cycamore:FuelFab"), config, simdur);
773 sim.AddSource("pustream").Finalize();
774 sim.AddSource("pustreambad").Finalize();
775 sim.AddSource("natu").Finalize();
776 sim.AddSink("recyclefuel").recipe("uox").capacity(2 * max_provide).Finalize();
777 sim.AddRecipe("uox", c_uox());
778 sim.AddRecipe("pustream", c_pustream());
779 sim.AddRecipe("pustreambad", c_pustreambad());
780 sim.AddRecipe("natu", c_natu());
781 int id = sim.Run();
782
783 std::vector<Cond> conds;
784 conds.push_back(Cond("Commodity", "==", std::string("recyclefuel")));
785 QueryResult qr = sim.db().Query("Transactions", &conds);
786 ASSERT_EQ(1, qr.rows.size()) << "failed to meet fuel request";
787 Material::Ptr m = sim.GetMaterial(qr.GetVal<int>("ResourceId"));
788
789 EXPECT_NEAR(max_provide, m->quantity(), cyclus::CY_NEAR_ZERO) << "matched trade uses more fiss than available";
790}
791
792// swap to topup inventory but are limited by fiss inventory quantity. This
793// test makes sure the provided fuel is much less than requested due to a
794// small fiss inventory.
795TEST(FuelFabTests, SwapTopup_FissConstrained) {
796 cyclus::Env::SetNucDataPath();
797 std::string config =
798 "<fill_commods> <val>natu</val> </fill_commods>"
799 "<fill_recipe>natu</fill_recipe>"
800 "<fill_size>0</fill_size>"
801 ""
802 "<fiss_commods> <val>pustreambad</val> </fiss_commods>"
803 "<fiss_recipe>pustreambad</fiss_recipe>"
804 "<fiss_size>1</fiss_size>"
805 ""
806 "<topup_commod>pustream</topup_commod>"
807 "<topup_recipe>pustream</topup_recipe>"
808 "<topup_size>10000</topup_size>"
809 ""
810 "<outcommod>recyclefuel</outcommod>"
811 "<spectrum>thermal</spectrum>"
812 "<throughput>10000</throughput>"
813 ;
814 double fissinv = 1;
815 int simdur = 2;
816
817 // compute max fuel mass providable in a single time step
818 double w_fiss = CosiWeight(c_pustreambad(), "thermal");
819 double w_topup = CosiWeight(c_pustream(), "thermal");
820 double w_target = CosiWeight(c_uox(), "thermal");
821 double topup_frac = HighFrac(w_fiss, w_target, w_topup);
822 double fiss_frac = LowFrac(w_fiss, w_target, w_topup);
823 fiss_frac = AtomToMassFrac(fiss_frac, c_pustreambad(), c_pustream());
824 topup_frac = AtomToMassFrac(topup_frac, c_pustream(), c_pustreambad());
825 double max_provide = fissinv / fiss_frac;
826
827 cyclus::MockSim sim(cyclus::AgentSpec(":cycamore:FuelFab"), config, simdur);
828 sim.AddSource("pustream").Finalize();
829 sim.AddSource("pustreambad").Finalize();
830 sim.AddSource("natu").Finalize();
831 sim.AddSink("recyclefuel").recipe("uox").capacity(2 * max_provide).Finalize();
832 sim.AddRecipe("uox", c_uox());
833 sim.AddRecipe("pustream", c_pustream());
834 sim.AddRecipe("pustreambad", c_pustreambad());
835 sim.AddRecipe("natu", c_natu());
836 int id = sim.Run();
837
838 std::vector<Cond> conds;
839 conds.push_back(Cond("Commodity", "==", std::string("recyclefuel")));
840 QueryResult qr = sim.db().Query("Transactions", &conds);
841 ASSERT_EQ(1, qr.rows.size()) << "failed to meet fuel request";
842 Material::Ptr m = sim.GetMaterial(qr.GetVal<int>("ResourceId"));
843
844 EXPECT_NEAR(max_provide, m->quantity(), cyclus::CY_NEAR_ZERO) << "matched trade uses more fiss than available";
845}
846
847// Before this test and a fix, the fuel fab (partially) assumed each entire material
848// buffer had the same composition as the material on top of the buffer when
849// calculating stream mixing ratios for material to supply. This problem was
850// compounded by the fact that material weights are computed on an atom basis
851// and mixing is done on a mass basis - corresponding conversions resulted in
852// the fab being matched for more than it could actually supply - due to
853// thinking it had an inventory of higher quality material than was actually
854// the case. This test makes sure that doesn't happen again.
855TEST(FuelFabTests, HomogenousBuffers) {
856 std::string config =
857 "<fill_commods> <val>natu</val> </fill_commods>"
858 "<fill_recipe>natu</fill_recipe>"
859 "<fill_size>40</fill_size>"
860 ""
861 "<fiss_commods> <val>stream1</val> </fiss_commods>"
862 "<fiss_size>4</fiss_size>"
863 "<fiss_recipe>spentuox</fiss_recipe>"
864 ""
865 "<outcommod>out</outcommod>"
866 "<spectrum>thermal</spectrum>"
867 "<throughput>1e10</throughput>"
868 ;
869
870 CompMap m;
871 m[id("u235")] = 7;
872 m[id("u238")] = 86;
873 // the zr90 is important to force the atom-mass basis conversion to push the
874 // dre to overmatch in the direction we want.
875 m[id("zr90")] = 7;
876 Composition::Ptr c = Composition::CreateFromMass(m);
877
878 int simdur = 5;
879 cyclus::MockSim sim(cyclus::AgentSpec(":cycamore:FuelFab"), config, simdur);
880 sim.AddSource("stream1").start(0).lifetime(1).capacity(.01).recipe("special").Finalize();
881 sim.AddSource("stream1").start(1).lifetime(1).capacity(3.98).recipe("natu").Finalize();
882 sim.AddSource("natu").lifetime(1).Finalize();
883 sim.AddSink("out").start(2).capacity(4).lifetime(1).recipe("uox").Finalize();
884 sim.AddSink("out").start(2).capacity(4).lifetime(1).recipe("uox").Finalize();
885 sim.AddRecipe("uox", c_uox());
886 sim.AddRecipe("spentuox", c_pustream());
887 sim.AddRecipe("natu", c_natu());
888 sim.AddRecipe("special", c);
889 ASSERT_NO_THROW(sim.Run());
890}
891
892// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
893TEST(FuelFabTests, PositionInitialize) {
894 cyclus::Env::SetNucDataPath();
895 std::string config =
896 "<fill_commods> <val>natu</val> </fill_commods>"
897 "<fill_recipe>natu</fill_recipe>"
898 "<fill_size>0</fill_size>"
899 ""
900 "<fiss_commods> <val>pustreambad</val> </fiss_commods>"
901 "<fiss_recipe>pustreambad</fiss_recipe>"
902 "<fiss_size>1</fiss_size>"
903 ""
904 "<topup_commod>pustream</topup_commod>"
905 "<topup_recipe>pustream</topup_recipe>"
906 "<topup_size>10000</topup_size>"
907 ""
908 "<outcommod>recyclefuel</outcommod>"
909 "<spectrum>thermal</spectrum>"
910 "<throughput>10000</throughput>";
911 double fillinv = 1;
912 int simdur = 2;
913
914 double w_fill = CosiWeight(c_natu(), "thermal");
915 double w_fiss = CosiWeight(c_pustream(), "thermal");
916 double w_target = CosiWeight(c_uox(), "thermal");
917 double fiss_frac = HighFrac(w_fill, w_target, w_fiss);
918 double fill_frac = LowFrac(w_fill, w_target, w_fiss);
919 fiss_frac = AtomToMassFrac(fiss_frac, c_pustream(), c_natu());
920 fill_frac = AtomToMassFrac(fill_frac, c_natu(), c_pustream());
921 double max_provide = fillinv / fill_frac;
922
923 cyclus::MockSim sim(cyclus::AgentSpec(":cycamore:FuelFab"), config, simdur);
924 sim.AddSource("pustream").Finalize();
925 sim.AddSource("pustreambad").Finalize();
926 sim.AddSource("natu").Finalize();
927 sim.AddSink("recyclefuel").recipe("uox").capacity(2 * max_provide).Finalize();
928 sim.AddRecipe("uox", c_uox());
929 sim.AddRecipe("pustream", c_pustream());
930 sim.AddRecipe("pustreambad", c_pustreambad());
931 sim.AddRecipe("natu", c_natu());
932 int id = sim.Run();
933
934 QueryResult qr = sim.db().Query("AgentPosition", NULL);
935 EXPECT_EQ(qr.GetVal<double>("Latitude"), 0.0);
936 EXPECT_EQ(qr.GetVal<double>("Longitude"), 0.0);
937}
938
939TEST(FuelFabTests, PositionInitialize2) {
940 cyclus::Env::SetNucDataPath();
941 std::string config =
942 "<fill_commods> <val>natu</val> </fill_commods>"
943 "<fill_recipe>natu</fill_recipe>"
944 "<fill_size>0</fill_size>"
945 ""
946 "<fiss_commods> <val>pustreambad</val> </fiss_commods>"
947 "<fiss_recipe>pustreambad</fiss_recipe>"
948 "<fiss_size>1</fiss_size>"
949 ""
950 "<topup_commod>pustream</topup_commod>"
951 "<topup_recipe>pustream</topup_recipe>"
952 "<topup_size>10000</topup_size>"
953 ""
954 "<outcommod>recyclefuel</outcommod>"
955 "<spectrum>thermal</spectrum>"
956 "<throughput>10000</throughput>"
957 "<latitude>-90.0</latitude> "
958 "<longitude>-120.0</longitude> "
959 ;
960 double fillinv = 1;
961 int simdur = 2;
962
963 double w_fill = CosiWeight(c_natu(), "thermal");
964 double w_fiss = CosiWeight(c_pustream(), "thermal");
965 double w_target = CosiWeight(c_uox(), "thermal");
966 double fiss_frac = HighFrac(w_fill, w_target, w_fiss);
967 double fill_frac = LowFrac(w_fill, w_target, w_fiss);
968 fiss_frac = AtomToMassFrac(fiss_frac, c_pustream(), c_natu());
969 fill_frac = AtomToMassFrac(fill_frac, c_natu(), c_pustream());
970 double max_provide = fillinv / fill_frac;
971
972 cyclus::MockSim sim(cyclus::AgentSpec(":cycamore:FuelFab"), config, simdur);
973 sim.AddSource("pustream").Finalize();
974 sim.AddSource("pustreambad").Finalize();
975 sim.AddSource("natu").Finalize();
976 sim.AddSink("recyclefuel").recipe("uox").capacity(2 * max_provide).Finalize();
977 sim.AddRecipe("uox", c_uox());
978 sim.AddRecipe("pustream", c_pustream());
979 sim.AddRecipe("pustreambad", c_pustreambad());
980 sim.AddRecipe("natu", c_natu());
981 int id = sim.Run();
982
983 QueryResult qr = sim.db().Query("AgentPosition", NULL);
984 EXPECT_EQ(qr.GetVal<double>("Latitude"), -90.0);
985 EXPECT_EQ(qr.GetVal<double>("Longitude"), -120.0);
986}
987
988} // namespace fuelfabtests
989} // namespace cycamore
double HighFrac(double w_low, double w_target, double w_high, double eps)
double CosiWeight(Composition::Ptr c, const std::string &spectrum)
bool ValidWeights(double w_low, double w_target, double w_high)
double AtomToMassFrac(double atomfrac, Composition::Ptr c1, Composition::Ptr c2)
double LowFrac(double w_low, double w_target, double w_high, double eps)