diff --git a/documentation/source/development/add-vars.md b/documentation/source/development/add-vars.md index e62fcfa23c..07dbaffe9d 100644 --- a/documentation/source/development/add-vars.md +++ b/documentation/source/development/add-vars.md @@ -55,7 +55,7 @@ Code example in the `input.py` file: To add a `PROCESS` iteration variable please follow the steps below, in addition to the instructions for adding an input variable: -1. The parameter `ipnvars` in module `numerics` of `numerics.f90` will normally be greater than the actual number of iteration variables, and does not need to be changed. +1. The parameter `N_ITERATION_VARIABLES_MAX` in module `numerics` of `numerics.f90` will normally be greater than the actual number of iteration variables, and does not need to be changed. 2. Append a new iteration number key to the end of the `ITERATION_VARIABLES` dictionary in `iteration_variables.py`. The associated variable is the corresponding key value. 3. Set the variable origin file and then the associated lower and upper bounds 4. Update the `lablxc` description in `numerics.f90`. @@ -80,7 +80,7 @@ ITERATION_VARIABLES = { New figures of merit are added to `PROCESS` in the following way: -1. Increment the parameter `ipnfoms` in module `numerics` in source file `numerics.f90` to accommodate the new figure of merit. +1. Increment the parameter `N_FIGURES_MERIT_MAX` in module `numerics` in source file `numerics.f90` to accommodate the new figure of merit. 2. Assign a description of the new figure of merit to the relevant element of array `lablmm` in module `numerics` in the source file `numerics.f90`. diff --git a/documentation/source/io/input-guide.md b/documentation/source/io/input-guide.md index 4176d5ba1b..af3daad215 100644 --- a/documentation/source/io/input-guide.md +++ b/documentation/source/io/input-guide.md @@ -112,16 +112,16 @@ 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 lablmm for descriptions) +i_figure_merit = 1 * Switch for figure-of-merit (see lablmm for descriptions) ``` -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 c709679158..08fac323c6 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 019c217257..f50dd9831c 100644 --- a/documentation/source/usage/troubleshooting.md +++ b/documentation/source/usage/troubleshooting.md @@ -46,9 +46,9 @@ The is the option to turn on extra debugging output; to do this, set `verbose = ### 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_eval_IN.DAT b/examples/data/large_tokamak_eval_IN.DAT index 464a9a84ba..15c2f7fca5 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_modess_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 62bca42a29..4596349e1b 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 9aacab06e3..a4d2c8136b 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 e7e6849de7..93657f5ab7 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/process/core/caller.py b/process/core/caller.py index 4d842d29f9..dbbad1601c 100644 --- a/process/core/caller.py +++ b/process/core/caller.py @@ -98,7 +98,7 @@ def call_models(self, xc: np.ndarray, m: int) -> tuple[float, np.ndarray]: for _ in range(10): self._call_models_once(xc, self.data) # Evaluate objective function and constraints - objf = objective_function(data_structure.numerics.minmax, self.data) + objf = objective_function(data_structure.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, data: DataStructure): nvars = len(xc) # Increment the call counter - data_structure.numerics.ncalls += 1 + data_structure.numerics.n_model_calls += 1 # Convert variables set_scaled_iteration_variable(xc, nvars, self.data) @@ -417,7 +417,7 @@ def write_output_files( ifail : int solver return code """ - n = data_structure.numerics.nvar + n = data_structure.numerics.n_iteration_variables x = data_structure.numerics.xcm[:n] # Call models, ensuring output mfiles are fully idempotent caller = Caller(models, data) diff --git a/process/core/final.py b/process/core/final.py index be55db69cb..657151b0ef 100644 --- a/process/core/final.py +++ b/process/core/final.py @@ -32,7 +32,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 numerics.ioptimz == -2: + if numerics.i_process_run_mode == -2: output_evaluation(data) # Print non-idempotence warning to OUT.DAT only @@ -57,18 +57,23 @@ def output_evaluation(data): po.oblnkl(constants.NOUT) # Evaluate objective function - norm_objf = objective_function(numerics.minmax, data) + norm_objf = objective_function(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( - numerics.neqns + numerics.nineqns, -1, data + numerics.n_equality_constraints + numerics.n_inequality_constraints, -1, data ) labels = [ numerics.lablcc[j] - for j in [i - 1 for i in numerics.icc[: numerics.neqns + numerics.nineqns]] + for j in [ + i - 1 + for i in numerics.icc[ + : numerics.n_equality_constraints + numerics.n_inequality_constraints + ] + ] ] 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)] @@ -83,7 +88,7 @@ def output_evaluation(data): po.write(constants.NOUT, tabulate(table_data, headers="keys")) - for i in range(numerics.neqns): + for i in range(numerics.n_equality_constraints): constraint_id = numerics.icc[i] po.ovarre( constants.MFILE, @@ -92,11 +97,11 @@ def output_evaluation(data): residual_error[i], ) - for i in range(numerics.nineqns): - constraint_id = numerics.icc[numerics.neqns + i] + for i in range(numerics.n_inequality_constraints): + constraint_id = numerics.icc[numerics.n_equality_constraints + i] po.ovarre( constants.MFILE, - f"{labels[numerics.neqns + i]}", + f"{labels[numerics.n_equality_constraints + i]}", f"(ineq_con{constraint_id:03d})", - residual_error[numerics.neqns + i], + residual_error[numerics.n_equality_constraints + i], ) diff --git a/process/core/init.py b/process/core/init.py index 731d3d7e6d..dfc77995bb 100644 --- a/process/core/init.py +++ b/process/core/init.py @@ -160,27 +160,29 @@ def run_summary(): process_output.oblnkl(outfile) process_output.ocmmnt( - outfile, f"Equality constraints : {data_structure.numerics.neqns}" + outfile, + f"Equality constraints : {data_structure.numerics.n_equality_constraints}", ) process_output.ocmmnt( outfile, - f"Inequality constraints : {data_structure.numerics.nineqns}", + f"Inequality constraints : {data_structure.numerics.n_inequality_constraints}", ) process_output.ocmmnt( outfile, - f"Total constraints : {data_structure.numerics.nineqns + data_structure.numerics.neqns}", + f"Total constraints : {data_structure.numerics.n_inequality_constraints + data_structure.numerics.n_equality_constraints}", ) process_output.ocmmnt( - outfile, f"Iteration variables : {data_structure.numerics.nvar}" + outfile, + f"Iteration variables : {data_structure.numerics.n_iteration_variables}", ) # If optimising, write objective function and convergence parameter - if data_structure.numerics.ioptimz == 1: + if data_structure.numerics.i_process_run_mode == 1: process_output.ocmmnt( outfile, f"Max iterations : {data_structure.global_variables.maxcal}", ) - if data_structure.numerics.minmax > 0: + if data_structure.numerics.i_figure_merit > 0: minmax_string = " -- minimise " minmax_sign = "+" else: @@ -188,11 +190,11 @@ def run_summary(): minmax_sign = "-" fom_string = data_structure.numerics.lablmm[ - abs(data_structure.numerics.minmax) - 1 + abs(data_structure.numerics.i_figure_merit) - 1 ] process_output.ocmmnt( outfile, - f"Figure of merit : {minmax_sign}{abs(data_structure.numerics.minmax)}{minmax_string}{fom_string}", + f"Figure of merit : {minmax_sign}{abs(data_structure.numerics.i_figure_merit)}{minmax_string}{fom_string}", ) process_output.ocmmnt( outfile, @@ -217,12 +219,18 @@ def run_summary(): process_output.ovarst(mfile, "Input filename", "(fileprefix)", f'"{fileprefix}"') process_output.ovarin( - mfile, "Optimisation switch", "(ioptimz)", data_structure.numerics.ioptimz + mfile, + "Optimisation switch", + "(i_process_run_mode)", + data_structure.numerics.i_process_run_mode, ) # If optimising, write figure of merit switch - if data_structure.numerics.ioptimz == 1: + if data_structure.numerics.i_process_run_mode == 1: process_output.ovarin( - mfile, "Figure of merit switch", "(minmax)", data_structure.numerics.minmax + mfile, + "Figure of merit switch", + "(i_figure_merit)", + data_structure.numerics.i_figure_merit, ) @@ -251,23 +259,32 @@ def check_process(inputs, data): # noqa: ARG001 and ensures other dependent variables are given suitable values. """ # Check that there are sufficient iteration variables - if data_structure.numerics.nvar < data_structure.numerics.neqns: + if ( + data_structure.numerics.n_iteration_variables + < data_structure.numerics.n_equality_constraints + ): raise ProcessValidationError( "Insufficient iteration variables to solve the problem! NVAR < NEQNS", - nvar=data_structure.numerics.nvar, - neqns=data_structure.numerics.neqns, + n_iteration_variables=data_structure.numerics.n_iteration_variables, + n_equality_constraints=data_structure.numerics.n_equality_constraints, ) # Check that sufficient elements of ixc and icc have been specified - if (data_structure.numerics.ixc[: data_structure.numerics.nvar] == 0).any(): + if ( + data_structure.numerics.ixc[: data_structure.numerics.n_iteration_variables] == 0 + ).any(): raise ProcessValidationError( "The number of iteration variables specified is smaller than the number stated in ixc", - nvar=data_structure.numerics.nvar, + n_iteration_variables=data_structure.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_structure.numerics.ixc[: data_structure.numerics.nvar] == 13).any() and ( - data_structure.numerics.ixc[: data_structure.numerics.nvar] == 140 + if ( + data_structure.numerics.ixc[: data_structure.numerics.n_iteration_variables] + == 13 + ).any() and ( + data_structure.numerics.ixc[: data_structure.numerics.n_iteration_variables] + == 140 ).any(): raise ProcessValidationError( "Iteration variables 13 and 140 cannot be used simultaneously", @@ -275,15 +292,20 @@ def check_process(inputs, data): # noqa: ARG001 # Can't use c_tf_turn as interation var, constraint or input if i_tf_turns_integer == 1 if ( - data_structure.numerics.ixc[: data_structure.numerics.nvar] == 60 + data_structure.numerics.ixc[: data_structure.numerics.n_iteration_variables] + == 60 ).any() and data_structure.tfcoil_variables.i_tf_turns_integer == 1: raise ProcessValidationError( "Iteration variable 60 (TF current per turn, c_tf_turn) cannot be used with the TF coil integer turn model (i_tf_turns_integer == 1) as it is a calculated output instead for this model. However, the maximum current per turn can be constrained with constraint 77." ) # Can't have icc 77 and ixc 60 at the same time - if (data_structure.numerics.ixc[: data_structure.numerics.nvar] == 60).any() and ( - data_structure.numerics.icc[: data_structure.numerics.nvar] == 77 + if ( + data_structure.numerics.ixc[: data_structure.numerics.n_iteration_variables] + == 60 + ).any() and ( + data_structure.numerics.icc[: data_structure.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." @@ -291,21 +313,23 @@ def check_process(inputs, data): # noqa: ARG001 if ( data_structure.numerics.icc[ - : data_structure.numerics.neqns + data_structure.numerics.nineqns + : data_structure.numerics.n_equality_constraints + + data_structure.numerics.n_inequality_constraints ] == 0 ).any(): raise ProcessValidationError( - "The number of constraints specified is smaller than the number stated in neqns+nineqns", - neqns=data_structure.numerics.neqns, - nineqns=data_structure.numerics.nineqns, + "The number of constraints specified is smaller than the number stated in n_equality_constraints+n_inequality_constraints", + n_equality_constraints=data_structure.numerics.n_equality_constraints, + n_inequality_constraints=data_structure.numerics.n_inequality_constraints, ) # Deprecate constraints for depcrecated_constraint in [3, 4, 10, 74, 42]: if ( data_structure.numerics.icc[ - : data_structure.numerics.neqns + data_structure.numerics.nineqns + : data_structure.numerics.n_equality_constraints + + data_structure.numerics.n_inequality_constraints ] == depcrecated_constraint ).any(): @@ -316,7 +340,8 @@ def check_process(inputs, data): # noqa: ARG001 # MDK Report error if constraint 63 is used with old vacuum model if ( data_structure.numerics.icc[ - : data_structure.numerics.neqns + data_structure.numerics.nineqns + : data_structure.numerics.n_equality_constraints + + data_structure.numerics.n_inequality_constraints ] == 63 ).any() and data.vacuum.i_vacuum_pumping != "simple": @@ -360,7 +385,10 @@ def check_process(inputs, data): # noqa: ARG001 # Stop the run if oacdcp is used as an optimisation variable # As the current density is now calculated from b_plasma_toroidal_on_axis without constraint 10 - if (data_structure.numerics.ixc[: data_structure.numerics.nvar] == 12).any(): + if ( + data_structure.numerics.ixc[: data_structure.numerics.n_iteration_variables] + == 12 + ).any(): raise ProcessValidationError( "The 1/R toroidal B field dependency constraint is being depreciated" ) @@ -412,8 +440,13 @@ def check_process(inputs, data): # noqa: ARG001 ) if ( - data_structure.numerics.ioptimz >= 0 - and (data_structure.numerics.ixc[: data_structure.numerics.nvar] == 4).any() + data_structure.numerics.i_process_run_mode >= 0 + and ( + data_structure.numerics.ixc[ + : data_structure.numerics.n_iteration_variables + ] + == 4 + ).any() and data_structure.numerics.boundl[3] < data.physics.temp_plasma_pedestal_kev * 1.001 ): @@ -433,7 +466,10 @@ def check_process(inputs, data): # noqa: ARG001 if ( data.physics.f_nd_plasma_pedestal_greenwald < 0 or not ( - data_structure.numerics.ixc[: data_structure.numerics.nvar] == 145 + data_structure.numerics.ixc[ + : data_structure.numerics.n_iteration_variables + ] + == 145 ).any() ): # Issue #589 Pedestal density is set manually using nd_plasma_pedestal_electron but it is less than nd_plasma_separatrix_electron. @@ -468,22 +504,31 @@ def check_process(inputs, data): # noqa: ARG001 # Issue #862 : Variable nd_plasma_electron_on_axis/nd_plasma_pedestal_electron ratio without constraint eq 81 (nd_plasma_electron_on_axis>nd_plasma_pedestal_electron) # -> Potential hollowed density profile if ( - data_structure.numerics.ioptimz >= 0 + data_structure.numerics.i_process_run_mode >= 0 and not ( data_structure.numerics.icc[ - : data_structure.numerics.neqns + data_structure.numerics.nineqns + : data_structure.numerics.n_equality_constraints + + data_structure.numerics.n_inequality_constraints ] == 81 ).any() ): if ( - data_structure.numerics.ixc[: data_structure.numerics.nvar] == 145 + data_structure.numerics.ixc[ + : data_structure.numerics.n_iteration_variables + ] + == 145 ).any(): warn( "nd_plasma_pedestal_electron set with f_nd_plasma_pedestal_greenwald without constraint eq 81 (nd_plasma_pedestal_electron 273.15 K" @@ -640,7 +696,10 @@ def check_process(inputs, data): # noqa: ARG001 # Check if conductor upper limit is properly set to 50 K or below if ( - data_structure.numerics.ixc[: data_structure.numerics.nvar] == 20 + data_structure.numerics.ixc[ + : data_structure.numerics.n_iteration_variables + ] + == 20 ).any() and data_structure.numerics.boundu[19] > 50.0: raise ProcessValidationError( "Too large CP conductor temperature (temp_cp_average). Upper limit for cryo-al < 50 K" @@ -674,7 +733,8 @@ def check_process(inputs, data): # noqa: ARG001 == TFConductorModel.HELIUM_COOLED_ALUMINIUM and ( data_structure.numerics.icc[ - : data_structure.numerics.neqns + data_structure.numerics.nineqns + : data_structure.numerics.n_equality_constraints + + data_structure.numerics.n_inequality_constraints ] == 85 ).any() @@ -699,7 +759,12 @@ def check_process(inputs, data): # noqa: ARG001 # Checking the CP TF top radius if ( abs(data.build.r_cp_top) > 0 - or (data_structure.numerics.ixc[: data_structure.numerics.nvar] == 174).any() + or ( + data_structure.numerics.ixc[ + : data_structure.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" @@ -748,7 +813,8 @@ def check_process(inputs, data): # noqa: ARG001 # Constraint 10 is dedicated to ST designs with demountable joints if ( data_structure.numerics.icc[ - : data_structure.numerics.neqns + data_structure.numerics.nineqns + : data_structure.numerics.n_equality_constraints + + data_structure.numerics.n_inequality_constraints ] == 10 ).any(): @@ -771,12 +837,23 @@ def check_process(inputs, data): # noqa: ARG001 if ( ( not ( - (data_structure.numerics.ixc[: data_structure.numerics.nvar] == 16).any() + ( + data_structure.numerics.ixc[ + : data_structure.numerics.n_iteration_variables + ] + == 16 + ).any() or ( - data_structure.numerics.ixc[: data_structure.numerics.nvar] == 29 + data_structure.numerics.ixc[ + : data_structure.numerics.n_iteration_variables + ] + == 29 ).any() or ( - data_structure.numerics.ixc[: data_structure.numerics.nvar] == 42 + data_structure.numerics.ixc[ + : data_structure.numerics.n_iteration_variables + ] + == 42 ).any() ) ) # No dr_bore,dr_cs_tf_gap, dr_cs iteration @@ -792,13 +869,15 @@ def check_process(inputs, data): # noqa: ARG001 and ( ( data_structure.numerics.icc[ - : data_structure.numerics.neqns + data_structure.numerics.nineqns + : data_structure.numerics.n_equality_constraints + + data_structure.numerics.n_inequality_constraints ] == 31 ).any() or ( data_structure.numerics.icc[ - : data_structure.numerics.neqns + data_structure.numerics.nineqns + : data_structure.numerics.n_equality_constraints + + data_structure.numerics.n_inequality_constraints ] == 32 ).any() @@ -980,7 +1059,10 @@ def check_process(inputs, data): # noqa: ARG001 # Check if the WP/conductor radial thickness (dr_tf_wp_with_insulation) is large enough # To contains the insulation, cooling and the support structure # Rem : Only verified if the WP thickness is used - if (data_structure.numerics.ixc[: data_structure.numerics.nvar] == 140).any(): + if ( + data_structure.numerics.ixc[: data_structure.numerics.n_iteration_variables] + == 140 + ).any(): # Minimal WP thickness if data_structure.tfcoil_variables.i_tf_sup == TFConductorModel.SUPERCONDUCTING: dr_tf_wp_min = 2.0 * ( @@ -991,7 +1073,12 @@ def check_process(inputs, data): # noqa: ARG001 ) # Steel conduit thickness (can be an iteration variable) - if (data_structure.numerics.ixc[: data_structure.numerics.nvar] == 58).any(): + if ( + data_structure.numerics.ixc[ + : data_structure.numerics.n_iteration_variables + ] + == 58 + ).any(): dr_tf_wp_min += 2.0 * data_structure.numerics.boundl[57] else: dr_tf_wp_min += 2.0 * data_structure.tfcoil_variables.dx_tf_turn_steel @@ -1148,7 +1235,8 @@ def check_process(inputs, data): # noqa: ARG001 # Cannot use temperature margin constraint with REBCO TF coils if ( data_structure.numerics.icc[ - : data_structure.numerics.neqns + data_structure.numerics.nineqns + : data_structure.numerics.n_equality_constraints + + data_structure.numerics.n_inequality_constraints ] == 36 ).any() and ( @@ -1162,7 +1250,8 @@ def check_process(inputs, data): # noqa: ARG001 # Cannot use temperature margin constraint with REBCO CS coils if ( data_structure.numerics.icc[ - : data_structure.numerics.neqns + data_structure.numerics.nineqns + : data_structure.numerics.n_equality_constraints + + data_structure.numerics.n_inequality_constraints ] == 60 ).any() and data.pf_coil.i_cs_superconductor == 8: @@ -1180,7 +1269,8 @@ def check_process(inputs, data): # noqa: ARG001 # Cannot use TF coil strain limit if i_str_wp is off: if ( data_structure.numerics.icc[ - : data_structure.numerics.neqns + data_structure.numerics.nineqns + : data_structure.numerics.n_equality_constraints + + data_structure.numerics.n_inequality_constraints ] == 88 ).any() and data_structure.tfcoil_variables.i_str_wp == 0: @@ -1197,11 +1287,15 @@ def set_active_constraints(): ] = True num_constraints += 1 - if data_structure.numerics.neqns < 0: - # The value of neqns has not been set in the input file. Default = 0. - data_structure.numerics.neqns = num_constraints - data_structure.numerics.nineqns + if data_structure.numerics.n_equality_constraints < 0: + # The value of n_equality_constraints has not been set in the input file. Default = 0. + data_structure.numerics.n_equality_constraints = ( + num_constraints - data_structure.numerics.n_inequality_constraints + ) else: - data_structure.numerics.nineqns = num_constraints - data_structure.numerics.neqns + data_structure.numerics.n_inequality_constraints = ( + num_constraints - data_structure.numerics.n_equality_constraints + ) def set_device_type(data): diff --git a/process/core/input.py b/process/core/input.py index efd35b33ba..85b8c71c7c 100644 --- a/process/core/input.py +++ b/process/core/input.py @@ -33,8 +33,8 @@ def _ixc_additional_actions(_name, value: int, _array_index, _config): - data_structure.numerics.ixc[data_structure.numerics.nvar] = value - data_structure.numerics.nvar += 1 + data_structure.numerics.ixc[data_structure.numerics.n_iteration_variables] = value + data_structure.numerics.n_iteration_variables += 1 def _icc_additional_actions(_name, value: int, _array_index, _config): @@ -106,17 +106,17 @@ def __post_init__(self): "runtitle": InputVariable(data_structure.global_variables, str), "verbose": InputVariable(data_structure.global_variables, int, choices=[0, 1]), "run_tests": InputVariable(data_structure.global_variables, int, choices=[0, 1]), - "ioptimz": InputVariable(data_structure.numerics, int, choices=[1, -2]), + "i_process_run_mode": InputVariable(data_structure.numerics, int, choices=[1, -2]), "epsvmc": InputVariable(data_structure.numerics, float, range=(0.0, 1.0)), "boundl": InputVariable(data_structure.numerics, float, array=True), "boundu": InputVariable(data_structure.numerics, float, array=True), "epsfcn": InputVariable(data_structure.numerics, float, range=(0.0, 1.0)), "maxcal": InputVariable(data_structure.global_variables, int, range=(0, 10000)), - "minmax": InputVariable(data_structure.numerics, int), - "neqns": InputVariable( + "i_figure_merit": InputVariable(data_structure.numerics, int), + "n_equality_constraints": InputVariable( data_structure.numerics, int, range=(0, ConstraintManager.num_constraints()) ), - "nineqns": InputVariable( + "n_inequality_constraints": InputVariable( data_structure.numerics, int, range=(0, ConstraintManager.num_constraints()) ), "alphaj": InputVariable("physics", float, range=(0.0, 10.0)), @@ -1277,14 +1277,14 @@ def __post_init__(self): "ixc": InputVariable( None, int, - range=(1, data_structure.numerics.ipnvars), + range=(1, data_structure.numerics.N_ITERATION_VARIABLES_MAX), additional_actions=_ixc_additional_actions, set_variable=False, ), "icc": InputVariable( None, int, - range=(1, data_structure.numerics.ipeqns), + range=(1, data_structure.numerics.N_CONSTRAINT_EQUATIONS_MAX), additional_actions=_icc_additional_actions, set_variable=False, ), diff --git a/process/core/io/in_dat/base.py b/process/core/io/in_dat/base.py index 1ac910e677..dfc8849c3e 100644 --- a/process/core/io/in_dat/base.py +++ b/process/core/io/in_dat/base.py @@ -17,7 +17,7 @@ from process.core.io.data_structure_dicts import get_dicts from process.core.solver.constraints import ConstraintManager -# ioptimz values +# i_process_run_mode values ioptimz_des = { "-2": "for no optimisation, no VMCOM or HYBRD", "-1": "for no optimisation HYBRD only", @@ -426,8 +426,8 @@ 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 @@ -453,14 +453,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 = ioptimz_des[str(iop_val)] - 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)): @@ -1306,7 +1306,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 f43f92306d..91bf45d944 100644 --- a/process/core/io/mfile/base.py +++ b/process/core/io/mfile/base.py @@ -603,7 +603,7 @@ def get_mfile_initial_ixc_values(file_path: Path, data: DataStructure): iteration_variable_names = [] iteration_variable_values = [] - for i in range(data_structure.numerics.nvar): + for i in range(data_structure.numerics.n_iteration_variables): ivar = data_structure.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 4d0197d86b..74bffe827b 100644 --- a/process/core/io/obsolete_vars.py +++ b/process/core/io/obsolete_vars.py @@ -448,6 +448,9 @@ "vcool": "vel_cp_coolant_midplane", "rcool": "radius_cp_coolant_channel", "fl_h_threshold": None, + "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 1e3848e13d..b28c026bf0 100644 --- a/process/core/io/plot/solutions.py +++ b/process/core/io/plot/solutions.py @@ -137,7 +137,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: @@ -436,7 +436,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 @@ -446,7 +446,8 @@ def _plot_solutions( else: numerics.init_numerics() objf_list = { - numerics.lablmm[int(abs(minmax)) - 1] for minmax in diffs_df["minmax"] + numerics.lablmm[int(abs(i_figure_merit)) - 1] + 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 866224a4fb..0c7f37f0ac 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -7820,9 +7820,9 @@ def plot_header(axis: plt.Axes, mfile: MFile, scan: int): (f"!{mfile.get('time', scan=-1)}", "Time:", ""), (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"!{OBJECTIVE_NAMES[abs(int(mfile.get('minmax', scan=-1)))]}", + f"!{OBJECTIVE_NAMES[abs(int(mfile.get('i_figure_merit', scan=-1)))]}", "Optimising:", "", ), @@ -10378,7 +10378,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 = [] @@ -11578,10 +11578,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 = mfile.get("ioptimz", scan=-1) - minmax_switch = mfile.get("minmax", scan=-1) or "N/A" + optmisation_switch = mfile.get("i_process_run_mode", scan=-1) + minmax_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 @@ -11589,7 +11589,7 @@ 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): @@ -11660,13 +11660,13 @@ def plot_cover_page( # Box 3: Run Settings settings_info = ( f"• Optimisation Switch: {int(optmisation_switch)}\n" - f"• Figure of Merit Switch (minmax): {minmax_switch}\n" + f"• Figure of Merit Switch (i_figure_merit): {minmax_switch}\n" f"• Fail Status (ifail): {int(ifail)}\n" f"• Number of Iteration Variables: {int(nvars)}\n" f"{objective_text}\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/scan.py b/process/core/scan.py index 1d08e034c1..78f1efcdc0 100644 --- a/process/core/scan.py +++ b/process/core/scan.py @@ -259,7 +259,9 @@ def post_optimise(self, ifail: int): ifail: int : """ - numerics.sqsumsq = sum(r**2 for r in numerics.rcm[: numerics.neqns]) ** 0.5 + numerics.sqsumsq = ( + sum(r**2 for r in numerics.rcm[: numerics.n_equality_constraints]) ** 0.5 + ) process_output.oheadr(constants.NOUT, "Numerics") if self.solver == "fsolve": @@ -335,24 +337,33 @@ def post_optimise(self, ifail: int): logger.warning(f"High final constraint residues. {numerics.sqsumsq=}") process_output.ovarin( - constants.NOUT, "Number of iteration variables", "(nvar)", numerics.nvar + constants.NOUT, + "Number of iteration variables", + "(n_iteration_variables)", + numerics.n_iteration_variables, ) process_output.ovarin( constants.NOUT, "Number of constraints (total)", - "(neqns+nineqns)", - numerics.neqns + numerics.nineqns, + "(n_equality_constraints+n_inequality_constraints)", + numerics.n_equality_constraints + numerics.n_inequality_constraints, ) process_output.ovarin( - constants.NOUT, "Optimisation switch", "(ioptimz)", numerics.ioptimz + constants.NOUT, + "Optimisation switch", + "(i_process_run_mode)", + numerics.i_process_run_mode, ) # Objective function output: none for fsolve if self.solver != "fsolve": process_output.ovarin( - constants.NOUT, "Figure of merit switch", "(minmax)", numerics.minmax + constants.NOUT, + "Figure of merit switch", + "(i_figure_merit)", + numerics.i_figure_merit, ) - objf_name = f'"{numerics.lablmm[abs(numerics.minmax) - 1]}"' + objf_name = f'"{numerics.lablmm[abs(numerics.i_figure_merit) - 1]}"' numerics.objf_name = objf_name @@ -388,8 +399,8 @@ def post_optimise(self, ifail: int): process_output.ovarin( constants.NOUT, "Number of optimising solver iterations", - "(nviter)", - numerics.nviter, + "(n_solver_iterations)", + numerics.n_solver_iterations, "OP ", ) process_output.oblnkl(constants.NOUT) @@ -409,7 +420,7 @@ def post_optimise(self, ifail: int): else: string1 = "PROCESS has failed to optimise" - string2 = "minimise" if numerics.minmax > 0 else "maximise" + string2 = "minimise" if numerics.i_figure_merit > 0 else "maximise" process_output.write( constants.NOUT, @@ -420,7 +431,7 @@ def post_optimise(self, ifail: int): # Output optimisation parameters solution_vector_table = [] - for i in range(numerics.nvar): + for i in range(numerics.n_iteration_variables): numerics.xcs[i] = numerics.xcm[i] * numerics.scafc[i] name = numerics.lablxc[numerics.ixc[i] - 1] @@ -523,12 +534,14 @@ def post_optimise(self, ifail: int): ) con1, con2, err, _, lab = constraints.constraint_eqns( - numerics.neqns + numerics.nineqns, -1, self.data + numerics.n_equality_constraints + numerics.n_inequality_constraints, + -1, + self.data, ) # Write equality constraints to mfile equality_constraint_table = [] - for i in range(numerics.neqns): + for i in range(numerics.n_equality_constraints): name = numerics.lablcc[numerics.icc[i] - 1] equality_constraint_table.append([ @@ -582,7 +595,7 @@ def post_optimise(self, ifail: int): ) # Write inequality constraints - if numerics.nineqns > 0: + if numerics.n_inequality_constraints > 0: inequality_constraint_table = [] # Inequalities not necessarily satisfied when evaluating process_output.osubhd( @@ -596,7 +609,10 @@ def post_optimise(self, ifail: int): "might be violated.", ) - for i in range(numerics.neqns, numerics.neqns + numerics.nineqns): + for i in range( + numerics.n_equality_constraints, + numerics.n_equality_constraints + numerics.n_inequality_constraints, + ): name = numerics.lablcc[numerics.icc[i] - 1] constraint = constraints.ConstraintManager.evaluate_constraint( int(numerics.icc[i]), self.data diff --git a/process/core/solver/evaluators.py b/process/core/solver/evaluators.py index d892bb6e61..4aa31692e3 100644 --- a/process/core/solver/evaluators.py +++ b/process/core/solver/evaluators.py @@ -71,9 +71,9 @@ def fcnvmc1(self, _n, m, xv, ifail): sqsumconfsq = math.sqrt(summ) logger.debug("Key evaluator values:") - logger.debug(f"{numerics.nviter = }") + logger.debug(f"{numerics.n_solver_iterations = }") logger.debug(f"{(1 - (ifail % 7)) - 1 = }") - logger.debug(f"{(numerics.nviter % 2) - 1 = }") + logger.debug(f"{(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 4cbbadfff9..6a08d722da 100644 --- a/process/core/solver/iteration_variables.py +++ b/process/core/solver/iteration_variables.py @@ -266,7 +266,7 @@ def check_iteration_variable(iteration_variable_value, name: str = ""): def load_iteration_variables(data): """Loads the physics and engineering variables into the optimisation variable array.""" - for i in range(data_structure.numerics.nvar): + for i in range(data_structure.numerics.n_iteration_variables): variable_index = data_structure.numerics.ixc[i] iteration_variable = ITERATION_VARIABLES[variable_index] @@ -389,7 +389,7 @@ def set_scaled_iteration_variable(xc, nn: int, data: DataStructure): def load_scaled_bounds(): """Sets the scaled bounds of the iteration variables.""" - for i in range(data_structure.numerics.nvar): + for i in range(data_structure.numerics.n_iteration_variables): variable_index = data_structure.numerics.ixc[i] - 1 data_structure.numerics.itv_scaled_lower_bounds[i] = ( data_structure.numerics.boundl[variable_index] diff --git a/process/core/solver/objectives.py b/process/core/solver/objectives.py index 852bbd86f9..291a97fec8 100644 --- a/process/core/solver/objectives.py +++ b/process/core/solver/objectives.py @@ -24,7 +24,7 @@ } -def objective_function(minmax: int, data: DataStructure) -> float: +def objective_function(i_figure_merit: int, data: DataStructure) -> float: """Calculate the specified objective function Parameters @@ -53,11 +53,11 @@ def objective_function(minmax: int, data: DataStructure) -> float: data structure object for providing data to the objective function """ - figure_of_merit = abs(minmax) + figure_of_merit = abs(i_figure_merit) # -1 = maximise # +1 = minimise - objective_sign = np.sign(minmax) + objective_sign = np.sign(i_figure_merit) match figure_of_merit: case 1: @@ -94,7 +94,9 @@ def objective_function(minmax: int, data: DataStructure) -> float: objective_metric = data.times.t_plant_pulse_burn / 2.0e4 case 15: if data.costs.i_plant_availability != 1: - raise ProcessValueError("minmax=15 requires i_plant_availability=1") + raise ProcessValueError( + "i_figure_merit=15 requires i_plant_availability=1" + ) objective_metric = data.costs.f_t_plant_available case 16: objective_metric = 0.95 * (data.physics.rmajor / 9.0) - 0.05 * ( diff --git a/process/core/solver/solver.py b/process/core/solver/solver.py index a285de39c7..dee9fd6c32 100644 --- a/process/core/solver/solver.py +++ b/process/core/solver/solver.py @@ -179,10 +179,10 @@ def solve(self) -> int: bb = None if self.b is not None: - bb = np.identity(numerics.nvar) * self.b + bb = np.identity(numerics.n_iteration_variables) * self.b def _solver_callback(i: int, _result, _x, convergence_param: float): - numerics.nviter = i + 1 + numerics.n_solver_iterations = i + 1 global_variables.convergence_parameter = convergence_param print( f"{i + 1} | Convergence Parameter: {convergence_param:.3E}", @@ -256,7 +256,9 @@ def _ineq_cons_satisfied( except ValueError: itervar_name_list = "" - for count, iter_var in enumerate(numerics.ixc[: numerics.nvar]): + for count, iter_var in enumerate( + numerics.ixc[: numerics.n_iteration_variables] + ): itervar_name = 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 5094c5a9e2..2b12dd03f8 100644 --- a/process/core/solver/solver_handler.py +++ b/process/core/solver/solver_handler.py @@ -37,14 +37,14 @@ 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 - n = numerics.nvar + n = numerics.n_iteration_variables x = numerics.xcm[:n] bndl = numerics.itv_scaled_lower_bounds[:n] bndu = numerics.itv_scaled_upper_bounds[:n] # Define total number of constraints and equality constraints - m = numerics.neqns + numerics.nineqns - meq = numerics.neqns + m = numerics.n_equality_constraints + numerics.n_inequality_constraints + meq = numerics.n_equality_constraints # Evaluators() calculates the objective and constraint functions and # their gradients for a given vector x @@ -82,8 +82,8 @@ def run(self): # If VMCON has exited with error code 5 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) - if ifail == 5 and numerics.nviter < 2: + # Only do this if VMCON has not iterated (n_solver_iterations=1) + if ifail == 5 and numerics.n_solver_iterations < 2: print( "VMCON error code = 5. Rerunning VMCON with a new initial " "estimate of the second derivative matrix." diff --git a/process/data_structure/numerics.py b/process/data_structure/numerics.py index cbe7f39283..951f265f5b 100644 --- a/process/data_structure/numerics.py +++ b/process/data_structure/numerics.py @@ -1,32 +1,32 @@ import numpy as np -ipnvars: int = 177 -"""total number of variables available for iteration""" +N_ITERATION_VARIABLES_MAX: int = 177 +"""Total number of variables available for iteration (i.e. optimisation)""" -ipeqns: int = 92 -"""number of constraint equations available""" +N_CONSTRAINT_EQUATIONS_MAX: int = 92 +"""Number of constraint equations available to be activated""" -ipnfoms: int = 19 +N_FIGURES_MERIT_MAX: int = 19 """number of available figures of merit""" -ipvlam: int = ipeqns + 2 * ipnvars + 1 -iptnt: int = (ipeqns * (3 * ipeqns + 13)) / 2 -ipvp1: int = ipnvars + 1 +ipvlam: int = N_CONSTRAINT_EQUATIONS_MAX + 2 * N_ITERATION_VARIABLES_MAX + 1 +iptnt: int = (N_CONSTRAINT_EQUATIONS_MAX * (3 * N_CONSTRAINT_EQUATIONS_MAX + 13)) / 2 +ipvp1: int = N_ITERATION_VARIABLES_MAX + 1 -ioptimz: int = None +i_process_run_mode: int = None """Code operation switch: * -2 for evaluation mode (i.e. no optimisation) * 1 for optimisation mode (e.g. via VMCON) """ -minmax: int = None +i_figure_merit: int = None """ Switch for figure-of-merit (see lablmm for descriptions) negative => maximise, positive => minimise """ lablmm: list[str] = None -"""lablmm(ipnfoms) : labels describing figures of merit: