diff --git a/examples/complex.xml b/examples/complex.xml index b7c099b..f77c0a5 100644 --- a/examples/complex.xml +++ b/examples/complex.xml @@ -55,6 +55,7 @@ mox mox + 10 TwoSource diff --git a/examples/simple.xml b/examples/simple.xml index 03c4f0b..cff6d3a 100644 --- a/examples/simple.xml +++ b/examples/simple.xml @@ -14,10 +14,6 @@ cycamore Sink - - cycamore - Reactor - agents NullRegion @@ -74,25 +70,6 @@ - - - TwoReactor - - - uox - spent_uox - uox - spent_uox - 10 - 2 - 1 - 3 - 1 - 100 - - - - @@ -116,10 +93,6 @@ 1 OneReactor - OneInst diff --git a/openmcyclus/DepleteReactor.py b/openmcyclus/DepleteReactor.py index 2aa34ec..d998d1b 100644 --- a/openmcyclus/DepleteReactor.py +++ b/openmcyclus/DepleteReactor.py @@ -1,4 +1,4 @@ -from cyclus.agents import Facility +from cyclus.agents import Facility from cyclus import lib import cyclus.typesystem as ts import math @@ -8,13 +8,12 @@ import openmc.deplete as od - class DepleteReactor(Facility): ''' - Archetype class to model a reactor facility that is + Archetype class to model a reactor facility that is coupled to the stand alone depletion solver in OpenMC. - With the exception of the depletion solver, this - archetype has the same functionality as the + With the exception of the depletion solver, this + archetype has the same functionality as the cycamore:Reactor archetype. ''' @@ -25,16 +24,16 @@ class DepleteReactor(Facility): ) fuel_inrecipes = ts.VectorString( - doc = "Fresh fuel recipe", - tooltip = "Fresh fuel recipe", - uilabel = "Input commodity recipe" + doc="Fresh fuel recipe", + tooltip="Fresh fuel recipe", + uilabel="Input commodity recipe" ) - + fuel_prefs = ts.VectorDouble( - doc = "Fuel incommod preference", - tooltip = "Fuel incommod preference", - uilabel = "Fuel incommod preference", - default = [] + doc="Fuel incommod preference", + tooltip="Fuel incommod preference", + uilabel="Fuel incommod preference", + default=[] ) fuel_outcommods = ts.VectorString( @@ -45,91 +44,90 @@ class DepleteReactor(Facility): ) fuel_outrecipes = ts.VectorString( - doc = "Spent fuel recipe", - tooltip = "Spent fuel recipe", - uilabel = "Output commodity recipe" + doc="Spent fuel recipe", + tooltip="Spent fuel recipe", + uilabel="Output commodity recipe" ) assem_size = ts.Double( - doc = "Mass (kg) of a single fuel assembly", + doc="Mass (kg) of a single fuel assembly", tooltip="Mass (kg) of a single fuel assembly", uilabel="Assembly Size", units="kg", - default= 0 + default=0 ) cycle_time = ts.Double( doc="Amount of time between requests for new fuel", - tooltip = "Amount of time between requests for new fuel", + tooltip="Amount of time between requests for new fuel", uilabel="Cycle Time", units="months", default=0 ) refuel_time = ts.Int( - doc = "Time steps for refueling", + doc="Time steps for refueling", tooltip="Time steps for refueling", uilabel="refueltime", - default = 0 + default=0 ) n_assem_core = ts.Int( - doc = "Number of assemblies in a core", - tooltip = "Number of assemblies in a core", - uilabel = "n_assem_core", + doc="Number of assemblies in a core", + tooltip="Number of assemblies in a core", + uilabel="n_assem_core", default=0 ) - n_assem_batch = ts.Int( - doc = "Number of assemblies per batch", - tooltip = "Number of assemblies per batch", - uilabel = "n_assem_batch", + n_assem_batch = ts.Int( + doc="Number of assemblies per batch", + tooltip="Number of assemblies per batch", + uilabel="n_assem_batch", default=0 ) n_assem_fresh = ts.Int( - doc = "Number of fresh fuel assemblies to keep on hand "\ - "if possible", - default = 0, - range = [0,3], + doc="Number of fresh fuel assemblies to keep on hand " + "if possible", + default=0, + range=[0, 3], uilabel="Minimum fresh fuel inventory", - units = "assemblies" + units="assemblies" ) n_assem_spent = ts.Int( - doc = "Number of spent fuel assemblies that can be stored "\ - "on-site before reactor operation stalls", - default = 10000000, + doc="Number of spent fuel assemblies that can be stored " + "on-site before reactor operation stalls", + default=10000000, uilabel="Maximum spent fuel inventory", - units = "assemblies" + units="assemblies" ) power_cap = ts.Double( - doc = "Maximum amount of power (MWe) produced", - tooltip = "Maximum amount of power (MWe) produced", - uilabel = "power_cap", - units = "MW", + doc="Maximum amount of power (MWe) produced", + tooltip="Maximum amount of power (MWe) produced", + uilabel="power_cap", + units="MW", default=0 ) decom_transmute_all = ts.String( - doc = "If true, the archetype transmutes all assemblies "\ - "upon decommisioning. If false, the archetype only "\ - "transmutes half.", - default = 0 + doc="If true, the archetype transmutes all assemblies " + "upon decommisioning. If false, the archetype only " + "transmutes half.", + default=0 ) model_path = ts.String( - doc = "Path to files with the OpenMC model information", - tooltip = "Path to files with OpenMC model", - default = "/home/abachmann/openmcyclus/tests/" + doc="Path to files with the OpenMC model information", + tooltip="Path to files with OpenMC model", + default="/home/abachmann/openmcyclus/tests/" ) chain_file = ts.String( - doc = "File with OpenMC decay chain information", - tooltip = "Absolute path to decay chain file" + doc="File with OpenMC decay chain information", + tooltip="Absolute path to decay chain file" ) - fresh_fuel = ts.ResBufMaterialInv() core = ts.ResBufMaterialInv() spent_fuel = ts.ResBufMaterialInv() @@ -138,208 +136,244 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fresh_fuel_entry_times = [] self.core_entry_times = [] - self.fresh_fuel.capacity = self.assem_size*self.n_assem_fresh - self.core.capacity = self.assem_size*self.n_assem_core - self.spent_fuel.capacity = self.assem_size*self.n_assem_spent + self.fresh_fuel.capacity = self.assem_size * self.n_assem_fresh + self.core.capacity = self.assem_size * self.n_assem_core + self.spent_fuel.capacity = self.assem_size * self.n_assem_spent self.cycle_step = 0 self.power_name = "power" self.discharged = False self.resource_indexes = {} - self.deplete = Depletion(self.model_path, - self.prototype, self.chain_file, + self.deplete = Depletion(self.model_path, + self.prototype, self.chain_file, self.cycle_time, self.power_cap) def tick(self): ''' - Logic to implement at the tick phase of each - time step. - - If the prototype is retired, then that is recorded, - fuel is transmuted, and the prototype is decommissioned. + Logic to implement at the tick phase of each + time step. - If it's the end of a cycle, that is recorded. If it is - after a cycle ends, and fuel has not been discharged, - then the fuel is discharged. If it's after a cycle ends, then + If the prototype is retired, then that is recorded, + fuel is transmuted, and the prototype is decommissioned. + + If it's the end of a cycle, that is recorded. If it is + after a cycle ends, and fuel has not been discharged, + then the fuel is discharged. If it's after a cycle ends, then fuel is loaded ''' - print("time:", self.context.time, "tick") if self.retired(): - print("time:", self.context.time, "retired") - # self.record("RETIRED", "") + # self.record("RETIRED", "") if self.context.time == self.exit_time + 1: if self.decom_transmute_all == 1: self.transmute(math.ceil(self.n_assem_core)) else: - self.transmute(math.ceil(self.n_assem_core/2)) + self.transmute(math.ceil(self.n_assem_core / 2)) while self.core.count > 0: if self.discharge() == False: break - print("time:", self.context.time, "end discharge loop") - while (self.fresh_fuel.count > 0) and (self.spent_fuel.space >= self.assem_size): + while ( + self.fresh_fuel.count > 0) and ( + self.spent_fuel.space >= self.assem_size): self.spent_fuel.push(self.fresh_fuel.pop()) - print("time:", self.context.time, "decommission?", self.check_decommission_condition()) if self.check_decommission_condition(): self.decommission() - - print("time:", self.context.time, "end retired loop") - if self.cycle_step == self.cycle_time: - print("time:", self.context.time, "transmute", math.ceil(self.n_assem_batch)) + if self.cycle_step == self.cycle_time: self.transmute(math.ceil(self.n_assem_batch)) if (self.cycle_step >= self.cycle_time) and (self.discharged == False): self.discharged = self.discharge() if self.cycle_step >= self.cycle_time: - print("time:", self.context.time, "load") - print("time:", self.context.time, self.core.count) - print("time:", self.context.time, self.fresh_fuel.count) self.load() - #lib.Logger('5', str("DepleteReactor" + str(self.power_cap) + "is ticking")) - print("time:", self.context.time, "end tick") + # lib.Logger('5', str("DepleteReactor" + str(self.power_cap) + "is ticking")) return - + def tock(self): ''' - Logic to implement at the tock phase of each - time step. + Logic to implement at the tock phase of each + time step. - If the prototype is retired, then nothing happens. + If the prototype is retired, then nothing happens. If it's after a cycle ends and the refueling time has passed, - the core is full, and fuel has been discharged, then - the discharged variable is changed to false and the + the core is full, and fuel has been discharged, then + the discharged variable is changed to false and the cycle length counter is restarted. - If it's the beginning of a new cycle and the core is full, - then a cycle start is recorded. + If it's the beginning of a new cycle and the core is full, + then a cycle start is recorded. - If it's in the middle of a cycle and the core is full, the - the power_cap value is recorded as power generated. If these - conditions aren't met, then a power of 0 is recorded. + If it's in the middle of a cycle and the core is full, the + the power_cap value is recorded as power generated. If these + conditions aren't met, then a power of 0 is recorded. - If it's in the middle of a cycle or the core is full, then - the cycle duration counter increases by one. + If it's in the middle of a cycle or the core is full, then + the cycle duration counter increases by one. ''' - print("time:", self.context.time, "tock") if self.retired(): return - - if (self.cycle_step >= self.cycle_time+self.refuel_time) and (self.core.count == self.n_assem_core) and (self.discharged == True): + + if ( + self.cycle_step >= self.cycle_time + + self.refuel_time) and ( + self.core.count == self.n_assem_core) and ( + self.discharged): self.discharged = False self.cycle_step = 0 - + if (self.cycle_step == 0) and (self.core.count == self.n_assem_core): - #self.record("CYCLE_START", "") + # self.record("CYCLE_START", "") print("Cycle start") - if (self.cycle_step >=0) and (self.cycle_step < self.cycle_time) and (self.core.count == self.n_assem_core): + if (self.cycle_step >= 0) and (self.cycle_step < self.cycle_time) and ( + self.core.count == self.n_assem_core): lib.record_time_series(lib.POWER, self, self.power_cap) lib.record_time_series("supplyPOWER", self, int(self.power_cap)) - print("time:", self.context.time, "record 100 power") else: lib.record_time_series(lib.POWER, self, 0) lib.record_time_series("supplyPOWER", self, int(0)) - print("time:", self.context.time, "record 0 power") if (self.cycle_step > 0) or (self.core.count == self.n_assem_core): - self.cycle_step += 1 - print("time:", self.context.time, "end tock, cycle step:", self.cycle_step) - return + self.cycle_step += 1 + + return def enter_notify(self): - super().enter_notify() + ''' + Calls the enter_notify method of the parent class. + Also defines a list for the input commodity preferences if + not are provided by the user. + ''' + super().enter_notify() if len(self.fuel_prefs) == 0: - self.fuel_prefs = [0]*len(self.fuel_incommods) - + self.fuel_prefs = [1] * len(self.fuel_incommods) + def check_decommission_condition(self): ''' - If the core and the spent fuel are empty, then the core can be + If the core and the spent fuel are empty, then the core can be decommissioned. + + Returns: + -------- + Bool: True if conditions are met, otherwise False ''' - print("time:", self.context.time, "core count:", self.core.count) if (self.core.count == 0) and (self.spent_fuel.count == 0): return True else: return False - def get_material_requests(self): # phase 1 + def get_material_requests(self): # phase 1 ''' - Send out bid for fuel_incommods. + Send out bid for fuel_incommods. + + The number of assemblies to order is the total number needed + for the core less what is currently in the core plus + how many need to be in the fresh fuel storage less how many + exist there already. - The number of assemblies to order is the total number needed - for the core less what is currently in the core plus - how many need to be in the fresh fuel storage less how many - exist there already. + If the reactor has a finite lifetime, calculate the number of + cycles left and the number of assemblies needed for the + lifetime of the reactor. Order whichever number is + lower. - If the reactor has a finite lifetime, calculate the number of - cycles left and the number of assemblies needed for the - lifetime of the reactor. Order whichever number is - lower. - - If the reactor does not need more fuel or is retired, then + If the reactor does not need more fuel or is retired, then submit no bids for materials. - ''' - port = [] - n_assem_order = self.n_assem_core - self.core.count + self.n_assem_fresh + self.fresh_fuel.count - print("time:", self.context.time, "request", n_assem_order, "assemblies") + + Create a request portfolio for each assembly needed to be ordered. + Create an untracked material for each possible in commodity that + can be used to meet the demand of each assembly. Set up a mutual request + (logic OR) for each material that can meet a single assembly demand. + Apply the mass constraint of an assembly size for each assembly to + order. + + Returns: + -------- + ports: list of dictionaries + Format: [{"commodities": + [{commod_name(str):Material object, + "preference":int/float}, ...], + "constraints:int/float}, ...] + Defines the request portfolio for the facility. + ''' + ports = [] + n_assem_order = self.n_assem_core - self.core.count + \ + self.n_assem_fresh - self.fresh_fuel.count if self.exit_time != -1: time_left = self.exit_time - self.context.time + 1 time_left_cycle = self.cycle_time + self.refuel_time - self.cycle_step - n_cycles_left = (time_left - time_left_cycle)/(self.cycle_time + self.refuel_time) + n_cycles_left = (time_left - time_left_cycle) / \ + (self.cycle_time + self.refuel_time) n_cycles_left = math.ceil(n_cycles_left) - n_need = max(0, n_cycles_left*self.n_assem_batch - self.n_assem_fresh + self.n_assem_core - self.core.count) + n_need = max( + 0, + n_cycles_left * + self.n_assem_batch - + self.n_assem_fresh + + self.n_assem_core - + self.core.count) n_assem_order = min(n_assem_order, n_need) if n_assem_order == 0 or self.retired(): - return port + return ports - for ii in range(n_assem_order): + for ii in range(n_assem_order): + port = [] for jj in range(0, len(self.fuel_incommods)): commod = self.fuel_incommods[jj] pref = self.fuel_prefs[jj] - recipe = self.context.get_recipe(commod) - material = ts.Material.create_untracked(self.assem_size, recipe) - lib.record_time_series("demand"+commod, self, self.assem_size) - port.append({"commodities":{commod:material}, "constraints":self.assem_size}) - print("time:", self.context.time, "finish get_material_requests") - return port + recipe = self.context.get_recipe(self.fuel_inrecipes[jj]) + material = ts.Material.create_untracked( + self.assem_size, recipe) + port.append({commod: material, "preference": pref}) + lib.record_time_series( + "demand" + commod, self, self.assem_size) + ports.append({"commodities": port, "constraints": self.assem_size}) + return ports - def get_material_bids(self, requests): # phase 2 + def get_material_bids(self, requests): # phase 2 ''' Read bids for fuel_outcommods and return bid portfolio. - If the unique_commods_ string is empty, add the names of + If the unique_commods_ string is empty, add the names of the fuel out commodities. - For each of the items in the unique commodities list, - if there are no requests for a given commodity name, then - continue to the next commodity. Get the recipe for + For each of the items in the unique commodities list, + if there are no requests for a given commodity name, then + continue to the next commodity. Get the recipe for each commodity requested. - Looking at the composition for each commodity, - if there is no composition, then move to the next commodity. + Looking at the composition for each commodity, + if there is no composition, then move to the next commodity. - For each request of each commodity, sum the total request - for the commodity. Then create a bid for each request of + For each request of each commodity, sum the total request + for the commodity. Then create a bid for each request of each commodity. - For each material composition sum to total mass of + For each material composition sum to total mass of the composition. Add a constraint of the total quantity of the commodity available. - Create a bid portfolio for each request that can be met. + Create a bid portfolio for each request that can be met. + + Parameters: + ----------- + requests: dict + dictionary of requests, given by Cyclus, not to be defined by the user. + + Returns: + -------- + port: dict + dictionary of materials held by facility that meets a request + from another facility ''' - print("time:", self.context.time, "start get material_bids") got_mats = False bids = [] port = [] for commod_index, commod in enumerate(self.fuel_outcommods): reqs = requests[commod] - print("time:", self.context.time, "reqs:", reqs) if len(reqs) == 0: continue elif (got_mats == False): @@ -347,17 +381,17 @@ def get_material_bids(self, requests): # phase 2 if len(all_mats) == 0: tot_qty = 0 continue - if commod in all_mats: + if commod in all_mats: mats = [all_mats[commod]] - + else: mats = [] - print("time:", self.context.time, "mats to trade matching request commod:", mats) if len(mats) == 0: - continue + continue + + recipe_comp = self.context.get_recipe( + self.fuel_outrecipes[commod_index]) - recipe_comp = self.context.get_recipe(self.fuel_outrecipes[commod_index]) - for req in reqs: tot_bid = 0 for jj in range(len(mats)): @@ -365,66 +399,82 @@ def get_material_bids(self, requests): # phase 2 qty = min(req.target.quantity, self.assem_size) mat = ts.Material.create_untracked(qty, recipe_comp) for kk in range(self.spent_fuel.count): - bids.append({'request':req,'offer':mat}) + bids.append({'request': req, 'offer': mat}) if tot_bid >= req.target.quantity: break tot_qty = 0 for mat in mats: tot_qty += mat.quantity if len(bids) == 0: - return + return - port = {'bids':bids} - print("time:", self.context.time, "portfolio:", port) - print("time:", self.context.time, "respond", len(bids), "assemblies") + port = {'bids': bids} return port - def get_material_trades(self, trades): #phase 5.1 + def get_material_trades(self, trades): # phase 5.1 ''' Trade away material in the spent_fuel material buffer. Pull out the material from spent fuel inventory - For each trade, get the commodity name, get the + For each trade, get the commodity name, get the composition of the trade. - Then trade the materials from the spent fuel inventory. + Then trade the materials from the spent fuel inventory. + + Parameters: + ----------- + trades: tuple + tuple of material objects requested matched to the + material responses + + Returns: + -------- + responses: dict + dictionary of {Material object:Material object}. The + key is the Material request being matched. The value is the + Material in the spent fuel inventory ''' responses = {} mats = self.pop_spent() - print("time:", self.context.time, "trade away", len(trades), "assemblies") for ii in range(len(trades)): - print("time:", self.context.time, "number of trades:", len(trades)) commodity = trades[ii].request.commodity mat = mats[commodity].pop(-1) responses[trades[ii]] = mat self.resource_indexes.pop(mat.obj_id) self.push_spent(mats) - return responses + return responses - def accept_material_trades(self, responses): # phase 5.2 + def accept_material_trades(self, responses): # phase 5.2 ''' Accept bid for fuel_incommods - The number of assemblies to be loaded is the minimum - of the number of responses or the number of - assemblies the core is missing (full core minus how many - assemblies are present). If the number of assemblies to + The number of assemblies to be loaded is the minimum + of the number of responses or the number of + assemblies the core is missing (full core minus how many + assemblies are present). If the number of assemblies to load is greater than 0, then record this number. - For each trade in the responses, get the commodity requested - in the trade and reset the index. + For each trade in the responses, get the commodity requested + in the trade and reset the index. + + If the core is not full, the put the material in the core. + If the core is full, the put the material in the fresh + fuel inventory. + + Parameters: + ----------- + responses: dict + dictionary of {Material object:Material object}. The + key is the Material request being matched. The value is the + Material in the spent fuel inventory - If the core is not full, the put the material in the core. - If the core is full, the put the material in the fresh - fuel inventory. ''' - n_load = min(len(responses), self.n_assem_core - self.core.count) - print("time:", self.context.time, "accept", n_load, "assemblies") + n_load = len(responses) if n_load > 0: ss = str(n_load) + " assemblies" - #self.record("LOAD", ss) + # self.record("LOAD", ss) for trade in responses: commodity = trade.request.commodity material = trade.request.target @@ -433,11 +483,16 @@ def accept_material_trades(self, responses): # phase 5.2 self.core.push(material) else: self.fresh_fuel.push(material) - return + + return def retired(self): ''' Determine if the prototype is retired + + Return: + ------- + Bool: true if the conditions are met, False if they aren't met ''' if (self.exit_time != -1) and (self.context.time > self.exit_time): return 1 @@ -446,83 +501,98 @@ def retired(self): def discharge(self): ''' + Determine the number of assemblies to discharge. If the space for + spent fuel assemblies is less than the number that need + to be discharged, then don't discharge and return false + + Record the number of assemblies discharged. + + Remove the correct number of assemblies from the core. + Get the name of each spent fuel assembly. Then get the + mass of each spent fuel commodity discharged. Return true if the + fuel has been discharged. + + Returns: + -------- + Bool: True if fuel is discharged, false if fuel is not discharged. ''' npop = min(self.n_assem_batch, self.core.count) if (self.n_assem_spent - self.spent_fuel.count) < npop: - #self.record("DISCHARGE", "failed") + # self.record("DISCHARGE", "failed") return False ss = str(npop) + " assemblies" - #self.record("DISCHARGE", ss) - print("time:", self.context.time, "discharge", ss) + # self.record("DISCHARGE", ss) core_pop = self.core.pop_n(npop) for ii in range(len(core_pop)): self.spent_fuel.push(core_pop[ii]) - tot_spent = 0 - + for ii in range(len(self.fuel_outcommods)): spent_mats = self.peek_spent() + tot_spent = 0 if self.fuel_outcommods[ii] in spent_mats: mats = spent_mats[self.fuel_outcommods[ii]] tot_spent += mats.quantity - lib.record_time_series("supply"+self.fuel_outcommods[ii], self, tot_spent) + lib.record_time_series( + "supply" + self.fuel_outcommods[ii], self, tot_spent) return True def load(self): ''' - Determine number of assemblies to load, either the - number of assemblies needed for the core less the number - already in the core or the number held by the - fresh fuel inventory. If no assemblies are needed, then - return. + Determine number of assemblies to load, either the + number of assemblies needed for the core less the number + already in the core or the number held by the + fresh fuel inventory. If no assemblies are needed, then + return. - Record the number of assemblies that are to be loaded, then move - them from the fresh fuel inventory to the core inventory. + Record the number of assemblies that are to be loaded, then move + them from the fresh fuel inventory to the core inventory. ''' - n = min((self.n_assem_core-self.core.count), self.fresh_fuel.count) - print("time:", self.context.time, "load", n, "assemblies") + n = min((self.n_assem_core - self.core.count), self.fresh_fuel.count) if n == 0: return ss = str(n) + " assemblies" - #self.record("LOAD", ss) + # self.record("LOAD", ss) assemblies = self.fresh_fuel.pop_n(n) for ii in range(len(assemblies)): self.core.push(assemblies[ii]) - return + return def transmute(self, n_assem): ''' - Get the material composition of the minimum of the - number of assemblies specified or the number of - assemblies in the core. + Get the material composition of the minimum of the + number of assemblies specified or the number of + assemblies in the core. - If there are more assemblies in the core than what will - be removed, then rotate the untransmuted materials to the back + If there are more assemblies in the core than what will + be removed, then rotate the untransmuted materials to the back of the buffer. Record the number of assemblies to be transmuted. Transmute the fuel - by changing the recipe of the material to that of the + by changing the recipe of the material to that of the fuel_outrecipes - There seem to be two Transmute functions in the cycamore reactor? + Parameters: + ----------- + n_assem: int + Number of assemblies to be transmuted ''' old = self.core.pop_n(min(n_assem, self.core.count)) for ii in range(len(old)): - self.core.push(old[ii]) + self.core.push(old[ii]) ss = str(len(old)) + " assemblies" - #self.record("TRANSMUTE", ss) - print("time:", self.context.time, "transmute", ss) + # self.record("TRANSMUTE", ss) for ii in range(len(old)): - print("Call OpenMC") model = self.deplete.read_model() micro_xs = self.deplete.read_microxs() - ind_op = od.IndependentOperator(model.materials, micro_xs, - str(self.model_path + self.chain_file)) + ind_op = od.IndependentOperator( + model.materials, micro_xs, str( + self.model_path + self.chain_file)) ind_op.output_dir = self.model_path integrator = od.PredictorIntegrator(ind_op, np.ones( - int(self.cycle_time)*30), power=int(self.power_cap)*1000*3, + int(self.cycle_time) * 30), power=int(self.power_cap) * 1000 * 3, timestep_units='d') integrator.integrate() @@ -532,53 +602,66 @@ def transmute(self, n_assem): def record(self, event, val): ''' - Record a reactor event to the output database with the + Record a reactor event to the output database with the given name and note val Parameters: ----------- - event: str + event: str name of event val: str - value of event + value of event to be recorded ''' - events = self.context.new_datum("ReactorEvents") # creates tables with name ReactorEvents + events = self.context.new_datum( + "ReactorEvents") # creates tables with name ReactorEvents datum = lib.Datum("Event") lib.Datum.add_val(datum, "Event", event) events.add_val("Value", val) events.record() return - def index_res(self, material, incommod): ''' - For the name of any item in the fuel in_commods list - match the name of the commodity given, then + For the name of any item in the fuel in_commods list + match the name of the commodity given, then the object Id of the material. - If the name of the given commodity isn't in the - fuel in_commods list, then return an error. + If the name of the given commodity isn't in the + fuel in_commods list, then return an error. + + Parameters: + ----------- + material: Material + Cyclus Material object to be checked + incommod: str + commodity name to compare against ''' try: for ii in range(len(self.fuel_incommods)): if self.fuel_incommods[ii] == incommod: - self.resource_indexes[material.obj_id] = ii + self.resource_indexes[material.obj_id] = ii return - except: - raise ValueError ( - "openmcyclus.DepleteReactor:DepleteReactor received "\ + except BaseException: + raise ValueError( + "openmcyclus.DepleteReactor:DepleteReactor received " "unsupported incommod material" ) def pop_spent(self): ''' - For each assembly in the spent fuel inventory, - get the commodity name of it, and push the material - back to the spent fuel inventory (??). + For each assembly in the spent fuel inventory, + get the commodity name of it, and push the material + back to the spent fuel inventory. + + Then reverse the order of the material in the + mapped materials to put the oldest assemblies + first and make sure they get traded away first. - Then reverse the order of the material in the - mapped materials to put the oldest assemblies - first and make sure they get traded away first. + Returns: + -------- + mapped: dict + Keys are the commodity names (str) and the values are + lists of Material objects from the spent fuel inventory ''' mats = self.spent_fuel.pop_n(self.spent_fuel.count) mapped = {} @@ -587,23 +670,35 @@ def pop_spent(self): for ii in range(len(mats)): commod = self.get_commod(mats[ii], 'out') mapped[commod].append(mats[ii]) - print("time:", self.context.time, "pop_spent mapped:", mapped) return mapped def push_spent(self, leftover): ''' Reverse the order of materials in the leftover list + Parameters: + ----------- + leftover: dict + Keys are the commodity names (str). Values are a list of + Material objects. ''' for commod in leftover: leftover[commod].reverse for material in leftover[commod]: self.spent_fuel.push(material) - return + return def peek_spent(self): ''' - + Creates a dictionary of the materials in the spent + fuel inventory based on their commodity name. + + Returns: + -------- + mapped: dict + Keys are the commodity names of the spent fuel. + Values are the Materials with the given commodity names. + ''' mapped = {} if self.spent_fuel.count > 0: @@ -611,35 +706,71 @@ def peek_spent(self): for ii in range(len(mats)): self.spent_fuel.push(mats[ii]) commod = self.get_commod(mats[ii], 'out') - mapped[commod] = mats[ii] + mapped[commod] = mats[ii] return mapped def get_commod(self, material, flow): ''' - Get the index in fuel_incommods or fuel_outcommods + Get the index in fuel_incommods or fuel_outcommods corresponding to the object id of a material + + Parameters: + ----------- + material: Material obj + Material object to be queried + flow: str + input or output commodity. "in" if input, "out" + if output commodity. + + Return: + ------- + string + name of commodity for the queried material ''' ii = self.resource_indexes[material.obj_id] if flow == 'in': return self.fuel_incommods[ii] elif flow == 'out': return self.fuel_outcommods[ii] - + def get_recipe(self, material, flow): ''' - Get the index in fuel_inrecipes or fuel_outrecipes + Get the index in fuel_inrecipes or fuel_outrecipes corresponding to the object id of a material + + Parameters: + ----------- + material: Material obj + Material object to be queried + flow: str + input or output recipe. "in" if input, "out" + if output recipt. + + Return: + ------- + string + name of recipe for the queried material ''' ii = self.resource_indexes[material.obj_id] if flow == 'in': return self.fuel_inrecipes[ii] elif flow == 'out': return self.fuel_outrecipes[ii] - + def get_pref(self, material): ''' - Get the index in fuel_prefs + Get the index in fuel_prefs corresponding to the object id of a material + + Parameters: + ----------- + material: Material obj + Material object to be queried. + + Return: + ------- + string + preference for the queried material ''' ii = self.resource_indexes[material.obj_id] return self.fuel_prefs[ii] diff --git a/tests/integration_tests/test_depletereactor.py b/tests/integration_tests/test_depletereactor.py index c0100f9..f9b8d64 100644 --- a/tests/integration_tests/test_depletereactor.py +++ b/tests/integration_tests/test_depletereactor.py @@ -61,13 +61,14 @@ def find_ids(self, spec, a, spec_col="Spec", id_col="AgentId"): spec: str value to find in a column a: table - database table to searc + database table to search spec_col: str Name of column to search id_col: str column name to search Returns: + -------- array ''' @@ -157,10 +158,10 @@ def test_transactions(self): unique, counts = np.unique(commodities, return_counts=True) count_dict = dict(zip(unique,counts)) assert len(tbl) == 12 - assert 'uox' not in unique - assert count_dict['mox'] == 6 - assert 'spent_uox' not in unique - assert count_dict['spent_mox'] == 6 + assert count_dict['uox'] == 2 + assert count_dict['mox'] == 4 + assert count_dict['spent_uox'] == 2 + assert count_dict['spent_mox'] == 4 assert all(times == [3,3,3,5,5,8,8,11,11,13,13,13]) def test_resources(self):