diff --git a/doc/installation.rst b/doc/installation.rst index faf7b7a79..923471212 100644 --- a/doc/installation.rst +++ b/doc/installation.rst @@ -10,6 +10,7 @@ SaltProc has the following dependencies: #. `PyTables`_ #. `NetworkX`_ #. `PyDotPlus`_ + #. `jsonschema`_ .. _Serpent: http://montecarlo.vtt.fi .. _PyNE: http://pyne.io @@ -24,6 +25,7 @@ SaltProc has the following dependencies: .. _matplotlib: http://matplotlib.org .. _ViTables: http://vitables.org .. _GitHub: http://github.com/arfc/saltproc +.. _jsonschema: https://github.com/Julian/jsonschema .. _conda package manager: https://docs.conda.io/en/latest/ .. _mamba: https://github.com/mamba-org/mamba diff --git a/environment.yml b/environment.yml index 3cb0f07e7..8330b28f3 100644 --- a/environment.yml +++ b/environment.yml @@ -9,5 +9,6 @@ dependencies: - networkx - pydotplus - pytest + - jsonschema - pip: - argparse==1.4.0 diff --git a/examples/msbr/msbr.serpent b/examples/msbr/msbr.serpent index acbc8974b..e381a7f18 100644 --- a/examples/msbr/msbr.serpent +++ b/examples/msbr/msbr.serpent @@ -51,9 +51,9 @@ therm gr 900 gre7.18t gre7.20t % 900K C-nat set title "MSBR Saltproc long-term, 91% removal, BOL, fresh fuel, ENDF" -set acelib "/home/andrei2/serpent/xsdata/endfb7/sss_endfb7.xsdata" -set declib "/home/andrei2/serpent/xsdata/endfb7/sss_endfb7.dec" -set nfylib "/home/andrei2/serpent/xsdata/endfb7/sss_endfb7.nfy" +set acelib "sss_endfb7.xsdata" +set declib "sss_endfb7.dec" +set nfylib "sss_endfb7.nfy" %set acelib "sss_jeff312.xsdata" %set declib "sss_jeff33.dec" diff --git a/examples/msbr/msbr_main.json b/examples/msbr/msbr_main.json index 202c1ea19..7355f9be2 100644 --- a/examples/msbr/msbr_main.json +++ b/examples/msbr/msbr_main.json @@ -1,18 +1,29 @@ { - "Path to Serpent executable": "sss2", - "File containing processing system objects": "msbr_objects.json", - "Graph file containing processing system structure": "msbr.dot", - "User's Serpent input file with reactor model": "msbr.serpent", - "Path output data storing folder": "../../saltproc/data/", - "Output HDF5 database file name": "msbr_kl_100_saltproc.h5", - "Number of neutrons per generation": 50, - "Number of active generations": 20, - "Number of inactive generations": 20, - "Restart simulation from the step when it stopped?": false, - "Geometry file/files to use in Serpent runs": "geometry/msbr_full.ini", - "Switch to another geometry when keff drops below 1?": false, - "Salt mass flow rate throughout reactor core (g/s)": 9920000, - "Number of steps for constant power and depletion interval case": 12, - "Depletion step interval or Cumulative time (end of step) (d)": 3, - "Reactor power or power step list during depletion step (W)": 2250000000 + "proc_input_file": "msbr_objects.json", + "dot_input_file": "msbr.dot", + "output_path": "./data", + "num_depsteps": 12, + "depcode": { + "codename": "serpent", + "exec_path": "sss2", + "template_inputfile_path": "./msbr.serpent", + "iter_inputfile": "saltproc_serpent", + "iter_matfile": "saltproc_mat", + "npop": 50, + "active_cycles": 20, + "inactive_cycles": 20, + "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 + }, + "reactor": { + "volume": 1.0, + "mass_flowrate": 9920000, + "power_levels": [ 2250000000 ], + "dep_step_length_cumulative": [ 3 ] + } } diff --git a/examples/tap/tap.serpent b/examples/tap/tap.serpent index 5e8501ce1..7f42ab30d 100644 --- a/examples/tap/tap.serpent +++ b/examples/tap/tap.serpent @@ -19,9 +19,9 @@ include "mats/non_burnable_mats.ini" set title "TAP MSR test 5-d step" %therm zrh_h 900 hzr05.32t hzr06.32t % 900K H-Zr -%set acelib "/home/andrei2/serpent/xsdata/endfb7/sss_endfb7.xsdata" -%set declib "/home/andrei2/serpent/xsdata/endfb7/sss_endfb7.dec" -%set nfylib "/home/andrei2/serpent/xsdata/endfb7/sss_endfb7.nfy" +%set acelib "sss_endfb7.xsdata" +%set declib "sss_endfb7.dec" +%set nfylib "sss_endfb7.nfy" set acelib "sss_jeff312.xsdata" set declib "sss_jeff33.dec" diff --git a/examples/tap/tap_main.json b/examples/tap/tap_main.json index 83f521bef..08cb8e0d6 100644 --- a/examples/tap/tap_main.json +++ b/examples/tap/tap_main.json @@ -1,34 +1,45 @@ { - "Path to Serpent executable": "sss2", - "File containing processing system objects": "tap_objects.json", - "Graph file containing processing system structure": "tap.dot", - "User's Serpent input file with reactor model": "tap.serpent", - "Path output data storing folder": "../../saltproc/data/", - "Output HDF5 database file name": "db_saltproc.h5", - "Number of neutrons per generation": 50, - "Number of active generations": 20, - "Number of inactive generations": 20, - "Restart simulation from the step when it stopped?": false, - "Geometry file/files to use in Serpent runs": [ - "geometry/347_base.ini", - "geometry/406.ini", - "geometry/427.ini", - "geometry/505.ini", - "geometry/576.ini", - "geometry/633.ini", - "geometry/681.ini", - "geometry/840.ini", - "geometry/880.ini", - "geometry/900.ini", - "geometry/988.ini", - "geometry/1126.ini", - "geometry/1338.ini", - "geometry/1498.ini", - "geometry/1668_all.ini" - ], - "Switch to another geometry when keff drops below 1?": true, - "Salt mass flow rate throughout reactor core (g/s)": 9920000, - "Number of steps for constant power and depletion interval case": 3, - "Depletion step interval or Cumulative time (end of step) (d)": 2, - "Reactor power or power step list during depletion step (W)": 1250000000 + "proc_input_file": "tap_objects.json", + "dot_input_file": "tap.dot", + "output_path": "./data", + "num_depsteps": 3, + "depcode": { + "codename": "serpent", + "exec_path": "sss2", + "template_inputfile_path": "./tap.serpent", + "iter_inputfile": "saltproc_serpent", + "iter_matfile": "saltproc_mat", + "npop": 50, + "active_cycles": 20, + "inactive_cycles": 20, + "geo_file_paths": [ + "./geometry/347_base.ini", + "./geometry/406.ini", + "./geometry/427.ini", + "./geometry/505.ini", + "./geometry/576.ini", + "./geometry/633.ini", + "./geometry/681.ini", + "./geometry/840.ini", + "./geometry/880.ini", + "./geometry/900.ini", + "./geometry/988.ini", + "./geometry/1126.ini", + "./geometry/1338.ini", + "./geometry/1498.ini", + "./geometry/1668_all.ini" + ] + }, + "simulation": { + "sim_name": "test_simulation", + "db_name": "db_saltproc.h5", + "restart_flag": false, + "adjust_geo": true + }, + "reactor": { + "volume": 1.0, + "mass_flowrate": 9920000, + "power_levels": [ 1250000000 ], + "dep_step_length_cumulative": [ 2 ] + } } diff --git a/examples/tap/tap_plot.serpent b/examples/tap/tap_plot.serpent index 209253d92..830e07db8 100644 --- a/examples/tap/tap_plot.serpent +++ b/examples/tap/tap_plot.serpent @@ -19,9 +19,9 @@ include "geometry/347_base.ini" set title "TAP MSR test 5-d step" %therm zrh_h 900 hzr05.32t hzr06.32t % 900K H-Zr -%set acelib "/home/andrei2/serpent/xsdata/endfb7/sss_endfb7.xsdata" -%set declib "/home/andrei2/serpent/xsdata/endfb7/sss_endfb7.dec" -%set nfylib "/home/andrei2/serpent/xsdata/endfb7/sss_endfb7.nfy" +%set acelib "sss_endfb7.xsdata" +%set declib "sss_endfb7.dec" +%set nfylib "sss_endfb7.nfy" set acelib "sss_jeff312.xsdata" set declib "sss_jeff33.dec" diff --git a/requirements.txt b/requirements.txt index 9bd617ae1..64f47e02d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,7 @@ pyne numpy scipy json +jsonschema pytables networkx pydotplus diff --git a/saltproc/app.py b/saltproc/app.py index 852365664..033aa0c1b 100644 --- a/saltproc/app.py +++ b/saltproc/app.py @@ -11,6 +11,7 @@ import os import copy import json +import jsonschema from collections import OrderedDict import gc import networkx as nx @@ -19,43 +20,18 @@ import numpy as np -input_path = os.getcwd() - -input_file = os.path.join(input_path, 'data/saltproc_serpent') -iter_matfile = os.path.join(input_path, 'data/saltproc_mat') - - -def check_restart(restart_flag): - """If the user set `Restart simulation from the step when it stopped?` - for `False` clean out iteration files and database from previous run. - - Parameters - ---------- - restart_flag : bool - Is the current simulation restarted? - """ - if not restart_flag: - try: - os.remove(db_file) - os.remove(iter_matfile) - os.remove(input_file) - print("Previous run output files were deleted.") - except OSError as e: - pass - - def parse_arguments(): """Parses arguments from command line. Parameters - ----------- + ---------- Returns - -------- + ------- n: int - Number of nodes for use in Serpent simulation. + Number of nodes for use in depletion code simulation. d: int - Number of cores for use in Serpent simulation. + Number of cores for use in depletion code simulation. i: str Path and name of main SaltProc input file (json format). @@ -64,11 +40,13 @@ def parse_arguments(): parser.add_argument('-n', # Number of nodes to use type=int, default=1, - help='number of cluster nodes to use in Serpent') - parser.add_argument('-d', # Number of nodes to use + help='number of cluster nodes to use in \ + depletion code simulation') + parser.add_argument('-d', # Number of cores to use type=int, default=1, - help='number of threads to use in Serpent') + help='number of threads to use in \ + depletion code simulation') parser.add_argument('-i', # main input file type=str, default=None, @@ -81,63 +59,87 @@ def read_main_input(main_inp_file): """Reads main SaltProc input file (json format). Parameters - ----------- + ---------- main_inp_file : str Path to SaltProc main input file and name of this file. """ + + input_schema = os.path.join(os.path.dirname(os.path.realpath(__file__)), + './input_schema.json') with open(main_inp_file) as f: j = json.load(f) - global exec_path, spc_inp_file, dot_inp_file, template_file, db_file - exec_path = j["Path to Serpent executable"] + with open(input_schema) as s: + v = json.load(s) + try: + jsonschema.validate(instance=j, schema=v) + except jsonschema.exceptions.ValidationError: + print("Your input file improperly structured.\ + Please see saltproc/tests/test.json for an example.") + + # Global input path + path_prefix = os.getcwd() + input_path = os.path.join(path_prefix, os.path.dirname(f.name)) + + # Saltproc settings + global spc_inp_file, dot_inp_file, output_path, num_depsteps spc_inp_file = os.path.join( os.path.dirname(f.name), - j["File containing processing system objects"]) + j['proc_input_file']) dot_inp_file = os.path.join( os.path.dirname(f.name), - j["Graph file containing processing system structure"]) - template_file = os.path.join( - os.path.dirname(f.name), - j["User's Serpent input file with reactor model"]) - db_path = j["Path output data storing folder"] - db_file = os.path.join( - os.path.dirname(f.name), - db_path, - j["Output HDF5 database file name"]) - # Read Monte Carlo setups - global neutron_pop, active_cycles, inactive_cycles - neutron_pop = j["Number of neutrons per generation"] - active_cycles = j["Number of active generations"] - inactive_cycles = j["Number of inactive generations"] - # Read advanced simulatiion parameters - global adjust_geo, restart_flag, core_massflow_rate - adjust_geo = j["Switch to another geometry when keff drops below 1?"] - restart_flag = j["Restart simulation from the step when it stopped?"] - # Read paths to geometry files - global geo_file - if adjust_geo: - geo_list = j["Geometry file/files to use in Serpent runs"] - geo_file = [g for g in geo_list] - elif not adjust_geo: - geo_file = [j["Geometry file/files to use in Serpent runs"]] - core_massflow_rate = \ - j["Salt mass flow rate throughout reactor core (g/s)"] - global depl_hist, power_hist - depl_hist = \ - j["Depletion step interval or Cumulative time (end of step) (d)"] - power_hist = \ - j["Reactor power or power step list during depletion step (W)"] - depsteps = \ - j["Number of steps for constant power and depletion interval case"] - if depsteps is not None and isinstance(depl_hist, (float, int)): - if depsteps < 0.0 or not int: + j['dot_input_file']) + output_path = j['output_path'] + num_depsteps = j['num_depsteps'] + + # Global output path + output_path = os.path.join(input_path, output_path) + j['output_path'] = output_path + + # Class settings + global depcode_inp, simulation_inp, reactor_inp + depcode_inp = j['depcode'] + simulation_inp = j['simulation'] + reactor_inp = j['reactor'] + + depcode_inp['template_inputfile_path'] = os.path.join( + input_path, depcode_inp['template_inputfile_path']) + geo_list = depcode_inp['geo_file_paths'] + + # Global geometry file paths + geo_file_paths = [] + for g in geo_list: + geo_file_paths += [os.path.join(input_path, g)] + depcode_inp['geo_file_paths'] = geo_file_paths + + # Global output file paths + depcode_inp['iter_inputfile'] = os.path.join( + output_path, depcode_inp['iter_inputfile']) + depcode_inp['iter_matfile'] = os.path.join( + output_path, depcode_inp['iter_matfile']) + db_name = os.path.join( + output_path, simulation_inp['db_name']) + simulation_inp['db_name'] = db_name + + dep_step_length_cumulative = reactor_inp['dep_step_length_cumulative'] + power_levels = reactor_inp['power_levels'] + if num_depsteps is not None and len(dep_step_length_cumulative) == 1: + if num_depsteps < 0.0 or not int: raise ValueError('Depletion step interval cannot be negative') else: - step = int(depsteps) - deptot = float(depl_hist) * step - depl_hist = np.linspace(float(depl_hist), deptot, num=step) - power_hist = float(power_hist) * np.ones_like(depl_hist) - elif depsteps is None and isinstance(depl_hist, (np.ndarray, list)): - if len(depl_hist) != len(power_hist): + step = int(num_depsteps) + deptot = float(dep_step_length_cumulative[0]) * step + dep_step_length_cumulative = \ + np.linspace(float(dep_step_length_cumulative[0]), + deptot, + num=step) + power_levels = float(power_levels[0]) * \ + np.ones_like(dep_step_length_cumulative) + reactor_inp['dep_step_length_cumulative'] = \ + dep_step_length_cumulative + reactor_inp['power_levels'] = power_levels + elif num_depsteps is None and isinstance(dep_step_length_cumulative, + (np.ndarray, list)): + if len(dep_step_length_cumulative) != len(power_levels): raise ValueError( 'Depletion step list and power list shape mismatch') @@ -218,7 +220,7 @@ def read_dot(dot_file): Path to `.dot` file with reprocessing system structure. Returns - -------- + ------- mat_name : str Name of burnable material which reprocessing scheme described in `.dot` file. @@ -240,11 +242,11 @@ def read_dot(dot_file): return mat_name, paths_list -def reprocessing(mat): +def reprocessing(mats): """Applies reprocessing scheme to burnable materials. Parameters - ----------- + ---------- mats : dict of str to Materialflow Dictionary that contains `Materialflow` objects with burnable material data right after irradiation in the core. @@ -255,7 +257,7 @@ def reprocessing(mat): `Materialflow` object holding composition and properties. Returns - -------- + ------- waste : dict of str to Materialflow ``key`` @@ -274,40 +276,41 @@ def reprocessing(mat): inmass = {} extracted_mass = {} waste = OrderedDict() - forked_mat = OrderedDict() + forked_mats = OrderedDict() prcs = read_processes_from_input() - mat_name_dot, paths = read_dot(dot_inp_file) + mats_name_dot, paths = read_dot(dot_inp_file) for mname in prcs.keys(): # iterate over materials waste[mname] = {} - forked_mat[mname] = [] - inmass[mname] = float(mat[mname].mass) + forked_mats[mname] = [] + inmass[mname] = float(mats[mname].mass) print("Material mass before reprocessing %f g" % inmass[mname]) - if mname == 'fuel' and mat_name_dot == 'fuel': + if mname == 'fuel' and mats_name_dot == 'fuel': w = 'waste_' ctr = 0 for path in paths: - forked_mat[mname].append(copy.deepcopy(mat[mname])) - print("Material mass %f" % mat[mname].mass) + forked_mats[mname].append(copy.deepcopy(mats[mname])) + print("Material mass %f" % mats[mname].mass) for p in path: # Calculate fraction of the flow going to the process p divisor = float(prcs[mname][p].mass_flowrate / prcs[mname]['core_outlet'].mass_flowrate) print('Process %s, divisor=%f' % (p, divisor)) # Update materialflow byt multiplying it by flow fraction - forked_mat[mname][ctr] = \ - divisor * copy.deepcopy(forked_mat[mname][ctr]) + forked_mats[mname][ctr] = \ + divisor * copy.deepcopy(forked_mats[mname][ctr]) waste[mname][w + p] = \ - prcs[mname][p].rem_elements(forked_mat[mname][ctr]) + prcs[mname][p].rem_elements(forked_mats[mname][ctr]) ctr += 1 # Sum all forked material objects together - mat[mname] = forked_mat[mname][0] # initilize correct obj instance - for idx in range(1, len(forked_mat[mname])): - mat[mname] += forked_mat[mname][idx] - print('1 Forked material mass %f' % (forked_mat[mname][0].mass)) - print('2 Forked material mass %f' % (forked_mat[mname][1].mass)) + # initilize correct obj instance + mats[mname] = forked_mats[mname][0] + for idx in range(1, len(forked_mats[mname])): + mats[mname] += forked_mats[mname][idx] + print('1 Forked material mass %f' % (forked_mats[mname][0].mass)) + print('2 Forked material mass %f' % (forked_mats[mname][1].mass)) print('\nMass balance %f g = %f + %f + %f + %f + %f + %f' % (inmass[mname], - mat[mname].mass, + mats[mname].mass, waste[mname]['waste_sparger'].mass, waste[mname]['waste_entrainment_separator'].mass, waste[mname]['waste_nickel_filter'].mass, @@ -316,30 +319,28 @@ def reprocessing(mat): # Bootstrap for many materials if mname == 'ctrlPois': waste[mname]['removal_tb_dy'] = \ - prcs[mname]['removal_tb_dy'].rem_elements(mat[mname]) - extracted_mass[mname] = inmass[mname] - float(mat[mname].mass) - del prcs, inmass, mname, forked_mat, mat_name_dot, paths, divisor + prcs[mname]['removal_tb_dy'].rem_elements(mats[mname]) + extracted_mass[mname] = inmass[mname] - float(mats[mname].mass) + del prcs, inmass, mname, forked_mats, mats_name_dot, paths, divisor return waste, extracted_mass -def refill(mat, extracted_mass, waste_dict): +def refill(mats, extracted_mass, waste_dict): """Makes up material loss in removal processes by adding fresh fuel. Parameters - ----------- - mat : dict of str to Materialflow + ---------- + mats : dict of str to Materialflow ``key`` Name of burnable material. ``value`` `Materialflow` object after performing all removals. - extracted_mass: dict of str to float - - ``key`` + extracted_mass : dict of str to float Name of burnable material. ``value`` Mass removed as waste in reprocessing function for each material. - waste : dict of str to Materialflow + waste_dict : dict of str to Materialflow ``key`` Process name. @@ -347,7 +348,7 @@ def refill(mat, extracted_mass, waste_dict): `Materialflow` object containing waste streams data. Returns - -------- + ------- refilled_mats: dict of str to Materialflow Dictionary that contains `Materialflow` objects. @@ -356,19 +357,19 @@ def refill(mat, extracted_mass, waste_dict): ``value`` `Materialflow` object after adding fresh fuel. """ - print('Fuel before refill ^^^', mat['fuel'].print_attr()) + print('Fuel before refill ^^^', mats['fuel'].print_attr()) feeds = read_feeds_from_input() - refill_mat = OrderedDict() + refill_mats = OrderedDict() for mn, v in feeds.items(): # iterate over materials - refill_mat[mn] = {} + refill_mats[mn] = {} for feed_n, fval in feeds[mn].items(): # works with one feed only scale = extracted_mass[mn] / feeds[mn][feed_n].mass - refill_mat[mn] = scale * feeds[mn][feed_n] - waste_dict[mn]['feed_' + str(feed_n)] = refill_mat[mn] - mat[mn] += refill_mat[mn] - print('Refilled fresh material %s %f g' % (mn, refill_mat[mn].mass)) - print('Refill Material ^^^', refill_mat[mn].print_attr()) - print('Fuel after refill ^^^', mat[mn].print_attr()) + refill_mats[mn] = scale * feeds[mn][feed_n] + waste_dict[mn]['feed_' + str(feed_n)] = refill_mats[mn] + mats[mn] += refill_mats[mn] + print('Refilled fresh material %s %f g' % (mn, refill_mats[mn].mass)) + print('Refill Material ^^^', refill_mats[mn].print_attr()) + print('Fuel after refill ^^^', mats[mn].print_attr()) return waste_dict @@ -381,56 +382,70 @@ def run(): read_main_input(sp_input) # Print out input information print('Initiating Saltproc:\n' - '\tRestart = ' + str(restart_flag) + '\n' - '\tTemplate File Path = ' + os.path.abspath(template_file) + '\n' - '\tInput File Path = ' + os.path.abspath(input_file) + '\n' - '\tMaterial File Path = ' + os.path.abspath(iter_matfile) + '\n' - '\tOutput HDF5 DB Path = ' + os.path.abspath(db_file) + '\n' - ) + '\tRestart = ' + + str(simulation_inp['restart_flag']) + + '\n' + '\tTemplate File Path = ' + + os.path.abspath(depcode_inp['template_inputfile_path']) + + '\n' + '\tInput File Path = ' + + os.path.abspath(depcode_inp['iter_inputfile']) + + '\n' + '\tMaterial File Path = ' + + os.path.abspath(depcode_inp['iter_matfile']) + + '\n' + '\tOutput HDF5 database Path = ' + + os.path.abspath(simulation_inp['db_name']) + + '\n') # Intializing objects - serpent = DepcodeSerpent( - exec_path=exec_path, - template_path=template_file, - input_path=input_file, - iter_matfile=iter_matfile, - geo_file=geo_file, - npop=neutron_pop, - active_cycles=active_cycles, - inactive_cycles=inactive_cycles) + if depcode_inp['codename'] == 'serpent': + depcode = DepcodeSerpent( + exec_path=depcode_inp['exec_path'], + template_inputfile_path=depcode_inp['template_inputfile_path'], + iter_inputfile=depcode_inp['iter_inputfile'], + iter_matfile=depcode_inp['iter_matfile'], + geo_files=depcode_inp['geo_file_paths'], + npop=depcode_inp['npop'], + active_cycles=depcode_inp['active_cycles'], + inactive_cycles=depcode_inp['inactive_cycles']) + else: + raise ValueError( + f'{depcode_inp["codename"]} is not a supported depletion code') + simulation = Simulation( sim_name='Super test', - sim_depcode=serpent, + sim_depcode=depcode, core_number=cores, node_number=nodes, - h5_file=db_file, - iter_matfile=iter_matfile) + restart_flag=simulation_inp['restart_flag'], + adjust_geo=simulation_inp['adjust_geo'], + db_path=simulation_inp['db_name']) + msr = Reactor( - volume=1.0, - mass_flowrate=core_massflow_rate, - power_levels=power_hist, - depl_hist=depl_hist) + volume=reactor_inp['volume'], + mass_flowrate=reactor_inp['mass_flowrate'], + power_levels=reactor_inp['power_levels'], + dep_step_length_cumulative=reactor_inp['dep_step_length_cumulative']) # Check: Restarting previous simulation or starting new? - check_restart(restart_flag) + simulation.check_restart() # Run sequence # Start sequence - for dts in range(len(depl_hist)): - print("\n\n\nStep #%i has been started" % (dts + 1)) - serpent.write_depcode_input(template_file, - input_file, - msr, - dts, - restart_flag) - serpent.run_depcode(cores, nodes) - if dts == 0 and restart_flag is False: # First step + for dep_step in range(len(msr.dep_step_length_cumulative)): + print("\n\n\nStep #%i has been started" % (dep_step + 1)) + simulation.sim_depcode.write_depcode_input(msr, + dep_step, + simulation.restart_flag) + depcode.run_depcode(cores, nodes) + if dep_step == 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 dts) - mats = serpent.read_dep_comp(input_file, False) - simulation.store_mat_data(mats, dts - 1, 'before_reproc') + # Parse and store data for initial state (beginning of dep_step) + mats = depcode.read_dep_comp(False) + simulation.store_mat_data(mats, dep_step - 1, False) # Finish of First step # Main sequence - mats = serpent.read_dep_comp(input_file, True) - simulation.store_mat_data(mats, dts, 'before_reproc') + mats = depcode.read_dep_comp(True) + simulation.store_mat_data(mats, dep_step, False) simulation.store_run_step_info() # Reprocessing here print("\nMass and volume of fuel before reproc %f g; %f cm3" % @@ -455,17 +470,17 @@ def run(): # mats['ctrlPois'].vol)) print("Removed mass [g]:", rem_mass) # Store in DB after reprocessing and refill (right before next depl) - simulation.store_after_repr(mats, waste_feed_st, dts) - serpent.write_mat_file(mats, iter_matfile, simulation.burn_time) + simulation.store_after_repr(mats, waste_feed_st, dep_step) + depcode.write_mat_file(mats, simulation.burn_time) del mats, waste_st, waste_feed_st, rem_mass gc.collect() # Switch to another geometry? - if adjust_geo and simulation.read_k_eds_delta(dts, restart_flag): - simulation.switch_to_next_geometry() + if simulation.adjust_geo and simulation.read_k_eds_delta(dep_step): + depcode.switch_to_next_geometry() print("\nTime at the end of current depletion step %fd" % simulation.burn_time) print("Simulation succeeded.\n") '''print("Reactor object data.\n", msr.mass_flowrate, msr.power_levels, - msr.depl_hist)''' + msr.dep_step_length_cumulative)''' diff --git a/saltproc/depcode.py b/saltproc/depcode.py index 3e5702abc..16cf83776 100644 --- a/saltproc/depcode.py +++ b/saltproc/depcode.py @@ -3,6 +3,7 @@ import os import shutil import re +import tables as tb from pyne import nucname as pyname from pyne import serpent from abc import ABC, abstractmethod @@ -15,15 +16,15 @@ class Depcode(ABC): population, active, and inactive cycles. Contains methods to read template and output files, and write new input files for the depletion code. - """ + """ def __init__(self, codename, exec_path, - template_path, - input_path, + template_inputfile_path, + iter_inputfile, iter_matfile, - geo_file=None, + geo_files=None, npop=50, active_cycles=20, inactive_cycles=20): @@ -35,14 +36,14 @@ def __init__(self, Name of depletion code. exec_path : str Path to depletion code executable. - template_path : str - Path to user input file for depletion code. - input_path : str - Name of input file for depletion code rerunning. + template_inputfile_path : str + Path to depletion code input file template. + iter_inputfile : str + Name of depletion code input file for depletion code rerunning. iter_matfile : str Name of iterative, rewritable material file for depletion code rerunning. This file is modified during the simulation. - geo_file : str or list, optional + geo_files : 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. @@ -56,10 +57,10 @@ def __init__(self, """ self.codename = codename self.exec_path = exec_path - self.template_path = template_path - self.input_path = input_path + self.template_inputfile_path = template_inputfile_path + self.iter_inputfile = iter_inputfile self.iter_matfile = iter_matfile - self.geo_file = geo_file + self.geo_files = geo_files self.npop = npop self.active_cycles = active_cycles self.inactive_cycles = inactive_cycles @@ -67,15 +68,25 @@ def __init__(self, self.sim_info = {} @abstractmethod - def read_dep_comp(self, dep_file, read_at_end=False): + def read_depcode_info(self): + """Parses initial depletion code info data from depletion code + output and stores it in the `Depcode` object's ``sim_info`` attribute. + """ + + @abstractmethod + def read_depcode_step_param(self): + """Parses data from depletion code output for each step and stores + it in `Depcode` object's ``param`` attributes. + """ + + @abstractmethod + def read_dep_comp(self, read_at_end=False): """Reads the depleted material data from the depcode simulation and returns a dictionary with a `Materialflow` object for each burnable material. Parameters ---------- - dep_file : str - Path to file containing results of depletion simulation read_at_end : bool, optional Controls at which moment in the depletion step to read the data. If `True`, the function reads data at the end of the @@ -107,15 +118,17 @@ def run_depcode(self, cores, nodes): """ @abstractmethod - def write_depcode_input(self, temp, inp, reactor, dep_step, restart): + def switch_to_next_geometry(self): + """Inserts line with path to next depletion code geometry file at the + beginning of the depletion code iteration input file. + """ + + @abstractmethod + def write_depcode_input(self, reactor, dep_step, restart): """ Writes prepared data into depletion code input file(s). Parameters ---------- - temp : str - Path to depletion code template file - inp : str - Path to input file for depletion code rerunning reactor : Reactor Contains information about power load curve and cumulative depletion time for the integration test. @@ -126,7 +139,7 @@ def write_depcode_input(self, temp, inp, reactor, dep_step, restart): """ @abstractmethod - def write_mat_file(self, dep_dict, mat_file, dep_end_time): + def write_mat_file(self, dep_dict, dep_end_time): """Writes the iteration input file containing the burnable materials composition used in depletion runs and updated after each depletion step. @@ -140,8 +153,6 @@ def write_mat_file(self, dep_dict, mat_file, dep_end_time): Name of burnable material. ``value`` `Materialflow` object holding composition and properties. - mat_file : str - Path to file containing burnable materials composition. dep_end_time : float Current time at the end of the depletion step (d). @@ -159,10 +170,10 @@ class DepcodeSerpent(Depcode): def __init__(self, exec_path="sss2", - template_path="reactor.serpent", - input_path="data/saltproc_reactor", + template_inputfile_path="reactor.serpent", + iter_inputfile="data/saltproc_reactor", iter_matfile="data/saltproc_mat", - geo_file=None, + geo_files=None, npop=50, active_cycles=20, inactive_cycles=20): @@ -172,14 +183,14 @@ def __init__(self, ---------- exec_path : str Path to Serpent2 executable. - template_path : str + template_inputfile_path : str Path to user input file for Serpent2. - input_path : str - Name of input file for Serpent2 rerunning. + iter_inputfile : str + Name of Serpent2 input file for Serpent2 rerunning. iter_matfile : str Name of iterative, rewritable material file for Serpent2 rerunning. This file is modified during the simulation. - geo_file : str or list, optional + geo_files : 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. @@ -193,10 +204,10 @@ def __init__(self, """ super().__init__("serpent", exec_path, - template_path, - input_path, + template_inputfile_path, + iter_inputfile, iter_matfile, - geo_file, + geo_files, npop, active_cycles, inactive_cycles) @@ -223,11 +234,13 @@ def change_sim_par(self, template_data): if len(sim_param) > 1: print('ERROR: Template file %s contains multiple lines with ' 'simulation parameters:\n' - % (self.template_path), sim_param) + % (self.template_inputfile_path), sim_param) return elif len(sim_param) < 1: - print('ERROR: Template file %s does not contain line with ' - 'simulation parameters.' % (self.template_path)) + print( + 'ERROR: Template file %s does not contain line with ' + 'simulation parameters.' % + (self.template_inputfile_path)) return args = 'set pop %i %i %i\n' % (self.npop, self.active_cycles, self.inactive_cycles) @@ -249,11 +262,11 @@ def create_iter_matfile(self, template_data): List of strings containing modified user template file. """ - data_dir = os.path.dirname(self.template_path) + data_dir = os.path.dirname(self.template_inputfile_path) include_str = [s for s in template_data if s.startswith("include ")] if not include_str: print('ERROR: Template file %s has no ' - ' statements ' % (self.template_path)) + ' statements ' % (self.template_inputfile_path)) return src_file = include_str[0].split()[1][1:-1] if not os.path.isabs(src_file): @@ -265,7 +278,7 @@ def create_iter_matfile(self, template_data): ' materials description or ' ' statement is not appears' ' as first statement\n' - % (self.template_path)) + % (self.template_inputfile_path)) return # Create data directory try: @@ -319,24 +332,20 @@ def get_nuc_name(self, nuc_code): nuc_name = pyname.name(nuc_code)[:-1] + 'm' + str(meta_flag) else: nuc_name = pyname.name(nuc_code) - nuc_zzaaam = self.sss_meta_zzz(pyname.zzaaam(nuc_code)) + nuc_zzaaam = \ + self.convert_nuclide_name_serpent_to_zam(pyname.zzaaam(nuc_code)) return nuc_name, nuc_zzaaam - def get_tra_or_dec(self, input_file): - """Returns the isotopes map to transform isotope `zzaaam` code to - Serpent2. Uses Serpent2 `*.out` file with list of all isotopes in - simulation. - - Parameters - ---------- - input_file : str - Serpent2 input file name and path. + def create_nuclide_name_map_zam_to_serpent(self): + """ Create a map that accepts nuclide names in `zzaaam` format and + returns the Serpent2 nuclide code format. Uses Serpent2 `*.out` file + with list of all nuclides in simulation. Returns ------- - isotope_map : dict of str to str - Contains mapping for isotopes names from `zzaaam` to Serpent2 name - imported from Serpent2 ouput file: + nuclide_map : dict of str to str + Contains mapping for nuclide names from `zzaaam` to Serpent2 + format imported from Serpent2 ouput file: ``key`` The key is nuclide name in `zzaaam` format. For example, @@ -348,7 +357,7 @@ def get_tra_or_dec(self, input_file): """ map_dict = {} # Construct path to the *.out File - out_file = os.path.join('%s.out' % input_file) + out_file = os.path.join('%s.out' % self.iter_inputfile) file = open(out_file, 'r') str_list = file.read().split('\n') # Stop-line @@ -381,17 +390,15 @@ def insert_path_to_geometry(self, template_data): """ template_data.insert(5, # Inserts on 6th line - 'include \"' + str(self.geo_file[0]) + '\"\n') + 'include \"' + str(self.geo_files[0]) + '\"\n') return template_data - def read_dep_comp(self, input_file, read_at_end=False): + def read_dep_comp(self, read_at_end=False): """Reads the Serpent2 `*_dep.m` file and returns a dictionary with a `Materialflow` object for each burnable material. Parameters ---------- - input_file : str - Path to Serpent2 input file. read_at_end : bool, optional Controls at which moment in the depletion step to read the data. If `True`, the function reads data at the end of the @@ -409,11 +416,13 @@ def read_dep_comp(self, input_file, read_at_end=False): `Materialflow` object holding composition and properties. """ - moment = 0 + # Determine moment in depletion step to read data from if read_at_end: moment = 1 + else: + moment = 0 - dep_file = os.path.join('%s_dep.m' % input_file) + dep_file = os.path.join('%s_dep.m' % self.iter_inputfile) dep = serpent.parse_dep(dep_file, make_mats=False) self.days = dep['DAYS'][moment] # Read materials names from the file @@ -433,20 +442,21 @@ def read_dep_comp(self, input_file, read_at_end=False): mats[m].mass = mats[m].density * volume mats[m].vol = volume mats[m].burnup = dep['MAT_' + m + '_BURNUP'][moment] - self.get_tra_or_dec(self.input_path) + self.create_nuclide_name_map_zam_to_serpent() return mats def read_depcode_info(self): """Parses initial simulation info data from Serpent2 output and stores it in the `DepcodeSerpent` object's ``sim_info`` attributes. """ - res = serpent.parse_res(self.input_path + "_res.m") - self.sim_info['serpent_version'] = \ - res['VERSION'][0].decode('utf-8') + res = serpent.parse_res(self.iter_inputfile + "_res.m") + depcode_name, depcode_ver = res['VERSION'][0].decode('utf-8').split() + self.sim_info['depcode_name'] = depcode_name + self.sim_info['depcode_version'] = depcode_ver self.sim_info['title'] = res['TITLE'][0].decode('utf-8') - self.sim_info['serpent_input_filename'] = \ + self.sim_info['depcode_input_filename'] = \ res['INPUT_FILE_NAME'][0].decode('utf-8') - self.sim_info['serpent_working_dir'] = \ + self.sim_info['depcode_working_dir'] = \ res['WORKING_DIRECTORY'][0].decode('utf-8') self.sim_info['xs_data_path'] = \ res['XS_DATA_FILE_PATH'][0].decode('utf-8') @@ -460,7 +470,7 @@ def read_depcode_step_param(self): """Parses data from Serpent2 output for each step and stores it in `DepcodeSerpent` object's ``param`` attributes. """ - res = serpent.parse_res(self.input_path + "_res.m") + res = serpent.parse_res(self.iter_inputfile + "_res.m") self.param['keff_bds'] = res['IMP_KEFF'][0] self.param['keff_eds'] = res['IMP_KEFF'][1] self.param['breeding_ratio'] = res['CONVERSION_RATIO'][1] @@ -475,23 +485,23 @@ def read_depcode_step_param(self): self.param['fission_mass_bds'] = res['INI_FMASS'][1] self.param['fission_mass_eds'] = res['TOT_FMASS'][1] - def read_depcode_template(self, template_path): - """Reads prepared Serpent2 template (input) file for use in - other class functions that prepare the input file for multiple runs. + def read_plaintext_file(self, file_path): + """Reads the content of a plaintext file for use by other methods. Parameters ---------- - template_path : str - Path to user template file for depletion code. + file_path : str + Path to file. Returns ------- - tempalate_data : list - List of strings containing user template file. + file_data : list + List of strings containing file lines. """ - file = open(template_path, 'r') - template_data = file.readlines() + template_data = [] + with open(file_path, 'r') as file: + template_data = file.readlines() return template_data def replace_burnup_parameters( @@ -524,10 +534,11 @@ def replace_burnup_parameters( 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.depl_hist[0] + current_depstep = reactor.dep_step_length_cumulative[0] else: - current_depstep = reactor.depl_hist[current_depstep_idx] - \ - reactor.depl_hist[current_depstep_idx - 1] + current_depstep = \ + reactor.dep_step_length_cumulative[current_depstep_idx] - \ + reactor.dep_step_length_cumulative[current_depstep_idx - 1] for line in template_data: if line.startswith('set power '): line_idx = template_data.index(line) @@ -560,21 +571,21 @@ def run_depcode(self, cores, nodes): self.exec_path, '-omp', str(cores), - self.input_path) + self.iter_inputfile) elif self.exec_path.startswith('/apps/exp_ctl/'): # check if Falcon args = ( 'mpiexec', self.exec_path, - self.input_path, + self.iter_inputfile, '-omp', str(18)) else: - args = (self.exec_path, '-omp', str(cores), self.input_path) + args = (self.exec_path, '-omp', str(cores), self.iter_inputfile) print('Running %s' % (self.codename)) try: subprocess.check_output( args, - cwd=os.path.split(self.template_path)[0], + cwd=os.path.split(self.template_inputfile_path)[0], stderr=subprocess.STDOUT) except subprocess.CalledProcessError as error: print(error.output.decode("utf-8")) @@ -582,7 +593,7 @@ def run_depcode(self, cores, nodes): % (self.codename)) print('Finished Serpent2 Run') - def sss_meta_zzz(self, nuc_code): + def convert_nuclide_name_serpent_to_zam(self, nuc_code): """Checks Serpent2-specific meta stable-flag for zzaaam. For instance, 47310 instead of 471101 for `Ag-110m1`. Metastable isotopes represented with `aaa` started with ``3``. @@ -610,10 +621,33 @@ def sss_meta_zzz(self, nuc_code): zzaaam = nuc_code return int(zzaaam) + def switch_to_next_geometry(self): + """Inserts line with path to next Serpent geometry file at the + beginning of the Serpent iteration input file. + """ + geo_line_n = 5 + f = open(self.iter_inputfile, 'r') + data = f.readlines() + f.close() + + current_geo_file = data[geo_line_n].split('\"')[1] + current_geo_idx = self.geo_files.index(current_geo_file) + try: + new_geo_file = self.geo_files[current_geo_idx + 1] + except IndexError: + print('No more geometry files available \ + and the system went subcritical \n\n') + print('Aborting simulation') + return + new_data = [d.replace(current_geo_file, new_geo_file) for d in data] + print('Switching to next geometry file: ', new_geo_file) + + f = open(self.iter_inputfile, 'w') + f.writelines(new_data) + f.close() + def write_depcode_input( self, - temp_file, - inp_file, reactor, dep_step, restart): @@ -621,10 +655,6 @@ def write_depcode_input( Parameters ---------- - template_file : str - Path to Serpent2 template file. - input_file : str - Path to input file for Serpent2 rerunning. reactor : Reactor Contains information about power load curve and cumulative depletion time for the integration test. @@ -636,20 +666,20 @@ def write_depcode_input( """ if dep_step == 0 and not restart: - data = self.read_depcode_template(temp_file) + data = self.read_plaintext_file(self.template_inputfile_path) data = self.insert_path_to_geometry(data) data = self.change_sim_par(data) data = self.create_iter_matfile(data) else: - data = self.read_depcode_template(inp_file) + data = self.read_plaintext_file(self.iter_inputfile) data = self.replace_burnup_parameters(data, reactor, dep_step) if data: - out_file = open(inp_file, 'w') + out_file = open(self.iter_inputfile, 'w') out_file.writelines(data) out_file.close() - def write_mat_file(self, dep_dict, mat_file, dep_end_time): + def write_mat_file(self, dep_dict, dep_end_time): """Writes the iteration input file containing the burnable materials composition used in Serpent2 runs and updated after each depletion step. @@ -663,14 +693,12 @@ def write_mat_file(self, dep_dict, mat_file, dep_end_time): Name of burnable material. ``value`` `Materialflow` object holding composition and properties. - mat_file : str - Path to file containing burnable materials composition. dep_end_time : float Current time at the end of the depletion step (d). """ - matf = open(mat_file, 'w') + matf = open(self.iter_matfile, 'w') matf.write('%% Material compositions (after %f days)\n\n' % dep_end_time) for key, value in dep_dict.items(): diff --git a/saltproc/input_schema.json b/saltproc/input_schema.json new file mode 100644 index 000000000..9446f0e1b --- /dev/null +++ b/saltproc/input_schema.json @@ -0,0 +1,121 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/arfc/saltproc", + "title": "SaltProc input file schema", + "type": "object", + "properties": { + "proc_input_file": { + "description": "File containing processing system objects", + "type": "string", + "pattern": "^(.*)\\.json$" + }, + "dot_input_file": { + "description": "Graph file containing processing system structure", + "type": "string", + "pattern": "^(.*)\\.dot$" + }, + "output_path": { + "description": "Path output data storing folder", + "type": "string", + "pattern": "^\\.\\/(.*)$" + }, + "num_depsteps": { + "description": "Number of steps for constant power and depletion interval case", + "type": "number" + }, + "depcode": { + "description": "Depcode class input parameters", + "type": "object", + "properties": { + "codename": { + "description": "Name of depletion code", + "type": "string" }, + "exec_path": { + "description": "Path to depletion code executable", + "type": "string" }, + "template_inputfile_path": { + "description": "Path to user's template depletion code input file with reactor model", + "type": "string", + "pattern": "^\\.\\/(.*)$"}, + "iter_inputfile": { + "description": "Name of depletion code input file for depletion code rerunning", + "type": "string" }, + "iter_matfile": { + "description": "Name of iterable, rewritable material file for depletion code rerunning", + "type": "string" }, + "npop": { + "description": "Number of neutrons per generation", + "type": "number", + "minimum": 0}, + "active_cycles": { + "description": "number of active generations", + "type": "number", + "minimum": 0}, + "inactive_cycles": { + "description": "Number of inactive generations", + "type": "number", + "minimum": 0}, + "geo_file_paths": { + "description": "Path(s) to geometry file(s) to swtich to in depletion code runs", + "type": "array", + "items": { "type": "string"}, + "minItems": 1, + "uniqueItems": false + } + }, + "required": ["codename", "exec_path", "template_inputfile_path", "iter_inputfile", "iter_matfile", "npop", "active_cycles","inactive_cycles", "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", + "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", + "minimum": 0}, + "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"] + } + }, + "required": ["proc_input_file", "dot_input_file", "output_path", "depcode", "simulation", "reactor"] +} diff --git a/saltproc/reactor.py b/saltproc/reactor.py index 690b22889..2d8dbcce2 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], - depl_hist=[1]): + dep_step_length_cumulative=[1]): """Initializes the class. Parameters @@ -15,10 +15,10 @@ def __init__(self, volume : float Total volume of the reactor core (:math:`cm^3`). mass_flowrate : float - Total mass flowrate through reactor (g/s). + Total mass flowrate through reactor core (g/s). power_levels : array [:math:`N_{steps}` x1] Normalized power level for each depletion step (W). - depl_hist : array [:math:`N_{steps}` x1] + dep_step_length_cumulative : array [:math:`N_{steps}` x1] Cumulative depletion time (d). """ @@ -26,4 +26,4 @@ def __init__(self, self.volume = volume self.mass_flowrate = mass_flowrate self.power_levels = power_levels - self.depl_hist = depl_hist + self.dep_step_length_cumulative = dep_step_length_cumulative diff --git a/saltproc/simulation.py b/saltproc/simulation.py index 95693fac6..3e5dfa84f 100644 --- a/saltproc/simulation.py +++ b/saltproc/simulation.py @@ -5,110 +5,99 @@ class Simulation(): - """Class sets up parameters for simulation, runs Serpent, parses output, - creates input. + """Class for handling simulation information. Contains information + for running simulation wiht parallelism. Also contains the simulation + name, a `Depcode` object, and the filename for the simulation database. + Contains methods to store simulation metadata and depletion results in + a database, predict reactor criticality at next depletion step, and + switch simulation geometry. + """ def __init__( self, sim_name="default", - sim_depcode="SERPENT", + sim_depcode="depcode", core_number=1, node_number=1, - h5_file="db_saltproc.h5", - compression=tb.Filters(complevel=9, - complib='blosc', - fletcher32=True), - iter_matfile="./saltproc_mat"): + db_path="db_saltproc.h5", + restart_flag=True, + adjust_geo=False, + compression_params=tb.Filters(complevel=9, + complib='blosc', + fletcher32=True), + ): """Initializes the Simulation object. Parameters ---------- - sim_name: str - Name of simulation may contain number of reference case, paper name - or other specific information to identify simulation. - sim_depcode: obj - Depcode object initiated using Depcode class. - cores: int - Number of cores to use for Serpent run (`-omp` flag in Serpent). - nodes: int - Number of nodes to use for Serpent run (`-mpi` flag in Serpent). - h5_file: str - Name of HDF5 database. - compression: Pytables filter object - HDF5 database compression parameters. - iter_matfile: str - Name of file containing burnable materials composition used in - Serpent runs and update after each depletion step. + sim_name : str + Name to identify the simulation. May contain information such as + the number of a reference case, a paper name, or some other + specific information identify the simulation. + sim_depcode : `Depcode` object + An instance of one of the `Depcode` child classes + cores : int + Number of cores to use for depletion code run (`-omp` flag in + Serpent). + nodes : int + Number of nodes to use for depletion code run (`-mpi` flag in + Serpent). + db_path : str + Path of HDF5 database that stores simulation information and + data. + restart_flag : bool + This value determines our initial condition. If `True`, then + then we run the simulation starting from the inital material + composition in the material input file inside our `depcode` + object. If `False`, then we runthe simulation starting from + the final material composition resulting within the `.h5` + database. + adjust_geo : bool + This value determines if we switch reactor geometry when keff + drops below 1.0 + compression_params : Pytables filter object + Compression parameters for HDF5 database. + """ # initialize all object attributes self.sim_name = sim_name self.sim_depcode = sim_depcode self.core_number = core_number self.node_number = node_number - self.h5_file = h5_file - self.compression = compression - self.iter_matfile = iter_matfile + self.db_path = db_path + self.restart_flag = restart_flag + self.adjust_geo = adjust_geo + self.compression_params = compression_params - def runsim_no_reproc(self, reactor, nsteps): - """Run simulation sequence for integral test. No reprocessing involved, - just re-running Serpent for comparision with model output. + def check_restart(self): + """If the user set `restart_flag` + for `False` clean out iteration files and database from previous run. Parameters ---------- - reactor : Reactor - Contains information about power load curve and cumulative - depletion time for the integration test. - nsteps : int - Number of depletion time steps in integration test run. + restart_flag : bool + Is the current simulation restarted? """ + if not self.restart_flag: + try: + os.remove(self.db_path) + os.remove(self.sim_depcode.iter_matfile) + os.remove(self.sim_depcode.iter_inputfile) + print("Previous run output files were deleted.") + except OSError as e: + pass - ###################################################################### - # Start sequence - for dts in range(nsteps): - print("\nStep #%i has been started" % (dts + 1)) - if dts == 0: # First step - self.sim_depcode.write_depcode_input( - self.sim_depcode.template_path, - self.sim_depcode.input_path, - reactor, - dts, - False) - self.sim_depcode.run_depcode( - self.core_number, - self.node_number) - # Read general simulation data which never changes - self.store_run_init_info() - # Parse and store data for initial state (beginning of dts) - mats = self.sim_depcode.read_dep_comp( - self.sim_depcode.input_path, - False) - self.store_mat_data(mats, dts, 'before_reproc') - # Finish of First step - # Main sequence - else: - self.sim_depcode.run_depcode( - self.core_number, - self.node_number) - mats = self.sim_depcode.read_dep_comp( - self.sim_depcode.input_path, - True) - self.store_mat_data(mats, dts, 'before_reproc') - self.store_run_step_info() - self.sim_depcode.write_mat_file( - mats, - self.iter_matfile, - self.burn_time) - - def store_after_repr(self, after_mats, waste_dict, step): - """Adds to HDF5 database waste streams data for each process after - reprocessing was performed (grams per depletion step). + def store_after_repr(self, after_mats, waste_dict, dep_step): + """Add data for waste streams [grams per depletion step] of each + process to the HDF5 database after reprocessing. Parameters ---------- after_mats : `Materialflow` - Burnable material stream object after performing reprocessing. + `Materialflow` object representing a material stream after + performing reprocessing. waste_dict : dict of str to Materialflow Dictionary that maps `Process` objects to waste `Materialflow` objects. @@ -117,12 +106,15 @@ def store_after_repr(self, after_mats, waste_dict, step): `Process` name (`str`) ``value`` `Materialflow` object containing waste streams data. - step : int - Current depletion step. + dep_step : int + Current depletion time step. """ streams_gr = 'in_out_streams' - db = tb.open_file(self.h5_file, mode='a', filters=self.compression) + db = tb.open_file( + self.db_path, + mode='a', + filters=self.compression_params) for mn in waste_dict.keys(): # iterate over materials mat_node = getattr(db.root.materials, mn) if not hasattr(mat_node, streams_gr): @@ -162,13 +154,14 @@ def store_after_repr(self, after_mats, waste_dict, step): del iso_wt_frac del iso_idx # Also save materials AFTER reprocessing and refill here - self.store_mat_data(after_mats, step, 'after_reproc') + self.store_mat_data(after_mats, dep_step, True) db.close() - def store_mat_data(self, mats, d_step, moment): - """Initializes HDF5/Pytables database (if not exist) or append burnable - material composition, mass, density, volume, temperature, burnup, - mass_flowrate, void_fraction, at the current depletion step to it. + def store_mat_data(self, mats, dep_step, store_at_end=False): + """Initialize the HDF5/Pytables database (if it doesn't exist) or + append the following data at the current depletion step to the + database: burnable material composition, mass, density, volume, + temperature, burnup, mass_flowrate, void_fraction. Parameters ---------- @@ -179,13 +172,21 @@ def store_mat_data(self, mats, d_step, moment): Name of burnable material. ``value`` `Materialflow` object holding composition and properties. - d_step : int + dep_step : int Current depletion step. - moment : int - Indicates at which moment in the depletion step store the data. `0` - refers the beginning, `1` refers the end of depletion step. + store_at_end : bool, optional + Controls at which moment in the depletion step to store data from. + If `True`, the function stores data from the end of the + depletion step. Otherwise, the function stores data from the + beginning of the depletion step. """ + # Determine moment in depletion step from which to store data + if store_at_end: + dep_step_str = "after_reproc" + else: + dep_step_str = "before_reproc" + # Moment when store compositions iso_idx = OrderedDict() # numpy array row storage data for material physical properties @@ -199,8 +200,13 @@ def store_mat_data(self, mats, d_step, moment): ('burnup', float) ]) - print('\nStoring material data for depletion step #%i.' % (d_step + 1)) - db = tb.open_file(self.h5_file, mode='a', filters=self.compression) + print( + '\nStoring material data for depletion step #%i.' % + (dep_step + 1)) + db = tb.open_file( + self.db_path, + mode='a', + filters=self.compression_params) if not hasattr(db.root, 'materials'): comp_group = db.create_group('/', 'materials', @@ -216,11 +222,11 @@ def store_mat_data(self, mats, d_step, moment): key) # Create group for composition and parameters before reprocessing mat_node = getattr(db.root.materials, key) - if not hasattr(mat_node, moment): + if not hasattr(mat_node, dep_step_str): db.create_group(mat_node, - moment, + dep_step_str, 'Material data before reprocessing') - comp_pfx = '/materials/' + str(key) + '/' + str(moment) + comp_pfx = '/materials/' + str(key) + '/' + dep_step_str # Read isotopes from Materialflow for material for nuc_code, wt_frac in mats[key].comp.items(): # Dictonary in format {isotope_name : index(int)} @@ -266,7 +272,7 @@ def store_mat_data(self, mats, d_step, moment): np.empty(0, dtype=mpar_dtype), "Material parameters data") print('Dumping Material %s data %s to %s.' % - (key, moment, os.path.abspath(self.h5_file))) + (key, dep_step_str, os.path.abspath(self.db_path))) # Add row for the timestep to EArray and Material Parameters table earr.append(np.array([iso_wt_frac], dtype=np.float64)) mpar_table.append(mpar_array) @@ -276,10 +282,11 @@ def store_mat_data(self, mats, d_step, moment): db.close() def store_run_step_info(self): - """Adds to database Serpent and Saltproc simulation parameters - (execution time, memory usage), multiplication factor, breeding ratio, + """Adds the following depletion code and SaltProc simulation + data at the current depletion step to the database: + execution time, memory usage, multiplication factor, breeding ratio, delayed neutron precursor data, fission mass, cumulative depletion - time, power level for the current time step. + time, power level. """ # Read info from depcode _res.m File @@ -287,7 +294,6 @@ def store_run_step_info(self): # Initialize beta groups number b_g = len(self.sim_depcode.param['beta_eff']) # numpy array row storage for run info - class Step_info(tb.IsDescription): keff_bds = tb.Float32Col((2,)) keff_eds = tb.Float32Col((2,)) @@ -301,7 +307,10 @@ class Step_info(tb.IsDescription): fission_mass_bds = tb.Float32Col() fission_mass_eds = tb.Float32Col() # Open or restore db and append data to it - db = tb.open_file(self.h5_file, mode='a', filters=self.compression) + db = tb.open_file( + self.db_path, + mode='a', + filters=self.compression_params) try: step_info_table = db.get_node( db.root, @@ -312,7 +321,7 @@ class Step_info(tb.IsDescription): step_info_table = db.create_table( db.root, 'simulation_parameters', - Step_info, + Step_info, # self.sim_depcode.Step_info, "Simulation parameters after each timestep") # Intializing burn_time array at the first depletion step self.burn_time = 0.0 @@ -320,6 +329,7 @@ class Step_info(tb.IsDescription): # Define row of table as step_info step_info = step_info_table.row # Define all values in the row + step_info['keff_bds'] = self.sim_depcode.param['keff_bds'] step_info['keff_eds'] = self.sim_depcode.param['keff_eds'] step_info['breeding_ratio'] = self.sim_depcode.param[ @@ -338,24 +348,33 @@ class Step_info(tb.IsDescription): 'fission_mass_bds'] step_info['fission_mass_eds'] = self.sim_depcode.param[ 'fission_mass_eds'] + # Inject the Record value into the table step_info.append() step_info_table.flush() db.close() def store_run_init_info(self): - """Adds to database Serpent and Saltproc simulation parameters before - starting depletion sequence. + """Adds the following depletion code and SaltProc simulation parameters + to the database: + neutron population, active cycles, inactive cycles, depletion code + version simulation title, depetion code input file path, depletion code + working directory, cross section data path, # of OMP threads, # of MPI + tasks, memory optimization mode (Serpent), depletion timestep size. + """ # numpy arraw row storage for run info + # delete and make this datatype specific + # to Depcode subclasses sim_info_dtype = np.dtype([ ('neutron_population', int), ('active_cycles', int), ('inactive_cycles', int), - ('serpent_version', 'S20'), + ('depcode_name', 'S20'), + ('depcode_version', 'S20'), ('title', 'S90'), - ('serpent_input_filename', 'S90'), - ('serpent_working_dir', 'S90'), + ('depcode_input_filename', 'S90'), + ('depcode_working_dir', 'S90'), ('xs_data_path', 'S90'), ('OMP_threads', int), ('MPI_tasks', int), @@ -368,11 +387,12 @@ def store_run_init_info(self): sim_info_row = ( self.sim_depcode.npop, self.sim_depcode.active_cycles, - self.sim_depcode.inactive_cycles, - self.sim_depcode.sim_info['serpent_version'], + self.sim_depcode.inactive_cycles, # delete the below + self.sim_depcode.sim_info['depcode_name'], + self.sim_depcode.sim_info['depcode_version'], self.sim_depcode.sim_info['title'], - self.sim_depcode.sim_info['serpent_input_filename'], - self.sim_depcode.sim_info['serpent_working_dir'], + self.sim_depcode.sim_info['depcode_input_filename'], + self.sim_depcode.sim_info['depcode_working_dir'], self.sim_depcode.sim_info['xs_data_path'], self.sim_depcode.sim_info['OMP_threads'], self.sim_depcode.sim_info['MPI_tasks'], @@ -380,8 +400,12 @@ def store_run_init_info(self): self.sim_depcode.sim_info['depletion_timestep'] ) sim_info_array = np.array([sim_info_row], dtype=sim_info_dtype) + # Open or restore db and append datat to it - db = tb.open_file(self.h5_file, mode='a', filters=self.compression) + db = tb.open_file( + self.db_path, + mode='a', + filters=self.compression_params) try: sim_info_table = db.get_node(db.root, 'initial_depcode_siminfo') except Exception: @@ -393,32 +417,7 @@ def store_run_init_info(self): sim_info_table.flush() db.close() - def switch_to_next_geometry(self): - """Inserts line with path to next Serpent geometry file at the - beginning of the Serpent iteration input file. - """ - geo_line_n = 5 - f = open(self.sim_depcode.input_path, 'r') - data = f.readlines() - f.close() - - current_geo_file = data[geo_line_n].split('\"')[1] - current_geo_idx = self.sim_depcode.geo_file.index(current_geo_file) - try: - new_geo_file = self.sim_depcode.geo_file[current_geo_idx + 1] - except IndexError: - print('No more geometry files available \ - and the system went subcritical \n\n') - print('Simulation ended') - return - new_data = [d.replace(current_geo_file, new_geo_file) for d in data] - print('Switching to next geometry file: ', new_geo_file) - - f = open(self.sim_depcode.input_path, 'w') - f.writelines(new_data) - f.close() - - def read_k_eds_delta(self, current_timestep, restart): + def read_k_eds_delta(self, current_timestep): """Reads from database delta between previous and current `keff` at the end of depletion step and returns `True` if predicted `keff` at the next depletion step drops below 1. @@ -427,8 +426,6 @@ def read_k_eds_delta(self, current_timestep, restart): ---------- current_timestep : int Number of current depletion time step. - restart : bool - Was this simulation restarted? Returns ------- @@ -437,9 +434,9 @@ def read_k_eds_delta(self, current_timestep, restart): """ - if current_timestep > 3 or restart: + if current_timestep > 3 or self.restart_flag: # Open or restore db and read data - db = tb.open_file(self.h5_file, mode='r') + db = tb.open_file(self.db_path, mode='r') sim_param = db.root.simulation_parameters k_eds = np.array([x['keff_eds'][0] for x in sim_param.iterrows()]) db.close() @@ -453,8 +450,9 @@ def read_k_eds_delta(self, current_timestep, restart): return False def check_switch_geo_trigger(self, current_time, switch_time): - """Compares current timestep with user defined time to switch geometry. - Returns `True` if its time. + """Compares the current timestep with the user defined times + at which to switch reactor geometry, and returns `True` if there + is a match. Parameters ---------- diff --git a/saltproc/tests/integration_tests/const_repr/tap_main_test.json b/saltproc/tests/integration_tests/const_repr/tap_main_test.json index 62cdf5b63..86d50f528 100644 --- a/saltproc/tests/integration_tests/const_repr/tap_main_test.json +++ b/saltproc/tests/integration_tests/const_repr/tap_main_test.json @@ -1,18 +1,29 @@ { - "Path to Serpent executable": "sss2", - "File containing processing system objects": "input_5leu.json", - "Graph file containing processing system structure": "tap.dot", - "User's Serpent input file with reactor model": "tap_test.serpent", - "Path output data storing folder": "./", - "Output HDF5 database file name": "db_saltproc.h5", - "Number of neutrons per generation": 50, - "Number of active generations": 20, - "Number of inactive generations": 20, - "Restart simulation from the step when it stopped?": false, - "Geometry file/files to use in Serpent runs": "347_base.ini", - "Switch to another geometry when keff drops below 1?": false, - "Salt mass flow rate throughout reactor core (g/s)": 9.92E+6, - "Number of steps for constant power and depletion interval case": 2, - "Depletion step interval or Cumulative time (end of step) (d)": 5, - "Reactor power or power step list during depletion step (W)": 1.250E+9 + "proc_input_file": "input_5leu.json", + "dot_input_file": "tap.dot", + "output_path": "./data", + "num_depsteps": 2, + "depcode": { + "codename": "serpent", + "exec_path": "sss2", + "template_inputfile_path": "./tap_test.serpent", + "iter_inputfile": "saltproc_serpent", + "iter_matfile": "saltproc_mat", + "npop": 50, + "active_cycles": 20, + "inactive_cycles": 20, + "geo_file_paths": ["./347_base.ini"] + }, + "simulation": { + "sim_name": "tap_integration_test", + "db_name": "db_saltproc.h5", + "restart_flag": false, + "adjust_geo": false + }, + "reactor": { + "volume": 1.0, + "mass_flowrate": 9.92E+6, + "power_levels": [ 1.250E+9 ], + "dep_step_length_cumulative": [ 5 ] + } } diff --git a/saltproc/tests/integration_tests/const_repr/test_const_reproc_run.py b/saltproc/tests/integration_tests/const_repr/test_const_reproc_run.py index 06dc565fe..33023da31 100644 --- a/saltproc/tests/integration_tests/const_repr/test_const_reproc_run.py +++ b/saltproc/tests/integration_tests/const_repr/test_const_reproc_run.py @@ -9,8 +9,8 @@ path = os.path.realpath(__file__) sys.path.append(os.path.dirname(os.path.dirname(path))) directory = os.path.dirname(path) -db_exp_file = directory+'/2step_non_ideal_2.h5' -db_file = directory+'/db_saltproc.h5' +db_exp_file = directory + '/2step_non_ideal_2.h5' +db_file = directory + '/data/db_saltproc.h5' tol = 1e-9 diff --git a/saltproc/tests/integration_tests/no_repro/test_no_reproc_run.py b/saltproc/tests/integration_tests/no_repro/test_no_reproc_run.py index c316ff849..301b2a9cf 100644 --- a/saltproc/tests/integration_tests/no_repro/test_no_reproc_run.py +++ b/saltproc/tests/integration_tests/no_repro/test_no_reproc_run.py @@ -21,11 +21,11 @@ depcode = DepcodeSerpent( exec_path='sss2', - template_path=directory + + template_inputfile_path=directory + '/saltproc_9d.inp', - input_path=sss_file, + iter_inputfile=sss_file, iter_matfile=iter_matfile, - geo_file=[ + geo_files=[ os.path.join( directory, '../../test_geo.inp')], @@ -36,18 +36,65 @@ sim_depcode=depcode, core_number=1, node_number=1, - h5_file=db_file, - iter_matfile=iter_matfile) + db_path=db_file) tap = Reactor(volume=1.0, power_levels=[1.250E+09], - depl_hist=[3]) + dep_step_length_cumulative=[3]) + + +def runsim_no_reproc(simulation, reactor, nsteps): + """Run simulation sequence for integration test. No reprocessing + involved, just re-running depletion code for comparision with model + output. + + Parameters + ---------- + reactor : `Reactor` + Contains information about power load curve and cumulative + depletion time for the integration test. + nsteps : int + Number of depletion time steps in integration test run. + + """ + + ###################################################################### + # Start sequence + for dep_step in range(nsteps): + print("\nStep #%i has been started" % (dep_step + 1)) + if dep_step == 0: # First step + simulation.sim_depcode.write_depcode_input( + reactor, + dep_step, + False) + simulation.sim_depcode.run_depcode( + simulation.core_number, + simulation.node_number) + # Read general simulation data which never changes + simulation.store_run_init_info() + # Parse and store data for initial state (beginning of dep_step) + mats = simulation.sim_depcode.read_dep_comp( + False) + simulation.store_mat_data(mats, dep_step, False) + # Finish of First step + # Main sequence + else: + simulation.sim_depcode.run_depcode( + simulation.core_number, + simulation.node_number) + mats = simulation.sim_depcode.read_dep_comp( + True) + simulation.store_mat_data(mats, dep_step, False) + simulation.store_run_step_info() + simulation.sim_depcode.write_mat_file( + mats, + simulation.burn_time) @pytest.mark.slow # @pytest.mark.skip -def test_integration_3step_saltproc_no_reproc_heavy(): - simulation.runsim_no_reproc(tap, 2) +def test_integration_2step_saltproc_no_reproc_heavy(): + runsim_no_reproc(simulation, tap, 2) saltproc_out = sss_file + '_dep.m' dep_ser = serpent.parse_dep( directory + '/serpent_9d_dep.m', diff --git a/saltproc/tests/test.json b/saltproc/tests/test.json index 2360a8d97..207f0efa9 100644 --- a/saltproc/tests/test.json +++ b/saltproc/tests/test.json @@ -1,18 +1,29 @@ { - "Path to Serpent executable": "/home/andrei2/serpent/serpent2/src_2131/sss2", - "File containing processing system objects": "processes.json", - "Graph file containing processing system structure": "test.dot", - "User's Serpent input file with reactor model": "tap_test1.serpent", - "Path output data storing folder": "./", - "Output HDF5 database file name": "db_saltproc.h5", - "Number of neutrons per generation": 50, - "Number of active generations": 20, - "Number of inactive generations": 20, - "Restart simulation from the step when it stopped?": false, - "Geometry file/files to use in Serpent runs": "347_base.ini", - "Switch to another geometry when keff drops below 1?": false, - "Salt mass flow rate throughout reactor core (g/s)": 9.92E+6, - "Number of steps for constant power and depletion interval case": 2, - "Depletion step interval or Cumulative time (end of step) (d)": 5, - "Reactor power or power step list during depletion step (W)": 1.250E+9 + "proc_input_file": "processes.json", + "dot_input_file": "test.dot", + "output_path": "./data", + "num_depsteps": 2, + "depcode": { + "codename": "serpent", + "exec_path": "sss2", + "template_inputfile_path": "./tap_test1.serpent", + "iter_inputfile": "saltproc_serpent", + "iter_matfile": "saltproc_mat", + "npop": 50, + "active_cycles": 20, + "inactive_cycles": 20, + "geo_file_paths": ["./347_base.ini"] + }, + "simulation": { + "sim_name": "test_simulation", + "db_name": "db_saltproc.h5", + "restart_flag": false, + "adjust_geo": false + }, + "reactor": { + "volume": 1.0, + "mass_flowrate": 9.92E+6, + "power_levels": [ 1.250E+9 ], + "dep_step_length_cumulative": [ 5 ] + } } diff --git a/saltproc/tests/test_app.py b/saltproc/tests/test_app.py index b73aacb1f..6ed47dadb 100644 --- a/saltproc/tests/test_app.py +++ b/saltproc/tests/test_app.py @@ -8,27 +8,32 @@ sys.path.append(os.path.dirname(os.path.dirname(path))) # global class object directory = os.path.dirname(path) -input_file = directory + '/test' +iter_inputfile = directory + '/test' main_input = directory + '/test.json' dot_input = directory + '/test.dot' serpent = DepcodeSerpent(exec_path='sss2', - template_path=directory + '/template.inp', - input_path=input_file, + template_inputfile_path=directory + '/template.inp', + iter_inputfile=iter_inputfile, iter_matfile=directory + '/material', - geo_file=None) + geo_files=None) def test_read_main_input(): app.read_main_input(main_input) - assert app.neutron_pop == 50 - assert app.active_cycles == 20 - assert app.active_cycles == 20 - assert app.db_file == directory + '/./db_saltproc.h5' - assert app.geo_file == ['347_base.ini'] - assert app.restart_flag is False - np.testing.assert_equal(app.power_hist, [1.250E+9, 1.250E+9]) - np.testing.assert_equal(app.depl_hist, [5, 10]) + assert app.depcode_inp['codename'] == "serpent" + assert app.depcode_inp['npop'] == 50 + assert app.depcode_inp['active_cycles'] == 20 + assert app.depcode_inp['inactive_cycles'] == 20 + assert app.simulation_inp['db_name'] == directory + \ + '/./data/db_saltproc.h5' + assert app.depcode_inp['geo_file_paths'] == [directory + '/./347_base.ini'] + assert app.simulation_inp['restart_flag'] is False + np.testing.assert_equal( + app.reactor_inp['power_levels'], [ + 1.250E+9, 1.250E+9]) + np.testing.assert_equal(app.reactor_inp['dep_step_length_cumulative'], + [5, 10]) def test_read_processes_from_input(): @@ -63,7 +68,7 @@ def test_read_dot(): def test_reprocessing(): - mats = serpent.read_dep_comp(input_file, True) + mats = serpent.read_dep_comp(True) waste_st, rem_mass = app.reprocessing(mats) assert rem_mass['fuel'] == 1401.0846504569054 assert rem_mass['ctrlPois'] == 0.0 @@ -73,7 +78,7 @@ def test_reprocessing(): def test_refill(): - mats = serpent.read_dep_comp(input_file, True) + mats = serpent.read_dep_comp(True) waste_st, rem_mass = app.reprocessing(mats) m_after_refill = app.refill(mats, rem_mass, waste_st) assert m_after_refill['fuel']['feed_leu']['U235'] == 43.573521906078334 diff --git a/saltproc/tests/test_depcode.py b/saltproc/tests/test_depcode.py index 58ff7ffc6..de85a4fa8 100644 --- a/saltproc/tests/test_depcode.py +++ b/saltproc/tests/test_depcode.py @@ -3,6 +3,7 @@ from saltproc import Reactor import os import sys +import shutil import numpy as np path = os.path.realpath(__file__) @@ -11,19 +12,28 @@ # global class object directory = os.path.dirname(path) -serpent = DepcodeSerpent(exec_path='sss2', - template_path=directory + '/template.inp', - input_path=directory + '/test', - iter_matfile=directory + '/material', - geo_file=[os.path.join(directory, '../test_geo.inp')]) +serpent = DepcodeSerpent( + exec_path='sss2', + template_inputfile_path=directory + + '/template.inp', + iter_inputfile=directory + + '/test', + iter_matfile=directory + + '/material', + geo_files=[ + os.path.join( + directory, + '../test_geo.inp')]) msr = Reactor(volume=1.0, power_levels=[1.250E+09, 1.250E+09, 5.550E+09], - depl_hist=[111.111, 2101.9, 3987.5]) + dep_step_length_cumulative=[111.111, 2101.9, 3987.5]) +geo_test_input = directory + '/test_geometry_switch.inp' -def test_get_tra_or_dec(): - serpent.get_tra_or_dec(serpent.input_path) + +def test_create_nuclide_name_map_zam_to_serpent(): + serpent.create_nuclide_name_map_zam_to_serpent() assert serpent.iso_map[380880] == '38088.09c' assert serpent.iso_map[962400] == '96240.09c' assert serpent.iso_map[952421] == '95342.09c' @@ -34,18 +44,19 @@ def test_get_tra_or_dec(): assert serpent.iso_map[410911] == '410911' -def test_sss_meta_zzz(): - assert serpent.sss_meta_zzz(47310) == 471101 - assert serpent.sss_meta_zzz(95342) == 952421 - assert serpent.sss_meta_zzz(61348) == 611481 - assert serpent.sss_meta_zzz(52327) == 521271 - assert serpent.sss_meta_zzz(1001) == 1001 - assert serpent.sss_meta_zzz(1002) == 1002 - assert serpent.sss_meta_zzz(48315) == 481151 +def test_convert_nuclide_name_serpent_to_zam(): + assert serpent.convert_nuclide_name_serpent_to_zam(47310) == 471101 + assert serpent.convert_nuclide_name_serpent_to_zam(95342) == 952421 + assert serpent.convert_nuclide_name_serpent_to_zam(61348) == 611481 + assert serpent.convert_nuclide_name_serpent_to_zam(52327) == 521271 + assert serpent.convert_nuclide_name_serpent_to_zam(1001) == 1001 + assert serpent.convert_nuclide_name_serpent_to_zam(1002) == 1002 + assert serpent.convert_nuclide_name_serpent_to_zam(48315) == 481151 -def test_read_depcode_template(): - template_str = serpent.read_depcode_template(serpent.template_path) +def test_read_plaintext_file(): + template_str = serpent.read_plaintext_file( + serpent.template_inputfile_path) assert template_str[6] == '%therm zrh_h 900 hzr05.32t hzr06.32t\n' assert template_str[18] == 'set pop 30 20 10\n' assert template_str[22] == 'set bumode 2\n' @@ -57,7 +68,7 @@ def test_change_sim_par(): serpent.active_cycles = 101 serpent.inactive_cycles = 33 out = serpent.change_sim_par( - serpent.read_depcode_template(serpent.template_path) + serpent.read_plaintext_file(serpent.template_inputfile_path) ) assert out[18] == 'set pop %i %i %i\n' % ( serpent.npop, @@ -84,11 +95,20 @@ def test_get_nuc_name(): def test_read_depcode_info(): serpent.read_depcode_info() - assert serpent.sim_info['serpent_version'] == 'Serpent 2.1.31' + assert serpent.sim_info['depcode_name'] == 'Serpent' + assert serpent.sim_info['depcode_version'] == '2.1.31' assert serpent.sim_info['title'] == 'Untitled' + assert serpent.sim_info['depcode_input_filename'] == \ + '/home/andrei2/Desktop/git/saltproc/develop/saltproc/data/saltproc_tap' + assert serpent.sim_info['depcode_working_dir'] == \ + '/home/andrei2/Desktop/git/saltproc/develop/saltproc' + assert serpent.sim_info['xs_data_path'] == \ + '/home/andrei2/serpent/xsdata/jeff312/sss_jeff312.xsdata' + assert serpent.sim_info['MPI_tasks'] == 1 assert serpent.sim_info['OMP_threads'] == 4 assert serpent.sim_info['memory_optimization_mode'] == 4 + assert serpent.sim_info['depletion_timestep'] == 3.0 def test_read_depcode_step_param(): @@ -103,7 +123,7 @@ def test_read_depcode_step_param(): def test_read_dep_comp(): - mats = serpent.read_dep_comp(serpent.input_path, True) + mats = serpent.read_dep_comp(True) assert mats['fuel']['U235'] == 3499538.3359278883 assert mats['fuel']['U238'] == 66580417.24509208 assert mats['fuel']['F19'] == 37145139.35897285 @@ -115,10 +135,11 @@ def test_read_dep_comp(): def test_write_mat_file(): - mats = serpent.read_dep_comp(serpent.input_path, True) - mat_file = serpent.input_path + '.mat' - serpent.write_mat_file(mats, mat_file, 12.0) - mat_str = serpent.read_depcode_template(mat_file) + mats = serpent.read_dep_comp(True) + iter_matfile_old = serpent.iter_matfile + serpent.iter_inputfile = serpent.iter_inputfile + '.mat' + serpent.write_mat_file(mats, 12.0) + mat_str = serpent.read_plaintext_file(serpent.iter_matfile) assert mat_str[0] == '% Material compositions (after 12.000000 days)\n' if 'fuel' in mat_str[3]: assert mat_str[3].split()[-1] == '2.27175E+07' @@ -128,49 +149,50 @@ def test_write_mat_file(): elif 'ctrlPois' in mat_str[3]: assert mat_str[3].split()[-1] == '1.11635E+04' assert mat_str[4] == ' 1001.09c -1.21000137902945E-35\n' - os.remove(mat_file) + os.remove(serpent.iter_matfile) + serpent.iter_matfile = iter_matfile_old def test_insert_path_to_geometry(): - d = serpent.read_depcode_template(serpent.template_path) + d = serpent.read_plaintext_file(serpent.template_inputfile_path) d_new = serpent.insert_path_to_geometry(d) assert d_new[5].split('/')[-1] == 'test_geo.inp"\n' def test_replace_burnup_parameters(): - time = msr.depl_hist.copy() + time = msr.dep_step_length_cumulative.copy() time.insert(0, 0.0) depsteps = np.diff(time) - d = serpent.read_depcode_template(serpent.template_path) + d = serpent.read_plaintext_file(serpent.template_inputfile_path) for idx in range(len(msr.power_levels)): d = serpent.replace_burnup_parameters(d, msr, idx) - out_file = open(serpent.template_path + str(idx), 'w') + out_file = open(serpent.template_inputfile_path + str(idx), 'w') out_file.writelines(d) out_file.close() - d_new = serpent.read_depcode_template( - serpent.template_path + str(idx)) + d_new = serpent.read_plaintext_file( + serpent.template_inputfile_path + str(idx)) assert d_new[8].split()[4] == 'daystep' assert d_new[8].split()[2] == str("%5.9E" % msr.power_levels[idx]) assert d_new[8].split()[5] == str("%7.5E" % depsteps[idx]) - os.remove(serpent.template_path + str(idx)) + os.remove(serpent.template_inputfile_path + str(idx)) def test_create_iter_matfile(): - d = serpent.read_depcode_template(serpent.template_path) + d = serpent.read_plaintext_file(serpent.template_inputfile_path) out = serpent.create_iter_matfile(d) assert out[0].split()[-1] == '\"' + serpent.iter_matfile + '\"' os.remove(serpent.iter_matfile) def test_write_depcode_input(): - serpent.write_depcode_input(serpent.template_path, - serpent.input_path + '_write_test', - msr, + iter_inputfile_old = serpent.iter_inputfile + serpent.iter_inputfile = serpent.iter_inputfile + '_write_test' + serpent.write_depcode_input(msr, 0, False) - d = serpent.read_depcode_template(serpent.input_path + '_write_test') + d = serpent.read_plaintext_file(serpent.iter_inputfile) print(d[0]) assert d[0].split('/')[-2] == 'tests' assert d[0].split('/')[-1] == 'material"\n' @@ -178,5 +200,19 @@ def test_write_depcode_input(): assert d[8].split()[4] == 'daystep' assert d[8].split()[-1] == '1.11111E+02' assert d[20] == 'set pop 1111 101 33\n' - os.remove(serpent.input_path + '_write_test') + os.remove(serpent.iter_inputfile) os.remove(serpent.iter_matfile) + + serpent.iter_inputfile = iter_inputfile_old + + +def test_switch_to_next_geometry(): + shutil.copy2(geo_test_input, serpent.iter_inputfile + '_test') + iter_inputfile_old = serpent.iter_inputfile + serpent.iter_inputfile = serpent.iter_inputfile + '_test' + serpent.geo_files += ['../../examples/406.inp', '../../examples/988.inp'] + serpent.switch_to_next_geometry() + d = serpent.read_plaintext_file(serpent.iter_inputfile) + assert d[5].split('/')[-1] == '988.inp"\n' + os.remove(serpent.iter_inputfile) + serpent.iter_inputfile = iter_inputfile_old diff --git a/saltproc/tests/test_materialflow.py b/saltproc/tests/test_materialflow.py index 303a4d7d1..fe839dc77 100644 --- a/saltproc/tests/test_materialflow.py +++ b/saltproc/tests/test_materialflow.py @@ -6,20 +6,20 @@ sys.path.append(os.path.dirname(os.path.dirname(path))) # global class object directory = os.path.dirname(path) -input_file = directory + '/test' +iter_inputfile = directory + '/test' serpent = DepcodeSerpent( exec_path='/home/andrei2/serpent/serpent2/src_2131/sss2', - template_path=directory + + template_inputfile_path=directory + '/template.inp', - input_path=input_file, + iter_inputfile=iter_inputfile, iter_matfile=directory + '/material', - geo_file=None) + geo_files=None) def test_get_mass(): - mats = serpent.read_dep_comp(input_file, True) + mats = serpent.read_dep_comp(True) assert mats['fuel'].get_mass() == 112683343.50000001 assert mats['fuel']['U235'] == 3499538.3359278883 assert mats['fuel']['U238'] == 66580417.24509208 @@ -33,7 +33,7 @@ def test_get_mass(): def test_scale_matflow(): - mats = serpent.read_dep_comp(input_file, True) + mats = serpent.read_dep_comp(True) scale_factor = 0.7 scaled_matflow = mats['fuel'].scale_matflow(scale_factor) assert scaled_matflow[922350000] == scale_factor * 3499538.3359278883 @@ -43,7 +43,7 @@ def test_scale_matflow(): def test_copy_pymat_attrs(): - mats = serpent.read_dep_comp(input_file, True) + mats = serpent.read_dep_comp(True) target_mat = mats['fuel'] target_mat.copy_pymat_attrs(mats['ctrlPois']) assert target_mat.density == 5.873 diff --git a/saltproc/tests/test_process.py b/saltproc/tests/test_process.py index 6e3fd5b3d..ee5f58e51 100644 --- a/saltproc/tests/test_process.py +++ b/saltproc/tests/test_process.py @@ -8,16 +8,16 @@ sys.path.append(os.path.dirname(os.path.dirname(path))) # global class object directory = os.path.dirname(path) -input_file = directory + '/test' +iter_inputfile = directory + '/test' serpent = DepcodeSerpent( exec_path='/home/andrei2/serpent/serpent2/src_2131/sss2', - template_path=directory + + template_inputfile_path=directory + '/template.inp', - input_path=input_file, + iter_inputfile=iter_inputfile, iter_matfile=directory + '/material', - geo_file=None) + geo_files=None) process = Process(mass_flowrate=10, capacity=99.0, @@ -26,7 +26,7 @@ def test_rem_elements(): - mats = serpent.read_dep_comp(input_file, True) + mats = serpent.read_dep_comp(True) waste = process.rem_elements(mats['fuel']) np.testing.assert_almost_equal(waste[541350000], 19.79776930513891) np.testing.assert_almost_equal(waste[541360000], 176.44741987005173) diff --git a/saltproc/tests/test_separator.py b/saltproc/tests/test_separator.py index 6438c441c..686db7968 100644 --- a/saltproc/tests/test_separator.py +++ b/saltproc/tests/test_separator.py @@ -8,16 +8,16 @@ sys.path.append(os.path.dirname(os.path.dirname(path))) # global class object directory = os.path.dirname(path) -input_file = directory + '/test' +iter_inputfile = directory + '/test' serpent = DepcodeSerpent( exec_path='/home/andrei2/serpent/serpent2/src_2131/sss2', - template_path=directory + + template_inputfile_path=directory + '/template.inp', - input_path=input_file, + iter_inputfile=iter_inputfile, iter_matfile=directory + '/material', - geo_file=None) + geo_files=None) process = Separator(mass_flowrate=10, capacity=99.0, @@ -26,7 +26,7 @@ def test_rem_elements(): - mats = serpent.read_dep_comp(input_file, True) + mats = serpent.read_dep_comp(True) waste = process.rem_elements(mats['fuel']) np.testing.assert_almost_equal(waste[541350000], 19.5320018359295) np.testing.assert_almost_equal(waste[541360000], 174.0787699729534) diff --git a/saltproc/tests/test_simulation.py b/saltproc/tests/test_simulation.py index 8c3c2bc7b..07f82219f 100644 --- a/saltproc/tests/test_simulation.py +++ b/saltproc/tests/test_simulation.py @@ -1,22 +1,25 @@ from __future__ import absolute_import, division, print_function from saltproc import DepcodeSerpent from saltproc import Simulation +import saltproc.app import os import sys -import shutil +import numpy as np +import tables as tb path = os.path.realpath(__file__) sys.path.append(os.path.dirname(os.path.dirname(path))) # global class object directory = os.path.dirname(path) -input_file = directory + '/test' -geo_test_input = directory + '/test_geometry_switch.inp' +iter_inputfile = directory + '/test' +main_input = directory + '/test.json' +dot_input = directory + '/test.dot' serpent = DepcodeSerpent( exec_path='/home/andrei2/serpent/serpent2/src_2131/sss2', - template_path=directory + '/template.inp', - input_path=input_file, + template_inputfile_path=directory + '/template.inp', + iter_inputfile=iter_inputfile, iter_matfile=directory + '/material', - geo_file=[ + geo_files=[ '../../examples/406.inp', '../../examples/988.inp']) @@ -24,18 +27,414 @@ sim_depcode=serpent, core_number=1, node_number=1, - h5_file=directory + '/test_db.h5', - iter_matfile=serpent.iter_matfile) + db_path=directory + '/test_db.h5') -def test_read_k_eds_delta(): - assert simulation.read_k_eds_delta(7, False) is False +def test_check_switch_geo_trigger(): + """ + This unit test checks that ``check_switch_geo_trigger`` functions + consistently with its docstring. + """ + + switch_times = [1.0, 3, -31, 86.23333, 1e-16, 2e-18, "two o clock"] + assert simulation.check_switch_geo_trigger(1.0, switch_times) is True + assert simulation.check_switch_geo_trigger(3, switch_times) is True + assert simulation.check_switch_geo_trigger(-32, switch_times) is False + assert simulation.check_switch_geo_trigger(86.233, switch_times) is False + assert simulation.check_switch_geo_trigger(1e-16, switch_times) is True + assert simulation.check_switch_geo_trigger(5e-18, switch_times) is False + assert simulation.check_switch_geo_trigger("three o clock", + switch_times) is False + assert simulation.check_switch_geo_trigger("two o clock", + switch_times) is True + + +def test_store_after_repr(): + """ + This unit test checks that select entries that ``store_after_repr_()` + stores in the database match the corresponding entries from the input + explicity in value and implicitly in type. + + unchecked processes: waste_bypass + waste_core_inlet + waste_core_outlet + waste_heat_exchanger + removal_tb_dy + feed_pure_gd + + """ + saltproc.app.read_main_input(main_input) + + # read data + mats = simulation.sim_depcode.read_dep_comp( + True) + waste_st, rem_mass = saltproc.app.reprocessing(mats) + m_after_refill = saltproc.app.refill(mats, rem_mass, waste_st) + + fuel_st = m_after_refill['fuel'] + + f_feed_leu = fuel_st['feed_leu'] + f_entrain_sep = fuel_st['waste_entrainment_separator'] + f_liq_met = fuel_st['waste_liquid_metal'] + f_nickel_filt = fuel_st['waste_nickel_filter'] + f_sparger = fuel_st['waste_sparger'] + + # we want to keep the old path for other sims, but for this + # test we'll want a fresh db + db_path_old = simulation.db_path + db_file = serpent.iter_inputfile + '.h5' + simulation.db_path = db_file + + # store data + simulation.store_mat_data(mats, 0, False) + simulation.store_after_repr(mats, m_after_refill, 0) + + # read stored data + try: + db = tb.open_file(simulation.db_path, mode='r', + filters=simulation.compression_params) + except Exception: + print('Unable to assign correct value to db.\ + See error stack for more info.') + + tmats = db.root.materials + tfuel_st = tmats.fuel.in_out_streams + + tf_feed_leu = tfuel_st.feed_leu[0] + tf_entrain_sep = tfuel_st.waste_entrainment_separator[0] + tf_liq_met = tfuel_st.waste_liquid_metal[0] + tf_nickel_filt = tfuel_st.waste_nickel_filter[0] + tf_sparger = tfuel_st.waste_sparger[0] + try: + assert tf_feed_leu[0] == f_feed_leu['Li6'] + assert tf_feed_leu[1] == f_feed_leu['Li7'] + assert tf_feed_leu[2] == f_feed_leu['F19'] + assert tf_feed_leu[3] == f_feed_leu['U235'] + assert tf_feed_leu[4] == f_feed_leu['U238'] + + assert tf_entrain_sep[0] == f_entrain_sep['H1'] + assert tf_entrain_sep[1] == f_entrain_sep['H2'] + assert tf_entrain_sep[459] == f_entrain_sep['Kr91'] + assert tf_entrain_sep[461] == f_entrain_sep['Kr93'] + assert tf_entrain_sep[991] == f_entrain_sep['Xe135'] + assert tf_entrain_sep[993] == f_entrain_sep['Xe136'] + + assert tf_liq_met[117] == f_liq_met['Ca49'] + assert tf_liq_met[288] == f_liq_met['Cu69'] + assert tf_liq_met[513] == f_liq_met['Sr102'] + assert tf_liq_met[518] == f_liq_met['Y89'] + assert tf_liq_met[1123] == f_liq_met['Pr159'] + assert tf_liq_met[1124] == f_liq_met['Nd142'] + + assert tf_nickel_filt[309] == f_nickel_filt['Zn70'] + assert tf_nickel_filt[437] == f_nickel_filt['Br90'] + assert tf_nickel_filt[964] == f_nickel_filt['I134'] + assert tf_nickel_filt[630] == f_nickel_filt['Tc99'] + + assert tf_sparger[0] == f_sparger['H1'] + assert tf_sparger[1] == f_sparger['H2'] + assert tf_sparger[459] == f_sparger['Kr91'] + assert tf_sparger[461] == f_sparger['Kr93'] + assert tf_sparger[991] == f_sparger['Xe135'] + assert tf_sparger[993] == f_sparger['Xe136'] + except AssertionError: + db.close() + os.remove(db_file) + simulation.db_path = db_path_old + raise AssertionError("incorrect Value") + except KeyError: + db.close() + os.remove(db_file) + simulation.db_path = db_path_old + raise KeyError("incorrect key") + except BaseException: + db.close() + os.remove(db_file) + simulation.db_path = db_path_old + print("something went wrong. See error stack for details") + + # close the file + db.close() + + # delete test file + os.remove(db_file) + + # use original db path + simulation.db_path = db_path_old + + +def test_store_mat_data(): + """ + This unit test checks that select entries that ``store_mat_data_()` + stores in the database match the corresponding entries from the input + explicity in value and implicitly in type. + """ + # read data + mats_before = simulation.sim_depcode.read_dep_comp( + False) + mats_after = simulation.sim_depcode.read_dep_comp( + True) + + fuel_before = mats_before['fuel'] + pois_before = mats_before['ctrlPois'] + + fuel_after = mats_after['fuel'] + pois_after = mats_after['ctrlPois'] + + # we want to keep the old path for other sims, but for this + # test we'll want a fresh db + db_path_old = simulation.db_path + db_file = serpent.iter_inputfile + '.h5' + simulation.db_path = db_file + + # store data at end + simulation.store_mat_data(mats_before, 0, False) + simulation.store_mat_data(mats_after, 0, True) + + # read stored data + try: + db = tb.open_file(simulation.db_path, mode='r', + filters=simulation.compression_params) + except Exception: + print('Unable to assign correct value to db.\ + See error stack for more info.') + + tmats = db.root.materials + + tfuel_before = tmats.fuel.before_reproc.comp[0] + tfuel_before_params = tmats.fuel.before_reproc.parameters[0] + + tfuel_after = tmats.fuel.after_reproc.comp[0] + tfuel_after_params = tmats.fuel.after_reproc.parameters[0] + + tpois_before = tmats.ctrlPois.before_reproc.comp[0] + tpois_before_params = tmats.ctrlPois.before_reproc.parameters[0] + tpois_after = tmats.ctrlPois.after_reproc.comp[0] + tpois_after_params = tmats.ctrlPois.after_reproc.parameters[0] -def test_switch_to_next_geometry(): - shutil.copy2(geo_test_input, serpent.input_path + '_test') - serpent.input_path = serpent.input_path + '_test' - simulation.switch_to_next_geometry() - d = serpent.read_depcode_template(serpent.input_path) - assert d[5].split('/')[-1] == '988.inp"\n' - os.remove(serpent.input_path) + # Check the mass fractions + try: + assert tfuel_before[1566] == fuel_before['U235'] + assert tfuel_before[1570] == fuel_before['U238'] + assert tfuel_before[37] == fuel_before['F19'] + assert tfuel_before[8] == fuel_before['Li7'] + assert tfuel_before[1610] == fuel_before['Cm240'] + assert tfuel_before[1589] == fuel_before['Pu239'] + assert tpois_before[1213] == pois_before['Gd155'] + assert tpois_before[32] == pois_before['O16'] + + assert tfuel_after[1566] == fuel_after['U235'] + assert tfuel_after[1570] == fuel_after['U238'] + assert tfuel_after[37] == fuel_after['F19'] + assert tfuel_after[8] == fuel_after['Li7'] + assert tfuel_after[1610] == fuel_after['Cm240'] + assert tfuel_after[1589] == fuel_after['Pu239'] + assert tpois_after[1213] == pois_after['Gd155'] + assert tpois_after[32] == pois_after['O16'] + + # Check the parameters + assert tfuel_before_params[0] == fuel_before.mass + assert tfuel_before_params[1] == fuel_before.density + assert tfuel_before_params[2] == fuel_before.vol + assert tfuel_before_params[3] == fuel_before.temp + assert tfuel_before_params[4] == fuel_before.mass_flowrate + assert tfuel_before_params[5] == fuel_before.void_frac + assert tfuel_before_params[6] == fuel_before.burnup + + assert tpois_before_params[0] == pois_before.mass + assert tpois_before_params[1] == pois_before.density + assert tpois_before_params[2] == pois_before.vol + assert tpois_before_params[3] == pois_before.temp + assert tpois_before_params[4] == pois_before.mass_flowrate + assert tpois_before_params[5] == pois_before.void_frac + assert tpois_before_params[6] == pois_before.burnup + + assert tfuel_after_params[0] == fuel_after.mass + assert tfuel_after_params[1] == fuel_after.density + assert tfuel_after_params[2] == fuel_after.vol + assert tfuel_after_params[3] == fuel_after.temp + assert tfuel_after_params[4] == fuel_after.mass_flowrate + assert tfuel_after_params[5] == fuel_after.void_frac + assert tfuel_after_params[6] == fuel_after.burnup + + assert tpois_after_params[0] == pois_after.mass + assert tpois_after_params[1] == pois_after.density + assert tpois_after_params[2] == pois_after.vol + assert tpois_after_params[3] == pois_after.temp + assert tpois_after_params[4] == pois_after.mass_flowrate + assert tpois_after_params[5] == pois_after.void_frac + assert tpois_after_params[6] == pois_after.burnup + except AssertionError: + db.close() + os.remove(db_file) + simulation.db_path = db_path_old + raise AssertionError("incorrect Value") + except KeyError: + db.close() + os.remove(db_file) + simulation.db_path = db_path_old + raise KeyError("incorrect key") + except BaseException: + db.close() + os.remove(db_file) + simulation.db_path = db_path_old + print("something went wrong. See error stack for details") + + # close the file + db.close() + + # delete test file + os.remove(db_file) + + # use original db path + simulation.db_path = db_path_old + + +def test_store_run_init_info(): + """ + This unit test checks that the entries ``store_run_init_info()` + stores in the database match the corresponding entries from the input + explicity in value and implicitly in type. + """ + + # read data + simulation.sim_depcode.read_depcode_info() + init_info = simulation.sim_depcode.sim_info + + # we want to keep the old path for other sims, but for this + # test we'll want a fresh db + db_path_old = simulation.db_path + db_file = serpent.iter_inputfile + '.h5' + simulation.db_path = db_file + + # store data at + simulation.store_run_init_info() + + # read stored data + try: + db = tb.open_file(simulation.db_path, mode='r', + filters=simulation.compression_params) + except Exception: + db.close() + print('Unable to assign correct value to db.\ + See error stack for more info.') + + tinit_info = db.root.initial_depcode_siminfo[0] + + try: + assert tinit_info[0] == simulation.sim_depcode.npop + assert tinit_info[1] == simulation.sim_depcode.active_cycles + assert tinit_info[2] == simulation.sim_depcode.inactive_cycles + assert str(tinit_info[3])[2:-1] == init_info['depcode_name'] + assert str(tinit_info[4])[2:-1] == init_info['depcode_version'] + assert str(tinit_info[5])[2:-1] == init_info['title'] + assert str(tinit_info[6])[2:-1] == init_info['depcode_input_filename'] + assert str(tinit_info[7])[2:-1] == init_info['depcode_working_dir'] + assert str(tinit_info[8])[2:-1] == init_info['xs_data_path'] + assert tinit_info[9] == init_info['OMP_threads'] + assert tinit_info[10] == init_info['MPI_tasks'] + assert tinit_info[11] == init_info['memory_optimization_mode'] + assert tinit_info[12] == init_info['depletion_timestep'] + except AssertionError: + db.close() + os.remove(db_file) + simulation.db_path = db_path_old + raise AssertionError("incorrect Value") + except KeyError: + db.close() + os.remove(db_file) + simulation.db_path = db_path_old + raise KeyError("incorrect key") + except BaseException: + db.close() + os.remove(db_file) + simulation.db_path = db_path_old + print("something went wrong. See error stack for details") + + print("") + + # close the file + db.close() + + # delete test file + os.remove(db_file) + + # use original db path + simulation.db_path = db_path_old + + +def test_store_run_step_info(): + """ + This unit test checks that the entries ``store_run_step_info()` + stores in the database match the corresponding entries from the input + explicity in value and implicitly in type. + """ + # read data + simulation.sim_depcode.read_depcode_step_param() + step_info = simulation.sim_depcode.param + + # we want to keep the old path for other sims, but for this + # test we'll want a fresh db + db_path_old = simulation.db_path + db_file = serpent.iter_inputfile + '.h5' + simulation.db_path = db_file + + # store data at + simulation.store_run_step_info() + + # read stored data + try: + db = tb.open_file(simulation.db_path, mode='r', + filters=simulation.compression_params) + except Exception: + print('Unable to assign correct value to db.\ + See error stack for more info.') + + tstep_info = db.root.simulation_parameters[0] + try: + assert np.array_equal(tstep_info[0], + step_info['beta_eff'].astype('float32')) + assert np.array_equal(tstep_info[1], + step_info['breeding_ratio'].astype('float32')) + assert tstep_info[2] == simulation.burn_time + assert np.array_equal(tstep_info[3], + step_info['delayed_neutrons_lambda']. + astype('float32')) + assert tstep_info[4] == step_info['fission_mass_bds'].astype('float32') + assert tstep_info[5] == step_info['fission_mass_eds'].astype('float32') + assert np.array_equal(tstep_info[6], + step_info['keff_bds'].astype('float32')) + assert np.array_equal(tstep_info[7], + step_info['keff_eds'].astype('float32')) + assert tstep_info[8] == step_info['memory_usage'].astype('float32') + assert tstep_info[9] == step_info['power_level'].astype('float32') + assert tstep_info[10] == step_info['execution_time'].astype('float32') + except AssertionError: + db.close() + os.remove(db_file) + simulation.db_path = db_path_old + raise AssertionError("incorrect Value") + except KeyError: + db.close() + os.remove(db_file) + simulation.db_path = db_path_old + raise KeyError("incorrect key") + except BaseException: + db.close() + os.remove(db_file) + simulation.db_path = db_path_old + print("something went wrong. See error stack for details") + + # close the file + db.close() + + # delete test file + os.remove(db_file) + + # use original db path + simulation.db_path = db_path_old + + +def test_read_k_eds_delta(): + assert simulation.read_k_eds_delta(7) is False diff --git a/saltproc/tests/test_sparger.py b/saltproc/tests/test_sparger.py index 70ff5b325..b80318227 100644 --- a/saltproc/tests/test_sparger.py +++ b/saltproc/tests/test_sparger.py @@ -8,16 +8,16 @@ sys.path.append(os.path.dirname(os.path.dirname(path))) # global class object directory = os.path.dirname(path) -input_file = directory + '/test' +iter_inputfile = directory + '/test' serpent = DepcodeSerpent( exec_path='/home/andrei2/serpent/serpent2/src_2131/sss2', - template_path=directory + + template_inputfile_path=directory + '/template.inp', - input_path=input_file, + iter_inputfile=iter_inputfile, iter_matfile=directory + '/material', - geo_file=None) + geo_files=None) process = Sparger(mass_flowrate=10, capacity=99.0, @@ -26,7 +26,7 @@ def test_rem_elements(): - mats = serpent.read_dep_comp(input_file, True) + mats = serpent.read_dep_comp(True) waste = process.rem_elements(mats['fuel']) np.testing.assert_almost_equal(waste[541350000], 8.061014535231715) np.testing.assert_almost_equal(waste[541360000], 71.8437109936129) diff --git a/saltproc/version.py b/saltproc/version.py index bdfb452b8..7e2162a0b 100644 --- a/saltproc/version.py +++ b/saltproc/version.py @@ -85,7 +85,8 @@ MINOR = _version_minor MICRO = _version_micro VERSION = __version__ -PACKAGE_DATA = {'saltproc': [pjoin('data', '*')]} +PACKAGE_DATA = {'saltproc': [pjoin('data', '*'), + './input_schema.json']} PACKAGES = ["saltproc", "saltproc.tests"] REQUIRES = ["numpy", @@ -93,4 +94,5 @@ "networkx", "pydotplus", "pytest", - "argparse"] + "argparse", + "jsonschema"]