316 lines
13 KiB
Python
316 lines
13 KiB
Python
""" builder.py
|
|
|
|
Provides a main class that facilitates construction and output of any piece of
|
|
the BEAVRS geometry.
|
|
|
|
"""
|
|
|
|
import warnings
|
|
|
|
import openmc
|
|
|
|
import beavrs.constants as c
|
|
from beavrs.materials import openmc_materials
|
|
from beavrs.pincells import Pincells
|
|
from beavrs.assemblies import Assemblies
|
|
from beavrs.baffle import Baffle
|
|
from beavrs.core import Core
|
|
from beavrs.univzero import UniverseZero
|
|
from beavrs.plots import Plots
|
|
|
|
# Suppress DeprecationWarnings from OpenMC's Cell.add_surface(...) method
|
|
warnings.simplefilter('once', DeprecationWarning)
|
|
|
|
class BEAVRS(object):
|
|
""" Main BEAVRS class"""
|
|
|
|
def __init__(self, boron_ppm=c.nominalBoronPPM, is_symmetric=False, is_2d=False):
|
|
""" We build the entire geometry in memory in the constructor """
|
|
|
|
# TODO: make the control rod bank insertion heights attributes
|
|
|
|
# Setup the materials
|
|
self.mats = openmc_materials(ppm=boron_ppm)
|
|
|
|
self.pincells = Pincells(self.mats)
|
|
self.assemblies = Assemblies(self.pincells, self.mats)
|
|
self.baffle = Baffle(self.assemblies, self.mats)
|
|
self.core = Core(self.pincells, self.assemblies, self.baffle, is_symmetric=is_symmetric)
|
|
self.main_universe = UniverseZero(self.core, self.mats, is_2d=is_2d)
|
|
|
|
self.openmc_geometry = openmc.Geometry(self.main_universe)
|
|
|
|
self.settings_batches = 10
|
|
self.settings_inactive = 5
|
|
self.settings_particles = 1000
|
|
baffle_flat = 7.5*c.latticePitch
|
|
if is_2d:
|
|
self.settings_sourcebox = [-baffle_flat, -baffle_flat, c.struct_LowestExtent_2d,
|
|
baffle_flat, baffle_flat, c.struct_HighestExtent_2d]
|
|
else:
|
|
self.settings_sourcebox = [-baffle_flat, -baffle_flat, c.fuel_Rod_bot,
|
|
baffle_flat, baffle_flat, c.fuel_Rod_top]
|
|
self.settings_output_tallies = False
|
|
self.settings_summary = True
|
|
self.settings_output_distribmats = False
|
|
|
|
self.tally_meshes = []
|
|
self.tallies = []
|
|
|
|
self.dd_mesh_dimension = None
|
|
self.dd_mesh_lower_left = None
|
|
self.dd_mesh_upper_right = None
|
|
self.dd_nodemap = None
|
|
self.dd_truncate = False
|
|
self.dd_interactions = False
|
|
|
|
# These were not chosen with any application in mind: tailor them to your needs!
|
|
self.depletion_nuclides = [
|
|
'Ag107', 'Ag109', 'Ag111', 'Am241', 'Am243', 'As75',
|
|
'Au197', 'Ba134', 'Ba135', 'Ba136', 'Ba137', 'Ba138',
|
|
'Ba140', 'Br79', 'Br81', 'Cd108', 'Cd110', 'Cd111',
|
|
'Cd112', 'Cd113', 'Cd114', 'Cd116', 'Ce140',
|
|
'Ce141', 'Ce142', 'Ce143', 'Ce144', 'Cm244', 'Co59',
|
|
'Cs133', 'Cs134', 'Cs135', 'Cs136', 'Cs137', 'Dy160',
|
|
'Dy161', 'Dy162', 'Dy163', 'Dy164', 'Er166', 'Er167',
|
|
'Eu151', 'Eu152', 'Eu153', 'Eu154', 'Eu155', 'Eu156',
|
|
'Eu157', 'Gd154', 'Gd155', 'Gd156', 'Gd157', 'Gd158',
|
|
'Gd160', 'Ge72', 'Ge73', 'Ge74', 'Ge76', 'Ho165',
|
|
'I127', 'I129', 'I130', 'I131', 'I135', 'In113',
|
|
'In115', 'Kr80', 'Kr82', 'Kr83', 'Kr84', 'Kr85',
|
|
'Kr86', 'La139', 'La140', 'Lu175', 'Lu176', 'Mo100',
|
|
'Mo94', 'Mo95', 'Mo96', 'Mo97', 'Mo98', 'Mo99',
|
|
'Nb93', 'Nb94', 'Nb95', 'Nd142', 'Nd143', 'Nd144',
|
|
'Nd145', 'Nd146', 'Nd147', 'Nd148', 'Nd150', 'Np237',
|
|
'Pa233', 'Pd104', 'Pd105', 'Pd106', 'Pd107', 'Pd108',
|
|
'Pd110', 'Pm147', 'Pm148', 'Pm149', 'Pm151', 'Pr141',
|
|
'Pr142', 'Pr143', 'Pu238', 'Pu239', 'Pu240', 'Pu241',
|
|
'Pu242', 'Rb85', 'Rb86', 'Rb87', 'Re185', 'Re187',
|
|
'Rh103', 'Rh105', 'Ru100', 'Ru101', 'Ru102', 'Ru103',
|
|
'Ru104', 'Ru105', 'Ru106', 'Ru99', 'Sb121', 'Sb123',
|
|
'Sb124', 'Sb125', 'Sb126', 'Se76', 'Se77', 'Se78',
|
|
'Se80', 'Se82', 'Sm147', 'Sm148', 'Sm149', 'Sm150',
|
|
'Sm151', 'Sm152', 'Sm153', 'Sm154', 'Sn115', 'Sn116',
|
|
'Sn117', 'Sn118', 'Sn119', 'Sn120', 'Sn122', 'Sn123',
|
|
'Sn124', 'Sn125', 'Sn126', 'Sr86', 'Sr87', 'Sr88',
|
|
'Sr89', 'Sr90', 'Ta181', 'Tb159', 'Tb160', 'Tc99',
|
|
'Te122', 'Te123', 'Te124', 'Te125', 'Te126',
|
|
'Te128', 'Te130', 'Te132', 'Th232', 'U233',
|
|
'U234', 'U235', 'U236', 'U238', 'W182', 'W183',
|
|
'W184', 'W186', 'Xe128', 'Xe129', 'Xe130', 'Xe131',
|
|
'Xe132', 'Xe133', 'Xe134', 'Xe135', 'Xe136', 'Y89',
|
|
'Y90', 'Y91', 'Zr90', 'Zr91', 'Zr92', 'Zr93', 'Zr94',
|
|
'Zr95', 'Zr96']
|
|
nuclides = []
|
|
for nuc in self.depletion_nuclides:
|
|
nuclides.append(openmc.Nuclide(nuc))
|
|
self.depletion_nuclides = nuclides
|
|
|
|
# The OpenMC model container to be populated.
|
|
self.model = openmc.Model()
|
|
|
|
def set_boron_ppm(self, ppm):
|
|
self.mats = openmc_materials(ppm=ppm)
|
|
|
|
def write_openmc_geometry(self):
|
|
self.openmc_geometry.export_to_xml()
|
|
|
|
def write_openmc_materials(self):
|
|
materials_file = openmc.Materials(self.mats.values())
|
|
materials_file.export_to_xml()
|
|
|
|
def get_openmc_plots(self):
|
|
self.plots = Plots(self.mats)
|
|
return openmc.Plots(self.plots.plots)
|
|
|
|
def write_openmc_plots(self):
|
|
self.plots = Plots(self.mats)
|
|
self.get_openmc_plots().export_to_xml()
|
|
|
|
def get_openmc_settings_file(self):
|
|
settings_file = openmc.Settings()
|
|
settings_file.batches = self.settings_batches
|
|
settings_file.inactive = self.settings_inactive
|
|
settings_file.particles = self.settings_particles
|
|
if self.dd_mesh_dimension:
|
|
settings_file.dd_mesh_dimension = self.dd_mesh_dimension
|
|
settings_file.dd_mesh_lower_left = self.dd_mesh_lower_left
|
|
settings_file.dd_mesh_upper_right = self.dd_mesh_upper_right
|
|
if self.dd_nodemap:
|
|
settings_file.dd_nodemap = self.dd_nodemap
|
|
settings_file.dd_allow_leakage = self.dd_truncate
|
|
settings_file.dd_count_interactions = self.dd_interactions
|
|
settings_file.source = openmc.IndependentSource(space=openmc.stats.Box(
|
|
self.settings_sourcebox[:3], self.settings_sourcebox[3:]))
|
|
output = {'tallies': self.settings_output_tallies,
|
|
'summary': self.settings_summary}
|
|
settings_file.output = output
|
|
return settings_file
|
|
|
|
def write_openmc_settings(self):
|
|
self.get_openmc_settings_file().export_to_xml()
|
|
|
|
def get_openmc_tallies_file(self):
|
|
if len(self.tallies) == 0: return
|
|
tallies_file = openmc.Tallies()
|
|
for mesh in self.tally_meshes:
|
|
tallies_file.add_mesh(mesh)
|
|
for tally in self.tallies:
|
|
tallies_file.append(tally)
|
|
return tallies_file
|
|
|
|
def write_openmc_tallies(self):
|
|
if len(self.tallies) == 0: return
|
|
self.get_openmc_tallies_file().export_to_xml()
|
|
|
|
def build_openmc_model(self):
|
|
self.model = openmc.Model()
|
|
self.model.geometry = self.openmc_geometry
|
|
self.model.materials = openmc.Materials(self.mats.values())
|
|
self.model.settings = self.get_openmc_settings_file()
|
|
if len(self.tallies) != 0:
|
|
self.model.tallies = self.get_openmc_tallies_file()
|
|
self.model.plots = self.get_openmc_plots()
|
|
return self.model
|
|
|
|
def write_openmc_model(self):
|
|
self.build_openmc_model().export_to_model_xml()
|
|
|
|
def set_params(self, particles=None, batches=None, inactive=None):
|
|
if particles: self.settings_particles = particles
|
|
if batches: self.settings_batches = batches
|
|
if inactive: self.settings_inactive = inactive
|
|
|
|
def set_dd_mesh(self, nx, ny, nz, nodemap=None):
|
|
""" Sets a DD mesh that covers the whole geometry
|
|
"""
|
|
self.dd_mesh_dimension = [nx, ny, nz]
|
|
self.dd_mesh_lower_left = [-c.rpvOR, -c.rpvOR, c.struct_LowestExtent]
|
|
self.dd_mesh_upper_right = [c.rpvOR, c.rpvOR, c.struct_HighestExtent]
|
|
self.dd_nodemap = nodemap
|
|
self.dd_truncate = False
|
|
|
|
def set_dd_mesh_restricted(self, nx, ny, nz, nodemap=None):
|
|
""" Sets a DD mesh that truncates tracking outside the shield panels
|
|
"""
|
|
self.dd_mesh_dimension = [nx, ny, nz]
|
|
self.dd_mesh_lower_left = [-c.neutronShieldOR, -c.neutronShieldOR, c.struct_LowestExtent]
|
|
self.dd_mesh_upper_right = [c.neutronShieldOR, c.neutronShieldOR, c.struct_HighestExtent]
|
|
self.dd_nodemap = nodemap
|
|
self.dd_truncate = True
|
|
|
|
def set_dd_mesh_assembly_restricted(self, nz, nodemap=None):
|
|
""" Sets a restricted DD mesh that is assembly-sized radially, and user-specified axially
|
|
"""
|
|
lp = c.latticePitch
|
|
self.dd_mesh_dimension = [17, 17, nz]
|
|
self.dd_mesh_lower_left = [-8*lp, -8*lp, c.struct_LowestExtent]
|
|
self.dd_mesh_upper_right = [8*lp, 8*lp, c.struct_HighestExtent]
|
|
self.dd_nodemap = nodemap
|
|
self.dd_truncate = True
|
|
|
|
def set_dd_mesh_3assembly_restricted(self, nz, nodemap=None):
|
|
""" Sets a restricted DD mesh that is 3x3 assebly-sized radially, and user-specified axially
|
|
"""
|
|
lp = c.latticePitch
|
|
self.dd_mesh_dimension = [7, 7, nz]
|
|
self.dd_mesh_lower_left = [-10*lp, -10*lp, c.struct_LowestExtent]
|
|
self.dd_mesh_upper_right = [10*lp, 10*lp, c.struct_HighestExtent]
|
|
self.dd_nodemap = nodemap
|
|
self.dd_truncate = True
|
|
|
|
def reset_dd(self):
|
|
""" Undoes set_dd_mesh and set_dd_mesh_restricted
|
|
"""
|
|
self.dd_mesh_dimension = None
|
|
self.dd_mesh_lower_left = None
|
|
self.dd_mesh_upper_right = None
|
|
self.dd_nodemap = None
|
|
self.dd_truncate = False
|
|
|
|
def set_distributed_materials(self):
|
|
""" Sets depletable regions to distributed materials
|
|
|
|
"""
|
|
|
|
# Distribute fuel materials
|
|
for enr in self.pincells.enrichments:
|
|
self.openmc_mats['Fuel %s' % enr].set_as_distrib_comp()
|
|
|
|
# TODO: distribute other regions as necessary (bpra pins, rcca pins, etc)
|
|
|
|
def set_fuel_otf_files(self, files):
|
|
""" Sets OTF material files for the specified material names
|
|
|
|
files should be a list of file paths equal to the number of enrichments
|
|
|
|
"""
|
|
for enr,f in zip(self.pincells.enrichments, files):
|
|
if f:
|
|
self.openmc_mats['Fuel %s' % enr].set_otf_mat_file(f)
|
|
|
|
def reset_fuel_otf_files(self):
|
|
""" Undoes set_fuel_otf_files
|
|
"""
|
|
for enr in self.pincells.enrichments:
|
|
self.openmc_mats['Fuel %s' % enr].set_otf_mat_file(None)
|
|
|
|
def get_all_fuel_cells(self):
|
|
cells = []
|
|
universes = []
|
|
for enr in self.pincells.enrichments[:3]:
|
|
fuel_pin = self.pincells.u_fuel_active_pin[enr]
|
|
name = "Fuel rod active region - {0} enr radial 0: Fuel {0}".format(enr)
|
|
cells.append(fuel_pin.get_cell_by_name(name))
|
|
universes.append(fuel_pin)
|
|
return zip(cells, universes)
|
|
|
|
def set_fuel_depletion_tallies(self):
|
|
""" Sets up distributed tallies needed for depletion of fresh fuel
|
|
|
|
NOTE: this was so far created only for benchmarking openmc performance,
|
|
and is not really what we'll end up doing for depletion
|
|
|
|
"""
|
|
scores = ["total", "absorption", "fission", "nu-fission"]
|
|
|
|
for c,u in self.get_all_fuel_cells():
|
|
|
|
# Instantiate tally Filter
|
|
filter_ = openmc.DistribcellFilter(c.id)
|
|
|
|
# Instantiate the Tally
|
|
tally = openmc.Tally()
|
|
tally.filters = [filter_]
|
|
tally.scores = scores
|
|
nuclides = self.openmc_mats[c.fill.name].nuclides
|
|
tally.nuclides = [nuc for nuc, ao, typ in nuclides]
|
|
self.tallies.append(tally)
|
|
|
|
def set_fuel_distrib_tallies(self, scores):
|
|
""" Sets up distribcell tallies on fuel pins
|
|
|
|
NOTE: currently this only sets them up for the 1.6, 2.4, and 3.1 pins
|
|
|
|
"""
|
|
|
|
for c,u in self.get_all_fuel_cells():
|
|
|
|
# Instantiate tally Filter
|
|
filter_ = openmc.DistribcellFilter(c._id)
|
|
|
|
# Instantiate the Tally
|
|
tally = openmc.Tally()
|
|
tally.filters = [filter_]
|
|
tally.scores = [scores]
|
|
self.tallies.append(tally)
|
|
|
|
def set_full_depletion_mats(self):
|
|
""" Adds 207 depletion nuclides to fuel materials
|
|
"""
|
|
|
|
for name,mat in self.mats.items():
|
|
if not name in ['Fuel 1.6%', 'Fuel 2.4%', 'Fuel 3.1%', 'Fuel 3.2%', 'Fuel 3.4%']: continue
|
|
for nuc in self.depletion_nuclides:
|
|
if not nuc._name in mat._nuclides:
|
|
mat.add_nuclide(nuc, 1e-14)
|