From cc96bb99e8140ddba767c6e995749ed09b8456bb Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 3 Jun 2026 09:12:34 +0100 Subject: [PATCH 1/7] Remove Fortran references from docstrings and comments across multiple files --- process/core/caller.py | 3 +- process/core/input.py | 12 +----- process/core/io/data_structure_dicts.py | 1 - process/core/io/in_dat/base.py | 43 +--------------------- process/core/io/vary_run/tools.py | 1 - process/core/output.py | 1 - process/core/process_output.py | 1 - process/core/scan.py | 2 +- process/core/solver/iteration_variables.py | 5 +-- process/core/solver/solver_handler.py | 8 +--- process/data_structure/numerics.py | 2 - process/main.py | 27 +++++--------- process/models/pfcoil.py | 6 +-- process/models/physics/physics.py | 2 +- process/models/tfcoil/base.py | 2 +- process/models/tfcoil/superconducting.py | 5 --- tests/integration/test_vmcon.py | 2 +- tests/unit/models/test_pfcoil.py | 5 +-- tracking/tracking_data.py | 2 - 19 files changed, 23 insertions(+), 107 deletions(-) diff --git a/process/core/caller.py b/process/core/caller.py index 01365d26ff..5ac162b3ff 100644 --- a/process/core/caller.py +++ b/process/core/caller.py @@ -249,8 +249,7 @@ def _call_models_once(self, xc: np.ndarray): """Call the physics and engineering models. This method is the principal caller of all the physics and - engineering models. Some are Fortran subroutines within modules, others - will be methods on Python model objects. + engineering models. Parameters ---------- diff --git a/process/core/input.py b/process/core/input.py index bfc45f88c3..fd0ef465d1 100644 --- a/process/core/input.py +++ b/process/core/input.py @@ -47,7 +47,7 @@ class InputVariable: """A variable to be parsed from the input file.""" module: Any - """The Fortran module that this variable should be set on.""" + """The module that this variable should be set on.""" type: type """The expected type of the variable""" range: tuple[NumberType, NumberType] | None = None @@ -1156,7 +1156,6 @@ def parse_input_file(data_structure_obj: DataStructure): continue # matches (variable name, array index, value) - # NOTE: array index is Fortran-based hence starts at 1. line_match = re.match( r"([a-zA-Z0-9_]+)(?:\(([0-9]+)\))?[ ]*=[ ]*([ +\-a-zA-Z0-9.,]+).*", stripped_line, @@ -1342,12 +1341,8 @@ def set_scalar_variable(name: str, value: ValidInputTypes, config: InputVariable """ current_value = getattr(config.module, name, ...) - # use ... sentinel because None is probably a valid return from Fortran - # and definately will be when moving to a Python data structure if current_value is ...: - error_msg = ( - f"Fortran module '{config.module}' does not have a variable '{name}'." - ) + error_msg = f"Module '{config.module}' does not have a variable '{name}'." raise ProcessValueError(error_msg) setattr(config.module, name, value) @@ -1356,9 +1351,6 @@ def set_scalar_variable(name: str, value: ValidInputTypes, config: InputVariable def set_array_variable(name: str, value: str, array_index: int, config: InputVariable): """Set an array variable in the `config.module`. - The way PROCESS input files are structured, each element of the array is provided on one line - so this function just needs to set the `value` at `array_index` (-1) because of Fortran-based indexing. - Parameters ---------- name : diff --git a/process/core/io/data_structure_dicts.py b/process/core/io/data_structure_dicts.py index 1a5d0e51bb..7f4f690fb3 100644 --- a/process/core/io/data_structure_dicts.py +++ b/process/core/io/data_structure_dicts.py @@ -59,7 +59,6 @@ def publish(self): class SourceDictionary(Dictionary): - # Dictionary created from Fortran source def __init__(self, name, dict_creator_func): Dictionary.__init__(self, name) # Function that creates the dict diff --git a/process/core/io/in_dat/base.py b/process/core/io/in_dat/base.py index 31989bc748..1fd2b2a9ee 100644 --- a/process/core/io/in_dat/base.py +++ b/process/core/io/in_dat/base.py @@ -1152,11 +1152,6 @@ def process_line(self, line_type, line): line_commentless = line.split("*")[0] array_name = line_commentless.split("(")[0] empty_array = dicts["DICT_DEFAULT"][array_name] - # empty_array is what the array is initialised to when it is - # declared in the Fortran. If the array is declared but not - # initialised until later in a separate "init" subroutine, then - # empty_array will be None. This is a side-effect of the need to - # re-initialise Fortran arrays in a separate subroutine. if empty_array is None: # Array is declared but not initialised at the same time; @@ -1405,61 +1400,25 @@ def process_array(self, line, empty_array): empty_array: Default array for this array name """ - # TODO This is a mess after the Fortran module variable - # re-initialisation work. It is hard to see how this can be improved - # as the regex method of Fortran inspection (i.e. Python-Fortran - # dictionaries) is increasingly untenable. An attempt is made here, - # but with a view to the dictionaries method being dropped in future - # in light of increasing Python f2py conversion. - line_commentless = line.split("*")[0] if "*" in line else line name = line_commentless.split("(")[0] index = int(line_commentless.split("(")[1].split(")")[0]) - 1 - # The -1 assumes all Fortran arrays begin at 1: they don't in Process! - # This bug would cause a Fortran index of 0 to be interpreted as a - # Python index of -1 (last element). This didn't cause any exceptions, - # but would obviously set the wrong list index. This now throws - # exceptions when trying to access [-1] of an empty list []. Hence it - # must be guarded against with the following: + if index == -1: index = 0 - # This will cause Fortran index 0 and 1 to overwrite the same Python - # index of 0, which is clearly awful. However, it is equally bad to the - # previous case above, where Fortran [0] would overwrite Python [-1]. - # There isn't a way of reconciling this without knowing whether the - # Fortran array begins at 0 or 1. - # TODO Either enforce Fortran arrays that start at 1 throughout, or - # find a way of determining the starting index of the array value = line_commentless.split("=")[-1].replace(",", "") - # Many arrays are now declared but not initialised until the "init" - # subroutine is run in each Fortran module, to allow re-initialisation - # on demand for a new run etc. - # In Fortran, we have a declared but uninitialised array of a given - # length. This results in an empty list in the value attribute here. - # However, we need to set the value for a given index. Therefore - # make a list of Nones, so that we can set the value for a given - # index. We don't know the length of the Fortran array (its value is - # []), so we have to make the Python list as long as this Fortran index. - # This way, at least the list is long enough for this Fortran index. - # TODO Again, this requires a more robust solution - list_len = len(self.data[name].value) # Length of Python list in self.data max_list_index = list_len - 1 # The greatest index in that Python list array_len = index + 1 - # The Fortran array must be at least this long - # If the default array is an empty list, make a list of Nones of - # matching length to the Fortran array if len(empty_array) == 0: empty_array = [None] * array_len - # If the Pyhton list is an empty list, make a list of Nones of - # matching length to the Fortran array if list_len == 0: self.data[name].value = [None] * array_len # Check the Python list is long enough to store the new index diff --git a/process/core/io/vary_run/tools.py b/process/core/io/vary_run/tools.py index 641367f6c3..fe7571e429 100644 --- a/process/core/io/vary_run/tools.py +++ b/process/core/io/vary_run/tools.py @@ -350,7 +350,6 @@ def set_variable_in_indat(in_dat, varname, value): elif "(" in varname: name = varname.split("(")[0] - # Fortran numbering converted to Python numbering! identity = int((varname.split("("))[1].split(")")[0]) - 1 in_dat.change_array(name, identity, float(value)) diff --git a/process/core/output.py b/process/core/output.py index 1368eaae7f..54f1698777 100644 --- a/process/core/output.py +++ b/process/core/output.py @@ -16,7 +16,6 @@ def write(models, data, _outfile): models : process.main.Models physics and engineering model objects _outfile : int - Fortran output unit identifier """ # ensure we are capturing warnings that occur in the 'output' stage as these are warnings diff --git a/process/core/process_output.py b/process/core/process_output.py index ff8956bd8b..26a23c9781 100644 --- a/process/core/process_output.py +++ b/process/core/process_output.py @@ -173,7 +173,6 @@ def ovarre(file, descr: str, varnam: str, value, output_flag: str = ""): if isinstance(value, np.ndarray): value = value.item() if isinstance(value, bytes): - # TODO: remove when Fortran is gone value = value.decode().strip() if isinstance(value, str): # try and convert the value to a float diff --git a/process/core/scan.py b/process/core/scan.py index 7a33222c49..fd956cedbe 100644 --- a/process/core/scan.py +++ b/process/core/scan.py @@ -189,7 +189,7 @@ def _missing_(cls, var): class Scan: - """Perform a parameter scan using the Fortran scan module.""" + """Perform a parameter scan using the scan module.""" def __init__(self, models: Model, solver: str, data: DataStructure): """Immediately run the run_scan() method. diff --git a/process/core/solver/iteration_variables.py b/process/core/solver/iteration_variables.py index a3add624a0..16cd1a6bba 100644 --- a/process/core/solver/iteration_variables.py +++ b/process/core/solver/iteration_variables.py @@ -15,7 +15,7 @@ class IterationVariable: name: str """The name of the variable""" module: str | Any - """The Fortran module that this variable should be set on.""" + """The module that this variable should be set on.""" lower_bound: float """The default lower bound of the iteration variable""" upper_bound: float @@ -28,7 +28,6 @@ class IterationVariable: """If `module.name` is an array, the iteration variable can only modify `array_index` of that array. - NOTE: The indexes start at 0 (despite indexing Fortran arrays). """ @@ -263,8 +262,6 @@ def load_iteration_variables(data): variable_index = data_structure.numerics.ixc[i] iteration_variable = ITERATION_VARIABLES[variable_index] - # use ... as the default return value because None might be a valid return from Fortran? - module = ( getattr(data, iteration_variable.module) if isinstance(iteration_variable.module, str) diff --git a/process/core/solver/solver_handler.py b/process/core/solver/solver_handler.py index 5094c5a9e2..f66917db7d 100644 --- a/process/core/solver/solver_handler.py +++ b/process/core/solver/solver_handler.py @@ -30,13 +30,9 @@ def __init__(self, models, solver_name, data): def run(self): """Run solver and retry if it fails in certain ways.""" - # Initialise iteration variables and bounds in Fortran load_iteration_variables(self.data) load_scaled_bounds() - # 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 x = numerics.xcm[:n] bndl = numerics.itv_scaled_lower_bounds[:n] @@ -96,12 +92,10 @@ def run(self): return ifail def output(self): - """Store results back in Fortran numerics module. + """Store results back in numerics module. Objective function value, solution vector and constraints vector. """ numerics.norm_objf = self.solver.objf - # Slicing required due to Fortran arrays being maximum possible, rather - # than required, size numerics.xcm[: self.solver.x.shape[0]] = self.solver.x numerics.rcm[: self.solver.conf.shape[0]] = self.solver.conf diff --git a/process/data_structure/numerics.py b/process/data_structure/numerics.py index 8499302c80..a048a7c414 100644 --- a/process/data_structure/numerics.py +++ b/process/data_structure/numerics.py @@ -156,8 +156,6 @@ def description(self): active_constraints: list[bool] = True """Logical array showing which constraints are active""" -# TODO Do not change the comments for lablcc: they are used to create the -# Python-Fortran dictionaries. This must be improved on. lablcc: list[str] = None """Labels describing constraint equations (corresponding itvs)