diff --git a/CHANGELOG.rst b/CHANGELOG.rst index bbb17d391..0a67ead68 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 0def041f6..99b578645 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 = qty - (n_full_bids * 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,41 @@ 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; + + Material::Ptr m = inventory.Pop(qty); + + 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); + } + + 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; + } + + 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; } - 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 ee4f72813..b257d9b09 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 c0daf839e..42c6fe551 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,69 @@ 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.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); +} + +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 > SourceTest::GetContext(int nreqs, std::string commod) { using cyclus::Material; diff --git a/src/source_tests.h b/src/source_tests.h index 52413a21e..73130e1c8 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();