Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 7 additions & 24 deletions documentation/source/development/add-vars.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,36 +105,19 @@ objective_function():

After following the instruction to add an input variable, you can make the variable a scan variable by following these steps:

1. Increment the parameter `IPNSCNV` defined in `scan_variables.py` in the data_structure directory, to accommodate the new scanning variable. The incremented value will identify your scan variable.
1. Update the `ScanVariables` enum in the `scan.py` file by adding a new case statement connecting the variable to the scan integer switch, the variable name and a short description.

2. Add a short description of the new scanning variable in the `nsweep` comment in `scan_variables.py`, alongside its identification number.

3. Update the `ScanVariables` enum in the `scan.py` file by adding a new case statement connecting the variable to the scan integer switch, the variable name and a short description.

4. 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:
```fortran

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>
```
2. 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.

`SCAN_VARIABLES` case example:

```python
class ScanVariables(Enum):
aspect: ScanVariable("aspect", "Aspect_ratio", 1),
pflux_div_heat_load_max_mw: ScanVariable("pflux_div_heat_load_max_mw", "Div_heat_limit_(MW/m2)", 2),
class ScanVariables(ScanVariable, Enum):
aspect = (1, SVE.P),
pflux_div_heat_load_max_mw = (2, SVE.D)
...
Bc2_0K: ScanVariable("Bc2(0K)", "GL_NbTi Bc2(0K)", 54),
dr_shld_inboard : ScanVariable("dr_shld_inboard", "Inboard neutronic shield", 55),
b_crit_upper_nbti = (54, SVE.T)
dr_shld_inboard = (55, SVE.B),
```

---------------
Expand Down
100 changes: 99 additions & 1 deletion process/core/caller.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
from tabulate import tabulate

from process.core import constants
from process.core.final import finalise
from process.core import process_output as po
from process.core.io.mfile import MFile
from process.core.process_output import OutputFileManager, ovarre
from process.core.solver import constraints
from process.core.solver.iteration_variables import set_scaled_iteration_variable
from process.core.solver.objectives import objective_function
from process.data_structure.blanket_variables import BlktModelTypes
from process.data_structure.numerics import PROCESSRunMode
from process.models.tfcoil.base import TFConductorModel
from process.models.tfcoil.superconducting import SuperconductingTFTurnType

Expand Down Expand Up @@ -394,6 +395,103 @@ def _call_models_once(self, xc: np.ndarray):
# FISPACT and LOCA model (not used)- removed


def finalise(models, data, ifail: int, non_idempotent_msg: str | None = None):
"""Routine to print out the final point in the scan.

Writes to OUT.DAT and MFILE.DAT.

Parameters
----------
models : process.main.Models
physics and engineering model objects
data: DataStructure
data structure object to provide data to evaluate the constraints
ifail : int
error flag
non_idempotent_msg : None | str, optional
warning about non-idempotent variables, defaults to None
"""
if ifail == 1:
po.oheadr(constants.NOUT, "Final Feasible Point")
else:
po.oheadr(constants.NOUT, "Final UNFEASIBLE Point")

# Output relevant to no optimisation
if data.numerics.ioptimz == PROCESSRunMode.EVALUATION:
output_evaluation(data)

# Print non-idempotence warning to OUT.DAT only
if non_idempotent_msg:
po.oheadr(constants.NOUT, "NON-IDEMPOTENT VARIABLES")
po.ocmmnt(constants.NOUT, non_idempotent_msg)

# Write output to OUT.DAT and MFILE.DAT
models.write(data, constants.NOUT)


def output_evaluation(data):
"""Write output for an evaluation run of PROCESS

Parameters
----------
data: DataStructure
data structure object to provide data to evaluate the constraints
"""
po.oheadr(constants.NOUT, "Numerics")
po.ocmmnt(constants.NOUT, "PROCESS has performed an evaluation run.")
po.oblnkl(constants.NOUT)

# Evaluate objective function
norm_objf = objective_function(data.numerics.minmax, data)
po.ovarre(constants.MFILE, "Normalised objective function", "(norm_objf)", norm_objf)

# Print the residuals of the constraint equations

residual_error, value, residual, symbols, units = constraints.constraint_eqns(
data.numerics.neqns + data.numerics.nineqns, -1, data
)

labels = [
data.numerics.lablcc[j - 1]
for j in data.numerics.icc[: data.numerics.neqns + data.numerics.nineqns]
]

def _fmt(a, units):
return [f"{c} {u}" for c, u in zip(a, units, strict=False)]

po.write(
constants.NOUT,
tabulate(
{
"Constraint Name": labels,
"Constraint Type": symbols,
"Physical constraint": _fmt(value, units),
"Constraint residual": _fmt(residual, units),
"Normalised residual": residual_error,
},
headers="keys",
),
)

for i in range(data.numerics.neqns):
constraint_id = data.numerics.icc[i]
po.ovarre(
constants.MFILE,
f"{labels[i]} normalised residue",
f"(eq_con{constraint_id:03d})",
residual_error[i],
)

for i in range(data.numerics.nineqns):
constraint_id = data.numerics.icc[data.numerics.neqns + i]
po.ovarre(
constants.MFILE,
f"{labels[data.numerics.neqns + i]}",
f"(ineq_con{constraint_id:03d})",
residual_error[data.numerics.neqns + i],
)


def write_output_files(
models: Models, data: DataStructure, ifail: int, *, runtime: float | None = None
):
Expand Down
105 changes: 0 additions & 105 deletions process/core/final.py
Original file line number Diff line number Diff line change
@@ -1,105 +0,0 @@
"""Final output at the end of a scan."""

from tabulate import tabulate

from process.core import constants
from process.core import output as op
from process.core import process_output as po
from process.core.solver import constraints
from process.core.solver.objectives import objective_function
from process.data_structure.numerics import PROCESSRunMode


def finalise(models, data, ifail: int, non_idempotent_msg: str | None = None):
"""Routine to print out the final point in the scan.

Writes to OUT.DAT and MFILE.DAT.

Parameters
----------
models : process.main.Models
physics and engineering model objects
data: DataStructure
data structure object to provide data to evaluate the constraints
ifail : int
error flag
non_idempotent_msg : None | str, optional
warning about non-idempotent variables, defaults to None
"""
if ifail == 1:
po.oheadr(constants.NOUT, "Final Feasible Point")
else:
po.oheadr(constants.NOUT, "Final UNFEASIBLE Point")

# Output relevant to no optimisation
if data.numerics.ioptimz == PROCESSRunMode.EVALUATION:
output_evaluation(data)

# Print non-idempotence warning to OUT.DAT only
if non_idempotent_msg:
po.oheadr(constants.NOUT, "NON-IDEMPOTENT VARIABLES")
po.ocmmnt(constants.NOUT, non_idempotent_msg)

# Write output to OUT.DAT and MFILE.DAT
op.write(models, data, constants.NOUT)


def output_evaluation(data):
"""Write output for an evaluation run of PROCESS

Parameters
----------
data: DataStructure
data structure object to provide data to evaluate the constraints
"""
po.oheadr(constants.NOUT, "Numerics")
po.ocmmnt(constants.NOUT, "PROCESS has performed an evaluation run.")
po.oblnkl(constants.NOUT)

# Evaluate objective function
norm_objf = objective_function(data.numerics.minmax, data)
po.ovarre(constants.MFILE, "Normalised objective function", "(norm_objf)", norm_objf)

# Print the residuals of the constraint equations

residual_error, value, residual, symbols, units = constraints.constraint_eqns(
data.numerics.neqns + data.numerics.nineqns, -1, data
)

labels = [
data.numerics.lablcc[j]
for j in [
i - 1
for i in data.numerics.icc[: data.numerics.neqns + data.numerics.nineqns]
]
]
physical_constraint = [f"{c} {u}" for c, u in zip(value, units, strict=False)]
physical_residual = [f"{c} {u}" for c, u in zip(residual, units, strict=False)]

table_data = {
"Constraint Name": labels,
"Constraint Type": symbols,
"Physical constraint": physical_constraint,
"Constraint residual": physical_residual,
"Normalised residual": residual_error,
}

po.write(constants.NOUT, tabulate(table_data, headers="keys"))

for i in range(data.numerics.neqns):
constraint_id = data.numerics.icc[i]
po.ovarre(
constants.MFILE,
f"{labels[i]} normalised residue",
f"(eq_con{constraint_id:03d})",
residual_error[i],
)

for i in range(data.numerics.nineqns):
constraint_id = data.numerics.icc[data.numerics.neqns + i]
po.ovarre(
constants.MFILE,
f"{labels[data.numerics.neqns + i]}",
f"(ineq_con{constraint_id:03d})",
residual_error[data.numerics.neqns + i],
)
13 changes: 2 additions & 11 deletions process/core/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -1099,24 +1099,15 @@ def __post_init__(self):
"scan",
int,
choices=range(IPNSCNS + 1),
array=True,
),
"nsweep": InputVariable(
"scan",
int,
choices=range(1, IPNSCNV + 1),
),
"isweep_2": InputVariable(
"scan",
int,
choices=range(IPNSCNS + 1),
),
"nsweep_2": InputVariable(
"scan",
int,
choices=range(1, IPNSCNV + 1),
array=True,
),
"sweep": InputVariable("scan", float, array=True),
"sweep_2": InputVariable("scan", float, array=True),
"impvardiv": InputVariable(
"reinke",
int,
Expand Down
4 changes: 2 additions & 2 deletions process/core/io/plot/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,10 @@ def plot_scans_cli(
list(map(float, filter(None, x_axis_max))),
list(map(float, filter(None, x_axis_range))),
y_axis_percent,
y_axis_percent2,
list(map(float, filter(None, y_axis_max))),
list(map(float, filter(None, y_axis2_max))),
list(map(float, filter(None, y_axis_range))),
y_axis_percent2,
list(map(float, filter(None, y_axis2_max))),
list(map(float, filter(None, y_axis_range2))),
label_name,
twod_contour,
Expand Down
Loading