Reactivity Perturbations and Feedback Coefficient Calculations
This application mode is used to calculate reactivity perturbations over different sequences of various state changes.
Note
This application mode is optimized to only extract reactivity differences. If the effect of perturbations on other parameters is also of interest, consider using the cases application mode instead.
Typical Use Cases
Calculate temperature based reactivity feedback tables.
Calculate reactivity tables from control rod movement.
Perform local (per channel) state perturbations.
Prepare complete reactivity tables for system codes such as RELAP.
Additional Input Parameters
In the following fbc denotes a feedback_coefficients parameter set, created as follows:
import applications.feedback_coefficients as app
fbc = app.parameters()
This parameters set is an extension of critical case and therefore supports all those input parameters (including the general parameters). Perturbation sequences are specified using the following two methods:
- fbc.add_sequence(name, *seq)
Adds a sequence of perturbations.
- Parameters:
name (str) – Tag that is used to identify this sequence of perturbations.
seq – The perturbations themselves. See the description below on the accepted values.
The entries in seq can either be:
A single state_parameter or a complete state instance. For example
>>> fbc.add_sequence('FT', state_parameters.fuel_temperature(24 * units.degC), state_parameters.fuel_temperature(80 * units.degC))
or
fbc.add_sequence('Temps', (state_parameters.fuel_temperature(24 * units.degC), state_parameters.moderator_temperature(24 * units.degC)), (state_parameters.fuel_temperature(80 * units.degC), state_parameters.moderator_temperature(60 * units.degC)))
A single travel instance or a dict of travel instances. For example, to move all banks
>>> fbc.add_sequence('all-banks', control.percentage_inserted(10.), control.percentage_inserted(20.))
or if only a single bank should be perturbed
fbc.add_sequence('bank-1', {'bank-1': control.percentage_inserted(10.)}, {'bank-1': control.percentage_inserted(20.)})
A labeledgrid or hexagonal grid containing a local (per channel) state perturbation. Each entry in the grid can again be a single state_parameter or a complete state instance. For example
grids = list() for temp in [24 * units.degC, 40 * units.degC, 80 * units.degC] ft = state_parameters.fuel_temperature(temp) m = \ [[_, 'A', 'B', 'C'], [1, _, ft, _], [2, _, _, -]] grids.append(m) fbc.add_sequence('B1-FT', *grids)
A power value, for example
>>> fbc.add_sequence('power', 1 * unit.MW, 1.2 * units.MW, 2 * units.MW)
Note
When performing power perturbations you would likely need to toggle the
app_param.thermal_hydraulic_feedbackoption. This will however, affect all your calculations, and should then not be used in the same application input which also calculates other state perturbations (such as fuel temperature).A general function which accepts a critical_case parameter set as input, and performs an arbitrary perturbation on these parameters. For example
def f1(parm): # do something pass def f2(parm): # do something else pass fbc.add_sequence('general', f1, f2)
This functionality can be used to perform perturbations on the model of arbitrary complexity. See the last example for more detail.
Note
When passing an arbitrary function the actual state space is unknown, and the delta between two perturbations is assumed to be 1. To override this behavior, consider creating a function object derived from Custom and specifying the base and delta methods. See the examples.
Attention
The type of entries in a sequence should not be mixed. For example,
>>> fbc.add_sequence('bad-idea', state_parameter.fuel_temperature(100 * units.degC), control.percentage_inserted(50.0))
is invalid. Add two sequences instead:
>>> fbc.add_sequence('FT', state_parameter.fuel_temperature(100 * units.degC)) >>> fbc.add_sequence('ROD', control.percentage_inserted(50.0))
- fbc.add_perturbations(name, param, *seq)
Convenience method that adds a number of perturbations to a specific state parameter.
- Parameters:
name (str) – Tag that is used to identify this sequence of perturbations.
param (state_parameter) – The state parameter that should be perturbed.
seq – The sequence of (additive) perturbations.
Example:
>>> fbc.add_perturbations('FT', state_parameters.fuel_temperature, -10, +10, +50)
Attention
Since these perturbations are relative to the base state, it is important that when using this method the target param is set in the model.state.
Note
When adding to an existing sequence, the value is appended. Thus
fbc.add_sequence('my-seq', a, b)
is equivalent to:
fbc.add_sequence('my-seq', a)
fbc.add_sequence('my-seq', b)
Command Line Usage
This application supports all the standard application modes and options as described in General Application Command Line Interface (CLI).
Typical command line usage
oscar5 MY_REACTOR.projects.my_perturbations --target-mode <MODE> --config-file <CONFIG> run --threads 24
After output processing, a simple summary table is printed. See example below.
+-----------+-----------+-----------+-----------+---------------+
|Sequence |Index |Keff |dR (pcm) |dR/dx |
+===========+===========+===========+===========+===============+
|Nominal | |1.51818 | | |
+-----------+-----------+-----------+-----------+---------------+
|FT |0 |1.51859 |17.70 |-1.78 / kelvin |
+-----------+-----------+-----------+-----------+---------------+
| |1 |1.51619 |-86.15 |-1.76 / kelvin |
+-----------+-----------+-----------+-----------+---------------+
|MT |0 |1.51812 |-2.26 |0.23 / kelvin |
+-----------+-----------+-----------+-----------+---------------+
| |1 |1.51844 |11.32 |0.23 / kelvin |
+-----------+-----------+-----------+-----------+---------------+
More information can be extracted from the result files using the provided output tokens.
Output Tokens
The output tokens for this application mode are listed below. See also Using output tokens for general guidelines on using these tokens.
Note
In all the output tokens, the parameters sequence and state_parameter entries are only required when using the token to also drive input generations (via other application modes such as reload). It does not directly affect the output processing, and can therefore be omitted when using the tokens to only extract output.
- feedback_coefficients.multiplication_factors(tag, sequence=None, state_parameter=None, at=None, **kwargs)
Token used to extract the individual \(k_\mathrm{eff}\) values for a specified perturbation sequence.
- Parameters:
tag (str) – Which perturbation sequence to target. Corresponds to name in
fbc.add_sequence().sequence (list) – The sequence of perturbations. Must adhere to the rules outlined in
fbc.add_sequence().state_parameter (state_parameter) – If specified, the sequence is passed to
fbc.add_perturbations()instead.at (int or list[int]) – Customize at which cases the multiplication factor will be extracted.
The return type depends on the value of at:
If not specified (the default), a list of length equal to the perturbation sequence is returned.
If a single integer, only the \(k_\mathrm{eff}\) at this particular point in the sequence is returned.
Finally, if it is a list of integers, a list of length equal to at is returned, containing the \(k_\mathrm{eff}\) at these points in the sequence (in the order determined by at).
- feedback_coefficients.reactivities(tag, sequence=None, state_parameter=None, at=None, **kwargs)
Behaves just like
feedback_coefficients.multiplication_factors()but returns the total reactivity change (with respect to the unperturbed case) in pcm.
- feedback_coefficients.integral_points(tag, interpolation='cubic', sequence=None, state_parameter=None, at=None, **kwargs)
Token used to extract a point-wise representation of the cumulative reactivity curve.
- Parameters:
This will always return a pair of lists, the first being the state points (such as temperatures or bank positions), and the second the cumulative reactivities (in pcm) at these points. Exactly which points are returned depends on the value of at:
If not specified (the default), the points will correspond to the original perturbation sequence, but includes the base (unperturbed) point. Note that, in this case, the interpolation parameter is not used.
If it is a list of state points, these will be returned, with the corresponding cumulative reactivities. Thus at can be used to refine the mesh on which the curve is returned.
- feedback_coefficients.feedback_coefficients(tag, interpolation='cubic', sequence=None, state_parameter=None, at=None, **kwargs)
Behaves just like
feedback_coefficients.integral_points(), but the second sequence contains the reactivity rate of change (derivative of the cumulative reactivity curve) at the state points.Note
The interpolation parameter is always used in this case.
- feedback_coefficients.reactivity(tag, start, end, interpolation='cubic', sequence=None, state_parameter=None, at=None, **kwargs)
Token used to extract the reactivity change induced by moving from one point to another in the state space.
- Parameters:
start – Starting point
end – End point
Returns the delta reactivity (in pcm), when moving from start to end.
Examples
The following example will calculate reactivity differences on two sequences, then print the feedback coefficients
to a file on a finer mesh using the feedback_coefficients.feedback_coefficients().
import applications.feedback_coefficients as app
from ..configurations.my_config import model
from core import utilities, units, state_parameters
parameters = app.parameters()
parameters.project_name = 'feedback'
parameters.description = 'Calculate some reactivity perturbations'
parameters.working_directory = utilities.path_relative_to(__file__, 'FEEDBACK')
parameters.model = model
parameters.time_stamp = '01/01/2018 06:00:00'
model.state[state_parameters.fuel_temperature] = 30 * units.degC
model.state[state_parameters.moderator_temperature] = 30 * units.degC
model.state[state_parameters.moderator_density] = 0.998 * units.g/units.cc
model.facility_description.design_coolant_flow_direction = 'DOWNWARDS'
parameters.power_maps = True
parameters.power = 689655.0 * units.W
# Power feedback
# parameters.thermal_hydraulic_feedback = True
#
# parameters.add_sequence('power', 700 * units.kW, 1 * units.MW, 1.2 * units.MW)
parameters.add_perturbations('FT', state_parameters.fuel_temperature, -10, +10, +30, +50, +80)
parameters.add_perturbations('MT', state_parameters.moderator_temperature, -10, +10, +30, +50, +80)
parameters.particles = 64000
parameters.source_iteration = 100
parameters.max_iteration = 300
def fine_mesh(param, results):
"""
Extract feedback coefficients on a fine mesh and export to file.
"""
mesh = [(20 + 5 * i) * units.degC for i in range(16)]
pnts, fb = app.feedback_coefficients(tag='FT', at=mesh).get(results)
pnts, mb = app.feedback_coefficients(tag='MT', at=mesh).get(results)
with open(utilities.path_relative_to(__file__, 'coefficients.txt'), 'w') as of:
for p, f, m in zip(pnts, fb, mb):
of.write('{:.1f} {:.2f} {:.2f}\n'.format(
p.to('degC').magnitude, f.to('1/K').magnitude, m.to('1/K').magnitude))
parameters.add_result_hook(fine_mesh)
if __name__ == '__main__':
app.main(parameters)
The next example performs local perturbation in specified list of core channels.
import applications.feedback_coefficients as app
from ..configurations.my_config import model
from core import utilities, units, state_parameters
parameters = app.parameters()
parameters.project_name = 'per-channel'
parameters.description = 'Calculate state perturbations in a number of local channels'
parameters.working_directory = utilities.path_relative_to(__file__, 'PER-CHANNEL')
parameters.model = model
parameters.time_stamp = '01/01/2018 06:00:00'
model.state[state_parameters.fuel_temperature] = 30 * units.degC
model.state[state_parameters.moderator_temperature] = 30 * units.degC
model.state[state_parameters.moderator_density] = 0.998 * units.g/units.cc
channels = ['A1', 'A2', 'B1', 'C3']
pert = [state_parameters.moderator_temperature(20 * units.degC),
state_parameters.moderator_temperature(40 * units.degC)]
# global perturbation
parameters.add_sequence('mt', *pert)
# channel specific perturbations
for channel in channels:
for p in pert:
m = model.layout.new()
m[channel] = p
parameters.add_sequence(f'{channel}-mt', m)
if __name__ == '__main__':
app.main(parameters)
The final example employs a custom perturbation to model a rig drop.
import applications.feedback_coefficients as app
from ..configurations.my_config import model
from ..model import assemblies
from core import utilities, geometry, units, state_parameters
parameters = app.parameters()
parameters.project_name = 'rig-drop'
parameters.description = 'Calculate reactivity perturbations as a rig drops'
parameters.working_directory = utilities.path_relative_to(__file__, 'RIG-DROP')
parameters.model = model
parameters.time_stamp = '01/01/2018 06:00:00'
model.state[state_parameters.fuel_temperature] = 30 * units.degC
model.state[state_parameters.moderator_temperature] = 30 * units.degC
model.state[state_parameters.moderator_density] = 0.998 * units.g/units.cc
class DropRig(app.Custom):
@classmethod
def base():
return 50 * units.cm # completely outside the core
def delta():
return self.pos
def __init__(pos)
self.pos = pos
def __call__(param)
rig = assemblies.my_rig()
rig.add_transformation(geometry.translation(z=self.pos)
# load into position C5
param.model.core_map['C5'].load_facility('my-facility', rig)
for pos in [40 * units.cm, 30 * units.cm, 20 * units.cm, 10 * units.cm, \
5 * units.cm, 0.0 * units.cm, -10 * units.cm]:
parameters.add_sequence('rig-drop', DropRig(pos))
if __name__ == '__main__':
app.main(parameters)