Skip to content

Commit

Permalink
Merge pull request #1729 from nuclearkatie/packaging
Browse files Browse the repository at this point in the history
Package management by context and handling of repackaging process
  • Loading branch information
gonuke committed Apr 30, 2024
2 parents 7966963 + 7092890 commit ff37df4
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 72 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Since last release
* 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)
* AddMutalReqs and AddReciepe functions and exclusive bids in python API of DRE (#1584)
* Created Package class and optional declaration of packages in input files (#1673, #1699, #1712), package id is default unpackaged (#1711) and is a member of
* Created Package class and optional declaration of packages in input files (#1673, #1699, #1712, #1729), package id is default unpackaged (#1711) and is a member of
resources (materials/products) (#1675). Can pop resources as packaged from resource buffer, pushing resource onto a buffer defaults to stripping packaging (#1683)
* CI support for Rocky Linux (#1691)
* Added support for a ResBuf to behave as a single bulk storage with mixing & extraction of resources (#1687)
Expand Down
7 changes: 6 additions & 1 deletion src/context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ Context::Context(Timer* ti, Recorder* rec)
solver_(NULL),
trans_id_(0),
si_(0) {

rng_ = new RandomNumberGenerator();
}

Expand Down Expand Up @@ -204,13 +203,19 @@ void Context::AddPackage(std::string name, double fill_min, double fill_max,
}

Package::Ptr Context::GetPackageByName(std::string name) {
if (name == Package::unpackaged_name()) {
return Package::unpackaged();
}
if (packages_.count(name) == 0) {
throw KeyError("Invalid package name " + name);
}
return packages_[name];
}

Package::Ptr Context::GetPackageById(int id) {
if (id == Package::unpackaged_id()) {
return Package::unpackaged();
}
if (id < 0) {
throw ValueError("Invalid package id " + std::to_string(id));
}
Expand Down
86 changes: 40 additions & 46 deletions src/package.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,71 +4,65 @@
namespace cyclus {

// unpackaged id is 1, so start the user-declared packaging id at 2
int Package::next_id_ = 2;
int Package::next_package_id_ = 2;
Package::Ptr Package::unpackaged_ = NULL;

template <class T>
double Package::GetFillMass(typename T::Ptr r) {
if (r->quantity() < fill_min_) {
Package::Ptr Package::Create(std::string name, double fill_min, double fill_max, std::string strategy) {
if (fill_min < 0 || fill_max < 0) {
throw ValueError("fill_min and fill_max must be non-negative");
}
else if (fill_min > fill_max) {
throw ValueError("fill_min must be less than or equal to fill_max");
}
Ptr p(new Package(name, fill_min, fill_max, strategy));
return p;
}

// singleton pattern:
// if the static member is not yet set, create a new object
// otherwise return the object that already exists
Package::Ptr& Package::unpackaged() {

if (!unpackaged_) {
unpackaged_ = Ptr(new Package(unpackaged_name_));
}

return unpackaged_;
}

double Package::GetFillMass(double qty) {
if (qty < fill_min_) {
// less than one pkg of material available
return 0;
}

std::vector<typename T::Ptr> rs;
typename T::Ptr r_pkgd;
double fill_mass;
if (strategy_ == "first") {
fill_mass = fill_max_;
} else if (strategy_ == "equal") {
int num_min_fill = std::floor(r->quantity() / fill_min_);
int num_max_fill = std::ceil(r->quantity() / fill_max_);
int num_min_fill = std::floor(qty / fill_min_);
int num_max_fill = std::ceil(qty / fill_max_);
if (num_min_fill >= num_max_fill) {
// all material can fit in a package
double fill_mass = r->quantity() / num_max_fill;
double fill_mass = qty / num_max_fill;
} else {
// some material will remain unpackaged, fill up as many max packages as possible
fill_mass = fill_max_;
}
}
return fill_mass;
}

template <class T>
std::vector<typename T::Ptr> Package::PackageResource(typename T::Ptr r) {
std::vector<typename T::Ptr> rs_pkgd;
typename T::Ptr r_pkgd;

double fill_mass = GetFillMass(r);
if (fill_mass ==0) {
return rs_pkgd;
}

while (r->quantity() > fill_min_) {
double pkg_fill = std::min(r->quantity(), fill_mass);
r_pkgd = boost::dynamic_pointer_cast<T>(r->ExtractRes(pkg_fill));
r_pkgd->ChangePackageId(id_);
rs_pkgd.push_back(r_pkgd);
}
return rs_pkgd;
}

Package::Ptr Package::Create() {
Ptr p(new Package());
return p;
}

Package::Ptr Package::Create(std::string name, double fill_min, double fill_max, std::string strategy) {
if (fill_min < 0 || fill_max < 0) {
throw ValueError("fill_min and fill_max must be non-negative");
}
else if (fill_min > fill_max) {
throw ValueError("fill_min must be less than or equal to fill_max");
Package::Package(std::string name, double fill_min, double fill_max, std::string strategy) :
name_(name), fill_min_(fill_min), fill_max_(fill_max), strategy_(strategy) {
if (name == unpackaged_name_) {
if (unpackaged_) {
throw ValueError("can't create a new package with name 'unpackaged'");
}
id_ = unpackaged_id_;
} else {
id_ = next_package_id_++;
}
}
Ptr p(new Package(name, fill_min, fill_max, strategy));
return p;
}

Package::Package() : id_(next_id_++), fill_min_(0), fill_max_(std::numeric_limits<double>::max()) {}

Package::Package(std::string name, double fill_min, double fill_max, std::string strategy) : name_(name), id_(next_id_++), fill_min_(fill_min), fill_max_(fill_max), strategy_(strategy) {}

} // namespace cyclus
40 changes: 21 additions & 19 deletions src/package.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,16 @@ namespace cyclus {

/// Packager is a class that packages materials into discrete items in ways that mimic realistic nuclear material handling. Packages will eventually be a required parameter of resources.
class Package {

public:
typedef boost::shared_ptr<Package> Ptr;

// create a new package type. Should be called by the context only
// (see Context::AddPackage), unless you want an untracked package
// type (which you probably don't)
static Ptr Create(std::string name, double fill_min = 0,
double fill_max = std::numeric_limits<double>::max(),
std::string strategy = "first");

/// Returns optimal fill mass for a resource to be packaged. Can be used
/// to determine how to respond to requests for material, and to actually
/// package and send off trades.
Expand All @@ -30,20 +36,7 @@ class Package {
/// quantity = 5, fill_min = 3, fill_max = 4. num_min_fill = floor(5/3) = 1,
/// num_max_fill = ceil(5/4) = 2. num_min_fill < num_max_fill, so fill to
/// the max.
template <class T>
double GetFillMass(typename T::Ptr r);

/// Repackages a single resource into a package. If some quantity of the
/// resource cannot be packaged using the given packaging strategy and
/// restrictions, the remainder is left in the resource object.
template <class T>
std::vector<typename T::Ptr> PackageResource(typename T::Ptr r);

// create a new package type with default values
static Ptr Create();

// create a new package type
static Ptr Create(std::string name, double fill_min, double fill_max, std::string strategy);
double GetFillMass(double qty);

// returns package id
int id() const { return id_; }
Expand All @@ -59,13 +52,22 @@ class Package {
// returns the unpackaged id (1)
static int unpackaged_id() { return unpackaged_id_; }

protected:
Package();
Package(std::string name, double fill_min, double fill_max, std::string strategy);
// returns the unpackaged package name
static std::string unpackaged_name() { return unpackaged_name_; }

// returns the unpackaged singleton object
static Ptr& unpackaged();

private:
Package(std::string name,
double fill_min = 0,
double fill_max = std::numeric_limits<double>::max(),
std::string strategy = "first");

static const int unpackaged_id_ = 1;
static int next_id_;
static constexpr char unpackaged_name_[11] = "unpackaged";
static Ptr unpackaged_;
static int next_package_id_;

std::string name_;
int id_;
Expand Down
3 changes: 1 addition & 2 deletions src/product.cc
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,7 @@ void Product::ChangePackageId(int new_package_id) {
package_id_ = new_package_id;
return;
}

Package::Ptr p = ctx_->GetPackageById(package_id_);
Package::Ptr p = ctx_->GetPackageById(new_package_id);
double min = p->fill_min();
double max = p->fill_max();
if (quantity_ >= min && quantity_ <= max) {
Expand Down
28 changes: 28 additions & 0 deletions src/resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ class Resource {
/// Changes the product's package id
virtual void ChangePackageId(int new_package_id = Package::unpackaged_id()) {};

/// Repackages a single resource into a package. If some quantity of the
/// resource cannot be packaged using the given packaging strategy and
/// restrictions, the remainder is left in the resource object.
template <class T>
std::vector<typename T::Ptr> Package(Package::Ptr pkg);

private:
static int nextstate_id_;
static int nextobj_id_;
Expand All @@ -121,6 +127,28 @@ typename T::Ptr ResCast(Resource::Ptr r) {
return boost::dynamic_pointer_cast<T>(r);
}


template <class T>
std::vector<typename T::Ptr> Resource::Package(Package::Ptr pkg) {
std::vector<typename T::Ptr> ts_pkgd;
typename T::Ptr t_pkgd;

double fill_mass = pkg->GetFillMass(quantity());
if (fill_mass == 0) {
return ts_pkgd;
}

while (quantity() > pkg->fill_min()) {
double pkg_fill = std::min(quantity(), fill_mass);
t_pkgd = boost::dynamic_pointer_cast<T>(ExtractRes(pkg_fill));
t_pkgd->ChangePackageId(pkg->id());
ts_pkgd.push_back(t_pkgd);
}

return ts_pkgd;
}


} // namespace cyclus

#endif // CYCLUS_SRC_RESOURCE_H_
3 changes: 0 additions & 3 deletions src/xml_file_loader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -329,8 +329,6 @@ void XMLFileLoader::LoadRecipes() {

void XMLFileLoader::LoadPackages() {
InfileTree xqe(*parser_);
// create default package
ctx_->AddPackage("default", 0, std::numeric_limits<int>::max(), "first");

std::string query = "/*/package";
int num_packages = xqe.NMatches(query);
Expand All @@ -344,7 +342,6 @@ void XMLFileLoader::LoadPackages() {

std::string strategy = cyclus::OptionalQuery<std::string>(qe, "strategy", "first");

boost::shared_ptr<Package> p = Package::Create(name, fill_min, fill_max, strategy);
ctx_->AddPackage(name, fill_min, fill_max, strategy);
}
}
Expand Down
62 changes: 62 additions & 0 deletions tests/resource_tests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

using cyclus::Material;
using cyclus::Product;
using cyclus::Resource;
using cyclus::Package;

class Dummy : public cyclus::Region {
public:
Expand Down Expand Up @@ -99,3 +101,63 @@ TEST_F(ResourceTest, ProductExtractGraphid) {
EXPECT_NE(p1->state_id(), p3->state_id());
}

TEST_F(ResourceTest, DefaultPackageId) {
EXPECT_EQ(m1->package_id(), Package::unpackaged_id());
EXPECT_EQ(m2->package_id(), Package::unpackaged_id());
EXPECT_EQ(p1->package_id(), Package::unpackaged_id());
EXPECT_EQ(p2->package_id(), Package::unpackaged_id());

Product::Ptr p3 = p1->Extract(2);
EXPECT_EQ(p3->package_id(), Package::unpackaged_id());
}

TEST_F(ResourceTest, ChangePackageId) {
ctx->AddPackage("foo", 1, 5, "first");
Package::Ptr pkg = ctx->GetPackageByName("foo");
int pkg_id = pkg->id();

Product::Ptr p3 = p1->Extract(2);
p3->ChangePackageId(pkg_id);
EXPECT_EQ(p3->package_id(), pkg_id);
EXPECT_EQ(p1->package_id(), Package::unpackaged_id());

m1->ChangePackageId(pkg_id);
EXPECT_EQ(m1->package_id(), pkg_id);
}

TEST_F(ResourceTest, PackageResource) {
ctx->AddPackage("foo", 1, 5, "first");
Package::Ptr pkg = ctx->GetPackageByName("foo");
int pkg_id = pkg->id();

// nothing packaged
Product::Ptr p3 = p1->Extract(0.5);
std::vector<Product::Ptr> p3_pkgd = p3->Package<Product>(pkg);

// everything stays in old product, with same (default) package id
EXPECT_EQ(p3->package_id(), Package::unpackaged_id());
EXPECT_EQ(p3->quantity(), 0.5);

// all packaged
std::vector<Product::Ptr> p1_pkgd = p1->Package<Product>(pkg);
EXPECT_EQ(p1->quantity(), 0);
EXPECT_EQ(p1_pkgd.size(), 1);
EXPECT_EQ(p1_pkgd[0]->package_id(), pkg_id);

// // two packages
std::vector<Product::Ptr> p2_pkgd = p2->Package<Product>(pkg);
EXPECT_EQ(p2->quantity(), 0);
EXPECT_EQ(p2_pkgd.size(), 2);
EXPECT_EQ(p2_pkgd[0]->quantity(), 5);
EXPECT_EQ(p2_pkgd[1]->quantity(), 2);
EXPECT_EQ(p2_pkgd[0]->package_id(), pkg_id);
EXPECT_EQ(p2_pkgd[1]->package_id(), pkg_id);

Material::Ptr m3 = m2->ExtractQty(5.5);
std::vector<Material::Ptr> m3_pkgd = m3->Package<Material>(pkg);
EXPECT_EQ(m3->package_id(), Package::unpackaged_id());
EXPECT_EQ(m3->quantity(), 0.5);
EXPECT_EQ(m3_pkgd.size(), 1);
EXPECT_EQ(m3_pkgd[0]->package_id(), pkg_id);
EXPECT_EQ(m3_pkgd[0]->quantity(), 5);
}

0 comments on commit ff37df4

Please sign in to comment.