updated beavrs scripts of openmc to generate 2D and/or symmetric models
This commit is contained in:
parent
66caecf98b
commit
39af932296
9 changed files with 267 additions and 187 deletions
|
|
@ -35,5 +35,27 @@ model.write_openmc_settings()
|
|||
```
|
||||
|
||||
For convenience, also provided are a script `make_beavrs.py` to build the model
|
||||
and a Jupyter notebook `extract-assm.ipynb` to extract a single assembly of BEAVRS.
|
||||
and Jupyter notebooks `extract-assm.ipynb` and `extract-pin.ipynb` to extract
|
||||
single assemblies and pin cells of BEAVRS respectively.
|
||||
|
||||
For those looking to generate the input files for the 2D BEAVRS model and/or a model
|
||||
that is symmetric in the x-y plane, the `-d` and `-s` flags can be used with the
|
||||
`make_beavrs.py` script respectively. Details about these two models can be found
|
||||
below:
|
||||
|
||||
2D BEAVRS Model
|
||||
---------------
|
||||
|
||||
The 2D BEAVRS model is created from the 3D model by creating a bounding box around the
|
||||
full core to restrict the problem to one assembly-width outside the outer-most assemblies
|
||||
(i.e. 17 assemblies wide) in the x-y direction, and a 10cm slice in the z-direction.
|
||||
The x-y surfaces have vacuum boundary conditions, while the 10cm slice is arbitrarily
|
||||
chosen in a non-grid spacer region with reflective boundary conditions. The initial source
|
||||
distribution is also updated accordingly.
|
||||
|
||||
Symmetric BEAVRS Model in x-y Plane
|
||||
-----------------------------------
|
||||
|
||||
The only source of asymmetry in the x-y plane stems from the insertion of instrument tubes,
|
||||
so the symmetric model replaces all instrument tubes with empty guide tubes, similar to all
|
||||
other assemblies.
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class Assemblies(object):
|
|||
|
||||
def __init__(self, pincells, mats):
|
||||
""" Creates BEAVRS pincell universes """
|
||||
|
||||
|
||||
self.pins = pincells
|
||||
self.mats = mats
|
||||
|
||||
|
|
@ -63,12 +63,12 @@ class Assemblies(object):
|
|||
|
||||
def _add_bpra_layouts(self):
|
||||
""" Adds BEAVRS BPRA layouts """
|
||||
|
||||
|
||||
self.ba_specs = []
|
||||
|
||||
|
||||
u_gt = self.pins.u_gt
|
||||
u_bp = self.pins.u_bp
|
||||
|
||||
|
||||
self.ba_specs.append(('no BAs',{
|
||||
'a': u_gt, 'b': u_gt, 'c': u_gt,
|
||||
'd': u_gt, 'e': u_gt,
|
||||
|
|
@ -169,7 +169,7 @@ class Assemblies(object):
|
|||
'u': u_gt, 'v': u_gt,
|
||||
'w': u_gt, 'x': u_gt, 'y': u_gt,
|
||||
}))
|
||||
|
||||
|
||||
self.ba_specs.append(('15SW',{
|
||||
'a': u_gt, 'b': u_gt, 'c': u_gt,
|
||||
'd': u_gt, 'e': u_gt,
|
||||
|
|
@ -179,7 +179,7 @@ class Assemblies(object):
|
|||
'u': u_bp, 'v': u_gt,
|
||||
'w': u_bp, 'x': u_bp, 'y': u_bp,
|
||||
}))
|
||||
|
||||
|
||||
self.ba_specs.append(('15SE',{
|
||||
'a': u_gt, 'b': u_gt, 'c': u_gt,
|
||||
'd': u_gt, 'e': u_gt,
|
||||
|
|
@ -215,7 +215,7 @@ class Assemblies(object):
|
|||
""" Adds BEAVRS RCCA layouts """
|
||||
|
||||
self.cr_specs = []
|
||||
for bank_label, u_b in self.pins.u_rcca.items():
|
||||
for bank_label, u_b in sorted(self.pins.u_rcca.items()):
|
||||
self.cr_specs.append(("RCCA {0}".format(bank_label),{
|
||||
'a': u_b, 'b': u_b, 'c': u_b,
|
||||
'd': u_b, 'e': u_b,
|
||||
|
|
@ -229,24 +229,24 @@ class Assemblies(object):
|
|||
|
||||
def _add_all_assembly_combinations(self):
|
||||
""" Adds all BEAVRS assembly layouts
|
||||
|
||||
|
||||
Here we make every possible combination of enrichments, control and shutdown
|
||||
banks, with or without instrument tubes.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
|
||||
types_fuel = self.pins.enrichments
|
||||
types_gt = self.ba_specs + self.cr_specs
|
||||
types_instr = [(self.pins.u_it_nt,'no instr'), (self.pins.u_it,'instr')]
|
||||
|
||||
|
||||
self.u_fuel = {}
|
||||
self.u_fuel_no_sleeve = {}
|
||||
for enr in types_fuel:
|
||||
|
||||
|
||||
self.u_fuel[enr] = {}
|
||||
self.u_fuel_no_sleeve[enr] = {}
|
||||
for gt_label, gt_spec in types_gt:
|
||||
|
||||
|
||||
self.u_fuel[enr][gt_label] = {}
|
||||
self.u_fuel_no_sleeve[enr][gt_label] = {}
|
||||
for u_c, center_label in types_instr:
|
||||
|
|
@ -262,14 +262,14 @@ class Assemblies(object):
|
|||
lattice.setPosition('fuel', self.pins.u_fuel_p[enr])
|
||||
lattice.updatePositions(gt_spec)
|
||||
lattice.finalize()
|
||||
|
||||
|
||||
# Wrap the lattice with the grid sleeve universe
|
||||
u_lattice = InfinitePinCell(name='{0} universe'.format(name))
|
||||
u_lattice.add_ring(lattice, self.lattice_surfs, box=True)
|
||||
u_lattice.add_last_ring(self.pins.u_gridsleeve)
|
||||
self.u_fuel[enr][gt_label][center_label] = u_lattice
|
||||
u_lattice.finalize()
|
||||
|
||||
|
||||
# Store the lattice without the gridsleeve
|
||||
u_latticePins = InfinitePinCell(name='{0} pins'.format(name))
|
||||
u_latticePins.add_ring(lattice, self.lattice_surfs, box=True)
|
||||
|
|
|
|||
|
|
@ -18,14 +18,13 @@ 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):
|
||||
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
|
||||
|
|
@ -36,8 +35,8 @@ class BEAVRS(object):
|
|||
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)
|
||||
self.main_universe = UniverseZero(self.core, 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)
|
||||
|
||||
|
|
@ -45,8 +44,12 @@ class BEAVRS(object):
|
|||
self.settings_inactive = 5
|
||||
self.settings_particles = 1000
|
||||
baffle_flat = 7.5*c.latticePitch
|
||||
self.settings_sourcebox = [-baffle_flat, -baffle_flat, c.fuel_Rod_bot,
|
||||
baffle_flat, baffle_flat, c.fuel_Rod_top]
|
||||
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
|
||||
|
|
|
|||
|
|
@ -23,14 +23,14 @@ rcca_bank_steps_withdrawn = {
|
|||
rcca_banks = rcca_bank_steps_withdrawn.keys()
|
||||
|
||||
## pincell parameters
|
||||
pelletOR = 0.39218 #
|
||||
cladIR = 0.40005 #
|
||||
cladOR = 0.45720 #
|
||||
pelletOR = 0.39218 #
|
||||
cladIR = 0.40005 #
|
||||
cladOR = 0.45720 #
|
||||
rodGridSide_tb = 1.22030 #
|
||||
rodGridSide_i = 1.22098 #
|
||||
guideTubeIR = 0.56134 #
|
||||
guideTubeOR = 0.60198 #
|
||||
guideTubeDashIR = 0.50419 #
|
||||
guideTubeIR = 0.56134 #
|
||||
guideTubeOR = 0.60198 #
|
||||
guideTubeDashIR = 0.50419 #
|
||||
guideTubeDashOR = 0.54610 #
|
||||
rcca_clad_OR = 0.48387 # ML033530020 page 15
|
||||
rcca_clad_IR = 0.38608 # ML033530020 page 15
|
||||
|
|
@ -38,104 +38,106 @@ rcca_b4c_OR = 0.37338 # ML033530020 page 15
|
|||
rcca_aic_OR = 0.38227 # ML033530020 page 15
|
||||
rcca_spacer_OR = 0.37845 # ML033530020 page 15
|
||||
rcca_spring_OR = 0.06459 # Assumed same as fuel
|
||||
burnabs1 = 0.21400 #
|
||||
burnabs2 = 0.23051 #
|
||||
burnabs3 = 0.24130 #
|
||||
burnabs4 = 0.42672 #
|
||||
burnabs5 = 0.43688 #
|
||||
burnabs6 = 0.48387 #
|
||||
instrTubeIR = 0.43688 # no source
|
||||
instrTubeOR = 0.48387 # no source
|
||||
burnabs1 = 0.21400 #
|
||||
burnabs2 = 0.23051 #
|
||||
burnabs3 = 0.24130 #
|
||||
burnabs4 = 0.42672 #
|
||||
burnabs5 = 0.43688 #
|
||||
burnabs6 = 0.48387 #
|
||||
instrTubeIR = 0.43688 # no source
|
||||
instrTubeOR = 0.48387 # no source
|
||||
plenumSpringOR = 0.06459 # frapcon source - see beavrs_spring.ods
|
||||
|
||||
## lattice parameters
|
||||
pinPitch = 1.25984 #
|
||||
pinPitch = 1.25984 #
|
||||
latticePitch = 21.50364 #
|
||||
gridstrapSide = 21.49595 #
|
||||
gridstrapSide = 21.49595 #
|
||||
|
||||
## <!--- axials copied straight from rod_axials.ods
|
||||
|
||||
# Core Structures
|
||||
struct_LowestExtent = 0.00000 #
|
||||
struct_SupportPlate_bot = 20.0000 # arbitrary amount of water below core
|
||||
struct_SupportPlate_top = 25.0000 # guessed
|
||||
struct_LowerNozzle_bot = 25.0000 # same as struct_SupportPlate_top
|
||||
struct_LowerNozzle_top = 35.0000 # approx from ** NDR of 4.088in for lower nozzle height
|
||||
struct_UpperNozzle_bot = 423.049 # fig 4.2-3 from Watts Bar Unit 2 safety analysis report
|
||||
struct_UpperNozzle_top = 431.876 # fig 4.2-3 from Watts Bar Unit 2 safety analysis report
|
||||
struct_HighestExtent = 460.000 # arbitrary amount of water above core
|
||||
|
||||
# Fuel Rod
|
||||
fuel_Rod_bot = 35.0000 # same as struct_LowerNozzle_top
|
||||
fuel_LowerFitting_bot = 35.0000 # ML033530020 Fig 2-7
|
||||
fuel_LowerFitting_top = 36.7480 # ML033530020 Fig 2-7
|
||||
fuel_ActiveFuel_bot = 36.7480 # ML033530020 Fig 2-7
|
||||
fuel_ActiveFuel_top = 402.508 # ML033530020 Fig 2-7
|
||||
fuel_Plenum_bot = 402.508 # ML033530020 Fig 2-7
|
||||
fuel_Plenum_top = 417.164 # ML033530020 Fig 2-7
|
||||
fuel_UpperFitting_bot = 417.164 # ML033530020 Fig 2-7
|
||||
fuel_UpperFitting_top = 419.704 # ML033530020 Fig 2-7
|
||||
fuel_Rod_top = 419.704 # ML033530020 Fig 2-7
|
||||
|
||||
# RCCAs (0 steps withdraw, i.e., fully inserted)
|
||||
rcca_Rod_bot = 39.9580 # ML033530020 Fig 2-8
|
||||
rcca_LowerFitting_bot = 39.9580 # ML033530020 Fig 2-8
|
||||
rcca_LowerFitting_top = 41.8280 # ML033530020 Fig 2-8
|
||||
rcca_AIC_bot = 41.8280 # ML033530020 Fig 2-8
|
||||
rcca_AIC_top = 143.428 # ML033530020 Fig 2-8
|
||||
rcca_B4C_bot = 143.428 # ML033530020 Fig 2-8
|
||||
rcca_B4C_top = 402.508 # ML033530020 Fig 2-8
|
||||
rcca_Spacer_bot = 402.508 # ML033530020 Fig 2-8
|
||||
rcca_Spacer_top = 403.778 # ML033530020 Fig 2-8
|
||||
rcca_Plenum_bot = 403.778 # ML033530020 Fig 2-8
|
||||
rcca_Plenum_top = 415.558 # ML033530020 Fig 2-8
|
||||
rcca_Rod_top = 460.000 # same as struct_HighestExtent
|
||||
rcca_StepWidth = 1.58193 # Active height divided into 228 steps
|
||||
|
||||
# BPRAs
|
||||
bpra_Rod_bot = 38.6600 # ML033530020 Fig 2-9
|
||||
bpra_LowerFitting_bot = 38.6600 # ML033530020 Fig 2-9
|
||||
bpra_LowerFitting_top = 40.5580 # ML033530020 Fig 2-9
|
||||
bpra_Active_bot = 40.5580 # ML033530020 Fig 2-9
|
||||
bpra_Active_top = 401.238 # ML033530020 Fig 2-9
|
||||
bpra_Plenum_bot = 401.238 # ML033530020 Fig 2-9
|
||||
bpra_Plenum_top = 421.532 # ML033530020 Fig 2-9
|
||||
bpra_Rod_top = 431.876 # same as struct_UpperNozzle_top
|
||||
|
||||
# Guide Tube
|
||||
gt_Rod_bot = 35.0000 # same as fuel_Rod_bot
|
||||
gt_Dashpot_bot = 35.0000 #
|
||||
gt_Dashpot_top = 39.9580 # same as rcca_Rod_bot for full insertion
|
||||
gt_Upper_bot = 39.9580 #
|
||||
gt_Upper_top = 423.049 # same as struct_UpperNozzle_bot
|
||||
gt_Rod_top = 423.049 #
|
||||
|
||||
# Intrument Tube
|
||||
instr_Rod_bot = 35.0000 # same as fuel_Rod_bot
|
||||
instr_Rod_top = 423.049 # same as struct_UpperNozzle_bot
|
||||
|
||||
# Grid Spacers (centers provided by utiliy) (heights 1.65 top/bottom, 2.25 intermediate)
|
||||
grid1_bot = 37.1621 #
|
||||
grid1_top = 40.5200 #
|
||||
grid2_bot = 98.0250 #
|
||||
grid2_top = 103.740 #
|
||||
grid3_bot = 150.222 #
|
||||
grid3_top = 155.937 #
|
||||
grid4_bot = 202.419 #
|
||||
grid4_top = 208.134 #
|
||||
grid5_bot = 254.616 #
|
||||
grid5_top = 260.331 #
|
||||
grid6_bot = 306.813 #
|
||||
grid6_top = 312.528 #
|
||||
grid7_bot = 359.010 #
|
||||
grid7_top = 364.725 #
|
||||
grid8_bot = 411.806 #
|
||||
grid8_top = 415.164 #
|
||||
# Core Structures
|
||||
struct_LowestExtent = 0.00000 #
|
||||
struct_SupportPlate_bot = 20.0000 # arbitrary amount of water below core
|
||||
struct_SupportPlate_top = 25.0000 # guessed
|
||||
struct_LowerNozzle_bot = 25.0000 # same as struct_SupportPlate_top
|
||||
struct_LowerNozzle_top = 35.0000 # approx from ** NDR of 4.088in for lower nozzle height
|
||||
struct_LowestExtent_2d = 220.000 # arbitrary lower slice for 2D problem
|
||||
struct_HighestExtent_2d = 230.000 # arbitrary upper slice for 2D problem
|
||||
struct_UpperNozzle_bot = 423.049 # fig 4.2-3 from Watts Bar Unit 2 safety analysis report
|
||||
struct_UpperNozzle_top = 431.876 # fig 4.2-3 from Watts Bar Unit 2 safety analysis report
|
||||
struct_HighestExtent = 460.000 # arbitrary amount of water above core
|
||||
|
||||
# Fuel Rod
|
||||
fuel_Rod_bot = 35.0000 # same as struct_LowerNozzle_top
|
||||
fuel_LowerFitting_bot = 35.0000 # ML033530020 Fig 2-7
|
||||
fuel_LowerFitting_top = 36.7480 # ML033530020 Fig 2-7
|
||||
fuel_ActiveFuel_bot = 36.7480 # ML033530020 Fig 2-7
|
||||
fuel_ActiveFuel_top = 402.508 # ML033530020 Fig 2-7
|
||||
fuel_Plenum_bot = 402.508 # ML033530020 Fig 2-7
|
||||
fuel_Plenum_top = 417.164 # ML033530020 Fig 2-7
|
||||
fuel_UpperFitting_bot = 417.164 # ML033530020 Fig 2-7
|
||||
fuel_UpperFitting_top = 419.704 # ML033530020 Fig 2-7
|
||||
fuel_Rod_top = 419.704 # ML033530020 Fig 2-7
|
||||
|
||||
# RCCAs (0 steps withdraw, i.e., fully inserted)
|
||||
rcca_Rod_bot = 39.9580 # ML033530020 Fig 2-8
|
||||
rcca_LowerFitting_bot = 39.9580 # ML033530020 Fig 2-8
|
||||
rcca_LowerFitting_top = 41.8280 # ML033530020 Fig 2-8
|
||||
rcca_AIC_bot = 41.8280 # ML033530020 Fig 2-8
|
||||
rcca_AIC_top = 143.428 # ML033530020 Fig 2-8
|
||||
rcca_B4C_bot = 143.428 # ML033530020 Fig 2-8
|
||||
rcca_B4C_top = 402.508 # ML033530020 Fig 2-8
|
||||
rcca_Spacer_bot = 402.508 # ML033530020 Fig 2-8
|
||||
rcca_Spacer_top = 403.778 # ML033530020 Fig 2-8
|
||||
rcca_Plenum_bot = 403.778 # ML033530020 Fig 2-8
|
||||
rcca_Plenum_top = 415.558 # ML033530020 Fig 2-8
|
||||
rcca_Rod_top = 460.000 # same as struct_HighestExtent
|
||||
rcca_StepWidth = 1.58193 # Active height divided into 228 steps
|
||||
|
||||
# BPRAs
|
||||
bpra_Rod_bot = 38.6600 # ML033530020 Fig 2-9
|
||||
bpra_LowerFitting_bot = 38.6600 # ML033530020 Fig 2-9
|
||||
bpra_LowerFitting_top = 40.5580 # ML033530020 Fig 2-9
|
||||
bpra_Active_bot = 40.5580 # ML033530020 Fig 2-9
|
||||
bpra_Active_top = 401.238 # ML033530020 Fig 2-9
|
||||
bpra_Plenum_bot = 401.238 # ML033530020 Fig 2-9
|
||||
bpra_Plenum_top = 421.532 # ML033530020 Fig 2-9
|
||||
bpra_Rod_top = 431.876 # same as struct_UpperNozzle_top
|
||||
|
||||
# Guide Tube
|
||||
gt_Rod_bot = 35.0000 # same as fuel_Rod_bot
|
||||
gt_Dashpot_bot = 35.0000 #
|
||||
gt_Dashpot_top = 39.9580 # same as rcca_Rod_bot for full insertion
|
||||
gt_Upper_bot = 39.9580 #
|
||||
gt_Upper_top = 423.049 # same as struct_UpperNozzle_bot
|
||||
gt_Rod_top = 423.049 #
|
||||
|
||||
# Intrument Tube
|
||||
instr_Rod_bot = 35.0000 # same as fuel_Rod_bot
|
||||
instr_Rod_top = 423.049 # same as struct_UpperNozzle_bot
|
||||
|
||||
# Grid Spacers (centers provided by utiliy) (heights 1.65 top/bottom, 2.25 intermediate)
|
||||
grid1_bot = 37.1621 #
|
||||
grid1_top = 40.5200 #
|
||||
grid2_bot = 98.0250 #
|
||||
grid2_top = 103.740 #
|
||||
grid3_bot = 150.222 #
|
||||
grid3_top = 155.937 #
|
||||
grid4_bot = 202.419 #
|
||||
grid4_top = 208.134 #
|
||||
grid5_bot = 254.616 #
|
||||
grid5_top = 260.331 #
|
||||
grid6_bot = 306.813 #
|
||||
grid6_top = 312.528 #
|
||||
grid7_bot = 359.010 #
|
||||
grid7_top = 364.725 #
|
||||
grid8_bot = 411.806 #
|
||||
grid8_top = 415.164 #
|
||||
|
||||
# data for nozzle and support plate region
|
||||
n_nozzle_pins = 264 # number of pins
|
||||
# volume fractions from ML033530020 (table 2-3)
|
||||
nozzle_water_vol_frac = 0.8280 # water volume fraction
|
||||
nozzle_water_vol_frac = 0.8280 # water volume fraction
|
||||
nozzle_ss_vol_frac = 0.1720 # stainless steel volume fraction
|
||||
|
||||
## -- axials copied straight from rod_axials.ods -->
|
||||
|
|
|
|||
|
|
@ -57,22 +57,23 @@ class Core(object):
|
|||
'J___1', 'J___2', 'J___3', 'J___8', 'J___9', 'K__15', 'K__14', 'K__13',
|
||||
'K__12', 'K__11', 'K__10']
|
||||
|
||||
def __init__(self, pincells, assemblies, baffle):
|
||||
def __init__(self, pincells, assemblies, baffle, is_symmetric=False):
|
||||
""" Creates BEAVRS core lattice universe """
|
||||
|
||||
|
||||
self.pins = pincells
|
||||
self.assem = assemblies
|
||||
self.baffle = baffle
|
||||
|
||||
self.is_symmetric = is_symmetric
|
||||
|
||||
self._set_enrichment_positions()
|
||||
self._set_instrument_tube_positions()
|
||||
self._set_bpra_positions()
|
||||
self._set_rcca_positions()
|
||||
self._make_core_universe()
|
||||
|
||||
|
||||
def _set_enrichment_positions(self):
|
||||
""" Sets the specification for BEAVRS assembly enrichment positions """
|
||||
|
||||
|
||||
self.enr_positions ={
|
||||
'L___1': '3.1%', 'K___1': '3.1%', 'J___1': '3.1%', 'H___1': '3.1%', 'G___1': '3.1%', 'F___1': '3.1%', 'E___1': '3.1%',
|
||||
'N___2': '3.1%', 'M___2': '3.1%', 'L___2': '3.1%', 'K___2': '1.6%', 'J___2': '3.1%', 'H___2': '1.6%', 'G___2': '3.1%', 'F___2': '1.6%', 'E___2': '3.1%', 'D___2': '3.1%', 'C___2': '3.1%',
|
||||
|
|
@ -116,7 +117,7 @@ class Core(object):
|
|||
|
||||
def _set_bpra_positions(self):
|
||||
""" Sets the specification for BEAVRS BPRA positions """
|
||||
|
||||
|
||||
self.ba_positions = {
|
||||
'K___1': '6S', 'H___1': '6S', 'F___1': '6S',
|
||||
'L___2': '16', 'J___2': '20', 'G___2': '20', 'E___2': '16',
|
||||
|
|
@ -137,7 +138,7 @@ class Core(object):
|
|||
|
||||
def _set_rcca_positions(self):
|
||||
""" Sets the specification for BEAVRS RCCA positions """
|
||||
|
||||
|
||||
self.rcca_positions = {
|
||||
'M___2': 'SA', 'K___2': 'B', 'H___2': 'C', 'F___2': 'B', 'D___2': 'SA',
|
||||
'L___3': 'SD', 'J___3': 'SB', 'G___3': 'SB', 'E___3': 'SC',
|
||||
|
|
@ -172,12 +173,15 @@ class Core(object):
|
|||
ba = "RCCA {0}".format(self.rcca_positions[pos])
|
||||
else:
|
||||
ba = 'no BAs'
|
||||
if pos in self.instr_positions:
|
||||
instr = 'instr'
|
||||
else:
|
||||
if self.is_symmetric:
|
||||
instr = 'no instr'
|
||||
else:
|
||||
if pos in self.instr_positions:
|
||||
instr = 'instr'
|
||||
else:
|
||||
instr = 'no instr'
|
||||
lattice.setPosition(pos, self.assem.u_fuel[enr][ba][instr])
|
||||
lattice.finalize()
|
||||
|
||||
|
||||
self.u_coreLattice = lattice
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class TemplatedLattice(openmc.RectLattice):
|
|||
|
||||
def setTemplate(self, template):
|
||||
""" Set the lattice template
|
||||
|
||||
|
||||
:param template:
|
||||
"""
|
||||
self.template = template
|
||||
|
|
@ -26,13 +26,13 @@ class TemplatedLattice(openmc.RectLattice):
|
|||
def setPosition(self, key, univ):
|
||||
"""Set an individual position in the lattice"""
|
||||
self.positions[key] = univ
|
||||
|
||||
|
||||
def updatePositions(self, univs):
|
||||
"""Update multiple positions in the lattice with a dictionary"""
|
||||
self.positions.update(univs)
|
||||
|
||||
def finalize(self):
|
||||
|
||||
|
||||
if self.template == []:
|
||||
raise Exception("No template set for:\n{0}".format(self))
|
||||
|
||||
|
|
@ -54,14 +54,14 @@ _created_cells = {}
|
|||
|
||||
class InfinitePinCell(openmc.Universe):
|
||||
""" Class for creating a simple pincell universe infinite in the z direction
|
||||
|
||||
|
||||
InfinitePinCells consist of a set of radii and materials that define rings.
|
||||
|
||||
|
||||
This class provides an easy way to wrap a pincell inside additional rings, or
|
||||
a square grid around the outside.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
""" Create a new InfinitePinCell
|
||||
"""
|
||||
|
|
@ -74,19 +74,19 @@ class InfinitePinCell(openmc.Universe):
|
|||
|
||||
def add_ring(self, fill, surf, box=False, rot=None):
|
||||
""" Adds a ring to the pincell
|
||||
|
||||
|
||||
Pincells must be built from the inside out. Materials for new rings are from
|
||||
the surface of the previous ring to the provided new surface.
|
||||
|
||||
|
||||
If the ring we want to add is a box, surf should be a rectangular prism.
|
||||
|
||||
It's up to the user to check for overlapping cell definitions.
|
||||
|
||||
|
||||
:param fill: material or filling universe for new ring
|
||||
:param surf: outer surface of new ring (or a rectangular region)
|
||||
:param box: whether or not we're adding a boxy ring (e.g. for grids)
|
||||
:param rot: openmc rotation string for filled cells
|
||||
|
||||
|
||||
"""
|
||||
self.radii.append(surf)
|
||||
self.box.append(box)
|
||||
|
|
@ -195,12 +195,12 @@ class InfinitePinCell(openmc.Universe):
|
|||
|
||||
class AxialPinCell(openmc.Universe):
|
||||
""" Class for containing a complete axial description of a pincell
|
||||
|
||||
|
||||
AxialPinCells consist of a set of InfinitePincells and the axial planes that
|
||||
define the axial boundaries of each. They also allow for a fully-constructed
|
||||
pincell to be "wrapped" by another pincell, e.g., pincells containing grids or
|
||||
guide tubes.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
|
@ -214,11 +214,11 @@ class AxialPinCell(openmc.Universe):
|
|||
|
||||
def add_axial_section(self, axial_plane, pincell):
|
||||
""" Adds an axial section to the stack
|
||||
|
||||
|
||||
Stacks must be built from the bottom-up. Each new section goes from the
|
||||
previous axial_plane to the given axial_plane (or from infinity to the
|
||||
given plane if it's the first section added).
|
||||
|
||||
|
||||
It's up to the user to ensure that all planes are z-planes, and that
|
||||
sections are added in the correct order.
|
||||
|
||||
|
|
@ -235,9 +235,9 @@ class AxialPinCell(openmc.Universe):
|
|||
|
||||
def add_last_axial_section(self, pincell):
|
||||
""" Adds the last axial section to the top of the stack
|
||||
|
||||
|
||||
:param pincell: InfinitePincell or material that goes to infinity at the top
|
||||
|
||||
|
||||
"""
|
||||
self.pincells.append(pincell)
|
||||
if isinstance(pincell, InfinitePinCell):
|
||||
|
|
@ -265,7 +265,7 @@ class AxialPinCell(openmc.Universe):
|
|||
|
||||
def add_wrapper(self, wrapper, surf=None):
|
||||
""" Adds a pincell to wrap the height, returning a new InfinitePinCell
|
||||
|
||||
|
||||
This should only be called AFTER all axial sections are added.
|
||||
|
||||
If the splitting surface is not given, this function will use the outer-most
|
||||
|
|
@ -306,7 +306,7 @@ class AxialPinCell(openmc.Universe):
|
|||
|
||||
# Fill the outermost ring with the wrapping universe
|
||||
new_pin.add_last_ring(wrapper)
|
||||
|
||||
|
||||
_created_cells[new_name] = new_pin
|
||||
|
||||
return new_pin
|
||||
|
|
@ -321,7 +321,7 @@ class AxialPinCell(openmc.Universe):
|
|||
for pin in self.pincells:
|
||||
if isinstance(pin, InfinitePinCell) and not pin.finalized:
|
||||
pin.finalize()
|
||||
|
||||
|
||||
# Instantiate the axial cells
|
||||
for i, (pin, plane) in enumerate(zip(self.pincells, self.axials)):
|
||||
|
||||
|
|
@ -342,8 +342,8 @@ class AxialPinCell(openmc.Universe):
|
|||
label = "{0} axial top: {1}".format(self.name, self.pincells[-1].name)
|
||||
cell = openmc.Cell(name=label, fill=self.pincells[-1])
|
||||
cell.region = +self.axials[-1]
|
||||
|
||||
|
||||
self.add_cell(cell)
|
||||
|
||||
|
||||
self.finalized = True
|
||||
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ class Pincells(object):
|
|||
self.u_gridsleeve.add_axial_section(self.s_struct_upperNozzle_top, self.mats['Water SPN'])
|
||||
self.u_gridsleeve.add_last_axial_section(self.mats['Borated Water'])
|
||||
self.u_gridsleeve.finalize()
|
||||
|
||||
|
||||
def _add_fuel_pincells(self):
|
||||
""" Adds BEAVRS fuel pincells """
|
||||
|
||||
|
|
@ -173,7 +173,7 @@ class Pincells(object):
|
|||
self.s_fuel_upperFitting_top = openmc.ZPlane(name='Fuel upper fitting top', z0=c.fuel_UpperFitting_top)
|
||||
|
||||
# Fuel pincell universes
|
||||
|
||||
|
||||
self.u_fuel_active_pin = {}
|
||||
for enr in self.enrichments:
|
||||
self.u_fuel_active_pin[enr] = InfinitePinCell(name='Fuel rod active region - {0} enr'.format(enr))
|
||||
|
|
@ -207,7 +207,7 @@ class Pincells(object):
|
|||
|
||||
def _add_guide_tube_pincells(self):
|
||||
""" Adds BEAVRS guide tube pincells """
|
||||
|
||||
|
||||
# GT radial surfaces
|
||||
|
||||
self.s_gt_IR = openmc.ZCylinder(name='Guide tube IR', R=c.guideTubeIR)
|
||||
|
|
@ -346,7 +346,7 @@ class Pincells(object):
|
|||
|
||||
def _add_rcca_pincells(self):
|
||||
""" Adds BEAVRS RCCA pincells """
|
||||
|
||||
|
||||
# RCCA rod radial surfaces
|
||||
|
||||
self.s_rcca_clad_IR = openmc.ZCylinder(name='RCCA rod clad IR', R=c.rcca_clad_IR)
|
||||
|
|
@ -364,7 +364,7 @@ class Pincells(object):
|
|||
self.s_rcca_b4c_top = {}
|
||||
self.s_rcca_spacer_top = {}
|
||||
self.s_rcca_plenum_top = {}
|
||||
for b in c.rcca_banks:
|
||||
for b in sorted(c.rcca_banks):
|
||||
d = c.rcca_bank_steps_withdrawn[b]*c.rcca_StepWidth
|
||||
self.s_rcca_rod_bot[b] = openmc.ZPlane(name='Bottom of RCCA rod bank {0}'.format(b), z0=c.rcca_Rod_bot + d)
|
||||
self.s_rcca_lowerFitting_top[b] = openmc.ZPlane(name='Top of RCCA rod lower fitting bank {0}'.format(b), z0=c.rcca_LowerFitting_top + d)
|
||||
|
|
@ -386,23 +386,23 @@ class Pincells(object):
|
|||
self.u_rcca_aic.add_ring(self.mats['Air'], self.s_rcca_clad_IR)
|
||||
self.u_rcca_aic.add_last_ring( self.mats['Zircaloy 4'])
|
||||
self.u_rcca_aic.finalize()
|
||||
|
||||
|
||||
self.u_rcca_b4c = InfinitePinCell(name='RCCA B4C')
|
||||
self.u_rcca_b4c.add_ring(self.mats['B4C'], self.s_rcca_b4c_OR)
|
||||
self.u_rcca_b4c.add_ring(self.mats['Air'], self.s_rcca_clad_IR)
|
||||
self.u_rcca_b4c.add_last_ring(self.mats['Zircaloy 4'])
|
||||
self.u_rcca_b4c.finalize()
|
||||
|
||||
|
||||
self.u_rcca_spacer = InfinitePinCell(name='RCCA Spacer')
|
||||
self.u_rcca_spacer.add_ring(self.mats['SS304'], self.s_rcca_spacer_OR)
|
||||
self.u_rcca_spacer.add_ring(self.mats['Air'], self.s_rcca_clad_IR)
|
||||
self.u_rcca_spacer.add_last_ring(self.mats['Zircaloy 4'])
|
||||
self.u_rcca_spacer.finalize()
|
||||
|
||||
|
||||
# RCCA rod axial stack
|
||||
|
||||
self.u_rcca = {}
|
||||
for b in c.rcca_banks:
|
||||
for b in sorted(c.rcca_banks):
|
||||
self.u_rcca[b] = AxialPinCell(name='RCCA bank {0}'.format(b))
|
||||
self.u_rcca[b].add_axial_section(self.s_struct_supportPlate_bot, self.mats['Borated Water'])
|
||||
self.u_rcca[b].add_axial_section(self.s_struct_lowerNozzle_top, self.mats['Water SPN'])
|
||||
|
|
|
|||
|
|
@ -10,14 +10,14 @@ import openmc
|
|||
|
||||
class UniverseZero(openmc.Universe):
|
||||
|
||||
def __init__(self, core, mats):
|
||||
def __init__(self, core, mats, is_2d=False):
|
||||
""" Creates BEAVRS main universe """
|
||||
|
||||
super(UniverseZero, self).__init__(name='Main BEAVRS universe', universe_id=0)
|
||||
|
||||
|
||||
self.core = core
|
||||
self.mats = mats
|
||||
|
||||
self.is_2d = is_2d
|
||||
|
||||
self._add_outer_rings()
|
||||
self._add_shield_panels()
|
||||
self._add_core_barrel()
|
||||
|
|
@ -27,26 +27,31 @@ class UniverseZero(openmc.Universe):
|
|||
def _add_outer_rings(self):
|
||||
""" Adds BEAVRS RPV, liner, and downcomer """
|
||||
|
||||
self.s_upperBound = openmc.ZPlane(name='Highest Extent', z0=c.struct_HighestExtent, boundary_type='vacuum')
|
||||
self.s_lowerBound = openmc.ZPlane(name='Lowest Extent', z0=c.struct_LowestExtent, boundary_type='vacuum')
|
||||
# Change z-dimension based on whether 2D or 3D problem
|
||||
if self.is_2d:
|
||||
self.s_upperBound = openmc.ZPlane(name='Highest Extent', z0=c.struct_HighestExtent_2d, boundary_type='reflective')
|
||||
self.s_lowerBound = openmc.ZPlane(name='Lowest Extent', z0=c.struct_LowestExtent_2d, boundary_type='reflective')
|
||||
else:
|
||||
self.s_upperBound = openmc.ZPlane(name='Highest Extent', z0=c.struct_HighestExtent, boundary_type='vacuum')
|
||||
self.s_lowerBound = openmc.ZPlane(name='Lowest Extent', z0=c.struct_LowestExtent, boundary_type='vacuum')
|
||||
|
||||
# RPV
|
||||
|
||||
|
||||
self.s_RPVOR = openmc.ZCylinder(name='RPV OR', R=c.rpvOR, boundary_type='vacuum')
|
||||
self.s_RPVIR = openmc.ZCylinder(name='RPV IR', R=c.rpvIR)
|
||||
self.c_RPV = openmc.Cell(name="RPV", fill=self.mats['Carbon Steel'])
|
||||
self.c_RPV.region = (-self.s_RPVOR & +self.s_RPVIR &
|
||||
-self.s_upperBound & +self.s_lowerBound)
|
||||
|
||||
|
||||
# RPV liner
|
||||
|
||||
|
||||
self.s_linerIR = openmc.ZCylinder(name='RPV Liner IR', R=c.linerIR)
|
||||
self.c_liner = openmc.Cell(name="RPV Liner", fill=self.mats['SS304'])
|
||||
self.c_liner.region = (-self.s_RPVIR & +self.s_linerIR &
|
||||
-self.s_upperBound & +self.s_lowerBound)
|
||||
|
||||
|
||||
# Downcomer
|
||||
|
||||
|
||||
self.s_neutronShieldOR = openmc.ZCylinder(name='Shield Panel OR', R=c.neutronShieldOR)
|
||||
self.c_downcomer = openmc.Cell(name="Downcomer", fill=self.mats['Borated Water'])
|
||||
self.c_downcomer.region = (-self.s_linerIR & +self.s_neutronShieldOR &
|
||||
|
|
@ -62,33 +67,33 @@ class UniverseZero(openmc.Universe):
|
|||
self.s_ns_NWtop_SEbot = openmc.Plane(name='Shield Panel NWtop/SEbot', **c.neutronShield_NWtop_SEbot)
|
||||
self.s_ns_NEbot_SWtop = openmc.Plane(name='Shield Panel NEbot/SWtop', **c.neutronShield_NEbot_SWtop)
|
||||
self.s_ns_NEtop_SWbot = openmc.Plane(name='Shield Panel NEtop/SWbot', **c.neutronShield_NEtop_SWbot)
|
||||
|
||||
|
||||
self.c_shieldPanels = []
|
||||
|
||||
|
||||
self.c_sp_NW = openmc.Cell(name='NW Shield Panel', fill=self.mats['SS304'])
|
||||
self.c_sp_NW.region = (+self.s_neutronShieldIR & -self.s_neutronShieldOR &
|
||||
+self.s_ns_NWbot_SEtop & -self.s_ns_NWtop_SEbot &
|
||||
-self.s_upperBound & +self.s_lowerBound)
|
||||
self.c_shieldPanels.append(self.c_sp_NW)
|
||||
|
||||
|
||||
self.c_sp_SE = openmc.Cell(name='SE Shield Panel', fill=self.mats['SS304'])
|
||||
self.c_sp_SE.region = (+self.s_neutronShieldIR & -self.s_neutronShieldOR &
|
||||
-self.s_ns_NWbot_SEtop & +self.s_ns_NWtop_SEbot &
|
||||
-self.s_upperBound & +self.s_lowerBound)
|
||||
self.c_shieldPanels.append(self.c_sp_SE)
|
||||
|
||||
|
||||
self.c_sp_NE = openmc.Cell(name='NE Shield Panel', fill=self.mats['SS304'])
|
||||
self.c_sp_NE.region = (+self.s_neutronShieldIR & -self.s_neutronShieldOR &
|
||||
+self.s_ns_NEbot_SWtop & -self.s_ns_NEtop_SWbot &
|
||||
-self.s_upperBound & +self.s_lowerBound)
|
||||
self.c_shieldPanels.append(self.c_sp_NE)
|
||||
|
||||
|
||||
self.c_sp_SW = openmc.Cell(name='SW Shield Panel', fill=self.mats['SS304'])
|
||||
self.c_sp_SW.region = (+self.s_neutronShieldIR & -self.s_neutronShieldOR &
|
||||
-self.s_ns_NEbot_SWtop & +self.s_ns_NEtop_SWbot &
|
||||
-self.s_upperBound & +self.s_lowerBound)
|
||||
self.c_shieldPanels.append(self.c_sp_SW)
|
||||
|
||||
|
||||
self.c_sp_N = openmc.Cell(name='N Shield Water', fill=self.mats['Borated Water'])
|
||||
self.c_sp_N.region = (+self.s_neutronShieldIR & -self.s_neutronShieldOR &
|
||||
+self.s_ns_NWtop_SEbot & -self.s_ns_NEtop_SWbot &
|
||||
|
|
@ -100,19 +105,19 @@ class UniverseZero(openmc.Universe):
|
|||
+self.s_ns_NEtop_SWbot & -self.s_ns_NWtop_SEbot &
|
||||
-self.s_upperBound & +self.s_lowerBound)
|
||||
self.c_shieldPanels.append(self.c_sp_S)
|
||||
|
||||
|
||||
self.c_sp_E = openmc.Cell(name='E Shield Water', fill=self.mats['Borated Water'])
|
||||
self.c_sp_E.region = (+self.s_neutronShieldIR & -self.s_neutronShieldOR &
|
||||
+self.s_ns_NWbot_SEtop & +self.s_ns_NEbot_SWtop &
|
||||
-self.s_upperBound & +self.s_lowerBound)
|
||||
self.c_shieldPanels.append(self.c_sp_E)
|
||||
|
||||
|
||||
self.c_sp_W = openmc.Cell(name='W Shield Water', fill=self.mats['Borated Water'])
|
||||
self.c_sp_W.region = (+self.s_neutronShieldIR & -self.s_neutronShieldOR &
|
||||
-self.s_ns_NWbot_SEtop & -self.s_ns_NEbot_SWtop &
|
||||
-self.s_upperBound & +self.s_lowerBound)
|
||||
self.c_shieldPanels.append(self.c_sp_W)
|
||||
|
||||
|
||||
self.s_coreBarrelOR = openmc.ZCylinder(name='Core Barrel OR', R=c.coreBarrelOR)
|
||||
self.c_sp_inner = openmc.Cell(name='Water between barrel and shield', fill=self.mats['Borated Water'])
|
||||
self.c_sp_inner.region = (+self.s_coreBarrelOR & -self.s_neutronShieldIR &
|
||||
|
|
@ -122,14 +127,14 @@ class UniverseZero(openmc.Universe):
|
|||
|
||||
def _add_core_barrel(self):
|
||||
""" Adds BEAVRS core barrel and core lattice """
|
||||
|
||||
|
||||
# Core barrel
|
||||
|
||||
|
||||
self.s_coreBarrelIR = openmc.ZCylinder(name='Core Barrel IR', R=c.coreBarrelIR)
|
||||
self.c_coreBarrel = openmc.Cell(name="Core Barrel", fill=self.mats['SS304'])
|
||||
self.c_coreBarrel.region = (-self.s_coreBarrelOR & +self.s_coreBarrelIR &
|
||||
-self.s_upperBound & +self.s_lowerBound)
|
||||
|
||||
|
||||
# Core lattice
|
||||
|
||||
self.c_core = openmc.Cell(name="Core lattice", fill=self.core.u_coreLattice)
|
||||
|
|
@ -139,10 +144,40 @@ class UniverseZero(openmc.Universe):
|
|||
|
||||
def _create_main_universe(self):
|
||||
""" Creates the main BEAVRS universe """
|
||||
|
||||
self.add_cell(self.c_RPV)
|
||||
self.add_cell(self.c_liner)
|
||||
self.add_cell(self.c_downcomer)
|
||||
for cell in self.c_shieldPanels: self.add_cell(cell)
|
||||
self.add_cell(self.c_coreBarrel)
|
||||
self.add_cell(self.c_core)
|
||||
|
||||
# For 3D problem, add full core to main universe
|
||||
if not self.is_2d:
|
||||
self.add_cell(self.c_RPV)
|
||||
self.add_cell(self.c_liner)
|
||||
self.add_cell(self.c_downcomer)
|
||||
for cell in self.c_shieldPanels: self.add_cell(cell)
|
||||
self.add_cell(self.c_coreBarrel)
|
||||
self.add_cell(self.c_core)
|
||||
|
||||
# For 2D problem, create core universe separately and set bounding box as main universe
|
||||
else:
|
||||
# Define core universe that will be fill of main universe
|
||||
self.core_univ = openmc.Universe(name='BEAVRS core universe')
|
||||
self.core_univ.add_cell(self.c_RPV)
|
||||
self.core_univ.add_cell(self.c_liner)
|
||||
self.core_univ.add_cell(self.c_downcomer)
|
||||
for cell in self.c_shieldPanels: self.core_univ.add_cell(cell)
|
||||
self.core_univ.add_cell(self.c_coreBarrel)
|
||||
self.core_univ.add_cell(self.c_core)
|
||||
|
||||
# Define boundaries of bounding box
|
||||
outer_bound = 8.5*c.latticePitch
|
||||
self.s_leftBound = openmc.XPlane(name='Left Box', x0=-outer_bound, boundary_type='vacuum')
|
||||
self.s_rightBound = openmc.XPlane(name='Right Box', x0=outer_bound, boundary_type='vacuum')
|
||||
self.s_backBound = openmc.YPlane(name='Back Box', y0=-outer_bound, boundary_type='vacuum')
|
||||
self.s_frontBound = openmc.YPlane(name='Front Box', y0=outer_bound, boundary_type='vacuum')
|
||||
|
||||
# Bounding box
|
||||
|
||||
self.c_boundbox = openmc.Cell(name="Bounding box", fill=self.core_univ)
|
||||
self.c_boundbox.region = \
|
||||
(+self.s_leftBound & -self.s_rightBound &
|
||||
+self.s_backBound & -self.s_frontBound &
|
||||
-self.s_upperBound & +self.s_lowerBound)
|
||||
|
||||
self.add_cell(self.c_boundbox)
|
||||
|
|
|
|||
|
|
@ -2,13 +2,27 @@
|
|||
|
||||
import os
|
||||
from beavrs.builder import BEAVRS
|
||||
from optparse import OptionParser
|
||||
|
||||
try:
|
||||
os.mkdir('build')
|
||||
except OSError: pass
|
||||
os.chdir('build')
|
||||
|
||||
b = BEAVRS()
|
||||
usage = """usage: %prog [options]"""
|
||||
p = OptionParser(usage=usage)
|
||||
p.add_option('-d', '--2d', action='store_true', dest='is_2d',
|
||||
default=False, help='Create 2D BEAVRS input files,' \
|
||||
+ ' 3D by default')
|
||||
p.add_option('-s', '--symmetric', action='store_true', dest='is_symmetric',
|
||||
default=False, help='Create octant-symmetric input files,' \
|
||||
+ ' not symmetric by default')
|
||||
(options, args) = p.parse_args()
|
||||
|
||||
if not len(args) == 0:
|
||||
p.print_help()
|
||||
|
||||
b = BEAVRS(is_symmetric=options.is_symmetric, is_2d=options.is_2d)
|
||||
b.write_openmc_geometry()
|
||||
b.write_openmc_materials()
|
||||
b.write_openmc_plots()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue