refactor CE/LI and LE/QI

This commit is contained in:
liangjg 2019-01-23 15:08:55 -05:00
parent 9cc923faad
commit cbbac7df8d
6 changed files with 261 additions and 325 deletions

View file

@ -7,8 +7,10 @@ The integrator subcomponents.
from .cf4 import *
from .cecm import *
from .celi import *
from .cram import *
from .epc_rk4 import *
from .leqi import *
from .predictor import *
from .si_celi import *
from .si_leqi import *

View file

@ -1,176 +1,170 @@
""" The CE/LI CFQ4 integrator.
Implements the CE/LI Predictor-Corrector algorithm using commutator free
high order integrators.
This algorithm is mathematically defined as:
.. math:
y' = A(y, t) y(t)
A_p = A(y_n, t_n)
y_p = expm(A_p h) y_n
A_c = A(y_p, t_n)
A(t) = t/dt * A_c + (dt - t)/dt * A_p
Here, A(t) is integrated using the fourth order algorithm described below.
From
----
Thalhammer, Mechthild. "A fourth-order commutator-free exponential
integrator for nonautonomous differential equations." SIAM journal on
numerical analysis 44.2 (2006): 851-864.
"""
"""The CE/LI CFQ4 integrator."""
import copy
import os
import time
from collections.abc import Iterable
from mpi4py import MPI
from .cram import deplete
from ..results import Results
from ..abc import OperatorResult
from .cram import CRAM48
from .save_results import save_results
def celi_cfq4(operator, print_out=True):
""" Performs integration of an operator using the CE/LI CFQ4 algorithm.
# Functions to form the special matrix for depletion
def _celi_f1(chain, rates):
return 5/12 * chain.form_matrix(rates[0]) + \
1/12 * chain.form_matrix(rates[1])
def _celi_f2(chain, rates):
return 1/12 * chain.form_matrix(rates[0]) + \
5/12 * chain.form_matrix(rates[1])
def celi(operator, timesteps, power=None, power_density=None,
print_out=True):
r"""Deplete using the CE/LI CFQ4 algorithm.
Implements the CE/LI Predictor-Corrector algorithm using the [fourth order
commutator-free integrator]_.
The CE/LI algorithm is mathematically defined as:
.. math:
y' = A(y, t) y(t)
A_p = A(y_n, t_n)
y_p = expm(A_p h) y_n
A_c = A(y_p, t_n)
A(t) = t/dt * A_c + (dt - t)/dt * A_p
Here, A(t) is integrated using the fourth order algorithm CFQ4.
Parameters
----------
operator : Operator
operator : openmc.deplete.TransportOperator
The operator object to simulate on.
timesteps : iterable of float
Array of timesteps in units of [s]. Note that values are not cumulative.
power : float or iterable of float, optional
Power of the reactor in [W]. A single value indicates that the power is
constant over all timesteps. An iterable indicates potentially different
power levels for each timestep. For a 2D problem, the power can be given
in [W/cm] as long as the "volume" assigned to a depletion material is
actually an area in [cm^2]. Either `power` or `power_density` must be
specified.
power_density : float or iterable of float, optional
Power density of the reactor in [W/gHM]. It is multiplied by initial
heavy metal inventory to get total power if `power` is not speficied.
print_out : bool, optional
Whether or not to print out time.
References
----------
.. [fourth order commutator-free integrator]
Thalhammer, Mechthild. "A fourth-order commutator-free exponential
integrator for nonautonomous differential equations." SIAM journal on
numerical analysis 44.2 (2006): 851-864.
"""
if power is None:
if power_density is None:
raise ValueError(
"Neither power nor power density was specified.")
if not isinstance(power_density, Iterable):
power = power_density*operator.heavy_metal
else:
power = [i*operator.heavy_metal for i in power_density]
# Save current directory
dir_home = os.getcwd()
# Move to folder
os.makedirs(operator.settings.output_dir, exist_ok=True)
os.chdir(operator.settings.output_dir)
if not isinstance(power, Iterable):
power = [power]*len(timesteps)
# Generate initial conditions
vec = operator.initial_condition()
with operator as vec:
# Initialize time and starting index
if operator.prev_res is None:
t = 0.0
i_res = 0
else:
t = operator.prev_res[-1].time[-1]
i_res = len(operator.prev_res)
t = 0.0
for i, (dt, p) in enumerate(zip(timesteps, power)):
vec, t, _ = celi_inner(operator, vec, p, i, i_res, t, dt,
print_out)
for i, dt in enumerate(operator.settings.dt_vec):
vec, t, _ = celi_cfq4_inner(operator, vec, i, t, dt, print_out)
# Perform one last simulation
x = [copy.deepcopy(vec)]
op_results = [operator(x[0], power[-1])]
# Perform one last simulation
x = [copy.deepcopy(vec)]
seeds = []
eigvls = []
rates_array = []
eigvl, rates, seed = operator.eval(x[0])
# Create results, write to disk
Results.save(operator, x, op_results, [t, t], p, i_res + len(timesteps))
eigvls.append(eigvl)
seeds.append(seed)
rates_array.append(rates)
# Create results, write to disk
save_results(operator, x, rates_array, eigvls, seeds, [t, t],
len(operator.settings.dt_vec))
# Return to origin
os.chdir(dir_home)
def celi_cfq4_inner(operator, vec, i, t, dt, print_out):
def celi_inner(operator, vec, p, i, i_res, t, dt, print_out):
""" The inner loop of CE/LI CFQ4.
Parameters
----------
operator : Operator
The operator object to simulate on.
vec : list of numpy.array
x : list of nuclide vector
Nuclide vector, beginning of time.
i : Int
p : float
Power of the reactor in [W]
i : int
Current iteration number.
t : Float
i_res : int
Starting index, for restart calculation.
t : float
Time at start of step.
dt : Float
dt : float
Time step.
print_out : bool
Whether or not to print out time.
Returns
-------
x_result : list of numpy.array
list of numpy.array
Nuclide vector, end of time.
Float
float
Next time
ReactionRates
Reaction rates from beginning of step.
OperatorResult
Operator result from beginning of step.
"""
n_mats = len(vec)
chain = operator.chain
# Create vectors
x = [copy.deepcopy(vec)]
seeds = []
eigvls = []
rates_array = []
# Get beginning-of-timestep concentrations and reaction rates
# Avoid doing first transport run if already done in previous
# calculation
if i > 0 or operator.prev_res is None:
x = [copy.deepcopy(vec)]
op_results = [operator(x[0], p)]
eigvl, rates, seed = operator.eval(x[0])
else:
# Get initial concentration
x = [operator.prev_res[-1].data[0]]
eigvls.append(eigvl)
seeds.append(seed)
rates_array.append(copy.deepcopy(rates))
# Get rates
op_results = [operator.prev_res[-1]]
op_results[0].rates = op_results[0].rates[0]
x_result = []
# Set first stage value of keff
op_results[0].k = op_results[0].k[0]
t_start = time.time()
for mat in range(n_mats):
# Form matrix
f = operator.form_matrix(rates_array[0], mat)
# Scale reaction rates by ratio of powers
power_res = operator.prev_res[-1].power
ratio_power = p / power_res
op_results[0].rates *= ratio_power[0]
x_new = CRAM48(f, x[0][mat], dt)
# Deplete to end
x_new = deplete(chain, x[0], op_results[0].rates, dt, print_out)
x.append(x_new)
op_results.append(operator(x[1], p))
x_result.append(x_new)
t_end = time.time()
if MPI.COMM_WORLD.rank == 0:
if print_out:
print("Time to matexp: ", t_end - t_start)
x.append(x_result)
eigvl, rates, seed = operator.eval(x[1])
eigvls.append(eigvl)
seeds.append(seed)
rates_array.append(copy.deepcopy(rates))
x_result = []
t_start = time.time()
for mat in range(n_mats):
# Form matrices
f1 = dt * operator.form_matrix(rates_array[0], mat)
f2 = dt * operator.form_matrix(rates_array[1], mat)
# Perform commutator-free integral
x_new = copy.deepcopy(x[0][mat])
# Compute linearly interpolated f at points
# A{1,2} = f(1/2 -/+ sqrt(3)/6)
# Then
# a{1,2} = 1/4 +/- sqrt(3)/6
# m1 = a2 * A1 + a1 * A2
# m2 = a1 * A1 + a2 * A2
m1 = 1/12 * (f1 + 5 * f2)
m2 = 1/12 * (5 * f1 + f2)
x_new = CRAM48(m2, x_new, 1.0)
x_new = CRAM48(m1, x_new, 1.0)
x_result.append(x_new)
t_end = time.time()
if MPI.COMM_WORLD.rank == 0:
if print_out:
print("Time to matexp: ", t_end - t_start)
# Deplete with two matrix exponentials
rates = list(zip(op_results[0].rates, op_results[1].rates))
x_end = deplete(chain, x[0], rates, dt, print_out,
matrix_func=_celi_f1)
x_end = deplete(chain, x_end, rates, dt, print_out,
matrix_func=_celi_f2)
# Create results, write to disk
save_results(operator, x, rates_array, eigvls, seeds, [t, t + dt], i)
Results.save(operator, x, op_results, [t, t + dt], p, i_res + i)
return x_result, t + dt, rates_array[0]
# return updated time and vectors
return x_end, t + dt, op_results[0]

View file

@ -1,178 +1,156 @@
""" The LE/QI CFQ4 integrator.
Implements the LE/QI Predictor-Corrector algorithm using commutator free
high order integrators.
This algorithm is mathematically defined as:
.. math:
y' = A(y, t) y(t)
A_m1 = A(y_n-1, t_n-1)
A_0 = A(y_n, t_n)
A_l(t) linear extrapolation of A_m1, A_0
Integrate to t_n+1 to get y_p
A_c = A(y_p, y_n+1)
A_q(t) quadratic interpolation of A_m1, A_0, A_c
Here, A(t) is integrated using the fourth order algorithm described below.
From
----
Thalhammer, Mechthild. "A fourth-order commutator-free exponential
integrator for nonautonomous differential equations." SIAM journal on
numerical analysis 44.2 (2006): 851-864.
It is initialized using the CE/LI algorithm.
"""
"""The LE/QI CFQ4 integrator."""
import copy
import os
import time
from collections.abc import Iterable
from itertools import repeat
from mpi4py import MPI
from .celi import celi_inner
from .cram import deplete
from ..results import Results
from ..abc import OperatorResult
from .celi_cfq4 import celi_cfq4_inner
from .cram import CRAM48
from .save_results import save_results
def leqi_cfq4(operator, print_out=True):
""" Performs integration of an operator using the LE/QI CFQ4 algorithm.
# Functions to form the special matrix for depletion
def _leqi_f1(chain, inputs):
f1 = chain.form_matrix(inputs[0])
f2 = chain.form_matrix(inputs[1])
dt_l, dt = inputs[2], inputs[3]
return -dt / (12 * dt_l) * f1 + (dt + 6 * dt_l) / (12 * dt_l) * f2
def _leqi_f2(chain, inputs):
f1 = chain.form_matrix(inputs[0])
f2 = chain.form_matrix(inputs[1])
dt_l, dt = inputs[2], inputs[3]
return -5 * dt / (12 * dt_l) * f1 + (5 * dt + 6 * dt_l) / (12 * dt_l) * f2
def _leqi_f3(chain, inputs):
f1 = chain.form_matrix(inputs[0])
f2 = chain.form_matrix(inputs[1])
f3 = chain.form_matrix(inputs[2])
dt_l, dt = inputs[3], inputs[4]
return (-dt**2 / (12 * dt_l * (dt + dt_l)) * f1 +
(dt**2 + 6*dt*dt_l + 5*dt_l**2) / (12 * dt_l * (dt + dt_l)) * f2 +
dt_l / (12 * (dt + dt_l)) * f3)
def _leqi_f4(chain, inputs):
f1 = chain.form_matrix(inputs[0])
f2 = chain.form_matrix(inputs[1])
f3 = chain.form_matrix(inputs[2])
dt_l, dt = inputs[3], inputs[4]
return (-dt**2 / (12 * dt_l * (dt + dt_l)) * f1 +
(dt**2 + 2*dt*dt_l + dt_l**2) / (12 * dt_l * (dt + dt_l)) * f2 +
(4 * dt * dt_l + 5 * dt_l**2) / (12 * dt_l * (dt + dt_l)) * f3)
def leqi(operator, timesteps, power=None, power_density=None, print_out=True):
r"""Deplete using the LE/QI CFQ4 algorithm.
Implements the LE/QI Predictor-Corrector algorithm using the [fourth order
commutator-free integrator]_.
The LE/QI algorithm is mathematically defined as:
.. math:
y' = A(y, t) y(t)
A_m1 = A(y_n-1, t_n-1)
A_0 = A(y_n, t_n)
A_l(t) linear extrapolation of A_m1, A_0
Integrate to t_n+1 to get y_p
A_c = A(y_p, y_n+1)
A_q(t) quadratic interpolation of A_m1, A_0, A_c
Here, A(t) is integrated using the fourth order algorithm CFQ4.
It is initialized using the CE/LI algorithm.
Parameters
----------
operator : Operator
operator : openmc.deplete.TransportOperator
The operator object to simulate on.
timesteps : iterable of float
Array of timesteps in units of [s]. Note that values are not cumulative.
power : float or iterable of float, optional
Power of the reactor in [W]. A single value indicates that the power is
constant over all timesteps. An iterable indicates potentially different
power levels for each timestep. For a 2D problem, the power can be given
in [W/cm] as long as the "volume" assigned to a depletion material is
actually an area in [cm^2]. Either `power` or `power_density` must be
specified.
power_density : float or iterable of float, optional
Power density of the reactor in [W/gHM]. It is multiplied by initial
heavy metal inventory to get total power if `power` is not speficied.
print_out : bool, optional
Whether or not to print out time.
References
----------
.. [fourth order commutator-free integrator]
Thalhammer, Mechthild. "A fourth-order commutator-free exponential
integrator for nonautonomous differential equations." SIAM journal on
numerical analysis 44.2 (2006): 851-864.
"""
if power is None:
if power_density is None:
raise ValueError(
"Neither power nor power density was specified.")
if not isinstance(power_density, Iterable):
power = power_density*operator.heavy_metal
else:
power = [i*operator.heavy_metal for i in power_density]
# Save current directory
dir_home = os.getcwd()
# Move to folder
os.makedirs(operator.settings.output_dir, exist_ok=True)
os.chdir(operator.settings.output_dir)
if not isinstance(power, Iterable):
power = [power]*len(timesteps)
# Generate initial conditions
vec = operator.initial_condition()
with operator as vec:
# Initialize time and starting index
if operator.prev_res is None:
t = 0.0
i_res = 0
else:
t = operator.prev_res[-1].time[-1]
i_res = len(operator.prev_res)
n_mats = len(vec)
for i, (dt, p) in enumerate(zip(timesteps, power)):
# Perform SI-CE/LI CFQ4 for the first step
if i == 0:
# Save results for the last step
dt_l = dt
vec, t, op_res_last = celi_inner(operator, vec, p, i, i_res,
t, dt, print_out)
continue
t = 0.0
# Perform remaining LE/QI
x = [copy.deepcopy(vec)]
op_results = [operator(x[0], p)]
# Perform single step of CE/LI CFQ4
dt_l = operator.settings.dt_vec[0]
vec, t, rates_last = celi_cfq4_inner(operator, vec, 0, t, dt_l, print_out)
inputs = list(zip(op_res_last.rates, op_results[0].rates,
repeat(dt_l), repeat(dt)))
x_new = deplete(chain, x[0], inputs, dt, print_out,
matrix_func=_leqi_f1)
x_new = deplete(chain, x_new, inputs, dt, print_out,
matrix_func=_leqi_f2)
x.append(x_new)
op_results.append(operator(x[1], p))
# Perform remaining LE/QI
for i, dt in enumerate(operator.settings.dt_vec[1::]):
# Create vectors
inputs = list(zip(op_res_last.rates, op_results[0].rates,
op_results[1].rates, repeat(dt_l), repeat(dt)))
x_new = deplete(chain, x[0], inputs, dt, print_out,
matrix_func=_leqi_f3)
x_new = deplete(chain, x_new, inputs, dt, print_out,
matrix_func=_leqi_f4)
# Create results, write to disk
Results.save(operator, x, op_results, [t, t+dt], p, i_res+i)
# update results
x = [x_new]
op_res_last = copy.deepcopy(op_results[0])
t += dt
dt_l = dt
# Perform one last simulation
x = [copy.deepcopy(vec)]
seeds = []
eigvls = []
rates_array = []
eigvl, rates, seed = operator.eval(x[0])
eigvls.append(eigvl)
seeds.append(seed)
rates_array.append(copy.deepcopy(rates))
x_result = []
t_start = time.time()
for mat in range(n_mats):
# Form matrices
f1 = dt * operator.form_matrix(rates_last, mat)
f2 = dt * operator.form_matrix(rates_array[0], mat)
# Perform commutator-free integral
x_new = copy.deepcopy(x[0][mat])
# Compute linearly extrapolated f at points
# A{1,2} = f(1/2 -/+ sqrt(3)/6)
# Then
# a{1,2} = 1/4 +/- sqrt(3)/6
# m1 = a2 * A1 + a1 * A2
# m2 = a1 * A1 + a2 * A2
m1 = -5 * dt / (12 * dt_l) * f1 + (5 * dt + 6 * dt_l) / (12 * dt_l) * f2
m2 = -dt / (12 * dt_l) * f1 + (dt + 6 * dt_l) / (12 * dt_l) * f2
x_new = CRAM48(m2, x_new, 1.0)
x_new = CRAM48(m1, x_new, 1.0)
x_result.append(x_new)
t_end = time.time()
if MPI.COMM_WORLD.rank == 0:
if print_out:
print("Time to matexp: ", t_end - t_start)
x.append(x_result)
eigvl, rates, seed = operator.eval(x[1])
eigvls.append(eigvl)
seeds.append(seed)
rates_array.append(copy.deepcopy(rates))
x_result = []
t_start = time.time()
for mat in range(n_mats):
# Form matrices
f1 = dt * operator.form_matrix(rates_last, mat)
f2 = dt * operator.form_matrix(rates_array[0], mat)
f3 = dt * operator.form_matrix(rates_array[1], mat)
# Perform commutator-free integral
x_new = copy.deepcopy(x[0][mat])
# Compute quadratically interpolated f at points
# A{1,2} = f(1/2 -/+ sqrt(3)/6)
# Then
# a{1,2} = 1/4 +/- sqrt(3)/6
# m1 = a2 * A1 + a1 * A2
# m2 = a1 * A1 + a2 * A2
m1 = (-dt**2 / (12 * dt_l * (dt + dt_l)) * f1 +
(dt**2 + 2 * dt * dt_l + dt_l**2) / (12 * dt_l * (dt + dt_l)) * f2 +
(4 * dt * dt_l + 5 * dt_l**2) / (12 * dt_l * (dt + dt_l)) * f3)
m2 = (-dt**2/(12 * dt_l * (dt + dt_l)) * f1 +
(dt**2 + 6 * dt * dt_l + 5 * dt_l**2) / (12 * dt_l * (dt + dt_l)) * f2 +
dt_l / (12 * (dt + dt_l)) * f3)
x_new = CRAM48(m2, x_new, 1.0)
x_new = CRAM48(m1, x_new, 1.0)
x_result.append(x_new)
t_end = time.time()
if MPI.COMM_WORLD.rank == 0:
if print_out:
print("Time to matexp: ", t_end - t_start)
op_results = [operator(x[0], power[-1])]
# Create results, write to disk
save_results(operator, x, rates_array, eigvls, seeds, [t, t + dt], i + 1)
rates_last = copy.deepcopy(rates_array[0])
t += dt
dt_l = dt
vec = copy.deepcopy(x_result)
# Perform one last simulation
x = [copy.deepcopy(vec)]
seeds = []
eigvls = []
rates_array = []
eigvl, rates, seed = operator.eval(x[0])
eigvls.append(eigvl)
seeds.append(seed)
rates_array.append(rates)
# Create results, write to disk
save_results(operator, x, rates_array, eigvls, seeds, [t, t],
len(operator.settings.dt_vec))
# Return to origin
os.chdir(dir_home)
Results.save(operator, x, op_results, [t, t], p, i_res + len(timesteps))

View file

@ -6,17 +6,9 @@ from collections.abc import Iterable
from .cram import deplete
from ..results import Results
from ..abc import OperatorResult
from .celi import _celi_f1, _celi_f2
# Functions to form the special matrix for depletion
def _celi_f1(chain, rates):
return 5/12 * chain.form_matrix(rates[0]) + \
1/12 * chain.form_matrix(rates[1])
def _celi_f2(chain, rates):
return 1/12 * chain.form_matrix(rates[0]) + \
5/12 * chain.form_matrix(rates[1])
def si_celi(operator, timesteps, power=None, power_density=None,
print_out=True, m=10):
r"""Deplete using the SI-CE/LI CFQ4 algorithm.

View file

@ -5,42 +5,12 @@ from collections.abc import Iterable
from itertools import repeat
from .si_celi import si_celi_inner
from .leqi import _leqi_f1, _leqi_f2, _leqi_f3, _leqi_f4
from .cram import deplete
from ..results import Results
from ..abc import OperatorResult
# Functions to form the special matrix for depletion
def _leqi_f1(chain, inputs):
f1 = chain.form_matrix(inputs[0])
f2 = chain.form_matrix(inputs[1])
dt_l, dt = inputs[2], inputs[3]
return -dt / (12 * dt_l) * f1 + (dt + 6 * dt_l) / (12 * dt_l) * f2
def _leqi_f2(chain, inputs):
f1 = chain.form_matrix(inputs[0])
f2 = chain.form_matrix(inputs[1])
dt_l, dt = inputs[2], inputs[3]
return -5 * dt / (12 * dt_l) * f1 + (5 * dt + 6 * dt_l) / (12 * dt_l) * f2
def _leqi_f3(chain, inputs):
f1 = chain.form_matrix(inputs[0])
f2 = chain.form_matrix(inputs[1])
f3 = chain.form_matrix(inputs[2])
dt_l, dt = inputs[3], inputs[4]
return (-dt**2 / (12 * dt_l * (dt + dt_l)) * f1 +
(dt**2 + 6*dt*dt_l + 5*dt_l**2) / (12 * dt_l * (dt + dt_l)) * f2 +
dt_l / (12 * (dt + dt_l)) * f3)
def _leqi_f4(chain, inputs):
f1 = chain.form_matrix(inputs[0])
f2 = chain.form_matrix(inputs[1])
f3 = chain.form_matrix(inputs[2])
dt_l, dt = inputs[3], inputs[4]
return (-dt**2 / (12 * dt_l * (dt + dt_l)) * f1 +
(dt**2 + 2*dt*dt_l + dt_l**2) / (12 * dt_l * (dt + dt_l)) * f2 +
(4 * dt * dt_l + 5 * dt_l**2) / (12 * dt_l * (dt + dt_l)) * f3)
def si_leqi(operator, timesteps, power=None, power_density=None,
print_out=True, m=10):
r"""Deplete using the SI-LE/QI CFQ4 algorithm.

View file

@ -151,7 +151,7 @@ class Model(object):
# Perform depletion
check_value('method', method, ('cecm', 'predictor', 'cf4', 'epc_rk4',
'si_celi', 'si_leqi'))
'si_celi', 'si_leqi', 'celi', 'leqi'))
getattr(dep.integrator, method)(op, timesteps, **kwargs)
def export_to_xml(self):