From 2a24952857e29e6f218fb2c048b94937c86a0614 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 24 Jul 2019 22:55:08 -0500 Subject: [PATCH] Add script to build long assembly --- smr/build-assembly-long.py | 203 +++++++++++++++++++++++++++++++++++++ smr/smr/surfaces.py | 6 +- 2 files changed, 205 insertions(+), 4 deletions(-) create mode 100644 smr/build-assembly-long.py diff --git a/smr/build-assembly-long.py b/smr/build-assembly-long.py new file mode 100644 index 0000000..75373b9 --- /dev/null +++ b/smr/build-assembly-long.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python3 + +import argparse +import copy +from math import pi, isclose +from pathlib import Path + +import numpy as np +from tqdm import tqdm +import openmc + +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, \ + active_fuel_length +from smr.pins import pin_universes, make_stack + + +# Define command-line options +parser = argparse.ArgumentParser() +parser.add_argument('-m', '--multipole', action='store_true', + help='Whether to use multipole cross sections') +parser.add_argument('-t', '--tallies', choices=('cell', 'mat'), default='mat', + help='Whether to use distribmats or distribcells for tallies') +parser.add_argument('-a', '--axial', type=int, default=92, + 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) +args = parser.parse_args() + +# Make directory for inputs +if args.output_dir is None: + if args.depleted: + directory = Path('assembly-long-depleted') + else: + directory = Path('assembly-long-fresh') +else: + directory = args.output_dir +directory.mkdir(exist_ok=True) + +rings = [0.1*pin_pitch, 0.2*pin_pitch] + +assembly_long_surfs = [ + surfs['bottom FR'], + surfs['bot active core'], + surfs['top active core'], + surfs['top pin plenum'], + surfs['top FR'], + surfs['bot upper nozzle'], + surfs['top upper nozzle'] +] + +univs = pin_universes(rings, args.axial, args.depleted) +fuel_univ = make_stack( + 'Fuel (3.1%) stack no grid', + surfaces=assembly_long_surfs, + universes=[ + univs['water pin'], + univs['end plug'], + univs['Fuel pin (3.1%) no grid'], + univs['pin plenum'], + univs['end plug'], + univs['water pin'] + ] +) + +# Define the NumPy array indices for assembly locations where there +# may be CR guide tubes, instrument tubes and burnable absorbers +nonfuel_y = np.array([2,2,2,3,3,5,5,5,5,5,8,8,8,8,8,11,11,11,11,11,13,13,14,14,14]) +nonfuel_x = np.array([5,8,11,3,13,2,5,8,11,14,2,5,8,11,14,2,5,8,11,14,3,13,5,8,11]) + +universes = np.full((17,17), fuel_univ) +universes[nonfuel_y, nonfuel_x] = univs['GT empty'] + +# Instantiate the lattice +lattice = openmc.RectLattice(name='Pin lattice') +lattice.lower_left = (-17.*pin_pitch/2., -17.*pin_pitch/2.) +lattice.pitch = (pin_pitch, pin_pitch) +lattice.universes = universes + +# Add lattice to bounding cell +root_universe = openmc.Universe(name='Root universe') +cell = openmc.Cell(name='Lattice cell') +cell.fill = lattice +z_bounds = +surfs['bottom FR'] & -surfs['top FR'] +cell.region = surfs['lat grid box inner'] & z_bounds +root_universe.add_cell(cell) + +# Apply reflective boundaries on sides and vacuum on bottom/top +surfs['bottom FR'].boundary_type = 'vacuum' +surfs['top FR'].boundary_type = 'vacuum' +for halfspace in surfs['lat grid box inner']: + halfspace.surface.boundary_type = 'reflective' + +# 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 = active_fuel_length / args.axial +if args.tallies == 'mat': + # Count the number of instances for each cell and material + geometry.determine_paths(instances_only=True) + + for cell in tqdm(geometry.get_all_material_cells().values(), + desc='Differentiating materials'): + if cell.fill in materials: + # Fill cell with list of "differentiated" materials + cell.fill = [clone(cell.fill) for i in range(cell.num_instances)] + + # Determine volume of each fuel material + if 'UO2 Fuel' in cell.fill[0].name: + upper_right = cell.region.bounding_box[1] + if isclose(upper_right[0], rings[0]): + ri, ro = 0.0, rings[0] + elif isclose(upper_right[0], rings[1]): + ri, ro = rings[0], rings[1] + else: + ri, ro = rings[1], pellet_OR + for mat in cell.fill: + mat.volume = pi * (ro*ro - ri*ri) * h + else: + for mat in cell.fill: + mat.volume = 1.0 + +#### Create OpenMC "materials.xml" file +print('Getting materials...') +all_materials = geometry.get_all_materials() +print('Creating materials collection...') +materials = openmc.Materials(all_materials.values()) +print('Exporting materials to XML...') +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 = (-lattice_pitch/2, -lattice_pitch/2, bottom_fuel_stack) +upper_right = (lattice_pitch/2, 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 = 10000 +settings.output = {'tallies': False, 'summary': False} +settings.source = source +settings.sourcepoint = {'write': False} + +if args.multipole: + settings.temperature = { + 'multipole': True, + 'tolerance': 1000, + 'default': 531.5, + 'method': 'interpolation', + 'range': (500.0, 1300.0) + } + +settings.export_to_xml(str(directory / 'settings.xml')) + + +#### Create OpenMC "tallies.xml" file +tallies = openmc.Tallies() + +# Extract all fuel materials +materials = geometry.get_materials_by_name(name='Fuel', matching=False) + +# If using distribcells, create distribcell tally needed for depletion +if args.tallies == 'cell': + # Extract all cells filled by a fuel material + fuel_cells = [] + for cell in geometry.get_all_cells().values(): + if cell.fill in materials: + tally = openmc.Tally(name='depletion tally') + tally.scores = ['(n,p)', '(n,a)', '(n,gamma)', + 'fission', '(n,2n)', '(n,3n)', '(n,4n)'] + tally.nuclides = cell.fill.get_nuclides() + tally.filters.append(openmc.DistribcellFilter([cell])) + tallies.append(tally) + +# If using distribmats, create material tally needed for depletion +elif args.tallies == 'mat': + tally = openmc.Tally(name='depletion tally') + tally.scores = ['(n,p)', '(n,a)', '(n,gamma)', + 'fission', '(n,2n)', '(n,3n)', '(n,4n)'] + tally.nuclides = materials[0].get_nuclides() + tally.filters = [openmc.MaterialFilter(materials)] + tallies.append(tally) + +tallies.export_to_xml(str(directory / 'tallies.xml')) diff --git a/smr/smr/surfaces.py b/smr/smr/surfaces.py index 96d9678..0adf0d8 100644 --- a/smr/smr/surfaces.py +++ b/smr/smr/surfaces.py @@ -37,7 +37,7 @@ pellet_OR = 0.3195*INCHES/2 # ML17013A274, Table 4.1-2 pellet_length = 0.4*INCHES # ML17013A274, Table 4.1-2 clad_IR = 0.326*INCHES/2 # ML17013A274, Table 4.1-2 clad_OR = 0.374*INCHES/2 # ML17013A274, Table 4.1-2 -#active_fuel_length = 78.74*INCHES # ML17013A274, Figure 4.2-10 +active_fuel_length = 78.74*INCHES # ML17013A274, Figure 4.2-10 plenum_length = 5.311*INCHES # ML17013A274, Figure 4.2-10 fuel_rod_length = 85.00*INCHES # ML17013A274, Table 4.1-2 lower_end_cap_length = 0.575*INCHES # ML17007A001, Table 3-2 @@ -75,8 +75,6 @@ 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 -active_fuel_length = 10.0*pin_pitch - # core radial parameters core_barrel_IR = 74*INCHES/2 # ML17013A274, Table 4.1-2 core_barrel_OR = 78*INCHES/2 # ML17013A274, Table 4.1-2 @@ -85,7 +83,7 @@ rpv_IR = 96.5*INCHES/2 # ML17013A274, Table 5.3-1 rpv_OR = 105*INCHES/2 # ML17013A274, Table 5.3-1 # axial parameters -reference_z = -36.007 +reference_z = -36.6205 lowest_extent = reference_z bottom_support_plate = lowest_extent + 20.000 top_support_plate = bottom_support_plate + 5.000