diff --git a/src/storage.cc b/src/storage.cc index 964d4a667..42c141e5a 100644 --- a/src/storage.cc +++ b/src/storage.cc @@ -84,6 +84,13 @@ void Storage::InitBuyPolicyParameters() { } 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 if (active_buying_frequency_type == "FixedWithDisruption") { + if (active_buying_disruption < 0) { + throw cyclus::ValueError("Disruption must be greater than or equal to 0"); + } + active_dist_ = cyclus::BinaryIntDist::Ptr ( + new cyclus::BinaryIntDist(active_buying_disruption_probability, + active_buying_disruption, active_buying_val)); } else { throw cyclus::ValueError("Invalid active buying frequency type");} @@ -114,6 +121,13 @@ void Storage::InitBuyPolicyParameters() { } 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 if (dormant_buying_frequency_type == "FixedWithDisruption") { + if (dormant_buying_disruption < 0) { + throw cyclus::ValueError("Disruption must be greater than or equal to 0"); + } + dormant_dist_ = cyclus::BinaryIntDist::Ptr ( + new cyclus::BinaryIntDist(dormant_buying_disruption_probability, + dormant_buying_disruption, dormant_buying_val)); } else { throw cyclus::ValueError("Invalid dormant buying frequency type");} diff --git a/src/storage.h b/src/storage.h index d9ba40f4a..e66edac7e 100644 --- a/src/storage.h +++ b/src/storage.h @@ -234,12 +234,17 @@ class Storage #pragma cyclus var {"default": "Fixed",\ "tooltip": "Type of active buying frequency",\ - "doc": "Options: Fixed, Uniform, Normal, Binomial. Fixed requires active_buying_val. Uniform "\ + "doc": "Options: Fixed, Uniform, Normal, Binomial, FixedWithDisruption. "\ + "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. Binomial requires active_buying_end_probability.",\ + "active_buying_min and active_buying_max. Binomial requires active_buying_end_probability."\ + "FixedWithDisruption has a probability that any given cycle will have a disrupted, "\ + "active length. Once per cycle, a Bernoulli distribution (Binomial dist "\ + "with N=1) will be sampled to determine if typical or disrupted cycle. If typical, "\ + "active_buying_val is cycle length. If disrupted, active_buying_disruption.",\ "uitype": "combobox",\ - "categorical": ["Fixed", "Uniform", "Normal", "Binomial"],\ + "categorical": ["Fixed", "Uniform", "Normal", "Binomial", "FixedWithDisruption"],\ "uilabel": "Active Buying Frequency Type"} std::string active_buying_frequency_type; @@ -300,14 +305,36 @@ class Storage "uilabel": "Active Buying Offline Probability"} double active_buying_end_probability; + #pragma cyclus var {"default": 0,\ + "tooltip": "Probability that a cycle contains a disruption",\ + "doc": "Probability that the agent undergoes a disruption (disrupted active period) "\ + "during any given cycle. Required for FixedWithDisruption active_buying_frequency_type.",\ + "uitype": "range",\ + "range": [0.0, 1.0],\ + "uilabel": "Active Buying Disruption Probability"} + double active_buying_disruption_probability; + + #pragma cyclus var {"default": -1,\ + "tooltip": "Fixed length of disrupted active cycle",\ + "doc": "When a active cycle is disrupted, this is length of the active period instead "\ + "of active_buying_val. Required for FixedWithDisruption active_buying_frequency_type",\ + "uitype": "range",\ + "range": [0, CY_LARGE_INT]} + int active_buying_disruption; + #pragma cyclus var {"default": "Fixed",\ "tooltip": "Type of dormant buying frequency",\ - "doc": "Options: Fixed, Uniform, Normal, Binomial. Fixed requires dormant_buying_val. "\ + "doc": "Options: Fixed, Uniform, Normal, Binomial, FixedWithDisruption. "\ + "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. Binomial requires dormant_buying_end_probability.",\ + "and dormant_buying_max. Binomial requires dormant_buying_end_probability. "\ + "FixedWithDisruption has a probability that any given cycle will have a disrupted, "\ + "or long, outage. Once per cycle, a Bernoulli distribution (Binomial dist "\ + "with N=1) will be sampled to determine if typical or disrupted cycle. If typical, "\ + "dormant_buying_val is cycle length. If disrupted, dormant_buying_disruption.",\ "uitype": "combobox",\ - "categorical": ["Fixed", "Uniform", "Normal", "Binomial"],\ + "categorical": ["Fixed", "Uniform", "Normal", "Binomial", "FixedWithDisruption"],\ "uilabel": "Dormant Buying Frequency Type"} std::string dormant_buying_frequency_type; @@ -363,9 +390,26 @@ class Storage "Must be between 0 and 1",\ "uitype": "range", \ "range": [0.0, 1.0], \ - "uilabel": "Dormant Buying Offline Probability"} + "uilabel": "Dormant Buying Binomial Offline Probability"} double dormant_buying_end_probability; + #pragma cyclus var {"default": 0,\ + "tooltip": "Probability that a cycle contains a disruption",\ + "doc": "Probability that the agent undergoes a disruption (longer offline period) "\ + "during any given cycle. Required for FixedWithDisruption dormant_buying_frequency_type.",\ + "uitype": "range",\ + "range": [0.0, 1.0],\ + "uilabel": "Dormant Buying Disruption Probability"} + double dormant_buying_disruption_probability; + + #pragma cyclus var {"default": -1,\ + "tooltip": "Fixed length of disrupted cycle",\ + "doc": "When a dormant cycle is disrupted, this is length of the offline period instead "\ + "of dormant_buying_val. Required for FixedWithDisruption dormant_buying_frequency_type",\ + "uitype": "range",\ + "range": [0, CY_LARGE_INT]} + int dormant_buying_disruption; + #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 b6346b65d..8c1771b86 100644 --- a/src/storage_tests.cc +++ b/src/storage_tests.cc @@ -581,6 +581,42 @@ TEST_F(StorageTest, BinomialActiveDormant) { EXPECT_EQ(10, qr.GetVal("Time", 7)); } +TEST_F(StorageTest, DisruptionActiveDormant) { + std::string config = + " commod" + " commod1" + " 1" + " FixedWithDisruption" + " 0.4" + " 2" + " 5" + " FixedWithDisruption" + " 0.5" + " 1" + " 10"; + + int simdur = 50; + + cyclus::MockSim sim(cyclus::AgentSpec (":cycamore:Storage"), config, simdur); + sim.AddSource("commod").capacity(5).Finalize(); + int id = sim.Run(); + + cyclus::QueryResult qr = sim.db().Query("Transactions", NULL); + // confirm that transactions are only occurring during active periods + // first active length = 5 (disrupted) + EXPECT_EQ(0, qr.GetVal("Time", 0)); + EXPECT_EQ(1, qr.GetVal("Time", 1)); // ... end of active + + // first dormant length = 1 (not disrupted) + // second active length = 2 (not disrupted) + EXPECT_EQ(3, qr.GetVal("Time", 2)); // ... end of dormant + EXPECT_EQ(4, qr.GetVal("Time", 3)); + + // second dormant length = 10 (disrupted) + // third active length = 2 (not disrupted) + EXPECT_EQ(15, qr.GetVal("Time", 4)); // ... end of second active +} + TEST_F(StorageTest, FixedBuyingSize){ std::string config = " spent_fuel "