From 6896f02a32300f1a216e6fe87673df28843eda65 Mon Sep 17 00:00:00 2001 From: yardasol Date: Sun, 27 Nov 2022 23:14:01 -0600 Subject: [PATCH 01/23] add machinery to set depletion settings for openmc in main input file --- examples/msbr/msbr_main.json | 5 +- examples/tap/tap_main.json | 5 +- saltproc/abc.py | 2 +- saltproc/app.py | 108 ++++--- saltproc/input_schema.json | 288 ++++++++++++++---- saltproc/openmc_depcode.py | 89 +++--- saltproc/openmc_deplete.py | 60 ++-- saltproc/reactor.py | 15 +- saltproc/serpent_depcode.py | 42 +-- .../run_constant_reprocessing/tap_input.json | 5 +- .../run_no_reprocessing/test_input.json | 5 +- tests/openmc_data/depletion_settings.json | 1 - tests/openmc_data/tap_input.json | 15 +- tests/serpent_data/tap_input.json | 5 +- 14 files changed, 412 insertions(+), 233 deletions(-) delete mode 100644 tests/openmc_data/depletion_settings.json diff --git a/examples/msbr/msbr_main.json b/examples/msbr/msbr_main.json index 063fd4dfe..503b4a518 100644 --- a/examples/msbr/msbr_main.json +++ b/examples/msbr/msbr_main.json @@ -2,7 +2,7 @@ "proc_input_file": "msbr_objects.json", "dot_input_file": "msbr.dot", "output_path": "data", - "num_depsteps": 12, + "n_depletion_timesteps": 12, "depcode": { "codename": "serpent", "template_input_file_path": "msbr.serpent", @@ -18,6 +18,7 @@ "volume": 1.0, "mass_flowrate": 9920000, "power_levels": [ 2250000000 ], - "dep_step_length_cumulative": [ 3 ] + "depletion_timesteps": [ 3 ], + "timestep_units": "d" } } diff --git a/examples/tap/tap_main.json b/examples/tap/tap_main.json index 783516b70..df89b9217 100644 --- a/examples/tap/tap_main.json +++ b/examples/tap/tap_main.json @@ -2,7 +2,7 @@ "proc_input_file": "tap_objects.json", "dot_input_file": "tap.dot", "output_path": "data", - "num_depsteps": 3, + "n_depletion_timesteps": 3, "depcode": { "codename": "serpent", "template_input_file_path": "tap.serpent", @@ -34,6 +34,7 @@ "volume": 1.0, "mass_flowrate": 9920000, "power_levels": [ 1250000000 ], - "dep_step_length_cumulative": [ 2 ] + "depletion_timesteps": [ 2 ], + "timestep_units": "d" } } diff --git a/saltproc/abc.py b/saltproc/abc.py index 5389e2060..66e372859 100644 --- a/saltproc/abc.py +++ b/saltproc/abc.py @@ -36,7 +36,7 @@ def __init__(self, exec_path, template_input_file_path, geo_files): - """Initializes the Depcode object. + """Initializes a Depcode object. Parameters ---------- diff --git a/saltproc/app.py b/saltproc/app.py index a1b54c59c..64c3341e2 100644 --- a/saltproc/app.py +++ b/saltproc/app.py @@ -24,36 +24,28 @@ def run(): depcode = _create_depcode_object(object_input[0]) simulation = _create_simulation_object( object_input[1], depcode, cores, nodes) - msr = _create_reactor_object(object_input[2]) - - #if isinstance(depcode.runtime_inputfile, str): - # depcode.runtime_inputfile = (input_path / - # depcode.runtime_inputfile).resolve().as_posix() - #else: - # raise ValueError("not implemented") - #depcode.runtime_matfile = ( - # input_path / - # depcode.runtime_matfile).resolve().as_posix() + msr = _create_reactor_object(object_input[2], object_input[0]['codename']) + # Check: Restarting previous simulation or starting new? simulation.check_restart() # Run sequence # Start sequence - for dep_step in range(len(msr.dep_step_length_cumulative)): - print("\n\n\nStep #%i has been started" % (dep_step + 1)) + for step_idx in range(len(msr.depletion_timesteps)): + print("\n\n\nStep #%i has been started" % (step_idx + 1)) simulation.sim_depcode.write_runtime_input(msr, - dep_step, + step_idx, simulation.restart_flag) depcode.run_depletion_step(cores, nodes) - if dep_step == 0 and simulation.restart_flag is False: # First step + if step_idx == 0 and simulation.restart_flag is False: # First step # Read general simulation data which never changes simulation.store_run_init_info() - # Parse and store data for initial state (beginning of dep_step) + # Parse and store data for initial state (beginning of step_idx) mats = depcode.read_depleted_materials(False) - simulation.store_mat_data(mats, dep_step - 1, False) + simulation.store_mat_data(mats, step_idx - 1, False) # Finish of First step # Main sequence mats = depcode.read_depleted_materials(True) - simulation.store_mat_data(mats, dep_step, False) + simulation.store_mat_data(mats, step_idx, False) simulation.store_run_step_info() # Reprocessing here print("\nMass and volume of fuel before reproc: %f g, %f cm3" % @@ -83,12 +75,12 @@ def run(): # mats['ctrlPois'].vol)) print("Removed mass [g]:", extracted_mass) # Store in DB after reprocessing and refill (right before next depl) - simulation.store_after_repr(mats, waste_and_feed_streams, dep_step) + simulation.store_after_repr(mats, waste_and_feed_streams, step_idx) depcode.update_depletable_materials(mats, simulation.burn_time) del mats, waste_streams, waste_and_feed_streams, extracted_mass gc.collect() # Switch to another geometry? - if simulation.adjust_geo and simulation.read_k_eds_delta(dep_step): + if simulation.adjust_geo and simulation.read_k_eds_delta(step_idx): depcode.switch_to_next_geometry() print("\nTime at the end of current depletion step: %fd" % simulation.burn_time) @@ -96,7 +88,7 @@ def run(): '''print("Reactor object data.\n", msr.mass_flowrate, msr.power_levels, - msr.dep_step_length_cumulative)''' + msr.depletion_timesteps)''' def parse_arguments(): @@ -178,7 +170,7 @@ def read_main_input(main_inp_file): input_path / j['dot_input_file']).resolve().as_posix() output_path = j['output_path'] - num_depsteps = j['num_depsteps'] + n_depletion_steps = j['n_depletion_steps'] # Global output path output_path = (input_path / output_path) @@ -198,6 +190,9 @@ def read_main_input(main_inp_file): value = depcode_input['template_input_file_path'][key] depcode_input['template_input_file_path'][key] = ( input_path / value).resolve().as_posix() + depcode_input['chain_file_path'] = \ + (input_path / + depcode_input['chain_file_path']).resolve().as_posix() else: raise ValueError( f'{depcode_input["codename"]} ' @@ -217,7 +212,7 @@ def read_main_input(main_inp_file): simulation_input['db_name'] = db_name.resolve().as_posix() reactor_input = _process_main_input_reactor_params( - reactor_input, num_depsteps) + reactor_input, n_depletion_steps) return input_path, process_file, dot_file, ( depcode_input, simulation_input, reactor_input) @@ -244,14 +239,16 @@ def _create_depcode_object(depcode_input): depcode = SerpentDepcode elif codename == 'openmc': depcode = OpenMCDepcode + chain_file_path = depcode_input.pop('chain_file') + depletion_settings = depcode_input.pop('depletion_settings') else: raise ValueError( f'{depcode_input["codename"]} is not a supported depletion code') - depcode = depcode(depcode_input['output_path'], - depcode_input['exec_path'], - depcode_input['template_input_file_path'], - geo_files=depcode_input['geo_file_paths']) + depcode = depcode(*depcode_input) + if codename == 'openmc': + depcode.chain_file = chain_file + depcode.depletion_settings = depletion_settings return depcode @@ -269,44 +266,59 @@ def _create_simulation_object(simulation_input, depcode, cores, nodes): return simulation -def _create_reactor_object(reactor_input): +def _create_reactor_object(reactor_input, codename): """Helper function for `run()` """ - msr = Reactor( - volume=reactor_input['volume'], - mass_flowrate=reactor_input['mass_flowrate'], - power_levels=reactor_input['power_levels'], - dep_step_length_cumulative=reactor_input['dep_step_length_cumulative']) + timesteps = reactor_input['depletion_timesteps'] + timestep_type = reactor_input['timestep_type'] + if timestep_type == 'cumulative' and codename == 'openmc': + ts = list(np.diff(depletion_timesteps)) + reactor_input['depletion_timesteps'] = depletion_timesteps[0] + ts + + timestep_units = reactor_input['time_unit'] + if not(timestep_units in ('d', 'day')) and time_units.lower() != 'mWd/kg' and codename == 'serpent': + if timestep_units in ('s', 'sec'): + reactor_input['depletion_timesteps'] /= 60 * 60 * 24 + elif timestep_units in ('min', 'minute'): + reactor_input['depletion_timesteps'] /= 60 * 24 + elif timestep_units in ('h', 'hr', 'hour'): + reactor_input['depletion_timesteps'] /= 24 + elif timestep_units in ('a', 'year'): + reactor_input['depletion_timesteps'] *= 365.25 + else: + raise IOError(f'Unrecognized time unit: {timestep_units}') + + msr = Reactor(**reactor_input) return msr -def _process_main_input_reactor_params(reactor_input, num_depsteps): +def _process_main_input_reactor_params(reactor_input, n_depletion_steps): """ Process SaltProc reactor class input parameters based on the value and - data type of the `num_depsteps` parameter, and throw errors if the input + data type of the `n_depletion_steps` parameter, and throw errors if the input parameters are incorrect. """ - dep_step_length_cumulative = reactor_input['dep_step_length_cumulative'] + depletion_timesteps = reactor_input['depletion_timesteps'] power_levels = reactor_input['power_levels'] - if num_depsteps is not None and len(dep_step_length_cumulative) == 1: - if num_depsteps < 0.0 or not int: + if n_depletion_steps is not None and len(depletion_timesteps) == 1: + if n_depletion_steps < 0.0 or not int: raise ValueError('Depletion step interval cannot be negative') - # Make `power_levels` and `dep_step_length_cumulative` - # lists of length `num_depsteps` + # Make `power_levels` and `depletion_timesteps` + # lists of length `n_depletion_steps` else: - step = int(num_depsteps) - deptot = float(dep_step_length_cumulative[0]) * step - dep_step_length_cumulative = \ - np.linspace(float(dep_step_length_cumulative[0]), + step = int(n_depletion_steps) + deptot = float(depletion_timesteps[0]) * step + depletion_timesteps = \ + np.linspace(float(depletion_timesteps[0]), deptot, num=step) power_levels = float(power_levels[0]) * \ - np.ones_like(dep_step_length_cumulative) - reactor_input['dep_step_length_cumulative'] = \ - dep_step_length_cumulative + np.ones_like(depletion_timesteps) + reactor_input['depletion_timesteps'] = \ + depletion_timesteps reactor_input['power_levels'] = power_levels - elif num_depsteps is None and isinstance(dep_step_length_cumulative, + elif n_depletion_steps is None and isinstance(depletion_timesteps, (np.ndarray, list)): - if len(dep_step_length_cumulative) != len(power_levels): + if len(depletion_timesteps) != len(power_levels): raise ValueError( 'Depletion step list and power list shape mismatch') diff --git a/saltproc/input_schema.json b/saltproc/input_schema.json index 6e5e15f98..573a0b08f 100644 --- a/saltproc/input_schema.json +++ b/saltproc/input_schema.json @@ -19,7 +19,7 @@ "type": "string", "pattern": "^(.\\/)*(.*)$" }, - "num_depsteps": { + "n_depletion_timesteps": { "description": "Number of steps for constant power and depletion interval case", "type": "number" }, @@ -41,14 +41,11 @@ "type": "array", "items": { "type": "string"}, "minItems": 1, - "uniqueItems": true - } + "uniqueItems": true} }, "allOf": [ { - "if": { - "properties": { "codename": { "const": "serpent" } } - }, + "if": {"properties": { "codename": { "const": "serpent" }}}, "then": { "properties": { "exec_path": { @@ -64,8 +61,7 @@ }, { "if": { - "properties": { "codename": { "const": "openmc" } } - }, + "properties": { "codename": { "const": "openmc" }}}, "then": { "properties": { "exec_path": { @@ -85,14 +81,9 @@ "description": "OpenMC materials file", "type": "string", "pattern": "^(.\\/)*(.*)\\.xml$", - "default": "materials.xml"}, - "chain_file": { - "description": "OpenMC depletion chain file", - "type": "string", - "pattern": "^(.\\/)*(.*)\\.xml$"} + "default": "materials.xml"} }, - - "required": ["settings", "materials", "chain_file"] + "required": ["settings", "materials"] }, "geo_file_paths": { "description": "Path(s) to geometry file(s) to switch to in OpenMC code runs", @@ -101,66 +92,231 @@ "pattern": "^(.\\/)*(.*)\\.xml$"}, "minItems": 1, "uniqueItems": true, - "default": ["geometry.xml"] + "default": ["geometry.xml"]}, + "chain_file_path": { + "description": "Path to depletion chain file", + "pattern": "^(.\\/)*(.*)\\.xml$", + "type": "string"}, + "depletion_settings" : { + "description": "OpenMC depletion settings", + "type": "object", + "properties": { + "method": { + "description": "Integration method used for depletion", + "type": "string", + "enum": ["cecm", "predictor", "cf4", "epc_rk4", "si_celi", "si_leqi", "celi", "leqi"], + "default": "predictor"}, + "final_step": { + "description": "Indicate whether or not a transport solve should be run at the end of the last timestep", + "type": "boolean", + "default": true}, + "operator_kwargs": { + "description": "Keyword arguments passed to the depletion operator initalizer", + "type": "object", + "properties": { + "diff_burnable_mats": { + "description": "Whether to differentiate burnable materials with multiple instances.", + "type": "boolean", + "defualt": false}, + "normalization_mode": { + "description": "Indicate how tally resutls should be normalized", + "type": "string", + "enum": ["energy-deposition", "fission-q", "source-rate"], + "default": "fission-q"}, + "fission_q": { + "description": "Path to fission Q values", + "type": "string", + "default": null}, + "dilute_initial": { + "description": "Initial atom density to add for nuclides that are zero in initial condition.", + "type": "number", + "minimum": 0, + "default": 1000}, + "fission_yield_mode": { + "description": "Determine what fission energy helper is used", + "type": "string", + "enum": ["constant", "cutoff", "average"], + "default": "constant"}, + "fission_yield_opts": { + "description": "Arguments for the fission yield helper", + "type": "object", + "defualt": null}, + "reaction_rate_mode": { + "description": "Indicate how one-group reaction rates should be calculated", + "type": "string", + "enum": ["direct", "flux"], + "default": "direct"}, + "reaction_rate_opts": { + "type": "object", + "default": null}, + "reduce_chain": { + "description": "Whether or not to reduce the depletion chain.", + "type": "boolean", + "default": false}, + "reduce_chain_level": { + "description": "Depth of serach while reducing depletion chain", + "type": "number", + "default": null} + }, + "allOf": [ + { + "if": {"properties": { "fission_yield_mode": { "const": "constant" }}}, + "then": { + "properties": { + "fission_yield_opts": { + "type": "object", + "properties": { + "energy": { + "description": "Energy of fission yield libraries [MeV]", + "type": "number"} + }, + "default": null + } + } + } + }, + { + "if": {"properties": { "fission_yield_mode": { "const": "cutoff" }}}, + "then": { + "properties": { + "fission_yield_opts": { + "type": "object", + "proterties": { + "cutoff": { + "description": "Cutoff energy in eV", + "type": "number"}, + "thermal_energy": { + "description": "Energy of yield data corresponding to thermal yields", + "type": "number"}, + "fast_energy": { + "description": "Energy of yield data corresponding to fast yields", + "type": "number"} + }, + "default": null + } + } + } + }, + { + "if": {"properties": { "reaction_rate_mode": { "const": "flux" }}}, + "then": { + "properties": { + "reaction_rate_opts": { + "type": "object", + "properties": { + "energies": { + "description": "Energy group boundaries", + "type": "array", + "items": { + "type": "number", + "minItems": 2} + }, + "reactions": { + "description": "Reactions to tally", + "type": "array", + "items": { + "type": "string", + "minItems": 1} + }, + "nuclides": { + "description": "Nuclides on which to tally reactions", + "type": "array", + "items": { + "type": "string", + "minItems": 1} + } + }, + "default": null + } + } + } + } + ] + }, + "output": { + "description": "Capture OpenMC output from standard out", + "type": "boolean", + "default": true}, + "integrator_kwargs": { + "description": "Remaining keyword arguments for the depletion Integrator initalizer", + "type": "object", + "properties": { + "solver": { + "description": "Bateman equations solver type", + "type": "string", + "enum": ["cram16", "cram48"]}, + "n_steps": { + "description": "Number of stochastic iterations for stochastic integrators", + "type": "number", + "minimum": 1} + }, + "default": null + } + } } } } } - ], - "required": ["codename", "exec_path", "template_input_file_path", "npop", "active_cycles","inactive_cycles", "geo_file_paths"] + "required": ["codename", "exec_path", "template_input_file_path", "geo_file_paths"] }, - "simulation": { - "description": "Simulation class input parameters", - "type": "object", - "properties": { - "sim_name": { - "description": "Name of simulation", - "type": "string"}, - "db_name": { - "description": "Output HDF5 database file name", - "type": "string", + "simulation": { + "description": "Simulation class input parameters", + "type": "object", + "properties": { + "sim_name": { + "description": "Name of simulation", + "type": "string"}, + "db_name": { + "description": "Output HDF5 database file name", + "type": "string", "pattern": "^(.*)\\.h5$"}, - "restart_flag": { - "description": "Restart simulation from the step when it stopped?", - "type": "boolean"}, - "adjust_geo": { - "description": "switch to another geometry when keff drops below 1?", - "type": "boolean"} - }, - "requires": ["sim_name", "db_name", "restart_flag", "adjust_geo"] - }, - "reactor": { - "description": "Reactor class input parameters", - "type": "object", - "properties": { - "volume": { - "description": "reactor core volume [cm^3]", - "type": "number", + "restart_flag": { + "description": "Restart simulation from the step when it stopped?", + "type": "boolean"}, + "adjust_geo": { + "description": "switch to another geometry when keff drops below 1?", + "type": "boolean"} + }, + "required": ["sim_name", "db_name", "restart_flag", "adjust_geo"] + }, + "reactor": { + "description": "Reactor class input parameters", + "type": "object", + "properties": { + "volume": { + "description": "reactor core volume [cm^3]", + "type": "number", "minimum": 0}, - "mass_flowrate": { - "description": "Salt mass flowrate through reactor core [g/s]", - "type": "number", + "mass_flowrate": { + "description": "Salt mass flowrate through reactor core [g/s]", + "type": "number", "minimum": 0 }, - "power_levels": { - "description": "Reactor power or power step list durng depletion step [W]", - "type": "array", - "items": { "type": "number", - "minimum": 0}, - "minItems": 1, - "uniqueItems": false - }, - "dep_step_length_cumulative": { - "description": "Depletion step length(s) (cumulative) [d]", - "type": "array", - "items": { "type": "number", - "minimum": 0}, - "minItems": 1, - "uniqueItems": false - } - }, - "required": ["volume", "mass_flowrate", "power_levels", "dep_step_length_cumulative"] - } + "power_levels": { + "description": "Reactor power or power step list durng depletion step [W]", + "type": "array", + "items": { "type": "number", "minimum": 0}, + "minItems": 1, + "uniqueItems": false}, + "depletion_timesteps": { + "description": "Depletion step length(s)", + "type": "array", + "items": { "type": "number", "minimum": 0}, + "minItems": 1, + "uniqueItems": false}, + "timestep_type": { + "description": "Depletion step type", + "type": "string", + "enum": ["cumulative", "stepwise"], + "default": "stepwise"}, + "timestep_units": { + "description": "Timestep unit", + "type": "string", + "enum": ["s", "sec", "min", "minute", "h", "hr", "hour", "d", "day", "a", "year", "MWd/kg", "mwd/kg", "MWD/KG", "MWD/kg", "MWd/KG"] + } + }, + "required": ["volume", "mass_flowrate", "power_levels", "depletion_steps", "timestep_type", "timestep_units"] + } }, "required": ["proc_input_file", "dot_input_file", "output_path", "depcode", "simulation", "reactor"] } diff --git a/saltproc/openmc_depcode.py b/saltproc/openmc_depcode.py index 9b4b3eae8..700554b1f 100644 --- a/saltproc/openmc_depcode.py +++ b/saltproc/openmc_depcode.py @@ -12,11 +12,8 @@ from saltproc.abc import Depcode class OpenMCDepcode(Depcode): - """Class contains information about input, output, geometry, and - template files for running OpenMC depletion simulations. - Also contains neutrons population, active, and inactive cycles. - Contains methods to read template and output files, - write new input files for OpenMC. + """Interface for running depletion steps in OpenMC, as well as obtaining + depletion step results. Attributes ---------- @@ -38,6 +35,10 @@ class OpenMCDepcode(Depcode): Number of active cycles. inactive_cycles : int Number of inactive cycles. + depletion_settings : dict + ... + chain_file_path : str + Path to depletion chain file """ @@ -46,7 +47,7 @@ def __init__(self, exec_path, template_input_file_path, geo_files): - """Initializes the OpenMCDepcode object. + """Initializes a OpenMCDepcode object. Parameters ---------- @@ -132,17 +133,17 @@ def run_depletion_step(self, cores, nodes): '-n', str(nodes), 'python', - './deplete_openmc.py' - '-mat', + self.exec_path, + '--materials', self.runtime_matfile, - '-geo', + '--geometry', self.runtime_inputfile['geometry'], - '-set', + '--settings', self.runtime_inputfile['settings'], - '-tal', + '--tallites', self.runtime_inputfile['tallies'], - '-dep', - self.runtime_inputfile['depletion_settings']) + '--directory', + self.output_path.as_posix()) print('Running %s' % (self.codename)) # TODO: Need to figure out how to adapt this to openmc @@ -168,7 +169,7 @@ def switch_to_next_geometry(self): next_geometry.export_to_xml(path=self.runtime_inputfile['geometry']) del mats, next_geometry - def write_runtime_input(self, reactor, dep_step, restart): + def write_runtime_input(self, reactor, depletion_step, restart): """Write OpenMC runtime input files for running depletion step. Parameters @@ -176,13 +177,13 @@ def write_runtime_input(self, reactor, dep_step, restart): reactor : Reactor Contains information about power load curve and cumulative depletion time for the integration test. - dep_step : int + depletion_step : int Current depletion time step. restart : bool Is the current simulation restarted? """ - if dep_step == 0 and not restart: + if depletion_step == 0 and not restart: materials = openmc.Materials.from_xml( self.template_input_file_path['materials']) geometry = openmc.Geometry.from_xml( @@ -203,11 +204,11 @@ def write_runtime_input(self, reactor, dep_step, restart): materials.export_to_xml(self.runtime_matfile) geometry.export_to_xml(self.runtime_inputfile['geometry']) settings.export_to_xml(self.runtime_inputfile['settings']) - self.write_depletion_settings(reactor, dep_step) + self.write_depletion_settings(reactor, depletion_step) self.write_saltproc_openmc_tallies(materials, geometry) del materials, geometry, settings - def write_depletion_settings(self, reactor, current_depstep_idx): + def write_depletion_settings(self, reactor, step_idx): """Write the depeletion settings for the ``openmc.deplete`` module. @@ -216,46 +217,34 @@ def write_depletion_settings(self, reactor, current_depstep_idx): reactor : Reactor Contains information about power load curve and cumulative depletion time for the integration test. - current_depstep_idx : int + step_idx : int Current depletion step. """ - depletion_settings = {} - current_depstep_power = reactor.power_levels[current_depstep_idx] + current_power = reactor.power_levels[step_idx] # Get current depletion step length - if current_depstep_idx == 0: - current_depstep = reactor.dep_step_length_cumulative[0] - else: - current_depstep = \ - reactor.dep_step_length_cumulative[current_depstep_idx] - \ - reactor.dep_step_length_cumulative[current_depstep_idx - 1] + step_length = reactor.depletion_timesteps[step_idx] - out_path = os.path.dirname(self.runtime_inputfile['settings']) - depletion_settings['directory'] = out_path - depletion_settings['timesteps'] = [current_depstep] + self.depletion_settings['directory'] = self.output_path.as_posix() + self.depletion_settings['timesteps'] = [step_length] - operator_kwargs = {} + operator_kwargs = depletion_settings['operator_kwargs'] + input_path = Path(self.template_input_file_path).parents[0] + if not(operator_kwargs['fission_q'] is None): + operator_kwargs['fission_q'] = \ + (input_path / operator_kwargs['fission_q']).resolve().as_posix() - try: - operator_kwargs['chain_file'] = \ - self.template_input_file_path['chain_file'] - except KeyError: - raise SyntaxError("No chain file defined. Please provide \ - a chain file in your saltproc input file") - - integrator_kwargs = {} - integrator_kwargs['power'] = current_depstep_power - integrator_kwargs['timestep_units'] = 'd' # days - - depletion_settings['operator_kwargs'] = operator_kwargs - depletion_settings['integrator_kwargs'] = integrator_kwargs - - self.runtime_inputfile['depletion_settings'] = \ - os.path.join(out_path, 'depletion_settings.json') - json_dep_settings = json.JSONEncoder().encode(depletion_settings) - with open(self.runtime_inputfile['depletion_settings'], 'w') as f: - f.writelines(json_dep_settings) + self.depletion_settings['operator_kwargs'].update(operator_kwargs) + + integrator_kwargs = self.depletion_settings['integrator_kwargs'] + integrator_kwargs['power'] = current_power + integrator_kwargs['timestep_unitss'] = reactor.time_units # days + self.depletion_settings['integrator_kwargs'].update(integrator_kwargs) + + with open(self.output_path / 'depletion_settings.json') as f: + depletion_settings_json = json.dumps(self.depletion_settings, indent=4) + f.write(depletion_settings_json) def update_depletable_materials(self, mats, dep_end_time): """Updates material file with reprocessed material compositions. diff --git a/saltproc/openmc_deplete.py b/saltproc/openmc_deplete.py index ed3aaa679..20c6f1916 100644 --- a/saltproc/openmc_deplete.py +++ b/saltproc/openmc_deplete.py @@ -1,6 +1,8 @@ +import argparse +import json + import openmc import openmc.deplete as od -import argparse def parse_arguments(): @@ -16,60 +18,62 @@ def parse_arguments(): Path to openmc settings `.xml` file tallies : str Path to openmc tallies `.xml` file + dirctory : str + Directory to write the XML files to. depletion_settings : str Path to the OpenMCDepcode depletion_settings file """ parser = argparse.ArgumentParser() - parser.add_argument('-mat', + parser.add_argument('--materials', type=str, default=1, - help='path to openmc material \ - material xml file') - parser.add_argument('-geo', + help='path to openmc material material xml file') + parser.add_argument('--geometry', type=str, default=1, - help='path to openmc geometry \ - xml file') - parser.add_argument('-set', + help='path to openmc geometry xml file') + parser.add_argument('--settings', type=str, default=None, - help='path to openmc settings \ - xml file') - parser.add_argument('-tal', + help='path to openmc settings xml file') + parser.add_argument('--tallies', type=str, default=None, - help='path to openmc tallies \ - xml file') - parser.add_argument('-dep', - type=str, + help='path to openmc tallies xml file') + parser.add_argument('--directory', + type='str', default=None, - help='path to saltproc depletion \ - settings json file') + help='path to output directory') args = parser.parse_args() - return str(args.mat), str(args.geo), str(args.set), \ - str(args.dep), str(args.tal) + return str(args.materials), str(args.geometry), str(args.settings), \ + str(args.tallies), str(args.directory) args = parse_arguments() # Initalize OpenMC objects -materials = openmc.Materials.from_xml(path=args.mat) -geometry = openmc.Geometry.from_xml(path=args.geo, materials=materials) -settings = openmc.Settings.from_xml(args.set) -tallies = openmc.Tallies.from_xml(args.tal) +materials = openmc.Materials.from_xml(path=args.materials) +geometry = openmc.Geometry.from_xml(path=args.geometry, materials=materials) +settings = openmc.Settings.from_xml(args.settings) +tallies = openmc.Tallies.from_xml(args.tallies) model = openmc.model.Model(materials=materials, geometry=geometry, settings=settings, tallies=tallies) depletion_settings = {} -with open(args.dep) as f: +with open(f'{args.directory}/depletion_settings.json') as f: depletion_settings = json.load(f) -model.deplete(depletion_settings['timesteps'], - directory=depletion_settings['directory'], - operator_kwargs=depletion_settings['operator_kwargs'], - **depletion_settings['integrator_kwargs']) +timesteps = depletion_settings.pop('timesteps') +fission_q = depletion_settings['operator_kwargs']['fission_q'] +if not(fission_q is None): + with open(fission_q, 'r') as f: + fission_q = json.load(f) + + depletion_settings['operator_kwargs']['fission_q'] = fission_q + +model.deplete(timesteps, **depletion_settings) del materials, geometry, settings, tallies, model diff --git a/saltproc/reactor.py b/saltproc/reactor.py index 2d8dbcce2..61d3d06c3 100644 --- a/saltproc/reactor.py +++ b/saltproc/reactor.py @@ -7,7 +7,9 @@ def __init__(self, volume=1.0, mass_flowrate=0.0, power_levels=[0.0], - dep_step_length_cumulative=[1]): + depletion_steps=[1], + timestep_type='stepwise', + timestep_units='d'): """Initializes the class. Parameters @@ -18,12 +20,17 @@ def __init__(self, Total mass flowrate through reactor core (g/s). power_levels : array [:math:`N_{steps}` x1] Normalized power level for each depletion step (W). - dep_step_length_cumulative : array [:math:`N_{steps}` x1] - Cumulative depletion time (d). + depletion_timesteps : array [:math:`N_{steps}` x1] + Array of timesteps. + timestep_type: str + 'cumulative', 'stepwise'. + timestep_units : str + Timestep units """ # initialize all object attributes self.volume = volume self.mass_flowrate = mass_flowrate self.power_levels = power_levels - self.dep_step_length_cumulative = dep_step_length_cumulative + self.depletion_timesteps = depletion_timesteps + self.dep_step_units = dep_step_units diff --git a/saltproc/serpent_depcode.py b/saltproc/serpent_depcode.py index 3d69f35de..ecc0e5ef3 100644 --- a/saltproc/serpent_depcode.py +++ b/saltproc/serpent_depcode.py @@ -11,11 +11,8 @@ from saltproc.abc import Depcode class SerpentDepcode(Depcode): - """Class contains information about input, output, geometry, and - template files for running Serpent2 depletion simulations. - Also contains neutrons population, active, and inactive cycles. - Contains methods to read template and output files, - write new input files for Serpent2. + """Interface to run depletion steps in Serpent, as well as obtaining + depletion step results. Attributes ----------- @@ -46,7 +43,7 @@ def __init__(self, exec_path, template_input_file_path, geo_files): - """Initializes the SerpentDepcode object. + """Initializes a SerpentDepcode object. Parameters ---------- @@ -355,10 +352,8 @@ def read_plaintext_file(self, file_path): def set_power_load(self, file_lines, reactor, - current_depstep_idx): - """Add power load attributes in a :class:`Reactor` object to the - ``set power P dep daystep DEPSTEP`` line in the Serpent2 runtime input - file. + step_idx): + """Set the power for the current depletion step Parameters ---------- @@ -367,7 +362,7 @@ def set_power_load(self, reactor : Reactor Contains information about power load curve and cumulative depletion time for the integration test. - current_depstep_idx : int + step_idx : int Current depletion step. Returns @@ -378,21 +373,28 @@ def set_power_load(self, """ line_idx = 8 # burnup setting line index by default - current_depstep_power = reactor.power_levels[current_depstep_idx] - if current_depstep_idx == 0: - current_depstep = reactor.dep_step_length_cumulative[0] - else: - current_depstep = \ - reactor.dep_step_length_cumulative[current_depstep_idx] - \ - reactor.dep_step_length_cumulative[current_depstep_idx - 1] + current_power = reactor.power_levels[step_idx] + + step_length = reactor.depletion_timesteps[step_idx] + for line in file_lines: if line.startswith('set power '): line_idx = file_lines.index(line) del file_lines[line_idx] + if reactor.timestep_unitss == 'MWd/kg': + step_type = 'bu' + else: + step_type = 'day' + + if reactor.step_type == 'cumulative': + step_type += 'tot' + else: + step_type += 'step' + file_lines.insert(line_idx, # Insert on 9th line - 'set power %5.9E dep daystep %7.5E\n' % - (current_depstep_power, current_depstep)) + f'set power %5.9E dep %s %7.5E\n' % + (current_power, step_type, step_length)) return file_lines def run_depletion_step(self, cores, nodes): diff --git a/tests/integration_tests/run_constant_reprocessing/tap_input.json b/tests/integration_tests/run_constant_reprocessing/tap_input.json index 65e71188b..2a55678c1 100644 --- a/tests/integration_tests/run_constant_reprocessing/tap_input.json +++ b/tests/integration_tests/run_constant_reprocessing/tap_input.json @@ -2,7 +2,7 @@ "proc_input_file": "tap_processes.json", "dot_input_file": "../../tap_paths.dot", "output_path": ".", - "num_depsteps": 2, + "n_depletion_timesteps": 2, "depcode": { "codename": "serpent", "template_input_file_path": "tap_template.ini", @@ -18,6 +18,7 @@ "volume": 1.0, "mass_flowrate": 9.92E+6, "power_levels": [ 1.250E+9 ], - "dep_step_length_cumulative": [ 5 ] + "depletion_timesteps": [ 5 ], + "timestep_units": "d" } } diff --git a/tests/integration_tests/run_no_reprocessing/test_input.json b/tests/integration_tests/run_no_reprocessing/test_input.json index ef13cb42f..95e44a08f 100644 --- a/tests/integration_tests/run_no_reprocessing/test_input.json +++ b/tests/integration_tests/run_no_reprocessing/test_input.json @@ -2,7 +2,7 @@ "proc_input_file": "processes.json", "dot_input_file": "paths.dot", "output_path": ".", - "num_depsteps": 2, + "n_depletion_timesteps": 2, "depcode": { "codename": "serpent", "template_input_file_path": "test_input.ini", @@ -18,6 +18,7 @@ "volume": 1.0, "mass_flowrate": 0.0, "power_levels": [ 1.250E+9 ], - "dep_step_length_cumulative": [ 3 ] + "depletion_timesteps": [ 3 ], + "timestep_units": "d" } } diff --git a/tests/openmc_data/depletion_settings.json b/tests/openmc_data/depletion_settings.json deleted file mode 100644 index 939ecfb26..000000000 --- a/tests/openmc_data/depletion_settings.json +++ /dev/null @@ -1 +0,0 @@ -{"directory": "/home/ooblack/projects/saltproc/tests/openmc_data", "timesteps": [111.111], "operator_kwargs": {"chain_file": "/home/ooblack/projects/saltproc/tests/openmc_data/tap_chain.xml"}, "integrator_kwargs": {"power": 1250000000.0, "timestep_units": "d"}} \ No newline at end of file diff --git a/tests/openmc_data/tap_input.json b/tests/openmc_data/tap_input.json index 689ec4347..194fe759d 100644 --- a/tests/openmc_data/tap_input.json +++ b/tests/openmc_data/tap_input.json @@ -2,16 +2,20 @@ "proc_input_file": "../tap_processes.json", "dot_input_file": "../tap_paths.dot", "output_path": "../temp_data", - "num_depsteps": 2, + "n_depletion_timesteps": 2, "depcode": { "codename": "openmc", "exec_path": "openmc", "template_input_file_path": { "materials": "tap_materials.xml", - "settings": "tap_settings.xml", - "chain_file": "tap_chain.xml" + "settings": "tap_settings.xml" }, - "geo_file_paths": ["tap_geometry_base.xml"] + "geo_file_paths": ["tap_geometry_base.xml"], + "depletion_settings": { + "times_kwargs": { + "chain_file": "main.tmp" + } + } }, "simulation": { "sim_name": "tap_test_simulation_openmc", @@ -23,6 +27,7 @@ "volume": 1.0, "mass_flowrate": 9.92E+6, "power_levels": [ 1.250E+9 ], - "dep_step_length_cumulative": [ 5 ] + "depletion_timesteps": [ 5 ], + "timestep_units": "d" } } diff --git a/tests/serpent_data/tap_input.json b/tests/serpent_data/tap_input.json index 98ca10fd6..48457a818 100644 --- a/tests/serpent_data/tap_input.json +++ b/tests/serpent_data/tap_input.json @@ -2,7 +2,7 @@ "proc_input_file": "../tap_processes.json", "dot_input_file": "../tap_paths.dot", "output_path": "../temp_data", - "num_depsteps": 2, + "n_depletion_timesteps": 2, "depcode": { "codename": "serpent", "exec_path": "sss2", @@ -19,6 +19,7 @@ "volume": 1.0, "mass_flowrate": 9.92E+6, "power_levels": [ 1.250E+9 ], - "dep_step_length_cumulative": [ 5 ] + "depletion_timesteps": [ 5 ], + "timestep_units": "d" } } From 745c0374fb1ef726d4151b68438963cb935e6501 Mon Sep 17 00:00:00 2001 From: yardasol Date: Mon, 28 Nov 2022 16:44:01 -0600 Subject: [PATCH 02/23] fix json schema (format, defaults) --- examples/msbr/msbr_main.json | 2 +- examples/tap/tap_main.json | 2 +- saltproc/abc.py | 8 ++-- saltproc/app.py | 48 ++++++++++++++----- saltproc/input_schema.json | 18 ++++--- saltproc/openmc_depcode.py | 37 ++++++++------ saltproc/reactor.py | 4 +- saltproc/serpent_depcode.py | 12 ++--- tests/conftest.py | 20 ++++---- .../file_interface_openmc/test.py | 20 ++++---- .../file_interface_serpent/test.py | 8 ++-- .../run_constant_reprocessing/tap_input.json | 2 +- .../run_no_reprocessing/test.py | 2 +- .../run_no_reprocessing/test_input.json | 2 +- tests/openmc_data/tap_input.json | 9 +--- tests/serpent_data/tap_input.json | 2 +- tests/unit_tests/test_app.py | 2 +- 17 files changed, 118 insertions(+), 80 deletions(-) diff --git a/examples/msbr/msbr_main.json b/examples/msbr/msbr_main.json index 503b4a518..43d93da2b 100644 --- a/examples/msbr/msbr_main.json +++ b/examples/msbr/msbr_main.json @@ -2,7 +2,7 @@ "proc_input_file": "msbr_objects.json", "dot_input_file": "msbr.dot", "output_path": "data", - "n_depletion_timesteps": 12, + "n_depletion_steps": 12, "depcode": { "codename": "serpent", "template_input_file_path": "msbr.serpent", diff --git a/examples/tap/tap_main.json b/examples/tap/tap_main.json index df89b9217..f91e5fe48 100644 --- a/examples/tap/tap_main.json +++ b/examples/tap/tap_main.json @@ -2,7 +2,7 @@ "proc_input_file": "tap_objects.json", "dot_input_file": "tap.dot", "output_path": "data", - "n_depletion_timesteps": 3, + "n_depletion_steps": 3, "depcode": { "codename": "serpent", "template_input_file_path": "tap.serpent", diff --git a/saltproc/abc.py b/saltproc/abc.py index 66e372859..81e587f41 100644 --- a/saltproc/abc.py +++ b/saltproc/abc.py @@ -35,7 +35,7 @@ def __init__(self, output_path, exec_path, template_input_file_path, - geo_files): + geo_file_paths): """Initializes a Depcode object. Parameters @@ -51,7 +51,7 @@ def __init__(self, the input type (e.g. material, geometry, settings, etc.) as a string are keys, and the path to the input file are values. Type depends on depletion code in use. - geo_files : str or list, optional + geo_file_paths : str or list, optional Path to file that contains the reactor geometry. List of `str` if reactivity control by switching geometry is `On` or just `str` otherwise. @@ -61,7 +61,7 @@ def __init__(self, self.output_path = output_path self.exec_path = exec_path self.template_input_file_path = template_input_file_path - self.geo_files = geo_files + self.geo_file_paths = geo_file_paths self.neutronics_parameters = {} self.step_metadata = {} self.runtime_inputfile = None @@ -119,7 +119,7 @@ def run_depletion_step(self, cores, nodes): @abstractmethod def switch_to_next_geometry(self): """Changes the geometry used in the depletion code simulation to the - next geometry file in ``geo_files`` + next geometry file in ``geo_file_paths`` """ @abstractmethod diff --git a/saltproc/app.py b/saltproc/app.py index 64c3341e2..e1cd73865 100644 --- a/saltproc/app.py +++ b/saltproc/app.py @@ -6,6 +6,7 @@ import numpy as np import json import jsonschema +from jsonschema import Draft202012Validator, validators import gc import networkx as nx import pydotplus @@ -152,10 +153,12 @@ def read_main_input(main_inp_file): input_schema = (Path(__file__).parents[0] / 'input_schema.json') with open(main_inp_file) as f: j = json.load(f) + DefaultValidatingValidator = extend_with_default(Draft202012Validator) with open(input_schema) as s: v = json.load(s) try: - jsonschema.validate(instance=j, schema=v) + DefaultValidatingValidator(v).validate(j) + #jsonschema.validate(instance=j, schema=v) except jsonschema.exceptions.ValidationError: print("Your input file is improperly structured.\ Please see saltproc/tests/test.json for an example.") @@ -217,6 +220,20 @@ def read_main_input(main_inp_file): return input_path, process_file, dot_file, ( depcode_input, simulation_input, reactor_input) +def extend_with_default(validator_class): + validate_properties = validator_class.VALIDATORS["properties"] + def set_defaults(validator, properties, instance, schema): + for property, subschema in properties.items(): + if "default" in subschema: + instance.setdefault(property, subschema["default"]) + for error in validate_properties( + validator, properties, instance, schema, + ): + yield error + return validators.extend( + validator_class, {"properties" : set_defaults}, + ) + def _print_simulation_input_info(simulation_input, depcode_input): """Helper function for `run()` """ @@ -234,22 +251,29 @@ def _print_simulation_input_info(simulation_input, depcode_input): def _create_depcode_object(depcode_input): """Helper function for `run()` """ - codename = depcode_input['codename'] + codename = depcode_input.pop('codename') if codename == 'serpent': depcode = SerpentDepcode elif codename == 'openmc': depcode = OpenMCDepcode - chain_file_path = depcode_input.pop('chain_file') + chain_file_path = depcode_input.pop('chain_file_path') depletion_settings = depcode_input.pop('depletion_settings') else: raise ValueError( f'{depcode_input["codename"]} is not a supported depletion code') - depcode = depcode(*depcode_input) + depcode = depcode(**depcode_input) + #depcode = depcode( + # depcode_input['output_path'], + # depcode_input['exec_path'], + # depcode_input['template_input_file_path'], + # depcode_input['geo_file_paths']) if codename == 'openmc': - depcode.chain_file = chain_file + depcode.chain_file_path = chain_file_path depcode.depletion_settings = depletion_settings + depcode_input['codename'] = codename + return depcode @@ -305,12 +329,14 @@ def _process_main_input_reactor_params(reactor_input, n_depletion_steps): # Make `power_levels` and `depletion_timesteps` # lists of length `n_depletion_steps` else: - step = int(n_depletion_steps) - deptot = float(depletion_timesteps[0]) * step - depletion_timesteps = \ - np.linspace(float(depletion_timesteps[0]), - deptot, - num=step) + step_length = float(depletion_timesteps[0]) + if reactor_input['timestep_type'] == 'stepwise': + depletion_timesteps = [step_length] * n_depletion_steps + else: + depletion_timesteps = \ + np.linspace(step_length, + n_depletion_steps*step_length, + n_depletion_steps) power_levels = float(power_levels[0]) * \ np.ones_like(depletion_timesteps) reactor_input['depletion_timesteps'] = \ diff --git a/saltproc/input_schema.json b/saltproc/input_schema.json index 573a0b08f..e573fa326 100644 --- a/saltproc/input_schema.json +++ b/saltproc/input_schema.json @@ -17,9 +17,10 @@ "output_path": { "description": "Path output data storing folder", "type": "string", - "pattern": "^(.\\/)*(.*)$" + "pattern": "^(.\\/)*(.*)$", + "default": "saltproc_results" }, - "n_depletion_timesteps": { + "n_depletion_steps": { "description": "Number of steps for constant power and depletion interval case", "type": "number" }, @@ -67,6 +68,7 @@ "exec_path": { "description": "Path to OpenMC depletion script", "type": "string", + "enum": ["openmc_deplete.py"], "default": "openmc_deplete.py"}, "template_input_file_path": { "description": "Paths to OpenMC template input files", @@ -231,7 +233,8 @@ } } } - ] + ], + "default": {} }, "output": { "description": "Capture OpenMC output from standard out", @@ -250,15 +253,17 @@ "type": "number", "minimum": 1} }, - "default": null + "default": {} } - } + }, + "default": {} } } } } ], - "required": ["codename", "exec_path", "template_input_file_path", "geo_file_paths"] + "default": {}, + "required": ["codename", "exec_path", "template_input_file_path", "geo_file_paths", "chain_file_path", "depletion_settings"] }, "simulation": { "description": "Simulation class input parameters", @@ -315,6 +320,7 @@ "enum": ["s", "sec", "min", "minute", "h", "hr", "hour", "d", "day", "a", "year", "MWd/kg", "mwd/kg", "MWD/KG", "MWD/kg", "MWd/KG"] } }, + "default": {}, "required": ["volume", "mass_flowrate", "power_levels", "depletion_steps", "timestep_type", "timestep_units"] } }, diff --git a/saltproc/openmc_depcode.py b/saltproc/openmc_depcode.py index 700554b1f..b798c35af 100644 --- a/saltproc/openmc_depcode.py +++ b/saltproc/openmc_depcode.py @@ -3,6 +3,7 @@ import shutil import re import json +from pathlib import Path from pyne import nucname as pyname from pyne import serpent @@ -46,7 +47,7 @@ def __init__(self, output_path, exec_path, template_input_file_path, - geo_files): + geo_file_paths): """Initializes a OpenMCDepcode object. Parameters @@ -60,7 +61,7 @@ def __init__(self, material, and settings) for OpenMC. File type as strings are keys (e.g. 'geometry', 'settings', 'material'), and file path as strings are values. - geo_files : str or list, optional + geo_file_paths : str or list, optional Path to file that contains the reactor geometry. List of `str` if reactivity control by switching geometry is `On` or just `str` otherwise. @@ -69,13 +70,15 @@ def __init__(self, # if using the default depletion file, make sure we have the right path if exec_path == "openmc_deplete.py": - exec_path = (Path(__file__).parents[0] / exec_path) + exec_path = (Path(__file__).parents[0] / exec_path).resolve() + else: + exec_path == (Path(template_input_file_path['settings'].parents[0]) / exec_path).resolve() super().__init__("openmc", output_path, exec_path, template_input_file_path, - geo_files) + geo_file_paths) self.runtime_inputfile = \ {'geometry': (output_path / 'geometry.xml').resolve().as_posix(), 'settings': (output_path / 'settings.xml').resolve().as_posix()} @@ -160,11 +163,11 @@ def run_depletion_step(self, cores, nodes): def switch_to_next_geometry(self): """Switches the geometry file for the OpenMC depletion simulation to - the next geometry file in `geo_files`. + the next geometry file in `geo_file_paths`. """ mats = openmc.Materials.from_xml(self.runtime_matfile) next_geometry = openmc.Geometry.from_xml( - path=self.geo_files.pop(0), + path=self.geo_file_paths.pop(0), materials=mats) next_geometry.export_to_xml(path=self.runtime_inputfile['geometry']) del mats, next_geometry @@ -187,7 +190,7 @@ def write_runtime_input(self, reactor, depletion_step, restart): materials = openmc.Materials.from_xml( self.template_input_file_path['materials']) geometry = openmc.Geometry.from_xml( - self.geo_files[0], materials=materials) + self.geo_file_paths[0], materials=materials) settings = openmc.Settings.from_xml( self.template_input_file_path['settings']) self.npop = settings.particles @@ -229,20 +232,24 @@ def write_depletion_settings(self, reactor, step_idx): self.depletion_settings['directory'] = self.output_path.as_posix() self.depletion_settings['timesteps'] = [step_length] - operator_kwargs = depletion_settings['operator_kwargs'] - input_path = Path(self.template_input_file_path).parents[0] - if not(operator_kwargs['fission_q'] is None): - operator_kwargs['fission_q'] = \ - (input_path / operator_kwargs['fission_q']).resolve().as_posix() + operator_kwargs = {} + input_path = Path(self.template_input_file_path['materials']).parents[0] + try: + if not(operator_kwargs['fission_q'] is None): + operator_kwargs['fission_q'] = \ + (input_path / operator_kwargs['fission_q']).resolve().as_posix() + except KeyError: + pass + operator_kwargs['chain_file'] = self.chain_file_path self.depletion_settings['operator_kwargs'].update(operator_kwargs) - integrator_kwargs = self.depletion_settings['integrator_kwargs'] + integrator_kwargs = {} integrator_kwargs['power'] = current_power - integrator_kwargs['timestep_unitss'] = reactor.time_units # days + integrator_kwargs['timestep_units'] = reactor.timestep_units self.depletion_settings['integrator_kwargs'].update(integrator_kwargs) - with open(self.output_path / 'depletion_settings.json') as f: + with open(self.output_path / 'depletion_settings.json', 'w') as f: depletion_settings_json = json.dumps(self.depletion_settings, indent=4) f.write(depletion_settings_json) diff --git a/saltproc/reactor.py b/saltproc/reactor.py index 61d3d06c3..20a36c704 100644 --- a/saltproc/reactor.py +++ b/saltproc/reactor.py @@ -7,7 +7,7 @@ def __init__(self, volume=1.0, mass_flowrate=0.0, power_levels=[0.0], - depletion_steps=[1], + depletion_timesteps=[1], timestep_type='stepwise', timestep_units='d'): """Initializes the class. @@ -33,4 +33,4 @@ def __init__(self, self.mass_flowrate = mass_flowrate self.power_levels = power_levels self.depletion_timesteps = depletion_timesteps - self.dep_step_units = dep_step_units + self.timestep_units = timestep_units diff --git a/saltproc/serpent_depcode.py b/saltproc/serpent_depcode.py index ecc0e5ef3..03e96390b 100644 --- a/saltproc/serpent_depcode.py +++ b/saltproc/serpent_depcode.py @@ -42,7 +42,7 @@ def __init__(self, output_path, exec_path, template_input_file_path, - geo_files): + geo_file_paths): """Initializes a SerpentDepcode object. Parameters @@ -53,7 +53,7 @@ def __init__(self, Path to Serpent2 executable. template_input_file_path : str Path to user input file for Serpent2 - geo_files : str or list, optional + geo_file_paths : str or list, optional Path to file that contains the reactor geometry. List of `str` if reactivity control by switching geometry is `On` or just `str` otherwise. @@ -63,7 +63,7 @@ def __init__(self, output_path, exec_path, template_input_file_path, - geo_files) + geo_file_paths) self.runtime_inputfile = \ (output_path / 'runtime_input.serpent').resolve().as_posix() self.runtime_matfile = (output_path / 'runtime_mat.ini').resolve().as_posix() @@ -231,7 +231,7 @@ def insert_path_to_geometry(self, lines): """ lines.insert(5, # Inserts on 6th line - 'include \"' + str(self.geo_files[0]) + '\"\n') + 'include \"' + str(self.geo_file_paths[0]) + '\"\n') return lines def read_depleted_materials(self, read_at_end=False): @@ -462,9 +462,9 @@ def switch_to_next_geometry(self): lines = f.readlines() current_geo_file = lines[geo_line_n].split('\"')[1] - current_geo_idx = self.geo_files.index(current_geo_file) + current_geo_idx = self.geo_file_paths.index(current_geo_file) try: - new_geo_file = self.geo_files[current_geo_idx + 1] + new_geo_file = self.geo_file_paths[current_geo_idx + 1] except IndexError: print('No more geometry files available \ and the system went subcritical \n\n') diff --git a/tests/conftest.py b/tests/conftest.py index 10d55a243..9c0de9208 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -41,19 +41,19 @@ def openmc_depcode(cwd): depcode = _create_depcode_object(object_input[0]) # Openmc initlialization - openmc_input_path = (cwd / 'openmc_data') + #openmc_input_path = (cwd / 'openmc_data') - openmc_runtime_inputfiles = { - "geometry": "geometry.xml", - "settings": "settings.xml", - } + #openmc_runtime_inputfiles = { + # "geometry": "geometry.xml", + # "settings": "settings.xml", + #} - for key in openmc_runtime_inputfiles: - openmc_runtime_inputfiles[key] = \ - (openmc_input_path / openmc_runtime_inputfiles[key]).as_posix() + #for key in openmc_runtime_inputfiles: + # openmc_runtime_inputfiles[key] = \ + # (openmc_input_path / openmc_runtime_inputfiles[key]).as_posix() - depcode.runtime_inputfile = openmc_runtime_inputfiles - depcode.runtime_matfile = (openmc_input_path / 'materials.xml').as_posix() + #depcode.runtime_inputfile = openmc_runtime_inputfiles + #depcode.runtime_matfile = (openmc_input_path / 'materials.xml').as_posix() return depcode diff --git a/tests/integration_tests/file_interface_openmc/test.py b/tests/integration_tests/file_interface_openmc/test.py index a9f48c0bb..80526a90e 100644 --- a/tests/integration_tests/file_interface_openmc/test.py +++ b/tests/integration_tests/file_interface_openmc/test.py @@ -18,7 +18,9 @@ def geometry_switch(scope='module'): def msr(scope='module'): reactor = Reactor(volume=1.0, power_levels=[1.250E+09, 1.250E+09, 5.550E+09], - dep_step_length_cumulative=[111.111, 2101.9, 3987.5]) + depletion_timesteps=[111.111, 2101.9, 3987.5], + timestep_type='cumulative', + timestep_units='d') return reactor @@ -27,7 +29,7 @@ def test_write_runtime_input(openmc_depcode, msr): input_materials = openmc.Materials.from_xml( openmc_depcode.template_input_file_path['materials']) input_geometry = openmc.Geometry.from_xml( - openmc_depcode.geo_files[0], + openmc_depcode.geo_file_paths[0], materials=input_materials) input_cells = input_geometry.get_all_cells() @@ -74,13 +76,13 @@ def test_write_depletion_settings(openmc_depcode, msr): Unit test for `Depcodeopenmc_depcode.write_depletion_settings` """ openmc_depcode.write_depletion_settings(msr, 0) - with open(openmc_depcode.runtime_inputfile['depletion_settings']) as f: + with open(openmc_depcode.output_path / 'depletion_settings.json') as f: j = json.load(f) - assert j['directory'] == Path( - openmc_depcode.runtime_inputfile['settings']).parents[0].as_posix() - assert j['timesteps'][0] == msr.dep_step_length_cumulative[0] + assert Path(j['directory']).resolve() == Path( + openmc_depcode.runtime_inputfile['settings']).parents[0] + assert j['timesteps'][0] == msr.depletion_timesteps[0] assert j['operator_kwargs']['chain_file'] == \ - openmc_depcode.template_input_file_path['chain_file'] + openmc_depcode.chain_file_path assert j['integrator_kwargs']['power'] == msr.power_levels[0] assert j['integrator_kwargs']['timestep_units'] == 'd' @@ -93,7 +95,7 @@ def test_write_saltproc_openmc_tallies(openmc_depcode): mat = openmc.Materials.from_xml( openmc_depcode.template_input_file_path['materials']) geo = openmc.Geometry.from_xml( - openmc_depcode.geo_files[0], mat) + openmc_depcode.geo_file_paths[0], mat) openmc_depcode.write_saltproc_openmc_tallies(mat, geo) del mat, geo tallies = openmc.Tallies.from_xml(openmc_depcode.runtime_inputfile['tallies']) @@ -131,7 +133,7 @@ def test_switch_to_next_geometry(openmc_depcode): mat = openmc.Materials.from_xml( openmc_depcode.template_input_file_path['materials']) expected_geometry = openmc.Geometry.from_xml( - openmc_depcode.geo_files[0], mat) + openmc_depcode.geo_file_paths[0], mat) expected_cells = expected_geometry.get_all_cells() expected_lattices = expected_geometry.get_all_lattices() expected_surfaces = expected_geometry.get_all_surfaces() diff --git a/tests/integration_tests/file_interface_serpent/test.py b/tests/integration_tests/file_interface_serpent/test.py index e1e0bffd0..a9991ac1a 100644 --- a/tests/integration_tests/file_interface_serpent/test.py +++ b/tests/integration_tests/file_interface_serpent/test.py @@ -18,7 +18,9 @@ def geometry_switch(scope='module'): def msr(scope='module'): reactor = Reactor(volume=1.0, power_levels=[1.250E+09, 1.250E+09, 5.550E+09], - dep_step_length_cumulative=[111.111, 2101.9, 3987.5]) + depletion_timesteps=[111.111, 2101.9, 3987.5], + timestep_type='cumulative', + timestep_units='d') return reactor @@ -39,7 +41,7 @@ def test_runtime_input_from_template(serpent_depcode, msr): remove(serpent_depcode.runtime_matfile) # set_power_load - time = msr.dep_step_length_cumulative.copy() + time = msr.depletion_timesteps.copy() time.insert(0, 0.0) depsteps = np.diff(time) for idx in range(len(msr.power_levels)): @@ -83,7 +85,7 @@ def test_write_runtime_files(serpent_depcode, msr): assert file_data[20] == 'set pop 50 20 20\n' # switch_to_next_geometry - serpent_depcode.geo_files += ['../../examples/406.inp', + serpent_depcode.geo_file_paths += ['../../examples/406.inp', '../../examples/988.inp'] serpent_depcode.switch_to_next_geometry() file_data = serpent_depcode.read_plaintext_file(file) diff --git a/tests/integration_tests/run_constant_reprocessing/tap_input.json b/tests/integration_tests/run_constant_reprocessing/tap_input.json index 2a55678c1..1098d66fb 100644 --- a/tests/integration_tests/run_constant_reprocessing/tap_input.json +++ b/tests/integration_tests/run_constant_reprocessing/tap_input.json @@ -2,7 +2,7 @@ "proc_input_file": "tap_processes.json", "dot_input_file": "../../tap_paths.dot", "output_path": ".", - "n_depletion_timesteps": 2, + "n_depletion_steps": 2, "depcode": { "codename": "serpent", "template_input_file_path": "tap_template.ini", diff --git a/tests/integration_tests/run_no_reprocessing/test.py b/tests/integration_tests/run_no_reprocessing/test.py index fbc1c8f15..9f7f9317d 100644 --- a/tests/integration_tests/run_no_reprocessing/test.py +++ b/tests/integration_tests/run_no_reprocessing/test.py @@ -26,7 +26,7 @@ def setup(): simulation = app._create_simulation_object(object_input[1], depcode, 1, 1) - reactor = app._create_reactor_object(object_input[2]) + reactor = app._create_reactor_object(object_input[2], object_input[0]['codename']) return cwd, simulation, reactor, sss_file diff --git a/tests/integration_tests/run_no_reprocessing/test_input.json b/tests/integration_tests/run_no_reprocessing/test_input.json index 95e44a08f..9fbac7b9f 100644 --- a/tests/integration_tests/run_no_reprocessing/test_input.json +++ b/tests/integration_tests/run_no_reprocessing/test_input.json @@ -2,7 +2,7 @@ "proc_input_file": "processes.json", "dot_input_file": "paths.dot", "output_path": ".", - "n_depletion_timesteps": 2, + "n_depletion_steps": 2, "depcode": { "codename": "serpent", "template_input_file_path": "test_input.ini", diff --git a/tests/openmc_data/tap_input.json b/tests/openmc_data/tap_input.json index 194fe759d..843773787 100644 --- a/tests/openmc_data/tap_input.json +++ b/tests/openmc_data/tap_input.json @@ -2,20 +2,15 @@ "proc_input_file": "../tap_processes.json", "dot_input_file": "../tap_paths.dot", "output_path": "../temp_data", - "n_depletion_timesteps": 2, + "n_depletion_steps": 2, "depcode": { "codename": "openmc", - "exec_path": "openmc", "template_input_file_path": { "materials": "tap_materials.xml", "settings": "tap_settings.xml" }, "geo_file_paths": ["tap_geometry_base.xml"], - "depletion_settings": { - "times_kwargs": { - "chain_file": "main.tmp" - } - } + "chain_file_path": "test_chain.xml" }, "simulation": { "sim_name": "tap_test_simulation_openmc", diff --git a/tests/serpent_data/tap_input.json b/tests/serpent_data/tap_input.json index 48457a818..e5f40fb6d 100644 --- a/tests/serpent_data/tap_input.json +++ b/tests/serpent_data/tap_input.json @@ -2,7 +2,7 @@ "proc_input_file": "../tap_processes.json", "dot_input_file": "../tap_paths.dot", "output_path": "../temp_data", - "n_depletion_timesteps": 2, + "n_depletion_steps": 2, "depcode": { "codename": "serpent", "exec_path": "sss2", diff --git a/tests/unit_tests/test_app.py b/tests/unit_tests/test_app.py index ab4a5bcf2..c70552a81 100644 --- a/tests/unit_tests/test_app.py +++ b/tests/unit_tests/test_app.py @@ -31,7 +31,7 @@ def test_read_main_input(cwd, codename, ext): np.testing.assert_equal( reactor_input['power_levels'], [ 1.250E+9, 1.250E+9]) - np.testing.assert_equal(reactor_input['dep_step_length_cumulative'], + np.testing.assert_equal(reactor_input['depletion_timesteps'], [5, 10]) From 91b1e69191f495ba03141dd6f1a74926c418fa3b Mon Sep 17 00:00:00 2001 From: yardasol Date: Wed, 30 Nov 2022 14:09:56 -0600 Subject: [PATCH 03/23] move default schema code to new file; fix missing param in Reactor --- saltproc/_schema_default.py | 21 +++++++++++++++++++++ saltproc/app.py | 26 ++++++-------------------- saltproc/reactor.py | 1 + saltproc/serpent_depcode.py | 4 ++-- 4 files changed, 30 insertions(+), 22 deletions(-) create mode 100644 saltproc/_schema_default.py diff --git a/saltproc/_schema_default.py b/saltproc/_schema_default.py new file mode 100644 index 000000000..e91346a68 --- /dev/null +++ b/saltproc/_schema_default.py @@ -0,0 +1,21 @@ +from jsonschema import Draft202012Validator, validators + +def extend_with_default(validator_class): + validate_properties = validator_class.VALIDATORS["properties"] + + def set_defaults(validator, properties, instance, schema): + for property, subschema in properties.items(): + if "default" in subschema: + instance.setdefault(property, subschema["default"]) + + for error in validate_properties( + validator, properties, instance, schema, + ): + yield error + + return validators.extend( + validator_class, {"properties" : set_defaults}, + ) + + +DefaultValidatingValidator = extend_with_default(Draft202012Validator) diff --git a/saltproc/app.py b/saltproc/app.py index e1cd73865..db47ffa69 100644 --- a/saltproc/app.py +++ b/saltproc/app.py @@ -6,13 +6,13 @@ import numpy as np import json import jsonschema -from jsonschema import Draft202012Validator, validators import gc import networkx as nx import pydotplus from saltproc import SerpentDepcode, OpenMCDepcode, Simulation, Reactor from saltproc import Process, Sparger, Separator, Materialflow +from ._schema_default import DefaultValidatingValidator def run(): @@ -152,17 +152,18 @@ def read_main_input(main_inp_file): input_schema = (Path(__file__).parents[0] / 'input_schema.json') with open(main_inp_file) as f: - j = json.load(f) - DefaultValidatingValidator = extend_with_default(Draft202012Validator) + obj = json.load(f) with open(input_schema) as s: - v = json.load(s) + schema = json.load(s) try: - DefaultValidatingValidator(v).validate(j) + DefaultValidatingValidator(schema).validate(obj) #jsonschema.validate(instance=j, schema=v) except jsonschema.exceptions.ValidationError: print("Your input file is improperly structured.\ Please see saltproc/tests/test.json for an example.") + j = obj + print(j['reactor']['timestep_type']) # Global input path input_path = (Path.cwd() / Path(f.name).parents[0]) @@ -220,21 +221,6 @@ def read_main_input(main_inp_file): return input_path, process_file, dot_file, ( depcode_input, simulation_input, reactor_input) -def extend_with_default(validator_class): - validate_properties = validator_class.VALIDATORS["properties"] - def set_defaults(validator, properties, instance, schema): - for property, subschema in properties.items(): - if "default" in subschema: - instance.setdefault(property, subschema["default"]) - for error in validate_properties( - validator, properties, instance, schema, - ): - yield error - return validators.extend( - validator_class, {"properties" : set_defaults}, - ) - - def _print_simulation_input_info(simulation_input, depcode_input): """Helper function for `run()` """ print('Initiating Saltproc:\n' diff --git a/saltproc/reactor.py b/saltproc/reactor.py index 20a36c704..c655ec037 100644 --- a/saltproc/reactor.py +++ b/saltproc/reactor.py @@ -34,3 +34,4 @@ def __init__(self, self.power_levels = power_levels self.depletion_timesteps = depletion_timesteps self.timestep_units = timestep_units + self.timestep_type = timestep_type diff --git a/saltproc/serpent_depcode.py b/saltproc/serpent_depcode.py index 03e96390b..5582332ca 100644 --- a/saltproc/serpent_depcode.py +++ b/saltproc/serpent_depcode.py @@ -382,12 +382,12 @@ def set_power_load(self, line_idx = file_lines.index(line) del file_lines[line_idx] - if reactor.timestep_unitss == 'MWd/kg': + if reactor.timestep_units == 'MWd/kg': step_type = 'bu' else: step_type = 'day' - if reactor.step_type == 'cumulative': + if reactor.timestep_type == 'cumulative': step_type += 'tot' else: step_type += 'step' From c663d58767c7c4489c1c02dff9a13a35ed77a27b Mon Sep 17 00:00:00 2001 From: yardasol Date: Wed, 30 Nov 2022 14:10:34 -0600 Subject: [PATCH 04/23] fix input schema --- saltproc/input_schema.json | 169 +++++++++++++++++-------------------- 1 file changed, 78 insertions(+), 91 deletions(-) diff --git a/saltproc/input_schema.json b/saltproc/input_schema.json index e573fa326..91f66e596 100644 --- a/saltproc/input_schema.json +++ b/saltproc/input_schema.json @@ -27,38 +27,18 @@ "depcode": { "description": "Depcode class input parameters", "type": "object", - "properties": { - "codename": { - "description": "Name of depletion code", - "type": "string", - "enum": ["serpent", "openmc"]}, - "exec_path": { - "description": "Path to depletion code executable", - "type": "string" }, - "template_input_file_path": { - "description": "Path(s) to user's template depletion code input file(s) with reactor model"}, - "geo_file_paths": { - "description": "Path(s) to geometry file(s) to switch to in depletion code runs", - "type": "array", - "items": { "type": "string"}, - "minItems": 1, - "uniqueItems": true} - }, + "default": {}, "allOf": [ { "if": {"properties": { "codename": { "const": "serpent" }}}, "then": { "properties": { "exec_path": { - "description": "Path to Serpent executable", - "type": "string", "default": "sss2"}, "template_input_file_path": { - "description": "Path to Serpent template inputfile", - "type": "string", "pattern": "^(.\\/)*(.*)$"} } - } + } }, { "if": { @@ -67,12 +47,12 @@ "properties": { "exec_path": { "description": "Path to OpenMC depletion script", - "type": "string", - "enum": ["openmc_deplete.py"], + "const": "openmc_deplete.py", "default": "openmc_deplete.py"}, "template_input_file_path": { "description": "Paths to OpenMC template input files", "type": "object", + "required": ["settings", "materials"], "properties": { "settings": { "description": "OpenMC settings file", @@ -84,16 +64,11 @@ "type": "string", "pattern": "^(.\\/)*(.*)\\.xml$", "default": "materials.xml"} - }, - "required": ["settings", "materials"] + } }, "geo_file_paths": { - "description": "Path(s) to geometry file(s) to switch to in OpenMC code runs", - "type": "array", "items": { "type": "string", "pattern": "^(.\\/)*(.*)\\.xml$"}, - "minItems": 1, - "uniqueItems": true, "default": ["geometry.xml"]}, "chain_file_path": { "description": "Path to depletion chain file", @@ -102,6 +77,7 @@ "depletion_settings" : { "description": "OpenMC depletion settings", "type": "object", + "default": {}, "properties": { "method": { "description": "Integration method used for depletion", @@ -115,51 +91,6 @@ "operator_kwargs": { "description": "Keyword arguments passed to the depletion operator initalizer", "type": "object", - "properties": { - "diff_burnable_mats": { - "description": "Whether to differentiate burnable materials with multiple instances.", - "type": "boolean", - "defualt": false}, - "normalization_mode": { - "description": "Indicate how tally resutls should be normalized", - "type": "string", - "enum": ["energy-deposition", "fission-q", "source-rate"], - "default": "fission-q"}, - "fission_q": { - "description": "Path to fission Q values", - "type": "string", - "default": null}, - "dilute_initial": { - "description": "Initial atom density to add for nuclides that are zero in initial condition.", - "type": "number", - "minimum": 0, - "default": 1000}, - "fission_yield_mode": { - "description": "Determine what fission energy helper is used", - "type": "string", - "enum": ["constant", "cutoff", "average"], - "default": "constant"}, - "fission_yield_opts": { - "description": "Arguments for the fission yield helper", - "type": "object", - "defualt": null}, - "reaction_rate_mode": { - "description": "Indicate how one-group reaction rates should be calculated", - "type": "string", - "enum": ["direct", "flux"], - "default": "direct"}, - "reaction_rate_opts": { - "type": "object", - "default": null}, - "reduce_chain": { - "description": "Whether or not to reduce the depletion chain.", - "type": "boolean", - "default": false}, - "reduce_chain_level": { - "description": "Depth of serach while reducing depletion chain", - "type": "number", - "default": null} - }, "allOf": [ { "if": {"properties": { "fission_yield_mode": { "const": "constant" }}}, @@ -171,8 +102,7 @@ "energy": { "description": "Energy of fission yield libraries [MeV]", "type": "number"} - }, - "default": null + } } } } @@ -191,10 +121,9 @@ "description": "Energy of yield data corresponding to thermal yields", "type": "number"}, "fast_energy": { - "description": "Energy of yield data corresponding to fast yields", - "type": "number"} - }, - "default": null + "description": "Energy of yield data corresponding to fast yields", + "type": "number"} + } } } } @@ -227,13 +156,53 @@ "type": "string", "minItems": 1} } - }, - "default": null + } } } } } ], + "properties": { + "diff_burnable_mats": { + "description": "Whether to differentiate burnable materials with multiple instances.", + "type": "boolean", + "defualt": false}, + "normalization_mode": { + "description": "Indicate how tally resutls should be normalized", + "type": "string", + "enum": ["energy-deposition", "fission-q", "source-rate"], + "default": "fission-q"}, + "fission_q": { + "description": "Path to fission Q values", + "default": null}, + "dilute_initial": { + "description": "Initial atom density to add for nuclides that are zero in initial condition.", + "type": "number", + "minimum": 0, + "default": 1000}, + "fission_yield_mode": { + "description": "Determine what fission energy helper is used", + "type": "string", + "enum": ["constant", "cutoff", "average"], + "default": "constant"}, + "fission_yield_opts": { + "description": "Arguments for the fission yield helper", + "defualt": null}, + "reaction_rate_mode": { + "description": "Indicate how one-group reaction rates should be calculated", + "type": "string", + "enum": ["direct", "flux"], + "default": "direct"}, + "reaction_rate_opts": { + "default": null}, + "reduce_chain": { + "description": "Whether or not to reduce the depletion chain.", + "type": "boolean", + "default": false}, + "reduce_chain_level": { + "description": "Depth of serach while reducing depletion chain", + "default": null} + }, "default": {} }, "output": { @@ -255,15 +224,30 @@ }, "default": {} } - }, - "default": {} + } } } } } ], - "default": {}, - "required": ["codename", "exec_path", "template_input_file_path", "geo_file_paths", "chain_file_path", "depletion_settings"] + "properties": { + "codename": { + "description": "Name of depletion code", + "type": "string", + "enum": ["serpent", "openmc"]}, + "exec_path": { + "description": "Path to depletion code executable", + "type": "string"}, + "template_input_file_path": { + "description": "Path(s) to user's template depletion code input file(s) with reactor model"}, + "geo_file_paths": { + "description": "Path(s) to geometry file(s) to switch to in depletion code runs", + "type": "array", + "items": { "type": "string"}, + "minItems": 1, + "uniqueItems": true} + }, + "required": ["codename", "template_input_file_path", "geo_file_paths"] }, "simulation": { "description": "Simulation class input parameters", @@ -278,12 +262,15 @@ "pattern": "^(.*)\\.h5$"}, "restart_flag": { "description": "Restart simulation from the step when it stopped?", - "type": "boolean"}, + "type": "boolean", + "default": false}, "adjust_geo": { "description": "switch to another geometry when keff drops below 1?", - "type": "boolean"} + "type": "boolean", + "default": false} }, - "required": ["sim_name", "db_name", "restart_flag", "adjust_geo"] + "default": {}, + "required": ["sim_name", "db_name"] }, "reactor": { "description": "Reactor class input parameters", @@ -321,7 +308,7 @@ } }, "default": {}, - "required": ["volume", "mass_flowrate", "power_levels", "depletion_steps", "timestep_type", "timestep_units"] + "required": ["volume", "mass_flowrate", "power_levels", "depletion_timesteps", "timestep_units"] } }, "required": ["proc_input_file", "dot_input_file", "output_path", "depcode", "simulation", "reactor"] From 6ec136264234a39a61ef7ad64a7fa08372db8a00 Mon Sep 17 00:00:00 2001 From: yardasol Date: Wed, 30 Nov 2022 14:10:58 -0600 Subject: [PATCH 05/23] fix file_interface_serpent integration test --- tests/integration_tests/file_interface_serpent/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration_tests/file_interface_serpent/test.py b/tests/integration_tests/file_interface_serpent/test.py index a9991ac1a..03224d315 100644 --- a/tests/integration_tests/file_interface_serpent/test.py +++ b/tests/integration_tests/file_interface_serpent/test.py @@ -19,7 +19,7 @@ def msr(scope='module'): reactor = Reactor(volume=1.0, power_levels=[1.250E+09, 1.250E+09, 5.550E+09], depletion_timesteps=[111.111, 2101.9, 3987.5], - timestep_type='cumulative', + timestep_type='stepwise', timestep_units='d') return reactor From a85465496330006372c0c55b1336b65ec5dd9014 Mon Sep 17 00:00:00 2001 From: yardasol Date: Wed, 7 Dec 2022 12:24:31 -0600 Subject: [PATCH 06/23] add reactor fixutres; cleanup timestep handling; fix failing integration test --- saltproc/app.py | 65 ++++++++++--------- saltproc/serpent_depcode.py | 8 +-- tests/conftest.py | 20 +++++- .../file_interface_serpent/test.py | 38 +++++------ 4 files changed, 75 insertions(+), 56 deletions(-) diff --git a/saltproc/app.py b/saltproc/app.py index 6edca68f6..77b928cfd 100644 --- a/saltproc/app.py +++ b/saltproc/app.py @@ -25,7 +25,7 @@ def run(): depcode = _create_depcode_object(object_input[0]) simulation = _create_simulation_object( object_input[1], depcode, cores, nodes) - msr = _create_reactor_object(object_input[2], object_input[0]['codename']) + msr = _create_reactor_object(object_input[2]) # Check: Restarting previous simulation or starting new? simulation.check_restart() @@ -216,7 +216,7 @@ def read_main_input(main_inp_file): simulation_input['db_name'] = str(db_name.resolve()) reactor_input = _process_main_input_reactor_params( - reactor_input, n_depletion_steps) + reactor_input, n_depletion_steps, depcode_input['codename']) return input_path, process_file, dot_file, ( depcode_input, simulation_input, reactor_input) @@ -272,32 +272,13 @@ def _create_simulation_object(simulation_input, depcode, cores, nodes): return simulation -def _create_reactor_object(reactor_input, codename): +def _create_reactor_object(reactor_input): """Helper function for `run()` """ - timesteps = reactor_input['depletion_timesteps'] - timestep_type = reactor_input['timestep_type'] - if timestep_type == 'cumulative' and codename == 'openmc': - ts = list(np.diff(depletion_timesteps)) - reactor_input['depletion_timesteps'] = depletion_timesteps[0] + ts - - timestep_units = reactor_input['time_unit'] - if not(timestep_units in ('d', 'day')) and time_units.lower() != 'mwd/kg' and codename == 'serpent': - if timestep_units in ('s', 'sec'): - reactor_input['depletion_timesteps'] /= 60 * 60 * 24 - elif timestep_units in ('min', 'minute'): - reactor_input['depletion_timesteps'] /= 60 * 24 - elif timestep_units in ('h', 'hr', 'hour'): - reactor_input['depletion_timesteps'] /= 24 - elif timestep_units in ('a', 'year'): - reactor_input['depletion_timesteps'] *= 365.25 - else: - raise IOError(f'Unrecognized time unit: {timestep_units}') - msr = Reactor(**reactor_input) return msr -def _process_main_input_reactor_params(reactor_input, n_depletion_steps): +def _process_main_input_reactor_params(reactor_input, n_depletion_steps, codename): """ Process SaltProc reactor class input parameters based on the value and data type of the `n_depletion_steps` parameter, and throw errors if the input @@ -312,23 +293,43 @@ def _process_main_input_reactor_params(reactor_input, n_depletion_steps): # lists of length `n_depletion_steps` else: step_length = float(depletion_timesteps[0]) - if reactor_input['timestep_type'] == 'stepwise': - depletion_timesteps = [step_length] * n_depletion_steps - else: - depletion_timesteps = \ - np.linspace(step_length, - n_depletion_steps*step_length, - n_depletion_steps) + #if reactor_input['timestep_type'] == 'stepwise': + # depletion_timesteps = [step_length] * n_depletion_steps + depletion_timesteps = [step_length] * n_depletion_steps + #else: + # depletion_timesteps = \ + # np.linspace(step_length, + # n_depletion_steps*step_length, + # n_depletion_steps) + reactor_input['depletion_timesteps'] = depletion_timesteps power_levels = float(power_levels[0]) * \ np.ones_like(depletion_timesteps) - reactor_input['depletion_timesteps'] = \ - depletion_timesteps reactor_input['power_levels'] = power_levels elif n_depletion_steps is None and isinstance(depletion_timesteps, (np.ndarray, list)): if len(depletion_timesteps) != len(power_levels): raise ValueError( 'Depletion step list and power list shape mismatch') + else: + timesteps = reactor_input['depletion_timesteps'] + timestep_type = reactor_input['timestep_type'] + if timestep_type == 'cumulative': + ts = list(np.diff(depletion_timesteps)) + reactor_input['depletion_timesteps'] = depletion_timesteps[0] + ts + + timestep_units = reactor_input['time_unit'] + if not(timestep_units in ('d', 'day')) and time_units.lower() != 'mwd/kg' and codename == 'serpent': + if timestep_units in ('s', 'sec'): + reactor_input['depletion_timesteps'] /= 60 * 60 * 24 + elif timestep_units in ('min', 'minute'): + reactor_input['depletion_timesteps'] /= 60 * 24 + elif timestep_units in ('h', 'hr', 'hour'): + reactor_input['depletion_timesteps'] /= 24 + elif timestep_units in ('a', 'year'): + reactor_input['depletion_timesteps'] *= 365.25 + else: + raise IOError(f'Unrecognized time unit: {timestep_units}') + return reactor_input diff --git a/saltproc/serpent_depcode.py b/saltproc/serpent_depcode.py index eb757f550..232171cf1 100644 --- a/saltproc/serpent_depcode.py +++ b/saltproc/serpent_depcode.py @@ -387,10 +387,10 @@ def set_power_load(self, else: step_type = 'day' - if reactor.timestep_type == 'cumulative': - step_type += 'tot' - else: - step_type += 'step' + #if reactor.timestep_type == 'cumulative': + # step_type += 'tot' + #else: + step_type += 'step' file_lines.insert(line_idx, # Insert on 9th line f'set power %5.9E dep %s %7.5E\n' % diff --git a/tests/conftest.py b/tests/conftest.py index 9d27c570e..ecd251679 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,7 @@ from pathlib import Path import pytest -from saltproc.app import read_main_input, _create_depcode_object +from saltproc.app import read_main_input, _create_depcode_object, _create_reactor_object from saltproc import Simulation @@ -33,6 +33,15 @@ def serpent_depcode(cwd): return depcode +@pytest.fixture(scope='session') +def serpent_reactor(cwd): + saltproc_input = str(cwd / 'serpent_data' / 'tap_input.json') + _, _, _, object_input = read_main_input(saltproc_input) + reactor = _create_reactor_object(object_input[2]) + + return reactor + + @pytest.fixture(scope='session') def openmc_depcode(cwd): """OpenMCDepcode object for unit tests""" @@ -43,6 +52,15 @@ def openmc_depcode(cwd): return depcode +@pytest.fixture(scope='session') +def openmc_reactor(cwd): + openmc_input = str(cwd / 'openmc_data' / 'tap_input.json') + _, _, _, object_input = read_main_input(openmc_input) + reactor = _create_reactor_object(object_input[2]) + + return reactor + + @pytest.fixture(scope='session') def simulation(cwd, serpent_depcode): """Simulation object for unit tests""" diff --git a/tests/integration_tests/file_interface_serpent/test.py b/tests/integration_tests/file_interface_serpent/test.py index 03224d315..8440a3486 100644 --- a/tests/integration_tests/file_interface_serpent/test.py +++ b/tests/integration_tests/file_interface_serpent/test.py @@ -14,17 +14,17 @@ def geometry_switch(scope='module'): return (path / 'serpent_data' / 'tap_geometry_switch.ini') -@pytest.fixture -def msr(scope='module'): - reactor = Reactor(volume=1.0, - power_levels=[1.250E+09, 1.250E+09, 5.550E+09], - depletion_timesteps=[111.111, 2101.9, 3987.5], - timestep_type='stepwise', - timestep_units='d') - return reactor +#@pytest.fixture +#def msr(scope='module'): +# reactor = Reactor(volume=1.0, +# power_levels=[1.250E+09, 1.250E+09, 5.550E+09], +# depletion_timesteps=[111.111, 2101.9, 3987.5], +# timestep_type='stepwise', +# timestep_units='d') +# return reactor -def test_runtime_input_from_template(serpent_depcode, msr): +def test_runtime_input_from_template(serpent_depcode, serpent_reactor): file = serpent_depcode.template_input_file_path file_data = serpent_depcode.read_plaintext_file(file) @@ -41,18 +41,18 @@ def test_runtime_input_from_template(serpent_depcode, msr): remove(serpent_depcode.runtime_matfile) # set_power_load - time = msr.depletion_timesteps.copy() - time.insert(0, 0.0) - depsteps = np.diff(time) - for idx in range(len(msr.power_levels)): - file_data = serpent_depcode.set_power_load(file_data, msr, idx) + #time = msr.depletion_timesteps.copy() + #time.insert(0, 0.0) + #depsteps = np.diff(time) + for idx in range(len(serpent_reactor.power_levels)): + file_data = serpent_depcode.set_power_load(file_data, serpent_reactor, idx) assert file_data[8].split()[4] == 'daystep' - assert file_data[8].split()[2] == str("%5.9E" % msr.power_levels[idx]) - assert file_data[8].split()[5] == str("%7.5E" % depsteps[idx]) + assert file_data[8].split()[2] == str("%5.9E" % serpent_reactor.power_levels[idx]) + assert file_data[8].split()[5] == str("%7.5E" % serpent_reactor.depletion_timesteps[idx]) -def test_write_runtime_files(serpent_depcode, msr): +def test_write_runtime_files(serpent_depcode, serpent_reactor): mats = serpent_depcode.read_depleted_materials(True) # update_depletable_materials @@ -72,7 +72,7 @@ def test_write_runtime_files(serpent_depcode, msr): remove(serpent_depcode.runtime_matfile) # write_runtime_input - serpent_depcode.write_runtime_input(msr, + serpent_depcode.write_runtime_input(serpent_reactor, 0, False) @@ -81,7 +81,7 @@ def test_write_runtime_files(serpent_depcode, msr): assert file_data[0] == f'include "{serpent_depcode.runtime_matfile}"\n' assert file_data[8].split()[2] == '1.250000000E+09' assert file_data[8].split()[4] == 'daystep' - assert file_data[8].split()[-1] == '1.11111E+02' + assert file_data[8].split()[-1] == '5.00000E+00' assert file_data[20] == 'set pop 50 20 20\n' # switch_to_next_geometry From 68b210e7621ca0b32defef65a3e1153971d54827 Mon Sep 17 00:00:00 2001 From: yardasol Date: Wed, 7 Dec 2022 12:26:12 -0600 Subject: [PATCH 07/23] move lowercasing 'codename' to its first needed case --- saltproc/app.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/saltproc/app.py b/saltproc/app.py index 77b928cfd..6c26b9ae4 100644 --- a/saltproc/app.py +++ b/saltproc/app.py @@ -185,6 +185,7 @@ def read_main_input(main_inp_file): simulation_input = j['simulation'] reactor_input = j['reactor'] + depcode_input['codename'] = depcode_input['codename'].lower() if depcode_input['codename'] == 'serpent': depcode_input['template_input_file_path'] = str(( input_path / @@ -199,8 +200,8 @@ def read_main_input(main_inp_file): depcode_input['chain_file_path']).resolve()) else: raise ValueError( - f'{depcode_input["codename"]} ' - 'is not a supported depletion code') + f'{codename} is not a supported depletion code.' + Accepts: "serpent" or "openmc".) depcode_input['output_path'] = output_path geo_list = depcode_input['geo_file_paths'] @@ -237,7 +238,7 @@ def _print_simulation_input_info(simulation_input, depcode_input): def _create_depcode_object(depcode_input): """Helper function for `run()` """ - codename = depcode_input.pop('codename').lower() + codename = depcode_input.pop('codename') if codename == 'serpent': depcode = SerpentDepcode elif codename == 'openmc': From ee6210b02fd11868ae5c7213bf845c0888370e3b Mon Sep 17 00:00:00 2001 From: yardasol Date: Wed, 7 Dec 2022 13:52:58 -0600 Subject: [PATCH 08/23] update releasenotes --- doc/releasenotes/v0.5.0.rst | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/doc/releasenotes/v0.5.0.rst b/doc/releasenotes/v0.5.0.rst index b4e995d75..3f0e4ddab 100644 --- a/doc/releasenotes/v0.5.0.rst +++ b/doc/releasenotes/v0.5.0.rst @@ -97,18 +97,25 @@ Python API Changes - Input file format changes: + - Added depletion settings for OpenMC + - ``num_depsteps`` → ``n_depletion_steps`` - ``depcode['template_inputfile_path']`` → ``depcode['template_input_file_path']`` - The ``depcode['template_input_file_path']`` input variable now has the following depletion-code-depentent types: - ``str`` for ``Serpent2`` - ``dict`` of ``str`` to ``str`` for ``openmc`` - - ``depcode['iter_inputfile']`` → (removed) - ``depcode['iter_matfile']`` → (removed) + - ``depcode['npop']`` → (removed) + - ``depcode['active_cycles']`` → (removed) + - ``depcode['inactive_cycles']`` → (removed) + - ``reactor['dep_step_length_cumulative']`` → ``depletion_timesteps`` + - (new) → ``reactor['timestep_type']`` + - (new) → ``reactor['timestep_units']`` -.. note:: While the ``iter_inputfile`` and ``iter_matfile`` variables have been removed from the saltproc input file, - they remain part of the ``DepcodeSerpent`` class as attributes. Their use is unchanged, save for the fact they can no - longer be initialized by the user during object creation. +.. note:: Variables that have been removed from the saltproc input file + remain part of the ``Depcode`` classes as attributes. Their use is unchanged, save for the fact they can no + longer be initialized by the user from the SaltProc input file. - New/changed classes, methods, and attributes: @@ -130,6 +137,7 @@ Python API Changes - ``write_depcode_input()`` → ``write_runtime_input()`` - ``iter_inputfile`` → ``runtime_inputfile`` - ``iter_matfile`` → ``runtime_matfile`` + - ``geo_files`` → ``geo_file_paths`` - ``DepcodeSerpent`` → ``SerpentDepcode`` @@ -155,6 +163,7 @@ Python API Changes - ``write_depcode_input()`` → ``write_runtime_input()`` - ``iter_inputfile`` → ``runtime_inputfile`` - ``iter_matfile`` → ``runtime_matfile`` + - ``geo_files`` → ``geo_file_paths`` - ``OpenMCDepcode`` is a ``Depcode`` subclass that interfaces with ``openmc``. This class implements the following functions @@ -177,6 +186,12 @@ Python API Changes - ``calc_rem_efficiency()`` → ``calculate_removal_efficiency`` + - ``Reactor`` + + - ``dep_step_length_cumulative`` → ``depletion_timesteps`` + - (new) → ``timestep_units`` + - (new) → ``timestep_type`` + - ``Sparger`` - ``calc_rem_efficiency()`` → ``calculate_removal_efficiency`` From a1b2447b5ac61f1112e0c133aba68d84703f9d37 Mon Sep 17 00:00:00 2001 From: yardasol Date: Wed, 7 Dec 2022 14:37:38 -0600 Subject: [PATCH 09/23] remove cruft comments --- saltproc/serpent_depcode.py | 5 +---- .../file_interface_serpent/test.py | 13 ------------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/saltproc/serpent_depcode.py b/saltproc/serpent_depcode.py index 232171cf1..0aca6a303 100644 --- a/saltproc/serpent_depcode.py +++ b/saltproc/serpent_depcode.py @@ -387,12 +387,9 @@ def set_power_load(self, else: step_type = 'day' - #if reactor.timestep_type == 'cumulative': - # step_type += 'tot' - #else: step_type += 'step' - file_lines.insert(line_idx, # Insert on 9th line + file_lines.insert(line_idx, f'set power %5.9E dep %s %7.5E\n' % (current_power, step_type, step_length)) return file_lines diff --git a/tests/integration_tests/file_interface_serpent/test.py b/tests/integration_tests/file_interface_serpent/test.py index 8440a3486..805b65555 100644 --- a/tests/integration_tests/file_interface_serpent/test.py +++ b/tests/integration_tests/file_interface_serpent/test.py @@ -14,16 +14,6 @@ def geometry_switch(scope='module'): return (path / 'serpent_data' / 'tap_geometry_switch.ini') -#@pytest.fixture -#def msr(scope='module'): -# reactor = Reactor(volume=1.0, -# power_levels=[1.250E+09, 1.250E+09, 5.550E+09], -# depletion_timesteps=[111.111, 2101.9, 3987.5], -# timestep_type='stepwise', -# timestep_units='d') -# return reactor - - def test_runtime_input_from_template(serpent_depcode, serpent_reactor): file = serpent_depcode.template_input_file_path file_data = serpent_depcode.read_plaintext_file(file) @@ -41,9 +31,6 @@ def test_runtime_input_from_template(serpent_depcode, serpent_reactor): remove(serpent_depcode.runtime_matfile) # set_power_load - #time = msr.depletion_timesteps.copy() - #time.insert(0, 0.0) - #depsteps = np.diff(time) for idx in range(len(serpent_reactor.power_levels)): file_data = serpent_depcode.set_power_load(file_data, serpent_reactor, idx) From dd6c8ccc4b3f7606e3f5f4a507d50d9a0d866bfd Mon Sep 17 00:00:00 2001 From: yardasol Date: Wed, 7 Dec 2022 15:05:14 -0600 Subject: [PATCH 10/23] cleanup _process_main_input_reactor_params() --- saltproc/app.py | 80 +++++++++++++++++++++++-------------------------- 1 file changed, 37 insertions(+), 43 deletions(-) diff --git a/saltproc/app.py b/saltproc/app.py index 6c26b9ae4..8d9098a87 100644 --- a/saltproc/app.py +++ b/saltproc/app.py @@ -201,7 +201,7 @@ def read_main_input(main_inp_file): else: raise ValueError( f'{codename} is not a supported depletion code.' - Accepts: "serpent" or "openmc".) + 'Accepts: "serpent" or "openmc".') depcode_input['output_path'] = output_path geo_list = depcode_input['geo_file_paths'] @@ -285,56 +285,50 @@ def _process_main_input_reactor_params(reactor_input, n_depletion_steps, codenam data type of the `n_depletion_steps` parameter, and throw errors if the input parameters are incorrect. """ + depletion_timesteps = reactor_input['depletion_timesteps'] power_levels = reactor_input['power_levels'] - if n_depletion_steps is not None and len(depletion_timesteps) == 1: + + if n_depletion_steps is not None: if n_depletion_steps < 0.0 or not int: - raise ValueError('Depletion step interval cannot be negative') - # Make `power_levels` and `depletion_timesteps` - # lists of length `n_depletion_steps` + raise ValueError('There must be a positive integer number' + ' of depletion steps. Provided' + f' n_depletion_steps: {n_depletion_steps}') else: - step_length = float(depletion_timesteps[0]) - #if reactor_input['timestep_type'] == 'stepwise': - # depletion_timesteps = [step_length] * n_depletion_steps - depletion_timesteps = [step_length] * n_depletion_steps - #else: - # depletion_timesteps = \ - # np.linspace(step_length, - # n_depletion_steps*step_length, - # n_depletion_steps) - reactor_input['depletion_timesteps'] = depletion_timesteps - power_levels = float(power_levels[0]) * \ - np.ones_like(depletion_timesteps) - reactor_input['power_levels'] = power_levels - elif n_depletion_steps is None and isinstance(depletion_timesteps, - (np.ndarray, list)): - if len(depletion_timesteps) != len(power_levels): - raise ValueError( - 'Depletion step list and power list shape mismatch') - else: - timesteps = reactor_input['depletion_timesteps'] - timestep_type = reactor_input['timestep_type'] - if timestep_type == 'cumulative': - ts = list(np.diff(depletion_timesteps)) - reactor_input['depletion_timesteps'] = depletion_timesteps[0] + ts - - timestep_units = reactor_input['time_unit'] - if not(timestep_units in ('d', 'day')) and time_units.lower() != 'mwd/kg' and codename == 'serpent': - if timestep_units in ('s', 'sec'): - reactor_input['depletion_timesteps'] /= 60 * 60 * 24 - elif timestep_units in ('min', 'minute'): - reactor_input['depletion_timesteps'] /= 60 * 24 - elif timestep_units in ('h', 'hr', 'hour'): - reactor_input['depletion_timesteps'] /= 24 - elif timestep_units in ('a', 'year'): - reactor_input['depletion_timesteps'] *= 365.25 - else: - raise IOError(f'Unrecognized time unit: {timestep_units}') + if len(depletion_timesteps) == 1: + depletion_timesteps = depletion_timesteps * n_depletion_steps + if len(power_levels) == 1: + power_levels = power_levels * n_depletion_steps + + if len(depletion_timesteps) != len(power_levels): + raise ValueError('depletion_timesteps and power_levels length mismatch:' + f' depletion_timesteps has {len(depletion_timesteps)}' + f' entries and power_levels has {len(power_levels)}' + ' entries.') + + if reactor_input['timestep_type'] == 'cumulative': + ts = list(np.diff(depletion_timesteps)) + depletion_timesteps = depletion_timesteps[0] + ts + + timestep_units = reactor_input['timestep_units'] + # serpent base timestep units are days or mwd/kg + if not(timestep_units in ('d', 'day')) and time_units.lower() != 'mwd/kg' and codename == 'serpent': + if timestep_units in ('s', 'sec'): + depletion_timesteps /= 60 * 60 * 24 + elif timestep_units in ('min', 'minute'): + depletion_timesteps /= 60 * 24 + elif timestep_units in ('h', 'hr', 'hour'): + depletion_timesteps /= 24 + elif timestep_units in ('a', 'year'): + depletion_timesteps *= 365.25 + else: + raise IOError(f'Unrecognized time unit: {timestep_units}') + reactor_input['depletion_timesteps'] = depletion_timesteps + reactor_input['power_levels'] = power_levels return reactor_input - def reprocess_materials(mats, process_file, dot_file): """Applies extraction reprocessing scheme to burnable materials. From 544f35ed1a7d1b399add41dada649da28649ccca Mon Sep 17 00:00:00 2001 From: yardasol Date: Wed, 7 Dec 2022 15:05:58 -0600 Subject: [PATCH 11/23] add more coverage to test_read_main_input() --- tests/unit_tests/test_app.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/unit_tests/test_app.py b/tests/unit_tests/test_app.py index cdd97bfdd..bf1a54230 100644 --- a/tests/unit_tests/test_app.py +++ b/tests/unit_tests/test_app.py @@ -23,6 +23,28 @@ def test_read_main_input(cwd, codename, ext): assert depcode_input['codename'] == codename assert depcode_input['geo_file_paths'][0] == \ str(data_path / ('tap_geometry_base' + ext)) + if codename == 'openmc': + assert depcode_input['template_input_file_path'] == \ + {'materials': str((input_path / 'tap_materials.xml').resolve()), + 'settings': str((input_path / 'tap_settings.xml').resolve())} + assert depcode_input['chain_file_path'] == \ + str((input_path / 'test_chain.xml').resolve()) + assert depcode_input['depletion_settings'] == \ + {'method': 'predictor', + 'final_step': True, + 'operator_kwargs': {'normalization_mode': 'fission-q', + 'fission_q': None, + 'dilute_initial': 1000, + 'fission_yield_mode': 'constant', + 'reaction_rate_mode': 'direct', + 'reaction_rate_opts': None, + 'reduce_chain': False, + 'reduce_chain_level': None}, + 'output': True, + 'integrator_kwargs': {}} + elif codename == 'serpent': + assert depcode_input['template_input_file_path'] == \ + str((input_path / 'tap_template.ini').resolve()) assert simulation_input['db_name'] == \ str((data_path / '../temp_data/db_saltproc.h5').resolve()) @@ -32,8 +54,10 @@ def test_read_main_input(cwd, codename, ext): reactor_input['power_levels'], [ 1.250E+9, 1.250E+9]) np.testing.assert_equal(reactor_input['depletion_timesteps'], - [5, 10]) + [5, 5]) + assert reactor_input['timestep_units'] == 'd' + assert reactor_input['timestep_type'] == 'stepwise' def test_get_extraction_processes(proc_test_file): procs = get_extraction_processes(proc_test_file) From 434ac51c62206b7b4ad6b9a7ca0013bb7658e93a Mon Sep 17 00:00:00 2001 From: yardasol Date: Thu, 8 Dec 2022 13:53:54 -0600 Subject: [PATCH 12/23] remove input variables from input files that are equal to their defaults --- examples/msbr/msbr_main.json | 7 +------ examples/tap/tap_main.json | 6 +----- .../run_constant_reprocessing/tap_input.json | 7 +------ .../integration_tests/run_no_reprocessing/test_input.json | 8 ++------ tests/openmc_data/tap_input.json | 6 +----- tests/serpent_data/tap_input.json | 7 +------ 6 files changed, 7 insertions(+), 34 deletions(-) diff --git a/examples/msbr/msbr_main.json b/examples/msbr/msbr_main.json index 6509bcb5b..4862d4b53 100644 --- a/examples/msbr/msbr_main.json +++ b/examples/msbr/msbr_main.json @@ -1,19 +1,14 @@ { "proc_input_file": "msbr_objects.json", "dot_input_file": "msbr.dot", - "output_path": "data", "n_depletion_steps": 12, "depcode": { "codename": "serpent", - "exec_path": "sss2", "template_input_file_path": "msbr.serpent", "geo_file_paths": ["geometry/msbr_full.ini"] }, "simulation": { - "sim_name": "msbr_example_simulation", - "db_name": "msbr_kl_100_saltproc.h5", - "restart_flag": false, - "adjust_geo": false + "sim_name": "msbr_kl_100_simulation" }, "reactor": { "volume": 1.0, diff --git a/examples/tap/tap_main.json b/examples/tap/tap_main.json index aecff2141..d90380961 100644 --- a/examples/tap/tap_main.json +++ b/examples/tap/tap_main.json @@ -1,11 +1,9 @@ { "proc_input_file": "tap_objects.json", "dot_input_file": "tap.dot", - "output_path": "data", "n_depletion_steps": 3, "depcode": { "codename": "serpent", - "exec_path": "sss2", "template_input_file_path": "tap.serpent", "geo_file_paths": [ "geometry/347_base.ini", @@ -26,9 +24,7 @@ ] }, "simulation": { - "sim_name": "test_simulation", - "db_name": "db_saltproc.h5", - "restart_flag": false, + "sim_name": "tap_example_simulation", "adjust_geo": true }, "reactor": { diff --git a/tests/integration_tests/run_constant_reprocessing/tap_input.json b/tests/integration_tests/run_constant_reprocessing/tap_input.json index cf2582b88..730ad0d84 100644 --- a/tests/integration_tests/run_constant_reprocessing/tap_input.json +++ b/tests/integration_tests/run_constant_reprocessing/tap_input.json @@ -1,19 +1,14 @@ { "proc_input_file": "tap_processes.json", "dot_input_file": "../../tap_paths.dot", - "output_path": ".", "n_depletion_steps": 2, "depcode": { "codename": "serpent", - "exec_path": "sss2", "template_input_file_path": "tap_template.ini", "geo_file_paths": ["tap_geometry_base.ini"] }, "simulation": { - "sim_name": "tap_integration_test", - "db_name": "test_db.h5", - "restart_flag": false, - "adjust_geo": false + "sim_name": "tap_constant_reprocessing" }, "reactor": { "volume": 1.0, diff --git a/tests/integration_tests/run_no_reprocessing/test_input.json b/tests/integration_tests/run_no_reprocessing/test_input.json index 391ca7eba..31e0ac209 100644 --- a/tests/integration_tests/run_no_reprocessing/test_input.json +++ b/tests/integration_tests/run_no_reprocessing/test_input.json @@ -1,19 +1,15 @@ { "proc_input_file": "processes.json", "dot_input_file": "paths.dot", - "output_path": ".", "n_depletion_steps": 2, "depcode": { "codename": "serpent", - "exec_path": "sss2", "template_input_file_path": "test_input.ini", "geo_file_paths": ["../../serpent_data/tap_geometry_base.ini"] }, "simulation": { - "sim_name": "Integration test", - "db_name": "_test_db.h5", - "restart_flag": true, - "adjust_geo": false + "sim_name": "test_no_reprocessing", + "restart_flag": true }, "reactor": { "volume": 1.0, diff --git a/tests/openmc_data/tap_input.json b/tests/openmc_data/tap_input.json index 843773787..5fc05554d 100644 --- a/tests/openmc_data/tap_input.json +++ b/tests/openmc_data/tap_input.json @@ -1,7 +1,6 @@ { "proc_input_file": "../tap_processes.json", "dot_input_file": "../tap_paths.dot", - "output_path": "../temp_data", "n_depletion_steps": 2, "depcode": { "codename": "openmc", @@ -13,10 +12,7 @@ "chain_file_path": "test_chain.xml" }, "simulation": { - "sim_name": "tap_test_simulation_openmc", - "db_name": "db_saltproc.h5", - "restart_flag": false, - "adjust_geo": false + "sim_name": "tap_test_simulation_openmc" }, "reactor": { "volume": 1.0, diff --git a/tests/serpent_data/tap_input.json b/tests/serpent_data/tap_input.json index e5f40fb6d..41525d03e 100644 --- a/tests/serpent_data/tap_input.json +++ b/tests/serpent_data/tap_input.json @@ -1,19 +1,14 @@ { "proc_input_file": "../tap_processes.json", "dot_input_file": "../tap_paths.dot", - "output_path": "../temp_data", "n_depletion_steps": 2, "depcode": { "codename": "serpent", - "exec_path": "sss2", "template_input_file_path": "tap_template.ini", "geo_file_paths": ["tap_geometry_base.ini"] }, "simulation": { - "sim_name": "tap_test_simulation_serpent", - "db_name": "db_saltproc.h5", - "restart_flag": false, - "adjust_geo": false + "sim_name": "tap_test_simulation_serpent" }, "reactor": { "volume": 1.0, From d138aa543130cb74eb83a0e4696ae9085dc04267 Mon Sep 17 00:00:00 2001 From: yardasol Date: Thu, 8 Dec 2022 13:55:21 -0600 Subject: [PATCH 13/23] add default values for various parameters; consistency changes in code and tests --- saltproc/app.py | 4 ++ saltproc/input_schema.json | 3 +- tests/conftest.py | 71 +++++++++++-------- .../file_interface_openmc/test.py | 2 +- .../run_constant_reprocessing/test.py | 5 +- .../run_no_reprocessing/test.py | 29 +++----- tests/unit_tests/test_app.py | 2 +- tests/unit_tests/test_simulation.py | 4 ++ 8 files changed, 68 insertions(+), 52 deletions(-) diff --git a/saltproc/app.py b/saltproc/app.py index 8d9098a87..3dc66f575 100644 --- a/saltproc/app.py +++ b/saltproc/app.py @@ -180,6 +180,10 @@ def read_main_input(main_inp_file): output_path = (input_path / output_path) j['output_path'] = output_path.resolve() + # Create output directoy if it doesn't exist + if not Path(j['output_path']).exists(): + Path(j['output_path']).mkdir(parents=True) + # Class settings depcode_input = j['depcode'] simulation_input = j['simulation'] diff --git a/saltproc/input_schema.json b/saltproc/input_schema.json index b7bff026d..9b9f370b5 100644 --- a/saltproc/input_schema.json +++ b/saltproc/input_schema.json @@ -18,7 +18,7 @@ "description": "Path output data storing folder", "type": "string", "pattern": "^(.\\/)*(.*)$", - "default": "saltproc_results" + "default": "saltproc_runtime" }, "n_depletion_steps": { "description": "Number of steps for constant power and depletion interval case", @@ -259,6 +259,7 @@ "db_name": { "description": "Output HDF5 database file name", "type": "string", + "default": "saltproc_results.h5", "pattern": "^(.*)\\.h5$"}, "restart_flag": { "description": "Restart simulation from the step when it stopped?", diff --git a/tests/conftest.py b/tests/conftest.py index ecd251679..7363bbaf4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,7 @@ from pathlib import Path import pytest -from saltproc.app import read_main_input, _create_depcode_object, _create_reactor_object +from saltproc.app import read_main_input, _create_depcode_object, _create_simulation_object, _create_reactor_object from saltproc import Simulation @@ -23,54 +23,67 @@ def path_test_file(cwd): @pytest.fixture(scope='session') -def serpent_depcode(cwd): - """SerpentDepcode object for unit tests""" +def serpent_runtime(cwd, tmpdir_factory): + """SaltProc objects for Serpent unit tests""" saltproc_input = str(cwd / 'serpent_data' / 'tap_input.json') _, _, _, object_input = read_main_input(saltproc_input) depcode = _create_depcode_object(object_input[0]) depcode.runtime_inputfile = str(cwd / 'serpent_data' / 'tap_reference') + output_dir = str(depcode.output_path).split('/')[-1] + depcode.output_path = tmpdir_factory.mktemp(f'serpent_{output_dir}') - return depcode + simulation = _create_simulation_object(object_input[1], depcode, 1, 1) + #db_path=str( + # cwd / + # 'serpent_data' / + # 'tap_reference_db.h5')) + + reactor = _create_reactor_object(object_input[2]) + + return depcode, simulation, reactor @pytest.fixture(scope='session') -def serpent_reactor(cwd): - saltproc_input = str(cwd / 'serpent_data' / 'tap_input.json') - _, _, _, object_input = read_main_input(saltproc_input) - reactor = _create_reactor_object(object_input[2]) +def serpent_depcode(serpent_runtime): + """SerpentDepcode object for unit tests""" + depcode, _, _ = serpent_runtime + return depcode + +@pytest.fixture(scope='session') +def serpent_reactor(serpent_runtime): + _, _, reactor = serpent_runtime return reactor @pytest.fixture(scope='session') -def openmc_depcode(cwd): - """OpenMCDepcode object for unit tests""" +def simulation(serpent_runtime): + """Simulation object for unit tests""" + _, simulation, _ = serpent_runtime + return simulation + + +@pytest.fixture(scope='session') +def openmc_runtime(cwd, tmpdir_factory): + """SaltProc objects for OpenMC unit tests""" saltproc_input = str(cwd / 'openmc_data' / 'tap_input.json') _, _, _, object_input = read_main_input(saltproc_input) depcode = _create_depcode_object(object_input[0]) + output_dir = str(depcode.output_path).split('/')[-1] + depcode.output_path = tmpdir_factory.mktemp(f'openmc_{output_dir}') + reactor = _create_reactor_object(object_input[2]) - return depcode + return depcode, reactor @pytest.fixture(scope='session') -def openmc_reactor(cwd): - openmc_input = str(cwd / 'openmc_data' / 'tap_input.json') - _, _, _, object_input = read_main_input(openmc_input) - reactor = _create_reactor_object(object_input[2]) - - return reactor +def openmc_depcode(openmc_runtime): + """OpenMCDepcode objects for unit tests""" + depcode, _ = openmc_runtime + return depcode @pytest.fixture(scope='session') -def simulation(cwd, serpent_depcode): - """Simulation object for unit tests""" - simulation = Simulation( - sim_name='test_simulation', - sim_depcode=serpent_depcode, - core_number=1, - node_number=1, - db_path=str( - cwd / - 'serpent_data' / - 'tap_reference_db.h5')) - return simulation +def openmc_reactor(openmc_runtime): + _, reactor = openmc_runtime + return reactor diff --git a/tests/integration_tests/file_interface_openmc/test.py b/tests/integration_tests/file_interface_openmc/test.py index 80526a90e..1893317b5 100644 --- a/tests/integration_tests/file_interface_openmc/test.py +++ b/tests/integration_tests/file_interface_openmc/test.py @@ -79,7 +79,7 @@ def test_write_depletion_settings(openmc_depcode, msr): with open(openmc_depcode.output_path / 'depletion_settings.json') as f: j = json.load(f) assert Path(j['directory']).resolve() == Path( - openmc_depcode.runtime_inputfile['settings']).parents[0] + openmc_depcode.output_path) assert j['timesteps'][0] == msr.depletion_timesteps[0] assert j['operator_kwargs']['chain_file'] == \ openmc_depcode.chain_file_path diff --git a/tests/integration_tests/run_constant_reprocessing/test.py b/tests/integration_tests/run_constant_reprocessing/test.py index 1c12b7a34..0d41bf6b9 100644 --- a/tests/integration_tests/run_constant_reprocessing/test.py +++ b/tests/integration_tests/run_constant_reprocessing/test.py @@ -1,5 +1,6 @@ """Run SaltProc with reprocessing""" from pathlib import Path +import shutil import numpy as np import pytest @@ -11,7 +12,7 @@ @pytest.fixture def setup(scope='module'): cwd = str(Path(__file__).parents[0].resolve()) - test_db = cwd + '/test_db.h5' + test_db = cwd + '/saltproc_runtime/saltproc_results.h5' ref_db = cwd + '/tap_reference_db.h5' tol = 1e-9 @@ -28,6 +29,8 @@ def test_integration_2step_constant_ideal_removal_heavy(setup): np.testing.assert_equal(read_keff(test_db), read_keff(ref_db)) assert_db_almost_equal(test_db, ref_db, tol) + shutil.rmtree(cwd + '/saltproc_runtime') + def read_keff(file): db = tb.open_file(file, mode='r') sim_param = db.root.simulation_parameters diff --git a/tests/integration_tests/run_no_reprocessing/test.py b/tests/integration_tests/run_no_reprocessing/test.py index 7fbe545fd..4c923a2e4 100644 --- a/tests/integration_tests/run_no_reprocessing/test.py +++ b/tests/integration_tests/run_no_reprocessing/test.py @@ -1,7 +1,6 @@ """Run SaltProc without reprocessing""" -import os -import glob from pathlib import Path +import shutil import numpy as np import pytest @@ -14,31 +13,28 @@ @pytest.fixture def setup(): cwd = str(Path(__file__).parents[0].resolve()) - main_input = cwd + '/test_input.json' + saltproc_input = cwd + '/test_input.json' input_path, process_input_file, path_input_file, object_input = \ - app.read_main_input(main_input) + app.read_main_input(saltproc_input) depcode = app._create_depcode_object(object_input[0]) - sss_file = cwd + '/_test' - depcode.runtime_inputfile = sss_file - depcode.runtime_matfile = cwd + '/_test_mat' simulation = app._create_simulation_object(object_input[1], depcode, 1, 1) - reactor = app._create_reactor_object(object_input[2], object_input[0]['codename']) + reactor = app._create_reactor_object(object_input[2]) - return cwd, simulation, reactor, sss_file + return cwd, simulation, reactor @pytest.mark.slow def test_integration_2step_saltproc_no_reproc_heavy(setup): - cwd, simulation, reactor, sss_file = setup + cwd, simulation, reactor = setup runsim_no_reproc(simulation, reactor, 2) - saltproc_out = sss_file + '_dep.m' + output_path = str(simulation.sim_depcode.output_path) ref_result = serpent.parse_dep(cwd + '/reference_dep.m', make_mats=False) - test_result = serpent.parse_dep(saltproc_out, make_mats=False) + test_result = serpent.parse_dep(f'{output_path}/runtime_input.serpent_dep.m', make_mats=False) ref_mdens_error = np.loadtxt(cwd + '/reference_error') @@ -47,13 +43,8 @@ def test_integration_2step_saltproc_no_reproc_heavy(setup): test_mdens_error = np.array(ref_fuel_mdens - test_fuel_mdens) np.testing.assert_array_almost_equal(test_mdens_error, ref_mdens_error) - # Cleaning after testing - out_file_list = glob.glob(cwd + '/_test*') - for file in out_file_list: - try: - os.remove(file) - except OSError: - print("Error while deleting file : ", file) + + shutil.rmtree(cwd + '/saltproc_runtime') def runsim_no_reproc(simulation, reactor, nsteps): diff --git a/tests/unit_tests/test_app.py b/tests/unit_tests/test_app.py index bf1a54230..197f583fd 100644 --- a/tests/unit_tests/test_app.py +++ b/tests/unit_tests/test_app.py @@ -47,7 +47,7 @@ def test_read_main_input(cwd, codename, ext): str((input_path / 'tap_template.ini').resolve()) assert simulation_input['db_name'] == \ - str((data_path / '../temp_data/db_saltproc.h5').resolve()) + str((data_path / f'../{codename}_data/saltproc_runtime/saltproc_results.h5').resolve()) assert simulation_input['restart_flag'] is False np.testing.assert_equal( diff --git a/tests/unit_tests/test_simulation.py b/tests/unit_tests/test_simulation.py index f82a99b12..6d19016f8 100644 --- a/tests/unit_tests/test_simulation.py +++ b/tests/unit_tests/test_simulation.py @@ -1,4 +1,5 @@ """Test Simulation functions""" +from pathlib import Path def test_check_switch_geo_trigger(simulation): @@ -21,4 +22,7 @@ def test_check_switch_geo_trigger(simulation): def test_read_k_eds_delta(simulation): + old_db_path = simulation.db_path + simulation.db_path = str(Path(simulation.db_path).parents[1] / 'tap_reference_db.h5') assert simulation.read_k_eds_delta(7) is False + simulation.db_path = old_db_path From be03b9dac1616555a74458fb3c0e916031b9a2ec Mon Sep 17 00:00:00 2001 From: yardasol Date: Thu, 8 Dec 2022 14:29:07 -0600 Subject: [PATCH 14/23] update releasenotes --- doc/releasenotes/v0.5.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/releasenotes/v0.5.0.rst b/doc/releasenotes/v0.5.0.rst index 3f0e4ddab..8a5ffb80c 100644 --- a/doc/releasenotes/v0.5.0.rst +++ b/doc/releasenotes/v0.5.0.rst @@ -97,6 +97,7 @@ Python API Changes - Input file format changes: + - Added default values for certain input parameters - Added depletion settings for OpenMC - ``num_depsteps`` → ``n_depletion_steps`` - ``depcode['template_inputfile_path']`` → ``depcode['template_input_file_path']`` From 3bdc6d48d7d7af9e5d80f8fd1e8493ee1fb03662 Mon Sep 17 00:00:00 2001 From: yardasol Date: Tue, 10 Jan 2023 16:03:54 -0600 Subject: [PATCH 15/23] typo fixes in input_schema.json --- doc/fileformatspec/{inputfile.rst => saltproc_input.rst} | 0 saltproc/input_schema.json | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) rename doc/fileformatspec/{inputfile.rst => saltproc_input.rst} (100%) diff --git a/doc/fileformatspec/inputfile.rst b/doc/fileformatspec/saltproc_input.rst similarity index 100% rename from doc/fileformatspec/inputfile.rst rename to doc/fileformatspec/saltproc_input.rst diff --git a/saltproc/input_schema.json b/saltproc/input_schema.json index 9b9f370b5..41f63ffb4 100644 --- a/saltproc/input_schema.json +++ b/saltproc/input_schema.json @@ -131,7 +131,7 @@ "properties": { "fission_yield_opts": { "type": "object", - "proterties": { + "properties": { "cutoff": { "description": "Cutoff energy in eV", "type": "number"}, @@ -184,7 +184,7 @@ "diff_burnable_mats": { "description": "Whether to differentiate burnable materials with multiple instances.", "type": "boolean", - "defualt": false}, + "default": false}, "normalization_mode": { "description": "Indicate how tally resutls should be normalized", "type": "string", @@ -205,7 +205,7 @@ "default": "constant"}, "fission_yield_opts": { "description": "Arguments for the fission yield helper", - "defualt": null}, + "default": null}, "reaction_rate_mode": { "description": "Indicate how one-group reaction rates should be calculated", "type": "string", From ffaf326aaae9943b38f8e6dfdb0d942298d08d76 Mon Sep 17 00:00:00 2001 From: yardasol Date: Tue, 10 Jan 2023 16:05:04 -0600 Subject: [PATCH 16/23] overhaul input file description in docpages --- doc/fileformatspec/depcode_input.rst | 546 ++++++++++++++++++++++++ doc/fileformatspec/index.rst | 21 +- doc/fileformatspec/reactor_input.rst | 118 +++++ doc/fileformatspec/saltproc_input.rst | 120 +++++- doc/fileformatspec/simulation_input.rst | 65 +++ 5 files changed, 865 insertions(+), 5 deletions(-) create mode 100644 doc/fileformatspec/depcode_input.rst create mode 100644 doc/fileformatspec/reactor_input.rst create mode 100644 doc/fileformatspec/simulation_input.rst diff --git a/doc/fileformatspec/depcode_input.rst b/doc/fileformatspec/depcode_input.rst new file mode 100644 index 000000000..043f89ba2 --- /dev/null +++ b/doc/fileformatspec/depcode_input.rst @@ -0,0 +1,546 @@ +.. _depcode_input: + +``depcode`` Properties +====================== + +The ``depcode`` property has a large number of potential inputs depending on which depletion code we are using. The generic properties are given directly +below, and code-specific properties are given in their appropriate subsections. + +.. note:: The code-specific properties add to or modify the existing generic + properties. They do not replace them, but there can be new ones. + +Generic properties +------------------ + +Required properties: ``codename``, ``template_input_file_path``, ``geo_file_paths``. + +.. _codename_property: + +``codename`` +~~~~~~~~~~~~ + + :description: + Name of depletion code + + :type: + ``string`` + + :enum: + ``serpent``, ``openmc`` + + +.. _exec_path_property: + +``exec_path`` +~~~~~~~~~~~~~ + + :description: + Path to depletion code executable + + :type: + ``string`` + + +.. _template_input_file_path_property: + +``template_input_file_path`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + :description: + Path(s) to user's template depletion code input file(s) with reactor model + + +.. _geo_file_paths_property: + +``geo_file_paths`` +~~~~~~~~~~~~~~~~~~ + + :description: + Path(s) to geometry file(s) to switch to in depletion code runs + + :type: + ``array`` + + :items: + + :type: + ``string`` + + :minItems: + 1 + + :uniqueItems: + ``true`` + + +.. _serpent_specific_properties: + +Serpent-specific properties +--------------------------- + +.. _serpent_exec_path_property: + +``exec_path`` +~~~~~~~~~~~~~ + + :default: + ``sss2`` + + +.. _serpent_template_input_file_path_property: + +``template_input_file_path`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + :pattern: + ``^(.\\/)*(.*)$`` + + +.. _openmc_specific_properties: + +OpenMC-specific properties +-------------------------- + +.. _openmc_exec_path_property: + +``exec_path`` +~~~~~~~~~~~~~ + + :description: + Path to OpenMC depletion script + + :const: + ``openmc_deplete.py`` + + :default: + ``openmc_deplete.py`` + + +.. _openmc_template_input_file_path_property: + +``template_input_file_path`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + :description: + Paths to OpenMC template input files. + + :type: + ``object``. + + :required: + ``settings``, ``materials`` + + :properties: + + :settings: + + :description: + OpenMC settings file + + :type: + ``string`` + + :pattern: + ``^(.\\/)+(.+)\\.xml$`` + + :default: + ``settings.xml`` + + :materials: + + :description: + OpenMC materials file + + :type: + ``string`` + + :pattern: + ``^(.\\/)*(.*)\\.xml$`` + + :default: + ``materials.xml`` + + +.. _openmc_geo_file_paths_property: + +``geo_file_paths`` +~~~~~~~~~~~~~~~~~~ + + :items: + + :type: + ``string`` + + :pattern: + ``^(.\\/)*(.*)\\.xml$`` + + :default: + ``geometry.xml`` + + +.. _openmc_chain_file_path_property: + +``chain_file_path`` +~~~~~~~~~~~~~~~~~~~ + + :description: + Path to depletion chain file + + :pattern: + ``^(.\\/)*(.*)\\.xml$`` + + :type: + ``string`` + + +.. _opemc_depletion_settings_property: + +``depletion_settings`` +~~~~~~~~~~~~~~~~~~~~~~ + :description: + OpenMC depletion settings + + :type: + ``object``. + + :default: + ``{}`` + + :properties: + + :method: + + :description: + Integration method used for depletion + + :type: + ``string`` + + :enum: + ``cecm``, ``predictor``, ``cf4``, ``epc_rk4``, ``si_celi``, ``si_leqi``, + ``celi``, ``leqi`` + + :default: + ``predictor`` + + + :final_step: + + :description: + Indicate whether or not a transport solve should be run at the end of the + last timestep + + :type: + ``boolean`` + + :default: + ``true`` + + + :operator_kwargs: + + :description: + Keyword arguments passed to the depletion operator initalizer + + :type: + ``object`` + + :default: + ``{}`` + + :properties: + :ref:`openmc_operator_kwargs_properties` + + :output: + + :description: + Capture OpenMC output from standard out + + :type: + ``boolean`` + + :default: + ``true`` + + + :integrator_kwargs: + + :description: + Remaining keyword arguments for the depletion Integrator initalizer + + :type: + ``object`` + + :default: + ``{}`` + + :properties: + + :solver: + + :description: + Bateman equations solver type + + :type: + ``string`` + + :enum: + ``cram16``, ``cram48`` + + + :n_steps: + + :description: + Number of stochastic iterations for stochastic integrators + + :type: + ``number`` + + :minimum: + 1 + + +.. _openmc_operator_kwargs_properties: + +``operator_kwargs`` Properties +------------------------------ + +``diff_burnable_mats`` +~~~~~~~~~~~~~~~~~~~~~~ + + :description: + Whether to differentiate burnable materials with multiple instances. + + :type: + ``boolean`` + + :default: + ``false`` + + +``normalization_mode`` +~~~~~~~~~~~~~~~~~~~~~~ + + :description: + Indicate how tally resutls should be normalized + + :type: + ``string`` + + :enum: + ``energy-deposition``, ``fission-q``, ``source-rate`` + + :default: + ``fission-q`` + + +``fission_q`` +~~~~~~~~~~~~~ + + :description: + Path to fission Q values + + :default: + ``null`` + + +``dilute_initial`` +~~~~~~~~~~~~~~~~~~ + + :description: + Initial atom density to add for nuclides that are zero in initial + condition. + + :type: + ``number`` + + :minimum: + 0 + + :default: + 1000 + + +``fission_yield_mode`` +~~~~~~~~~~~~~~~~~~~~~~ + + :description: + Determine what fission energy helper is used + + :type: + ``string`` + + :enum: + ``constant``, ``cutoff``, ``average`` + + :default: + ``constant`` + + +``fission_yield_opts`` +~~~~~~~~~~~~~~~~~~~~~~ + + :description: + Arguments for the fission yield helper + + :default: + ``null``. See :ref:`openmc_constant_fission_yield_opts_properties` + and :ref:`openmc_cutoff_fission_yield_opts_properties` for object + properties when ``fission_yield_mode`` is ``constant`` and + ``cutoff``, respectively. + + +``reaction_rate_mode`` +~~~~~~~~~~~~~~~~~~~~~~ + + :description: + Indicate how one-group reaction rates should be calculated + + :type: + ``string`` + + :enum: + ``direct``, ``flux`` + + :default: + ``direct`` + + +``reaction_rate_opts`` +~~~~~~~~~~~~~~~~~~~~~~ + + :default: + ``null``. See :ref:`openmc_flux_reaction_rate_opts_properties` for + object properties when ``reaction_rate_mode`` is ``flux``. + + +``reduce_chain`` +~~~~~~~~~~~~~~~~ + + :description: + Whether or not to reduce the depletion chain. + + :type: + ``boolean`` + + :default: + ``false`` + + +``reduce_chain_level`` +~~~~~~~~~~~~~~~~~~~~~~ + + :description: + Depth of serach while reducing depletion chain + + :default: + ``null`` + + +.. _openmc_constant_fission_yield_opts_properties: + +``fission_yield_opts`` Properties -- ``constant`` fission yield mode +-------------------------------------------------------------------- + +``energy`` +~~~~~~~~~~ + + :description: + Energy of fission yield libraries [MeV] + + :type: + ``number`` + + +.. _openmc_cutoff_fission_yield_opts_properties: + +``fission_yield_opts`` Properties -- ``cutoff`` fission yield mode +------------------------------------------------------------------ + +``cutoff`` +~~~~~~~~~~ + + :description: + Cutoff energy in eV + + :type: + ``number`` + + +``thermal_energy`` +~~~~~~~~~~~~~~~~~~ + + :description: + Energy of yield data corresponding to thermal yields + + :type: + ``number`` + + +``fast_energy`` +~~~~~~~~~~~~~~~ + + :description: + Energy of yield data corresponding to fast yields + + :type: + ``number`` + + +.. _openmc_flux_reaction_rate_opts_properties: + +``reaction_rate_opts`` Properties -- ``flux`` reaction rate mode +---------------------------------------------------------------- + +``energies`` +~~~~~~~~~~~~ + + :description: + Energy group boundaries + + :type: + ``array`` + + :items: + + :type: + ``number`` + + :minItems: + 2 + + +``reactions`` +~~~~~~~~~~~~~ + + :description: + Reactions to tally + + :type: + ``array`` + + :items: + + :type: + ``string`` + + :minItems: + 1 + + +``nuclides`` +~~~~~~~~~~~~ + + :description: + Nuclides on which to tally reactions + + :type: + ``array`` + + :items: + + :type: + ``string`` + + :minItems: + 1 diff --git a/doc/fileformatspec/index.rst b/doc/fileformatspec/index.rst index 4c08dbc5e..bcee28b22 100644 --- a/doc/fileformatspec/index.rst +++ b/doc/fileformatspec/index.rst @@ -1,11 +1,28 @@ .. _fileformatspec: -========================== File Format Specifications ========================== +.. _fileformatspect_input_files: + +Input Files +----------- + +.. toctree:: + :maxdepth: 1 + + saltproc_input + depcode_input + simulation_input + reactor_input + + +.. _fileformatspec_output_files: + +Output Files +------------ + .. toctree:: :maxdepth: 1 - inputfile databasefile diff --git a/doc/fileformatspec/reactor_input.rst b/doc/fileformatspec/reactor_input.rst new file mode 100644 index 000000000..4d4477521 --- /dev/null +++ b/doc/fileformatspec/reactor_input.rst @@ -0,0 +1,118 @@ +.. _reactor_input: + +``reactor`` Properties +========================= + +Required properties: ``volume``, ``mass_flowrate``, ``power_levels``, +``depletion_timesteps``, ``timestep_units`` + +.. _volume_property: + +``volume`` +---------- + :description: + reactor core volume [cm^3] + + :type: + ``number`` + + :minimum: + 0 + + +.. _mass_flowrate_property: + +``mass_flowrate`` +----------------- + + :description: + Salt mass flowrate through reactor core [g/s] + + :type: + ``number`` + + :minimum: + 0 + + +.. _power_levels_property: + +``power_levels`` +---------------- + + :description: + Reactor power or power step list durng depletion step [W] + + :type: + ``array`` + + :items: + + :type: + ``number`` + + :minimum: + 0 + + :minItems: + 1 + + :uniqueItems: + ``false`` + + +.. _depletion_timesteps_property: + +``depletion_timesteps`` +----------------------- + + :description: + Depletion step length(s) + + :type: + ``array`` + + :items: + + :type: + ``number`` + + :minimum: + 0 + + :minItems: + 1 + + :uniqueItems: + ``false`` + +.. _timestep_type_property: + +``timestep_type`` +----------------- + + :description: + Depletion step type + + :type: + ``string`` + + :enum: + ``cumulative``, ``stepwise`` + + :default: + ``stepwise`` + +.. _timestep_unites_property: + +``timestep_units`` +------------------ + + :description: + Timestep unit + + :type: + ``string`` + + :enum: + ``s``, ``sec``, ``min``, ``minute``, ``h``, ``hr``, ``hour``, ``d``, ``day``, ``a``, ``year``, ``MWd/kg``, ``mwd/kg``, ``MWD/KG``, ``MWD/kg``, ``MWd/KG`` diff --git a/doc/fileformatspec/saltproc_input.rst b/doc/fileformatspec/saltproc_input.rst index 4afe867a4..5c0065d59 100644 --- a/doc/fileformatspec/saltproc_input.rst +++ b/doc/fileformatspec/saltproc_input.rst @@ -1,7 +1,121 @@ -=================== +.. _saltproc_input: + SaltProc Input File =================== -.. note:: The ``allOf`` block in under ``depcode`` picks out the correct schema for ``template_inputfiles_path`` based on the depletion code. +The main SaltProc input file is a JSON file validated against a JSON schema. +In this section, we will describe the structure of this schema. The top level +datatype of the schema is a JSON ``object``. + +Required properties are as follows: ``proc_input_file``, ``dot_input_file``, ``output_path``, ``depcode``, ``simulation``, ``reactor``. + +.. _proc_input_file_property: + +``proc_input_file`` +------------------- + + :description: + File containing processing system objects + + :type: + ``string`` + + :pattern: + ``^(.*)\\.json$`` + + +.. _dot_input_file_property: + +``dot_input_file`` +------------------ + + :description: + Graph file containing processing system structure + + :type: + ``string`` + + :pattern: + ``^(.*)\\.dot$`` + + +.. _output_path_property: + +``output_path`` +--------------- + + :description: + Path output data storing folder + + :type: + ``string`` + + :pattern: + ``^(.\\/)*(.*)$`` + + :default: + ``saltproc_runtime`` + + +.. _n_depletion_steps_property: + +``n_depletion_steps`` +--------------------- + + :description: + Number of steps for constant power and depletion interval case + + :type: + ``number`` + + +.. _depcode_property: + +``depcode`` +----------- + + :description: + Depcode class input parameters + + :type: + ``object`` + + :default: + ``{}`` + + :properties: + :ref:`depcode_input` + +.. _simulation_property: + +``simulation`` +-------------- + + :description: + Simulation class input parameters + + :type: + ``object`` + + :default: + ``{}`` + + :properties: + :ref:`simulation_input` + +.. _reactor_property: + +``reactor`` +----------- + + :description: + Reactor class input parameters + + :type: + ``object``. See :ref:`reactor_input` for object properties. + + :default: + ``{}`` -.. jsonschema:: ../../saltproc/input_schema.json + :properties: + :ref:`reactor_input` diff --git a/doc/fileformatspec/simulation_input.rst b/doc/fileformatspec/simulation_input.rst new file mode 100644 index 000000000..638f72ef9 --- /dev/null +++ b/doc/fileformatspec/simulation_input.rst @@ -0,0 +1,65 @@ +.. _simulation_input: + +``simulation`` Properties +========================= + +Required properties: ``sim_name``, ``db_name`` + +.. _sim_name_property: + +``sim_name`` +------------ + + :description: + Name of simulation + + :type: + ``string`` + + +.. _db_name_property: + +``db_name`` +----------- + + :description: + Output HDF5 database file name + + :type: + ``string`` + + :default: + ``saltproc_results.h5`` + + :pattern: + ``^(.*)\\.h5$`` + + +.. _restart_flag_property: + +``restart_flag`` +---------------- + + :description: + Restart simulation from the step when it stopped? + + :type: + ``boolean`` + + :default: + ``false`` + + +.. _adjust_geo_property: + +``adjust_geo`` +-------------- + + :description: + switch to another geometry when keff drops below 1? + + :type: + ``boolean`` + + :default: + ``false`` From c1f88359b5a4f99d032e08ace00c51728a524450 Mon Sep 17 00:00:00 2001 From: yardasol Date: Wed, 11 Jan 2023 12:37:45 -0600 Subject: [PATCH 17/23] add unevaluatedProperties keyword to limit json input to declared schemas --- saltproc/input_schema.json | 1 + 1 file changed, 1 insertion(+) diff --git a/saltproc/input_schema.json b/saltproc/input_schema.json index 41f63ffb4..7d531f01c 100644 --- a/saltproc/input_schema.json +++ b/saltproc/input_schema.json @@ -3,6 +3,7 @@ "$id": "https://github.com/arfc/saltproc", "title": "SaltProc input file schema", "type": "object", + "unevaluatedProperties": false, "properties": { "proc_input_file": { "description": "File containing processing system objects", From 3cc1a18345fd515ca8955d840fab1218665f3e0c Mon Sep 17 00:00:00 2001 From: yardasol Date: Wed, 11 Jan 2023 13:04:44 -0600 Subject: [PATCH 18/23] Revert "add unevaluatedProperties keyword to limit json input to declared schemas" This reverts commit c1f88359b5a4f99d032e08ace00c51728a524450. --- saltproc/input_schema.json | 1 - 1 file changed, 1 deletion(-) diff --git a/saltproc/input_schema.json b/saltproc/input_schema.json index 7d531f01c..41f63ffb4 100644 --- a/saltproc/input_schema.json +++ b/saltproc/input_schema.json @@ -3,7 +3,6 @@ "$id": "https://github.com/arfc/saltproc", "title": "SaltProc input file schema", "type": "object", - "unevaluatedProperties": false, "properties": { "proc_input_file": { "description": "File containing processing system objects", From 398cb13d36a6fc2e3d7c6bc34987f86fb964083a Mon Sep 17 00:00:00 2001 From: yardasol Date: Wed, 11 Jan 2023 13:53:15 -0600 Subject: [PATCH 19/23] Add tests that check various cases for OpenMC depletion input --- .../constant_fission_yield_input.json | 30 ++++++++ .../cutoff_fission_yield_input.json | 33 +++++++++ .../openmc_data/flux_reaction_rate_input.json | 33 +++++++++ tests/unit_tests/test_app.py | 70 +++++++++++++++---- 4 files changed, 154 insertions(+), 12 deletions(-) create mode 100644 tests/openmc_data/constant_fission_yield_input.json create mode 100644 tests/openmc_data/cutoff_fission_yield_input.json create mode 100644 tests/openmc_data/flux_reaction_rate_input.json diff --git a/tests/openmc_data/constant_fission_yield_input.json b/tests/openmc_data/constant_fission_yield_input.json new file mode 100644 index 000000000..f6497b641 --- /dev/null +++ b/tests/openmc_data/constant_fission_yield_input.json @@ -0,0 +1,30 @@ +{ + "proc_input_file": "../tap_processes.json", + "dot_input_file": "../tap_paths.dot", + "n_depletion_steps": 2, + "depcode": { + "codename": "openmc", + "template_input_file_path": { + "materials": "tap_materials.xml", + "settings": "tap_settings.xml" + }, + "geo_file_paths": ["tap_geometry_base.xml"], + "chain_file_path": "test_chain.xml", + "depletion_settings": { + "operator_kwargs": { + "fission_yield_mode": "constant", + "fission_yield_opts": {"energy": 0.0253} + } + } + }, + "simulation": { + "sim_name": "tap_test_simulation_openmc" + }, + "reactor": { + "volume": 1.0, + "mass_flowrate": 9.92E+6, + "power_levels": [ 1.250E+9 ], + "depletion_timesteps": [ 5 ], + "timestep_units": "d" + } +} diff --git a/tests/openmc_data/cutoff_fission_yield_input.json b/tests/openmc_data/cutoff_fission_yield_input.json new file mode 100644 index 000000000..656f111c2 --- /dev/null +++ b/tests/openmc_data/cutoff_fission_yield_input.json @@ -0,0 +1,33 @@ +{ + "proc_input_file": "../tap_processes.json", + "dot_input_file": "../tap_paths.dot", + "n_depletion_steps": 2, + "depcode": { + "codename": "openmc", + "template_input_file_path": { + "materials": "tap_materials.xml", + "settings": "tap_settings.xml" + }, + "geo_file_paths": ["tap_geometry_base.xml"], + "chain_file_path": "test_chain.xml", + "depletion_settings": { + "operator_kwargs": { + "fission_yield_mode": "cutoff", + "fission_yield_opts": { + "cutoff": 112.0, + "thermal_energy": 0.0253, + "fast_energy": 500000.0} + } + } + }, + "simulation": { + "sim_name": "tap_test_simulation_openmc" + }, + "reactor": { + "volume": 1.0, + "mass_flowrate": 9.92E+6, + "power_levels": [ 1.250E+9 ], + "depletion_timesteps": [ 5 ], + "timestep_units": "d" + } +} diff --git a/tests/openmc_data/flux_reaction_rate_input.json b/tests/openmc_data/flux_reaction_rate_input.json new file mode 100644 index 000000000..5d3c79774 --- /dev/null +++ b/tests/openmc_data/flux_reaction_rate_input.json @@ -0,0 +1,33 @@ +{ + "proc_input_file": "../tap_processes.json", + "dot_input_file": "../tap_paths.dot", + "n_depletion_steps": 2, + "depcode": { + "codename": "openmc", + "template_input_file_path": { + "materials": "tap_materials.xml", + "settings": "tap_settings.xml" + }, + "geo_file_paths": ["tap_geometry_base.xml"], + "chain_file_path": "test_chain.xml", + "depletion_settings": { + "operator_kwargs": { + "reaction_rate_mode": "flux", + "reaction_rate_opts": { + "energies": [0.0253, 500000.0], + "reactions": ["(n,gamma)"], + "nuclides": ["U235", "Pu239"]} + } + } + }, + "simulation": { + "sim_name": "tap_test_simulation_openmc" + }, + "reactor": { + "volume": 1.0, + "mass_flowrate": 9.92E+6, + "power_levels": [ 1.250E+9 ], + "depletion_timesteps": [ 5 ], + "timestep_units": "d" + } +} diff --git a/tests/unit_tests/test_app.py b/tests/unit_tests/test_app.py index 197f583fd..87c7f4361 100644 --- a/tests/unit_tests/test_app.py +++ b/tests/unit_tests/test_app.py @@ -6,6 +6,21 @@ from saltproc.app import read_main_input, get_extraction_processes from saltproc.app import get_feeds, get_extraction_process_paths +expected_depletion_settings = {'method': 'predictor', + 'final_step': True, + 'operator_kwargs': { + 'diff_burnable_mats': False, + 'normalization_mode': 'fission-q', + 'fission_q': None, + 'dilute_initial': 1000, + 'fission_yield_mode': 'constant', + 'fission_yield_opts': None, + 'reaction_rate_mode': 'direct', + 'reaction_rate_opts': None, + 'reduce_chain': False, + 'reduce_chain_level': None}, + 'output': True, + 'integrator_kwargs': {}} @pytest.mark.parametrize("codename, ext", [ ("serpent", ".ini"), @@ -30,18 +45,7 @@ def test_read_main_input(cwd, codename, ext): assert depcode_input['chain_file_path'] == \ str((input_path / 'test_chain.xml').resolve()) assert depcode_input['depletion_settings'] == \ - {'method': 'predictor', - 'final_step': True, - 'operator_kwargs': {'normalization_mode': 'fission-q', - 'fission_q': None, - 'dilute_initial': 1000, - 'fission_yield_mode': 'constant', - 'reaction_rate_mode': 'direct', - 'reaction_rate_opts': None, - 'reduce_chain': False, - 'reduce_chain_level': None}, - 'output': True, - 'integrator_kwargs': {}} + expected_depletion_settings elif codename == 'serpent': assert depcode_input['template_input_file_path'] == \ str((input_path / 'tap_template.ini').resolve()) @@ -59,6 +63,48 @@ def test_read_main_input(cwd, codename, ext): assert reactor_input['timestep_units'] == 'd' assert reactor_input['timestep_type'] == 'stepwise' +@pytest.mark.parametrize("filename", [ + "constant_fission_yield", + "cutoff_fission_yield", + "flux_reaction_rate"]) +def test_openmc_depletion_settings(cwd, filename): + data_path = 'openmc_data' + data_path = cwd / data_path + main_input = str(data_path / f'{filename}_input.json') + out = read_main_input(main_input) + input_path, process_input_file, path_input_file, object_input = out + depcode_input, simulation_input, reactor_input = object_input + + assert depcode_input['template_input_file_path'] == \ + {'materials': str((input_path / 'tap_materials.xml').resolve()), + 'settings': str((input_path / 'tap_settings.xml').resolve())} + assert depcode_input['chain_file_path'] == \ + str((input_path / 'test_chain.xml').resolve()) + + modified_operator_kwargs = expected_depletion_settings['operator_kwargs'].copy() + if filename == 'constant_fission_yield': + + operator_kwargs = {'fission_yield_opts': {'energy': 0.0253}} + modified_operator_kwargs.update(operator_kwargs) + elif filename == 'cutoff_fission_yield': + operator_kwargs = {'fission_yield_mode': 'cutoff', + 'fission_yield_opts': {'cutoff': 112.0, + 'thermal_energy': 0.0253, + 'fast_energy': 5.0e5}} + modified_operator_kwargs.update(operator_kwargs) + + elif filename == 'flux_reaction_rate': + operator_kwargs = {'reaction_rate_mode': 'flux', + 'reaction_rate_opts': {'energies': [0.0253, 500000.0], + 'reactions': ['(n,gamma)'], + 'nuclides': ['U235', 'Pu239']} + } + modified_operator_kwargs.update(operator_kwargs) + + assert depcode_input['depletion_settings']['operator_kwargs'] == \ + modified_operator_kwargs + + def test_get_extraction_processes(proc_test_file): procs = get_extraction_processes(proc_test_file) assert procs['fuel']['heat_exchanger'].volume == 1.37E+7 From 93446192ccfb1eab278e835f38a30fd149a4a66a Mon Sep 17 00:00:00 2001 From: yardasol Date: Wed, 11 Jan 2023 16:48:47 -0600 Subject: [PATCH 20/23] changes from @LukeSeifert's review --- doc/fileformatspec/reactor_input.rst | 4 +-- saltproc/app.py | 54 +++++++++++++--------------- saltproc/input_schema.json | 4 +-- saltproc/openmc_depcode.py | 4 +-- 4 files changed, 31 insertions(+), 35 deletions(-) diff --git a/doc/fileformatspec/reactor_input.rst b/doc/fileformatspec/reactor_input.rst index 4d4477521..c61378945 100644 --- a/doc/fileformatspec/reactor_input.rst +++ b/doc/fileformatspec/reactor_input.rst @@ -67,7 +67,7 @@ Required properties: ``volume``, ``mass_flowrate``, ``power_levels``, ----------------------- :description: - Depletion step length(s) + Depletion timestep size or list of timestep sizes :type: ``array`` @@ -115,4 +115,4 @@ Required properties: ``volume``, ``mass_flowrate``, ``power_levels``, ``string`` :enum: - ``s``, ``sec``, ``min``, ``minute``, ``h``, ``hr``, ``hour``, ``d``, ``day``, ``a``, ``year``, ``MWd/kg``, ``mwd/kg``, ``MWD/KG``, ``MWD/kg``, ``MWd/KG`` + ``s``, ``sec``, ``min``, ``minute``, ``h``, ``hr``, ``hour``, ``d``, ``day``, ``a``, ``year``, ``yr``, ``MWd/kg``, ``mwd/kg``, ``MWD/KG``, ``MWD/kg``, ``MWd/KG`` diff --git a/saltproc/app.py b/saltproc/app.py index 3dc66f575..45b28fdb6 100644 --- a/saltproc/app.py +++ b/saltproc/app.py @@ -15,6 +15,9 @@ from ._schema_default import DefaultValidatingValidator +_codename_map = {'serpent': SerpentDepcode, + 'openmc': OpenMCDepcode} + def run(): """ Inititializes main run""" nodes, cores, saltproc_input = parse_arguments() @@ -152,42 +155,40 @@ def read_main_input(main_inp_file): input_schema = (Path(__file__).parents[0] / 'input_schema.json') with open(main_inp_file) as f: - obj = json.load(f) + input_parameters = json.load(f) with open(input_schema) as s: schema = json.load(s) try: - DefaultValidatingValidator(schema).validate(obj) + DefaultValidatingValidator(schema).validate(input_parameters) #jsonschema.validate(instance=j, schema=v) except jsonschema.exceptions.ValidationError: print("Your input file is improperly structured.\ Please see saltproc/tests/test.json for an example.") - j = obj - print(j['reactor']['timestep_type']) # Global input path input_path = (Path.cwd() / Path(f.name).parents[0]) # Saltproc settings process_file = str((input_path / - j['proc_input_file']).resolve()) + input_parameters['proc_input_file']).resolve()) dot_file = str(( input_path / - j['dot_input_file']).resolve()) - output_path = j['output_path'] - n_depletion_steps = j['n_depletion_steps'] + input_parameters['dot_input_file']).resolve()) + output_path = input_parameters['output_path'] + n_depletion_steps = input_parameters['n_depletion_steps'] # Global output path output_path = (input_path / output_path) - j['output_path'] = output_path.resolve() + input_parameters['output_path'] = output_path.resolve() # Create output directoy if it doesn't exist - if not Path(j['output_path']).exists(): - Path(j['output_path']).mkdir(parents=True) + if not Path(input_parameters['output_path']).exists(): + Path(input_parameters['output_path']).mkdir(parents=True) # Class settings - depcode_input = j['depcode'] - simulation_input = j['simulation'] - reactor_input = j['reactor'] + depcode_input = input_parameters['depcode'] + simulation_input = input_parameters['simulation'] + reactor_input = input_parameters['reactor'] depcode_input['codename'] = depcode_input['codename'].lower() if depcode_input['codename'] == 'serpent': @@ -243,18 +244,14 @@ def _print_simulation_input_info(simulation_input, depcode_input): def _create_depcode_object(depcode_input): """Helper function for `run()` """ codename = depcode_input.pop('codename') - if codename == 'serpent': - depcode = SerpentDepcode - elif codename == 'openmc': - depcode = OpenMCDepcode - chain_file_path = depcode_input.pop('chain_file_path') + depcode = _codename_map[codename] + + if codename == 'openmc': depletion_settings = depcode_input.pop('depletion_settings') - else: - raise ValueError( - f'{codename} is not a supported depletion code.' - 'Accepts: "serpent" or "openmc".') + chain_file_path = depcode_input.pop('chain_file_path') depcode = depcode(**depcode_input) + if codename == 'openmc': depcode.chain_file_path = chain_file_path depcode.depletion_settings = depletion_settings @@ -298,11 +295,10 @@ def _process_main_input_reactor_params(reactor_input, n_depletion_steps, codenam raise ValueError('There must be a positive integer number' ' of depletion steps. Provided' f' n_depletion_steps: {n_depletion_steps}') - else: - if len(depletion_timesteps) == 1: - depletion_timesteps = depletion_timesteps * n_depletion_steps - if len(power_levels) == 1: - power_levels = power_levels * n_depletion_steps + if len(depletion_timesteps) == 1: + depletion_timesteps = depletion_timesteps * n_depletion_steps + if len(power_levels) == 1: + power_levels = power_levels * n_depletion_steps if len(depletion_timesteps) != len(power_levels): raise ValueError('depletion_timesteps and power_levels length mismatch:' @@ -323,7 +319,7 @@ def _process_main_input_reactor_params(reactor_input, n_depletion_steps, codenam depletion_timesteps /= 60 * 24 elif timestep_units in ('h', 'hr', 'hour'): depletion_timesteps /= 24 - elif timestep_units in ('a', 'year'): + elif timestep_units in ('a', 'year', 'yr'): depletion_timesteps *= 365.25 else: raise IOError(f'Unrecognized time unit: {timestep_units}') diff --git a/saltproc/input_schema.json b/saltproc/input_schema.json index 41f63ffb4..dafcd559c 100644 --- a/saltproc/input_schema.json +++ b/saltproc/input_schema.json @@ -292,7 +292,7 @@ "minItems": 1, "uniqueItems": false}, "depletion_timesteps": { - "description": "Depletion step length(s)", + "description": "Depletion timestep size or list of timestep sizes", "type": "array", "items": { "type": "number", "minimum": 0}, "minItems": 1, @@ -305,7 +305,7 @@ "timestep_units": { "description": "Timestep unit", "type": "string", - "enum": ["s", "sec", "min", "minute", "h", "hr", "hour", "d", "day", "a", "year", "MWd/kg", "mwd/kg", "MWD/KG", "MWD/kg", "MWd/KG"] + "enum": ["s", "sec", "min", "minute", "h", "hr", "hour", "d", "day", "a", "yr", "year", "MWd/kg", "mwd/kg", "MWD/KG", "MWD/kg", "MWd/KG"] } }, "default": {}, diff --git a/saltproc/openmc_depcode.py b/saltproc/openmc_depcode.py index 47b2ae502..32d0cc6b2 100644 --- a/saltproc/openmc_depcode.py +++ b/saltproc/openmc_depcode.py @@ -37,7 +37,7 @@ class OpenMCDepcode(Depcode): inactive_cycles : int Number of inactive cycles. depletion_settings : dict - ... + Keyword arguments to pass to :func:`openmc.model.deplete()`. chain_file_path : str Path to depletion chain file @@ -143,7 +143,7 @@ def run_depletion_step(self, cores, nodes): self.runtime_inputfile['geometry'], '--settings', self.runtime_inputfile['settings'], - '--tallites', + '--tallies', self.runtime_inputfile['tallies'], '--directory', str(self.output_path)) From df31b93171385da2f63ace876dc5f20186a1f35f Mon Sep 17 00:00:00 2001 From: yardasol Date: Tue, 17 Jan 2023 13:20:26 -0600 Subject: [PATCH 21/23] Changes from @LukeSeifert's second review --- saltproc/app.py | 23 +++++++---------------- saltproc/input_schema.json | 2 +- saltproc/openmc_depcode.py | 16 +++++++++++----- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/saltproc/app.py b/saltproc/app.py index 45b28fdb6..dfd4aa591 100644 --- a/saltproc/app.py +++ b/saltproc/app.py @@ -160,7 +160,6 @@ def read_main_input(main_inp_file): schema = json.load(s) try: DefaultValidatingValidator(schema).validate(input_parameters) - #jsonschema.validate(instance=j, schema=v) except jsonschema.exceptions.ValidationError: print("Your input file is improperly structured.\ Please see saltproc/tests/test.json for an example.") @@ -205,8 +204,8 @@ def read_main_input(main_inp_file): depcode_input['chain_file_path']).resolve()) else: raise ValueError( - f'{codename} is not a supported depletion code.' - 'Accepts: "serpent" or "openmc".') + f'{depcode_input["codename"]} is not a supported depletion' + ' code. Accepts: "serpent" or "openmc".') depcode_input['output_path'] = output_path geo_list = depcode_input['geo_file_paths'] @@ -245,17 +244,7 @@ def _create_depcode_object(depcode_input): """Helper function for `run()` """ codename = depcode_input.pop('codename') depcode = _codename_map[codename] - - if codename == 'openmc': - depletion_settings = depcode_input.pop('depletion_settings') - chain_file_path = depcode_input.pop('chain_file_path') - depcode = depcode(**depcode_input) - - if codename == 'openmc': - depcode.chain_file_path = chain_file_path - depcode.depletion_settings = depletion_settings - depcode_input['codename'] = codename return depcode @@ -280,11 +269,13 @@ def _create_reactor_object(reactor_input): return msr -def _process_main_input_reactor_params(reactor_input, n_depletion_steps, codename): +def _process_main_input_reactor_params(reactor_input, + n_depletion_steps, + codename): """ Process SaltProc reactor class input parameters based on the value and - data type of the `n_depletion_steps` parameter, and throw errors if the input - parameters are incorrect. + data type of the `n_depletion_steps` parameter as well as the depletion code + being used, and throw errors if the input parameters are incorrect. """ depletion_timesteps = reactor_input['depletion_timesteps'] diff --git a/saltproc/input_schema.json b/saltproc/input_schema.json index dafcd559c..73ca0936e 100644 --- a/saltproc/input_schema.json +++ b/saltproc/input_schema.json @@ -271,7 +271,7 @@ "default": false} }, "default": {}, - "required": ["sim_name", "db_name"] + "required": ["sim_name"] }, "reactor": { "description": "Reactor class input parameters", diff --git a/saltproc/openmc_depcode.py b/saltproc/openmc_depcode.py index 32d0cc6b2..1dea7aa44 100644 --- a/saltproc/openmc_depcode.py +++ b/saltproc/openmc_depcode.py @@ -36,10 +36,6 @@ class OpenMCDepcode(Depcode): Number of active cycles. inactive_cycles : int Number of inactive cycles. - depletion_settings : dict - Keyword arguments to pass to :func:`openmc.model.deplete()`. - chain_file_path : str - Path to depletion chain file """ @@ -47,7 +43,10 @@ def __init__(self, output_path, exec_path, template_input_file_path, - geo_file_paths): + geo_file_paths, + depletion_settings, + chain_file_path + ): """Initialize a OpenMCDepcode object. Parameters @@ -65,6 +64,10 @@ def __init__(self, Path to file that contains the reactor geometry. List of `str` if reactivity control by switching geometry is `On` or just `str` otherwise. + depletion_settings : dict + Keyword arguments to pass to :func:`openmc.model.deplete()`. + chain_file_path : str + Path to depletion chain file """ @@ -74,6 +77,9 @@ def __init__(self, else: exec_path == (Path(template_input_file_path['settings'].parents[0]) / exec_path).resolve() + self.depletion_settings = depletion_settings + self.chain_file_path = chain_file_path + super().__init__("openmc", output_path, exec_path, From 80625ae25ac0132285e8dc53695ff2b074e4aec8 Mon Sep 17 00:00:00 2001 From: yardasol Date: Tue, 17 Jan 2023 15:01:34 -0600 Subject: [PATCH 22/23] Add tests for timestep validating and scaling functions; changes from review of @samgdotson Co-authored-by: Sam Dotson --- saltproc/_schema_default.py | 2 +- saltproc/app.py | 88 +++++++++++++++++++++++++----------- saltproc/openmc_deplete.py | 1 - tests/conftest.py | 30 ++++++------ tests/unit_tests/test_app.py | 72 +++++++++++++++++++++++++++++ 5 files changed, 149 insertions(+), 44 deletions(-) diff --git a/saltproc/_schema_default.py b/saltproc/_schema_default.py index e91346a68..f7a7a0bac 100644 --- a/saltproc/_schema_default.py +++ b/saltproc/_schema_default.py @@ -18,4 +18,4 @@ def set_defaults(validator, properties, instance, schema): ) -DefaultValidatingValidator = extend_with_default(Draft202012Validator) +DefaultFillingValidator = extend_with_default(Draft202012Validator) diff --git a/saltproc/app.py b/saltproc/app.py index dfd4aa591..91c5f4a63 100644 --- a/saltproc/app.py +++ b/saltproc/app.py @@ -12,12 +12,20 @@ from saltproc import SerpentDepcode, OpenMCDepcode, Simulation, Reactor from saltproc import Process, Sparger, Separator, Materialflow -from ._schema_default import DefaultValidatingValidator +# Validator that fills defualt values of JSON schema before validating +from saltproc._schema_default import DefaultFillingValidator -_codename_map = {'serpent': SerpentDepcode, + +CODENAME_MAP = {'serpent': SerpentDepcode, 'openmc': OpenMCDepcode} +SECOND_UNITS = ('s', 'sec') +MINUTE_UNITS = ('min', 'minute') +HOUR_UNITS = ('h', 'hr', 'hour') +DAY_UNITS = ('d', 'day') +YEAR_UNITS = ('a', 'year', 'yr') + def run(): """ Inititializes main run""" nodes, cores, saltproc_input = parse_arguments() @@ -159,7 +167,7 @@ def read_main_input(main_inp_file): with open(input_schema) as s: schema = json.load(s) try: - DefaultValidatingValidator(schema).validate(input_parameters) + DefaultFillingValidator(schema).validate(input_parameters) except jsonschema.exceptions.ValidationError: print("Your input file is improperly structured.\ Please see saltproc/tests/test.json for an example.") @@ -189,12 +197,12 @@ def read_main_input(main_inp_file): simulation_input = input_parameters['simulation'] reactor_input = input_parameters['reactor'] - depcode_input['codename'] = depcode_input['codename'].lower() - if depcode_input['codename'] == 'serpent': + codename = depcode_input['codename'].lower() + if codename == 'serpent': depcode_input['template_input_file_path'] = str(( input_path / depcode_input['template_input_file_path']).resolve()) - elif depcode_input['codename'] == 'openmc': + elif codename == 'openmc': for key in depcode_input['template_input_file_path']: value = depcode_input['template_input_file_path'][key] depcode_input['template_input_file_path'][key] = str(( @@ -203,10 +211,10 @@ def read_main_input(main_inp_file): str((input_path / depcode_input['chain_file_path']).resolve()) else: - raise ValueError( - f'{depcode_input["codename"]} is not a supported depletion' - ' code. Accepts: "serpent" or "openmc".') + raise ValueError(f'{codename} is not a supported depletion code.' + ' Accepts: "serpent" or "openmc".') + depcode_input['codename'] = codename depcode_input['output_path'] = output_path geo_list = depcode_input['geo_file_paths'] @@ -243,7 +251,7 @@ def _print_simulation_input_info(simulation_input, depcode_input): def _create_depcode_object(depcode_input): """Helper function for `run()` """ codename = depcode_input.pop('codename') - depcode = _codename_map[codename] + depcode = CODENAME_MAP[codename] depcode = depcode(**depcode_input) depcode_input['codename'] = codename @@ -278,47 +286,75 @@ def _process_main_input_reactor_params(reactor_input, being used, and throw errors if the input parameters are incorrect. """ - depletion_timesteps = reactor_input['depletion_timesteps'] - power_levels = reactor_input['power_levels'] + depletion_timesteps = np.array(reactor_input['depletion_timesteps']) + power_levels = np.array(reactor_input['power_levels']) + depletion_timesteps, power_levels = \ + _validate_depletion_timesteps_power_levels(n_depletion_steps, + depletion_timesteps, + power_levels) + + + if reactor_input['timestep_type'] == 'cumulative': + depletion_timesteps = _convert_cumulative_to_stepwise(depletion_timesteps) + + timestep_units = reactor_input['timestep_units'] + depletion_timesteps = _scale_depletion_timesteps(timestep_units, + depletion_timesteps, + codename) + + reactor_input['depletion_timesteps'] = list(depletion_timesteps) + reactor_input['power_levels'] = list(power_levels) + return reactor_input + + +def _validate_depletion_timesteps_power_levels(n_depletion_steps, + depletion_timesteps, + power_levels): + """Ensure that the number of depletion timesteps and power levels match + `n_depletion_steps` if it is given. Otherwise, compare the lengths of + `depletion_timesteps` and `power_levels`""" if n_depletion_steps is not None: if n_depletion_steps < 0.0 or not int: raise ValueError('There must be a positive integer number' ' of depletion steps. Provided' f' n_depletion_steps: {n_depletion_steps}') if len(depletion_timesteps) == 1: - depletion_timesteps = depletion_timesteps * n_depletion_steps + depletion_timesteps = np.repeat(depletion_timesteps, n_depletion_steps) if len(power_levels) == 1: - power_levels = power_levels * n_depletion_steps + power_levels = np.repeat(power_levels, n_depletion_steps) if len(depletion_timesteps) != len(power_levels): raise ValueError('depletion_timesteps and power_levels length mismatch:' f' depletion_timesteps has {len(depletion_timesteps)}' f' entries and power_levels has {len(power_levels)}' ' entries.') + else: + return depletion_timesteps, power_levels - if reactor_input['timestep_type'] == 'cumulative': - ts = list(np.diff(depletion_timesteps)) - depletion_timesteps = depletion_timesteps[0] + ts - timestep_units = reactor_input['timestep_units'] +def _convert_cumulative_to_stepwise(depletion_timesteps): + ts = np.diff(depletion_timesteps) + return np.concatenate(([depletion_timesteps[0]], ts)) + + +def _scale_depletion_timesteps(timestep_units, depletion_timesteps, codename): + """Scale `depletion_timesteps` to the correct value based on `timestep_units`""" # serpent base timestep units are days or mwd/kg - if not(timestep_units in ('d', 'day')) and time_units.lower() != 'mwd/kg' and codename == 'serpent': - if timestep_units in ('s', 'sec'): + if not(timestep_units in DAY_UNITS) and timestep_units.lower() != 'mwd/kg' and codename == 'serpent': + if timestep_units in SECOND_UNITS: depletion_timesteps /= 60 * 60 * 24 - elif timestep_units in ('min', 'minute'): + elif timestep_units in MINUTE_UNITS: depletion_timesteps /= 60 * 24 - elif timestep_units in ('h', 'hr', 'hour'): + elif timestep_units in HOUR_UNITS: depletion_timesteps /= 24 - elif timestep_units in ('a', 'year', 'yr'): + elif timestep_units in YEAR_UNITS: depletion_timesteps *= 365.25 else: raise IOError(f'Unrecognized time unit: {timestep_units}') - reactor_input['depletion_timesteps'] = depletion_timesteps - reactor_input['power_levels'] = power_levels + return depletion_timesteps - return reactor_input def reprocess_materials(mats, process_file, dot_file): """Applies extraction reprocessing scheme to burnable materials. diff --git a/saltproc/openmc_deplete.py b/saltproc/openmc_deplete.py index 20c6f1916..2ea23ce49 100644 --- a/saltproc/openmc_deplete.py +++ b/saltproc/openmc_deplete.py @@ -62,7 +62,6 @@ def parse_arguments(): settings=settings, tallies=tallies) -depletion_settings = {} with open(f'{args.directory}/depletion_settings.json') as f: depletion_settings = json.load(f) diff --git a/tests/conftest.py b/tests/conftest.py index 7363bbaf4..87829b3fc 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -26,19 +26,16 @@ def path_test_file(cwd): def serpent_runtime(cwd, tmpdir_factory): """SaltProc objects for Serpent unit tests""" saltproc_input = str(cwd / 'serpent_data' / 'tap_input.json') - _, _, _, object_input = read_main_input(saltproc_input) - depcode = _create_depcode_object(object_input[0]) + depcode_input, simulation_input, reactor_input = \ + read_main_input(saltproc_input)[3] + depcode = _create_depcode_object(depcode_input) depcode.runtime_inputfile = str(cwd / 'serpent_data' / 'tap_reference') output_dir = str(depcode.output_path).split('/')[-1] depcode.output_path = tmpdir_factory.mktemp(f'serpent_{output_dir}') - simulation = _create_simulation_object(object_input[1], depcode, 1, 1) - #db_path=str( - # cwd / - # 'serpent_data' / - # 'tap_reference_db.h5')) + simulation = _create_simulation_object(simulation_input, depcode, 1, 1) - reactor = _create_reactor_object(object_input[2]) + reactor = _create_reactor_object(reactor_input) return depcode, simulation, reactor @@ -46,20 +43,20 @@ def serpent_runtime(cwd, tmpdir_factory): @pytest.fixture(scope='session') def serpent_depcode(serpent_runtime): """SerpentDepcode object for unit tests""" - depcode, _, _ = serpent_runtime + depcode = serpent_runtime[0] return depcode @pytest.fixture(scope='session') def serpent_reactor(serpent_runtime): - _, _, reactor = serpent_runtime + reactor = serpent_runtime[2] return reactor @pytest.fixture(scope='session') def simulation(serpent_runtime): """Simulation object for unit tests""" - _, simulation, _ = serpent_runtime + simulation = serpent_runtime[1] return simulation @@ -67,11 +64,12 @@ def simulation(serpent_runtime): def openmc_runtime(cwd, tmpdir_factory): """SaltProc objects for OpenMC unit tests""" saltproc_input = str(cwd / 'openmc_data' / 'tap_input.json') - _, _, _, object_input = read_main_input(saltproc_input) - depcode = _create_depcode_object(object_input[0]) + depcode_input, simulation_input, reactor_input = \ + read_main_input(saltproc_input)[3] + depcode = _create_depcode_object(depcode_input) output_dir = str(depcode.output_path).split('/')[-1] depcode.output_path = tmpdir_factory.mktemp(f'openmc_{output_dir}') - reactor = _create_reactor_object(object_input[2]) + reactor = _create_reactor_object(reactor_input) return depcode, reactor @@ -79,11 +77,11 @@ def openmc_runtime(cwd, tmpdir_factory): @pytest.fixture(scope='session') def openmc_depcode(openmc_runtime): """OpenMCDepcode objects for unit tests""" - depcode, _ = openmc_runtime + depcode = openmc_runtime[0] return depcode @pytest.fixture(scope='session') def openmc_reactor(openmc_runtime): - _, reactor = openmc_runtime + reactor = openmc_runtime[1] return reactor diff --git a/tests/unit_tests/test_app.py b/tests/unit_tests/test_app.py index 87c7f4361..03ab4d27c 100644 --- a/tests/unit_tests/test_app.py +++ b/tests/unit_tests/test_app.py @@ -4,6 +4,11 @@ import numpy as np import pytest from saltproc.app import read_main_input, get_extraction_processes +from saltproc.app import (_validate_depletion_timesteps_power_levels, + _convert_cumulative_to_stepwise, + _scale_depletion_timesteps) +from saltproc.app import (SECOND_UNITS, MINUTE_UNITS, HOUR_UNITS, DAY_UNITS, + YEAR_UNITS) from saltproc.app import get_feeds, get_extraction_process_paths expected_depletion_settings = {'method': 'predictor', @@ -63,6 +68,73 @@ def test_read_main_input(cwd, codename, ext): assert reactor_input['timestep_units'] == 'd' assert reactor_input['timestep_type'] == 'stepwise' + +def test_convert_cumulative_to_stepwise(): + timesteps = _convert_cumulative_to_stepwise([2, 4, 6]) + np.testing.assert_equal(timesteps, [2, 2, 2]) + + +@pytest.mark.parametrize("n_depletion_steps, depletion_timesteps, power_levels, throws_error", [ + (3, [1], [1], False), + (3, [1], [1, 1, 1], False), + (3, [1, 1, 1], [1], False), + (3, [1], [1, 1], True), + (3, [1, 1], [1, 1], False), + (None, [1, 1, 1], [1, 1, 1], False), + (None, [1], [1, 1, 1], True), + (None, [1, 1, 1], [1], True)]) +def test_validate_depletion_timesteps_power_levels(n_depletion_steps, + depletion_timesteps, + power_levels, + throws_error): + if throws_error: + with pytest.raises(ValueError): + _validate_depletion_timesteps_power_levels(n_depletion_steps, + depletion_timesteps, + power_levels) + else: + depletion_steps, power_levels = \ + _validate_depletion_timesteps_power_levels(n_depletion_steps, + depletion_timesteps, + power_levels) + assert (len(depletion_steps) == 2 or len(depletion_steps) == 3) + + +@pytest.mark.parametrize("expected_depletion_timesteps, timestep_units", [ + ([1/86400], SECOND_UNITS), + ([1/1440], MINUTE_UNITS), + ([1/24], HOUR_UNITS), + ([1.], DAY_UNITS), + ([365.25], YEAR_UNITS) +]) +def test_scale_depletion_timesteps(expected_depletion_timesteps, + timestep_units): + expected_depletion_timesteps = np.array(expected_depletion_timesteps) + base_timestep = np.array([1.]) + for unit in timestep_units: + input_timestep = base_timestep.copy() + scaled_timesteps = \ + _scale_depletion_timesteps(unit, input_timestep, 'serpent') + np.testing.assert_equal(scaled_timesteps, expected_depletion_timesteps) + input_timestep = base_timestep.copy() + scaled_timesteps = \ + _scale_depletion_timesteps(unit, input_timestep, 'openmc') + np.testing.assert_equal(scaled_timesteps, base_timestep) + input_timestep = base_timestep.copy() + scaled_timesteps = \ + _scale_depletion_timesteps('MWD/KG', input_timestep, 'serpent') + np.testing.assert_equal(scaled_timesteps, base_timestep) + input_timestep = base_timestep.copy() + scaled_timesteps = \ + _scale_depletion_timesteps('MWD/KG', input_timestep, 'openmc') + np.testing.assert_equal(scaled_timesteps, base_timestep) + + bad_unit = 'months' + with pytest.raises(IOError, + match=f'Unrecognized time unit: {bad_unit}'): + _scale_depletion_timesteps(bad_unit, [1], 'serpent') + + @pytest.mark.parametrize("filename", [ "constant_fission_yield", "cutoff_fission_yield", From 1f1d8affad39f394d708477fee8061e666d4e112 Mon Sep 17 00:00:00 2001 From: yardasol Date: Wed, 18 Jan 2023 12:00:35 -0600 Subject: [PATCH 23/23] update the endfb71 msbr serpent model to match the new syntax --- examples/msbr/msbr_endfb71.serpent | 2 +- examples/msbr/msbr_endfb71_main.json | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/examples/msbr/msbr_endfb71.serpent b/examples/msbr/msbr_endfb71.serpent index ee9c9bf7a..5f0cd9943 100644 --- a/examples/msbr/msbr_endfb71.serpent +++ b/examples/msbr/msbr_endfb71.serpent @@ -58,7 +58,7 @@ set sfylib "endfb71.sfy" % --- Neutron population and criticality cycles: -set pop 10000 125 25 1.0 1 %500 400 60 1.0 1 %30000 400 100 1.0 1 +set pop 300 400 10 set gcu -1 %set usym 0 3 2 0.0 0.0 0 90 diff --git a/examples/msbr/msbr_endfb71_main.json b/examples/msbr/msbr_endfb71_main.json index 485692235..2d0cd0d39 100644 --- a/examples/msbr/msbr_endfb71_main.json +++ b/examples/msbr/msbr_endfb71_main.json @@ -1,15 +1,11 @@ { "proc_input_file": "msbr_objects.json", "dot_input_file": "msbr.dot", - "output_path": "./data", - "num_depsteps": 12, + "n_depletion_steps": 12, "depcode": { "codename": "serpent", "exec_path": "sss2", "template_input_file_path": "./msbr_endfb71.serpent", - "npop": 50, - "active_cycles": 20, - "inactive_cycles": 20, "geo_file_paths": ["./geometry/msbr_full.ini"] }, "simulation": { @@ -22,6 +18,7 @@ "volume": 1.0, "mass_flowrate": 9920000, "power_levels": [ 2250000000 ], - "dep_step_length_cumulative": [ 3 ] + "depletion_timesteps": [3], + "timestep_units": "d" } }