Skip to content

Commit

Permalink
Merge remote-tracking branch 'rwc/discharge' into v1.3.0-release
Browse files Browse the repository at this point in the history
Merging @rwcarlsen's reactor retirement branch
  • Loading branch information
gidden committed May 22, 2015
2 parents abf3fd3 + 67607b9 commit 3f38e3e
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 15 deletions.
83 changes: 69 additions & 14 deletions src/reactor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ void Reactor::InitFrom(cyclus::QueryableBackend* b) {

void Reactor::EnterNotify() {
cyclus::Facility::EnterNotify();

// If the user ommitted fuel_prefs, we set it to zeros for each fuel
// type. Without this segfaults could occur - yuck.
if (fuel_prefs.size() == 0) {
Expand Down Expand Up @@ -98,17 +98,43 @@ void Reactor::EnterNotify() {
}
}

bool Reactor::CheckDecommissionCondition() {
return core.count() == 0 && spent.count() == 0;
}

void Reactor::Tick() {
// The following code must go in the Tick so they fire on the time step
// following the cycle_step update - allowing for the all reactor events to
// occur and be recorded on the "beginning" of a time step. Another reason
// they
// can't go at the beginnin of the Tock is so that resource exchange has a
// chance to occur after the discharge on this same time step.

if (retired()) {
Record("RETIRED", "");

if (context()->time() == exit_time()) { // only need to transmute once
Transmute(ceil(static_cast<double>(n_assem_core) / 2.0));
}
while (core.count() > 0) {
if (!Discharge()) {
break;
}
}
// in case a cycle lands exactly on our last time step, we will need to
// burn a batch from fresh inventory on this time step. When retired,
// this batch also needs to be discharged to spent fuel inventory.
while (fresh.count() > 0 && spent.space() >= assem_size) {
spent.Push(fresh.Pop());
}
return;
}

if (cycle_step == cycle_time) {
Transmute();
Record("CYCLE_END", "");
}

if (cycle_step >= cycle_time && !discharged) {
discharged = Discharge();
}
Expand Down Expand Up @@ -158,10 +184,26 @@ std::set<cyclus::RequestPortfolio<Material>::Ptr> Reactor::GetMatlRequests() {
std::set<RequestPortfolio<Material>::Ptr> ports;
Material::Ptr m;

int n_assem_order =
n_assem_core - core.count() + n_assem_fresh - fresh.count();
// second min expression reduces assembles to amount needed until
// retirement if it is near.
int n_assem_order = n_assem_core - core.count() + n_assem_fresh - fresh.count();

if (exit_time() != -1) {
// the +1 accounts for the fact that the reactor is alive and gets to
// operate during its exit_time time step.
int t_left = exit_time() - context()->time() + 1;
int t_left_cycle = cycle_time + refuel_time - cycle_step;
double n_cycles_left = static_cast<double>(t_left - t_left_cycle) /
static_cast<double>(cycle_time + refuel_time);
n_cycles_left = ceil(n_cycles_left);
int n_need = std::max(0.0, n_cycles_left * n_assem_batch - n_assem_fresh);
n_assem_order = std::min(n_assem_order, n_need);
}

if (n_assem_order == 0) {
return ports;
} else if (retired()) {
return ports;
}

for (int i = 0; i < n_assem_order; i++) {
Expand Down Expand Up @@ -189,7 +231,6 @@ void Reactor::GetMatlTrades(
using cyclus::Trade;

std::map<std::string, MatVec> mats = PopSpent();
std::vector<cyclus::Trade<cyclus::Material> >::const_iterator it;
for (int i = 0; i < trades.size(); i++) {
std::string commod = trades[i].request->commodity();
Material::Ptr m = mats[commod].back();
Expand Down Expand Up @@ -233,8 +274,16 @@ std::set<cyclus::BidPortfolio<Material>::Ptr> Reactor::GetMatlBids(

bool gotmats = false;
std::map<std::string, MatVec> all_mats;
for (int i = 0; i < fuel_outcommods.size(); i++) {
std::string commod = fuel_outcommods[i];

if (uniq_outcommods_.empty()) {
for (int i = 0; i < fuel_outcommods.size(); i++) {
uniq_outcommods_.insert(fuel_outcommods[i]);
}
}

std::set<std::string>::iterator it;
for (it = uniq_outcommods_.begin(); it != uniq_outcommods_.end(); ++it) {
std::string commod = *it;
std::vector<Request<Material>*>& reqs = commod_requests[commod];
if (reqs.size() == 0) {
continue;
Expand Down Expand Up @@ -275,6 +324,10 @@ std::set<cyclus::BidPortfolio<Material>::Ptr> Reactor::GetMatlBids(
}

void Reactor::Tock() {
if (retired()) {
return;
}

if (cycle_step >= cycle_time + refuel_time && core.count() == n_assem_core) {
discharged = false;
cycle_step = 0;
Expand All @@ -298,12 +351,15 @@ void Reactor::Tock() {
}
}

void Reactor::Transmute() {
// safe to assume full core.
MatVec old = core.PopN(n_assem_batch);
MatVec tail = core.PopN(core.count());
void Reactor::Transmute() { Transmute(n_assem_batch); }

void Reactor::Transmute(int n_assem) {
MatVec old = core.PopN(std::min(n_assem, core.count()));
core.Push(old);
core.Push(tail);
if (core.count() > old.size()) {
// rotate untransmuted mats back to back of buffer
core.Push(core.PopN(core.count() - old.size()));
}

std::stringstream ss;
ss << old.size() << " assemblies";
Expand All @@ -326,13 +382,12 @@ std::map<std::string, MatVec> Reactor::PeekSpent() {
}

bool Reactor::Discharge() {
if (n_assem_spent - spent.count() < n_assem_batch) {
int npop = std::min(n_assem_batch, core.count());
if (n_assem_spent - spent.count() < npop) {
Record("DISCHARGE", "failed");
return false; // not enough room in spent buffer
}

int npop = std::min(n_assem_batch, core.count());

std::stringstream ss;
ss << npop << " assemblies";
Record("DISCHARGE", ss.str());
Expand Down
32 changes: 31 additions & 1 deletion src/reactor.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ namespace cycamore {
/// becomes full, the reactor will halt operation at the end of the next cycle
/// until there is more room. Each time step, the reactor will try to trade
/// away as much of its spent fuel inventory as possible.
///
/// When the reactor reaches the end of its lifetime, it will discharge all
/// material from its core and trade away all its spent fuel as quickly as
/// possible. Full decommissioning will be delayed until all spent fuel is
/// gone. If the reactor has a full core when it is decommissioned (i.e. is
/// mid-cycle) when the reactor is decommissioned, half (rounded up to nearest
/// int) of its assemblies are transmuted to their respective burnt
/// compositions.

class Reactor : public cyclus::Facility,
public cyclus::toolkit::CommodityProducer {
#pragma cyclus note { \
Expand Down Expand Up @@ -83,7 +92,16 @@ class Reactor : public cyclus::Facility,
" operational cycle before the next begins. If the spent fuel inventory" \
" becomes full, the reactor will halt operation at the end of the next cycle" \
" until there is more room. Each time step, the reactor will try to trade" \
" away as much of its spent fuel inventory as possible.", \
" away as much of its spent fuel inventory as possible." \
"\n\n" \
"When the reactor reaches the end of its lifetime, it will discharge all" \
" material from its core and trade away all its spent fuel as quickly as" \
" possible. Full decommissioning will be delayed until all spent fuel is" \
" gone. If the reactor has a full core when it is decommissioned (i.e. is" \
" mid-cycle) when the reactor is decommissioned, half (rounded up to nearest" \
" int) of its assemblies are transmuted to their respective burnt" \
" compositions." \
"", \
}

public:
Expand All @@ -93,6 +111,7 @@ class Reactor : public cyclus::Facility,
virtual void Tick();
virtual void Tock();
virtual void EnterNotify();
virtual bool CheckDecommissionCondition();

virtual void AcceptMatlTrades(const std::vector<std::pair<
cyclus::Trade<cyclus::Material>, cyclus::Material::Ptr> >& responses);
Expand All @@ -117,6 +136,10 @@ class Reactor : public cyclus::Facility,
std::string fuel_outrecipe(cyclus::Material::Ptr m);
double fuel_pref(cyclus::Material::Ptr m);

bool retired() {
return exit_time() != -1 && context()->time() >= exit_time();
}

/// Store fuel info index for the given resource received on incommod.
void index_res(cyclus::Resource::Ptr m, std::string incommod);

Expand All @@ -131,6 +154,10 @@ class Reactor : public cyclus::Facility,
/// fully burnt state as defined by its outrecipe.
void Transmute();

/// Transmute the specified number of assemblies in the core to their
/// fully burnt state as defined by their outrecipe.
void Transmute(int n_assem);

/// Records a reactor event to the output db with the given name and note val.
void Record(std::string name, std::string val);

Expand Down Expand Up @@ -335,6 +362,9 @@ class Reactor : public cyclus::Facility,
"internal": True \
}
std::map<int, int> res_indexes;

// populated lazily and no need to persist.
std::set<std::string> uniq_outcommods_;
};

} // namespace cycamore
Expand Down
46 changes: 46 additions & 0 deletions src/reactor_tests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,52 @@ TEST(ReactorTests, RecipeChange) {
EXPECT_TRUE(0 < mq.mass(id("H1")));
}

TEST(ReactorTests, Retire) {
std::string config =
" <fuel_inrecipes> <val>lwr_fresh</val> </fuel_inrecipes> "
" <fuel_outrecipes> <val>lwr_spent</val> </fuel_outrecipes> "
" <fuel_incommods> <val>enriched_u</val> </fuel_incommods> "
" <fuel_outcommods> <val>waste</val> </fuel_outcommods> "
""
" <cycle_time>7</cycle_time> "
" <refuel_time>0</refuel_time> "
" <assem_size>300</assem_size> "
" <n_assem_fresh>1</n_assem_fresh> "
" <n_assem_core>3</n_assem_core> "
" <n_assem_batch>1</n_assem_batch> "
"";

int dur = 50;
int life = 36;
cyclus::MockSim sim(cyclus::AgentSpec(":cycamore:Reactor"), config, dur, life);
sim.AddSource("enriched_u").Finalize();
sim.AddSink("waste").Finalize();
sim.AddRecipe("lwr_fresh", c_uox());
sim.AddRecipe("lwr_spent", c_spentuox());
int id = sim.Run();

int ncore = 3;
int nbatch = 1;

// reactor should stop requesting new fresh fuel as it approaches retirement
int nassem_recv =
static_cast<int>(ceil(static_cast<double>(life) / 7.0)) * nbatch +
(ncore - nbatch);

std::vector<Cond> conds;
conds.push_back(Cond("ReceiverId", "==", id));
QueryResult qr = sim.db().Query("Transactions", &conds);
EXPECT_EQ(nassem_recv, qr.rows.size())
<< "failed to stop ordering near retirement";

// reactor should discharge all fuel before/by retirement
conds.clear();
conds.push_back(Cond("SenderId", "==", id));
qr = sim.db().Query("Transactions", &conds);
EXPECT_EQ(nassem_recv, qr.rows.size())
<< "failed to discharge all material by retirement time";
}

} // namespace reactortests
} // namespace cycamore

0 comments on commit 3f38e3e

Please sign in to comment.