Skip to content

Commit

Permalink
transport unit fill and max shippable transport units
Browse files Browse the repository at this point in the history
  • Loading branch information
nuclearkatie committed May 30, 2024
1 parent ede8606 commit c46afec
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 62 deletions.
60 changes: 31 additions & 29 deletions src/context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -224,39 +224,41 @@ Package::Ptr Context::GetPackage(std::string 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));
}
// iterate through the list of packages to get the one package with the correct id
std::map<std::string, Package::Ptr>::iterator it;
for (it = packages_.begin(); it != packages_.end(); ++it) {
if (it->second->id() == id) {
return it->second;
}
void Context::AddTransportUnit(std::string name, int fill_min, int fill_max,
std::string strategy) {
if (transport_units_.count(name) == 0) {
TransportUnit::Ptr tu = TransportUnit::Create(name, fill_min, fill_max,
strategy);
transport_units_[name] = tu;
RecordTransportUnit(tu);
} else {
throw KeyError("TransportUnit " + name + " already exists!");
}
throw ValueError("Invalid package id " + std::to_string(id));
}

void AddTransportUnit(std::string name, int fill_min, int fill_max,
std::string strategy) {
transport_units_[name] = TransportUnit::Create(name, fill_min, fill_max, strategy);
NewDatum("TransportUnit")
->AddVal("TransportUnit", name)
->AddVal("FillMin", fill_min)
->AddVal("FillMax", fill_max)
->AddVal("Strategy", strategy)
->AddVal("Id", transport_units_[name]->id())
->Record();
}
void Context::RecordTransportUnit(TransportUnit::Ptr tu) {
NewDatum("TransportUnit")
->AddVal("TransportUnitName", tu->name())
->AddVal("FillMin", tu->fill_min())
->AddVal("FillMax", tu->fill_max())
->AddVal("Strategy", tu->strategy())
->Record();
}

/// Retrieve a registered transport unit
TransportUnit::Ptr GetTransportUnitByName(std::string name);
/// Retrieve a registered transport unit
TransportUnit::Ptr Context::GetTransportUnit(std::string name) {
if (name == TransportUnit::unrestricted_name()) {
return TransportUnit::unrestricted();
}
if (transport_units_.size() == 0 ) {
throw KeyError("No user-created transport units exist");
}
if (transport_units_.count(name) == 0) {
throw KeyError("Invalid transport unit name " + name);
}
return transport_units_[name];
}

TransportUnit::Ptr GetTransportUnitById(int id);

void Context::InitSim(SimInfo si) {
NewDatum("Info")
Expand Down Expand Up @@ -311,7 +313,7 @@ void Context::InitSim(SimInfo si) {
int Context::time() {
return ti_->time();
}
LoadPacka

int Context::random() {
return rng_->random();
}
Expand Down
13 changes: 13 additions & 0 deletions src/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,19 @@ class Context {
/// Retrieve a registered package.
Package::Ptr GetPackage(std::string name);

/// Adds a transport unit type to a simulation-wide accessible list.
/// Agents should NOT add their own transport units.
void AddTransportUnit(std::string name, int fill_min = 0,
int fill_max = std::numeric_limits<int>::max(),
std::string strategy = "first");

/// Records transport unit information. Should be used first on unrestricted,
/// then to record user-declared transport units
void RecordTransportUnit(TransportUnit::Ptr);

/// Retrieve a registered transport unit.
TransportUnit::Ptr GetTransportUnit(std::string name);

int random();

/// Generates a random number on the range [0,1)]
Expand Down
57 changes: 30 additions & 27 deletions src/package.cc
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ Package::Package(std::string name, double fill_min, double fill_max,
}
}

// unrestricted id is 1, so start the user-declared transport id at 2
int TransportUnit::next_package_id_ = 2;
TransportUnit::Ptr TransportUnit::unrestricted_ = NULL;

TransportUnit::Ptr TransportUnit::Create(std::string name, int fill_min, int fill_max, std::string strategy) {
Expand Down Expand Up @@ -94,45 +92,50 @@ int TransportUnit::GetTransportUnitFill(int qty) {
return 0;
}

int fill_mass;
if (strategy_ == "first") {
fill_mass = fill_max_;
return fill_max_;
} else if (strategy_ == "equal") {
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
int fill_mass = qty / num_max_fill;
} else {
// some material will remain unrestricted, fill up as many transport
// units as possible
fill_mass = fill_max_;
// int division automatically rounds down. don't need floor in min, and
// get ceil by hand instead
int num_at_min_fill = qty / fill_min_;
int num_at_max_fill = (qty + fill_max_ - 1) / fill_max_;

if (num_at_min_fill >= num_at_max_fill) {
// all material *might* fit transport units. However, this is more
// challenging than packages because transport units are discrete. check:

double dbl_fill_mass = (double)qty / (double)num_at_max_fill;
return std::floor(dbl_fill_mass);
}
// some material will remain unrestricted, fill up as many transport
// units as possible. Or, perfect fill is possible but not with integer
// fill (see above). Should also start filling to max until last partial
// filled transport unit
return fill_max_;
}
return fill_mass;
}

int TransportUnit:TotalShippablePackages(int pkgs) {
int fill = GetTransportUnitFill(pkgs);
int shippable = std::floor(pkgs / fill) * fill;
int TransportUnit::MaxShippablePackages(int pkgs) {
int TU_fill;
int shippable = 0;

int remainder = pkgs % fill;
if (remainder > 0 && remainder >= fill_min_) {
shippable += remainder;
if (pkgs == 0 && pkgs < fill_min_) {
return 0;
}

while (pkgs > 0 && pkgs >= fill_min_) {
TU_fill = GetTransportUnitFill(pkgs);
shippable += TU_fill;
pkgs -= TU_fill;
}
return shippable;
}

TransportUnit::TransportUnit(std::string name, int fill_min, int fill_max, std::string strategy) :
name_(name), fill_min_(fill_min), fill_max_(fill_max), strategy_(strategy) {
if (name == unrestricted_name_) {
if (unrestricted_) {
throw ValueError("can't create a new transport unit with name 'unrestricted'");
}
id_ = unrestricted_id_;
} else {
id_ = next_transport_unit_id_++;
}
throw ValueError("can't create a new transport unit with name 'unrestricted'");
}
}

} // namespace cyclus
4 changes: 2 additions & 2 deletions src/package.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class TransportUnit {
int GetTransportUnitFill(int qty);

/// Returns the max number of packages that can be shipped
int TotalShippablePackages(int pkgs);
int MaxShippablePackages(int pkgs);

// returns package id
int id() const { return id_; }
Expand Down Expand Up @@ -115,7 +115,7 @@ class TransportUnit {
std::string strategy = "first");

static const int unrestricted_id_ = 1;
static constexpr char unrestricted_name_[11] = "unrestricted";
static constexpr char unrestricted_name_[13] = "unrestricted";
static Ptr unrestricted_;
static int next_tranport_unit_id_;

Expand Down
1 change: 1 addition & 0 deletions src/xml_file_loader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ void XMLFileLoader::LoadTransportUnits() {
std::string strategy = cyclus::OptionalQuery<std::string>(qe, "strategy", "first");

ctx_->AddTransportUnit(name, fill_min, fill_max, strategy);
}
}

void XMLFileLoader::LoadSpecs() {
Expand Down
12 changes: 12 additions & 0 deletions tests/context_tests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -134,5 +134,17 @@ TEST_F(ContextTests, DoublePackageNameThrow) {

ASSERT_THROW(ctx->AddPackage("foo"), cyclus::KeyError);

delete ctx;
}

TEST_F(ContextTests, DoubleTransportUnitNameThrow) {
Timer ti;
Recorder rec;
Context* ctx = new Context(&ti, &rec);

ctx->AddTransportUnit("foo");

ASSERT_THROW(ctx->AddTransportUnit("foo"), cyclus::KeyError);

delete ctx;
}
123 changes: 119 additions & 4 deletions tests/package_tests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
#include "test_agents/test_facility.h"

using cyclus::Package;
using cyclus::TransportUnit;

TEST(PackageTests, Create) {
TEST(PackageTests, CreatePackage) {

std::string p_exp_name = "foo";
double exp_min = 0.1;
Expand All @@ -32,7 +33,7 @@ TEST(PackageTests, Create) {

}

TEST(PackageTests, UnpackagedID) {
TEST(PackageTests, Unpackaged) {
EXPECT_EQ("unpackaged", Package::unpackaged_name());
}

Expand All @@ -46,14 +47,14 @@ TEST(PackageTests, InvalidPackage) {
EXPECT_THROW(Package::Create("foo", 100, 1, "first"), cyclus::ValueError);
}

TEST(PackageTests, GetFillMass) {
TEST(PackageTests, GetPackageFillMass) {
double min = 0.3;
double max = 0.9;
double tight_min = 0.85;

Package::Ptr p = Package::Create("foo", min, max, "first");
Package::Ptr q = Package::Create("bar", min, max, "equal");
Package::Ptr r = Package::Create("bar", tight_min, max, "equal");
Package::Ptr r = Package::Create("baz", tight_min, max, "equal");

double exp;

Expand All @@ -75,4 +76,118 @@ TEST(PackageTests, GetFillMass) {
EXPECT_EQ(max, p->GetFillMass(two_packages));
exp = two_packages / 2;
EXPECT_EQ(exp, q->GetFillMass(two_packages));
}

TEST(PackageTests, CreateTransportUnit) {

std::string p_exp_name = "foo";
int exp_min = 0.1;
int exp_max = 0.9;
std::string exp_strat = "first";

TransportUnit::Ptr p = TransportUnit::Create(p_exp_name, exp_min, exp_max,
exp_strat);

EXPECT_EQ(p_exp_name, p->name());
EXPECT_EQ(exp_min, p->fill_min());
EXPECT_EQ(exp_max, p->fill_max());
EXPECT_EQ(exp_strat, p->strategy());

EXPECT_NE(TransportUnit::unrestricted_name(), p->name());

std::string q_exp_name = "bar";
TransportUnit::Ptr q = TransportUnit::Create(q_exp_name, exp_min, exp_max,
exp_strat);
EXPECT_NE(TransportUnit::unrestricted_name(), q->name());
EXPECT_NE(q->name(), p->name());
}

TEST(PackageTests, Unrestricted) {
EXPECT_EQ("unrestricted", TransportUnit::unrestricted_name());
}

TEST(PackageTests, InvalidTransportUnit) {
// can't create package with name "unrestricted"
EXPECT_THROW(TransportUnit::Create("unrestricted", 0, 1, "first"),
cyclus::ValueError);
// can't have negative min/max
EXPECT_THROW(TransportUnit::Create("foo", -1, 1, "first"),
cyclus::ValueError);
EXPECT_THROW(TransportUnit::Create("foo", 0, -1, "first"),
cyclus::ValueError);
// can't have min bigger than max
EXPECT_THROW(TransportUnit::Create("foo", 100, 1, "first"),
cyclus::ValueError);
}

TEST(PackageTests, GetTransportUnitFillMass) {
int min = 3;
int max = 9;
int tight_min = 8;

TransportUnit::Ptr p = TransportUnit::Create("foo", min, max, "first");
TransportUnit::Ptr q = TransportUnit::Create("bar", min, max, "equal");
TransportUnit::Ptr r = TransportUnit::Create("baz", tight_min, max, "equal");

double exp;

int no_fit = 1;
EXPECT_EQ(0, p->GetTransportUnitFill(no_fit));
EXPECT_EQ(0, q->GetTransportUnitFill(no_fit));
EXPECT_EQ(0, r->GetTransportUnitFill(no_fit));

int perfect_fit = 9;
EXPECT_EQ(perfect_fit, p->GetTransportUnitFill(perfect_fit));
EXPECT_EQ(perfect_fit, q->GetTransportUnitFill(perfect_fit));
EXPECT_EQ(perfect_fit, r->GetTransportUnitFill(perfect_fit));

int two_full_packages = 18;
EXPECT_EQ(max, p->GetTransportUnitFill(perfect_fit));
EXPECT_EQ(max, q->GetTransportUnitFill(perfect_fit));
EXPECT_EQ(max, r->GetTransportUnitFill(perfect_fit));

int partial_fit = 11;
EXPECT_EQ(max, p->GetTransportUnitFill(partial_fit));
exp = std::floor(partial_fit / 2);
EXPECT_EQ(exp, q->GetTransportUnitFill(partial_fit));
EXPECT_EQ(max, r->GetTransportUnitFill(partial_fit));

int two_partial_packages = 17;
EXPECT_EQ(max, p->GetTransportUnitFill(two_partial_packages));
exp = std::floor(two_partial_packages / 2);
EXPECT_EQ(exp, q->GetTransportUnitFill(two_partial_packages));
}

TEST(PackageTests, MaxShippablePackages) {
int pq_min = 3;
int pq_max = 4;
int r_min = 6;
int r_max = 8;
TransportUnit::Ptr p = TransportUnit::Create("foo", pq_min, pq_max, "first");
TransportUnit::Ptr q = TransportUnit::Create("bar", pq_min, pq_max, "equal");
TransportUnit::Ptr r = TransportUnit::Create("baz", r_min, r_max, "equal");

int exp;

int none_fit = 2;
EXPECT_EQ(0, p->MaxShippablePackages(none_fit));
EXPECT_EQ(0, q->MaxShippablePackages(none_fit));
EXPECT_EQ(0, r->MaxShippablePackages(none_fit));

int all_fit = 8;
EXPECT_EQ(all_fit, p->MaxShippablePackages(all_fit));
EXPECT_EQ(all_fit, q->MaxShippablePackages(all_fit));
EXPECT_EQ(all_fit, r->MaxShippablePackages(all_fit));

// all can ship for p/q, but will go 4-3-3, only 8 ship for r
int partial = 10;
EXPECT_EQ(pq_max * 2, p->MaxShippablePackages(partial));
EXPECT_EQ(partial, q->MaxShippablePackages(partial));
EXPECT_EQ(r_max, r->MaxShippablePackages(partial));

int partial2 = 14;
EXPECT_EQ(pq_max * 3, p->MaxShippablePackages(partial2));
EXPECT_EQ(partial2, q->MaxShippablePackages(partial2));
EXPECT_EQ(partial2, r->MaxShippablePackages(partial2));

}

0 comments on commit c46afec

Please sign in to comment.