From 4ab97424889b6c91203bd1d3c0ad330da1768981 Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Tue, 16 Jul 2024 14:13:10 -0500 Subject: [PATCH 1/8] packaging in source, also resbuf in source --- src/source.cc | 101 ++++++++++++++++++++++++++++++++++++-------- src/source.h | 29 +++++++++++++ src/source_tests.cc | 37 ++++++++++++++++ src/source_tests.h | 4 +- 4 files changed, 152 insertions(+), 19 deletions(-) diff --git a/src/source.cc b/src/source.cc index 0def041f6c..48a80274da 100644 --- a/src/source.cc +++ b/src/source.cc @@ -13,6 +13,8 @@ Source::Source(cyclus::Context* ctx) inventory_size(std::numeric_limits::max()), latitude(0.0), longitude(0.0), + package(cyclus::Package::unpackaged_name()), + transport_unit(cyclus::TransportUnit::unrestricted_name()), coordinates(latitude, longitude) {} Source::~Source() {} @@ -20,7 +22,6 @@ Source::~Source() {} void Source::InitFrom(Source* m) { #pragma cyclus impl initfromcopy cycamore::Source cyclus::toolkit::CommodityProducer::Copy(m); - RecordPosition(); } void Source::InitFrom(cyclus::QueryableBackend* b) { @@ -28,7 +29,6 @@ void Source::InitFrom(cyclus::QueryableBackend* b) { namespace tk = cyclus::toolkit; tk::CommodityProducer::Add(tk::Commodity(outcommod), tk::CommodInfo(throughput, throughput)); - RecordPosition(); } std::string Source::str() { @@ -47,18 +47,39 @@ std::string Source::str() { << " commod producer members: " << " produces " << outcommod << "?: " << ans << " throughput: " << cyclus::toolkit::CommodityProducer::Capacity(outcommod) + << " with package type: " << package + << " and transport unit type: " << transport_unit << " cost: " << cyclus::toolkit::CommodityProducer::Cost(outcommod); return ss.str(); } +void Source::EnterNotify() { + using cyclus::CompMap; + using cyclus::Composition; + using cyclus::Material; + cyclus::Facility::EnterNotify(); + RecordPosition(); + + // create all source inventory and place into buf + cyclus::Material::Ptr all_inv; + Composition::Ptr blank_comp = Composition::CreateFromMass(CompMap()); + + all_inv = (outrecipe.empty() || context() == NULL) ? \ + Material::Create(this, inventory_size, blank_comp) : \ + Material::Create(this, inventory_size, context()->GetRecipe(outrecipe)); + inventory.Push(all_inv); +} + std::set::Ptr> Source::GetMatlBids( cyclus::CommodMap::type& commod_requests) { using cyclus::BidPortfolio; using cyclus::CapacityConstraint; using cyclus::Material; + using cyclus::Package; using cyclus::Request; + using cyclus::TransportUnit; - double max_qty = std::min(throughput, inventory_size); + double max_qty = std::min(throughput, inventory.quantity()); cyclus::toolkit::RecordTimeSeries("supply"+outcommod, this, max_qty); LOG(cyclus::LEV_INFO3, "Source") << prototype() << " is bidding up to " @@ -79,11 +100,35 @@ std::set::Ptr> Source::GetMatlBids( Request* req = *it; Material::Ptr target = req->target(); double qty = std::min(target->quantity(), max_qty); - Material::Ptr m = Material::CreateUntracked(qty, target->comp()); - if (!outrecipe.empty()) { - m = Material::CreateUntracked(qty, context()->GetRecipe(outrecipe)); + + // calculate packaging + double bid_qty = context()->GetPackage(package)->GetFillMass(qty); + int n_full_bids = static_cast(std::floor(qty / bid_qty)); + Package::ExceedsSplitLimits(n_full_bids); + + std::vector bids; + bids.assign(n_full_bids, bid_qty); + + double remaining_qty = std::fmod(qty, bid_qty); + if ((remaining_qty > cyclus::eps()) && (remaining_qty >= context()->GetPackage(package)->fill_min())) { + bids.push_back(remaining_qty); + } + + // calculate transport units + int shippable_pkgs = context()->GetTransportUnit(transport_unit) + ->MaxShippablePackages(bids.size()); + if (shippable_pkgs < bids.size()) { + bids.erase(bids.begin() + shippable_pkgs, bids.end()); + } + + std::vector::iterator bit; + for (bit = bids.begin(); bit != bids.end(); ++bit) { + Material::Ptr m; + m = outrecipe.empty() ? \ + Material::CreateUntracked(*bit, target->comp()) : \ + Material::CreateUntracked(*bit, context()->GetRecipe(outrecipe)); + port->AddBid(req, m, this); } - port->AddBid(req, m, this); } CapacityConstraint cc(max_qty); @@ -99,20 +144,40 @@ void Source::GetMatlTrades( using cyclus::Material; using cyclus::Trade; + int shippable_trades = context()->GetTransportUnit(transport_unit) + ->MaxShippablePackages(trades.size()); + std::vector >::const_iterator it; for (it = trades.begin(); it != trades.end(); ++it) { - double qty = it->amt; - inventory_size -= qty; - - Material::Ptr response; - if (!outrecipe.empty()) { - response = Material::Create(this, qty, context()->GetRecipe(outrecipe)); - } else { - response = Material::Create(this, qty, it->request->target()->comp()); + if (shippable_trades > 0) { + double qty = it->amt; + + // inventory_size -= qty; + + Material::Ptr m = inventory.Pop(qty); + if (outrecipe.empty()) { + m->Transmute(it->request->target()->comp()); + } + + std::vector m_pkgd = m->Package(context()->GetPackage(package)); + + if (m->quantity() > cyclus::eps()) { + // If not all material is packaged successfully, return the excess + // amount to the inventory + inventory.Push(m); + //inventory_size += m->quantity(); + } + + Material::Ptr response; + if (m_pkgd.size() > 0) { + response = m_pkgd[0]; + shippable_trades -= 1; + } + + responses.push_back(std::make_pair(*it, response)); + LOG(cyclus::LEV_INFO5, "Source") << prototype() << " sent an order" + << " for " << response->quantity() << " of " << outcommod; } - responses.push_back(std::make_pair(*it, response)); - LOG(cyclus::LEV_INFO5, "Source") << prototype() << " sent an order" - << " for " << qty << " of " << outcommod; } } diff --git a/src/source.h b/src/source.h index ee4f72813a..b257d9b09e 100644 --- a/src/source.h +++ b/src/source.h @@ -67,6 +67,8 @@ class Source : public cyclus::Facility, GetMatlBids(cyclus::CommodMap::type& commod_requests); + virtual void EnterNotify(); + virtual void GetMatlTrades( const std::vector< cyclus::Trade >& trades, std::vector, @@ -116,6 +118,28 @@ class Source : public cyclus::Facility, } double throughput; + #pragma cyclus var { \ + "default": "unpackaged", \ + "tooltip": "name of package to provide material in", \ + "doc": "Name of package that this source provides. Offers will only be" \ + "made in packagable quantities of material.", \ + "uilabel": "Output Package Type", \ + "uitype": "package", \ + } + std::string package; + + #pragma cyclus var { \ + "default": "unrestricted", \ + "tooltip": "name of transport unit to ship packages in", \ + "doc": "Name of transport unit that this source uses to ship packages of " \ + "material. Offers will only be made in shippable quantities of " \ + "packages. Optional if packaging is used, but use of transport " \ + "units requires packaging type to also be set", \ + "uilabel": "Output Transport Unit Type", \ + "uitype": "transportunit", \ + } + std::string transport_unit; + #pragma cyclus var { \ "default": 0.0, \ "uilabel": "Geographical latitude in degrees as a double", \ @@ -132,9 +156,14 @@ class Source : public cyclus::Facility, } double longitude; + #pragma cyclus var { \ + "tooltip":"Material buffer"} + cyclus::toolkit::ResBuf inventory; + cyclus::toolkit::Position coordinates; void RecordPosition(); + void SetPackage(); }; } // namespace cycamore diff --git a/src/source_tests.cc b/src/source_tests.cc index c0daf839e3..ed518cdb1a 100644 --- a/src/source_tests.cc +++ b/src/source_tests.cc @@ -15,6 +15,7 @@ void SourceTest::SetUp() { trader = tc.trader(); InitParameters(); SetUpSource(); + src_facility->EnterNotify(); } void SourceTest::TearDown() { @@ -24,6 +25,8 @@ void SourceTest::TearDown() { void SourceTest::InitParameters() { commod = "commod"; recipe_name = "recipe"; + package_name = "testpackage"; + tu_name = "testtu"; capacity = 5; // some magic number.. recipe = cyclus::Composition::CreateFromAtom(cyclus::CompMap()); @@ -64,6 +67,8 @@ TEST_F(SourceTest, AddBids) { boost::shared_ptr< ExchangeContext > ec = GetContext(nreqs, commod); + src_facility->EnterNotify(); + std::set::Ptr> ports = src_facility->GetMatlBids(ec.get()->commod_requests); @@ -153,6 +158,38 @@ TEST_F(SourceTest, Longitude) { } +TEST_F(SourceTest, Package) { + using cyclus::QueryResult; + using cyclus::Cond; + + std::string config = + "commod" + "testpackage" + "5"; + + int simdur = 3; + cyclus::MockSim sim(cyclus::AgentSpec (":cycamore:Source"), config, simdur); + + sim.context()->AddPackage(package_name, 3, 4, "first"); + package = sim.context()->GetPackage(package_name); + // sim.context()->AddTransportUnit(tu_name, 1, 2, "hybrid"); + // tu = sim.context()->GetTransportUnit(tu_name); + + sim.AddSink("commod").Finalize(); + + EXPECT_NO_THROW(sim.Run()); + + QueryResult qr_tr = sim.db().Query("Transactions", NULL); + EXPECT_EQ(qr_tr.rows.size(), 3); + + std::vector conds; + conds.push_back(Cond("PackageName", "==", package->name())); + QueryResult qr_res = sim.db().Query("Resources", &conds); + + EXPECT_EQ(qr_res.rows.size(), 3); + +} + boost::shared_ptr< cyclus::ExchangeContext > SourceTest::GetContext(int nreqs, std::string commod) { using cyclus::Material; diff --git a/src/source_tests.h b/src/source_tests.h index 52413a21e5..73130e1c84 100644 --- a/src/source_tests.h +++ b/src/source_tests.h @@ -19,9 +19,11 @@ class SourceTest : public ::testing::Test { cyclus::TestContext tc; TestFacility* trader; cycamore::Source* src_facility; - std::string commod, recipe_name; + std::string commod, recipe_name, package_name, tu_name; double capacity; cyclus::Composition::Ptr recipe; + cyclus::Package::Ptr package; + cyclus::TransportUnit::Ptr tu; virtual void SetUp(); virtual void TearDown(); From ba3e22a6b9d80db19abeb96aa08d48d26a3cb664 Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Tue, 16 Jul 2024 17:14:36 -0500 Subject: [PATCH 2/8] bug fix and transport test --- CHANGELOG.rst | 1 + src/source.cc | 3 +-- src/source_tests.cc | 39 +++++++++++++++++++++++++++++++++++---- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index bbb17d3915..0a67ead68a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,6 +7,7 @@ Since last release **Added:** * Added package parameter to storage (#603, #612) +* Added package parameter to source (#613) **Changed:** diff --git a/src/source.cc b/src/source.cc index 48a80274da..9844318a73 100644 --- a/src/source.cc +++ b/src/source.cc @@ -109,7 +109,7 @@ std::set::Ptr> Source::GetMatlBids( std::vector bids; bids.assign(n_full_bids, bid_qty); - double remaining_qty = std::fmod(qty, bid_qty); + double remaining_qty = qty - (n_full_bids * bid_qty); if ((remaining_qty > cyclus::eps()) && (remaining_qty >= context()->GetPackage(package)->fill_min())) { bids.push_back(remaining_qty); } @@ -165,7 +165,6 @@ void Source::GetMatlTrades( // If not all material is packaged successfully, return the excess // amount to the inventory inventory.Push(m); - //inventory_size += m->quantity(); } Material::Ptr response; diff --git a/src/source_tests.cc b/src/source_tests.cc index ed518cdb1a..42c6fe551d 100644 --- a/src/source_tests.cc +++ b/src/source_tests.cc @@ -172,8 +172,6 @@ TEST_F(SourceTest, Package) { sim.context()->AddPackage(package_name, 3, 4, "first"); package = sim.context()->GetPackage(package_name); - // sim.context()->AddTransportUnit(tu_name, 1, 2, "hybrid"); - // tu = sim.context()->GetTransportUnit(tu_name); sim.AddSink("commod").Finalize(); @@ -186,8 +184,41 @@ TEST_F(SourceTest, Package) { conds.push_back(Cond("PackageName", "==", package->name())); QueryResult qr_res = sim.db().Query("Resources", &conds); - EXPECT_EQ(qr_res.rows.size(), 3); - + EXPECT_EQ(qr_res.rows.size(), 3); +} + +TEST_F(SourceTest, TransportUnit) { + using cyclus::QueryResult; + using cyclus::Cond; + + std::string config = + "commod" + "testpackage" + "testtu" + "10"; + + int simdur = 2; + cyclus::MockSim sim(cyclus::AgentSpec (":cycamore:Source"), config, simdur); + + sim.context()->AddPackage(package_name, 3, 4, "equal"); + package = sim.context()->GetPackage(package_name); + sim.context()->AddTransportUnit(tu_name, 2, 2); + tu = sim.context()->GetTransportUnit(tu_name); + + sim.AddSink("commod").Finalize(); + + EXPECT_NO_THROW(sim.Run()); + + QueryResult qr_tr = sim.db().Query("Transactions", NULL); + EXPECT_EQ(qr_tr.rows.size(), 4); + + std::vector conds; + conds.push_back(Cond("PackageName", "==", package->name())); + QueryResult qr_res = sim.db().Query("Resources", &conds); + + EXPECT_EQ(qr_res.rows.size(), 4); + + QueryResult qr_allres = sim.db().Query("Resources", NULL); } boost::shared_ptr< cyclus::ExchangeContext > From 8f585ceea2b23f509b6ac126d7e32da865951857 Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Wed, 17 Jul 2024 16:07:56 -0500 Subject: [PATCH 3/8] transmute at end and don't transmute unless request comp is different --- src/source.cc | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/source.cc b/src/source.cc index 9844318a73..dc20275dba 100644 --- a/src/source.cc +++ b/src/source.cc @@ -151,14 +151,9 @@ void Source::GetMatlTrades( for (it = trades.begin(); it != trades.end(); ++it) { if (shippable_trades > 0) { double qty = it->amt; - - // inventory_size -= qty; Material::Ptr m = inventory.Pop(qty); - if (outrecipe.empty()) { - m->Transmute(it->request->target()->comp()); - } - + std::vector m_pkgd = m->Package(context()->GetPackage(package)); if (m->quantity() > cyclus::eps()) { @@ -173,6 +168,10 @@ void Source::GetMatlTrades( shippable_trades -= 1; } + if (outrecipe.empty() && response->comp() != it->request->target()->comp()) { + response->Transmute(it->request->target()->comp()); + } + responses.push_back(std::make_pair(*it, response)); LOG(cyclus::LEV_INFO5, "Source") << prototype() << " sent an order" << " for " << response->quantity() << " of " << outcommod; From a9d8f20e550ae1e1f07e633205826f4c204f8b4b Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Wed, 17 Jul 2024 20:35:51 -0500 Subject: [PATCH 4/8] comment about taking the first item from the packaged vector --- src/source.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/source.cc b/src/source.cc index dc20275dba..99b5786459 100644 --- a/src/source.cc +++ b/src/source.cc @@ -164,6 +164,9 @@ void Source::GetMatlTrades( Material::Ptr response; if (m_pkgd.size() > 0) { + // Because we responded (in GetMatlBids) with individual package-sized + // bids, each packaged vector is guaranteed to have no more than one + // package in it. This single packaged resource is our response response = m_pkgd[0]; shippable_trades -= 1; } From 2bc9bf45335e0a32101c324f58a38f84266095e9 Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Thu, 18 Jul 2024 12:57:46 -0500 Subject: [PATCH 5/8] add recipes to tests to avoid undesired transmutation --- src/source_tests.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/source_tests.cc b/src/source_tests.cc index 42c6fe551d..10208e7338 100644 --- a/src/source_tests.cc +++ b/src/source_tests.cc @@ -164,12 +164,14 @@ TEST_F(SourceTest, Package) { std::string config = "commod" + "recipe" "testpackage" "5"; int simdur = 3; cyclus::MockSim sim(cyclus::AgentSpec (":cycamore:Source"), config, simdur); + sim.context()->AddRecipe(recipe_name, recipe); sim.context()->AddPackage(package_name, 3, 4, "first"); package = sim.context()->GetPackage(package_name); @@ -193,6 +195,7 @@ TEST_F(SourceTest, TransportUnit) { std::string config = "commod" + "recipe" "testpackage" "testtu" "10"; @@ -200,6 +203,7 @@ TEST_F(SourceTest, TransportUnit) { int simdur = 2; cyclus::MockSim sim(cyclus::AgentSpec (":cycamore:Source"), config, simdur); + sim.context()->AddRecipe(recipe_name, recipe); sim.context()->AddPackage(package_name, 3, 4, "equal"); package = sim.context()->GetPackage(package_name); sim.context()->AddTransportUnit(tu_name, 2, 2); From 8126c14ee161c6ec9911a9b8b07d21c18bb4e8fb Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Thu, 18 Jul 2024 13:00:40 -0500 Subject: [PATCH 6/8] changelog --- CHANGELOG.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0a67ead68a..e4460ca1b1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,7 +7,7 @@ Since last release **Added:** * Added package parameter to storage (#603, #612) -* Added package parameter to source (#613) +* Added package parameter to source (#613, #617) **Changed:** From 15d1993f28740df172639a6afa1b7c36a31c7868 Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Thu, 18 Jul 2024 10:54:41 -0500 Subject: [PATCH 7/8] add transport units --- src/storage.cc | 3 ++- src/storage.h | 8 +++++++ src/storage_tests.cc | 53 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/storage.cc b/src/storage.cc index 9fc563e174..d37748ad75 100644 --- a/src/storage.cc +++ b/src/storage.cc @@ -183,9 +183,10 @@ void Storage::EnterNotify() { buy_policy.Start(); std::string package_name_ = context()->GetPackage(package)->name(); + std::string tu_name_ = context()->GetTransportUnit(transport_unit)->name(); if (out_commods.size() == 1) { sell_policy.Init(this, &stocks, std::string("stocks"), 1e+299, false, - sell_quantity, package_name_) + sell_quantity, package_name_, tu_name_) .Set(out_commods.front()) .Start(); diff --git a/src/storage.h b/src/storage.h index 01186fdf43..0674eba579 100644 --- a/src/storage.h +++ b/src/storage.h @@ -437,6 +437,14 @@ class Storage "uilabel": "Package"} std::string package; + #pragma cyclus var {"default": "unrestricted", \ + "tooltip": "Output transport unit", \ + "doc": "Outgoing material, after packaging, will be "\ + "further restricted by transport unit when trading.", \ + "uitype": "transportunit", \ + "uilabel": "Transport Unit"} + std::string transport_unit; + #pragma cyclus var {"tooltip":"Incoming material buffer"} cyclus::toolkit::ResBuf inventory; diff --git a/src/storage_tests.cc b/src/storage_tests.cc index f28a9d6865..adb4891d7d 100644 --- a/src/storage_tests.cc +++ b/src/storage_tests.cc @@ -1041,6 +1041,59 @@ TEST_F(StorageTest, PackageMerge) { EXPECT_EQ(1, qr_res.GetVal("Quantity", 1)); } +TEST_F(StorageTest, TransportUnit) { + std::string config = + " commodity " + " commodity1 " + " 3 " + " foo" + " bar"; + + int simdur = 3; + + cyclus::MockSim sim(cyclus::AgentSpec (":cycamore:Storage"), config, simdur); + sim.context()->AddPackage("foo", 1, 1, "first"); + cyclus::Package::Ptr p = sim.context()->GetPackage("foo"); + sim.context()->AddTransportUnit("bar", 2, 2, "first"); + + sim.AddSource("commodity").Finalize(); + sim.AddSink("commodity1").Finalize(); + + int id = sim.Run(); + + std::vector tr_conds; + tr_conds.push_back(cyclus::Cond("Commodity", "==", std::string("commodity1"))); + + cyclus::QueryResult qr_trans = sim.db().Query("Transactions", &tr_conds); + // 6 transactions. While all three units could be packaged in one time step, + // the transport unit only allows two packages to be transported. Therefore, + // zero transactions the first timestep (material coming to storage from + // source). Then only two packages can be shipped at time step 1. Then the + // two packages plus the leftover material is able to ship four packages, + // two transport units in time 2 + EXPECT_EQ(6, qr_trans.rows.size()); + + EXPECT_EQ(1, qr_trans.GetVal("Time", 0)); + EXPECT_EQ(1, qr_trans.GetVal("Time", 1)); + EXPECT_EQ(2, qr_trans.GetVal("Time", 2)); + EXPECT_EQ(2, qr_trans.GetVal("Time", 3)); + EXPECT_EQ(2, qr_trans.GetVal("Time", 4)); + EXPECT_EQ(2, qr_trans.GetVal("Time", 5)); + + for (int i = 0; i < qr_trans.rows.size(); i++) { + std::cerr << "transaction " << i << "is at time " << qr_trans.GetVal("Time", i) << std::endl; + } + + std::vector res_conds; + res_conds.push_back(cyclus::Cond("PackageName", "==", p->name())); + cyclus::QueryResult qr_res = sim.db().Query("Resources", &res_conds); + // All pkgd resources are size 1 + EXPECT_EQ(6, qr_res.rows.size()); + + EXPECT_EQ(1, qr_res.GetVal("Quantity", 0)); + EXPECT_EQ(1, qr_res.GetVal("Quantity", 5)); +} + } // namespace cycamore // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 5e4f9e07a821d9f2cc683291585fc5d34dd81996 Mon Sep 17 00:00:00 2001 From: Katie Mummah Date: Fri, 19 Jul 2024 11:07:44 -0500 Subject: [PATCH 8/8] changelog --- CHANGELOG.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e4460ca1b1..78093eb3f7 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,7 +6,7 @@ Since last release ====================== **Added:** -* Added package parameter to storage (#603, #612) +* Added package parameter to storage (#603, #612, #616) * Added package parameter to source (#613, #617) **Changed:**