diff --git a/documentation/source/development/add-vars.md b/documentation/source/development/add-vars.md index 703a648e52..e88cb7da03 100644 --- a/documentation/source/development/add-vars.md +++ b/documentation/source/development/add-vars.md @@ -128,9 +128,9 @@ Note that you will want to scale the `objective_metric` such that it is of the o The figure of merit can be selected in the `IN.DAT`: ``` -minmax = 20 +i_figure_merit = 20 ``` -Remember, setting `minmax = -20` would minimise instead of maximise our new variable. +Remember, setting `i_figure_merit = -20` would minimise instead of maximise our new variable. ----------------- diff --git a/documentation/source/io/input-guide.md b/documentation/source/io/input-guide.md index b2eb4ee2a4..979dffe8be 100644 --- a/documentation/source/io/input-guide.md +++ b/documentation/source/io/input-guide.md @@ -111,19 +111,19 @@ If bounds are not specified default values are used. The user can select which solver to use, but only one solver is available at present (VMCON). ``` -ioptimz = 1 * for optimisation VMCON only +i_process_run_mode = 1 * for optimisation VMCON only ``` The user can select the figure of merit to be used: ``` -minmax = 1 * Switch for figure-of-merit (see `FiguresOfMerit` for descriptions) +i_figure_merit = 1 * Switch for figure-of-merit (see `FiguresOfMerit` for descriptions) ``` !!! Info "Figures of Merit" - The full list of valid `minmax` values is documented in the API reference for [`process.data_structure.numerics.FiguresOfMerit`](../../source/reference/process/data_structure/numerics/#process.data_structure.numerics.FiguresOfMerit). + The full list of valid `i_figure_merit` values is documented in the API reference for [`process.data_structure.numerics.FiguresOfMerit`](../../source/reference/process/data_structure/numerics/#process.data_structure.numerics.FiguresOfMerit). -In this case the user is choosing option `1`, which is major radius. For `minmax` +In this case the user is choosing option `1`, which is major radius. For `i_figure_merit` * a **positive** value means **minimise** the figure of merit * a **negative** value means **maximise** the figure of merit diff --git a/documentation/source/solver/solver-guide.md b/documentation/source/solver/solver-guide.md index 361cd43017..be891cce1a 100644 --- a/documentation/source/solver/solver-guide.md +++ b/documentation/source/solver/solver-guide.md @@ -15,7 +15,7 @@ A PROCESS input file will, for example, define which constraint equations are be ``` ... -neqns = 3 +n_equality_constraints = 3 * Equalities icc = 1 * Beta @@ -33,7 +33,7 @@ icc = 15 * LH power threshold limit Here each `icc=n` statement tells PROCESS to activate a constraint with the name `n`. A list of the constraints and their corresponding names can be found [here](../../source/reference/process/data_structure/numerics/#process.data_structure.numerics.lablcc). -The `neqns = 3` statement is telling PROCESS to treat the first `3` equations as equality constraints, and the rest as inequality constraints. Therefore, it is imperative that all equality constraints are stated before any inequality constraints. +The `n_equality_constraints = 3` statement is telling PROCESS to treat the first `3` equations as equality constraints, and the rest as inequality constraints. Therefore, it is imperative that all equality constraints are stated before any inequality constraints. In both types of equations, an optimiser/solver uses the normalised residuals $c_i$ of the constraints (and sometimes its gradient, depending on the solver/optimiser) to guide the solution towards one that satisfies all of the constraints. @@ -96,9 +96,9 @@ known as the figure of merit. Several possible figures of merit are available, all of which are in the source file `evaluators.f90`. -Switch `minmax` is used to control which figure of merit is to be used. If the -figure of merit is to be minimised, `minmax` should be **positive**, and if a -maximised figure of merit is desired, `minmax` should be **negative**. +Switch `i_figure_merit` is used to control which figure of merit is to be used. If the +figure of merit is to be minimised, `i_figure_merit` should be **positive**, and if a +maximised figure of merit is desired, `i_figure_merit` should be **negative**. ## Convergence @@ -106,9 +106,9 @@ maximised figure of merit is desired, `minmax` should be **negative**. ## Optimisation mode -Switch `ioptimz` should be set to 1 for optimisation mode. +Switch `i_process_run_mode` should be set to 1 for optimisation mode. -If `ioptimz = 0`, a non-optimisation pass is performed first. Occasionally this provides a feasible set of initial conditions that aids convergence of the optimiser, but it is recommended to use `ioptimz = 1`. +If `i_process_run_mode = 0`, a non-optimisation pass is performed first. Occasionally this provides a feasible set of initial conditions that aids convergence of the optimiser, but it is recommended to use `i_process_run_mode = 1`. Enable all the relevant consistency equations, and it is advisable to enable the corresponding iterations variables. A number of limit equations (inequality constraints) can also be activated. In optimisation mode, the number of iteration variables is unlimited. @@ -137,11 +137,11 @@ Running `PROCESS` in evaluation mode requires few changes to be made to the inpu As before, the user must decide which constraint equations and iteration variables to activate. For example, an extract from an input file might look like: ``` * Evaluation problem: evaluate models consistently by solving equality constraints only -ioptimz = -2 * evaluation mode +i_process_run_mode = -2 * evaluation mode *---------------Constraint Equations---------------* * Define number of equality constraints -neqns = 2 +n_equality_constraints = 2 * Equalities icc = 1 * Beta diff --git a/documentation/source/usage/running-process.md b/documentation/source/usage/running-process.md index c7c7143956..aee845060a 100644 --- a/documentation/source/usage/running-process.md +++ b/documentation/source/usage/running-process.md @@ -1,12 +1,12 @@ # Running PROCESS -There are a number of ways to run PROCESS. The first two are determined by the value of the switch `ioptimz` in the input file: +There are a number of ways to run PROCESS. The first two are determined by the value of the switch `i_process_run_mode` in the input file: -`ioptimz = -2` for evaluation. The physics and engineering models are evaluated and the equality (e.g. model consistency) constraints will be solved using `scipy`'s `fsolve`. The input file and default values together define the input values of all variables. The values of the parameters used to solve the equalities (solution parameters) will be different in the solution, however. This is used when evaluating a set of input parameters (e.g. a "point") whilst ensuring that the models are self-consistent. +`i_process_run_mode = -2` for evaluation. The physics and engineering models are evaluated and the equality (e.g. model consistency) constraints will be solved using `scipy`'s `fsolve`. The input file and default values together define the input values of all variables. The values of the parameters used to solve the equalities (solution parameters) will be different in the solution, however. This is used when evaluating a set of input parameters (e.g. a "point") whilst ensuring that the models are self-consistent. -`ioptimz = 1` for optimisation. Those variables specified as iteration variables (specified by equations such as `ixc = 1` in the input file) are automatically varied during the iteration process, between the bounds given by the arrays `boundl` (lower bounds) and `boundu` (upper bounds). The input file contains the *initial* values of the iteration variables, but the final values will not be same. The iteration process continues until convergence, or until the maximum number of iterations (`maxcal`) is reached. If the code converges, the constraints will be satisfied and the Figure of Merit will be maximised or minimised. +`i_process_run_mode = 1` for optimisation. Those variables specified as iteration variables (specified by equations such as `ixc = 1` in the input file) are automatically varied during the iteration process, between the bounds given by the arrays `boundl` (lower bounds) and `boundu` (upper bounds). The input file contains the *initial* values of the iteration variables, but the final values will not be same. The iteration process continues until convergence, or until the maximum number of iterations (`maxcal`) is reached. If the code converges, the constraints will be satisfied and the Figure of Merit will be maximised or minimised. -If the optimisation fails to converge, a third option is available by using the command line option `-v` (VaryRun), together with a configuration file. PROCESS is run repeatedly in optimisation mode (`ioptimz` = 1 must be set), but a new input file is written each time, with different, randomly selected *initial* values of the iteration variables. The factor within which the initial values of the iteration variables are changed is `FACTOR` (in the configuration file). For example, `FACTOR = 1.1` will vary the initial values randomly by up to 10%. This is repeated until PROCESS converges, or until the maximum number of PROCESS runs (`NITER`) is reached. Sometimes this procedure will generate a converged solution when a single optimisation run does not. +If the optimisation fails to converge, a third option is available by using the command line option `-v` (VaryRun), together with a configuration file. PROCESS is run repeatedly in optimisation mode (`i_process_run_mode` = 1 must be set), but a new input file is written each time, with different, randomly selected *initial* values of the iteration variables. The factor within which the initial values of the iteration variables are changed is `FACTOR` (in the configuration file). For example, `FACTOR = 1.1` will vary the initial values randomly by up to 10%. This is repeated until PROCESS converges, or until the maximum number of PROCESS runs (`NITER`) is reached. Sometimes this procedure will generate a converged solution when a single optimisation run does not. A SCAN is available in any of these modes. One input variable can be scanned (`scan_dim = 1`) or two input variables (`scan_dim = 2`). A scan variable must not be an iteration variable. For details, see ScanVariables in the [scan module](../../source/reference/process/scan). diff --git a/documentation/source/usage/troubleshooting.md b/documentation/source/usage/troubleshooting.md index b9805e2c54..8ef92e0e75 100644 --- a/documentation/source/usage/troubleshooting.md +++ b/documentation/source/usage/troubleshooting.md @@ -44,9 +44,9 @@ necessity brief, and so cannot promise to lead to a more successful outcome. ### Optimisation problems On reflection it is perhaps surprising that PROCESS ever does manage to find the global minimum -figure of merit value, if there are `nvar` iteration variables active the search is -over `nvar`-dimensional parameter space, in which there may be many shallow minima of approximately -equal depth. Remember that `nvar` is usually of the order of twenty. +figure of merit value, if there are `n_iteration_variables` iteration variables active the search is +over `n_iteration_variables`-dimensional parameter space, in which there may be many shallow minima of approximately +equal depth. Remember that `n_iteration_variables` is usually of the order of twenty. The machine found by PROCESS may not, therefore, be the absolute optimal device. It is quite easy to have two or more solutions, with results only a few percent different, but a long way apart in diff --git a/examples/data/large_tokamak_IN.DAT b/examples/data/large_tokamak_IN.DAT index 33ca94ec74..38f47a3088 100644 --- a/examples/data/large_tokamak_IN.DAT +++ b/examples/data/large_tokamak_IN.DAT @@ -12,12 +12,12 @@ runtitle = Generic large tokamak * Figure of merit - minimise major radius -minmax = 1 +i_figure_merit = 1 * Error tolerance for VMCON epsvmc = 1e-7 -neqns = 3 +n_equality_constraints = 3 * Constraint Equations - Consistency Equations * ************************************************ diff --git a/examples/data/large_tokamak_eval_IN.DAT b/examples/data/large_tokamak_eval_IN.DAT index 80b3369813..f92b56ea19 100644 --- a/examples/data/large_tokamak_eval_IN.DAT +++ b/examples/data/large_tokamak_eval_IN.DAT @@ -1,9 +1,9 @@ * Evaluation problem: evaluate models consistently by solving equality constraints only -ioptimz = -2 +i_process_run_mode = -2 *---------------Constraint Equations---------------* * Define number of equality constraints -neqns = 2 +n_equality_constraints = 2 * Equalities icc = 1 * Beta diff --git a/examples/data/large_tokamak_varied_min_net_electric_IN.DAT b/examples/data/large_tokamak_varied_min_net_electric_IN.DAT index 1242997d88..0e6abd3b44 100644 --- a/examples/data/large_tokamak_varied_min_net_electric_IN.DAT +++ b/examples/data/large_tokamak_varied_min_net_electric_IN.DAT @@ -12,12 +12,12 @@ runtitle = Generic large tokamak * Figure of merit - minimise major radius -minmax = 1 +i_figure_merit = 1 * Error tolerance for VMCON epsvmc = 1e-7 -neqns = 3 +n_equality_constraints = 3 * Constraint Equations - Consistency Equations * ************************************************ diff --git a/examples/data/large_tokamak_varyrun_IN.DAT b/examples/data/large_tokamak_varyrun_IN.DAT index 1e93cb8cd7..9f0c33f3eb 100644 --- a/examples/data/large_tokamak_varyrun_IN.DAT +++ b/examples/data/large_tokamak_varyrun_IN.DAT @@ -12,12 +12,12 @@ runtitle = Generic large tokamak * Figure of merit - minimise major radius -minmax = 1 +i_figure_merit = 1 * Error tolerance for VMCON epsvmc = 1e-7 -neqns = 3 +n_equality_constraints = 3 * Constraint Equations - Consistency Equations * ************************************************ diff --git a/examples/data/mfile_to_csv_vars.json b/examples/data/mfile_to_csv_vars.json index 4a7bf41304..6192ea99a8 100644 --- a/examples/data/mfile_to_csv_vars.json +++ b/examples/data/mfile_to_csv_vars.json @@ -1,6 +1,6 @@ { "vars": [ - "minmax", + "i_figure_merit", "p_hcd_injected_max", "p_plant_electric_net_required_mw", "ripple_b_tf_plasma_edge_max", diff --git a/examples/data/scan_example_file_IN.DAT b/examples/data/scan_example_file_IN.DAT index e34e1708c3..98ddfa091d 100644 --- a/examples/data/scan_example_file_IN.DAT +++ b/examples/data/scan_example_file_IN.DAT @@ -12,7 +12,7 @@ runtitle = Generic large tokamak * Figure of merit - minimise major radius -minmax = 1 +i_figure_merit = 1 * Error tolerance for VMCON epsvmc = 1e-7 @@ -20,7 +20,7 @@ epsvmc = 1e-7 * Inequality constraint tolerance force_vmcon_inequality_tolerance = 1e-6 -neqns = 3 +n_equality_constraints = 3 * Scan Variables * ****************** diff --git a/process/core/caller.py b/process/core/caller.py index 1634777e53..c35a48375e 100644 --- a/process/core/caller.py +++ b/process/core/caller.py @@ -100,7 +100,7 @@ def call_models(self, xc: np.ndarray, m: int) -> tuple[float, np.ndarray]: for _ in range(10): self._call_models_once(xc) # Evaluate objective function and constraints - objf = objective_function(self.data.numerics.minmax, self.data) + objf = objective_function(self.data.numerics.i_figure_merit, self.data) conf, _, _, _, _ = constraints.constraint_eqns(m, -1, self.data) if objf_prev is None and conf_prev is None: @@ -262,7 +262,7 @@ def _call_models_once(self, xc: np.ndarray): nvars = len(xc) # Increment the call counter - self.data.numerics.ncalls += 1 + self.data.numerics.n_model_calls += 1 # Convert variables set_scaled_iteration_variable(xc, nvars, self.data) @@ -419,7 +419,7 @@ def finalise(models, data, ifail: int, non_idempotent_msg: str | None = None): po.oheadr(constants.NOUT, "Final UNFEASIBLE Point") # Output relevant to no optimisation - if data.numerics.ioptimz == PROCESSRunMode.EVALUATION: + if data.numerics.i_process_run_mode == PROCESSRunMode.EVALUATION: output_evaluation(data) # Print non-idempotence warning to OUT.DAT only @@ -444,18 +444,23 @@ def output_evaluation(data): po.oblnkl(constants.NOUT) # Evaluate objective function - norm_objf = objective_function(data.numerics.minmax, data) + norm_objf = objective_function(data.numerics.i_figure_merit, 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 + data.numerics.n_equality_constraints + data.numerics.n_inequality_constraints, + -1, + data, ) labels = [ data.numerics.lablcc[j - 1] - for j in data.numerics.icc[: data.numerics.neqns + data.numerics.nineqns] + for j in data.numerics.icc[ + : data.numerics.n_equality_constraints + + data.numerics.n_inequality_constraints + ] ] def _fmt(a, units): @@ -475,7 +480,7 @@ def _fmt(a, units): ), ) - for i in range(data.numerics.neqns): + for i in range(data.numerics.n_equality_constraints): constraint_id = data.numerics.icc[i] po.ovarre( constants.MFILE, @@ -484,13 +489,13 @@ def _fmt(a, units): residual_error[i], ) - for i in range(data.numerics.nineqns): - constraint_id = data.numerics.icc[data.numerics.neqns + i] + for i in range(data.numerics.n_inequality_constraints): + constraint_id = data.numerics.icc[data.numerics.n_equality_constraints + i] po.ovarre( constants.MFILE, - f"{labels[data.numerics.neqns + i]}", + f"{labels[data.numerics.n_equality_constraints + i]}", f"(ineq_con{constraint_id:03d})", - residual_error[data.numerics.neqns + i], + residual_error[data.numerics.n_equality_constraints + i], ) @@ -508,7 +513,7 @@ def write_output_files( ifail : int solver return code """ - n = data.numerics.nvar + n = data.numerics.n_iteration_variables x = data.numerics.xcm[:n] # Call models, ensuring output mfiles are fully idempotent caller = Caller(models, data) diff --git a/process/core/init.py b/process/core/init.py index 1b7e6dc508..7704fd05e8 100644 --- a/process/core/init.py +++ b/process/core/init.py @@ -174,35 +174,41 @@ def run_summary(data: DataStructure): process_output.ostars(outfile, 110) process_output.oblnkl(outfile) - process_output.ocmmnt(outfile, f"Equality constraints : {data.numerics.neqns}") + process_output.ocmmnt( + outfile, f"Equality constraints : {data.numerics.n_equality_constraints}" + ) process_output.ocmmnt( outfile, - f"Inequality constraints : {data.numerics.nineqns}", + f"Inequality constraints : {data.numerics.n_inequality_constraints}", ) process_output.ocmmnt( outfile, - f"Total constraints : {data.numerics.nineqns + data.numerics.neqns}", + f"Total constraints : " + f"{data.numerics.n_inequality_constraints + data.numerics.n_equality_constraints}", # noqa: E501 + ) + process_output.ocmmnt( + outfile, f"Iteration variables : {data.numerics.n_iteration_variables}" ) - process_output.ocmmnt(outfile, f"Iteration variables : {data.numerics.nvar}") # If optimising, write objective function and convergence parameter - if data.numerics.ioptimz == PROCESSRunMode.OPTIMISATION: + if data.numerics.i_process_run_mode == PROCESSRunMode.OPTIMISATION: process_output.ocmmnt( outfile, f"Max iterations : {data.globals.maxcal}", ) - if data.numerics.minmax > 0: - minmax_string = " -- minimise " - minmax_sign = "+" + if data.numerics.i_figure_merit > 0: + figure_merit_string = " -- minimise " + figure_merit_sign = "+" else: - minmax_string = " -- maximise " - minmax_sign = "-" + figure_merit_string = " -- maximise " + figure_merit_sign = "-" - fom_string = FiguresOfMerit(abs(data.numerics.minmax)).description + fom_string = FiguresOfMerit(abs(data.numerics.i_figure_merit)).description process_output.ocmmnt( outfile, - f"Figure of merit : {minmax_sign}{abs(data.numerics.minmax)}" - f"{minmax_string}{fom_string}", + f"Figure of merit : " + f"{figure_merit_sign}{abs(data.numerics.i_figure_merit)}" + f"{figure_merit_string}{fom_string}", ) process_output.ocmmnt( outfile, @@ -227,12 +233,18 @@ def run_summary(data: DataStructure): process_output.ovarre(mfile, "Input filename", "(fileprefix)", f'"{fileprefix}"') process_output.ovarre( - mfile, "Optimisation switch", "(ioptimz)", data.numerics.ioptimz + mfile, + "Optimisation switch", + "(i_process_run_mode)", + data.numerics.i_process_run_mode, ) # If optimising, write figure of merit switch - if data.numerics.ioptimz == PROCESSRunMode.OPTIMISATION: + if data.numerics.i_process_run_mode == PROCESSRunMode.OPTIMISATION: process_output.ovarre( - mfile, "Figure of merit switch", "(minmax)", data.numerics.minmax + mfile, + "Figure of merit switch", + "(i_figure_merit)", + data.numerics.i_figure_merit, ) @@ -250,25 +262,26 @@ def check_process(inputs, data): # noqa: ARG001 See individual ProcessValidationError instances for more details. """ # Check that there are sufficient iteration variables - if data.numerics.nvar < data.numerics.neqns: + if data.numerics.n_iteration_variables < data.numerics.n_equality_constraints: raise ProcessValidationError( - "Insufficient iteration variables to solve the problem! NVAR < NEQNS", - nvar=data.numerics.nvar, - neqns=data.numerics.neqns, + "Insufficient iteration variables to solve the problem! " + "n_iteration_variables < n_equality_constraints", + n_iteration_variables=data.numerics.n_iteration_variables, + n_equality_constraints=data.numerics.n_equality_constraints, ) # Check that sufficient elements of ixc and icc have been specified - if (data.numerics.ixc[: data.numerics.nvar] == 0).any(): + if (data.numerics.ixc[: data.numerics.n_iteration_variables] == 0).any(): raise ProcessValidationError( "The number of iteration variables specified is smaller than the number" " stated in ixc", - nvar=data.numerics.nvar, + n_iteration_variables=data.numerics.n_iteration_variables, ) # Check that dr_tf_wp_with_insulation (ixc = 140) and dr_tf_inboard (ixc = 13) # are not being used simultaneously as iteration variables - if (data.numerics.ixc[: data.numerics.nvar] == 13).any() and ( - data.numerics.ixc[: data.numerics.nvar] == 140 + if (data.numerics.ixc[: data.numerics.n_iteration_variables] == 13).any() and ( + data.numerics.ixc[: data.numerics.n_iteration_variables] == 140 ).any(): raise ProcessValidationError( "Iteration variables 13 and 140 cannot be used simultaneously", @@ -276,7 +289,9 @@ def check_process(inputs, data): # noqa: ARG001 # Can't use c_tf_turn as iteration var, constraint or # input if i_tf_turns_integer == 1 - if (data.numerics.ixc[: data.numerics.nvar] == 60).any() and TFWPIntegerTurnType( + if ( + data.numerics.ixc[: data.numerics.n_iteration_variables] == 60 + ).any() and TFWPIntegerTurnType( data.tfcoil.i_tf_turns_integer ) == TFWPIntegerTurnType.INTEGER: raise ProcessValidationError( @@ -287,26 +302,35 @@ def check_process(inputs, data): # noqa: ARG001 ) # Can't have icc 77 and ixc 60 at the same time - if (data.numerics.ixc[: data.numerics.nvar] == 60).any() and ( - data.numerics.icc[: data.numerics.nvar] == 77 + if (data.numerics.ixc[: data.numerics.n_iteration_variables] == 60).any() and ( + data.numerics.icc[: data.numerics.n_iteration_variables] == 77 ).any(): raise ProcessValidationError( "Cannot use iteration variable 60 (TF coil current per turn, c_tf_turn) and " "constraint 77 (maximum TF current per turn) simultaneously." ) - if (data.numerics.icc[: data.numerics.neqns + data.numerics.nineqns] == 0).any(): + if ( + data.numerics.icc[ + : data.numerics.n_equality_constraints + + data.numerics.n_inequality_constraints + ] + == 0 + ).any(): raise ProcessValidationError( "The number of constraints specified is smaller than the number stated" - " in neqns+nineqns", - neqns=data.numerics.neqns, - nineqns=data.numerics.nineqns, + " in n_equality_constraints+n_inequality_constraints", + n_equality_constraints=data.numerics.n_equality_constraints, + n_inequality_constraints=data.numerics.n_inequality_constraints, ) # Deprecate constraints for depcrecated_constraint in [3, 4, 10, 74, 42]: if ( - data.numerics.icc[: data.numerics.neqns + data.numerics.nineqns] + data.numerics.icc[ + : data.numerics.n_equality_constraints + + data.numerics.n_inequality_constraints + ] == depcrecated_constraint ).any(): raise ProcessValidationError( @@ -315,7 +339,11 @@ def check_process(inputs, data): # noqa: ARG001 # MDK Report error if constraint 63 is used with old vacuum model if ( - data.numerics.icc[: data.numerics.neqns + data.numerics.nineqns] == 63 + data.numerics.icc[ + : data.numerics.n_equality_constraints + + data.numerics.n_inequality_constraints + ] + == 63 ).any() and data.vacuum.i_vacuum_pumping != "simple": raise ProcessValidationError( "Constraint 63 is requested without the correct vacuum model (simple)" @@ -360,7 +388,7 @@ def check_process(inputs, data): # noqa: ARG001 # As the current density is now calculated from b_plasma_toroidal_on_axis # without constraint 10 - if (data.numerics.ixc[: data.numerics.nvar] == 12).any(): + if (data.numerics.ixc[: data.numerics.n_iteration_variables] == 12).any(): raise ProcessValidationError( "The 1/R toroidal B field dependency constraint is being depreciated" ) @@ -414,8 +442,8 @@ def check_process(inputs, data): # noqa: ARG001 ) if ( - data.numerics.ioptimz == PROCESSRunMode.OPTIMISATION - and (data.numerics.ixc[: data.numerics.nvar] == 4).any() + data.numerics.i_process_run_mode == PROCESSRunMode.OPTIMISATION + and (data.numerics.ixc[: data.numerics.n_iteration_variables] == 4).any() and data.numerics.boundl[3] < data.physics.temp_plasma_pedestal_kev * 1.001 ): logger.warning( @@ -488,12 +516,16 @@ def check_process(inputs, data): # noqa: ARG001 # (nd_plasma_electron_on_axis>nd_plasma_pedestal_electron) # -> Potential hollowed density profile if ( - data.numerics.ioptimz == PROCESSRunMode.OPTIMISATION + data.numerics.i_process_run_mode == PROCESSRunMode.OPTIMISATION and not ( - data.numerics.icc[: data.numerics.neqns + data.numerics.nineqns] == 81 + data.numerics.icc[ + : data.numerics.n_equality_constraints + + data.numerics.n_inequality_constraints + ] + == 81 ).any() ): - if (data.numerics.ixc[: data.numerics.nvar] == 145).any(): + if (data.numerics.ixc[: data.numerics.n_iteration_variables] == 145).any(): logger.warning( "nd_plasma_pedestal_electron set with" " f_nd_plasma_pedestal_greenwald" @@ -501,7 +533,7 @@ def check_process(inputs, data): # noqa: ARG001 " eq 81 (nd_plasma_pedestal_electron 50.0: raise ProcessValidationError( "Too large CP conductor temperature (temp_cp_average). Upper limit" @@ -681,7 +733,11 @@ def check_process(inputs, data): # noqa: ARG001 if ( data.tfcoil.i_tf_sup == TFConductorModel.HELIUM_COOLED_ALUMINIUM and ( - data.numerics.icc[: data.numerics.neqns + data.numerics.nineqns] == 85 + data.numerics.icc[ + : data.numerics.n_equality_constraints + + data.numerics.n_inequality_constraints + ] + == 85 ).any() and data.physics.itart == 1 ): @@ -702,7 +758,7 @@ def check_process(inputs, data): # noqa: ARG001 # Checking the CP TF top radius if ( abs(data.build.r_cp_top) > 0 - or (data.numerics.ixc[: data.numerics.nvar] == 174).any() + or (data.numerics.ixc[: data.numerics.n_iteration_variables] == 174).any() ) and data.build.i_r_cp_top != 1: raise ProcessValidationError( "To set the TF CP top value, you must use i_r_cp_top = 1" @@ -754,7 +810,11 @@ def check_process(inputs, data): # noqa: ARG001 # Constraint 10 is dedicated to ST designs with demountable joints if ( - data.numerics.icc[: data.numerics.neqns + data.numerics.nineqns] == 10 + data.numerics.icc[ + : data.numerics.n_equality_constraints + + data.numerics.n_inequality_constraints + ] + == 10 ).any(): raise ProcessValidationError( "Constraint equation 10 (CP lifetime) to used with ST desing (itart=1)" @@ -775,9 +835,9 @@ def check_process(inputs, data): # noqa: ARG001 if ( ( not ( - (data.numerics.ixc[: data.numerics.nvar] == 16).any() - or (data.numerics.ixc[: data.numerics.nvar] == 29).any() - or (data.numerics.ixc[: data.numerics.nvar] == 42).any() + (data.numerics.ixc[: data.numerics.n_iteration_variables] == 16).any() + or (data.numerics.ixc[: data.numerics.n_iteration_variables] == 29).any() + or (data.numerics.ixc[: data.numerics.n_iteration_variables] == 42).any() ) ) # No dr_bore,dr_cs_tf_gap, dr_cs iteration and ( @@ -791,10 +851,18 @@ def check_process(inputs, data): # noqa: ARG001 ) # dr_bore + dr_cs_tf_gap + dr_cs = 0 and ( ( - data.numerics.icc[: data.numerics.neqns + data.numerics.nineqns] == 31 + data.numerics.icc[ + : data.numerics.n_equality_constraints + + data.numerics.n_inequality_constraints + ] + == 31 ).any() or ( - data.numerics.icc[: data.numerics.neqns + data.numerics.nineqns] == 32 + data.numerics.icc[ + : data.numerics.n_equality_constraints + + data.numerics.n_inequality_constraints + ] + == 32 ).any() ) # Stress constraints (31 or 32) is used and ( @@ -969,7 +1037,7 @@ def check_process(inputs, data): # noqa: ARG001 # is large enough # To contains the insulation, cooling and the support structure # Rem : Only verified if the WP thickness is used - if (data.numerics.ixc[: data.numerics.nvar] == 140).any(): + if (data.numerics.ixc[: data.numerics.n_iteration_variables] == 140).any(): # Minimal WP thickness if data.tfcoil.i_tf_sup == TFConductorModel.SUPERCONDUCTING: dr_tf_wp_min = 2.0 * ( @@ -980,7 +1048,7 @@ def check_process(inputs, data): # noqa: ARG001 ) # Steel conduit thickness (can be an iteration variable) - if (data.numerics.ixc[: data.numerics.nvar] == 58).any(): + if (data.numerics.ixc[: data.numerics.n_iteration_variables] == 58).any(): dr_tf_wp_min += 2.0 * data.numerics.boundl[57] else: dr_tf_wp_min += 2.0 * data.tfcoil.dx_tf_turn_steel @@ -1166,7 +1234,11 @@ def check_process(inputs, data): # noqa: ARG001 # Cannot use temperature margin constraint with REBCO TF coils if ( - data.numerics.icc[: data.numerics.neqns + data.numerics.nineqns] == 36 + data.numerics.icc[ + : data.numerics.n_equality_constraints + + data.numerics.n_inequality_constraints + ] + == 36 ).any() and ( SuperconductorModel(data.tfcoil.i_tf_sc_mat).sc_type == SuperconductorMaterial.REBCO @@ -1177,7 +1249,11 @@ def check_process(inputs, data): # noqa: ARG001 # Cannot use temperature margin constraint with REBCO CS coils if ( - data.numerics.icc[: data.numerics.neqns + data.numerics.nineqns] == 60 + data.numerics.icc[ + : data.numerics.n_equality_constraints + + data.numerics.n_inequality_constraints + ] + == 60 ).any() and data.pf_coil.i_cs_superconductor == 8: raise ProcessValidationError( "turn off CS temperature margin constraint icc = 60 when using REBCO" @@ -1189,7 +1265,11 @@ def check_process(inputs, data): # noqa: ARG001 # Cannot use TF coil strain limit if i_str_wp is off: if ( - data.numerics.icc[: data.numerics.neqns + data.numerics.nineqns] == 88 + data.numerics.icc[ + : data.numerics.n_equality_constraints + + data.numerics.n_inequality_constraints + ] + == 88 ).any() and data.tfcoil.i_str_wp == 0: raise ProcessValidationError("Can't use constraint 88 if i_strain_tf == 0") @@ -1202,11 +1282,16 @@ def set_active_constraints(data: DataStructure): data.numerics.active_constraints[data.numerics.icc[i] - 1] = True num_constraints += 1 - if data.numerics.neqns < 0: - # The value of neqns has not been set in the input file. Default = 0. - data.numerics.neqns = num_constraints - data.numerics.nineqns + if data.numerics.n_equality_constraints < 0: + # The value of n_equality_constraints has not been set in the input file. + # Default = 0. + data.numerics.n_equality_constraints = ( + num_constraints - data.numerics.n_inequality_constraints + ) else: - data.numerics.nineqns = num_constraints - data.numerics.neqns + data.numerics.n_inequality_constraints = ( + num_constraints - data.numerics.n_equality_constraints + ) def set_device_type(data: DataStructure): diff --git a/process/core/input.py b/process/core/input.py index 93886b8a16..017247f206 100644 --- a/process/core/input.py +++ b/process/core/input.py @@ -15,7 +15,7 @@ ) from process.core.solver.constraints import ConstraintManager from process.data_structure.impurity_radiation_variables import N_IMPURITIES -from process.data_structure.numerics import IPNVARS +from process.data_structure.numerics import N_ITERATION_VARIABLES_MAX from process.data_structure.pfcoil_variables import N_PF_GROUPS_MAX from process.data_structure.physics_variables import N_CONFINEMENT_SCALINGS from process.data_structure.scan_variables import IPNSCNS, IPNSCNV @@ -37,8 +37,8 @@ def _ixc_additional_actions( _name, value: int, _array_index, _config, data: DataStructure ): - data.numerics.ixc[data.numerics.nvar] = value - data.numerics.nvar += 1 + data.numerics.ixc[data.numerics.n_iteration_variables] = value + data.numerics.n_iteration_variables += 1 def _icc_additional_actions( @@ -146,17 +146,17 @@ def bounds(self) -> tuple[NumberType | None, NumberType | None]: INPUT_VARIABLES = { "runtitle": InputVariable("globals", str), - "ioptimz": InputVariable("numerics", int, choices=[1, -2]), + "i_process_run_mode": InputVariable("numerics", int, choices=[1, -2]), "epsvmc": InputVariable("numerics", float, range=(0.0, 1.0)), "boundl": InputVariable("numerics", float, array=True), "boundu": InputVariable("numerics", float, array=True), "epsfcn": InputVariable("numerics", float, range=(0.0, 1.0)), "maxcal": InputVariable("globals", int, range=(0, 10000)), - "minmax": InputVariable("numerics", int), - "neqns": InputVariable( + "i_figure_merit": InputVariable("numerics", int), + "n_equality_constraints": InputVariable( "numerics", int, range=(0, ConstraintManager.num_constraints()) ), - "nineqns": InputVariable( + "n_inequality_constraints": InputVariable( "numerics", int, range=(0, ConstraintManager.num_constraints()) ), "alphaj": InputVariable("physics", float, range=(0.0, 10.0)), @@ -1160,7 +1160,7 @@ def bounds(self) -> tuple[NumberType | None, NumberType | None]: "ixc": InputVariable( None, int, - range=(1, IPNVARS), + range=(1, N_ITERATION_VARIABLES_MAX), additional_actions=_ixc_additional_actions, set_variable=False, ), @@ -1194,7 +1194,7 @@ def parse_input_file(data_structure_obj: DataStructure): """ # These get incremented when reading the file, so need # to ensure they are 0 before we parse the file - data_structure_obj.numerics.nvar = 0 + data_structure_obj.numerics.n_iteration_variables = 0 data_structure_obj.numerics.n_constraints = 0 input_file_path = ( diff --git a/process/core/io/in_dat/base.py b/process/core/io/in_dat/base.py index 32dc649e66..21be3d153c 100644 --- a/process/core/io/in_dat/base.py +++ b/process/core/io/in_dat/base.py @@ -385,8 +385,9 @@ def get_parameters(data, use_string_values=True): # dict of all module-level variables in source, grouped by module parameters = {} # dict of all parameters set in input file, grouped by module - # Include neqns to allow eq and ineq constraints to be defined in produced IN.DAT - exclusions = ["nvar", "icc", "ixc"] + # Include n_equality_constraints to allow eq and ineq constraints to be defined in + # produced IN.DAT + exclusions = ["n_iteration_variables", "icc", "ixc"] # Parameters to exclude # Change module keys from DICT_MODULE: replace spaces with underscores and @@ -421,14 +422,14 @@ def get_parameters(data, use_string_values=True): value = data["f_nd_impurity_electrons"].get_value[k] parameters[module][name] = value - elif item == "ioptimz": + elif item == "i_process_run_mode": name = item - ioptimz = {} - iop_val = data["ioptimz"].get_value + i_process_run_mode = {} + iop_val = data["i_process_run_mode"].get_value iop_comment = f"{PROCESSRunMode(iop_val).description}" - ioptimz["value"] = iop_val - ioptimz["comment"] = iop_comment - parameters[module][name] = ioptimz + i_process_run_mode["value"] = iop_val + i_process_run_mode["comment"] = iop_comment + parameters[module][name] = i_process_run_mode elif item == "zref": for j in range(len(data["zref"].get_value)): @@ -1269,7 +1270,7 @@ def process_constraint_equation(self, line): # Duplicate constraint equation number self.add_duplicate_variable(f"icc = {item}") # Don't sort the constraints! Preserves what's eq, what's ineq; - # first neqns are eqs, rest are ineqs + # first n_equality_constraints are eqs, rest are ineqs # self.data["icc"].value.sort() def process_iteration_variables(self, line): diff --git a/process/core/io/mfile/base.py b/process/core/io/mfile/base.py index 568a91e047..51aa59fd22 100644 --- a/process/core/io/mfile/base.py +++ b/process/core/io/mfile/base.py @@ -650,7 +650,7 @@ def get_mfile_initial_ixc_values(file_path: Path, data: DataStructure): iteration_variable_names = [] iteration_variable_values = [] - for i in range(data.numerics.nvar): + for i in range(data.numerics.n_iteration_variables): ivar = data.numerics.ixc[i].item() itv = iteration_variables.ITERATION_VARIABLES[ivar] diff --git a/process/core/io/obsolete_vars.py b/process/core/io/obsolete_vars.py index e096c85032..7227a4b773 100644 --- a/process/core/io/obsolete_vars.py +++ b/process/core/io/obsolete_vars.py @@ -474,6 +474,10 @@ "f_nd_alpha_electron": "f_nd_alpha_thermal_electron", "cost_model": "i_cost_model", "t_conductor": "dx_tf_turn_conduit_full_average", + "ioptimz": "i_process_run_mode", + "minmax": "i_figure_merit", + "neqns": "n_equality_constraints", + "nineqns": "n_inequality_constraints", } OBS_VARS_HELP = { diff --git a/process/core/io/plot/solutions.py b/process/core/io/plot/solutions.py index 34811f8f03..18260b96b2 100644 --- a/process/core/io/plot/solutions.py +++ b/process/core/io/plot/solutions.py @@ -141,7 +141,7 @@ def plot_mfile_solutions( filtered_results_df = _filter_vars_of_interest( results_df, opt_param_value_pattern=opt_param_value_pattern, - extra_var_names=["minmax"], + extra_var_names=["i_figure_merit"], ) if normalising_tag is not None: @@ -456,7 +456,7 @@ def _plot_solutions( # Acquire objective function name(s), then check only one type is being plotted # The objective function is found by first checking the NORM_OBJF_NAME column. # If this entry does not exist for any of the MFiles (ie they are old), - # then the code 'falls back' to the the minmax output. Which holds the same + # then the code 'falls back' to the the i_figure_merit output. Which holds the same # information, but is less descriptive. if ( NORM_OBJF_NAME in norm_objf_df.columns @@ -465,7 +465,8 @@ def _plot_solutions( objf_list = norm_objf_df[NORM_OBJF_NAME].unique() else: objf_list = { - FiguresOfMerit(abs(int(minmax))).description for minmax in diffs_df["minmax"] + FiguresOfMerit(abs(int(i_figure_merit))).description + for i_figure_merit in diffs_df["i_figure_merit"] } if len(objf_list) != 1: diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index eb95b14759..5c53ac2933 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -8234,9 +8234,9 @@ def plot_header(axis: plt.Axes, mfile: MFile, scan: int): (f"!{mfile.get('username', scan=-1)}", "User:", ""), ( ("!Evaluation", "Run type", "") - if isinstance(mfile.data["minmax"], MFileErrorClass) + if isinstance(mfile.data["i_figure_merit"], MFileErrorClass) else ( - f"!{FiguresOfMerit(abs(int(mfile.get('minmax', scan=-1)))).description}", + f"!{FiguresOfMerit(abs(int(mfile.get('i_figure_merit', scan=-1)))).description}", "Optimising:", "", ) @@ -10874,7 +10874,7 @@ def plot_iteration_variables(axis: plt.Axes, m_file: MFile, scan: int): """ # Get total number of iteration variables - n_itvars = int(m_file.get("nvar", scan=scan)) + n_itvars = int(m_file.get("n_iteration_variables", scan=scan)) y_labels = [] y_pos = [] @@ -12074,10 +12074,10 @@ def plot_cover_page( tagno = mfile.get("tagno", scan=-1) branch_name = mfile.get("branch_name", scan=-1) fileprefix = mfile.get("fileprefix", scan=-1) - optmisation_switch = int(mfile.get("ioptimz", scan=-1)) - minmax_switch = mfile.get("minmax", scan=-1) or "N/A" + optmisation_switch = int(mfile.get("i_process_run_mode", scan=-1)) + figure_merit_switch = mfile.get("i_figure_merit", scan=-1) or "N/A" ifail = mfile.get("ifail", scan=-1) - nvars = mfile.get("nvar", scan=-1) + nvars = mfile.get("n_iteration_variables", scan=-1) # Objective_function_name objf_name = mfile.get("objf_name", scan=-1) # Square_root_of_the_sum_of_squares_of_the_constraint_residuals @@ -12085,16 +12085,16 @@ def plot_cover_page( # VMCON_convergence_parameter convergence_parameter = mfile.get("convergence_parameter", scan=-1) or "N/A" # Number_of_optimising_solver_iterations - nviter = int(mfile.get("nviter", scan=-1)) or "N/A" + n_solver_iterations = int(mfile.get("n_solver_iterations", scan=-1)) or "N/A" # Objective name with minimising/maximising - if isinstance(minmax_switch, str): + if isinstance(figure_merit_switch, str): objective_text = "" - elif minmax_switch >= 0: - minmax_switch = int(minmax_switch) + elif figure_merit_switch >= 0: + figure_merit_switch = int(figure_merit_switch) objective_text = f" -> Minimising: {objf_name}" else: - minmax_switch = int(minmax_switch) + figure_merit_switch = int(figure_merit_switch) objective_text = f" -> Maximising: {objf_name}" axis.text( @@ -12157,13 +12157,13 @@ def plot_cover_page( settings_info = ( f"• Optimisation Switch: {int(optmisation_switch)}\n" f" {PROCESSRunMode(int(optmisation_switch)).description}\n" - f"• Figure of Merit Switch (minmax): {minmax_switch}\n" + f"• Figure of Merit Switch (i_figure_merit): {figure_merit_switch}\n" f" {objective_text}\n" f"• Fail Status (ifail): {int(ifail)}\n" f"• Number of Iteration Variables: {int(nvars)}\n" f"• Constraint Residuals (sqrt sum sq): {sqsumsq}\n" f"• Convergence Parameter: {convergence_parameter}\n" - f"• Solver Iterations: {nviter}\n" + f"• Solver Iterations: {n_solver_iterations}\n" f"• Runtime: {mfile.get('process_runtime', scan=-1):.6f} seconds" ) axis.text( diff --git a/process/core/io/vary_run/tools.py b/process/core/io/vary_run/tools.py index 8793ca5d34..396b617c17 100644 --- a/process/core/io/vary_run/tools.py +++ b/process/core/io/vary_run/tools.py @@ -297,7 +297,7 @@ def vary_iteration_variables(itervars, lbs, ubs, config: RunProcessConfig): return new_values -def get_solution_from_mfile(neqns, nvars, wdir=".", mfile="MFILE.DAT"): +def get_solution_from_mfile(n_equality_constraints, nvars, wdir=".", mfile="MFILE.DAT"): """Returns ifail - error_value of VMCON/PROCESS the objective functions @@ -319,10 +319,12 @@ def get_solution_from_mfile(neqns, nvars, wdir=".", mfile="MFILE.DAT"): constraints = m_file.get("sqsumsq") table_sol = [m_file.get(f"itvar{var_no + 1:03}") for var_no in range(nvars)] - table_res = [m_file.get(f"normres{con_no + 1:03}") for con_no in range(neqns)] + table_res = [ + m_file.get(f"normres{con_no + 1:03}") for con_no in range(n_equality_constraints) + ] if ifail != SolverOutputCondition.CONVERGED: - return ifail, "0", "0", ["0"] * nvars, ["0"] * neqns + return ifail, "0", "0", ["0"] * nvars, ["0"] * n_equality_constraints return ifail, objective_function, constraints, table_sol, table_res diff --git a/process/core/solver/constraints.py b/process/core/solver/constraints.py index c531a28be7..72c11014fd 100644 --- a/process/core/solver/constraints.py +++ b/process/core/solver/constraints.py @@ -2022,11 +2022,13 @@ def constraints_output(data: DataStructure, solver_name: str): "The following equality constraint residues should be close to zero:", ) - con1, con2, err, _, lab = constraint_eqns(nums.neqns + nums.nineqns, -1, data) + con1, con2, err, _, lab = constraint_eqns( + nums.n_equality_constraints + nums.n_inequality_constraints, -1, data + ) # Write equality constraints to mfile equality_constraint_table = [] - for i in range(nums.neqns): + for i in range(nums.n_equality_constraints): name = nums.lablcc[nums.icc[i] - 1] equality_constraint_table.append([ @@ -2062,7 +2064,7 @@ def constraints_output(data: DataStructure, solver_name: str): ) # Write inequality constraints - if nums.nineqns > 0: + if nums.n_inequality_constraints > 0: inequality_constraint_table = [] # Inequalities not necessarily satisfied when evaluating process_output.osubhd( @@ -2078,8 +2080,8 @@ def constraints_output(data: DataStructure, solver_name: str): ) for i in range( - nums.neqns, - nums.neqns + nums.nineqns, + nums.n_equality_constraints, + nums.n_equality_constraints + nums.n_inequality_constraints, ): name = nums.lablcc[nums.icc[i] - 1] constraint = ConstraintManager.evaluate_constraint(int(nums.icc[i]), data) diff --git a/process/core/solver/evaluators.py b/process/core/solver/evaluators.py index 588ef4da25..9a672e1884 100644 --- a/process/core/solver/evaluators.py +++ b/process/core/solver/evaluators.py @@ -69,9 +69,9 @@ def fcnvmc1(self, _n, m, xv, ifail): sqsumconfsq = math.sqrt(summ) logger.debug("Key evaluator values:") - logger.debug(f"{self.data.numerics.nviter = }") + logger.debug(f"{self.data.numerics.n_solver_iterations = }") logger.debug(f"{(1 - (ifail % 7)) - 1 = }") - logger.debug(f"{(self.data.numerics.nviter % 2) - 1 = }") + logger.debug(f"{(self.data.numerics.n_solver_iterations % 2) - 1 = }") logger.debug(f"{self.data.physics.temp_plasma_electron_vol_avg_kev = }") logger.debug(f"{self.data.costs.coe = }") logger.debug(f"{self.data.physics.rmajor = }") diff --git a/process/core/solver/iteration_variables.py b/process/core/solver/iteration_variables.py index c40f932ec1..d1df7c6feb 100644 --- a/process/core/solver/iteration_variables.py +++ b/process/core/solver/iteration_variables.py @@ -283,7 +283,7 @@ def load_iteration_variables(data): ProcessValueError If iteration variable is missing """ - for i in range(data.numerics.nvar): + for i in range(data.numerics.n_iteration_variables): variable_index = data.numerics.ixc[i] iteration_variable = ITERATION_VARIABLES[variable_index] @@ -405,7 +405,7 @@ def set_scaled_iteration_variable(xc, nn: int, data: DataStructure): def load_scaled_bounds(data: DataStructure): """Sets the scaled bounds of the iteration variables.""" - for i in range(data.numerics.nvar): + for i in range(data.numerics.n_iteration_variables): variable_index = data.numerics.ixc[i] - 1 data.numerics.itv_scaled_lower_bounds[i] = ( data.numerics.boundl[variable_index] * data.numerics.scale[i] diff --git a/process/core/solver/objectives.py b/process/core/solver/objectives.py index b0a46a7d79..bd99f5664c 100644 --- a/process/core/solver/objectives.py +++ b/process/core/solver/objectives.py @@ -8,12 +8,12 @@ from process.models.availability import AvailabilityModel -def objective_function(minmax: int, data: DataStructure) -> float: +def objective_function(i_figure_merit: int, data: DataStructure) -> float: """Calculate the specified objective function Parameters ---------- - minmax : int + i_figure_merit : int the ID and sign of the figure of merit to evaluate. A negative value indicates maximisation. A positive value indicates minimisation. @@ -40,16 +40,18 @@ def objective_function(minmax: int, data: DataStructure) -> float: Raises ------ ProcessValueError - If minmax=15 not used with i_plant_availability=1 + If i_figure_merit=15 not used with i_plant_availability=1 """ try: - figure_of_merit = FiguresOfMerit(abs(minmax)) + figure_of_merit = FiguresOfMerit(abs(i_figure_merit)) except ValueError as err: - raise ProcessValueError(f"Invalid minmax value: {minmax}") from err + raise ProcessValueError( + f"Invalid i_figure_merit value: {i_figure_merit}" + ) from err # -1 = maximise # +1 = minimise - objective_sign = np.sign(minmax) + objective_sign = np.sign(i_figure_merit) if figure_of_merit == FiguresOfMerit.MAJOR_RADIUS: objective_metric = 0.2 * data.physics.rmajor @@ -83,8 +85,8 @@ def objective_function(minmax: int, data: DataStructure) -> float: == AvailabilityModel.USER_INPUT ): raise ProcessValueError( - "minmax=15 requires `f_t_plant_available` to be calculated, not user " - "input" + "i_figure_merit=15 requires `f_t_plant_available` to be calculated, not " + "user input" ) objective_metric = data.costs.f_t_plant_available elif figure_of_merit == FiguresOfMerit.MIN_R0_MAX_TAU_BURN: diff --git a/process/core/solver/solver.py b/process/core/solver/solver.py index 513bebeafa..25fbd624bf 100644 --- a/process/core/solver/solver.py +++ b/process/core/solver/solver.py @@ -193,10 +193,10 @@ def solve(self) -> SolverOutputCondition: bb = None if self.b is not None: - bb = np.identity(self.data.numerics.nvar) * self.b + bb = np.identity(self.data.numerics.n_iteration_variables) * self.b def _solver_callback(i: int, _result, _x, convergence_param: float): - self.data.numerics.nviter = i + 1 + self.data.numerics.n_solver_iterations = i + 1 self.data.globals.convergence_parameter = convergence_param print( f"{i + 1} | Convergence Parameter: {convergence_param:.3E}", @@ -273,7 +273,7 @@ def _ineq_cons_satisfied( except ValueError: itervar_name_list = "" for count, iter_var in enumerate( - self.data.numerics.ixc[: self.data.numerics.nvar] + self.data.numerics.ixc[: self.data.numerics.n_iteration_variables] ): itervar_name = self.data.numerics.lablxc[iter_var - 1] itervar_name_list += f"{count}: {itervar_name} \n" diff --git a/process/core/solver/solver_handler.py b/process/core/solver/solver_handler.py index ecd5059493..5009203f44 100644 --- a/process/core/solver/solver_handler.py +++ b/process/core/solver/solver_handler.py @@ -51,9 +51,13 @@ def run(self): # Initialise iteration variables and bounds in Python: relies on Fortran # iteration variables being defined above # Trim maximum size arrays down to actually used size - x = self.data.numerics.xcm[: self.data.numerics.nvar] - bndl = self.data.numerics.itv_scaled_lower_bounds[: self.data.numerics.nvar] - bndu = self.data.numerics.itv_scaled_upper_bounds[: self.data.numerics.nvar] + x = self.data.numerics.xcm[: self.data.numerics.n_iteration_variables] + bndl = self.data.numerics.itv_scaled_lower_bounds[ + : self.data.numerics.n_iteration_variables + ] + bndu = self.data.numerics.itv_scaled_upper_bounds[ + : self.data.numerics.n_iteration_variables + ] # Evaluators() calculates the objective and constraint functions and # their gradients for a given vector x @@ -66,8 +70,9 @@ def run(self): self.solver.set_opt_params(x) # Define total number of constraints and equality constraints self.solver.set_constraints( - m=self.data.numerics.neqns + self.data.numerics.nineqns, - meq=self.data.numerics.neqns, + m=self.data.numerics.n_equality_constraints + + self.data.numerics.n_inequality_constraints, + meq=self.data.numerics.n_equality_constraints, ) ifail = self.solver.solve() @@ -83,10 +88,10 @@ def run(self): # If VMCON has exited with error code 5 # (ifail = SolverOutputCondition.NO_SOLUTION) try another run using a # multiple of the identity matrix as input for the Hessian b(n,n) - # Only do this if VMCON has not iterated (nviter=1) + # Only do this if VMCON has not iterated (n_solver_iterations=1) if ( ifail == SolverOutputCondition.NO_SOLUTION - and self.data.numerics.nviter < 2 + and self.data.numerics.n_solver_iterations < 2 ): print( "VMCON error code = 5 (SolverOutputCondition.NO_SOLUTION). " @@ -116,7 +121,7 @@ def output(self): def _numerics_output(self): nums = self.data.numerics - nums.sqsumsq = sum(r**2 for r in nums.rcm[: nums.neqns]) ** 0.5 + nums.sqsumsq = sum(r**2 for r in nums.rcm[: nums.n_equality_constraints]) ** 0.5 process_output.oheadr(constants.NOUT, "Numerics") s_type = ( @@ -163,19 +168,23 @@ def _numerics_output(self): logger.warning(f"High final constraint residues. {nums.sqsumsq=}") for d, var, v in ( - ("Number of iteration variables", "(nvar)", nums.nvar), + ( + "Number of iteration variables", + "(n_iteration_variables)", + nums.n_iteration_variables, + ), ( "Number of constraints (total)", - "(neqns+nineqns)", - nums.neqns + nums.nineqns, + "(n_equality_constraints+n_inequality_constraints)", + nums.n_equality_constraints + nums.n_inequality_constraints, ), - ("Optimisation switch", "(ioptimz)", nums.ioptimz), + ("Optimisation switch", "(i_process_run_mode)", nums.i_process_run_mode), ): process_output.ovarre(constants.NOUT, d, var, v) process_output.ocmmnt( constants.NOUT, - f" {PROCESSRunMode(nums.ioptimz).description}", + f" {PROCESSRunMode(nums.i_process_run_mode).description}", ) # Objective function output: none for fsolve @@ -183,11 +192,11 @@ def _numerics_output(self): process_output.ovarre( constants.NOUT, "Figure of merit switch", - "(minmax)", - nums.minmax, + "(i_figure_merit)", + nums.i_figure_merit, ) - nums.objf_name = f'"{FiguresOfMerit(abs(nums.minmax)).description}"' + nums.objf_name = f'"{FiguresOfMerit(abs(nums.i_figure_merit)).description}"' for d, var, v, o in ( ("Objective function name", "(objf_name)", nums.objf_name, ""), @@ -200,8 +209,8 @@ def _numerics_output(self): ), ( "Number of optimising solver iterations", - "(nviter)", - nums.nviter, + "(n_solver_iterations)", + nums.n_solver_iterations, "OP ", ), ): @@ -234,7 +243,7 @@ def _numerics_output(self): else "PROCESS has failed to optimise" ) + " the optimisation parameters to" - + ("minimise" if nums.minmax > 0 else "maximise") + + ("minimise" if nums.i_figure_merit > 0 else "maximise") + f" the objective function: {nums.objf_name}\n" ), ) @@ -246,7 +255,7 @@ def _optimisation_parameters_output(self): # Output optimisation parameters solution_vector_table = [] - for i in range(nums.nvar): + for i in range(nums.n_iteration_variables): nums.xcs[i] = nums.xcm[i] * nums.scafc[i] name = nums.lablxc[nums.ixc[i] - 1] diff --git a/process/data_structure/numerics.py b/process/data_structure/numerics.py index 57505f1c39..c45415f95b 100644 --- a/process/data_structure/numerics.py +++ b/process/data_structure/numerics.py @@ -42,7 +42,7 @@ class SolverOutputCondition(IntEnum): class PROCESSRunMode(IntEnum): """Enumeration of the available PROCESS run modes, which determine the behaviour - of the code in various places. This is controlled by the `ioptimz` variable + of the code in various places. This is controlled by the `i_process_run_mode` variable """ EVALUATION = (-2, "Evaluation mode (no optimisation)") @@ -135,11 +135,11 @@ def description(self): return self._description_ -IPNVARS = max(ITERATION_VARIABLES.keys()) +N_ITERATION_VARIABLES_MAX = max(ITERATION_VARIABLES.keys()) """total number of variables available for iteration""" # Set to a really large number so that it should never need to be changed -IPEQNS = 500 +N_CONSTRAINT_EQUATIONS_MAX = 500 """Maximum number of constraint equations available""" @@ -147,25 +147,23 @@ def description(self): class NumericsData: """Dataclass holding numerics variables""" - ioptimz: int = 1 - """Code operation switch: - * -2 for evaluation mode (i.e. no optimisation) - * 1 for optimisation mode (e.g. via VMCON) + i_process_run_mode: int = 1 + """PROCESS run mode (see `PROCESSRunMode` for descriptions) """ - minmax: int = 7 + i_figure_merit: int = 7 """ Switch for figure-of-merit (see `FiguresOfMerit` for descriptions) negative => maximise, positive => minimise """ n_constraints: int = 0 - """Total number of constraints (neqns + nineqns)""" + """Total number of constraints (n_equality_constraints + n_inequality_constraints)""" - ncalls: int = 0 + n_model_calls: int = 0 """number of function calls during solution""" - neqns: int = -1 + n_equality_constraints: int = -1 """number of equality constraints to be satisfied""" nfev1: int = 0 @@ -174,20 +172,24 @@ class NumericsData: nfev2: int = 0 """number of calls to FCNVMC1 (VMCON function caller) made""" - nineqns: int = 0 + n_inequality_constraints: int = 0 """number of inequality constraints VMCON must satisfy (leave at zero for now) """ - nvar: int = 0 + n_iteration_variables: int = 0 """number of iteration variables to use""" - nviter: int = 0 + n_solver_iterations: int = 0 """number of optimisation iterations performed""" - icc: list[int] = field(default_factory=lambda: np.array([0] * IPEQNS)) + icc: list[int] = field( + default_factory=lambda: np.array([0] * N_CONSTRAINT_EQUATIONS_MAX) + ) - active_constraints: list[bool] = field(default_factory=lambda: [False] * IPEQNS) + active_constraints: list[bool] = field( + default_factory=lambda: [False] * N_CONSTRAINT_EQUATIONS_MAX + ) """Logical array showing which constraints are active""" # TODO Do not change the comments for lablcc: they are used to create the @@ -391,12 +393,14 @@ class NumericsData: * (92) D/T/He3 ratio in fuel sums to 1 """ - ixc: list[int] = field(default_factory=lambda: np.array([0] * IPNVARS)) + ixc: list[int] = field( + default_factory=lambda: np.array([0] * N_ITERATION_VARIABLES_MAX) + ) """Array defining which iteration variables to activate (see lablxc for descriptions) """ - lablxc: list[str] = field(default_factory=lambda: [""] * IPNVARS) + lablxc: list[str] = field(default_factory=lambda: [""] * N_ITERATION_VARIABLES_MAX) """Labels describing iteration variables