diff --git a/src/sink.cc b/src/sink.cc index 5fde24893..d1fe861e0 100644 --- a/src/sink.cc +++ b/src/sink.cc @@ -41,6 +41,7 @@ Sink::~Sink() {} // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Sink::EnterNotify() { cyclus::Facility::EnterNotify(); + LOG(cyclus::LEV_INFO4, "SnkFac") << " using random behavior " << random_size_type; if (in_commod_prefs.size() == 0) { for (int i = 0; i < in_commods.size(); ++i) { @@ -52,6 +53,10 @@ void Sink::EnterNotify() { << " values, expected " << in_commods.size(); throw cyclus::ValueError(ss.str()); } + /// Create first requestAmt. Only used in testing, as a simulation will + /// overwrite this on Tick() + SetRequestAmt(); + RecordPosition(); } @@ -85,17 +90,21 @@ Sink::GetMatlRequests() { std::set::Ptr> ports; RequestPortfolio::Ptr port(new RequestPortfolio()); - double amt = RequestAmt(); Material::Ptr mat; + /// for testing + if (requestAmt > SpaceAvailable()) { + SetRequestAmt(); + } + if (recipe_name.empty()) { - mat = cyclus::NewBlankMaterial(amt); + mat = cyclus::NewBlankMaterial(requestAmt); } else { Composition::Ptr rec = this->context()->GetRecipe(recipe_name); - mat = cyclus::Material::CreateUntracked(amt, rec); + mat = cyclus::Material::CreateUntracked(requestAmt, rec); } - if (amt > cyclus::eps()) { + if (requestAmt > cyclus::eps()) { std::vector*> mutuals; for (int i = 0; i < in_commods.size(); i++) { mutuals.push_back(port->AddRequest(mat, this, in_commods[i], in_commod_prefs[i])); @@ -118,16 +127,15 @@ Sink::GetGenRsrcRequests() { std::set::Ptr> ports; RequestPortfolio::Ptr port(new RequestPortfolio()); - double amt = RequestAmt(); - if (amt > cyclus::eps()) { - CapacityConstraint cc(amt); + if (requestAmt > cyclus::eps()) { + CapacityConstraint cc(requestAmt); port->AddConstraint(cc); std::vector::const_iterator it; for (it = in_commods.begin(); it != in_commods.end(); ++it) { std::string quality = ""; // not clear what this should be.. - Product::Ptr rsrc = Product::CreateUntracked(amt, quality); + Product::Ptr rsrc = Product::CreateUntracked(requestAmt, quality); port->AddRequest(rsrc, this, *it); } @@ -164,9 +172,15 @@ void Sink::Tick() { using std::vector; LOG(cyclus::LEV_INFO3, "SnkFac") << prototype() << " is ticking {"; - double requestAmt = RequestAmt(); + SetRequestAmt(); + + LOG(cyclus::LEV_INFO3, "SnkFac") << prototype() << " has default request amount " << requestAmt; + // inform the simulation about what the sink facility will be requesting if (requestAmt > cyclus::eps()) { + LOG(cyclus::LEV_INFO4, "SnkFac") << prototype() + << " has request amount " << requestAmt + << " kg of " << in_commods[0] << "."; for (vector::iterator commod = in_commods.begin(); commod != in_commods.end(); commod++) { @@ -205,6 +219,29 @@ void Sink::RecordPosition() { ->Record(); } +void Sink::SetRequestAmt() { + double amt = SpaceAvailable(); + if (amt < cyclus::eps()) { + requestAmt = 0; + } + + if (random_size_type == "None") { + requestAmt = amt; + } + else if (random_size_type == "UniformReal") { + requestAmt = context()->random_uniform_real(0, amt); + } + else if (random_size_type == "NormalReal") { + requestAmt = context()->random_normal_real(amt * random_size_mean, + amt * random_size_stddev, + 0, amt); + } + else { + requestAmt = amt; + } + return; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - extern "C" cyclus::Agent* ConstructSink(cyclus::Context* ctx) { return new Sink(ctx); diff --git a/src/sink.h b/src/sink.h index f5fabedd5..e9e4de0d2 100644 --- a/src/sink.h +++ b/src/sink.h @@ -69,6 +69,9 @@ class Sink const std::vector< std::pair, cyclus::Product::Ptr> >& responses); + /// @brief SinkFacilities update request amount using random behavior + virtual void SetRequestAmt(); + /// add a commodity to the set of input commodities /// @param name the commodity name inline void AddCommodity(std::string name) { in_commods.push_back(name); } @@ -87,7 +90,7 @@ class Sink inline double InventorySize() const { return inventory.quantity(); } /// determines the amount to request - inline double RequestAmt() const { + inline double SpaceAvailable() const { return std::min(capacity, std::max(0.0, inventory.space())); } @@ -107,6 +110,7 @@ class Sink input_commodity_preferences() const { return in_commod_prefs; } private: + double requestAmt; /// all facilities must have at least one input commodity #pragma cyclus var {"tooltip": "input commodities", \ "doc": "commodities that the sink facility accepts", \ @@ -154,6 +158,46 @@ class Sink #pragma cyclus var {'capacity': 'max_inv_size'} cyclus::toolkit::ResBuf inventory; + /// random status (size of request) + #pragma cyclus var {"default": "None", \ + "tooltip": "type of random behavior when setting the " \ + "size of the request", \ + "uitype": "combobox", \ + "uilabel": "Random Size", \ + "categorical": ["None", "UniformReal", "UniformInt", "NormalReal", "NormalInt"], \ + "doc": "type of random behavior to use. Default None, " \ + "other options are 'UniformReal', 'UniformInt', " \ + "'NormalReal', and 'NormalInt'"} + std::string random_size_type; + + // random size mean (as a fraction of available space) + #pragma cyclus var {"default": 1.0, \ + "tooltip": "fraction of available space to determine the mean", \ + "uilabel": "Random Size Mean", \ + "uitype": "range", \ + "range": [0.0, 1e299], \ + "doc": "When a normal distribution is used to determine the " \ + "size of the request, this is the fraction of available " \ + "space to use as the mean. Default 1.0. Note " \ + "that values significantly above 1 without a " \ + "correspondingly large std dev may result in " \ + "inefficient use of the random number generator."} + double random_size_mean; + + // random size std dev (as a fraction of available space) + #pragma cyclus var {"default": 0.1, \ + "tooltip": "fraction of available space to determine the std dev", \ + "uilabel": "Random Size Std Dev", \ + "uitype": "range", \ + "range": [0.0, 1e299], \ + "doc": "When a normal distribution is used to determine the " \ + "size of the request, this is the fraction of available " \ + "space to use as the standard deviation. Default 0.1"} + double random_size_stddev; + + + // random status (frequencing/timing of request) + #pragma cyclus var { \ "default": 0.0, \ "uilabel": "Geographical latitude in degrees as a double", \ diff --git a/src/sink_tests.cc b/src/sink_tests.cc index 389177a27..630db4f5f 100644 --- a/src/sink_tests.cc +++ b/src/sink_tests.cc @@ -45,7 +45,7 @@ TEST_F(SinkTest, InitialState) { EXPECT_DOUBLE_EQ(0.0, src_facility->InventorySize()); EXPECT_DOUBLE_EQ(capacity_, src_facility->Capacity()); EXPECT_DOUBLE_EQ(inv_, src_facility->MaxInventorySize()); - EXPECT_DOUBLE_EQ(capacity_, src_facility->RequestAmt()); + EXPECT_DOUBLE_EQ(capacity_, src_facility->SpaceAvailable()); EXPECT_DOUBLE_EQ(0.0, src_facility->InventorySize()); std::string arr[] = {commod1_, commod2_}; std::vector vexp (arr, arr + sizeof(arr) / sizeof(arr[0]) ); @@ -66,7 +66,7 @@ TEST_F(SinkTest, Clone) { EXPECT_DOUBLE_EQ(0.0, cloned_fac->InventorySize()); EXPECT_DOUBLE_EQ(capacity_, cloned_fac->Capacity()); EXPECT_DOUBLE_EQ(inv_, cloned_fac->MaxInventorySize()); - EXPECT_DOUBLE_EQ(capacity_, cloned_fac->RequestAmt()); + EXPECT_DOUBLE_EQ(capacity_, cloned_fac->SpaceAvailable()); std::string arr[] = {commod1_, commod2_}; std::vector vexp (arr, arr + sizeof(arr) / sizeof(arr[0]) ); EXPECT_EQ(vexp, cloned_fac->input_commodities()); @@ -104,7 +104,7 @@ TEST_F(SinkTest, DISABLED_XMLInit) { EXPECT_EQ(vexp, fac.input_commodities()); EXPECT_DOUBLE_EQ(capacity_, fac.Capacity()); EXPECT_DOUBLE_EQ(inv_, fac.MaxInventorySize()); - EXPECT_DOUBLE_EQ(capacity_, fac.RequestAmt()); + EXPECT_DOUBLE_EQ(capacity_, fac.SpaceAvailable()); EXPECT_DOUBLE_EQ(0.0, fac.InventorySize()); } @@ -342,6 +342,74 @@ TEST_F(SinkTest, PositionInitialize2) { } +TEST_F(SinkTest, RandomUniform) { + using cyclus::QueryResult; + using cyclus::Cond; + + std::string config = + " " + " commods_1" + " " + " 10" + " UniformReal "; + + int simdur = 1; + cyclus::MockSim sim(cyclus::AgentSpec + (":cycamore:Sink"), config, simdur); + sim.AddSource("commods_1").capacity(10).Finalize(); + int id = sim.Run(); + + QueryResult qr = sim.db().Query("Resources", NULL); + EXPECT_EQ(qr.rows.size(), 1); + EXPECT_NEAR(qr.GetVal("Quantity"), 9.41273, 0.0001); +} + +TEST_F(SinkTest, RandomNormal) { + using cyclus::QueryResult; + using cyclus::Cond; + + std::string config = + " " + " commods_1" + " " + " 10" + " NormalReal "; + + int simdur = 1; + cyclus::MockSim sim(cyclus::AgentSpec + (":cycamore:Sink"), config, simdur); + sim.AddSource("commods_1").capacity(10).Finalize(); + int id = sim.Run(); + + QueryResult qr = sim.db().Query("Resources", NULL); + EXPECT_EQ(qr.rows.size(), 1); + EXPECT_NEAR(qr.GetVal("Quantity"), 9.60929, 0.0001); +} + +TEST_F(SinkTest, RandomNormalWithMeanSttdev) { + using cyclus::QueryResult; + using cyclus::Cond; + + std::string config = + " " + " commods_1" + " " + " 10" + " NormalReal " + " 0.5 " + " 0.2 "; + + int simdur = 1; + cyclus::MockSim sim(cyclus::AgentSpec + (":cycamore:Sink"), config, simdur); + sim.AddSource("commods_1").capacity(10).Finalize(); + int id = sim.Run(); + + QueryResult qr = sim.db().Query("Resources", NULL); + EXPECT_EQ(qr.rows.size(), 1); + EXPECT_NEAR(qr.GetVal("Quantity"), 1.52979, 0.0001); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - cyclus::Agent* SinkConstructor(cyclus::Context* ctx) { return new cycamore::Sink(ctx);