Guide for adding Variables & Constraints
Specific instructions must be followed to add an input, iteration variable,
optimisation figure of merit and constraints to the PROCESS code.
At all times the PROCESS style guide must be used.
Note
As the code is quickly converging towards a wholly Python codebase the respective files may change in type from .f90 to .py.
Add an input
To add a PROCESS input, please follow below:
-
Choose the most relevant module
XXand add the variable in theXX_variablesdefined inXX_variables.f90. -
Add a description of the input variable below the declaration, using the FORD formatting described in the standards section specifying the units.
-
Specify a sensible default value in the
init_XX_variables()function within the corresponding model.pymain file -
Add the parameter to the
INPUT_VARIABLESdictionary ininput.py.
Here is an example of the code to add:
Variable definition example in tfcoil_variables.f90:
real(dp) :: rho_tf_joints
!! TF joints surfacic resistivity [ohm.m]
!! Feldmetal joints assumed.
Variable initialization example in tf_coil.py:
def init_tfcoil_variables():
...
tfv.rho_tf_joints = 2.5e-10
Code example in the input.py file:
INPUT_VARIABLES = {
...
"rho_tf_joints": InputVariable(data_structure.tfcoil_variables, float, range=(0.0, 0.01)),
Add an iteration variable
To add a PROCESS iteration variable please follow the steps below, in addition to the instructions for adding an input variable:
- The parameter
ipnvarsin modulenumericsofnumerics.f90will normally be greater than the actual number of iteration variables, and does not need to be changed. - Append a new iteration number key to the end of the
ITERATION_VARIABLESdictionary initeration_variables.py. The associated variable is the corresponding key value. - Set the variable origin file and then the associated lower and upper bounds
- Update the
lablxcderscription innumerics.f90.
It should be noted that iteration variables must not be reset elsewhere in the code. That is, they may only be assigned new values when originally initialised (in the relevant module, or in the input file if required). Otherwise, the numerical procedure cannot adjust the value as it requires, and the program will fail.
Here is a code snippet showing how rmajor is defined in iteration_variables.py
ITERATION_VARIABLES = {
...
3: IterationVariable("rmajor", fortran.physics_variables, 0.1, 50.00),
Add a figure of merit
New figures of merit are added to PROCESS in the following way:
-
Increment the parameter
ipnfomsin modulenumericsin source filenumerics.f90to accommodate the new figure of merit. -
Assign a description of the new figure of merit to the relevant element of array
lablmmin modulenumericsin the source filenumerics.f90. -
Add the new figure of merit equation to
objective_function()inobjectives.py, following the method used in the existing examples. The value of figure of merit case should be of order unity, so select a reasonable scaling factor if necessary. -
Add the new figure of merit description to the
OBJECTIVES_NAMESdictionary inobjectives.py
An example can be found below:
objective_function():
...
match figure_of_merit:
...
case 1:
objective_metric = 0.2 * physics_variables.rmajor
Add a scan variable
After following the instruction to add an input variable, you can make the variable a scan variable by following these steps:
-
Increment the parameter
IPNSCNVdefined inscan_variables.pyin the data_structure directory, to accommodate the new scanning variable. The incremented value will identify your scan variable. -
Add a short description of the new scanning variable in the
nsweepcomment inscan_variables.py, alongside its identification number. -
Update the
SCAN_VARIABLESdictionary in thescan.pyfile by adding a new case statement connecting the variable to the scan integer switch, the variable name and a short description. -
Add a comment in the corresponding variable file in the data_structure directory, eg,
data_structure/[XX]_variables.py, to add the variable description indicating the scan switch number.
nsweep comment example:
integer :: nsweep = 1
!! nsweep /1/ : switch denoting quantity to scan:<UL>
!! <LI> 1 aspect
!! <LI> 2 pflux_div_heat_load_max_mw
...
!! <LI> 54 GL_nbti upper critical field at 0 Kelvin
!! <LI> 55 `dr_shld_inboard` : Inboard neutron shield thickness </UL>
SCAN_VARIABLES case example:
SCAN_VARIABLES = {
1: ScanVariable("aspect", "Aspect_ratio"),
2: ScanVariable("pflux_div_heat_load_max_mw", "Div_heat_limit_(MW/m2)"),
...
54: ScanVariable("Bc2(0K)", "GL_NbTi Bc2(0K)"),
55: ScanVariable("dr_shld_inboard", "Inboard neutronic shield"),
Add a constraint equation
Constraint equations are added to PROCESS in the process/constraints.py file. They are registered with the ConstraintManager whenever the application is run. Each equation has a unique name that is currently an integer, however upgrades to the input file format in the future will allow arbitrary hashable constraint names.
A constraint is simply added by registering the constraint to the manager using a decorator.
@ConstraintManager.register_constraint(1234, "m", "=")
def my_constraint_function(): ...
register_constraint function are:
- Name (again, currently an integer)
- Unit (for output reporting purposes)
- Symbol (e.g. =, >=, <=. Again, for output reporting purposes)
my_constraint_function should be named appropriately and return a ConstraintResult which contains the:
- Normalised residual error
- Constraint value
- Constraint error
@ConstraintManager.register_constraint(1234, "m", "=")
def my_constraint_function():
normalised_residual = ...
value = ...
error = ...
return ConstraintResult(normalised_residual, value, error)