From 2280fb5b7bd7dfb0fae547049a65bbb7950ee045 Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Fri, 19 Jan 2024 12:49:39 -0700 Subject: [PATCH 01/20] reorder quantity --- src/toolkit/matl_buy_policy.cc | 34 ++++++++++++++++++++++++++ src/toolkit/matl_buy_policy.h | 12 +++++++++ tests/toolkit/matl_buy_policy_tests.cc | 33 +++++++++++++++++++++++++ 3 files changed, 79 insertions(+) diff --git a/src/toolkit/matl_buy_policy.cc b/src/toolkit/matl_buy_policy.cc index 7a70a62146..de1c122b78 100644 --- a/src/toolkit/matl_buy_policy.cc +++ b/src/toolkit/matl_buy_policy.cc @@ -20,6 +20,8 @@ MatlBuyPolicy::MatlBuyPolicy() : quantize_(-1), fill_to_(1), req_when_under_(1), + reorder_amt_(0), + RQ_exclusive_(false), active_dist_(NULL), dormant_dist_(NULL), size_dist_(NULL){ @@ -95,6 +97,14 @@ void MatlBuyPolicy::init_active_dormant() { } } +void MatlBuyPolicy::set_reorder_amt(double x) { + reorder_amt_ = x; +} + +void MatlBuyPolicy::set_RQ_exclusive(bool x) { + RQ_exclusive_ = x; +} + MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, std::string name) { set_manager(manager); @@ -146,6 +156,22 @@ MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, return *this; } +MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, + std::string name, double req_when_under, + double reorder_amt, bool RQ_exclusive) { + set_manager(manager); + buf_ = buf; + name_ = name; + set_req_when_under(req_when_under); + set_reorder_amt(reorder_amt); + set_RQ_exclusive(RQ_exclusive); + if (RQ_exclusive) { + set_quantize(reorder_amt); + } + init_active_dormant(); + return *this; +} + MatlBuyPolicy& MatlBuyPolicy::Set(std::string commod) { CompMap c; c[10010000] = 1e-100; @@ -190,6 +216,7 @@ std::set::Ptr> MatlBuyPolicy::GetMatlRequests() { double amt; int current_time_ = manager()->context()->time(); + std::cerr << "for time " << current_time_ << ", make_req is " << make_req << std::endl; if (never_dormant() || current_time_ < next_active_end_) { // currently in the middle of active buying period @@ -210,13 +237,20 @@ std::set::Ptr> MatlBuyPolicy::GetMatlRequests() { LGH(INFO4) << "end of dormant period, next active time end: " << next_active_end_ << ", and next dormant time end: " << next_dormant_end_ << std::endl; } + std::cerr << "amt is " << amt << std::endl; + if (!make_req || amt < eps()) return ports; bool excl = Excl(); + if ((reorder_amt_ > 0) && (amt > reorder_amt_)) { + std::cerr << "reorder_amt_ is " << reorder_amt_ << std::endl; + amt = reorder_amt_; + } double req_amt = ReqQty(amt); int n_req = NReq(amt); LGH(INFO3) << "requesting " << amt << " kg via " << n_req << " request(s)" << std::endl; + std::cerr << "requesting " << amt << " kg via " << n_req << " request(s)" << std::endl; // one portfolio for each request for (int i = 0; i != n_req; i++) { diff --git a/src/toolkit/matl_buy_policy.h b/src/toolkit/matl_buy_policy.h index 7325e1ddee..88bb0866ee 100644 --- a/src/toolkit/matl_buy_policy.h +++ b/src/toolkit/matl_buy_policy.h @@ -72,6 +72,11 @@ class MatlBuyPolicy : public Trader { /// a single time step /// @param fill_to the amount or fraction of inventory to order when placing /// an order. This is equivalent to the S in an (s, S) inventory policy. + /// @param reorder_quantity is the size of request to place when the buf has + /// reached reorder point. This is equivalent to the Q in an (R, Q) inventory + /// policy. + /// @param RQ_exclusive If true, the policy will make a quantized request + /// for reorder_quantity /// @param req_when_under place an request when the buf's quantity is less /// than its capacity * fill_to (as a fraction). This is equivalent to the s /// in an (s, S) inventory policy. @@ -113,6 +118,9 @@ class MatlBuyPolicy : public Trader { MatlBuyPolicy& Init(Agent* manager, ResBuf* buf, std::string name, double throughput, double fill_to, double req_when_under, double quantize); + MatlBuyPolicy& Init(Agent* manager, ResBuf* buf, std::string name, + double req_when_under, double reorder_amt, + bool RQ_exclusive); /// @} /// Instructs the policy to fill its buffer with requests on the given @@ -198,10 +206,14 @@ class MatlBuyPolicy : public Trader { void set_quantize(double x); void set_throughput(double x); void init_active_dormant(); + void set_reorder_amt(double x); + void set_RQ_exclusive(bool x); ResBuf* buf_; std::string name_; double fill_to_, req_when_under_, quantize_, throughput_; + double reorder_amt_; + bool RQ_exclusive_; int next_active_end_= 0; int next_dormant_end_= 0; diff --git a/tests/toolkit/matl_buy_policy_tests.cc b/tests/toolkit/matl_buy_policy_tests.cc index 70e758f13e..880981087e 100644 --- a/tests/toolkit/matl_buy_policy_tests.cc +++ b/tests/toolkit/matl_buy_policy_tests.cc @@ -503,5 +503,38 @@ TEST_F(MatlBuyPolicyTests, RandomSizeAndFrequency) { delete a; } +TEST_F(MatlBuyPolicyTests, RQInventory) { + using cyclus::QueryResult; + + int dur = 5; + double req_when_under = 1; + double reorder_amt = 1; + bool RQ_exclusive = false; + + cyclus::MockSim sim(dur); + cyclus::Agent* a = new TestFacility(sim.context()); + sim.context()->AddPrototype(a->prototype(), a); + sim.agent = sim.context()->CreateAgent(a->prototype());sim.AddSource("commod1").Finalize(); + + TestFacility* fac = dynamic_cast(sim.agent); + + cyclus::toolkit::ResBuf inbuf; + inbuf.capacity(3); + cyclus::toolkit::MatlBuyPolicy policy; + policy.Init(fac, &inbuf, "inbuf", req_when_under, reorder_amt, RQ_exclusive) + .Set("commod1").Start(); + + EXPECT_NO_THROW(sim.Run()); + + QueryResult qr = sim.db().Query("Transactions", NULL); + EXPECT_EQ(0, qr.GetVal("Time", 0)); + + qr = sim.db().Query("Resources", NULL); + EXPECT_NEAR(1, qr.GetVal("Quantity", 0), 0.00001); + EXPECT_NEAR(1, qr.GetVal("Quantity", 1), 0.00001); + + delete a; +} + } } From 2097b88faa66031f3535024ccd24652448e77d00 Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Wed, 24 Jan 2024 15:15:46 -0700 Subject: [PATCH 02/20] first pass at resbuftracker --- src/toolkit/matl_buy_policy.cc | 38 ++++++-- src/toolkit/matl_buy_policy.h | 16 +++- src/toolkit/res_buf_tracker.h | 115 +++++++++++++++++++++++++ tests/bug1209and1210_test.cc | 6 +- tests/toolkit/matl_buy_policy_tests.cc | 56 ++++++++---- 5 files changed, 201 insertions(+), 30 deletions(-) create mode 100644 src/toolkit/res_buf_tracker.h diff --git a/src/toolkit/matl_buy_policy.cc b/src/toolkit/matl_buy_policy.cc index 7a70a62146..d89b0233bb 100644 --- a/src/toolkit/matl_buy_policy.cc +++ b/src/toolkit/matl_buy_policy.cc @@ -43,6 +43,23 @@ void MatlBuyPolicy::set_manager(Agent* m) { } } +void MatlBuyPolicy::create_buf_tracker() { + std::vector*> bufs = {buf_}; + buf_tracker_ = new ResBufTracker(); + buf_tracker_->Init(bufs, buf_->capacity()); +} + +void MatlBuyPolicy::set_buf_tracker(ResBufTracker* t) { + std::cerr << "set_buf_trackers" << std::endl; + if (t == NULL){ + std::vector*> bufs = {buf_}; + buf_tracker_->Init(bufs, buf_->capacity()); + } + else { + buf_tracker_ = t; + } +} + void MatlBuyPolicy::set_fill_to(double x) { if (x > 1) x /= buf_->capacity(); @@ -53,7 +70,7 @@ void MatlBuyPolicy::set_fill_to(double x) { void MatlBuyPolicy::set_req_when_under(double x) { if (x > 1) x /= buf_->capacity(); - assert(x > 0 && x <= 1.); + assert(x >= 0 && x <= 1.); req_when_under_ = x; } @@ -96,21 +113,27 @@ void MatlBuyPolicy::init_active_dormant() { } MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, - std::string name) { + std::string name, ResBufTracker* buf_tracker) { set_manager(manager); + std::cerr << "manager is " << manager << std::endl; buf_ = buf; name_ = name; + std::cerr << "beginning buf_tracker_ init" << std::endl; + set_buf_tracker(buf_tracker); init_active_dormant(); return *this; } MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, - std::string name, double throughput, boost::shared_ptr active_dist, + std::string name, ResBufTracker* buf_tracker, + double throughput, boost::shared_ptr active_dist, boost::shared_ptr dormant_dist, boost::shared_ptr size_dist) { set_manager(manager); buf_ = buf; name_ = name; + std::cerr << "beginning buf_tracker_ init" << std::endl; + set_buf_tracker(buf_tracker); set_throughput(throughput); active_dist_ = active_dist; dormant_dist_ = dormant_dist; @@ -120,11 +143,13 @@ MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, } MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, - std::string name, + std::string name, + ResBufTracker* buf_tracker, double fill_to, double req_when_under) { set_manager(manager); buf_ = buf; name_ = name; + create_buf_tracker(); set_fill_to(fill_to); set_req_when_under(req_when_under); init_active_dormant(); @@ -132,12 +157,15 @@ MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, } MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, - std::string name, double throughput, + std::string name, + ResBufTracker* buf_tracker, + double throughput, double fill_to, double req_when_under, double quantize) { set_manager(manager); buf_ = buf; name_ = name; + create_buf_tracker(); set_fill_to(fill_to); set_req_when_under(req_when_under); set_quantize(quantize); diff --git a/src/toolkit/matl_buy_policy.h b/src/toolkit/matl_buy_policy.h index 7325e1ddee..a04ed7f992 100644 --- a/src/toolkit/matl_buy_policy.h +++ b/src/toolkit/matl_buy_policy.h @@ -7,6 +7,7 @@ #include "composition.h" #include "material.h" #include "res_buf.h" +#include "res_buf_tracker.h" #include "trader.h" #include "random_number_generator.h" @@ -102,15 +103,18 @@ class MatlBuyPolicy : public Trader { /// Note that active and dormant periods are note currently compatible with /// (s, S) inventory management /// @{ - MatlBuyPolicy& Init(Agent* manager, ResBuf* buf, std::string name); MatlBuyPolicy& Init(Agent* manager, ResBuf* buf, std::string name, - double throughput, + ResBufTracker* buf_tracker); + MatlBuyPolicy& Init(Agent* manager, ResBuf* buf, std::string name, + ResBufTracker* buf_tracker, double throughput, boost::shared_ptr active_dist = NULL, boost::shared_ptr dormant_dist = NULL, boost::shared_ptr size_dist = NULL); MatlBuyPolicy& Init(Agent* manager, ResBuf* buf, std::string name, + ResBufTracker* buf_tracker, double fill_to, double req_when_under); MatlBuyPolicy& Init(Agent* manager, ResBuf* buf, std::string name, + ResBufTracker* buf_tracker, double throughput, double fill_to, double req_when_under, double quantize); /// @} @@ -144,8 +148,9 @@ class MatlBuyPolicy : public Trader { /// the total amount available to request inline double TotalAvailable() const { - return std::min(throughput_, - (fill_to_ * buf_->capacity()) - buf_->quantity()); + return std::min({throughput_, + (fill_to_ * buf_->capacity()) - buf_->quantity(), + buf_tracker_->space()}); } /// whether trades will be denoted as exclusive or not @@ -191,6 +196,8 @@ class MatlBuyPolicy : public Trader { }; void set_manager(Agent* m); + void create_buf_tracker(); + void set_buf_tracker(ResBufTracker* t); /// requires buf_ already set void set_fill_to(double x); /// requires buf_ already set @@ -200,6 +207,7 @@ class MatlBuyPolicy : public Trader { void init_active_dormant(); ResBuf* buf_; + ResBufTracker* buf_tracker_; std::string name_; double fill_to_, req_when_under_, quantize_, throughput_; diff --git a/src/toolkit/res_buf_tracker.h b/src/toolkit/res_buf_tracker.h new file mode 100644 index 0000000000..439cfbd5e7 --- /dev/null +++ b/src/toolkit/res_buf_tracker.h @@ -0,0 +1,115 @@ +#ifndef CYCLUS_SRC_TOOLKIT_RES_BUF_TRACKER_H_ +#define CYCLUS_SRC_TOOLKIT_RES_BUF_TRACKER_H_ + +#include "error.h" + +namespace cyclus { +namespace toolkit { + +/// ResBufTracker is a helper class that tracks the quantity of multiple +/// ResBufs. This can be useful when a single agent has multiple buffers that +/// but a limit should be placed on their combined inventory, such as a +/// facility-wide maximum inventory. Like ResBufs, ResBufTracker has an +/// infinite capacity unless explicitly changed. +/// +/// ResBufTracker does not hold any inventory itself, it only tracks the +/// quantities of other ResBufs. ResBufTracker currently tracks quantities +/// alone, not compositions. +/// +/// @code +/// class MyAgent : public cyclus:: Facility { +/// public: +/// EnterNotify() { +/// cyclus::Facility::EnterNotify(); +/// tracker_.Init({&inventory_, &outventory_}, facility_max_inv_size_); +/// +/// cyclus::MatlBuyPolicy p; +/// p.Init(this, &inventory_, "inventory", &tracker_); +/// } +/// protected: +/// cyclus::toolkit::ResBuf inventory_; +/// cyclus::toolkit::ResBuf outventory_; +/// +/// double facility_max_inv_size_ = 1000; +/// cyclus::toolkit::ResBufTracker tracker_; +/// } + +class ResBufTracker { + public: + /// Creates an uninitialized tracker. The Init function MUST be called before + /// the tracker is used. + ResBufTracker() : max_inv_size_(std::numeric_limits::max()), qty_(0) {}; + + ResBufTracker(std::vector*> bufs, + double max_inv_size = std::numeric_limits::max()) { + Init(bufs, max_inv_size); + } + + ~ResBufTracker() {}; + + /// Initializes the tracker with the given ResBufs. The tracker will have + /// infinite capacity unless explicitly changed. + void Init(std::vector*> bufs, + double max_inv_size = std::numeric_limits::max()) { + bufs_ = bufs; + max_inv_size_ = max_inv_size; + } + + /// Returns the total quantity of all tracked ResBufs. + /// @throws ValueError if the tracker has not been initialized (zero) + inline double quantity() { + int num = num_bufs(); + if (num == 0) { + throw ValueError("ResBufTracker has not been initialized, no buffers to track"); + } + for (int i = 0; i < num; i++) { + qty_ += bufs_[i]->quantity(); + } + return qty_; + } + + /// Returns the total capacity of all tracked ResBufs. + inline double capacity() {return max_inv_size_;}; + + /// Returns the remaining facility-wide space across all tracked ResBufs. + inline double space() {return std::max(0.0, max_inv_size_ - qty_);}; + + /// Returns the remaining space in the given ResBuf, considering the + /// facility-wide limitations. + inline double buf_space(ResBuf* buf) {return std::min(buf->space(), space());}; + + /// Returns true if there are no resources in any buffer + inline bool empty() {return quantity() == 0;}; + + /// Returns number of buffers being tracked + /// @throws ValueError if the tracker has not been initialized (zero) + inline int num_bufs() { + int num = bufs_.size(); + if (num == 0) { + throw ValueError("ResBufTracker has not been initialized, no buffers to track"); + } + return num;}; + + /// Change the total capacity across all ResBufs. The new capacity must be + /// greater than the current quantity. + /// @throws ValueError if the new capacity is less than the current quantity + void set_capacity(double cap) { + if (quantity() - cap > eps_rsrc()) { + std::stringstream ss; + ss << std::setprecision(17) << "new capacity " << cap << + " lower than existing quantity " << quantity(); + throw ValueError(ss.str()); + } + max_inv_size_ = cap; + } + + private: + double max_inv_size_; + double qty_; + std::vector*> bufs_; +}; + +} // namespace toolkit +} // namespace cyclus + +#endif // CYCLUS_SRC_TOOLKIT_RES_BUF_TRACKER_H_ \ No newline at end of file diff --git a/tests/bug1209and1210_test.cc b/tests/bug1209and1210_test.cc index 8d6ff44eab..e90bbb6ccd 100644 --- a/tests/bug1209and1210_test.cc +++ b/tests/bug1209and1210_test.cc @@ -22,8 +22,9 @@ class MultiTraderSink: public cyclus::Facility { void EnterNotify() { cyclus::Facility::EnterNotify(); - policy1.Init(this, &inbuf1, "inbuf1").Set("commod1").Start(); - policy2.Init(this, &inbuf2, "inbuf2").Set("commod2").Start(); + buff_tracker.Init({&inbuf1, &inbuf2}); + policy1.Init(this, &inbuf1, "inbuf1", &buff_tracker).Set("commod1").Start(); + policy2.Init(this, &inbuf2, "inbuf2", &buff_tracker).Set("commod2").Start(); } void Tick() {} @@ -33,6 +34,7 @@ class MultiTraderSink: public cyclus::Facility { cyclus::toolkit::ResBuf inbuf1; cyclus::toolkit::MatlBuyPolicy policy2; cyclus::toolkit::ResBuf inbuf2; + cyclus::toolkit::ResBufTracker buff_tracker; }; // issue 1210 was preventing the 1209 test from passing (causing segfaults), diff --git a/tests/toolkit/matl_buy_policy_tests.cc b/tests/toolkit/matl_buy_policy_tests.cc index 70e758f13e..fb98a98ec3 100644 --- a/tests/toolkit/matl_buy_policy_tests.cc +++ b/tests/toolkit/matl_buy_policy_tests.cc @@ -45,10 +45,14 @@ TEST_F(MatlBuyPolicyTests, Init) { double cap = 5; ResBuf buff; buff.capacity(cap); + ResBufTracker buff_tracker({&buff}); + std::cerr << "buf cap: " << buff.capacity() << std::endl; MatlBuyPolicy p; + std::cerr << "policy created" << std::endl; // defaults - p.Init(fac1, &buff, ""); + p.Init(fac1, &buff, "", &buff_tracker); + std::cerr << "policy initialized" << std::endl; double amt = p.TotalAvailable(); ASSERT_FLOAT_EQ(amt, cap); ASSERT_FLOAT_EQ(p.ReqQty(amt), cap); @@ -56,7 +60,7 @@ TEST_F(MatlBuyPolicyTests, Init) { // throughput double throughput = cap - 1; - p.Init(fac1, &buff, "", throughput, 1, 1, -1); + p.Init(fac1, &buff, "", &buff_tracker, throughput, 1, 1, -1); amt = p.TotalAvailable(); ASSERT_FLOAT_EQ(amt, throughput); ASSERT_FLOAT_EQ(p.ReqQty(amt), throughput); @@ -64,7 +68,7 @@ TEST_F(MatlBuyPolicyTests, Init) { // exclusive orders double quantize = 2.5; - p.Init(fac1, &buff, "", std::numeric_limits::max(), 1, 1, quantize); + p.Init(fac1, &buff, "", &buff_tracker, std::numeric_limits::max(), 1, 1, quantize); amt = p.TotalAvailable(); ASSERT_FLOAT_EQ(amt, cap); ASSERT_FLOAT_EQ(p.ReqQty(amt), quantize); @@ -73,9 +77,9 @@ TEST_F(MatlBuyPolicyTests, Init) { // S,s with nothing in buffer double S = 4, s = 2; // reset - p.Init(fac1, &buff, "", std::numeric_limits::max(), 1, 1, -1); + p.Init(fac1, &buff, "", &buff_tracker, std::numeric_limits::max(), 1, 1, -1); // use Ss constructor - p.Init(fac1, &buff, "", S, s); + p.Init(fac1, &buff, "", &buff_tracker, S, s); amt = p.TotalAvailable(); ASSERT_FLOAT_EQ(amt, S); ASSERT_FLOAT_EQ(p.ReqQty(amt), S); @@ -94,8 +98,9 @@ TEST_F(MatlBuyPolicyTests, StartStop) { double cap = 5; ResBuf buff; buff.capacity(cap); + ResBufTracker buff_tracker({&buff}); MatlBuyPolicy p; - ASSERT_THROW(p.Init(NULL, &buff, ""), ValueError); + ASSERT_THROW(p.Init(NULL, &buff, "", &buff_tracker), ValueError); } // Tests that matlbuypolicy sends out a request properly @@ -103,10 +108,11 @@ TEST_F(MatlBuyPolicyTests, OneReq) { double cap = 5; ResBuf buff; buff.capacity(cap); + ResBufTracker buff_tracker({&buff}); cyclus::Composition::Ptr c1 = cyclus::Composition::Ptr(new TestComp()); MatlBuyPolicy p; - p.Init(fac1, &buff, "").Set("commod1", c1); + p.Init(fac1, &buff, "", &buff_tracker).Set("commod1", c1); std::set::Ptr> obs = p.GetMatlRequests(); ASSERT_EQ(obs.size(), 1); ASSERT_EQ((*obs.begin())->requests().size(), 1); @@ -120,6 +126,7 @@ TEST_F(MatlBuyPolicyTests, MultipleReqs) { double cap = 5; ResBuf buff; buff.capacity(cap); + ResBufTracker buff_tracker({&buff}); std::string commod1("foo"), commod2("bar"); double p2 = 2.5; cyclus::Composition::Ptr c1 = cyclus::Composition::Ptr(new TestComp()); @@ -127,7 +134,7 @@ TEST_F(MatlBuyPolicyTests, MultipleReqs) { MatlBuyPolicy p; // two requests - p.Init(fac1, &buff, "").Set(commod1, c1).Set(commod2, c2, p2); + p.Init(fac1, &buff, "", &buff_tracker).Set(commod1, c1).Set(commod2, c2, p2); std::set::Ptr> obs = p.GetMatlRequests(); ASSERT_EQ(obs.size(), 1); ASSERT_EQ((*obs.begin())->requests().size(), 2); @@ -148,12 +155,13 @@ TEST_F(MatlBuyPolicyTests, Quantize) { double cap = 5; ResBuf buff; buff.capacity(cap); + ResBufTracker buff_tracker({&buff}); double p2 = 2.5; cyclus::Composition::Ptr c1 = cyclus::Composition::Ptr(new TestComp()); MatlBuyPolicy p; double quantize = 2.5; - p.Init(fac1, &buff, "", std::numeric_limits::max(), 1, 1, quantize).Set("commod1", c1); + p.Init(fac1, &buff, "", &buff_tracker, std::numeric_limits::max(), 1, 1, quantize).Set("commod1", c1); std::set::Ptr> obs = p.GetMatlRequests(); ASSERT_EQ(obs.size(), 2); ASSERT_EQ((*obs.begin())->requests().size(), 1); @@ -167,13 +175,14 @@ TEST_F(MatlBuyPolicyTests, MultiReqQuantize) { double cap = 5; ResBuf buff; buff.capacity(cap); + ResBufTracker buff_tracker({&buff}); cyclus::Composition::Ptr c1 = cyclus::Composition::Ptr(new TestComp()); cyclus::Composition::Ptr c2 = cyclus::Composition::Ptr(new TestComp()); MatlBuyPolicy p; // two portfolios with quantize double quantize = 2.5; - p.Init(fac1, &buff, "", std::numeric_limits::max(), 1, 1, quantize).Set("commod1", c1).Set("commod2", c2); + p.Init(fac1, &buff, "", &buff_tracker, std::numeric_limits::max(), 1, 1, quantize).Set("commod1", c1).Set("commod2", c2); std::set::Ptr> obs = p.GetMatlRequests(); ASSERT_EQ(obs.size(), 2); ASSERT_EQ((*obs.begin())->requests().size(), 2); @@ -204,8 +213,9 @@ TEST_F(MatlBuyPolicyTests, DefaultFixedActiveDormant) { TestFacility* fac = dynamic_cast(sim.agent); cyclus::toolkit::ResBuf inbuf; + ResBufTracker buf_tracker({&inbuf}); cyclus::toolkit::MatlBuyPolicy policy; - policy.Init(fac, &inbuf, "inbuf", throughput, a_d_dist, a_d_dist, NULL) + policy.Init(fac, &inbuf, "inbuf", &buf_tracker, throughput, a_d_dist, a_d_dist, NULL) .Set("commod1").Start(); EXPECT_NO_THROW(sim.Run()); @@ -240,8 +250,9 @@ TEST_F(MatlBuyPolicyTests, FixedActiveDormant) { TestFacility* fac = dynamic_cast(sim.agent); cyclus::toolkit::ResBuf inbuf; + ResBufTracker buf_tracker({&inbuf}); cyclus::toolkit::MatlBuyPolicy policy; - policy.Init(fac, &inbuf, "inbuf", throughput, a_d_dist, a_d_dist, size_dist) + policy.Init(fac, &inbuf, "inbuf", &buf_tracker, throughput, a_d_dist, a_d_dist, size_dist) .Set("commod1").Start(); EXPECT_NO_THROW(sim.Run()); @@ -274,8 +285,9 @@ TEST_F(MatlBuyPolicyTests, FixedActiveDormantMultipleCycles) { TestFacility* fac = dynamic_cast(sim.agent); cyclus::toolkit::ResBuf inbuf; + ResBufTracker buf_tracker({&inbuf}); cyclus::toolkit::MatlBuyPolicy policy; - policy.Init(fac, &inbuf, "inbuf", throughput, a_dist, d_dist, NULL) + policy.Init(fac, &inbuf, "inbuf", &buf_tracker, throughput, a_dist, d_dist, NULL) .Set("commod1").Start(); EXPECT_NO_THROW(sim.Run()); @@ -314,8 +326,9 @@ TEST_F(MatlBuyPolicyTests, UniformActiveDormant) { TestFacility* fac = dynamic_cast(sim.agent); cyclus::toolkit::ResBuf inbuf; + ResBufTracker buf_tracker({&inbuf}); cyclus::toolkit::MatlBuyPolicy policy; - policy.Init(fac, &inbuf, "inbuf", throughput, a_dist, d_dist, NULL) + policy.Init(fac, &inbuf, "inbuf", &buf_tracker, throughput, a_dist, d_dist, NULL) .Set("commod1").Start(); EXPECT_NO_THROW(sim.Run()); @@ -352,8 +365,9 @@ TEST_F(MatlBuyPolicyTests, NormalActiveDormant) { TestFacility* fac = dynamic_cast(sim.agent); cyclus::toolkit::ResBuf inbuf; + ResBufTracker buf_tracker({&inbuf}); cyclus::toolkit::MatlBuyPolicy policy; - policy.Init(fac, &inbuf, "inbuf", throughput, a_dist, d_dist, NULL) + policy.Init(fac, &inbuf, "inbuf", &buf_tracker, throughput, a_dist, d_dist, NULL) .Set("commod1").Start(); EXPECT_NO_THROW(sim.Run()); @@ -390,8 +404,9 @@ TEST_F(MatlBuyPolicyTests, MixedActiveDormant) { TestFacility* fac = dynamic_cast(sim.agent); cyclus::toolkit::ResBuf inbuf; + ResBufTracker buf_tracker({&inbuf}); cyclus::toolkit::MatlBuyPolicy policy; - policy.Init(fac, &inbuf, "inbuf", throughput, a_dist, d_dist, NULL) + policy.Init(fac, &inbuf, "inbuf", &buf_tracker, throughput, a_dist, d_dist, NULL) .Set("commod1").Start(); EXPECT_NO_THROW(sim.Run()); @@ -424,8 +439,9 @@ TEST_F(MatlBuyPolicyTests, RandomSizeUniform) { TestFacility* fac = dynamic_cast(sim.agent); cyclus::toolkit::ResBuf inbuf; + ResBufTracker buf_tracker({&inbuf}); cyclus::toolkit::MatlBuyPolicy policy; - policy.Init(fac, &inbuf, "inbuf", throughput, NULL, NULL, size_dist) + policy.Init(fac, &inbuf, "inbuf", &buf_tracker, throughput, NULL, NULL, size_dist) .Set("commod1").Start(); EXPECT_NO_THROW(sim.Run()); @@ -454,8 +470,9 @@ TEST_F(MatlBuyPolicyTests, RandomSizeNormal) { TestFacility* fac = dynamic_cast(sim.agent); cyclus::toolkit::ResBuf inbuf; + ResBufTracker buf_tracker({&inbuf}); cyclus::toolkit::MatlBuyPolicy policy; - policy.Init(fac, &inbuf, "inbuf", throughput, NULL, NULL, size_dist) + policy.Init(fac, &inbuf, "inbuf", &buf_tracker, throughput, NULL, NULL, size_dist) .Set("commod1").Start(); EXPECT_NO_THROW(sim.Run()); @@ -486,8 +503,9 @@ TEST_F(MatlBuyPolicyTests, RandomSizeAndFrequency) { TestFacility* fac = dynamic_cast(sim.agent); cyclus::toolkit::ResBuf inbuf; + ResBufTracker buf_tracker({&inbuf}); cyclus::toolkit::MatlBuyPolicy policy; - policy.Init(fac, &inbuf, "inbuf", throughput, a_dist, d_dist, size_dist) + policy.Init(fac, &inbuf, "inbuf", &buf_tracker, throughput, a_dist, d_dist, size_dist) .Set("commod1").Start(); EXPECT_NO_THROW(sim.Run()); From 72db4e3a4ad16333a1afadae46d35edbe1de3ab4 Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Thu, 25 Jan 2024 21:50:18 -0700 Subject: [PATCH 03/20] total inventory tracker --- src/toolkit/total_inv_tracker.h | 130 +++++++++++++++++++++++ tests/bug1209and1210_test.cc | 2 +- tests/toolkit/total_inv_tracker_tests.cc | 54 ++++++++++ tests/toolkit/total_inv_tracker_tests.h | 68 ++++++++++++ 4 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 src/toolkit/total_inv_tracker.h create mode 100644 tests/toolkit/total_inv_tracker_tests.cc create mode 100644 tests/toolkit/total_inv_tracker_tests.h diff --git a/src/toolkit/total_inv_tracker.h b/src/toolkit/total_inv_tracker.h new file mode 100644 index 0000000000..882540b759 --- /dev/null +++ b/src/toolkit/total_inv_tracker.h @@ -0,0 +1,130 @@ +#ifndef CYCLUS_SRC_TOOLKIT_TOTAL_INV_TRACKER_H_ +#define CYCLUS_SRC_TOOLKIT_TOTAL_INV_TRACKER_H_ + +#include "error.h" +#include "res_buf.h" + +namespace cyclus { +namespace toolkit { + +/// TotalInvTracker is a helper class that tracks the total inventory of +/// multiple ResBufs. This can be useful when a single agent uses multiple +/// buffers to manage their process, but a limit should be placed on their +/// combined inventory, such as a agent- or facility-wide maximum inventory. +/// Like ResBufs, TotalInvTracker has an infinite capacity unless explicitly +/// changed. +/// +/// TotalInvTracker does not hold any inventory itself, it only tracks the +/// quantities of other ResBufs. TotalInvTracker currently tracks quantities +/// alone, not compositions. +/// +/// @code +/// class MyAgent : public cyclus:: Facility { +/// public: +/// EnterNotify() { +/// cyclus::Facility::EnterNotify(); +/// tracker_.Init({&inventory_, &outventory_}, facility_max_inv_size_); +/// +/// cyclus::MatlBuyPolicy p; +/// p.Init(this, &inventory_, "inventory", &tracker_); +/// } +/// protected: +/// cyclus::toolkit::ResBuf inventory_; +/// cyclus::toolkit::ResBuf outventory_; +/// +/// double facility_max_inv_size_ = 1000; +/// cyclus::toolkit::TotalInvTracker tracker_; +/// } + +class TotalInvTracker { + public: + /// Creates an uninitialized tracker. The Init function MUST be called before + /// the tracker is used. + TotalInvTracker() : max_inv_size_(std::numeric_limits::max()), qty_(0) {}; + + TotalInvTracker(std::vector*> bufs, + double max_inv_size = std::numeric_limits::max()) { + Init(bufs, max_inv_size); + } + + ~TotalInvTracker() {}; + + /// Initializes the tracker with the given ResBufs. The tracker will have + /// infinite capacity unless explicitly changed. + void Init(std::vector*> bufs, + double max_inv_size = std::numeric_limits::max()) { + bufs_ = bufs; + max_inv_size_ = max_inv_size; + } + + /// Returns the total quantity of all tracked ResBufs. + /// @throws ValueError if the tracker has not been initialized (zero) + inline double quantity() { + int num = num_bufs(); + if (num == 0) { + throw ValueError("TotalInvTracker has not been initialized, no buffers to track"); + } + qty_ = 0; + for (int i = 0; i < num; i++) { + qty_ += bufs_[i]->quantity(); + } + return qty_; + } + + /// Returns the total capacity that could go in the ResBufs. Either the + /// capacity of the TotalInvTracker, or the sum of each ResBuf's capacity, + /// whichever is lower. + inline double capacity() { + int num = num_bufs(); + double cap = 0; + for (int i = 0; i < num; i++) { + cap += bufs_[i]->capacity(); + } + return std::min(cap, max_inv_size_); + }; + + /// Returns the total capacity of the traker. Does not include ResBufs + inline double tracker_capacity() { return max_inv_size_;} + + /// Returns the remaining facility-wide space across all tracked ResBufs. + inline double space() {return std::max(0.0, capacity() - quantity());}; + + /// Returns the remaining space in the given ResBuf, considering the + /// facility-wide limitations. + inline double buf_space(ResBuf* buf) {return std::min(buf->space(), space());}; + + /// Returns true if there are no resources in any buffer + inline bool empty() {return quantity() == 0;}; + + /// Returns number of buffers being tracked + /// @throws ValueError if the tracker has not been initialized (zero) + inline int num_bufs() { + int num = bufs_.size(); + if (num == 0) { + throw ValueError("TotalInvTracker has not been initialized, no buffers to track"); + } + return num;}; + + /// Change the total capacity across all ResBufs. The new capacity must be + /// greater than the current quantity. + /// @throws ValueError if the new capacity is less than the current quantity + void set_capacity(double cap) { + if (quantity() - cap > eps_rsrc()) { + std::stringstream ss; + ss << std::setprecision(17) << "new capacity " << cap << + " lower than existing quantity " << quantity(); + throw ValueError(ss.str()); + } + max_inv_size_ = cap; + } + + private: + double max_inv_size_; + double qty_; + std::vector*> bufs_; +}; + +} // namespace toolkit +} // namespace cyclus + +#endif // CYCLUS_SRC_TOOLKIT_TOTAL_INV_TRACKER_H_ \ No newline at end of file diff --git a/tests/bug1209and1210_test.cc b/tests/bug1209and1210_test.cc index e90bbb6ccd..52dc4d3a08 100644 --- a/tests/bug1209and1210_test.cc +++ b/tests/bug1209and1210_test.cc @@ -34,7 +34,7 @@ class MultiTraderSink: public cyclus::Facility { cyclus::toolkit::ResBuf inbuf1; cyclus::toolkit::MatlBuyPolicy policy2; cyclus::toolkit::ResBuf inbuf2; - cyclus::toolkit::ResBufTracker buff_tracker; + cyclus::toolkit::TotalInvTracker buff_tracker; }; // issue 1210 was preventing the 1209 test from passing (causing segfaults), diff --git a/tests/toolkit/total_inv_tracker_tests.cc b/tests/toolkit/total_inv_tracker_tests.cc new file mode 100644 index 0000000000..d72b85d9b7 --- /dev/null +++ b/tests/toolkit/total_inv_tracker_tests.cc @@ -0,0 +1,54 @@ +#include "total_inv_tracker_tests.h" + +#include + +namespace cyclus { +namespace toolkit { + +TEST_F(TotalInvTrackerTest, empty) { + EXPECT_THROW(empty_tracker_.empty(), ValueError); + EXPECT_THROW(empty_tracker_.quantity(), ValueError); + EXPECT_THROW(empty_tracker_.capacity(), ValueError); + EXPECT_THROW(empty_tracker_.set_capacity(-1), ValueError); + EXPECT_THROW(empty_tracker_.set_capacity(1000), ValueError); + EXPECT_THROW(empty_tracker_.space(), ValueError); +} + +TEST_F(TotalInvTrackerTest, quantity) { + EXPECT_NO_THROW(single_tracker_.quantity()); + EXPECT_NO_THROW(multi_tracker_.quantity()); + EXPECT_EQ(single_tracker_.quantity(), qty1_); + EXPECT_EQ(multi_tracker_.quantity(), qty1_ + qty2_); +} + +TEST_F(TotalInvTrackerTest, capacity) { + EXPECT_EQ(single_tracker_.capacity(), 20); + EXPECT_EQ(single_tracker_.tracker_capacity(), max_inv_size_); + EXPECT_EQ(multi_tracker_.capacity(), max_inv_size_); + EXPECT_EQ(multi_tracker_.tracker_capacity(), max_inv_size_); +} + +TEST_F(TotalInvTrackerTest, set_capacity) { + EXPECT_THROW(multi_tracker_.set_capacity(-1), ValueError); + EXPECT_THROW(multi_tracker_.set_capacity(10), ValueError); + EXPECT_NO_THROW(multi_tracker_.set_capacity(1000)); +} + +TEST_F(TotalInvTrackerTest, space) { + EXPECT_EQ(single_tracker_.space(), 20 - qty1_); + EXPECT_EQ(multi_tracker_.space(), max_inv_size_ - qty1_ - qty2_); +} + +TEST_F(TotalInvTrackerTest, buf_space) { + EXPECT_EQ(multi_tracker_.buf_space(&buf1_), buf1_.space()); + EXPECT_EQ(multi_tracker_.buf_space(&buf2_), max_inv_size_ - qty1_ - qty2_); +} + +TEST_F(TotalInvTrackerTest, num_bufs) { + EXPECT_THROW(empty_tracker_.num_bufs(), ValueError); + EXPECT_NO_THROW(multi_tracker_.num_bufs()); + EXPECT_EQ(multi_tracker_.num_bufs(), 3); +} + +} // namespace toolkit +} // namespace cyclus diff --git a/tests/toolkit/total_inv_tracker_tests.h b/tests/toolkit/total_inv_tracker_tests.h new file mode 100644 index 0000000000..d180401a7c --- /dev/null +++ b/tests/toolkit/total_inv_tracker_tests.h @@ -0,0 +1,68 @@ +#ifndef CYCLUS_TESTS_TOOLKIT_TOTAL_INV_TRACKER_TESTS_H +#define CYCLUS_TESTS_TOOLKIT_TOTAL_INV_TRACKER_TESTS_H + +#include + +#include "error.h" +#include "toolkit/total_inv_tracker.h" +#include "toolkit/res_buf.h" +#include "composition.h" +#include "material.h" +#include "test_agents/test_facility.h" + +namespace cyclus { +namespace toolkit { + +class TotalInvTrackerTest : public ::testing::Test { + protected: + cyclus::Timer ti; + cyclus::Recorder rec; + cyclus::Context* ctx; + TestFacility* fac; + + TotalInvTracker empty_tracker_; + TotalInvTracker single_tracker_; + TotalInvTracker multi_tracker_; + ResBuf buf0_, buf1_, buf2_, buf3_; + double max_inv_size_; + Material::Ptr mat0_, mat1_, mat2_; + double qty1_, qty2_; + + virtual void SetUp() { + try { + ctx = new cyclus::Context(&ti, &rec); + fac = new TestFacility(ctx); + + CompMap cm; + cm[1001] = 1.0; + qty1_ = 10; + qty2_ = 50; + mat0_ = Material::Create(fac, qty1_, Composition::CreateFromAtom(cm)); + mat1_ = Material::Create(fac, qty1_, Composition::CreateFromAtom(cm)); + mat2_ = Material::Create(fac, qty2_, Composition::CreateFromAtom(cm)); + + max_inv_size_ = 100; + + buf0_.capacity(20); + buf0_.Push(mat0_); + single_tracker_.Init({&buf0_}, max_inv_size_); + + buf1_.capacity(50); + buf2_.capacity(max_inv_size_); + buf3_.capacity(50); + buf1_.Push(mat1_); + buf2_.Push(mat2_); + multi_tracker_.Init({&buf1_, &buf2_, &buf3_}, max_inv_size_); + } catch (std::exception err) { + FAIL() << "An exception was thrown in the fixture SetUp."; + } + } + virtual void TearDown() { + delete ctx; + } +}; + +} // namespace toolkit +} // namespace cyclus + +#endif // CYCLUS_TESTS_TOOLKIT_TOTAL_INV_TRACKER_TESTS_H From 4981ca579f98622cabbcf6273a436d1e09fa48d0 Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Thu, 25 Jan 2024 21:51:32 -0700 Subject: [PATCH 04/20] implement TotalInvTracker in MatlBuyPolicy (still needs tests probably) --- src/toolkit/matl_buy_policy.cc | 16 ++-- src/toolkit/matl_buy_policy.h | 14 +-- src/toolkit/res_buf_tracker.h | 115 ------------------------- tests/toolkit/matl_buy_policy_tests.cc | 30 +++---- 4 files changed, 28 insertions(+), 147 deletions(-) delete mode 100644 src/toolkit/res_buf_tracker.h diff --git a/src/toolkit/matl_buy_policy.cc b/src/toolkit/matl_buy_policy.cc index d89b0233bb..d6b96c97c2 100644 --- a/src/toolkit/matl_buy_policy.cc +++ b/src/toolkit/matl_buy_policy.cc @@ -45,12 +45,11 @@ void MatlBuyPolicy::set_manager(Agent* m) { void MatlBuyPolicy::create_buf_tracker() { std::vector*> bufs = {buf_}; - buf_tracker_ = new ResBufTracker(); + buf_tracker_ = new TotalInvTracker(); buf_tracker_->Init(bufs, buf_->capacity()); } -void MatlBuyPolicy::set_buf_tracker(ResBufTracker* t) { - std::cerr << "set_buf_trackers" << std::endl; +void MatlBuyPolicy::set_buf_tracker(TotalInvTracker* t) { if (t == NULL){ std::vector*> bufs = {buf_}; buf_tracker_->Init(bufs, buf_->capacity()); @@ -113,26 +112,23 @@ void MatlBuyPolicy::init_active_dormant() { } MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, - std::string name, ResBufTracker* buf_tracker) { + std::string name, TotalInvTracker* buf_tracker) { set_manager(manager); - std::cerr << "manager is " << manager << std::endl; buf_ = buf; name_ = name; - std::cerr << "beginning buf_tracker_ init" << std::endl; set_buf_tracker(buf_tracker); init_active_dormant(); return *this; } MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, - std::string name, ResBufTracker* buf_tracker, + std::string name, TotalInvTracker* buf_tracker, double throughput, boost::shared_ptr active_dist, boost::shared_ptr dormant_dist, boost::shared_ptr size_dist) { set_manager(manager); buf_ = buf; name_ = name; - std::cerr << "beginning buf_tracker_ init" << std::endl; set_buf_tracker(buf_tracker); set_throughput(throughput); active_dist_ = active_dist; @@ -144,7 +140,7 @@ MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, std::string name, - ResBufTracker* buf_tracker, + TotalInvTracker* buf_tracker, double fill_to, double req_when_under) { set_manager(manager); buf_ = buf; @@ -158,7 +154,7 @@ MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, std::string name, - ResBufTracker* buf_tracker, + TotalInvTracker* buf_tracker, double throughput, double fill_to, double req_when_under, double quantize) { diff --git a/src/toolkit/matl_buy_policy.h b/src/toolkit/matl_buy_policy.h index a04ed7f992..d1b80e0624 100644 --- a/src/toolkit/matl_buy_policy.h +++ b/src/toolkit/matl_buy_policy.h @@ -7,7 +7,7 @@ #include "composition.h" #include "material.h" #include "res_buf.h" -#include "res_buf_tracker.h" +#include "total_inv_tracker.h" #include "trader.h" #include "random_number_generator.h" @@ -104,17 +104,17 @@ class MatlBuyPolicy : public Trader { /// (s, S) inventory management /// @{ MatlBuyPolicy& Init(Agent* manager, ResBuf* buf, std::string name, - ResBufTracker* buf_tracker); + TotalInvTracker* buf_tracker); MatlBuyPolicy& Init(Agent* manager, ResBuf* buf, std::string name, - ResBufTracker* buf_tracker, double throughput, + TotalInvTracker* buf_tracker, double throughput, boost::shared_ptr active_dist = NULL, boost::shared_ptr dormant_dist = NULL, boost::shared_ptr size_dist = NULL); MatlBuyPolicy& Init(Agent* manager, ResBuf* buf, std::string name, - ResBufTracker* buf_tracker, + TotalInvTracker* buf_tracker, double fill_to, double req_when_under); MatlBuyPolicy& Init(Agent* manager, ResBuf* buf, std::string name, - ResBufTracker* buf_tracker, + TotalInvTracker* buf_tracker, double throughput, double fill_to, double req_when_under, double quantize); /// @} @@ -197,7 +197,7 @@ class MatlBuyPolicy : public Trader { void set_manager(Agent* m); void create_buf_tracker(); - void set_buf_tracker(ResBufTracker* t); + void set_buf_tracker(TotalInvTracker* t); /// requires buf_ already set void set_fill_to(double x); /// requires buf_ already set @@ -207,7 +207,7 @@ class MatlBuyPolicy : public Trader { void init_active_dormant(); ResBuf* buf_; - ResBufTracker* buf_tracker_; + TotalInvTracker* buf_tracker_; std::string name_; double fill_to_, req_when_under_, quantize_, throughput_; diff --git a/src/toolkit/res_buf_tracker.h b/src/toolkit/res_buf_tracker.h deleted file mode 100644 index 439cfbd5e7..0000000000 --- a/src/toolkit/res_buf_tracker.h +++ /dev/null @@ -1,115 +0,0 @@ -#ifndef CYCLUS_SRC_TOOLKIT_RES_BUF_TRACKER_H_ -#define CYCLUS_SRC_TOOLKIT_RES_BUF_TRACKER_H_ - -#include "error.h" - -namespace cyclus { -namespace toolkit { - -/// ResBufTracker is a helper class that tracks the quantity of multiple -/// ResBufs. This can be useful when a single agent has multiple buffers that -/// but a limit should be placed on their combined inventory, such as a -/// facility-wide maximum inventory. Like ResBufs, ResBufTracker has an -/// infinite capacity unless explicitly changed. -/// -/// ResBufTracker does not hold any inventory itself, it only tracks the -/// quantities of other ResBufs. ResBufTracker currently tracks quantities -/// alone, not compositions. -/// -/// @code -/// class MyAgent : public cyclus:: Facility { -/// public: -/// EnterNotify() { -/// cyclus::Facility::EnterNotify(); -/// tracker_.Init({&inventory_, &outventory_}, facility_max_inv_size_); -/// -/// cyclus::MatlBuyPolicy p; -/// p.Init(this, &inventory_, "inventory", &tracker_); -/// } -/// protected: -/// cyclus::toolkit::ResBuf inventory_; -/// cyclus::toolkit::ResBuf outventory_; -/// -/// double facility_max_inv_size_ = 1000; -/// cyclus::toolkit::ResBufTracker tracker_; -/// } - -class ResBufTracker { - public: - /// Creates an uninitialized tracker. The Init function MUST be called before - /// the tracker is used. - ResBufTracker() : max_inv_size_(std::numeric_limits::max()), qty_(0) {}; - - ResBufTracker(std::vector*> bufs, - double max_inv_size = std::numeric_limits::max()) { - Init(bufs, max_inv_size); - } - - ~ResBufTracker() {}; - - /// Initializes the tracker with the given ResBufs. The tracker will have - /// infinite capacity unless explicitly changed. - void Init(std::vector*> bufs, - double max_inv_size = std::numeric_limits::max()) { - bufs_ = bufs; - max_inv_size_ = max_inv_size; - } - - /// Returns the total quantity of all tracked ResBufs. - /// @throws ValueError if the tracker has not been initialized (zero) - inline double quantity() { - int num = num_bufs(); - if (num == 0) { - throw ValueError("ResBufTracker has not been initialized, no buffers to track"); - } - for (int i = 0; i < num; i++) { - qty_ += bufs_[i]->quantity(); - } - return qty_; - } - - /// Returns the total capacity of all tracked ResBufs. - inline double capacity() {return max_inv_size_;}; - - /// Returns the remaining facility-wide space across all tracked ResBufs. - inline double space() {return std::max(0.0, max_inv_size_ - qty_);}; - - /// Returns the remaining space in the given ResBuf, considering the - /// facility-wide limitations. - inline double buf_space(ResBuf* buf) {return std::min(buf->space(), space());}; - - /// Returns true if there are no resources in any buffer - inline bool empty() {return quantity() == 0;}; - - /// Returns number of buffers being tracked - /// @throws ValueError if the tracker has not been initialized (zero) - inline int num_bufs() { - int num = bufs_.size(); - if (num == 0) { - throw ValueError("ResBufTracker has not been initialized, no buffers to track"); - } - return num;}; - - /// Change the total capacity across all ResBufs. The new capacity must be - /// greater than the current quantity. - /// @throws ValueError if the new capacity is less than the current quantity - void set_capacity(double cap) { - if (quantity() - cap > eps_rsrc()) { - std::stringstream ss; - ss << std::setprecision(17) << "new capacity " << cap << - " lower than existing quantity " << quantity(); - throw ValueError(ss.str()); - } - max_inv_size_ = cap; - } - - private: - double max_inv_size_; - double qty_; - std::vector*> bufs_; -}; - -} // namespace toolkit -} // namespace cyclus - -#endif // CYCLUS_SRC_TOOLKIT_RES_BUF_TRACKER_H_ \ No newline at end of file diff --git a/tests/toolkit/matl_buy_policy_tests.cc b/tests/toolkit/matl_buy_policy_tests.cc index fb98a98ec3..b21cc1dd58 100644 --- a/tests/toolkit/matl_buy_policy_tests.cc +++ b/tests/toolkit/matl_buy_policy_tests.cc @@ -45,7 +45,7 @@ TEST_F(MatlBuyPolicyTests, Init) { double cap = 5; ResBuf buff; buff.capacity(cap); - ResBufTracker buff_tracker({&buff}); + TotalInvTracker buff_tracker({&buff}); std::cerr << "buf cap: " << buff.capacity() << std::endl; MatlBuyPolicy p; std::cerr << "policy created" << std::endl; @@ -98,7 +98,7 @@ TEST_F(MatlBuyPolicyTests, StartStop) { double cap = 5; ResBuf buff; buff.capacity(cap); - ResBufTracker buff_tracker({&buff}); + TotalInvTracker buff_tracker({&buff}); MatlBuyPolicy p; ASSERT_THROW(p.Init(NULL, &buff, "", &buff_tracker), ValueError); } @@ -108,7 +108,7 @@ TEST_F(MatlBuyPolicyTests, OneReq) { double cap = 5; ResBuf buff; buff.capacity(cap); - ResBufTracker buff_tracker({&buff}); + TotalInvTracker buff_tracker({&buff}); cyclus::Composition::Ptr c1 = cyclus::Composition::Ptr(new TestComp()); MatlBuyPolicy p; @@ -126,7 +126,7 @@ TEST_F(MatlBuyPolicyTests, MultipleReqs) { double cap = 5; ResBuf buff; buff.capacity(cap); - ResBufTracker buff_tracker({&buff}); + TotalInvTracker buff_tracker({&buff}); std::string commod1("foo"), commod2("bar"); double p2 = 2.5; cyclus::Composition::Ptr c1 = cyclus::Composition::Ptr(new TestComp()); @@ -155,7 +155,7 @@ TEST_F(MatlBuyPolicyTests, Quantize) { double cap = 5; ResBuf buff; buff.capacity(cap); - ResBufTracker buff_tracker({&buff}); + TotalInvTracker buff_tracker({&buff}); double p2 = 2.5; cyclus::Composition::Ptr c1 = cyclus::Composition::Ptr(new TestComp()); MatlBuyPolicy p; @@ -175,7 +175,7 @@ TEST_F(MatlBuyPolicyTests, MultiReqQuantize) { double cap = 5; ResBuf buff; buff.capacity(cap); - ResBufTracker buff_tracker({&buff}); + TotalInvTracker buff_tracker({&buff}); cyclus::Composition::Ptr c1 = cyclus::Composition::Ptr(new TestComp()); cyclus::Composition::Ptr c2 = cyclus::Composition::Ptr(new TestComp()); MatlBuyPolicy p; @@ -213,7 +213,7 @@ TEST_F(MatlBuyPolicyTests, DefaultFixedActiveDormant) { TestFacility* fac = dynamic_cast(sim.agent); cyclus::toolkit::ResBuf inbuf; - ResBufTracker buf_tracker({&inbuf}); + TotalInvTracker buf_tracker({&inbuf}); cyclus::toolkit::MatlBuyPolicy policy; policy.Init(fac, &inbuf, "inbuf", &buf_tracker, throughput, a_d_dist, a_d_dist, NULL) .Set("commod1").Start(); @@ -250,7 +250,7 @@ TEST_F(MatlBuyPolicyTests, FixedActiveDormant) { TestFacility* fac = dynamic_cast(sim.agent); cyclus::toolkit::ResBuf inbuf; - ResBufTracker buf_tracker({&inbuf}); + TotalInvTracker buf_tracker({&inbuf}); cyclus::toolkit::MatlBuyPolicy policy; policy.Init(fac, &inbuf, "inbuf", &buf_tracker, throughput, a_d_dist, a_d_dist, size_dist) .Set("commod1").Start(); @@ -285,7 +285,7 @@ TEST_F(MatlBuyPolicyTests, FixedActiveDormantMultipleCycles) { TestFacility* fac = dynamic_cast(sim.agent); cyclus::toolkit::ResBuf inbuf; - ResBufTracker buf_tracker({&inbuf}); + TotalInvTracker buf_tracker({&inbuf}); cyclus::toolkit::MatlBuyPolicy policy; policy.Init(fac, &inbuf, "inbuf", &buf_tracker, throughput, a_dist, d_dist, NULL) .Set("commod1").Start(); @@ -326,7 +326,7 @@ TEST_F(MatlBuyPolicyTests, UniformActiveDormant) { TestFacility* fac = dynamic_cast(sim.agent); cyclus::toolkit::ResBuf inbuf; - ResBufTracker buf_tracker({&inbuf}); + TotalInvTracker buf_tracker({&inbuf}); cyclus::toolkit::MatlBuyPolicy policy; policy.Init(fac, &inbuf, "inbuf", &buf_tracker, throughput, a_dist, d_dist, NULL) .Set("commod1").Start(); @@ -365,7 +365,7 @@ TEST_F(MatlBuyPolicyTests, NormalActiveDormant) { TestFacility* fac = dynamic_cast(sim.agent); cyclus::toolkit::ResBuf inbuf; - ResBufTracker buf_tracker({&inbuf}); + TotalInvTracker buf_tracker({&inbuf}); cyclus::toolkit::MatlBuyPolicy policy; policy.Init(fac, &inbuf, "inbuf", &buf_tracker, throughput, a_dist, d_dist, NULL) .Set("commod1").Start(); @@ -404,7 +404,7 @@ TEST_F(MatlBuyPolicyTests, MixedActiveDormant) { TestFacility* fac = dynamic_cast(sim.agent); cyclus::toolkit::ResBuf inbuf; - ResBufTracker buf_tracker({&inbuf}); + TotalInvTracker buf_tracker({&inbuf}); cyclus::toolkit::MatlBuyPolicy policy; policy.Init(fac, &inbuf, "inbuf", &buf_tracker, throughput, a_dist, d_dist, NULL) .Set("commod1").Start(); @@ -439,7 +439,7 @@ TEST_F(MatlBuyPolicyTests, RandomSizeUniform) { TestFacility* fac = dynamic_cast(sim.agent); cyclus::toolkit::ResBuf inbuf; - ResBufTracker buf_tracker({&inbuf}); + TotalInvTracker buf_tracker({&inbuf}); cyclus::toolkit::MatlBuyPolicy policy; policy.Init(fac, &inbuf, "inbuf", &buf_tracker, throughput, NULL, NULL, size_dist) .Set("commod1").Start(); @@ -470,7 +470,7 @@ TEST_F(MatlBuyPolicyTests, RandomSizeNormal) { TestFacility* fac = dynamic_cast(sim.agent); cyclus::toolkit::ResBuf inbuf; - ResBufTracker buf_tracker({&inbuf}); + TotalInvTracker buf_tracker({&inbuf}); cyclus::toolkit::MatlBuyPolicy policy; policy.Init(fac, &inbuf, "inbuf", &buf_tracker, throughput, NULL, NULL, size_dist) .Set("commod1").Start(); @@ -503,7 +503,7 @@ TEST_F(MatlBuyPolicyTests, RandomSizeAndFrequency) { TestFacility* fac = dynamic_cast(sim.agent); cyclus::toolkit::ResBuf inbuf; - ResBufTracker buf_tracker({&inbuf}); + TotalInvTracker buf_tracker({&inbuf}); cyclus::toolkit::MatlBuyPolicy policy; policy.Init(fac, &inbuf, "inbuf", &buf_tracker, throughput, a_dist, d_dist, size_dist) .Set("commod1").Start(); From e357a06576531a2085ae380c05c9e095e891cd5d Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Fri, 26 Jan 2024 13:08:30 -0700 Subject: [PATCH 05/20] CHANGELOG --- CHANGELOG.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 144cb5fb94..78fd3d6f7b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -25,6 +25,7 @@ Since last release * Add random number generator (Mersenne Twister 19937, from boost) and the ability to set the seed in the simulation control block (#1599, #1639) * Allow randomness in request frequency and size through buy policy (#1634) * Adds support for Cython3 (#1636) +* Adds TotalInvTracker, which allows an inventory cap to be set for multiple resource buffers, and is now required for material buy policy (#1646) **Changed:** From 6fd26df264386fd5c4a4e01fb62523ce26d01d95 Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Fri, 26 Jan 2024 13:14:34 -0700 Subject: [PATCH 06/20] throw error if init with bad parameters --- src/toolkit/total_inv_tracker.h | 6 +++++ tests/toolkit/total_inv_tracker_tests.cc | 33 ++++++++++++++---------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/toolkit/total_inv_tracker.h b/src/toolkit/total_inv_tracker.h index 882540b759..90232af1dc 100644 --- a/src/toolkit/total_inv_tracker.h +++ b/src/toolkit/total_inv_tracker.h @@ -53,7 +53,13 @@ class TotalInvTracker { /// infinite capacity unless explicitly changed. void Init(std::vector*> bufs, double max_inv_size = std::numeric_limits::max()) { + if (bufs.size() == 0) { + throw ValueError("TotalInvTracker must be initialized with at least one ResBuf"); + } bufs_ = bufs; + if (max_inv_size <= 0) { + throw ValueError("TotalInvTracker must be initialized with a positive capacity"); + } max_inv_size_ = max_inv_size; } diff --git a/tests/toolkit/total_inv_tracker_tests.cc b/tests/toolkit/total_inv_tracker_tests.cc index d72b85d9b7..d9c1ff1371 100644 --- a/tests/toolkit/total_inv_tracker_tests.cc +++ b/tests/toolkit/total_inv_tracker_tests.cc @@ -5,13 +5,11 @@ namespace cyclus { namespace toolkit { -TEST_F(TotalInvTrackerTest, empty) { - EXPECT_THROW(empty_tracker_.empty(), ValueError); - EXPECT_THROW(empty_tracker_.quantity(), ValueError); - EXPECT_THROW(empty_tracker_.capacity(), ValueError); - EXPECT_THROW(empty_tracker_.set_capacity(-1), ValueError); - EXPECT_THROW(empty_tracker_.set_capacity(1000), ValueError); - EXPECT_THROW(empty_tracker_.space(), ValueError); + +TEST_F(TotalInvTrackerTest, Init) { + EXPECT_THROW(empty_tracker_.Init({}), ValueError); + EXPECT_THROW(empty_tracker_.Init({&buf0_, &buf1_}, 0), ValueError); + EXPECT_THROW(empty_tracker_.Init({&buf0_, &buf1_}, -1), ValueError); } TEST_F(TotalInvTrackerTest, quantity) { @@ -28,12 +26,6 @@ TEST_F(TotalInvTrackerTest, capacity) { EXPECT_EQ(multi_tracker_.tracker_capacity(), max_inv_size_); } -TEST_F(TotalInvTrackerTest, set_capacity) { - EXPECT_THROW(multi_tracker_.set_capacity(-1), ValueError); - EXPECT_THROW(multi_tracker_.set_capacity(10), ValueError); - EXPECT_NO_THROW(multi_tracker_.set_capacity(1000)); -} - TEST_F(TotalInvTrackerTest, space) { EXPECT_EQ(single_tracker_.space(), 20 - qty1_); EXPECT_EQ(multi_tracker_.space(), max_inv_size_ - qty1_ - qty2_); @@ -44,11 +36,26 @@ TEST_F(TotalInvTrackerTest, buf_space) { EXPECT_EQ(multi_tracker_.buf_space(&buf2_), max_inv_size_ - qty1_ - qty2_); } +TEST_F(TotalInvTrackerTest, empty) { + EXPECT_THROW(empty_tracker_.empty(), ValueError); + EXPECT_THROW(empty_tracker_.quantity(), ValueError); + EXPECT_THROW(empty_tracker_.capacity(), ValueError); + EXPECT_THROW(empty_tracker_.set_capacity(-1), ValueError); + EXPECT_THROW(empty_tracker_.set_capacity(1000), ValueError); + EXPECT_THROW(empty_tracker_.space(), ValueError); +} + TEST_F(TotalInvTrackerTest, num_bufs) { EXPECT_THROW(empty_tracker_.num_bufs(), ValueError); EXPECT_NO_THROW(multi_tracker_.num_bufs()); EXPECT_EQ(multi_tracker_.num_bufs(), 3); } +TEST_F(TotalInvTrackerTest, set_capacity) { + EXPECT_THROW(multi_tracker_.set_capacity(-1), ValueError); + EXPECT_THROW(multi_tracker_.set_capacity(10), ValueError); + EXPECT_NO_THROW(multi_tracker_.set_capacity(1000)); +} + } // namespace toolkit } // namespace cyclus From 80f3bfe16613f11e99bad0594e04f6080acd3e06 Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Mon, 29 Jan 2024 09:40:24 -0700 Subject: [PATCH 07/20] more renaming to total_inv --- src/toolkit/matl_buy_policy.cc | 16 +++++----------- src/toolkit/matl_buy_policy.h | 3 +-- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/toolkit/matl_buy_policy.cc b/src/toolkit/matl_buy_policy.cc index d6b96c97c2..dbf54e3aaa 100644 --- a/src/toolkit/matl_buy_policy.cc +++ b/src/toolkit/matl_buy_policy.cc @@ -43,13 +43,7 @@ void MatlBuyPolicy::set_manager(Agent* m) { } } -void MatlBuyPolicy::create_buf_tracker() { - std::vector*> bufs = {buf_}; - buf_tracker_ = new TotalInvTracker(); - buf_tracker_->Init(bufs, buf_->capacity()); -} - -void MatlBuyPolicy::set_buf_tracker(TotalInvTracker* t) { +void MatlBuyPolicy::set_total_inv_tracker(TotalInvTracker* t) { if (t == NULL){ std::vector*> bufs = {buf_}; buf_tracker_->Init(bufs, buf_->capacity()); @@ -116,7 +110,7 @@ MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, set_manager(manager); buf_ = buf; name_ = name; - set_buf_tracker(buf_tracker); + set_total_inv_tracker(buf_tracker); init_active_dormant(); return *this; } @@ -129,7 +123,7 @@ MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, set_manager(manager); buf_ = buf; name_ = name; - set_buf_tracker(buf_tracker); + set_total_inv_tracker(buf_tracker); set_throughput(throughput); active_dist_ = active_dist; dormant_dist_ = dormant_dist; @@ -145,7 +139,7 @@ MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, set_manager(manager); buf_ = buf; name_ = name; - create_buf_tracker(); + set_total_inv_tracker(buf_tracker); set_fill_to(fill_to); set_req_when_under(req_when_under); init_active_dormant(); @@ -161,7 +155,7 @@ MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, set_manager(manager); buf_ = buf; name_ = name; - create_buf_tracker(); + set_total_inv_tracker(buf_tracker); set_fill_to(fill_to); set_req_when_under(req_when_under); set_quantize(quantize); diff --git a/src/toolkit/matl_buy_policy.h b/src/toolkit/matl_buy_policy.h index d1b80e0624..33cfb92e33 100644 --- a/src/toolkit/matl_buy_policy.h +++ b/src/toolkit/matl_buy_policy.h @@ -196,8 +196,7 @@ class MatlBuyPolicy : public Trader { }; void set_manager(Agent* m); - void create_buf_tracker(); - void set_buf_tracker(TotalInvTracker* t); + void set_total_inv_tracker(TotalInvTracker* t); /// requires buf_ already set void set_fill_to(double x); /// requires buf_ already set From 249aa157d90a0ead22739e695b8bdbecd9df7bb2 Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Mon, 29 Jan 2024 09:40:45 -0700 Subject: [PATCH 08/20] test for implementation in buy pol --- tests/toolkit/matl_buy_policy_tests.cc | 46 ++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/toolkit/matl_buy_policy_tests.cc b/tests/toolkit/matl_buy_policy_tests.cc index b21cc1dd58..630c50fd61 100644 --- a/tests/toolkit/matl_buy_policy_tests.cc +++ b/tests/toolkit/matl_buy_policy_tests.cc @@ -196,6 +196,52 @@ TEST_F(MatlBuyPolicyTests, MultiReqQuantize) { ASSERT_FLOAT_EQ(req->target()->quantity(), quantize); } +TEST_F(MatlBuyPolicyTests, TotalInvTracker) { + using cyclus::QueryResult; + + int dur = 5; + double throughput = 1; + + cyclus::MockSim sim(dur); + cyclus::Agent* a = new TestFacility(sim.context()); + sim.context()->AddPrototype(a->prototype(), a); + sim.agent = sim.context()->CreateAgent(a->prototype()); + TestFacility* fac = dynamic_cast(sim.agent); + sim.AddSource("commod1").Finalize(); + + cyclus::toolkit::ResBuf inbuf; + inbuf.capacity(10); + cyclus::toolkit::ResBuf otherbuf; + otherbuf.capacity(10); + + cyclus::CompMap cm; + cm[1001] = 1; + cyclus::Material::Ptr mat = cyclus::Material::Create(fac, 2, cyclus::Composition::CreateFromAtom(cm)); + otherbuf.Push(mat); + + double total_capacity = 5; + TotalInvTracker buf_tracker({&inbuf, &otherbuf}, total_capacity); + cyclus::toolkit::MatlBuyPolicy policy; + policy.Init(fac, &inbuf, "inbuf", &buf_tracker, throughput) + .Set("commod1").Start(); + + EXPECT_FALSE(buf_tracker.empty()); + EXPECT_EQ(buf_tracker.quantity(), 2); + EXPECT_EQ(buf_tracker.capacity(), total_capacity); + EXPECT_EQ(buf_tracker.space(), total_capacity - 2); + + EXPECT_NO_THROW(sim.Run()); + + QueryResult qr = sim.db().Query("Transactions", NULL); + int n_trans = qr.rows.size(); + EXPECT_EQ(n_trans, 3); + + EXPECT_EQ(buf_tracker.quantity(), 5); + EXPECT_EQ(buf_tracker.space(), 0); + EXPECT_EQ(inbuf.space(), inbuf.capacity() - inbuf.quantity()); + EXPECT_EQ(buf_tracker.buf_space(&inbuf), 0); +} + TEST_F(MatlBuyPolicyTests, DefaultFixedActiveDormant) { using cyclus::QueryResult; From 2a6517d7911536b09738cd8a6fa5bcdc5797447f Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Tue, 30 Jan 2024 14:32:46 -0700 Subject: [PATCH 09/20] RQ and sS buy policies --- src/toolkit/matl_buy_policy.cc | 87 +++++++++++++++------------------- src/toolkit/matl_buy_policy.h | 47 +++++++++--------- 2 files changed, 62 insertions(+), 72 deletions(-) diff --git a/src/toolkit/matl_buy_policy.cc b/src/toolkit/matl_buy_policy.cc index ae908c62db..fe7c758cdc 100644 --- a/src/toolkit/matl_buy_policy.cc +++ b/src/toolkit/matl_buy_policy.cc @@ -18,10 +18,8 @@ MatlBuyPolicy::MatlBuyPolicy() : name_(""), throughput_(std::numeric_limits::max()), quantize_(-1), - fill_to_(1), - req_when_under_(1), - reorder_amt_(0), - RQ_exclusive_(false), + fill_to_(std::numeric_limits::max()), + req_at_(std::numeric_limits::max()), active_dist_(NULL), dormant_dist_(NULL), size_dist_(NULL){ @@ -55,18 +53,29 @@ void MatlBuyPolicy::set_total_inv_tracker(TotalInvTracker* t) { } } +void MatlBuyPolicy::set_inv_policy(std::string inv_policy, double fill, double req_at) { + set_req_at(req_at); + if ((inv_policy == "sS") || (inv_policy == "Ss")) { + set_fill_to(fill); + } + else if ((inv_policy == "RQ") || (inv_policy == "QR")) { + set_quantize(fill); + // maximum amount that an RQ policy could achieve is req_at + fill + set_fill_to(req_at + fill); + } + else { + throw ValueError("Invalid inventory policy"); + } +} + void MatlBuyPolicy::set_fill_to(double x) { - if (x > 1) - x /= buf_->capacity(); - assert(x > 0 && x <= 1.); + assert(x > 0); fill_to_ = x; } -void MatlBuyPolicy::set_req_when_under(double x) { - if (x > 1) - x /= buf_->capacity(); - assert(x >= 0 && x <= 1.); - req_when_under_ = x; +void MatlBuyPolicy::set_req_at(double x) { + assert(x >= 0); + req_at_ = x; } void MatlBuyPolicy::set_quantize(double x) { @@ -107,14 +116,6 @@ void MatlBuyPolicy::init_active_dormant() { } } -void MatlBuyPolicy::set_reorder_amt(double x) { - reorder_amt_ = x; -} - -void MatlBuyPolicy::set_RQ_exclusive(bool x) { - RQ_exclusive_ = x; -} - MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, std::string name, TotalInvTracker* buf_tracker) { set_manager(manager); @@ -143,15 +144,14 @@ MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, } MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, - std::string name, - TotalInvTracker* buf_tracker, - double fill_to, double req_when_under) { + std::string name, TotalInvTracker* buf_tracker, + double throughput, double quantize) { set_manager(manager); buf_ = buf; name_ = name; set_total_inv_tracker(buf_tracker); - set_fill_to(fill_to); - set_req_when_under(req_when_under); + set_throughput(throughput); + set_quantize(quantize); init_active_dormant(); return *this; } @@ -160,32 +160,28 @@ MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, std::string name, TotalInvTracker* buf_tracker, double throughput, - double fill_to, double req_when_under, - double quantize) { + std::string inv_policy, + double fill_behav, double req_at) { set_manager(manager); buf_ = buf; name_ = name; set_total_inv_tracker(buf_tracker); - set_fill_to(fill_to); - set_req_when_under(req_when_under); - set_quantize(quantize); + set_inv_policy(inv_policy, fill_behav, req_at); set_throughput(throughput); init_active_dormant(); return *this; } MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, - std::string name, double req_when_under, - double reorder_amt, bool RQ_exclusive) { + std::string name, + TotalInvTracker* buf_tracker, + std::string inv_policy, + double fill_behav, double req_at) { set_manager(manager); buf_ = buf; name_ = name; - set_req_when_under(req_when_under); - set_reorder_amt(reorder_amt); - set_RQ_exclusive(RQ_exclusive); - if (RQ_exclusive) { - set_quantize(reorder_amt); - } + set_total_inv_tracker(buf_tracker); + set_inv_policy(inv_policy, fill_behav, req_at); init_active_dormant(); return *this; } @@ -230,11 +226,13 @@ void MatlBuyPolicy::Stop() { std::set::Ptr> MatlBuyPolicy::GetMatlRequests() { rsrc_commods_.clear(); std::set::Ptr> ports; - bool make_req = buf_->quantity() < req_when_under_ * buf_->capacity(); + if (!MakeReq()) { + return ports; + } + double amt; int current_time_ = manager()->context()->time(); - std::cerr << "for time " << current_time_ << ", make_req is " << make_req << std::endl; if (never_dormant() || current_time_ < next_active_end_) { // currently in the middle of active buying period @@ -255,20 +253,13 @@ std::set::Ptr> MatlBuyPolicy::GetMatlRequests() { LGH(INFO4) << "end of dormant period, next active time end: " << next_active_end_ << ", and next dormant time end: " << next_dormant_end_ << std::endl; } - std::cerr << "amt is " << amt << std::endl; - - if (!make_req || amt < eps()) + if (amt < eps()) return ports; bool excl = Excl(); - if ((reorder_amt_ > 0) && (amt > reorder_amt_)) { - std::cerr << "reorder_amt_ is " << reorder_amt_ << std::endl; - amt = reorder_amt_; - } double req_amt = ReqQty(amt); int n_req = NReq(amt); LGH(INFO3) << "requesting " << amt << " kg via " << n_req << " request(s)" << std::endl; - std::cerr << "requesting " << amt << " kg via " << n_req << " request(s)" << std::endl; // one portfolio for each request for (int i = 0; i != n_req; i++) { diff --git a/src/toolkit/matl_buy_policy.h b/src/toolkit/matl_buy_policy.h index 332edb6668..bfe8274aef 100644 --- a/src/toolkit/matl_buy_policy.h +++ b/src/toolkit/matl_buy_policy.h @@ -71,16 +71,12 @@ class MatlBuyPolicy : public Trader { /// @param name a unique name identifying this policy /// @param throughput a constraining value for total transaction quantities in /// a single time step - /// @param fill_to the amount or fraction of inventory to order when placing + /// @param inv_policy the inventory policy to use. Options are "sS" and "RQ" + /// @param fill_behav the amount or fraction of inventory to order when placing /// an order. This is equivalent to the S in an (s, S) inventory policy. - /// @param reorder_quantity is the size of request to place when the buf has - /// reached reorder point. This is equivalent to the Q in an (R, Q) inventory - /// policy. - /// @param RQ_exclusive If true, the policy will make a quantized request - /// for reorder_quantity - /// @param req_when_under place an request when the buf's quantity is less - /// than its capacity * fill_to (as a fraction). This is equivalent to the s - /// in an (s, S) inventory policy. + + /// @param req_at the inventory minimum. If the buffer is below + /// this value, new material will be requested up to /// @param quantize If quantize is greater than zero, the policy will make /// exclusive, integral quantize kg requests. Otherwise, single requests will /// be sent to fill the buffer's empty space. @@ -116,15 +112,15 @@ class MatlBuyPolicy : public Trader { boost::shared_ptr dormant_dist = NULL, boost::shared_ptr size_dist = NULL); MatlBuyPolicy& Init(Agent* manager, ResBuf* buf, std::string name, - TotalInvTracker* buf_tracker, - double fill_to, double req_when_under); + TotalInvTracker* buf_tracker, double throughput, + double quantize); MatlBuyPolicy& Init(Agent* manager, ResBuf* buf, std::string name, TotalInvTracker* buf_tracker, - double throughput, double fill_to, - double req_when_under, double quantize); + double throughput, std::string inv_policy, + double fill_behav, double req_at); MatlBuyPolicy& Init(Agent* manager, ResBuf* buf, std::string name, - double req_when_under, double reorder_amt, - bool RQ_exclusive); + TotalInvTracker* buf_tracker, std::string inv_policy, + double fill_behav, double req_at); /// @} /// Instructs the policy to fill its buffer with requests on the given @@ -157,12 +153,17 @@ class MatlBuyPolicy : public Trader { /// the total amount available to request inline double TotalAvailable() const { return std::min({throughput_, - (fill_to_ * buf_->capacity()) - buf_->quantity(), + fill_to_ - buf_->quantity(), + buf_->space(), buf_tracker_->space()}); } + /// whether a request can be made + inline bool MakeReq() const { return buf_->quantity() <= req_at_; } + /// whether trades will be denoted as exclusive or not - inline bool Excl() const { return quantize_ > 0; } + inline bool Excl() const { + return quantize_ > 0; } /// the amount requested per each request @@ -205,22 +206,20 @@ class MatlBuyPolicy : public Trader { void set_manager(Agent* m); void set_total_inv_tracker(TotalInvTracker* t); + void set_inv_policy(std::string p, double x, + double y = std::numeric_limits::max()); /// requires buf_ already set void set_fill_to(double x); /// requires buf_ already set - void set_req_when_under(double x); + void set_req_at(double x); void set_quantize(double x); void set_throughput(double x); void init_active_dormant(); - void set_reorder_amt(double x); - void set_RQ_exclusive(bool x); ResBuf* buf_; TotalInvTracker* buf_tracker_; - std::string name_; - double fill_to_, req_when_under_, quantize_, throughput_; - double reorder_amt_; - bool RQ_exclusive_; + std::string name_, inv_policy; + double fill_to_, req_at_, quantize_, throughput_; int next_active_end_= 0; int next_dormant_end_= 0; From 17b7c05706d1b08cc531e2f28e519c845185c4bd Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Tue, 30 Jan 2024 14:33:01 -0700 Subject: [PATCH 10/20] RQ and sS buy policy tests --- tests/toolkit/matl_buy_policy_tests.cc | 109 ++++++++++++++----------- 1 file changed, 63 insertions(+), 46 deletions(-) diff --git a/tests/toolkit/matl_buy_policy_tests.cc b/tests/toolkit/matl_buy_policy_tests.cc index 223547245a..9751419ae7 100644 --- a/tests/toolkit/matl_buy_policy_tests.cc +++ b/tests/toolkit/matl_buy_policy_tests.cc @@ -42,17 +42,14 @@ class MatlBuyPolicyTests: public ::testing::Test { }; TEST_F(MatlBuyPolicyTests, Init) { - double cap = 5; + double cap = 10; ResBuf buff; buff.capacity(cap); TotalInvTracker buff_tracker({&buff}); - std::cerr << "buf cap: " << buff.capacity() << std::endl; MatlBuyPolicy p; - std::cerr << "policy created" << std::endl; // defaults p.Init(fac1, &buff, "", &buff_tracker); - std::cerr << "policy initialized" << std::endl; double amt = p.TotalAvailable(); ASSERT_FLOAT_EQ(amt, cap); ASSERT_FLOAT_EQ(p.ReqQty(amt), cap); @@ -60,7 +57,7 @@ TEST_F(MatlBuyPolicyTests, Init) { // throughput double throughput = cap - 1; - p.Init(fac1, &buff, "", &buff_tracker, throughput, 1, 1, -1); + p.Init(fac1, &buff, "", &buff_tracker, throughput); amt = p.TotalAvailable(); ASSERT_FLOAT_EQ(amt, throughput); ASSERT_FLOAT_EQ(p.ReqQty(amt), throughput); @@ -68,19 +65,28 @@ TEST_F(MatlBuyPolicyTests, Init) { // exclusive orders double quantize = 2.5; - p.Init(fac1, &buff, "", &buff_tracker, std::numeric_limits::max(), 1, 1, quantize); + p.Init(fac1, &buff, "", &buff_tracker, std::numeric_limits::max(), + quantize); amt = p.TotalAvailable(); ASSERT_FLOAT_EQ(amt, cap); ASSERT_FLOAT_EQ(p.ReqQty(amt), quantize); ASSERT_EQ(p.NReq(amt), static_cast(cap / quantize)); +} + +TEST_F(MatlBuyPolicyTests, Init_sS) { + double cap = 10; + ResBuf buff; + buff.capacity(cap); + TotalInvTracker buff_tracker({&buff}); + MatlBuyPolicy p; - // S,s with nothing in buffer +// S,s with nothing in buffer double S = 4, s = 2; - // reset - p.Init(fac1, &buff, "", &buff_tracker, std::numeric_limits::max(), 1, 1, -1); + p.Init(fac1, &buff, "", &buff_tracker, std::numeric_limits::max()); // use Ss constructor - p.Init(fac1, &buff, "", &buff_tracker, S, s); - amt = p.TotalAvailable(); + p.Init(fac1, &buff, "", &buff_tracker, "sS", S, s); + ASSERT_TRUE(p.MakeReq()); + double amt = p.TotalAvailable(); ASSERT_FLOAT_EQ(amt, S); ASSERT_FLOAT_EQ(p.ReqQty(amt), S); ASSERT_EQ(p.NReq(amt), 1); @@ -88,10 +94,52 @@ TEST_F(MatlBuyPolicyTests, Init) { // S,s with something in buffer Composition::Ptr c; buff.Push(Material::CreateUntracked(s, c)); + p.Init(fac1, &buff, "", &buff_tracker, "sS", S, s); + ASSERT_TRUE(p.MakeReq()); amt = p.TotalAvailable(); ASSERT_FLOAT_EQ(amt, S - s); ASSERT_FLOAT_EQ(p.ReqQty(amt), S - s); ASSERT_EQ(p.NReq(amt), 1); + + // S,s with too much in the buffer + buff.Push(Material::CreateUntracked(s, c)); + p.Init(fac1, &buff, "", &buff_tracker, "sS", S, s); + ASSERT_FALSE(p.MakeReq()); +} + +TEST_F(MatlBuyPolicyTests, Init_RQ) { + double cap = 10; + ResBuf buff; + buff.capacity(cap); + TotalInvTracker buff_tracker({&buff}); + MatlBuyPolicy p; + +// R,Q with nothing in buffer + double R = 2, Q = 4; + // reset + p.Init(fac1, &buff, "", &buff_tracker, std::numeric_limits::max()); + // use RQ constructor + p.Init(fac1, &buff, "", &buff_tracker, "RQ", Q, R); + ASSERT_TRUE(p.MakeReq()); + double amt = p.TotalAvailable(); + ASSERT_FLOAT_EQ(amt, 6); + ASSERT_FLOAT_EQ(p.ReqQty(amt), Q); + ASSERT_EQ(p.NReq(amt), 1); + + // R,Q with something in the buffer + Composition::Ptr c; + buff.Push(Material::CreateUntracked(R, c)); + p.Init(fac1, &buff, "", &buff_tracker, "RQ", Q, R); + ASSERT_TRUE(p.MakeReq()); + amt = p.TotalAvailable(); + ASSERT_FLOAT_EQ(amt, 4); + ASSERT_FLOAT_EQ(p.ReqQty(amt), Q); + ASSERT_EQ(p.NReq(amt), 1); + + // R,Q with too much in the buffer + buff.Push(Material::CreateUntracked(R, c)); + p.Init(fac1, &buff, "", &buff_tracker, "RQ", Q, R); + ASSERT_FALSE(p.MakeReq()); } TEST_F(MatlBuyPolicyTests, StartStop) { @@ -161,7 +209,8 @@ TEST_F(MatlBuyPolicyTests, Quantize) { MatlBuyPolicy p; double quantize = 2.5; - p.Init(fac1, &buff, "", &buff_tracker, std::numeric_limits::max(), 1, 1, quantize).Set("commod1", c1); + p.Init(fac1, &buff, "", &buff_tracker, std::numeric_limits::max(), + quantize).Set("commod1", c1); std::set::Ptr> obs = p.GetMatlRequests(); ASSERT_EQ(obs.size(), 2); ASSERT_EQ((*obs.begin())->requests().size(), 1); @@ -182,7 +231,8 @@ TEST_F(MatlBuyPolicyTests, MultiReqQuantize) { // two portfolios with quantize double quantize = 2.5; - p.Init(fac1, &buff, "", &buff_tracker, std::numeric_limits::max(), 1, 1, quantize).Set("commod1", c1).Set("commod2", c2); + p.Init(fac1, &buff, "", &buff_tracker, std::numeric_limits::max(), + quantize).Set("commod1", c1).Set("commod2", c2); std::set::Ptr> obs = p.GetMatlRequests(); ASSERT_EQ(obs.size(), 2); ASSERT_EQ((*obs.begin())->requests().size(), 2); @@ -567,38 +617,5 @@ TEST_F(MatlBuyPolicyTests, RandomSizeAndFrequency) { delete a; } -TEST_F(MatlBuyPolicyTests, RQInventory) { - using cyclus::QueryResult; - - int dur = 5; - double req_when_under = 1; - double reorder_amt = 1; - bool RQ_exclusive = false; - - cyclus::MockSim sim(dur); - cyclus::Agent* a = new TestFacility(sim.context()); - sim.context()->AddPrototype(a->prototype(), a); - sim.agent = sim.context()->CreateAgent(a->prototype());sim.AddSource("commod1").Finalize(); - - TestFacility* fac = dynamic_cast(sim.agent); - - cyclus::toolkit::ResBuf inbuf; - inbuf.capacity(3); - cyclus::toolkit::MatlBuyPolicy policy; - policy.Init(fac, &inbuf, "inbuf", req_when_under, reorder_amt, RQ_exclusive) - .Set("commod1").Start(); - - EXPECT_NO_THROW(sim.Run()); - - QueryResult qr = sim.db().Query("Transactions", NULL); - EXPECT_EQ(0, qr.GetVal("Time", 0)); - - qr = sim.db().Query("Resources", NULL); - EXPECT_NEAR(1, qr.GetVal("Quantity", 0), 0.00001); - EXPECT_NEAR(1, qr.GetVal("Quantity", 1), 0.00001); - - delete a; -} - } } From bbb20b438736a338c70d3465eaef887839146d65 Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Tue, 30 Jan 2024 14:33:51 -0700 Subject: [PATCH 11/20] add totalinvtracker as known type so archetypes can init buypol with one --- cli/cycpp.py | 10 +++++++++- cyclus/gentypesystem.py | 15 ++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/cli/cycpp.py b/cli/cycpp.py index 5e72815a7d..9eb8ab576d 100755 --- a/cli/cycpp.py +++ b/cli/cycpp.py @@ -102,11 +102,14 @@ ('{0}::toolkit::ResBuf'.format(CYCNS), CYCNS + '::Product'), ('{0}::toolkit::ResBuf'.format(CYCNS), CYCNS + '::Material'), '{0}::toolkit::ResMap'.format(CYCNS), + '{0}::toolkit::TotalInvTracker'.format(CYCNS), + ('{0}::toolkit::TotalInvTracker'.format(CYCNS), '{0}::toolkit::ResBuf'.format(CYCNS), CYCNS + '::Material'), } TEMPLATES = {'std::vector', 'std::set', 'std::list', 'std::pair', 'std::map', '{0}::toolkit::ResBuf'.format(CYCNS), - CYCNS + '::toolkit::ResMap',} + CYCNS + '::toolkit::ResMap', + CYCNS + '::toolkit::TotalInvTracker',} WRANGLERS = { '{0}::Agent'.format(CYCNS), @@ -1079,6 +1082,7 @@ def impl(self, ind=" "): "m->{var}.capacity());\n"), CYCNS + '::toolkit::ResBuf': "{var}.capacity(m->{var}.capacity());\n", CYCNS + '::toolkit::ResMap': '{var}.obj_ids(m->obj_ids());\n', + CYCNS + '::toolkit::TotalInvTracker': "{var}.capacity();\n", } class InitFromDbFilter(CodeGeneratorFilter): @@ -1145,6 +1149,7 @@ def impl(self, ind=" "): CYCNS + '::toolkit::ResBuf': '{var}.capacity({capacity});\n', CYCNS + '::toolkit::ResMap': ( '{var}.obj_ids(qr.GetVal<{tstr}>("{var}"))\n;'), + CYCNS + '::toolkit::TotalInvTracker': '{var}.capacity();\n', } @@ -1893,6 +1898,7 @@ def impl(self, ind=" "): CYCNS + '::toolkit::ResourceBuff': None, CYCNS + '::toolkit::ResBuf': None, CYCNS + '::toolkit::ResMap': '{var}.obj_ids()', + CYCNS + '::Toolkit::TotalInvTracker': None, } @@ -1947,6 +1953,7 @@ def impl(self, ind=" "): 'invs[\"{var}\"] = {var}.PopNRes({var}.count());\n' '{var}.Push(invs["{var}"]);\n'), CYCNS + '::toolkit::ResMap': "invs[\"{var}\"] = {var}.ResValues();\n", + CYCNS + '::toolkit::TotalInvTracker': ';\n', } @@ -1995,6 +2002,7 @@ def impl(self, ind=" "): CYCNS + '::toolkit::ResourceBuff': "{var}.PushAll(inv[\"{var}\"]);\n", CYCNS + '::toolkit::ResBuf': "{var}.Push(inv[\"{var}\"]);\n", CYCNS + '::toolkit::ResMap': "{var}.ResValues(inv[\"{var}\"]);\n", + CYCNS + '::toolkit::TotalInvTracker': ";\n", } diff --git a/cyclus/gentypesystem.py b/cyclus/gentypesystem.py index 5bc60fcd21..366a465987 100644 --- a/cyclus/gentypesystem.py +++ b/cyclus/gentypesystem.py @@ -377,6 +377,7 @@ def convert_to_cpp(self, x, t): 'std::list': 'std_list', 'std::vector': 'std_vector', 'cyclus::toolkit::ResBuf': 'cpp_cyclus.ResBuf', + 'cyclus::toolkit::TotalInvTracker': 'cpp_cyclus.TotalInvTracker', 'cyclus::toolkit::ResMap': 'cpp_cyclus.ResMap', } @@ -384,7 +385,7 @@ def convert_to_cpp(self, x, t): RESOURCES = ['MATERIAL', 'PRODUCT'] INVENTORIES = ['cyclus::toolkit::ResourceBuff', 'cyclus::toolkit::ResBuf', - 'cyclus::toolkit::ResMap'] + 'cyclus::toolkit::TotalInvTracker', 'cyclus::toolkit::ResMap'] USE_SHARED_PTR = ('MATERIAL', 'PRODUCT', 'cyclus::Material', 'cyclus::Product') @@ -419,6 +420,7 @@ def convert_to_cpp(self, x, t): 'std::list': 'std_list', 'std::vector': 'std_vector', 'cyclus::toolkit::ResBuf': 'res_buf', + 'cyclus::toolkit::TotalInvTracker': 'total_inv_tracker', 'cyclus::toolkit::ResMap': 'res_map', } @@ -453,6 +455,7 @@ def convert_to_cpp(self, x, t): 'std::list': 'List', 'std::vector': 'Vector', 'cyclus::toolkit::ResBuf': 'ResBuf', + 'cyclus::toolkit::TotalInvTracker': 'total_inv_tracker', 'cyclus::toolkit::ResMap': 'ResMap', } @@ -489,6 +492,7 @@ def convert_to_cpp(self, x, t): 'std::list': ('val',), 'std::vector': ('val',), 'cyclus::toolkit::ResBuf': ('val',), + 'cyclus::toolkit::TotalInvTracker': ('val',), 'cyclus::toolkit::ResMap': ('key', 'val'), } @@ -509,6 +513,7 @@ def convert_to_cpp(self, x, t): 'std::list': 'np.NPY_OBJECT', 'std::vector': 'np.NPY_OBJECT', 'cyclus::toolkit::ResBuf': 'np.NPY_OBJECT', + 'cyclus::toolkit::TotalInvTracker': 'np.NPY_OBJECT', 'cyclus::toolkit::ResMap': 'np.NPY_OBJECT', } @@ -529,6 +534,7 @@ def convert_to_cpp(self, x, t): 'std::list': '[]', 'std::vector': '[]', 'cyclus::toolkit::ResBuf': 'None', + 'cyclus::toolkit::TotalInvTracker': 'None', 'cyclus::toolkit::ResMap': 'None', } @@ -646,6 +652,7 @@ def convert_to_cpp(self, x, t): ' py{var}[i] = {var}_i\n', 'py{var}'), 'cyclus::toolkit::ResBuf': ('', '', 'None'), + 'cyclus::toolkit::TotalInvTracker': ('', '', 'None'), 'cyclus::toolkit::ResMap': ('', '', 'None'), } @@ -758,6 +765,12 @@ def convert_to_cpp(self, x, t): 'py{var} = <_{classname}> {var}\n' 'cpp{var} = deref(py{var}.ptx)\n', 'cpp{var}'), + 'cyclus::toolkit::TotalInvTracker': ( + 'cdef _{classname} py{var}\n' + 'cdef cpp_cyclus.TotalInvTracker[{valtype}] cpp{var}\n', + 'py{var} = <_{classname}> {var}\n' + 'cpp{var} = deref(py{var}.ptx)\n', + 'cpp{var}'), 'cyclus::toolkit::ResMap': ( 'cdef _{classname} py{var}\n' 'cdef cpp_cyclus.ResMap[{keytype}, {valtype}] cpp{var}\n', From 10e24373d417ebc44ab207c054e6aa68ff202d9d Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Wed, 31 Jan 2024 10:44:50 -0700 Subject: [PATCH 12/20] cumulative cap inventory --- src/toolkit/matl_buy_policy.cc | 66 +++++++++++++++++++++++--- src/toolkit/matl_buy_policy.h | 17 ++++--- tests/toolkit/matl_buy_policy_tests.cc | 35 ++++++++++++++ 3 files changed, 105 insertions(+), 13 deletions(-) diff --git a/src/toolkit/matl_buy_policy.cc b/src/toolkit/matl_buy_policy.cc index fe7c758cdc..8f5c000157 100644 --- a/src/toolkit/matl_buy_policy.cc +++ b/src/toolkit/matl_buy_policy.cc @@ -22,7 +22,9 @@ MatlBuyPolicy::MatlBuyPolicy() : req_at_(std::numeric_limits::max()), active_dist_(NULL), dormant_dist_(NULL), - size_dist_(NULL){ + size_dist_(NULL), + ccap_(-1), + cycle_total_inv_(0){ Warn( "MatlBuyPolicy is experimental and its API may be subject to change"); } @@ -78,6 +80,11 @@ void MatlBuyPolicy::set_req_at(double x) { req_at_ = x; } +void MatlBuyPolicy::set_ccap(double x) { + assert(x > 0); + ccap_ = x; +} + void MatlBuyPolicy::set_quantize(double x) { assert(x != 0); quantize_ = x; @@ -109,7 +116,11 @@ void MatlBuyPolicy::init_active_dormant() { if (dormant_dist_->sample() < 0) { next_dormant_end_ = -1; LGH(INFO4) << "dormant length -1, always active" << std::endl; - } + } + else if (ccap_ > 0) { + next_dormant_end_ = -1; + LGH(INFO4) << "dormant length set at -1 for first active period of ccap cycle" << std::endl; + } else { SetNextDormantTime(); LGH(INFO4) << "first dormant time end: " << next_dormant_end_ << std::endl; @@ -186,6 +197,22 @@ MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, return *this; } +MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, + std::string name, + TotalInvTracker* buf_tracker, + double throughput, double ccap, + boost::shared_ptr dormant_dist) { + set_manager(manager); + buf_ = buf; + name_ = name; + set_total_inv_tracker(buf_tracker); + set_throughput(throughput); + set_ccap(ccap); + dormant_dist_ = dormant_dist; + init_active_dormant(); + return *this; +} + MatlBuyPolicy& MatlBuyPolicy::Set(std::string commod) { CompMap c; c[10010000] = 1e-100; @@ -226,6 +253,8 @@ void MatlBuyPolicy::Stop() { std::set::Ptr> MatlBuyPolicy::GetMatlRequests() { rsrc_commods_.clear(); std::set::Ptr> ports; + + // (s,S)/(R,Q) inventory policy handling if (!MakeReq()) { return ports; } @@ -234,6 +263,7 @@ std::set::Ptr> MatlBuyPolicy::GetMatlRequests() { int current_time_ = manager()->context()->time(); + // period inventory handling if (never_dormant() || current_time_ < next_active_end_) { // currently in the middle of active buying period amt = TotalAvailable() * SampleRequestSize(); @@ -249,8 +279,15 @@ std::set::Ptr> MatlBuyPolicy::GetMatlRequests() { // finished dormant. starting buying and sample/set length of active period amt = TotalAvailable() * SampleRequestSize(); SetNextActiveTime(); - SetNextDormantTime(); - LGH(INFO4) << "end of dormant period, next active time end: " << next_active_end_ << ", and next dormant time end: " << next_dormant_end_ << std::endl; + if (ccap_ < 0) { + SetNextDormantTime(); + LGH(INFO4) << "end of dormant period, next active time end: " << next_active_end_ << ", and next dormant time end: " << next_dormant_end_ << std::endl; + } + else {next_dormant_end_ = -1;} + } + + if ((ccap_ != -1) && ((cycle_total_inv_ + amt) > ccap_)) { + amt = ccap_ - cycle_total_inv_; } if (amt < eps()) @@ -290,6 +327,17 @@ void MatlBuyPolicy::AcceptMatlTrades( LGH(INFO3) << "got " << it->second->quantity() << " kg of " << it->first.request->commodity() << std::endl; buf_->Push(it->second); + // ccap handling + if (ccap_ > 0) { + cycle_total_inv_ += it->second->quantity(); + } + } + // check if cumulative cap has been reached. If yes, then sample for dormant + // length and reset cycle_total_inv + if ((ccap_ > 0) && ((ccap_ - cycle_total_inv_) < eps())) { + SetNextDormantTime(); + LGH(INFO3) << "cycle cumulative inventory has been reached. Dormant period will end at " << next_dormant_end_ << std::endl; + cycle_total_inv_ = 0; } } @@ -299,9 +347,15 @@ void MatlBuyPolicy::SetNextActiveTime() { }; void MatlBuyPolicy::SetNextDormantTime() { - if (next_dormant_end_ < 0) {} + if (ccap_ > 0) { + // need the +1 when not using next_active_end_ + next_dormant_end_ = ( + dormant_dist_->sample() + manager()->context()->time() + 1); + } + else if (next_dormant_end_ < 0) {} else { - next_dormant_end_ = dormant_dist_->sample() + next_active_end_; + next_dormant_end_ = dormant_dist_->sample() + + std::max(next_active_end_, 1); } return; } diff --git a/src/toolkit/matl_buy_policy.h b/src/toolkit/matl_buy_policy.h index bfe8274aef..74ca572456 100644 --- a/src/toolkit/matl_buy_policy.h +++ b/src/toolkit/matl_buy_policy.h @@ -71,12 +71,10 @@ class MatlBuyPolicy : public Trader { /// @param name a unique name identifying this policy /// @param throughput a constraining value for total transaction quantities in /// a single time step - /// @param inv_policy the inventory policy to use. Options are "sS" and "RQ" - /// @param fill_behav the amount or fraction of inventory to order when placing - /// an order. This is equivalent to the S in an (s, S) inventory policy. - - /// @param req_at the inventory minimum. If the buffer is below - /// this value, new material will be requested up to + /// @param inv_policy the inventory policy to use. Options are "sS", "RQ". Each inventory policy options has two additional required parameters. An (s,S) inventory policy orders material only when the buffer is below the minimum value, s, and orders only enough to bring the buffer to the maximum value, S. An (s,S) policy allows partial orders. Set s as req_at and S as fill. An (R,Q) inventory policy requests material when the buffer is below the minimum value, R, and places an exclusive request for size Q. Set R as req_at and Q as fill. + /// @param req_at, the inventory minimum (s, and R) for (s,S) and (R,Q),. If the buffer has less than or equal to value, new material will be requested based on the policy in place. + /// @param fill_behav, the quantity govering the fill strategy for inventory policies. For (s,S), this is the maximum value, and material will be ordered up to this amount. For (R,Q), this is the quantity of material that will be ordered (exclusive). + /// @param ccap the cumulative capacity of material to be received in one active cycle. A ccap inventory policy allows for a cumulative capacity of material to be received in one active cycle. Once the cumulative capacity is recieved, the agent enters a dormant period. Also requires dormant distributions using dormant_dist. /// @param quantize If quantize is greater than zero, the policy will make /// exclusive, integral quantize kg requests. Otherwise, single requests will /// be sent to fill the buffer's empty space. @@ -121,6 +119,10 @@ class MatlBuyPolicy : public Trader { MatlBuyPolicy& Init(Agent* manager, ResBuf* buf, std::string name, TotalInvTracker* buf_tracker, std::string inv_policy, double fill_behav, double req_at); + MatlBuyPolicy& Init(Agent* manager, ResBuf* buf, std::string name, + TotalInvTracker* buf_tracker, double throughput, + double ccap, + boost::shared_ptr dormant_dist); /// @} /// Instructs the policy to fill its buffer with requests on the given @@ -212,6 +214,7 @@ class MatlBuyPolicy : public Trader { void set_fill_to(double x); /// requires buf_ already set void set_req_at(double x); + void set_ccap(double x); void set_quantize(double x); void set_throughput(double x); void init_active_dormant(); @@ -219,7 +222,7 @@ class MatlBuyPolicy : public Trader { ResBuf* buf_; TotalInvTracker* buf_tracker_; std::string name_, inv_policy; - double fill_to_, req_at_, quantize_, throughput_; + double fill_to_, req_at_, quantize_, throughput_, ccap_, cycle_total_inv_; int next_active_end_= 0; int next_dormant_end_= 0; diff --git a/tests/toolkit/matl_buy_policy_tests.cc b/tests/toolkit/matl_buy_policy_tests.cc index 9751419ae7..f105344243 100644 --- a/tests/toolkit/matl_buy_policy_tests.cc +++ b/tests/toolkit/matl_buy_policy_tests.cc @@ -617,5 +617,40 @@ TEST_F(MatlBuyPolicyTests, RandomSizeAndFrequency) { delete a; } +TEST_F(MatlBuyPolicyTests, CCap_Inventory) { + using cyclus::QueryResult; + + double ccap = 2; + boost::shared_ptr d_dist = boost::shared_ptr(new FixedIntDist(2)); + + int dur = 8; + double throughput = 1; + + cyclus::MockSim sim(dur); + cyclus::Agent* a = new TestFacility(sim.context()); + sim.context()->AddPrototype(a->prototype(), a); + sim.agent = sim.context()->CreateAgent(a->prototype()); + sim.AddSource("commod1").Finalize(); + + TestFacility* fac = dynamic_cast(sim.agent); + + cyclus::toolkit::ResBuf inbuf; + TotalInvTracker buf_tracker({&inbuf}); + cyclus::toolkit::MatlBuyPolicy policy; + policy.Init(fac, &inbuf, "inbuf", &buf_tracker, throughput, ccap, d_dist) + .Set("commod1").Start(); + + EXPECT_NO_THROW(sim.Run()); + + // check that transactions happen as expected (at time steps 0 and 1, then 4 and 5) + QueryResult qr = sim.db().Query("Transactions", NULL); + EXPECT_EQ(0, qr.GetVal("Time", 0)); + EXPECT_EQ(1, qr.GetVal("Time", 1)); + EXPECT_EQ(4, qr.GetVal("Time", 2)); + EXPECT_EQ(5, qr.GetVal("Time", 3)); + + delete a; +} + } } From 974648f34cdcec047bd32ab215efdd4f4cc80d73 Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Wed, 31 Jan 2024 19:53:37 -0600 Subject: [PATCH 13/20] ability to get capacity of resbufs, not including tracker --- src/toolkit/matl_buy_policy.h | 2 +- src/toolkit/total_inv_tracker.h | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/toolkit/matl_buy_policy.h b/src/toolkit/matl_buy_policy.h index 74ca572456..50c7e54799 100644 --- a/src/toolkit/matl_buy_policy.h +++ b/src/toolkit/matl_buy_policy.h @@ -161,7 +161,7 @@ class MatlBuyPolicy : public Trader { } /// whether a request can be made - inline bool MakeReq() const { return buf_->quantity() <= req_at_; } + inline bool MakeReq() const { return buf_tracker_->quantity() <= req_at_; } /// whether trades will be denoted as exclusive or not inline bool Excl() const { diff --git a/src/toolkit/total_inv_tracker.h b/src/toolkit/total_inv_tracker.h index 90232af1dc..657abeef50 100644 --- a/src/toolkit/total_inv_tracker.h +++ b/src/toolkit/total_inv_tracker.h @@ -81,13 +81,19 @@ class TotalInvTracker { /// capacity of the TotalInvTracker, or the sum of each ResBuf's capacity, /// whichever is lower. inline double capacity() { + return std::min(total_capacity_bufs(), max_inv_size_); + }; + + // Returns the sum of the capacities of all buffers. Does not include the + // capacity of the tracker + inline double total_capacity_bufs() { int num = num_bufs(); double cap = 0; for (int i = 0; i < num; i++) { cap += bufs_[i]->capacity(); } - return std::min(cap, max_inv_size_); - }; + return cap; + } /// Returns the total capacity of the traker. Does not include ResBufs inline double tracker_capacity() { return max_inv_size_;} From b7b4707b6a3a5fda7eaee51f2596f890be2382e8 Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Sun, 4 Feb 2024 13:46:58 -0700 Subject: [PATCH 14/20] Apply suggestions from code review Co-authored-by: Paul Wilson Signed-off-by: Katie Mummah --- cli/cycpp.py | 2 +- src/toolkit/matl_buy_policy.cc | 9 +++++---- src/toolkit/total_inv_tracker.h | 3 ++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/cli/cycpp.py b/cli/cycpp.py index 9eb8ab576d..097f53837c 100755 --- a/cli/cycpp.py +++ b/cli/cycpp.py @@ -1898,7 +1898,7 @@ def impl(self, ind=" "): CYCNS + '::toolkit::ResourceBuff': None, CYCNS + '::toolkit::ResBuf': None, CYCNS + '::toolkit::ResMap': '{var}.obj_ids()', - CYCNS + '::Toolkit::TotalInvTracker': None, + CYCNS + '::toolkit::TotalInvTracker': None, } diff --git a/src/toolkit/matl_buy_policy.cc b/src/toolkit/matl_buy_policy.cc index 8f5c000157..00005479c2 100644 --- a/src/toolkit/matl_buy_policy.cc +++ b/src/toolkit/matl_buy_policy.cc @@ -57,10 +57,11 @@ void MatlBuyPolicy::set_total_inv_tracker(TotalInvTracker* t) { void MatlBuyPolicy::set_inv_policy(std::string inv_policy, double fill, double req_at) { set_req_at(req_at); - if ((inv_policy == "sS") || (inv_policy == "Ss")) { + std::transform(inv_policy.begin(), inv_policy.end(), inv_policy.begin(), ::tolower); + if ((inv_policy == "ss")) { set_fill_to(fill); } - else if ((inv_policy == "RQ") || (inv_policy == "QR")) { + else if ((inv_policy == "rq") || (inv_policy == "qr")) { set_quantize(fill); // maximum amount that an RQ policy could achieve is req_at + fill set_fill_to(req_at + fill); @@ -286,8 +287,8 @@ std::set::Ptr> MatlBuyPolicy::GetMatlRequests() { else {next_dormant_end_ = -1;} } - if ((ccap_ != -1) && ((cycle_total_inv_ + amt) > ccap_)) { - amt = ccap_ - cycle_total_inv_; + if ( ccap_ != -1 ) { + amt = std::min(amt, ccap_ - cycle_total_inv_); } if (amt < eps()) diff --git a/src/toolkit/total_inv_tracker.h b/src/toolkit/total_inv_tracker.h index 657abeef50..fdd7736e65 100644 --- a/src/toolkit/total_inv_tracker.h +++ b/src/toolkit/total_inv_tracker.h @@ -115,7 +115,8 @@ class TotalInvTracker { if (num == 0) { throw ValueError("TotalInvTracker has not been initialized, no buffers to track"); } - return num;}; + return num; + } /// Change the total capacity across all ResBufs. The new capacity must be /// greater than the current quantity. From ee811609f97d2884536aec7508b4be13ee1a8d3c Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Sun, 4 Feb 2024 16:17:12 -0700 Subject: [PATCH 15/20] address @gonuke comments --- src/toolkit/matl_buy_policy.cc | 18 +++++++++--------- src/toolkit/matl_buy_policy.h | 7 ++++--- src/toolkit/total_inv_tracker.h | 15 +++++++-------- tests/toolkit/matl_buy_policy_tests.cc | 4 ++-- tests/toolkit/total_inv_tracker_tests.cc | 7 ++++--- 5 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/toolkit/matl_buy_policy.cc b/src/toolkit/matl_buy_policy.cc index 00005479c2..7deedb4bc4 100644 --- a/src/toolkit/matl_buy_policy.cc +++ b/src/toolkit/matl_buy_policy.cc @@ -20,11 +20,11 @@ MatlBuyPolicy::MatlBuyPolicy() : quantize_(-1), fill_to_(std::numeric_limits::max()), req_at_(std::numeric_limits::max()), + ccap_(-1), + cycle_total_inv_(0), active_dist_(NULL), dormant_dist_(NULL), - size_dist_(NULL), - ccap_(-1), - cycle_total_inv_(0){ + size_dist_(NULL){ Warn( "MatlBuyPolicy is experimental and its API may be subject to change"); } @@ -118,7 +118,7 @@ void MatlBuyPolicy::init_active_dormant() { next_dormant_end_ = -1; LGH(INFO4) << "dormant length -1, always active" << std::endl; } - else if (ccap_ > 0) { + else if (use_cumulative_capacity()) { next_dormant_end_ = -1; LGH(INFO4) << "dormant length set at -1 for first active period of ccap cycle" << std::endl; } @@ -280,14 +280,14 @@ std::set::Ptr> MatlBuyPolicy::GetMatlRequests() { // finished dormant. starting buying and sample/set length of active period amt = TotalAvailable() * SampleRequestSize(); SetNextActiveTime(); - if (ccap_ < 0) { + if (!use_cumulative_capacity()) { SetNextDormantTime(); LGH(INFO4) << "end of dormant period, next active time end: " << next_active_end_ << ", and next dormant time end: " << next_dormant_end_ << std::endl; } else {next_dormant_end_ = -1;} } - if ( ccap_ != -1 ) { + if (use_cumulative_capacity()) { amt = std::min(amt, ccap_ - cycle_total_inv_); } @@ -329,13 +329,13 @@ void MatlBuyPolicy::AcceptMatlTrades( << it->first.request->commodity() << std::endl; buf_->Push(it->second); // ccap handling - if (ccap_ > 0) { + if (use_cumulative_capacity()) { cycle_total_inv_ += it->second->quantity(); } } // check if cumulative cap has been reached. If yes, then sample for dormant // length and reset cycle_total_inv - if ((ccap_ > 0) && ((ccap_ - cycle_total_inv_) < eps())) { + if (use_cumulative_capacity() && ((ccap_ - cycle_total_inv_) < eps())) { SetNextDormantTime(); LGH(INFO3) << "cycle cumulative inventory has been reached. Dormant period will end at " << next_dormant_end_ << std::endl; cycle_total_inv_ = 0; @@ -348,7 +348,7 @@ void MatlBuyPolicy::SetNextActiveTime() { }; void MatlBuyPolicy::SetNextDormantTime() { - if (ccap_ > 0) { + if (use_cumulative_capacity()) { // need the +1 when not using next_active_end_ next_dormant_end_ = ( dormant_dist_->sample() + manager()->context()->time() + 1); diff --git a/src/toolkit/matl_buy_policy.h b/src/toolkit/matl_buy_policy.h index 50c7e54799..6d520557de 100644 --- a/src/toolkit/matl_buy_policy.h +++ b/src/toolkit/matl_buy_policy.h @@ -164,8 +164,7 @@ class MatlBuyPolicy : public Trader { inline bool MakeReq() const { return buf_tracker_->quantity() <= req_at_; } /// whether trades will be denoted as exclusive or not - inline bool Excl() const { - return quantize_ > 0; } + inline bool Excl() const { return quantize_ > 0; } /// the amount requested per each request @@ -189,6 +188,8 @@ class MatlBuyPolicy : public Trader { return (next_dormant_end_ < 0 || next_active_end_ < 0); }; + inline bool use_cumulative_capacity() {return ccap_ > 0;}; + /// Trader Methods /// @{ virtual std::set::Ptr> GetMatlRequests(); @@ -207,7 +208,7 @@ class MatlBuyPolicy : public Trader { }; void set_manager(Agent* m); - void set_total_inv_tracker(TotalInvTracker* t); + void set_total_inv_tracker(TotalInvTracker* t = NULL); void set_inv_policy(std::string p, double x, double y = std::numeric_limits::max()); /// requires buf_ already set diff --git a/src/toolkit/total_inv_tracker.h b/src/toolkit/total_inv_tracker.h index fdd7736e65..16da9fd7e8 100644 --- a/src/toolkit/total_inv_tracker.h +++ b/src/toolkit/total_inv_tracker.h @@ -67,9 +67,6 @@ class TotalInvTracker { /// @throws ValueError if the tracker has not been initialized (zero) inline double quantity() { int num = num_bufs(); - if (num == 0) { - throw ValueError("TotalInvTracker has not been initialized, no buffers to track"); - } qty_ = 0; for (int i = 0; i < num; i++) { qty_ += bufs_[i]->quantity(); @@ -82,7 +79,7 @@ class TotalInvTracker { /// whichever is lower. inline double capacity() { return std::min(total_capacity_bufs(), max_inv_size_); - }; + } // Returns the sum of the capacities of all buffers. Does not include the // capacity of the tracker @@ -96,17 +93,19 @@ class TotalInvTracker { } /// Returns the total capacity of the traker. Does not include ResBufs - inline double tracker_capacity() { return max_inv_size_;} + inline double tracker_capacity() { return max_inv_size_; } /// Returns the remaining facility-wide space across all tracked ResBufs. - inline double space() {return std::max(0.0, capacity() - quantity());}; + inline double space() { return std::max(0.0, capacity() - quantity()); } /// Returns the remaining space in the given ResBuf, considering the /// facility-wide limitations. - inline double buf_space(ResBuf* buf) {return std::min(buf->space(), space());}; + inline double constrained_buf_space(ResBuf* buf) { + return std::min(buf->space(), space()); + } /// Returns true if there are no resources in any buffer - inline bool empty() {return quantity() == 0;}; + inline bool empty() { return quantity() == 0; } /// Returns number of buffers being tracked /// @throws ValueError if the tracker has not been initialized (zero) diff --git a/tests/toolkit/matl_buy_policy_tests.cc b/tests/toolkit/matl_buy_policy_tests.cc index f105344243..3ae9e521fe 100644 --- a/tests/toolkit/matl_buy_policy_tests.cc +++ b/tests/toolkit/matl_buy_policy_tests.cc @@ -42,7 +42,7 @@ class MatlBuyPolicyTests: public ::testing::Test { }; TEST_F(MatlBuyPolicyTests, Init) { - double cap = 10; + double cap = 5; ResBuf buff; buff.capacity(cap); TotalInvTracker buff_tracker({&buff}); @@ -289,7 +289,7 @@ TEST_F(MatlBuyPolicyTests, TotalInvTracker) { EXPECT_EQ(buf_tracker.quantity(), 5); EXPECT_EQ(buf_tracker.space(), 0); EXPECT_EQ(inbuf.space(), inbuf.capacity() - inbuf.quantity()); - EXPECT_EQ(buf_tracker.buf_space(&inbuf), 0); + EXPECT_EQ(buf_tracker.constrained_buf_space(&inbuf), 0); } TEST_F(MatlBuyPolicyTests, DefaultFixedActiveDormant) { diff --git a/tests/toolkit/total_inv_tracker_tests.cc b/tests/toolkit/total_inv_tracker_tests.cc index d9c1ff1371..222e855963 100644 --- a/tests/toolkit/total_inv_tracker_tests.cc +++ b/tests/toolkit/total_inv_tracker_tests.cc @@ -31,9 +31,10 @@ TEST_F(TotalInvTrackerTest, space) { EXPECT_EQ(multi_tracker_.space(), max_inv_size_ - qty1_ - qty2_); } -TEST_F(TotalInvTrackerTest, buf_space) { - EXPECT_EQ(multi_tracker_.buf_space(&buf1_), buf1_.space()); - EXPECT_EQ(multi_tracker_.buf_space(&buf2_), max_inv_size_ - qty1_ - qty2_); +TEST_F(TotalInvTrackerTest, constrained_buf_space) { + EXPECT_EQ(multi_tracker_.constrained_buf_space(&buf1_), buf1_.space()); + EXPECT_EQ(multi_tracker_.constrained_buf_space(&buf2_), + max_inv_size_ - qty1_ - qty2_); } TEST_F(TotalInvTrackerTest, empty) { From c4ac399d2236f34cf79848a180c626d2d82fd88a Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Sun, 4 Feb 2024 18:13:24 -0700 Subject: [PATCH 16/20] rearrange setting amt and cycle times based on governing policy --- src/toolkit/matl_buy_policy.cc | 78 ++++++++++++++++---------- src/toolkit/matl_buy_policy.h | 35 +++++++++--- tests/toolkit/matl_buy_policy_tests.cc | 2 +- 3 files changed, 74 insertions(+), 41 deletions(-) diff --git a/src/toolkit/matl_buy_policy.cc b/src/toolkit/matl_buy_policy.cc index 7deedb4bc4..223b015780 100644 --- a/src/toolkit/matl_buy_policy.cc +++ b/src/toolkit/matl_buy_policy.cc @@ -20,7 +20,7 @@ MatlBuyPolicy::MatlBuyPolicy() : quantize_(-1), fill_to_(std::numeric_limits::max()), req_at_(std::numeric_limits::max()), - ccap_(-1), + cumulative_cap_(-1), cycle_total_inv_(0), active_dist_(NULL), dormant_dist_(NULL), @@ -81,9 +81,9 @@ void MatlBuyPolicy::set_req_at(double x) { req_at_ = x; } -void MatlBuyPolicy::set_ccap(double x) { +void MatlBuyPolicy::set_cumulative_cap(double x) { assert(x > 0); - ccap_ = x; + cumulative_cap_ = x; } void MatlBuyPolicy::set_quantize(double x) { @@ -120,7 +120,7 @@ void MatlBuyPolicy::init_active_dormant() { } else if (use_cumulative_capacity()) { next_dormant_end_ = -1; - LGH(INFO4) << "dormant length set at -1 for first active period of ccap cycle" << std::endl; + LGH(INFO4) << "dormant length set at -1 for first active period of cumulative capacity cycle" << std::endl; } else { SetNextDormantTime(); @@ -201,14 +201,14 @@ MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, MatlBuyPolicy& MatlBuyPolicy::Init(Agent* manager, ResBuf* buf, std::string name, TotalInvTracker* buf_tracker, - double throughput, double ccap, + double throughput, double cumulative_cap, boost::shared_ptr dormant_dist) { set_manager(manager); buf_ = buf; name_ = name; set_total_inv_tracker(buf_tracker); set_throughput(throughput); - set_ccap(ccap); + set_cumulative_cap(cumulative_cap); dormant_dist_ = dormant_dist; init_active_dormant(); return *this; @@ -255,40 +255,55 @@ std::set::Ptr> MatlBuyPolicy::GetMatlRequests() { rsrc_commods_.clear(); std::set::Ptr> ports; - // (s,S)/(R,Q) inventory policy handling - if (!MakeReq()) { - return ports; - } - double amt; int current_time_ = manager()->context()->time(); - // period inventory handling - if (never_dormant() || current_time_ < next_active_end_) { - // currently in the middle of active buying period + // Three step process to determine size of amount and reset any necessary + // cycle times + + // Step 1: determine if inventory policy allows for a request to be made + // Handing for (s,S)/(R,Q) inventory policies + if (!MakeReq()) { + return ports; + } + // Handling for cumulative capacity inventory policy + // buy while not in dormant, don't buy while dormant. Make sure the request + // is less than or equal to the space remaining in the cycle capacity + // (cumulative cap minus current cycle inventory) + if (use_cumulative_capacity()) { + if (no_cycle_end_time() || current_time_ == next_dormant_end_){ + amt = std::min((TotalAvailable() * SampleRequestSize()), + (cumulative_cap_ - cycle_total_inv_)); + } + else if (current_time_ < next_dormant_end_) { + amt = 0; + LGH(INFO3) << "in dormant period, no request" << std::endl; + } + } + // Step 2: determine if active/dormant cycle times allow for a request + // if no cycles, or if in the middle of active period, or if dormant period + // just ended, then request + else if (no_cycle_end_time() || (current_time_ < next_active_end_) || + (current_time_ == next_dormant_end_)) { amt = TotalAvailable() * SampleRequestSize(); } + // if in the middle of dormant period, then don't request else if (current_time_ < next_dormant_end_) { - // finished active. starting dormancy and sample/set length of dormant period amt = 0; LGH(INFO3) << "in dormant period, no request" << std::endl; } - // the following is an if rather than if-else statement because if dormant - // length is zero, buy policy should return to active immediately + + // Step 3: Finally, determine if active/dormant cycle times need to be reset. + // If reaching the end of a cumulative cap cycle, set next_dormant_end_ = -1, + // otherwise sample for next dormant. if (current_time_ == next_dormant_end_) { - // finished dormant. starting buying and sample/set length of active period - amt = TotalAvailable() * SampleRequestSize(); SetNextActiveTime(); - if (!use_cumulative_capacity()) { + if (use_cumulative_capacity()) { next_dormant_end_ = -1; } + else { SetNextDormantTime(); LGH(INFO4) << "end of dormant period, next active time end: " << next_active_end_ << ", and next dormant time end: " << next_dormant_end_ << std::endl; - } - else {next_dormant_end_ = -1;} - } - - if (use_cumulative_capacity()) { - amt = std::min(amt, ccap_ - cycle_total_inv_); + } } if (amt < eps()) @@ -328,17 +343,18 @@ void MatlBuyPolicy::AcceptMatlTrades( LGH(INFO3) << "got " << it->second->quantity() << " kg of " << it->first.request->commodity() << std::endl; buf_->Push(it->second); - // ccap handling + // cumulative capacity handling if (use_cumulative_capacity()) { cycle_total_inv_ += it->second->quantity(); } } // check if cumulative cap has been reached. If yes, then sample for dormant // length and reset cycle_total_inv - if (use_cumulative_capacity() && ((ccap_ - cycle_total_inv_) < eps())) { - SetNextDormantTime(); - LGH(INFO3) << "cycle cumulative inventory has been reached. Dormant period will end at " << next_dormant_end_ << std::endl; - cycle_total_inv_ = 0; + if (use_cumulative_capacity() && ( + (cumulative_cap_ - cycle_total_inv_) < eps())) { + SetNextDormantTime(); + LGH(INFO3) << "cycle cumulative inventory has been reached. Dormant period will end at " << next_dormant_end_ << std::endl; + cycle_total_inv_ = 0; } } diff --git a/src/toolkit/matl_buy_policy.h b/src/toolkit/matl_buy_policy.h index 6d520557de..8df8f41a26 100644 --- a/src/toolkit/matl_buy_policy.h +++ b/src/toolkit/matl_buy_policy.h @@ -71,10 +71,26 @@ class MatlBuyPolicy : public Trader { /// @param name a unique name identifying this policy /// @param throughput a constraining value for total transaction quantities in /// a single time step - /// @param inv_policy the inventory policy to use. Options are "sS", "RQ". Each inventory policy options has two additional required parameters. An (s,S) inventory policy orders material only when the buffer is below the minimum value, s, and orders only enough to bring the buffer to the maximum value, S. An (s,S) policy allows partial orders. Set s as req_at and S as fill. An (R,Q) inventory policy requests material when the buffer is below the minimum value, R, and places an exclusive request for size Q. Set R as req_at and Q as fill. - /// @param req_at, the inventory minimum (s, and R) for (s,S) and (R,Q),. If the buffer has less than or equal to value, new material will be requested based on the policy in place. - /// @param fill_behav, the quantity govering the fill strategy for inventory policies. For (s,S), this is the maximum value, and material will be ordered up to this amount. For (R,Q), this is the quantity of material that will be ordered (exclusive). - /// @param ccap the cumulative capacity of material to be received in one active cycle. A ccap inventory policy allows for a cumulative capacity of material to be received in one active cycle. Once the cumulative capacity is recieved, the agent enters a dormant period. Also requires dormant distributions using dormant_dist. + /// @param inv_policy the inventory policy to use. Options are "sS", "RQ". + /// Each inventory policy options has two additional required parameters. An + /// (s,S) inventory policy orders material only when the buffer is below the + /// minimum value, s, and orders only enough to bring the buffer to the + /// maximum value, S. An (s,S) policy allows partial orders. Set s as req_at + /// and S as fill. An (R,Q) inventory policy requests material when the + /// buffer is below the minimum value, R, and places an exclusive request for + /// size Q. Set R as req_at and Q as fill. + /// @param req_at, the inventory minimum (s, and R) for (s,S) and (R,Q),. If + /// the buffer has less than or equal to value, new material will be + /// requested based on the policy in place. + /// @param fill_behav, the quantity govering the fill strategy for inventory + /// policies. For (s,S), this is the maximum value, and material will be + /// ordered up to this amount. For (R,Q), this is the quantity of material + /// that will be ordered (exclusive). + /// @param cumulative_cap the cumulative capacity of material to be received + /// in one active cycle. A cumulative cap inventory policy allows for a + /// cumulative capacity of material to be received in one active cycle. Once + /// the cumulative capacity is recieved, the agent enters a dormant period. + /// Also requires dormant distributions using dormant_dist. /// @param quantize If quantize is greater than zero, the policy will make /// exclusive, integral quantize kg requests. Otherwise, single requests will /// be sent to fill the buffer's empty space. @@ -121,7 +137,7 @@ class MatlBuyPolicy : public Trader { double fill_behav, double req_at); MatlBuyPolicy& Init(Agent* manager, ResBuf* buf, std::string name, TotalInvTracker* buf_tracker, double throughput, - double ccap, + double cumulative_cap, boost::shared_ptr dormant_dist); /// @} @@ -184,11 +200,11 @@ class MatlBuyPolicy : public Trader { return rsrc_commods_; }; - inline bool never_dormant() { + inline bool no_cycle_end_time() { return (next_dormant_end_ < 0 || next_active_end_ < 0); }; - inline bool use_cumulative_capacity() {return ccap_ > 0;}; + inline bool use_cumulative_capacity() {return cumulative_cap_ > 0;}; /// Trader Methods /// @{ @@ -215,7 +231,7 @@ class MatlBuyPolicy : public Trader { void set_fill_to(double x); /// requires buf_ already set void set_req_at(double x); - void set_ccap(double x); + void set_cumulative_cap(double x); void set_quantize(double x); void set_throughput(double x); void init_active_dormant(); @@ -223,7 +239,8 @@ class MatlBuyPolicy : public Trader { ResBuf* buf_; TotalInvTracker* buf_tracker_; std::string name_, inv_policy; - double fill_to_, req_at_, quantize_, throughput_, ccap_, cycle_total_inv_; + double fill_to_, req_at_, quantize_, throughput_, cumulative_cap_, + cycle_total_inv_; int next_active_end_= 0; int next_dormant_end_= 0; diff --git a/tests/toolkit/matl_buy_policy_tests.cc b/tests/toolkit/matl_buy_policy_tests.cc index 3ae9e521fe..9543b41404 100644 --- a/tests/toolkit/matl_buy_policy_tests.cc +++ b/tests/toolkit/matl_buy_policy_tests.cc @@ -617,7 +617,7 @@ TEST_F(MatlBuyPolicyTests, RandomSizeAndFrequency) { delete a; } -TEST_F(MatlBuyPolicyTests, CCap_Inventory) { +TEST_F(MatlBuyPolicyTests, Cumulative_Cap_Inventory) { using cyclus::QueryResult; double ccap = 2; From 974bca913b32fdcc7e4ba101d847b2b6c297e760 Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Mon, 5 Feb 2024 18:04:04 -0700 Subject: [PATCH 17/20] refactor amt --- src/toolkit/matl_buy_policy.cc | 62 ++++++++------------------ src/toolkit/matl_buy_policy.h | 9 +++- tests/toolkit/matl_buy_policy_tests.cc | 9 ++-- 3 files changed, 30 insertions(+), 50 deletions(-) diff --git a/src/toolkit/matl_buy_policy.cc b/src/toolkit/matl_buy_policy.cc index 223b015780..cee53a0f2e 100644 --- a/src/toolkit/matl_buy_policy.cc +++ b/src/toolkit/matl_buy_policy.cc @@ -73,6 +73,7 @@ void MatlBuyPolicy::set_inv_policy(std::string inv_policy, double fill, double r void MatlBuyPolicy::set_fill_to(double x) { assert(x > 0); + assert(x <= buf_->capacity()); fill_to_ = x; } @@ -255,56 +256,18 @@ std::set::Ptr> MatlBuyPolicy::GetMatlRequests() { rsrc_commods_.clear(); std::set::Ptr> ports; - double amt; + double amt = 0; int current_time_ = manager()->context()->time(); - // Three step process to determine size of amount and reset any necessary - // cycle times + double max_request_amt = use_cumulative_capacity() ? cumulative_cap_ - cycle_total_inv_ : std::numeric_limits::max(); - // Step 1: determine if inventory policy allows for a request to be made - // Handing for (s,S)/(R,Q) inventory policies - if (!MakeReq()) { - return ports; - } - // Handling for cumulative capacity inventory policy - // buy while not in dormant, don't buy while dormant. Make sure the request - // is less than or equal to the space remaining in the cycle capacity - // (cumulative cap minus current cycle inventory) - if (use_cumulative_capacity()) { - if (no_cycle_end_time() || current_time_ == next_dormant_end_){ - amt = std::min((TotalAvailable() * SampleRequestSize()), - (cumulative_cap_ - cycle_total_inv_)); - } - else if (current_time_ < next_dormant_end_) { - amt = 0; - LGH(INFO3) << "in dormant period, no request" << std::endl; - } - } - // Step 2: determine if active/dormant cycle times allow for a request - // if no cycles, or if in the middle of active period, or if dormant period - // just ended, then request - else if (no_cycle_end_time() || (current_time_ < next_active_end_) || - (current_time_ == next_dormant_end_)) { - amt = TotalAvailable() * SampleRequestSize(); - } - // if in the middle of dormant period, then don't request - else if (current_time_ < next_dormant_end_) { - amt = 0; - LGH(INFO3) << "in dormant period, no request" << std::endl; + if (MakeReq() && !dormant(current_time_)) { + amt = std::min(TotalAvailable() * SampleRequestSize(), max_request_amt); } + else { LGH(INFO3) << "in dormant period, no request" << std::endl; } - // Step 3: Finally, determine if active/dormant cycle times need to be reset. - // If reaching the end of a cumulative cap cycle, set next_dormant_end_ = -1, - // otherwise sample for next dormant. - if (current_time_ == next_dormant_end_) { - SetNextActiveTime(); - if (use_cumulative_capacity()) { next_dormant_end_ = -1; } - else { - SetNextDormantTime(); - LGH(INFO4) << "end of dormant period, next active time end: " << next_active_end_ << ", and next dormant time end: " << next_dormant_end_ << std::endl; - } - } + CheckActiveDormantCumulativeTimes(); if (amt < eps()) return ports; @@ -381,6 +344,17 @@ double MatlBuyPolicy::SampleRequestSize() { return size_dist_->sample(); } +void MatlBuyPolicy::CheckActiveDormantCumulativeTimes() { + if (manager()->context()->time() == next_dormant_end_) { + SetNextActiveTime(); + if (use_cumulative_capacity()) { next_dormant_end_ = -1; } + else { + SetNextDormantTime(); + LGH(INFO4) << "end of dormant period, next active time end: " << next_active_end_ << ", and next dormant time end: " << next_dormant_end_ << std::endl; + } + } + return; +} } // namespace toolkit } // namespace cyclus diff --git a/src/toolkit/matl_buy_policy.h b/src/toolkit/matl_buy_policy.h index 8df8f41a26..79516c7a81 100644 --- a/src/toolkit/matl_buy_policy.h +++ b/src/toolkit/matl_buy_policy.h @@ -202,9 +202,13 @@ class MatlBuyPolicy : public Trader { inline bool no_cycle_end_time() { return (next_dormant_end_ < 0 || next_active_end_ < 0); - }; + } + + inline bool dormant(int time) { + return (time >= next_active_end_ && time < next_dormant_end_); + } - inline bool use_cumulative_capacity() {return cumulative_cap_ > 0;}; + inline bool use_cumulative_capacity() { return cumulative_cap_ > 0; } /// Trader Methods /// @{ @@ -216,6 +220,7 @@ class MatlBuyPolicy : public Trader { void SetNextActiveTime(); void SetNextDormantTime(); double SampleRequestSize(); + void CheckActiveDormantCumulativeTimes(); private: struct CommodDetail { diff --git a/tests/toolkit/matl_buy_policy_tests.cc b/tests/toolkit/matl_buy_policy_tests.cc index 9543b41404..100104c388 100644 --- a/tests/toolkit/matl_buy_policy_tests.cc +++ b/tests/toolkit/matl_buy_policy_tests.cc @@ -266,7 +266,8 @@ TEST_F(MatlBuyPolicyTests, TotalInvTracker) { cyclus::CompMap cm; cm[1001] = 1; - cyclus::Material::Ptr mat = cyclus::Material::Create(fac, 2, cyclus::Composition::CreateFromAtom(cm)); + double mat_size = 2; + cyclus::Material::Ptr mat = cyclus::Material::Create(fac, mat_size, cyclus::Composition::CreateFromAtom(cm)); otherbuf.Push(mat); double total_capacity = 5; @@ -276,9 +277,9 @@ TEST_F(MatlBuyPolicyTests, TotalInvTracker) { .Set("commod1").Start(); EXPECT_FALSE(buf_tracker.empty()); - EXPECT_EQ(buf_tracker.quantity(), 2); + EXPECT_EQ(buf_tracker.quantity(), mat_size); EXPECT_EQ(buf_tracker.capacity(), total_capacity); - EXPECT_EQ(buf_tracker.space(), total_capacity - 2); + EXPECT_EQ(buf_tracker.space(), total_capacity - mat_size); EXPECT_NO_THROW(sim.Run()); @@ -286,7 +287,7 @@ TEST_F(MatlBuyPolicyTests, TotalInvTracker) { int n_trans = qr.rows.size(); EXPECT_EQ(n_trans, 3); - EXPECT_EQ(buf_tracker.quantity(), 5); + EXPECT_EQ(buf_tracker.quantity(), total_capacity); EXPECT_EQ(buf_tracker.space(), 0); EXPECT_EQ(inbuf.space(), inbuf.capacity() - inbuf.quantity()); EXPECT_EQ(buf_tracker.constrained_buf_space(&inbuf), 0); From 0b2b2d210799fedf1121c594b76abc2529f97a79 Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Mon, 5 Feb 2024 18:10:29 -0700 Subject: [PATCH 18/20] fix indenting --- src/toolkit/total_inv_tracker.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/toolkit/total_inv_tracker.h b/src/toolkit/total_inv_tracker.h index 16da9fd7e8..6f40815750 100644 --- a/src/toolkit/total_inv_tracker.h +++ b/src/toolkit/total_inv_tracker.h @@ -53,14 +53,14 @@ class TotalInvTracker { /// infinite capacity unless explicitly changed. void Init(std::vector*> bufs, double max_inv_size = std::numeric_limits::max()) { - if (bufs.size() == 0) { - throw ValueError("TotalInvTracker must be initialized with at least one ResBuf"); - } - bufs_ = bufs; - if (max_inv_size <= 0) { - throw ValueError("TotalInvTracker must be initialized with a positive capacity"); - } - max_inv_size_ = max_inv_size; + if (bufs.size() == 0) { + throw ValueError("TotalInvTracker must be initialized with at least one ResBuf"); + } + bufs_ = bufs; + if (max_inv_size <= 0) { + throw ValueError("TotalInvTracker must be initialized with a positive capacity"); + } + max_inv_size_ = max_inv_size; } /// Returns the total quantity of all tracked ResBufs. From 915adc3ca2fa597fc224db9c39bd95b6ef009fdd Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Mon, 5 Feb 2024 21:15:36 -0700 Subject: [PATCH 19/20] Apply suggestions from code review Co-authored-by: Paul Wilson Signed-off-by: Katie Mummah --- src/toolkit/matl_buy_policy.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/toolkit/matl_buy_policy.cc b/src/toolkit/matl_buy_policy.cc index cee53a0f2e..e776868367 100644 --- a/src/toolkit/matl_buy_policy.cc +++ b/src/toolkit/matl_buy_policy.cc @@ -79,7 +79,7 @@ void MatlBuyPolicy::set_fill_to(double x) { void MatlBuyPolicy::set_req_at(double x) { assert(x >= 0); - req_at_ = x; + req_at_ = std::min(x, buf_->capacity()); } void MatlBuyPolicy::set_cumulative_cap(double x) { @@ -332,8 +332,7 @@ void MatlBuyPolicy::SetNextDormantTime() { next_dormant_end_ = ( dormant_dist_->sample() + manager()->context()->time() + 1); } - else if (next_dormant_end_ < 0) {} - else { + else if (next_dormant_end_ >= 0) { next_dormant_end_ = dormant_dist_->sample() + std::max(next_active_end_, 1); } @@ -346,9 +345,10 @@ double MatlBuyPolicy::SampleRequestSize() { void MatlBuyPolicy::CheckActiveDormantCumulativeTimes() { if (manager()->context()->time() == next_dormant_end_) { - SetNextActiveTime(); if (use_cumulative_capacity()) { next_dormant_end_ = -1; } else { + SetNextActiveTime(); + SetNextDormantTime(); LGH(INFO4) << "end of dormant period, next active time end: " << next_active_end_ << ", and next dormant time end: " << next_dormant_end_ << std::endl; } From 7913770a1ca40fb4ba7b809151cc37bbb8a79444 Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Mon, 5 Feb 2024 21:45:29 -0700 Subject: [PATCH 20/20] check that buf is in tracker --- src/toolkit/matl_buy_policy.cc | 10 +++++++--- src/toolkit/total_inv_tracker.h | 7 +++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/toolkit/matl_buy_policy.cc b/src/toolkit/matl_buy_policy.cc index e776868367..a00a6e0658 100644 --- a/src/toolkit/matl_buy_policy.cc +++ b/src/toolkit/matl_buy_policy.cc @@ -50,6 +50,11 @@ void MatlBuyPolicy::set_total_inv_tracker(TotalInvTracker* t) { std::vector*> bufs = {buf_}; buf_tracker_->Init(bufs, buf_->capacity()); } + else if (!t->buf_in_tracker(buf_)) { + std::stringstream ss; + ss << "TotalInvTracker does not contain ResBuf used in buy policy"; + throw ValueError(ss.str()); + } else { buf_tracker_ = t; } @@ -73,8 +78,7 @@ void MatlBuyPolicy::set_inv_policy(std::string inv_policy, double fill, double r void MatlBuyPolicy::set_fill_to(double x) { assert(x > 0); - assert(x <= buf_->capacity()); - fill_to_ = x; + fill_to_ = std::min(x, buf_->capacity()); } void MatlBuyPolicy::set_req_at(double x) { @@ -84,7 +88,7 @@ void MatlBuyPolicy::set_req_at(double x) { void MatlBuyPolicy::set_cumulative_cap(double x) { assert(x > 0); - cumulative_cap_ = x; + cumulative_cap_ = std::min(x, buf_->capacity()); } void MatlBuyPolicy::set_quantize(double x) { diff --git a/src/toolkit/total_inv_tracker.h b/src/toolkit/total_inv_tracker.h index 6f40815750..92b1918281 100644 --- a/src/toolkit/total_inv_tracker.h +++ b/src/toolkit/total_inv_tracker.h @@ -130,6 +130,13 @@ class TotalInvTracker { max_inv_size_ = cap; } + bool buf_in_tracker(ResBuf* buf) { + for (int i = 0; i < num_bufs(); i++) { + if (bufs_[i] == buf) { return true; } + } + return false; + } + private: double max_inv_size_; double qty_;