ANL11-A2 reactor: multigroup Neutron Diffusion
This notebooks implements the generation of the snapshots arising from the ANL11-A2 reactor (2D) from the Argonne Code Center - Supplement 2.
This notebook may took a while to run, consider converting it into a .py
script.
[1]:
from mpi4py import MPI
import numpy as np
import os
from IPython.display import clear_output
import gmsh
from dolfinx.io.gmshio import model_to_mesh, read_from_msh
from pyforce.tools.functions_list import FunctionsList
from pyforce.tools.backends import LoopProgress
from pyforce.tools.write_read import StoreFunctionsList
import pickle
from neutronics import steady_neutron_diff
Mesh and parameters
The geometry and the main physical parameters will be assigned.
Importing geometry and meshing
The geometry and the mesh are imported from “ANL11A2_octave.msh”, generated with GMSH.
[2]:
gdim = 2
model_rank = 0
mesh_comm = MPI.COMM_WORLD
use_msh = False
# If the snapshots are generated from a different computer than the one used for the offline/online simulation, there may be issues in the number of dofs
if (use_msh):
domain, ct, ft = read_from_msh("ANL11A2_octave.msh", comm = mesh_comm, rank = model_rank, gdim = gdim)
else:
# Initialize the gmsh module
gmsh.initialize()
# Load the .geo file
gmsh.merge('ANL11A2_octave.geo')
gmsh.model.geo.synchronize()
# Set algorithm (adaptive = 1, Frontal-Delaunay = 6)
gmsh.option.setNumber("Mesh.Algorithm", 6)
gmsh.model.mesh.generate(gdim)
gmsh.model.mesh.optimize("Netgen")
# Domain
domain, ct, ft = model_to_mesh(gmsh.model, comm = mesh_comm, rank = model_rank, gdim = gdim )
gmsh.finalize()
fuel1_marker = 1
fuel2_marker = 2
fuel_rod_marker = 3
refl_marker = 4
void_marker = 10
sym_marker = 20
tdim = domain.topology.dim
fdim = tdim - 1
domain.topology.create_connectivity(fdim, tdim)
clear_output()
Definition of the physical parameters
Since there are different subdomains in \(\Omega\), the values of the parameters changes according to the region. No thermal feedback effects are considered.
[ ]:
regions = [fuel1_marker, fuel2_marker, fuel_rod_marker, refl_marker]
neutronics_param = dict()
neutronics_param['Energy Groups'] = 2
neutronics_param['xs_a'] = [np.array([0.01, 0.01, 0.01, 0.]),
np.array([0.085, 0.08, 0.13, 0.01])]
neutronics_param['nu_xs_f'] = [np.array([0., 0., 0., 0.]),
np.array([0.135, 0.135, 0.135, 0.])]
neutronics_param['chi'] = [np.array([1,1,1,1]),
np.array([0,0,0,0])]
neutronics_param['B2z'] = [np.array([0.8e-4]*4),
np.array([0.8e-4]*4)]
neutronics_param['xs_s'] = [[np.array([0]*4), np.array([0.02, 0.02, 0.02, 0.04])],
[np.array([0]*4), np.array([0]*4)]]
# The values in the reflector regions are the one of the benchmark
neutronics_param['D'] = [np.array([1.5, 1.5, 1.5, 2.]),
np.array([0.4, 0.4, 0.4, 0.3])]
# Normalisation parameters for the flux
nu_value = 1
Ef = 1
reactor_power = 1
# Void boundary conditions
albedo = [0.4692] * 2
Generation of the snapshots
This section generates the snapshots from the Finite Element solver, dependent on the parameters \(D_1\) and \(\Sigma_{a,2}\), in the reflector (marker=4) and fuel+rod region (marker=3) respectively:
\begin{equation*} D_1^{\text{refl}} \in [0.5, 3] \qquad \qquad \Sigma_{a,2}^{\text{rod}} = [0.08, 0.15] \end{equation*}
The variational forms are generated to prepare for the numerical solution and the subsequent generation of the snapshots.
[4]:
import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)
neutr_steady_problem = steady_neutron_diff(domain, ct, ft, neutronics_param, regions, void_marker,
albedo = albedo)
neutr_steady_problem.assembleForm()
Train Set
The train set of snapshots is computed and stored.
[8]:
D_1_refl_train = np.arange(0.5 - 0.1/2, 3.00 + 0.1/2, 0.1)
xs_a2_rod_train = np.arange(0.08 - 0.005/2, 0.15 + 0.005/2, 0.05)
# Number of training snapshots
Ns = len(D_1_refl_train) * len(xs_a2_rod_train)
# Create structures to save the data
train_param = list()
param_idx = 0
phi_1_train = FunctionsList(neutr_steady_problem.V.sub(0).collapse()[0])
phi_2_train = FunctionsList(neutr_steady_problem.V.sub(1).collapse()[0])
progress_bar = LoopProgress(msg='Generating the training snapshots', final = Ns)
for ii in range(len(D_1_refl_train)):
for jj in range(len(xs_a2_rod_train)):
# Update the diffusion coefficient in the reflector and fuel1 region (respectively)
neutr_steady_problem.phys_param['D'][0][3] = D_1_refl_train[ii]
neutr_steady_problem.phys_param['xs_a'][1][2] = xs_a2_rod_train[jj]
# Solve the eigenvalue problem
res = neutr_steady_problem.solve(power = reactor_power, Ef=Ef, nu = nu_value, maxIter = 500, verbose=False)
# Store the snapshots
phi_1_train.append(res[0][0].flatten())
phi_2_train.append(res[0][1].flatten())
# Store parameters
train_param.append(dict())
train_param[param_idx]['D_1_refl'] = D_1_refl_train[ii]
train_param[param_idx]['xs_a_2_rod'] = xs_a2_rod_train[jj]
train_param[param_idx]['k_eff'] = res[1]
# Update bar
param_idx += 1
progress_bar.update(1, percentage=False)
Generating the training snapshots: 2.000 / 2.00 - 13.351 s/it
The train snapshots and the correspondent parameters are stored using pyforce and pickle.
[ ]:
path='./Snapshots/'
if not os.path.exists(path):
os.makedirs(path)
StoreFunctionsList(neutr_steady_problem.domain, phi_1_train, 'phi_1', path+'train_snap_phi_1')
StoreFunctionsList(neutr_steady_problem.domain, phi_2_train, 'phi_2', path+'train_snap_phi_2')
with open(path+'train.param', 'wb') as f:
pickle.dump([train_param], f)
Test Set
The test set of snapshots is computed and stored.
[ ]:
D_1_refl_test = np.arange(0.50, 3.001, 0.1)
xs_a2_rod_test = np.arange(0.08, 0.151, 0.05)
# Number of training snapshots
Ns = len(D_1_refl_test) * len(xs_a2_rod_test)
# Create structures to save the data
test_param = list()
param_idx = 0
phi_1_test = FunctionsList(neutr_steady_problem.V.sub(0).collapse()[0])
phi_2_test = FunctionsList(neutr_steady_problem.V.sub(1).collapse()[0])
progress_bar = LoopProgress(msg='Generating the test snapshots', final = Ns)
for ii in range(len(D_1_refl_test)):
for jj in range(len(xs_a2_rod_test)):
# Update the diffusion coefficient in the reflector and fuel2 region (respectively)
neutr_steady_problem.phys_param['D'][0][3] = D_1_refl_test[ii]
neutr_steady_problem.phys_param['xs_a'][1][2] = xs_a2_rod_test[jj]
# Solve the eigenvalue problem
res = neutr_steady_problem.solve(power = reactor_power, Ef=Ef, nu = nu_value, maxIter = 500, verbose=False)
# Store the snapshots
phi_1_test.append(res[0][0].flatten())
phi_2_test.append(res[0][1].flatten())
# Store parameters
test_param.append(dict())
test_param[param_idx]['D_1_refl'] = D_1_refl_test[ii]
test_param[param_idx]['xs_a_2_rod'] = xs_a2_rod_test[jj]
test_param[param_idx]['k_eff'] = res[1]
# Update bar
param_idx += 1
progress_bar.update(1, percentage=False)
The test snapshots and the correspondent parameters are stored using pyforce and pickle.
[ ]:
StoreFunctionsList(neutr_steady_problem.domain, phi_1_test, 'phi_1', path+'test_snap_phi_1')
StoreFunctionsList(neutr_steady_problem.domain, phi_2_test, 'phi_2', path+'test_snap_phi_2')
with open(path+'test.param', 'wb') as f:
pickle.dump([test_param], f)