diff --git a/.gitignore b/.gitignore index 835e504e75..ad488f2da1 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ suffix.h *.pyc rs.cred *.h5 -tests/run_inputs.py \ No newline at end of file +*.dat +tests/run_inputs.py diff --git a/input/recycle.xml b/input/recycle.xml new file mode 100644 index 0000000000..406994c642 --- /dev/null +++ b/input/recycle.xml @@ -0,0 +1,183 @@ + + flat + + 600 + 1 + 2000 + + + + cycamore Source + cycamore Sink + cycamore Enrichment + cycamore Reactor + cycamore FuelFab + cycamore Separations + + + + enrichment + + + natl_u + natl_u + uox + 0.003 + waste + 1e100 + 1e100 + + + + + + separations + + + + sep_stream + + 1e100 + + Pu .99 + + + + + waste + 30001 + 30001 + spent_uox + 2.0 + + + + + + fuelfab + + + depleted_u + depleted_u + 30001 + + sep_stream + 15000 + + thermal + mox + 30001 + + + + + + reactor + + + fresh_uox fresh_mox + spent_uox spent_mox + uox mox + spent_uox waste + 1.0 2.0 + + 17 + 2 + 30000 + 3 + 1 + + + + + + repo + + + waste + 1e100 + + + + + + depleted_src + + + depleted_u + depleted_u + + + + + repo1 repo + r1 reactor + depleted1 depleted_src + fab1 fuelfab + sep1 separations + enrich1 enrichment + + + natl_u + mass + U235 0.711 + U238 99.289 + + + + fresh_uox + mass + U2350.04 + U2380.96 + + + + depleted_u + mass + U2350.003 + U2380.997 + + + + fresh_mox + mass + U235 0.0027381 + U238 0.9099619 + Pu238 0.001746 + Pu239 0.045396 + Pu240 0.020952 + Pu241 0.013095 + Pu242 0.005238 + + + + spent_mox + mass + U235 0.0017381 + U238 0.90 + Pu238 0.001746 + Pu239 0.0134 + Pu240 0.020952 + Pu241 0.013095 + Pu242 0.005238 + + + + spent_uox + mass + U235 156.729 + U236 102.103 + U238 18280.324 + Np237 13.656 + Pu238 5.043 + Pu239 106.343 + Pu240 41.357 + Pu241 36.477 + Pu242 15.387 + Am241 1.234 + Am243 3.607 + Cm244 0.431 + Cm245 1.263 + + + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5701534c73..6a29fcff22 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,6 +6,8 @@ USE_CYCLUS("cycamore" "fuel_fab") USE_CYCLUS("cycamore" "enrichment_facility") +USE_CYCLUS("cycamore" "separations") + USE_CYCLUS("cycamore" "sink") USE_CYCLUS("cycamore" "source") diff --git a/src/separations.cc b/src/separations.cc new file mode 100644 index 0000000000..a81e26e211 --- /dev/null +++ b/src/separations.cc @@ -0,0 +1,288 @@ +#include "separations.h" + +using cyclus::Material; +using cyclus::Composition; +using cyclus::toolkit::ResBuf; +using cyclus::toolkit::MatVec; +using cyclus::KeyError; +using cyclus::ValueError; +using cyclus::Request; +using cyclus::CompMap; + +namespace cycamore { + +Separations::Separations(cyclus::Context* ctx) : cyclus::Facility(ctx) { + cyclus::Warn( + "the Separations archetype " + "is experimental"); +} + +cyclus::Inventories Separations::SnapshotInv() { + cyclus::Inventories invs; + + // these inventory names are intentionally convoluted so as to not clash + // with the user-specified stream commods that are used as the separations + // streams inventory names. + invs["leftover-inv-name"] = leftover.PopNRes(leftover.count()); + leftover.Push(invs["leftover-inv-name"]); + invs["feed-inv-name"] = feed.PopNRes(feed.count()); + feed.Push(invs["feed-inv-name"]); + + std::map >::iterator it; + for (it = streambufs.begin(); it != streambufs.end(); ++it) { + invs[it->first] = it->second.PopNRes(it->second.count()); + it->second.Push(invs[it->first]); + } + + return invs; +} + +void Separations::InitInv(cyclus::Inventories& inv) { + leftover.Push(inv["leftover-inv-name"]); + feed.Push(inv["feed-inv-name"]); + + cyclus::Inventories::iterator it; + for (it = inv.begin(); it != inv.end(); ++it) { + streambufs[it->first].Push(it->second); + } +} + +typedef std::pair > Stream; +typedef std::map StreamSet; + +void Separations::EnterNotify() { + cyclus::Facility::EnterNotify(); + StreamSet::iterator it; + for (it = streams_.begin(); it != streams_.end(); ++it) { + std::string name = it->first; + Stream stream = it->second; + double cap = stream.first; + if (cap >= 0) { + streambufs[name].capacity(cap); + } + } + + if (feed_commod_prefs.size() == 0) { + for (int i = 0; i < feed_commods.size(); i++) { + feed_commod_prefs.push_back(0); + } + } +} + +void Separations::Tick() { + if (feed.count() == 0) { + return; + } + + Material::Ptr mat = feed.Pop(std::min(throughput, feed.quantity())); + double orig_qty = mat->quantity(); + + StreamSet::iterator it; + double maxfrac = 1; + std::map stagedsep; + for (it = streams_.begin(); it != streams_.end(); ++it) { + Stream info = it->second; + std::string name = it->first; + stagedsep[name] = SepMaterial(info.second, mat); + double frac = streambufs[name].space() / stagedsep[name]->quantity(); + if (frac < maxfrac) { + maxfrac = frac; + } + } + + std::map::iterator itf; + for (itf = stagedsep.begin(); itf != stagedsep.end(); ++itf) { + std::string name = itf->first; + Material::Ptr m = itf->second; + if (m->quantity() > 0) { + streambufs[name].Push( + mat->ExtractComp(m->quantity() * maxfrac, m->comp())); + } + } + + if (maxfrac == 1) { + if (mat->quantity() > 0) { + // unspecified separations fractions go to leftovers + leftover.Push(mat); + } + } else { // maxfrac is < 1 + // push back any leftover feed due to separated stream inv size constraints + feed.Push(mat->ExtractQty((1 - maxfrac) * orig_qty)); + if (mat->quantity() > 0) { + // unspecified separations fractions go to leftovers + leftover.Push(mat); + } + } +} + +// Note that this returns an untracked material that should just be used for +// its composition and qty - not in any real inventories, etc. +Material::Ptr SepMaterial(std::map effs, Material::Ptr mat) { + CompMap cm = mat->comp()->mass(); + cyclus::compmath::Normalize(&cm, mat->quantity()); + double tot_qty = 0; + CompMap sepcomp; + + CompMap::iterator it; + for (it = cm.begin(); it != cm.end(); ++it) { + int nuc = it->first; + int elem = (nuc / 10000000) * 10000000; + double eff = 0; + if (effs.count(nuc) > 0) { + eff = effs[nuc]; + } else if (effs.count(elem) > 0) { + eff = effs[elem]; + } else { + continue; + } + + double qty = it->second; + double sepqty = qty * eff; + sepcomp[nuc] = sepqty; + tot_qty += sepqty; + } + + Composition::Ptr c = Composition::CreateFromMass(sepcomp); + return Material::CreateUntracked(tot_qty, c); +}; + +std::set::Ptr> +Separations::GetMatlRequests() { + using cyclus::RequestPortfolio; + std::set::Ptr> ports; + bool exclusive = false; + + if (feed.space() > cyclus::eps()) { + RequestPortfolio::Ptr port(new RequestPortfolio()); + + Material::Ptr m = cyclus::NewBlankMaterial(feed.space()); + if (!feed_recipe.empty()) { + Composition::Ptr c = context()->GetRecipe(feed_recipe); + m = Material::CreateUntracked(feed.space(), c); + } + + std::vector*> reqs; + for (int i = 0; i < feed_commods.size(); i++) { + std::string commod = feed_commods[i]; + double pref = feed_commod_prefs[i]; + reqs.push_back(port->AddRequest(m, this, commod, pref, exclusive)); + } + port->AddMutualReqs(reqs); + ports.insert(port); + } + + return ports; +} + +void Separations::GetMatlTrades( + const std::vector >& trades, + std::vector, Material::Ptr> >& + responses) { + using cyclus::Trade; + + std::vector >::const_iterator it; + for (int i = 0; i < trades.size(); i++) { + std::string commod = trades[i].request->commodity(); + if (commod == leftover_commod) { + double amt = std::min(leftover.quantity(), trades[i].amt); + Material::Ptr m = leftover.Pop(amt); + responses.push_back(std::make_pair(trades[i], m)); + } else if (streambufs.count(commod) > 0) { + double amt = std::min(streambufs[commod].quantity(), trades[i].amt); + Material::Ptr m = streambufs[commod].Pop(amt); + responses.push_back(std::make_pair(trades[i], m)); + } else { + throw ValueError("invalid commodity " + commod + + " on trade matched to prototype " + prototype()); + } + } +} + +void Separations::AcceptMatlTrades(const std::vector< + std::pair, Material::Ptr> >& responses) { + std::vector, + cyclus::Material::Ptr> >::const_iterator trade; + + for (trade = responses.begin(); trade != responses.end(); ++trade) { + feed.Push(trade->second); + } +} + +std::set::Ptr> Separations::GetMatlBids( + cyclus::CommodMap::type& commod_requests) { + using cyclus::BidPortfolio; + + bool exclusive = false; + std::set::Ptr> ports; + + // bid streams + std::map >::iterator it; + for (it = streambufs.begin(); it != streambufs.end(); ++it) { + std::string commod = it->first; + std::vector*>& reqs = commod_requests[commod]; + if (reqs.size() == 0) { + continue; + } else if (streambufs[commod].quantity() < cyclus::eps()) { + continue; + } + + MatVec mats = streambufs[commod].PopN(streambufs[commod].count()); + streambufs[commod].Push(mats); + + BidPortfolio::Ptr port(new BidPortfolio()); + + for (int j = 0; j < reqs.size(); j++) { + Request* req = reqs[j]; + double tot_bid = 0; + for (int k = 0; k < mats.size(); k++) { + Material::Ptr m = mats[k]; + tot_bid += m->quantity(); + port->AddBid(req, m, this, exclusive); + if (tot_bid >= req->target()->quantity()) { + break; + } + } + } + + double tot_qty = streambufs[commod].quantity(); + cyclus::CapacityConstraint cc(tot_qty); + port->AddConstraint(cc); + ports.insert(port); + } + + // bid leftovers + std::vector*>& reqs = commod_requests[leftover_commod]; + if (reqs.size() > 0 && leftover.quantity() >= cyclus::eps()) { + MatVec mats = leftover.PopN(leftover.count()); + leftover.Push(mats); + + BidPortfolio::Ptr port(new BidPortfolio()); + + for (int j = 0; j < reqs.size(); j++) { + Request* req = reqs[j]; + double tot_bid = 0; + for (int k = 0; k < mats.size(); k++) { + Material::Ptr m = mats[k]; + tot_bid += m->quantity(); + port->AddBid(req, m, this, exclusive); + if (tot_bid >= req->target()->quantity()) { + break; + } + } + } + + cyclus::CapacityConstraint cc(leftover.quantity()); + port->AddConstraint(cc); + ports.insert(port); + } + + return ports; +} + +void Separations::Tock() {} + +extern "C" cyclus::Agent* ConstructSeparations(cyclus::Context* ctx) { + return new Separations(ctx); +} + +} // namespace cycamore diff --git a/src/separations.h b/src/separations.h new file mode 100644 index 0000000000..da118ad6d4 --- /dev/null +++ b/src/separations.h @@ -0,0 +1,182 @@ +#ifndef CYCAMORE_SRC_SEPARATIONS_H_ +#define CYCAMORE_SRC_SEPARATIONS_H_ + +#include "cyclus.h" + +namespace cycamore { + +/// SepMaterial returns a material object that represents the composition and +/// quantity resulting from the separation of material from mat using the given +/// mass-based efficiencies. Each key in effs represents a nuclide or element +/// (canonical PyNE form), and each value is the corresponding mass-based +/// separations efficiency for that nuclide or element. Note that this returns +/// an untracked material that should only be used for its composition and qty +/// - not in any real inventories, etc. +cyclus::Material::Ptr SepMaterial(std::map effs, + cyclus::Material::Ptr mat); + +/// Separations processes feed material into one or more streams containing +/// specific elements and/or nuclides. It uses mass-based efficiencies. +/// +/// User defined separations streams are specified as groups of +/// component-efficiency pairs where 'component' means either a particular +/// element or a particular nuclide. Each component's paired efficiency +/// represents the mass fraction of that component in the feed that is +/// separated into that stream. The efficiencies of a particular component +/// across all streams must sum up to less than or equal to one. If less than +/// one, the remainining material is sent to a waste inventory and +/// (potentially) traded away from there. +/// +/// The facility receives material into a feed inventory that it processes with +/// a specified throughput each time step. Each output stream has a +/// corresponding output inventory size/limit. If the facility is unable to +/// reduce its stocks by trading and hits this limit for any of its output +/// streams, further processing/separations of feed material will halt until +/// room is again available in the output streams. +class Separations : public cyclus::Facility { +#pragma cyclus note { \ + "doc": \ + "Separations processes feed material into one or more streams containing" \ + " specific elements and/or nuclides. It uses mass-based efficiencies." \ + "\n\n" \ + "User defined separations streams are specified as groups of" \ + " component-efficiency pairs where 'component' means either a particular" \ + " element or a particular nuclide. Each component's paired efficiency" \ + " represents the mass fraction of that component in the feed that is" \ + " separated into that stream. The efficiencies of a particular component" \ + " across all streams must sum up to less than or equal to one. If less than" \ + " one, the remainining material is sent to a waste inventory and" \ + " (potentially) traded away from there." \ + "\n\n" \ + "The facility receives material into a feed inventory that it processes with" \ + " a specified throughput each time step. Each output stream has a" \ + " corresponding output inventory size/limit. If the facility is unable to" \ + " reduce its stocks by trading and hits this limit for any of its output" \ + " streams, further processing/separations of feed material will halt until" \ + " room is again available in the output streams." \ + "", \ +} + public: + Separations(cyclus::Context* ctx); + virtual ~Separations(){}; + + virtual void Tick(); + virtual void Tock(); + virtual void EnterNotify(); + + virtual void AcceptMatlTrades(const std::vector, cyclus::Material::Ptr> >& responses); + + virtual std::set::Ptr> + GetMatlRequests(); + + virtual std::set::Ptr> GetMatlBids( + cyclus::CommodMap::type& commod_requests); + + virtual void GetMatlTrades( + const std::vector >& trades, + std::vector, + cyclus::Material::Ptr> >& responses); + + #pragma cyclus clone + #pragma cyclus initfromcopy + #pragma cyclus infiletodb + #pragma cyclus initfromdb + #pragma cyclus schema + #pragma cyclus annotations + #pragma cyclus snapshot + // the following pragmas are ommitted and the functions are written + // manually in order to handle the vector of resource buffers: + // + // #pragma cyclus snapshotinv + // #pragma cyclus initinv + + virtual cyclus::Inventories SnapshotInv(); + virtual void InitInv(cyclus::Inventories& inv); + + private: + #pragma cyclus var { \ + "doc" : "Maximum quantity of feed material that can be processed per time step.", \ + "units": "kg", \ + } + double throughput; + + #pragma cyclus var { \ + "doc": "Ordered list of commodities on which to request feed material to separate." \ + " Order only matters for matching up with feed commodity preferences if specified.", \ + "uitype": ["oneormore", "incommodity"], \ + } + std::vector feed_commods; + + #pragma cyclus var { \ + "default": [], \ + "doc": "Feed commodity request preferences for each of the given feed commodities (same order)." \ + " If unspecified, default is to use zero for all preferences.", \ + } + std::vector feed_commod_prefs; + + #pragma cyclus var { \ + "doc": "Name for recipe to be used in feed requests." \ + " Empty string results in use of a dummy recipe.", \ + "uitype": "recipe", \ + "default": "", \ + } + std::string feed_recipe; + + #pragma cyclus var { \ + "doc" : "Maximum amount of feed material to keep on hand.", \ + "units" : "kg", \ + } + double feedbuf_size; + + #pragma cyclus var { \ + "capacity" : "feedbuf_size", \ + } + cyclus::toolkit::ResBuf feed; + + #pragma cyclus var { \ + "doc" : "Maximum amount of leftover separated material (not included in" \ + " any other stream) that can be stored." \ + " If full, the facility halts operation until space becomes available.", \ + "default": 1e299, \ + "units": "kg", \ + } + double leftoverbuf_size; + + #pragma cyclus var { \ + "doc": "Commodity on which to trade the leftover separated material stream." \ + " This MUST NOT be the same as any commodity used to define the other separations streams.", \ + "uitype": "outcommodity", \ + "default": "default-waste-stream", \ + } + std::string leftover_commod; + + #pragma cyclus var { \ + "capacity" : "leftoverbuf_size", \ + } + cyclus::toolkit::ResBuf leftover; + + #pragma cyclus var { \ + "alias": ["streams", "commod", ["info", "buf_size", ["efficiencies", "comp", "eff"]]], \ + "uitype": ["oneormore", "outcommodity", ["pair", "double", ["oneormore", "nuclide", "double"]]], \ + "doc": "Output streams for separations." \ + " Each stream must have a unique name identifying the commodity on which its material is traded," \ + " a max buffer capacity in kg (neg values indicate infinite size)," \ + " and a set of component efficiencies." \ + " 'comp' is a component to be separated into the stream" \ + " (e.g. U, Pu, etc.) and 'eff' is the mass fraction of the component" \ + " that is separated from the feed into this output stream." \ + " If any stream buffer is full, the facility halts operation until space becomes available." \ + " The sum total of all component efficiencies across streams must be less than or equal to 1" \ + " (e.g. sum of U efficiencies for all streams must be <= 1).", \ + } + std::map > > streams_; + + // custom SnapshotInv and InitInv and EnterNotify are used to persist this + // state var. + std::map > streambufs; +}; + +} // namespace cycamore + +#endif // CYCAMORE_SRC_SEPARATIONS_H_ diff --git a/src/separations_tests.cc b/src/separations_tests.cc new file mode 100644 index 0000000000..0fb1b42c60 --- /dev/null +++ b/src/separations_tests.cc @@ -0,0 +1,90 @@ +#include "separations.h" + +#include +#include +#include "cyclus.h" + +using pyne::nucname::id; +using cyclus::Composition; +using cyclus::CompMap; +using cyclus::Material; +using cyclus::QueryResult; +using cyclus::Cond; +using cyclus::toolkit::MatQuery; + +namespace cycamore { + +TEST(SeparationsTests, SepMaterial) { + CompMap comp; + comp[id("U235")] = 10; + comp[id("U238")] = 90; + comp[id("Pu239")] = 1; + comp[id("Pu240")] = 2; + comp[id("Am241")] = 3; + comp[id("Am242")] = 2.8; + double qty = 100; + Composition::Ptr c = Composition::CreateFromMass(comp); + Material::Ptr mat = Material::CreateUntracked(qty, c); + + std::map effs; + effs[id("U")] = .7; + effs[id("Pu")] = .4; + effs[id("Am241")] = .4; + + Material::Ptr sep = SepMaterial(effs, mat); + MatQuery mqorig(mat); + MatQuery mqsep(sep); + + EXPECT_DOUBLE_EQ(effs[id("U")] * mqorig.mass("U235"), mqsep.mass("U235")); + EXPECT_DOUBLE_EQ(effs[id("U")] * mqorig.mass("U238"), mqsep.mass("U238")); + EXPECT_DOUBLE_EQ(effs[id("Pu")] * mqorig.mass("Pu239"), mqsep.mass("Pu239")); + EXPECT_DOUBLE_EQ(effs[id("Pu")] * mqorig.mass("Pu240"), mqsep.mass("Pu240")); + EXPECT_DOUBLE_EQ(effs[id("Am241")] * mqorig.mass("Am241"), mqsep.mass("Am241")); + EXPECT_DOUBLE_EQ(0, mqsep.mass("Am242")); +} + +TEST(SeparationsTests, SepMixElemAndNuclide) { + std::string config = + "" + " stream1" + " " + " -1" + " " + " U 0.6" + " Pu239 .7" + " " + " " + "" + "" + "waste" + "100" + "100" + " feed " + ; + + CompMap m; + m[id("u235")] = 0.08; + m[id("u238")] = 0.9; + m[id("Pu239")] = .01; + m[id("Pu240")] = .01; + Composition::Ptr c = Composition::CreateFromMass(m); + + int simdur = 2; + cyclus::MockSim sim(cyclus::AgentSpec(":cycamore:Separations"), config, simdur); + sim.AddSource("feed").recipe("recipe1").Finalize(); + sim.AddSink("stream1").capacity(100).Finalize(); + sim.AddRecipe("recipe1", c); + int id = sim.Run(); + + std::vector conds; + conds.push_back(Cond("SenderId", "==", id)); + int resid = sim.db().Query("Transactions", &conds).GetVal("ResourceId"); + MatQuery mq (sim.GetMaterial(resid)); + EXPECT_DOUBLE_EQ(m[922350000]*0.6*100, mq.mass("U235")); + EXPECT_DOUBLE_EQ(m[922380000]*0.6*100, mq.mass("U238")); + EXPECT_DOUBLE_EQ(m[942390000]*0.7*100, mq.mass("Pu239")); + EXPECT_DOUBLE_EQ(0, mq.mass("Pu240")); +} + +} // namespace cycamore + diff --git a/tests/test_regression.py b/tests/test_regression.py index 9c315b3a9f..449f7d9cd9 100644 --- a/tests/test_regression.py +++ b/tests/test_regression.py @@ -375,3 +375,93 @@ def test_deployment(self): assert_equal(depl_time[np.where(agent_ids == source1_id[0])], 2) assert_equal(depl_time[np.where(agent_ids == source1_id[1])], 3) +class TestRecycle(TestRegression): + """This class tests the input/recycle.xml file. + """ + def __init__(self, *args, **kwargs): + super(TestRecycle, self).__init__(*args, **kwargs) + + # this test requires separations which isn't supported by hdf5 + # so we force sqlite: + base, _ = os.path.splitext(self.outf) + self.ext = '.sqlite' + self.outf = base + self.ext + self.inf = "../input/recycle.xml" + self.sql = """ + SELECT t.time as time,SUM(c.massfrac*r.quantity) as qty FROM transactions as t + JOIN resources as r ON t.resourceid=r.resourceid AND r.simid=t.simid + JOIN agententry as send ON t.senderid=send.agentid AND send.simid=t.simid + JOIN agententry as recv ON t.receiverid=recv.agentid AND recv.simid=t.simid + JOIN compositions as c ON c.qualid=r.qualid AND c.simid=r.simid + WHERE send.prototype=? AND recv.prototype=? AND c.nucid=? + GROUP BY t.time;""" + + def do_compare(self, fromfac, tofac, nuclide, exp_invs): + conn = sqlite3.connect(self.outf) + c = conn.cursor() + eps = 1e-10 + simdur = len(exp_invs) + + invs = [0.0] * simdur + for i, row in enumerate(c.execute(self.sql, (fromfac, tofac, nuclide))): + t = row[0] + invs[t] = row[1] + + expfname = 'exp_recycle_{0}-{1}-{2}.dat'.format(fromfac, tofac, nuclide) + with open(expfname, 'w') as f: + for t, val in enumerate(exp_invs): + f.write('{0} {1}\n'.format(t, val)) + obsfname = 'obs_recycle_{0}-{1}-{2}.dat'.format(fromfac, tofac, nuclide) + with open(obsfname, 'w') as f: + for t, val in enumerate(invs): + f.write('{0} {1}\n'.format(t, val)) + + i = 0 + for exp, obs in zip(invs, exp_invs): + i += 1 + self.assertAlmostEquals(exp, obs, msg='mismatch at t={0}'.format(i)) + + os.remove(expfname) + os.remove(obsfname) + + def test_pu239_sep_repo(self): + simdur = 600 + exp = [0.0] * simdur + exp[18] = 1.700222672 + exp[37] = 1.700222672 + exp[56] = 1.700222672 + exp[75] = 1.700222672 + exp[94] = 1.700222672 + exp[113] = 1.700222672 + exp[132] = 1.700222672 + exp[151] = 1.700222672 + exp[170] = 1.700222672 + exp[189] = 1.700222672 + exp[208] = 1.700222672 + exp[227] = 1.700222672 + exp[246] = 1.700222672 + exp[284] = 1.700222672 + exp[303] = 1.700222672 + exp[322] = 1.700222672 + exp[341] = 1.700222672 + exp[360] = 1.700222672 + exp[379] = 1.700222672 + exp[398] = 1.700222672 + exp[417] = 1.700222672 + exp[436] = 1.700222672 + exp[474] = 1.700222672 + exp[493] = 1.700222672 + exp[512] = 1.700222672 + exp[531] = 1.700222672 + exp[550] = 1.700222672 + exp[569] = 1.700222672 + exp[588] = 1.700222672 + self.do_compare('separations', 'repo', 942390000, exp) + + def test_pu239_reactor_repo(self): + simdur = 600 + exp = [0.0] * simdur + exp[264] = 420.42772559790944 + exp[454] = 420.42772559790944 + self.do_compare('reactor', 'repo', 942390000, exp) +