diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a580f31c2..6bff22dd9 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -10,6 +10,7 @@ Since last release * Added package parameter to source (#613, #617, #621, #623, #630) * Added default keep packaging to reactor (#618, #619) * Added support for Ubuntu 24.04 (#633) +* Added (negative)binomial distributions for disruption modeling to storage (#635) **Changed:** diff --git a/src/storage.cc b/src/storage.cc index 6be703644..964d4a667 100644 --- a/src/storage.cc +++ b/src/storage.cc @@ -78,6 +78,13 @@ void Storage::InitBuyPolicyParameters() { active_dist_ = cyclus::NormalIntDist::Ptr (new cyclus::NormalIntDist(active_buying_mean, active_buying_stddev, active_buying_min, active_buying_max)); } + else if (active_buying_frequency_type == "Binomial") { + if (active_buying_end_probability < 0 || active_buying_end_probability > 1) { + throw cyclus::ValueError("Active buying end probability must be between 0 and 1"); + } + int success = 1; // only one success is needed to end the active buying period + active_dist_ = cyclus::NegativeBinomialIntDist::Ptr (new cyclus::NegativeBinomialIntDist(success, active_buying_end_probability)); + } else { throw cyclus::ValueError("Invalid active buying frequency type");} @@ -101,6 +108,13 @@ void Storage::InitBuyPolicyParameters() { dormant_dist_ = cyclus::NormalIntDist::Ptr (new cyclus::NormalIntDist(dormant_buying_mean, dormant_buying_stddev, dormant_buying_min, dormant_buying_max)); } + else if (dormant_buying_frequency_type == "Binomial") { + if (dormant_buying_end_probability < 0 || dormant_buying_end_probability > 1) { + throw cyclus::ValueError("Dormant buying end probability must be between 0 and 1"); + } + int success = 1; // only one success is needed to end the dormant buying period + dormant_dist_ = cyclus::NegativeBinomialIntDist::Ptr (new cyclus::NegativeBinomialIntDist(success, dormant_buying_end_probability)); + } else { throw cyclus::ValueError("Invalid dormant buying frequency type");} diff --git a/src/storage.h b/src/storage.h index 7bf539c9b..d9ba40f4a 100644 --- a/src/storage.h +++ b/src/storage.h @@ -234,12 +234,12 @@ class Storage #pragma cyclus var {"default": "Fixed",\ "tooltip": "Type of active buying frequency",\ - "doc": "Options: Fixed, Uniform, Normal. Fixed requires active_buying_val. Uniform "\ + "doc": "Options: Fixed, Uniform, Normal, Binomial. Fixed requires active_buying_val. Uniform "\ "requires active_buying_min and active_buying_max. Normal "\ "requires active_buying_mean and active_buying_std, with optional "\ - "active_buying_min and active_buying_max.",\ + "active_buying_min and active_buying_max. Binomial requires active_buying_end_probability.",\ "uitype": "combobox",\ - "categorical": ["Fixed", "Uniform", "Normal"],\ + "categorical": ["Fixed", "Uniform", "Normal", "Binomial"],\ "uilabel": "Active Buying Frequency Type"} std::string active_buying_frequency_type; @@ -290,14 +290,24 @@ class Storage "uilabel": "Active Buying Frequency Standard Deviation"} double active_buying_stddev; + #pragma cyclus var {"default": 0,\ + "tooltip": "Probability that agent will go offline during the next time step",\ + "doc": "Binomial distribution has a fixed probability of going dormant at any given "\ + "timestep, like a weighted coin flip. Required for Binomial active_buying_frequency_type. "\ + "Must be between 0 and 1",\ + "uitype": "range", \ + "range": [0.0, 1.0], \ + "uilabel": "Active Buying Offline Probability"} + double active_buying_end_probability; + #pragma cyclus var {"default": "Fixed",\ "tooltip": "Type of dormant buying frequency",\ - "doc": "Options: Fixed, Uniform, Normal. Fixed requires dormant_buying_val. Uniform "\ - "requires dormant_buying_min and dormant_buying_max. Normal requires "\ + "doc": "Options: Fixed, Uniform, Normal, Binomial. Fixed requires dormant_buying_val. "\ + "Uniform requires dormant_buying_min and dormant_buying_max. Normal requires "\ "dormant_buying_mean and dormant_buying_std, with optional dormant_buying_min "\ - "and dormant_buying_max.",\ + "and dormant_buying_max. Binomial requires dormant_buying_end_probability.",\ "uitype": "combobox",\ - "categorical": ["Fixed", "Uniform", "Normal"],\ + "categorical": ["Fixed", "Uniform", "Normal", "Binomial"],\ "uilabel": "Dormant Buying Frequency Type"} std::string dormant_buying_frequency_type; @@ -346,6 +356,16 @@ class Storage "uilabel": "Dormant Buying Frequency Standard Deviation"} double dormant_buying_stddev; + #pragma cyclus var {"default": 0,\ + "tooltip": "Probability that agent will return to active during the next time step",\ + "doc": "Binomial distribution has a fixed probability of going active at any given "\ + "timestep, like a weighted coin flip. Required for Binomial dormant_buying_frequency_type. "\ + "Must be between 0 and 1",\ + "uitype": "range", \ + "range": [0.0, 1.0], \ + "uilabel": "Dormant Buying Offline Probability"} + double dormant_buying_end_probability; + #pragma cyclus var {"default": "Fixed",\ "tooltip": "Type of behavior used to determine size of buy request",\ "doc": "Behavior function used to determine the size of requests made. All values are "\ diff --git a/src/storage_tests.cc b/src/storage_tests.cc index 8bd80238c..b6346b65d 100644 --- a/src/storage_tests.cc +++ b/src/storage_tests.cc @@ -551,6 +551,36 @@ TEST_F(StorageTest, UniformActiveNormalDormant){ EXPECT_EQ(9, qr.GetVal("Time", 4)); } +TEST_F(StorageTest, BinomialActiveDormant) { + std::string config = + " commod " + " commod1 " + " 1" + " Binomial" + " 0.2" + " Binomial" + " 0.3"; + + int simdur = 30; + + cyclus::MockSim sim(cyclus::AgentSpec (":cycamore:Storage"), config, simdur); + sim.AddSource("commod").capacity(5).Finalize(); + + int id = sim.Run(); + + std::vector conds; + conds.push_back(cyclus::Cond("Commodity", "==", std::string("commod"))); + cyclus::QueryResult qr = sim.db().Query("Transactions", &conds); + // confirm that transactions are only occurring during active periods + // first active length = 7 + EXPECT_EQ(0, qr.GetVal("Time", 0)); + EXPECT_EQ(1, qr.GetVal("Time", 1)); + // ... end of active + EXPECT_EQ(6, qr.GetVal("Time", 6)); + // transactions resume at time 10 + EXPECT_EQ(10, qr.GetVal("Time", 7)); +} + TEST_F(StorageTest, FixedBuyingSize){ std::string config = " spent_fuel " @@ -1080,10 +1110,6 @@ TEST_F(StorageTest, TransportUnit) { EXPECT_EQ(2, qr_trans.GetVal("Time", 4)); EXPECT_EQ(2, qr_trans.GetVal("Time", 5)); - for (int i = 0; i < qr_trans.rows.size(); i++) { - std::cerr << "transaction " << i << "is at time " << qr_trans.GetVal("Time", i) << std::endl; - } - std::vector res_conds; res_conds.push_back(cyclus::Cond("PackageName", "==", p->name())); cyclus::QueryResult qr_res = sim.db().Query("Resources", &res_conds);