diff --git a/smr/build-assembly-long.py b/smr/build-assembly-long.py index 623907c..79734c3 100644 --- a/smr/build-assembly-long.py +++ b/smr/build-assembly-long.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import argparse +import copy from math import pi, isclose from pathlib import Path @@ -8,9 +9,10 @@ import numpy as np from tqdm import tqdm import openmc -from smr.materials import materials, clone +from smr.materials import materials, mats from smr.surfaces import surfs, lattice_pitch, pin_pitch, bottom_fuel_stack, \ - top_active_core, pellet_OR, active_fuel_length + top_active_core, pellet_OR, clad_OR, clad_IR, guide_tube_IR, guide_tube_OR, \ + active_fuel_length from smr.pins import pin_universes, make_stack @@ -18,18 +20,14 @@ from smr.pins import pin_universes, make_stack parser = argparse.ArgumentParser() parser.add_argument('--multipole', action='store_true', help='Use multipole cross sections') -parser.add_argument('--no-multipole', dest='multipole', action='store_false', +parser.add_argument('--no-multipole', action='store_false', help='Do not use multipole cross sections') -parser.add_argument('--clone', action='store_true', - help='Clone materials for each cell instance') -parser.add_argument('--no-clone', dest='clone', action='store_false', - help='Do not clone materials for each cell instance') parser.add_argument('-a', '--axial', type=int, default=100, help='Number of axial subdivisions in fuel') parser.add_argument('-d', '--depleted', action='store_true', help='Whether UO2 compositions should represent depleted fuel') parser.add_argument('-o', '--output-dir', type=Path, default=None) -parser.set_defaults(clone=False, multipole=True) +parser.set_defaults(multipole=True) args = parser.parse_args() # Make directory for inputs @@ -103,22 +101,11 @@ h = active_fuel_length / args.axial fuel_mats = {} -# Count the number of instances for each cell and material -if args.clone: - geometry.determine_paths(instances_only=True) - for cell in tqdm(geometry.get_all_material_cells().values(), - desc='Differentiating materials / assigning volume'): + desc='Assigning volume'): if cell.fill in materials: - # Determine if this material is fuel - is_fuel = 'UO2 Fuel' in cell.fill.name - - # Fill cell with list of "differentiated" materials if requested - if args.clone: - cell.fill = [clone(cell.fill) for i in range(cell.num_instances)] - # Determine volume of each fuel material - if is_fuel: + if 'UO2 Fuel' in cell.fill.name: upper_right = cell.region.bounding_box[1] if isclose(upper_right[0], rings[0]): ri, ro = 0.0, rings[0] @@ -126,25 +113,14 @@ for cell in tqdm(geometry.get_all_material_cells().values(), ri, ro = rings[0], rings[1] else: ri, ro = rings[1], pellet_OR - - if args.clone: - for mat in cell.fill: - mat.volume = pi * (ro*ro - ri*ri) * h + if ri not in fuel_mats: + cell.fill = cell.fill.clone() + cell.fill.volume = pi * (ro*ro - ri*ri) * h + fuel_mats[ri] = cell.fill else: - # In non-clone mode, we still need to create a copy of the - # material for each ring since they get different volumes - if ri not in fuel_mats: - cell.fill = cell.fill.clone() - cell.fill.volume = pi * (ro*ro - ri*ri) * h - fuel_mats[ri] = cell.fill - else: - cell.fill = fuel_mats[ri] + cell.fill = fuel_mats[ri] else: - if args.clone: - for mat in cell.fill: - mat.volume = 1.0 - else: - cell.fill.volume = 1.0 + cell.fill.volume = 1.0 #### Create OpenMC "materials.xml" file print('Getting materials...') @@ -174,13 +150,14 @@ settings.particles = 10000 settings.output = {'tallies': False, 'summary': False} settings.source = source settings.sourcepoint = {'write': False} -settings.temperature = { - 'default': 531.5, - 'method': 'interpolation', - 'range': (500.0, 1300.0) -} + if args.multipole: - settings.temperature['multipole'] = True - settings.temperature['tolerance'] = 1000 + settings.temperature = { + 'multipole': True, + 'tolerance': 1000, + 'default': 531.5, + 'method': 'interpolation', + 'range': (500.0, 1300.0) + } settings.export_to_xml(str(directory / 'settings.xml')) diff --git a/smr/build-assembly-short.py b/smr/build-assembly-short.py index dc13456..c06c67b 100644 --- a/smr/build-assembly-short.py +++ b/smr/build-assembly-short.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import argparse +import copy from math import pi, isclose from pathlib import Path @@ -8,7 +9,7 @@ import numpy as np from tqdm import tqdm import openmc -from smr.materials import materials, clone +from smr.materials import materials, mats from smr.surfaces import surfs, lattice_pitch, pin_pitch, bottom_fuel_stack, \ top_active_core, pellet_OR, clad_OR, clad_IR, guide_tube_IR, guide_tube_OR from smr.pins import pin_universes @@ -84,6 +85,14 @@ for halfspace in surfs['lat grid box inner']: # Define geometry with a single assembly geometry = openmc.Geometry(root_universe) + +def clone(material): + """Perform copy of material but share nuclide densities""" + shared_mat = copy.copy(material) + shared_mat.id = None + return shared_mat + + #### "Differentiate" the geometry if using distribmats h = 10.0*pin_pitch / args.axial if args.tallies == 'mat': diff --git a/smr/build-assembly.py b/smr/build-assembly.py index d4bc166..392b114 100644 --- a/smr/build-assembly.py +++ b/smr/build-assembly.py @@ -1,15 +1,17 @@ #!/usr/bin/env python3 import argparse +import copy from pathlib import Path import numpy as np from tqdm import tqdm import openmc -from smr.materials import materials, clone +from smr.materials import materials from smr.surfaces import surfs, lattice_pitch, bottom_fuel_stack, top_active_core, pellet_OR from smr.assemblies import assembly_universes +from smr.plots import assembly_plots from smr import inlet_temperature @@ -17,12 +19,8 @@ from smr import inlet_temperature parser = argparse.ArgumentParser() parser.add_argument('--multipole', action='store_true', help='Use multipole cross sections') -parser.add_argument('--no-multipole', dest='multipole', action='store_false', +parser.add_argument('--no-multipole', action='store_false', help='Do not use multipole cross sections') -parser.add_argument('--clone', action='store_true', - help='Clone materials for each cell instance') -parser.add_argument('--no-clone', dest='clone', action='store_false', - help='Do not clone materials for each cell instance') parser.add_argument('-t', '--tallies', choices=('cell', 'mat'), default='mat', help='Whether to use distribmats or distribcells for tallies') parser.add_argument('-r', '--rings', type=int, default=10, @@ -32,7 +30,7 @@ parser.add_argument('-a', '--axial', type=int, default=196, parser.add_argument('-d', '--depleted', action='store_true', help='Whether UO2 compositions should represent depleted fuel') parser.add_argument('-o', '--output-dir', type=Path, default=None) -parser.set_defaults(clone=False, multipole=True) +parser.set_defaults(multipole=True) args = parser.parse_args() # Make directory for inputs @@ -51,17 +49,25 @@ if args.rings > 1: else: ring_radii = None assembly = assembly_universes(ring_radii, args.axial, args.depleted) -lattice_sides = openmc.model.rectangular_prism(lattice_pitch, lattice_pitch, - boundary_type='reflective') +lattice_sides = openmc.model.get_rectangular_prism(lattice_pitch, lattice_pitch, + boundary_type='reflective') main_cell = openmc.Cell( - fill=assembly['Assembly (3.1%)'], + fill=assembly['Assembly (3.1%) 16BA'], region=lattice_sides & +surfs['lower bound'] & -surfs['upper bound'] ) root_univ = openmc.Universe(cells=[main_cell]) geometry = openmc.Geometry(root_univ) + +def clone(material): + """Perform copy of material but share nuclide densities""" + shared_mat = copy.copy(material) + shared_mat.id = None + return shared_mat + + #### "Differentiate" the geometry if using distribmats -if args.clone: +if args.tallies == 'mat': # Count the number of instances for each cell and material geometry.determine_paths(instances_only=True) @@ -101,7 +107,7 @@ settings.inactive = 100 settings.particles = 10000 settings.output = {'tallies': False, 'summary': False} settings.source = source -settings.sourcepoint = {'write': False} +settings.sourcepoint_write = False settings.temperature = { 'default': inlet_temperature, 'method': 'interpolation', @@ -143,3 +149,7 @@ elif args.tallies == 'mat': tallies.append(tally) tallies.export_to_xml(str(directory / 'tallies.xml')) + +# Create plots +plots = assembly_plots(main_cell.fill) +plots.export_to_xml(str(directory / 'plots.xml')) diff --git a/smr/build-core-fresh.py b/smr/build-core-fresh.py index 9747ae8..4265056 100644 --- a/smr/build-core-fresh.py +++ b/smr/build-core-fresh.py @@ -1,14 +1,17 @@ #!/usr/bin/env python3 +import os +import shutil +import copy import argparse from math import pi from pathlib import Path import numpy as np -import openmc -from tqdm import tqdm -from smr.materials import materials, clone +import openmc +from smr.materials import materials +from smr.plots import core_plots from smr.surfaces import lattice_pitch, bottom_fuel_stack, top_active_core, \ pellet_OR, active_fuel_length from smr.core import core_geometry @@ -19,12 +22,8 @@ from smr import inlet_temperature parser = argparse.ArgumentParser() parser.add_argument('--multipole', action='store_true', help='Use multipole cross sections') -parser.add_argument('--no-multipole', dest='multipole', action='store_false', +parser.add_argument('--no-multipole', action='store_false', help='Do not use multipole cross sections') -parser.add_argument('--clone', action='store_true', - help='Clone materials for each cell instance') -parser.add_argument('--no-clone', dest='clone', action='store_false', - help='Do not clone materials for each cell instance') parser.add_argument('-r', '--rings', type=int, default=10, help='Number of annular regions in fuel') parser.add_argument('-a', '--axial', type=int, default=196, @@ -32,7 +31,7 @@ parser.add_argument('-a', '--axial', type=int, default=196, parser.add_argument('-d', '--depleted', action='store_true', help='Whether UO2 compositions should represent depleted fuel') parser.add_argument('-o', '--output-dir', type=Path, default=None) -parser.set_defaults(clone=False, multipole=True) +parser.set_defaults(multipole=True) args = parser.parse_args() # Make directory for inputs @@ -54,36 +53,22 @@ geometry = core_geometry(ring_radii, args.axial, args.depleted) h = active_fuel_length / args.axial fuel_mats = {} -# Count the number of instances for each cell and material -if args.clone: - geometry.determine_paths(instances_only=True) - fuel_volume = pi * pellet_OR**2 * h / args.rings -for cell in tqdm(geometry.get_all_cells().values(), - desc='Differentiating materials / assigning volume'): +for cell in geometry.get_all_cells().values(): if cell.fill in materials: - # Determine if this material is fuel - name = cell.fill.name - is_fuel = 'UO2 Fuel' in name - # Determine volume of each fuel material - if is_fuel: - if args.clone: - # Fill cell with list of "differentiated" materials if requested - cell.fill = [clone(cell.fill) for i in range(cell.num_instances)] - for mat in cell.fill: - mat.volume = fuel_volume + if 'UO2 Fuel' in cell.fill.name: + r_o = cell.region.bounding_box[1][0] + if r_o not in fuel_mats: + cell.fill = cell.fill.clone() + cell.fill.volume = fuel_volume + fuel_mats[r_o] = cell.fill else: - r_o = cell.region.bounding_box[1][0] - if (name, r_o) not in fuel_mats: - cell.fill = cell.fill.clone() - cell.fill.volume = fuel_volume - fuel_mats[name, r_o] = cell.fill - else: - cell.fill = fuel_mats[name, r_o] + cell.fill = fuel_mats[r_o] else: cell.fill.volume = 1.0 + #### Create OpenMC "materials.xml" file all_materials = geometry.get_all_materials() materials = openmc.Materials(all_materials.values()) @@ -108,7 +93,8 @@ settings.inactive = 100 settings.particles = 10000 settings.output = {'tallies': False, 'summary': False} settings.source = source -settings.sourcepoint = {'write': False} +settings.sourcepoint_write = False + settings.temperature = { 'default': inlet_temperature, 'method': 'interpolation', @@ -119,3 +105,4 @@ if args.multipole: settings.temperature['tolerance'] = 1000 settings.export_to_xml(str(directory / 'settings.xml')) + diff --git a/smr/build-core-long.py b/smr/build-core-long.py deleted file mode 100644 index 4033d6e..0000000 --- a/smr/build-core-long.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -from math import pi, isclose -from pathlib import Path - -import openmc -from smr.materials import materials -from smr.surfaces import bottom_fuel_stack, top_active_core, \ - pellet_OR, pin_pitch, clad_IR, clad_OR, active_fuel_length -from smr.core import core_geometry -from smr import inlet_temperature -import smr.surfaces - -# Define command-line options -parser = argparse.ArgumentParser() -parser.add_argument('--multipole', action='store_true', - help='Use multipole cross sections') -parser.add_argument('--no-multipole', dest='multipole', action='store_false', - help='Do not use multipole cross sections') -parser.add_argument('-a', '--axial', type=int, default=100, - help='Number of axial subdivisions in fuel') -parser.add_argument('-d', '--depleted', action='store_true', - help='Whether UO2 compositions should represent depleted fuel') -parser.add_argument('-o', '--output-dir', type=Path, default=None) -parser.set_defaults(multipole=True) -args = parser.parse_args() - -# Make directory for inputs -if args.output_dir is None: - if args.depleted: - directory = Path('core-long-depleted') - else: - directory = Path('core-long-fresh') -else: - directory = args.output_dir -directory.mkdir(exist_ok=True) - -# Modify lattice pitch -smr.surfaces.lattice_pitch = lattice_pitch = 17*smr.surfaces.pin_pitch - -ring_radii = [0.1*pin_pitch, 0.2*pin_pitch] - -geometry = core_geometry(ring_radii, args.axial, args.depleted) - -h = active_fuel_length / args.axial -fuel_mats = {} - -for cell in geometry.get_all_cells().values(): - if cell.fill in materials: - # Determine volume of each fuel material - name = cell.fill.name - if 'UO2 Fuel' in name: - upper_right = cell.region.bounding_box[1][0] - if isclose(upper_right, ring_radii[0]): - ri, ro = 0.0, ring_radii[0] - elif isclose(upper_right, ring_radii[1]): - ri, ro = ring_radii[0], ring_radii[1] - else: - ri, ro = ring_radii[1], pellet_OR - if (name, ri) not in fuel_mats: - cell.fill = cell.fill.clone() - cell.fill.volume = pi * (ro*ro - ri*ri) * h - fuel_mats[name, ri] = cell.fill - else: - cell.fill = fuel_mats[name, ri] - elif name == 'Helium': - cell.fill.volume = pi * (clad_IR**2 - pellet_OR**2) * h - elif name == 'M5': - # Clad is not subdivided - cell.fill.volume = pi * (clad_OR**2 - clad_IR**2) * active_fuel_length - else: - cell.fill.volume = 1.0 - - -#### Create OpenMC "materials.xml" file -all_materials = geometry.get_all_materials() -materials = openmc.Materials(all_materials.values()) -materials.export_to_xml(str(directory / 'materials.xml')) - -#### Create OpenMC "geometry.xml" file -geometry.export_to_xml(str(directory / 'geometry.xml')) - - -#### Create OpenMC "settings.xml" file - -# Construct uniform initial source distribution over fissionable zones -lower_left = [-7.*lattice_pitch/2., -7.*lattice_pitch/2., bottom_fuel_stack] -upper_right = [+7.*lattice_pitch/2., +7.*lattice_pitch/2., top_active_core] -source = openmc.source.Source(space=openmc.stats.Box(lower_left, upper_right)) -source.space.only_fissionable = True - -settings = openmc.Settings() -settings.batches = 200 -settings.inactive = 100 -settings.particles = 20_000_000 -settings.output = {'tallies': False, 'summary': False} -settings.source = source -settings.sourcepoint = {'write': False} -settings.temperature = { - 'default': inlet_temperature, - 'method': 'interpolation', - 'range': (300.0, 1500.0), -} -if args.multipole: - settings.temperature['multipole'] = True - settings.temperature['tolerance'] = 1000 - -settings.export_to_xml(str(directory / 'settings.xml')) - -# Check assembly power distribution -core_lattice = geometry.get_cells_by_fill_name('Main core')[0].fill -mesh = openmc.RegularMesh.from_rect_lattice(core_lattice) -assembly_power = openmc.Tally() -assembly_power.filters = [openmc.MeshFilter(mesh)] -assembly_power.scores = ['nu-fission'] -tallies = openmc.Tallies([assembly_power]) -tallies.export_to_xml(directory / 'tallies.xml') diff --git a/smr/build-core-short.py b/smr/build-core-short.py index 2f5339d..317a8a0 100644 --- a/smr/build-core-short.py +++ b/smr/build-core-short.py @@ -1,18 +1,25 @@ #!/usr/bin/env python3 +import os +import shutil +import copy import argparse from math import pi, isclose from pathlib import Path +import numpy as np + import openmc from smr.materials import materials -from smr.surfaces import bottom_fuel_stack, top_active_core, \ +from smr.plots import core_plots +from smr.surfaces import lattice_pitch, bottom_fuel_stack, top_active_core, \ pellet_OR, surfs, pin_pitch, clad_IR, clad_OR import smr.surfaces import smr.pins from smr.core import core_geometry from smr import inlet_temperature + # Define command-line options parser = argparse.ArgumentParser() parser.add_argument('--multipole', action='store_true', @@ -37,9 +44,6 @@ else: directory = args.output_dir directory.mkdir(exist_ok=True) -# Modify lattice pitch -smr.surfaces.lattice_pitch = lattice_pitch = 17*smr.surfaces.pin_pitch - # Modify fuel length length = 3. * pin_pitch smr.surfaces.active_fuel_length = length @@ -109,7 +113,8 @@ settings.inactive = 100 settings.particles = 10000 settings.output = {'tallies': False, 'summary': False} settings.source = source -settings.sourcepoint = {'write': False} +settings.sourcepoint_write = False + settings.temperature = { 'default': inlet_temperature, 'method': 'interpolation', diff --git a/smr/milestones.md b/smr/milestones.md deleted file mode 100644 index bb89864..0000000 --- a/smr/milestones.md +++ /dev/null @@ -1,20 +0,0 @@ -# Milestone Models - -- **AD-SE-08-61, Coupled Multiphysics Driver Implementation** --- This milestone - used the singlerod short/long problems. Generating the model was done with the - script `tests/singlerod/make_openmc_model.py` from the ENRICO repository - (there is a `--short` command line option to generate the short version) - -- **AD-SE-08-66, Coupled Assembly Analysis** --- This milestone used the - assembly short and long (v2) problems. Generating the model was done with - `smr/build-assembly-long.py -a 100 --clone` on the ecp-benchmarks repository - (git commit `631fefe`, after pull request #14). In these models, materials are - fully differentiated across each fuel ring/axial segment. - -- **AD-SE-08-73, Full core coupled-physics simulation** --- This milestone used - the core-short and core-long (90 layer) models. Generating the models was done - with `smr/build-core-short.py` and `smr/build-assembly-long.py -a 90` on the - ecp-benchmarks repository (git commit `c7b89db`, after pull request #16). In - these models, materials are not differentiated and no grid spacers are - present. The lattice pitch is modified to be exactly 17 times the pin pitch - (slightly different than NuScale specification). diff --git a/smr/smr/core.py b/smr/smr/core.py index 81763fe..68f3275 100644 --- a/smr/smr/core.py +++ b/smr/smr/core.py @@ -5,9 +5,9 @@ import numpy as np import openmc from .materials import mats +from .surfaces import surfs, lattice_pitch from .reflector import reflector_universes from .assemblies import assembly_universes -from smr import surfaces def core_geometry(ring_radii, num_axial, depleted): @@ -34,7 +34,6 @@ def core_geometry(ring_radii, num_axial, depleted): # Construct main core lattice core = openmc.RectLattice(name='Main core') - lattice_pitch = surfaces.lattice_pitch core.lower_left = (-9*lattice_pitch/2, -9*lattice_pitch/2) core.pitch = (lattice_pitch, lattice_pitch) universes = np.tile(reflector['solid'], (9, 9)) @@ -120,7 +119,6 @@ def core_geometry(ring_radii, num_axial, depleted): core.universes = universes root_univ = openmc.Universe(universe_id=0, name='root universe') - surfs = surfaces.surfs # Cylinder filled with core lattice cell = openmc.Cell(name='Main core') diff --git a/smr/smr/materials.py b/smr/smr/materials.py index ee6c754..72a39d6 100644 --- a/smr/smr/materials.py +++ b/smr/smr/materials.py @@ -1,7 +1,5 @@ """Instantiate the OpenMC Materials needed by the core model.""" -import copy - import openmc from openmc.data import atomic_weight, atomic_mass, water_density @@ -254,10 +252,3 @@ mats['UO2 3.1 depleted'] = mat # Construct a collection of Materials to export to XML materials = openmc.Materials(mats.values()) - - -def clone(material): - """Perform copy of material but share nuclide densities""" - shared_mat = copy.copy(material) - shared_mat.id = None - return shared_mat diff --git a/smr/smr/reflector.py b/smr/smr/reflector.py index 74aa4bd..ed143cc 100644 --- a/smr/smr/reflector.py +++ b/smr/smr/reflector.py @@ -12,7 +12,7 @@ converted to actual dimensions by scaling according to the width of an assembly. import openmc from .materials import mats -from smr import surfaces +from .surfaces import lattice_pitch def make_reflector(name, parameters): @@ -91,7 +91,6 @@ def reflector_universes(): # All pixel widths are scaled according to the actual width of an assembly # divided by the width of an assembly in pixels - lattice_pitch = surfaces.lattice_pitch scale = lattice_pitch/width # Physical positions diff --git a/smr/smr/surfaces.py b/smr/smr/surfaces.py index b454c61..d91ec92 100644 --- a/smr/smr/surfaces.py +++ b/smr/smr/surfaces.py @@ -69,7 +69,8 @@ spacer_height = 1.750*INCHES # ML17013A274, Figure 4.2-7 # assembly parameters assembly_length = 95.89*INCHES # ML17013A274, Table 4.1-2 pin_pitch = 0.496*INCHES # ML17013A274, Table 4.1-2 -lattice_pitch = 8.466*INCHES # ML17013A274, Table 4.1-2 +#lattice_pitch = 8.466*INCHES # ML17013A274, Table 4.1-2 +lattice_pitch = 17*pin_pitch grid_strap_side = 21.47270 top_nozzle_height = 3.551*INCHES # ML17013A274, Figure 4.2-2 top_nozzle_width = 8.406*INCHES # ML17013A274, Figure 4.2-2