Skip to content

Commit 622db4c

Browse files
authoredFeb 7, 2024
Merge pull request #574 from nuclearkatie/R_Q_inventory
2 parents 2fe80d8 + 6381dfd commit 622db4c

File tree

4 files changed

+158
-16
lines changed

4 files changed

+158
-16
lines changed
 

‎CHANGELOG.rst‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ cycamore Change Log
88
* GitHub workflows for building/testing on a PR and push to `main` (#549, #564)
99
* Add functionality for random behavior on the size (#550) and frequency (#565) of a sink
1010
* GitHub workflow to check that the CHANGELOG has been updated (#562)
11+
* Added inventory policies to Storage through the material buy policy (#574)
1112

1213
**Changed:**
1314

‎src/storage.cc‎

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Storage::Storage(cyclus::Context* ctx)
1010
latitude(0.0),
1111
longitude(0.0),
1212
coordinates(latitude, longitude) {
13+
inventory_tracker.Init({&inventory, &stocks, &ready, &processing}, 1e299);
1314
cyclus::Warn<cyclus::EXPERIMENTAL_WARNING>(
1415
"The Storage Facility is experimental.");};
1516

@@ -130,11 +131,28 @@ void Storage::InitBuyPolicyParameters() {
130131
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
131132
void Storage::EnterNotify() {
132133
cyclus::Facility::EnterNotify();
133-
134-
InitBuyPolicyParameters();
135-
136-
buy_policy.Init(this, &inventory, std::string("inventory"), throughput,
137-
active_dist_, dormant_dist_, size_dist_);
134+
135+
inventory_tracker.set_capacity(max_inv_size);
136+
if (reorder_point < 0) {
137+
InitBuyPolicyParameters();
138+
buy_policy.Init(this, &inventory, std::string("inventory"),
139+
&inventory_tracker, throughput, active_dist_,
140+
dormant_dist_, size_dist_);
141+
}
142+
else if (reorder_quantity > 0) {
143+
if (reorder_point + reorder_quantity > max_inv_size) {
144+
throw cyclus::ValueError(
145+
"reorder_point + reorder_quantity must be less than or equal to max_inv_size");
146+
}
147+
buy_policy.Init(this, &inventory, std::string("inventory"),
148+
&inventory_tracker, throughput, "RQ",
149+
reorder_quantity, reorder_point);
150+
}
151+
else {
152+
buy_policy.Init(this, &inventory, std::string("inventory"),
153+
&inventory_tracker, throughput, "sS",
154+
max_inv_size, reorder_point);
155+
}
138156

139157
// dummy comp, use in_recipe if provided
140158
cyclus::CompMap v;
@@ -208,10 +226,6 @@ void Storage::Tick() {
208226

209227
LOG(cyclus::LEV_INFO4, "ComCnv") << "current capacity " << max_inv_size << " - " << processing.quantity() << " - " << ready.quantity() << " - " << stocks.quantity() << " = " << current_capacity();
210228

211-
// Set available capacity for Buy Policy
212-
inventory.capacity(current_capacity());
213-
214-
215229
if (current_capacity() > cyclus::eps_rsrc()) {
216230
LOG(cyclus::LEV_INFO4, "ComCnv")
217231
<< " has capacity for " << current_capacity() << ".";

‎src/storage.h‎

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,11 @@ class Storage
143143
/* --- Storage Members --- */
144144

145145
/// @brief current maximum amount that can be added to processing
146-
inline double current_capacity() const {
147-
return (max_inv_size - processing.quantity() - stocks.quantity()
148-
- ready.quantity()); }
146+
inline double current_capacity() {
147+
return (inventory_tracker.space()); }
148+
149+
/// @brief returns total capacity
150+
inline double capacity() { return inventory_tracker.capacity(); }
149151

150152
/// @brief returns the time key for ready materials
151153
int ready_time(){ return context()->time() - residence_time; }
@@ -296,10 +298,10 @@ class Storage
296298
"uilabel": "Dormant Buying Frequency Type"}
297299
std::string dormant_buying_frequency_type;
298300

299-
#pragma cyclus var {"default": 0,\
301+
#pragma cyclus var {"default": -1,\
300302
"tooltip": "Fixed dormant buying frequency",\
301303
"doc": "The length in time steps of the dormant buying period. Required for fixed "\
302-
"dormant_buying_frequency_type. Can be zero and agent will only be active (default behavior)",\
304+
"dormant_buying_frequency_type. Default is -1, agent has no dormant period and stays active.",\
303305
"uitype": "range", \
304306
"range": [-1, 1e299], \
305307
"uilabel": "Dormant Buying Frequency Value"}
@@ -401,6 +403,47 @@ class Storage
401403
"uilabel": "Buying Size Standard Deviation"}
402404
double buying_size_stddev;
403405

406+
#pragma cyclus var {"default": -1,\
407+
"tooltip":"Reorder point",\
408+
"doc":"The point at which the facility will request more material. "\
409+
"Above this point, no request will be made. Must be less than max_inv_size."\
410+
"If paired with reorder_quantity, this agent will have an (R,Q) inventory policy. "\
411+
"If reorder_point is used alone, this agent will have an (s,S) inventory policy, "\
412+
" with S (the maximum) being set at max_inv_size.",\
413+
"uilabel":"Reorder Point"}
414+
double reorder_point;
415+
416+
#pragma cyclus var {"default": -1,\
417+
"tooltip":"Reorder amount (R,Q inventory policy)",\
418+
"doc":"The amount of material that will be requested when the reorder point is reached. "\
419+
"Exclusive request, so will demand exactly reorder_quantity."\
420+
"Reorder_point + reorder_quantity must be less than max_inv_size.",\
421+
"uilabel":"Reorder Quantity"}
422+
double reorder_quantity;
423+
424+
#pragma cyclus var {"default": 1,\
425+
"tooltip": "Length of the active buying "\
426+
"period",\
427+
"doc":"During the length of the active buying "\
428+
"period, agent exhibits regular behavior. "\
429+
"If paired with dormant buying period, "\
430+
"alternates between buying and not buying, "\
431+
"regardless if space is available",\
432+
"uilabel":"Active Buying Period"}
433+
int active_buying;
434+
435+
#pragma cyclus var {"default": 0,\
436+
"tooltip": "Length of the dormant buying "\
437+
"period",\
438+
"doc":"During the length of the dormant buying "\
439+
"period, agent will not request any new "\
440+
"material from the DRE. Paired with active "\
441+
"buying period, alternates between buying "\
442+
"and not buying, regardless if space is "\
443+
"available",\
444+
"uilabel":"Dormant (No Buying) Period"}
445+
int dormant_buying;
446+
404447
#pragma cyclus var {"tooltip":"Incoming material buffer"}
405448
cyclus::toolkit::ResBuf<cyclus::Material> inventory;
406449

@@ -418,6 +461,9 @@ class Storage
418461
#pragma cyclus var {"tooltip":"Buffer for material still waiting for required residence_time"}
419462
cyclus::toolkit::ResBuf<cyclus::Material> processing;
420463

464+
#pragma cyclus var {"tooltip": "Total Inventory Tracker to restrict maximum agent inventory"}
465+
cyclus::toolkit::TotalInvTracker inventory_tracker;
466+
421467
//// A policy for requesting material
422468
cyclus::toolkit::MatlBuyPolicy buy_policy;
423469

‎src/storage_tests.cc‎

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ void StorageTest::SetUpStorage(){
3939
src_facility_->out_commods = out_c1;
4040
src_facility_->residence_time = residence_time;
4141
src_facility_->max_inv_size = max_inv_size;
42+
src_facility_->inventory_tracker.set_capacity(max_inv_size);
4243
src_facility_->throughput = throughput;
4344
src_facility_->discrete_handling = discrete_handling;
4445
}
@@ -457,6 +458,7 @@ TEST_F(StorageTest, MultipleCommods){
457458
EXPECT_EQ(1, n_trans2) << "expected 1 transactions, got " << n_trans;
458459
}
459460

461+
460462
// Should get one transaction in a 2 step simulation when agent is active for
461463
// one step and dormant for one step
462464
TEST_F(StorageTest, ActiveDormant){
@@ -624,7 +626,7 @@ TEST_F(StorageTest, NormalActiveDormantBuyingSize){
624626
" <buying_size_mean>0.5</buying_size_mean>"
625627
" <buying_size_stddev>0.1</buying_size_stddev>";
626628

627-
int simdur = 15;
629+
int simdur = 15;
628630

629631
cyclus::MockSim sim(cyclus::AgentSpec (":cycamore:Storage"), config, simdur);
630632

@@ -702,7 +704,6 @@ TEST_F(StorageTest, IncorrectBuyPolSetupMinMax) {
702704
cyclus::MockSim sim(cyclus::AgentSpec (":cycamore:Storage"),
703705
config_uniform_min_bigger_max, simdur);
704706
EXPECT_THROW(sim.Run(), cyclus::ValueError);
705-
706707
}
707708

708709
TEST_F(StorageTest, PositionInitialize){
@@ -753,6 +754,86 @@ TEST_F(StorageTest, Longitude){
753754
EXPECT_EQ(qr.GetVal<double>("Longitude"), 35.0);
754755
}
755756

757+
TEST_F(StorageTest, RQ_Inventory_Invalid) {
758+
std::string config =
759+
" <in_commods> <val>spent_fuel</val> </in_commods> "
760+
" <out_commods> <val>dry_spent</val> </out_commods> "
761+
" <max_inv_size>5</max_inv_size>"
762+
" <reorder_point>2</reorder_point>"
763+
" <reorder_quantity>10</reorder_quantity>";
764+
765+
int simdur = 2;
766+
767+
cyclus::MockSim sim(cyclus::AgentSpec(":cycamore:Storage"), config, simdur);
768+
769+
EXPECT_THROW(int id = sim.Run(), cyclus::ValueError);
770+
}
771+
772+
TEST_F(StorageTest, RQ_Inventory) {
773+
std::string config =
774+
" <in_commods> <val>spent_fuel</val> </in_commods> "
775+
" <out_commods> <val>dry_spent</val> </out_commods> "
776+
" <max_inv_size>5</max_inv_size>"
777+
" <reorder_point>2</reorder_point>"
778+
" <reorder_quantity>3</reorder_quantity>";
779+
780+
int simdur = 5;
781+
782+
cyclus::MockSim sim(cyclus::AgentSpec (":cycamore:Storage"), config, simdur);
783+
784+
sim.AddSource("spent_fuel").capacity(5).Finalize();
785+
sim.AddSink("dry_spent").Finalize();
786+
787+
int id = sim.Run();
788+
789+
std::vector<cyclus::Cond> conds;
790+
conds.push_back(cyclus::Cond("Commodity", "==", std::string("spent_fuel")));
791+
cyclus::QueryResult qr = sim.db().Query("Transactions", &conds);
792+
int n_trans = qr.rows.size();
793+
794+
EXPECT_EQ(3, n_trans) << "expected 3 transactions, got " << n_trans;
795+
// check that the transactions occur at the expected time (0, 2, 4)
796+
EXPECT_EQ(0, qr.GetVal<int>("Time", 0));
797+
EXPECT_EQ(2, qr.GetVal<int>("Time", 1));
798+
EXPECT_EQ(4, qr.GetVal<int>("Time", 2));
799+
800+
// check that all transactions are of size 3
801+
qr = sim.db().Query("Resources", NULL);
802+
EXPECT_EQ(3, qr.GetVal<double>("Quantity", 0));
803+
}
804+
805+
TEST_F(StorageTest, sS_Inventory) {
806+
std::string config =
807+
" <in_commods> <val>spent_fuel</val> </in_commods> "
808+
" <out_commods> <val>dry_spent</val> </out_commods> "
809+
" <max_inv_size>5</max_inv_size>"
810+
" <reorder_point>2</reorder_point>";
811+
812+
int simdur = 5;
813+
814+
cyclus::MockSim sim(cyclus::AgentSpec (":cycamore:Storage"), config, simdur);
815+
816+
sim.AddSource("spent_fuel").capacity(5).Finalize();
817+
sim.AddSink("dry_spent").Finalize();
818+
819+
int id = sim.Run();
820+
821+
std::vector<cyclus::Cond> conds;
822+
conds.push_back(cyclus::Cond("Commodity", "==", std::string("spent_fuel")));
823+
cyclus::QueryResult qr = sim.db().Query("Transactions", &conds);
824+
int n_trans = qr.rows.size();
825+
EXPECT_EQ(3, n_trans) << "expected 3 transactions, got " << n_trans;
826+
// check that the transactions occur at the expected time (0, 2, 4)
827+
EXPECT_EQ(0, qr.GetVal<int>("Time", 0));
828+
EXPECT_EQ(2, qr.GetVal<int>("Time", 1));
829+
EXPECT_EQ(4, qr.GetVal<int>("Time", 2));
830+
831+
// check that all transactions are of size 5
832+
qr = sim.db().Query("Resources", NULL);
833+
EXPECT_EQ(5, qr.GetVal<double>("Quantity", 0));
834+
}
835+
836+
756837
} // namespace cycamore
757838

758839
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

0 commit comments

Comments
 (0)
Please sign in to comment.