diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 58c19ddfdc..73bbf791b5 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -19,7 +19,7 @@ Since last release * Rely on ``python3`` in environment instead of ``python`` (#1747) * Remove ``pandas`` as build dependency (#1748) * Consistently use hyphens in ``install.py`` flags (#1748) -* Material sell policy can package materials (#1749, #1774, #1775) +* Material sell policy can package materials (#1749, #1774, #1775, #1795) * Use miniforge for conda CI builds instead of miniconda (#1763) * Define constants ``CY_LARGE_DOUBLE``, ``CY_LARGE_INT``, and ``CY_NEAR_ZERO`` (#1757) * Warning and limits on number of packages that can be created from a resource at once (#1771) diff --git a/src/material.cc b/src/material.cc index 186674ea70..9a80b075d8 100644 --- a/src/material.cc +++ b/src/material.cc @@ -93,10 +93,7 @@ Material::Ptr Material::ExtractComp(double qty, Composition::Ptr c, // this material regardless of composition. other->prev_decay_time_ = prev_decay_time_; - if (qty_ > cyclus::eps()) { - tracker_.Extract(&other->tracker_); - } // else, this material is being fully extracted and nothing has effectively - // changed. Don't need to bump state, parent, etc. + tracker_.Extract(&other->tracker_); return other; } @@ -143,15 +140,35 @@ void Material::Transmute(Composition::Ptr c) { } } +Resource::Ptr Material::PackageExtract(double qty, std::string new_package_name) { + if ((qty - qty_) > eps_rsrc()) { + throw ValueError("Attempted to extract more quantity than exists."); + } + + qty_ -= qty; + Material::Ptr other(new Material(ctx_, qty, comp_, new_package_name)); + + // Decay called on the extracted material should have the same dt as for + // this material regardless of composition. + other->prev_decay_time_ = prev_decay_time_; + + // this call to res_tracker must come first before the parent resource + // state id gets modified + other->tracker_.Package(&tracker_); + if (qty_ > eps_rsrc()) { + tracker_.Modify(); + } + return boost::static_pointer_cast(other); +} + void Material::ChangePackage(std::string new_package_name) { - if (new_package_name == package_name_ || ctx_ == NULL) { + if (ctx_ == NULL) { // no change needed return; } else if (new_package_name == Package::unpackaged_name()) { // unpackaged has functionally no restrictions package_name_ = new_package_name; - tracker_.Package(); return; } @@ -160,10 +177,10 @@ void Material::ChangePackage(std::string new_package_name) { double max = p->fill_max(); if (qty_ >= min && qty_ <= max) { package_name_ = new_package_name; - tracker_.Package(); } else { throw ValueError("Material quantity is outside of package fill limits."); } + tracker_.Package(); } void Material::Decay(int curr_time) { diff --git a/src/material.h b/src/material.h index 008f9fde06..7d08eae319 100644 --- a/src/material.h +++ b/src/material.h @@ -158,6 +158,9 @@ class Material: public Resource { virtual std::string package_name(); + virtual Resource::Ptr PackageExtract(double qty, + std::string new_package_name = Package::unpackaged_name()); + /// Changes the package id. Checks that the resource fits the package /// type minimum and maximum mass criteria. virtual void ChangePackage(std::string new_package_name = Package::unpackaged_name()); diff --git a/src/package.cc b/src/package.cc index 8f5b9f3a7a..b5ab64e301 100644 --- a/src/package.cc +++ b/src/package.cc @@ -1,5 +1,6 @@ #include "package.h" #include "error.h" +#include "cyc_limits.h" namespace cyclus { @@ -30,7 +31,7 @@ Package::Ptr& Package::unpackaged() { } std::pair Package::GetFillMass(double qty) { - if (qty < fill_min_) { + if ((qty - fill_min_) < -eps_rsrc()) { // less than one pkg of material available return std::pair (0, 0); } diff --git a/src/product.cc b/src/product.cc index 254624ca1b..3081512c9b 100644 --- a/src/product.cc +++ b/src/product.cc @@ -2,6 +2,7 @@ #include "error.h" #include "logger.h" +#include "cyc_limits.h" namespace cyclus { @@ -76,6 +77,23 @@ std::string Product::package_name() { return package_name_; } +Resource::Ptr Product::PackageExtract(double qty, std::string new_package_name) { + if (qty > quantity_) { + throw ValueError("Attempted to extract more quantity than exists."); + } + + quantity_ -= qty; + Product::Ptr other(new Product(ctx_, qty, quality_, new_package_name)); + + // this call to res_tracker must come first before the parent resource + // state id gets modified + other->tracker_.Package(&tracker_); + if (quantity_ > cyclus::eps_rsrc()) { + tracker_.Modify(); + } + return boost::static_pointer_cast(other); +} + void Product::ChangePackage(std::string new_package_name) { if (new_package_name == package_name_ || ctx_ == NULL) { // no change needed diff --git a/src/product.h b/src/product.h index 99f4091d69..68d734093b 100644 --- a/src/product.h +++ b/src/product.h @@ -74,6 +74,8 @@ class Product : public Resource { /// Returns the package id. virtual std::string package_name(); + virtual Resource::Ptr PackageExtract(double qty, std::string new_package_name = Package::unpackaged_name()); + /// Changes the product's package id virtual void ChangePackage(std::string new_package_name = Package::unpackaged_name()); diff --git a/src/res_tracker.cc b/src/res_tracker.cc index d9c877bab0..244c497558 100644 --- a/src/res_tracker.cc +++ b/src/res_tracker.cc @@ -23,7 +23,8 @@ void ResTracker::Create(Agent* creator) { parent1_ = 0; parent2_ = 0; - Record(); + bool bumpId = false; + Record(bumpId); ctx_->NewDatum("ResCreators") ->AddVal("ResourceId", res_->state_id()) ->AddVal("AgentId", creator->id()) @@ -51,7 +52,7 @@ void ResTracker::Extract(ResTracker* removed) { Record(); } - + removed->parent1_ = res_->state_id(); removed->parent2_ = 0; removed->tracked_ = tracked_; @@ -69,17 +70,36 @@ void ResTracker::Absorb(ResTracker* absorbed) { Record(); } -void ResTracker::Package() { +void ResTracker::Package(ResTracker* parent) { if (!tracked_) { return; } - + parent2_ = 0; + tracked_ = tracked_; package_name_ = res_->package_name(); - Record(); + + if (parent != NULL) { + parent1_ = parent->res_->state_id(); + + // Resource was just created, with packaging info, and assigned a state id. + // Do not need to bump again + bool bumpId = false; + Record(bumpId); + } else { + // Resource was not just created. It is being re-packaged. It needs to be + // bumped to get a new state id. + parent1_ = res_->state_id(); + Record(); + } + + + } -void ResTracker::Record() { - res_->BumpStateId(); +void ResTracker::Record(bool bumpId) { + if (bumpId) { + res_->BumpStateId(); + } ctx_->NewDatum("Resources") ->AddVal("ResourceId", res_->state_id()) ->AddVal("ObjId", res_->obj_id()) @@ -92,7 +112,6 @@ void ResTracker::Record() { ->AddVal("Parent1", parent1_) ->AddVal("Parent2", parent2_) ->Record(); - res_->Record(ctx_); } diff --git a/src/res_tracker.h b/src/res_tracker.h index c4e83b71e5..ce650bf6b4 100644 --- a/src/res_tracker.h +++ b/src/res_tracker.h @@ -42,11 +42,14 @@ class ResTracker { /// decay). void Modify(); - /// Should be called when a resource's package gets modified - void Package(); + /// Should be called when a resource's package gets modified. If the resource + /// was just created from a parent resource, the parent should be passed in. + /// If the resource is just being repackaged (e.g. to unpackaged), the parent + /// should be NULL. + void Package(ResTracker* parent = NULL); private: - void Record(); + void Record(bool bumpId = true); int parent1_; int parent2_; diff --git a/src/resource.h b/src/resource.h index 919a36a323..aa29a7617b 100644 --- a/src/resource.h +++ b/src/resource.h @@ -6,6 +6,7 @@ #include #include "package.h" +#include "cyc_limits.h" class SimInitTest; @@ -94,6 +95,8 @@ class Resource { /// Returns the package id. virtual std::string package_name() { return Package::unpackaged_name(); }; + virtual Ptr PackageExtract(double qty, std::string new_package_name) = 0; + /// Changes the product's package id virtual void ChangePackage(std::string new_package_name = Package::unpackaged_name()) {}; @@ -107,6 +110,12 @@ class Resource { static int nextstate_id_; static int nextobj_id_; int state_id_; + // Setting the state id should only be done when extracting one resource + void state_id(int st_id) { + state_id_ = st_id; + } + + int obj_id_; }; @@ -143,10 +152,9 @@ std::vector Resource::Package(Package::Ptr pkg) { int approx_num_pkgs = fill.second; Package::ExceedsSplitLimits(approx_num_pkgs); - while (quantity() > 0 && quantity() >= pkg->fill_min()) { + while (quantity() > 0 && (quantity() - pkg->fill_min()) >= -eps_rsrc()) { double pkg_fill = std::min(quantity(), fill_mass); - t_pkgd = boost::dynamic_pointer_cast(ExtractRes(pkg_fill)); - t_pkgd->ChangePackage(pkg->name()); + t_pkgd = boost::dynamic_pointer_cast(PackageExtract(pkg_fill, pkg->name())); ts_pkgd.push_back(t_pkgd); } return ts_pkgd; diff --git a/src/toolkit/matl_buy_policy.cc b/src/toolkit/matl_buy_policy.cc index a00a6e0658..5db7fc4519 100644 --- a/src/toolkit/matl_buy_policy.cc +++ b/src/toolkit/matl_buy_policy.cc @@ -318,7 +318,7 @@ void MatlBuyPolicy::AcceptMatlTrades( // check if cumulative cap has been reached. If yes, then sample for dormant // length and reset cycle_total_inv if (use_cumulative_capacity() && ( - (cumulative_cap_ - cycle_total_inv_) < eps())) { + (cumulative_cap_ - cycle_total_inv_) < eps_rsrc())) { 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_sell_policy.cc b/src/toolkit/matl_sell_policy.cc index 603b7b558c..50e6105e18 100644 --- a/src/toolkit/matl_sell_policy.cc +++ b/src/toolkit/matl_sell_policy.cc @@ -265,7 +265,7 @@ void MatlSellPolicy::GetMatlTrades( if (shippable_pkgs > 0){ qty = it->amt; LGH(INFO3) << " sending " << qty << " kg of " << it->request->commodity(); - Material::Ptr mat = buf_->Pop(qty, cyclus::eps_rsrc()); + Material::Ptr mat = buf_->Pop(qty, eps_rsrc()); Material::Ptr trade_mat; // don't go through packaging if you don't need to. packaging always bumps @@ -274,7 +274,7 @@ void MatlSellPolicy::GetMatlTrades( if (package_->name() != mat->package_name()) { // packaging needed std::vector mat_pkgd = mat->Package(package_); - if (mat->quantity() > eps()) { + if (mat->quantity() > eps_rsrc()) { // push any extra material that couldn't be packaged back onto buffer // don't push unless there's leftover material buf_->Push(mat);