From 29a12c468af2e91dc89f880b75d10f560829b7b6 Mon Sep 17 00:00:00 2001 From: Clair Mould <86794332+clmould@users.noreply.github.com> Date: Thu, 27 Aug 2026 15:19:59 +0100 Subject: [PATCH 1/6] move validate_input to InDat class and rename to check_obsolete_variables --- process/core/init.py | 8 +- process/core/io/in_dat/base.py | 136 ++++++++++++++++++++++++++++++++- process/main.py | 122 +---------------------------- 3 files changed, 143 insertions(+), 123 deletions(-) diff --git a/process/core/init.py b/process/core/init.py index c8bcfa2392..422776be68 100644 --- a/process/core/init.py +++ b/process/core/init.py @@ -15,6 +15,7 @@ from process.core import constants, process_output from process.core.exceptions import ProcessValidationError from process.core.input import parse_input_file +from process.core.io.in_dat.base import InDat from process.core.solver import iteration_variables from process.core.solver.constraints import ConstraintManager from process.data_structure.blanket_variables import BlktModelTypes @@ -52,7 +53,7 @@ from process.core.data_structure.base import DataStructure -def init_process(data: DataStructure): +def init_process(data: DataStructure, update_obsolete: bool = False): """Routine that calls the initialisation routines This routine calls the main initialisation routines that set @@ -65,7 +66,10 @@ def init_process(data: DataStructure): # Creating and open the files MFile and OUTFile process_output.OutputFileManager.open_files(data.globals.output_prefix) - # Input any desired new initial values + filename = data.globals.output_prefix + "IN.DAT" + # InDat reads in and updates obsolete variables if requested + in_dat = InDat(filename=filename, update_obsolete=update_obsolete) # noqa: F841 + inputs = parse_input_file(data) # Set active constraints diff --git a/process/core/io/in_dat/base.py b/process/core/io/in_dat/base.py index 5e82b6a22c..e3a4088fa7 100644 --- a/process/core/io/in_dat/base.py +++ b/process/core/io/in_dat/base.py @@ -13,6 +13,7 @@ import sys from re import sub +from process.core.data_structure import obsolete_vars as ov from process.core.data_structure.dicts import get_dicts from process.core.exceptions import ProcessValidationError from process.core.solver.constraints import ConstraintManager @@ -1028,9 +1029,15 @@ class InDat: - Writing IN.DAT files - Storing information in dictionary for use in other codes - Alterations to IN.DAT + - Checking for and updating obsolete variables """ - def __init__(self, filename="IN.DAT", start_line=0): + def __init__( + self, + filename: str, + start_line: int = 0, + update_obsolete: bool = False, + ): """Initialise class Parameters @@ -1039,9 +1046,15 @@ def __init__(self, filename="IN.DAT", start_line=0): Name of input IN.DAT start_line: Line to start reading from + update_obsolete: + Whether to update obsolete variables in the IN.DAT or not """ self.filename = filename self.start_line = start_line + self.update_obsolete = update_obsolete + + # Check for obsolete variables and update if requested + self.check_obsolete_variables() # Initialise parameters self.in_dat_lines = [] @@ -1633,6 +1646,127 @@ def write_in_dat(self, output_filename="new_IN.DAT"): # Write parameters write_parameters(self.data, output) + def check_obsolete_variables(self): + """Checks the input IN.DAT file for any obsolete variables in the OBS_VARS dict + contained within obsolete_variables.py. + If obsolete variables are found, and if `update_obsolete` is set to True, + they are either removed or replaced by their updated names as specified + in the OBS_VARS dictionary. + + Raises + ------ + ValueError + If obsolete variables are present in the input file and update_obsolete + is False. + """ + obsolete_variables = ov.OBS_VARS + obsolete_vars_help_message = ov.OBS_VARS_HELP + + filename = self.filename + variables_in_in_dat = [] + modified_lines = [] + changes_made = [] # To store details of the changes + + with open(filename) as file: + for line in file: + # Skip comment lines or lines without an assignment + if line.startswith("*") or "=" not in line: + modified_lines.append(line) + continue + + # Extract the variable name before the separator + raw_variable_name = line.split("=", 1)[0].strip() + # handle cases where the variable name might have parentheses + variable_name = ( + raw_variable_name.split("(", 1)[0] + if "(" in raw_variable_name + else raw_variable_name + ) + + # Check if the variable is obsolete and needs replacing + if variable_name in obsolete_variables: + replacement = obsolete_variables.get(variable_name) + if self.update_obsolete: + # Prepare replacement or removal + if replacement is None: + # If no replacement is defined, comment out the line + modified_lines.append(f"* Obsolete: {line}") + changes_made.append( + f"Commented out obsolete variable: {variable_name}" + ) + else: + if isinstance(replacement, list): + # Raise an error if replacement is a list + replacement_str = ", ".join(replacement) + raise ValueError( + f"The variable '{variable_name}' is obsolete and " + "should be replaced by the following variables: " + f"{replacement_str}. " + "Please set their values accordingly." + ) + # Replace obsolete variable + modified_line = line.replace(variable_name, replacement, 1) + modified_lines.append( + f"* Replaced '{variable_name}' with " + f"'{replacement}'\n{modified_line}" + ) + changes_made.append( + f"Replaced '{variable_name}' with '{replacement}'" + ) + variables_in_in_dat.append(variable_name) + else: + # If replacement is False, add the line as-is + modified_lines.append(line) + variables_in_in_dat.append(variable_name) + else: + modified_lines.append(line) + + obs_vars_in_in_dat = [ + var for var in variables_in_in_dat if var in obsolete_variables + ] + + if obs_vars_in_in_dat: + if self.update_obsolete: + # If update_obsolete is True, write the modified content to the file + with open(filename, "w") as file: + file.writelines(modified_lines) + print( + "The IN.DAT file has been updated to replace or " + "comment out obsolete variables." + ) + print("Summary of changes made:") + for change in changes_made: + print(f" - {change}") + else: + # Only print the report if update_obsolete is False + message = ( + "The IN.DAT file contains obsolete variables " + "from the OBS_VARS dictionary. " + "The obsolete variables in your IN.DAT file are: " + f"{obs_vars_in_in_dat}. " + "Either remove these or replace them with " + "their updated variable names. " + "Use the --update-obsolete flag for this " + "to be done automatically." + ) + for obs_var in obs_vars_in_in_dat: + replacement = obsolete_variables.get(obs_var) + if replacement is None: + message += ( + f"\n\n{obs_var} is an obsolete variable " + "and needs to be removed." + ) + else: + message += ( + f"\n\n{obs_var} is an obsolete variable " + f"and needs to be replaced by {replacement}." + ) + message += f" {obsolete_vars_help_message.get(obs_var, '')}" + raise ValueError(message) + + else: + print("The IN.DAT file does not contain any obsolete variables.") + @property def number_of_constraints(self): """ diff --git a/process/main.py b/process/main.py index 1b5fec339c..21cb530d1c 100644 --- a/process/main.py +++ b/process/main.py @@ -335,7 +335,7 @@ def __init__( self.input_file = Path(input_file) self.data = data_structure or DataStructure() - self.validate_input(update_obsolete) + self.update_obsolete = update_obsolete logging_model_handler.clear_logs() self.set_filenames(filepath_out) self.initialise() @@ -422,7 +422,7 @@ def initialise(self): initialise_imprad(self.data) # Reads in input file - init.init_process(self.data) + init.init_process(self.data, self.update_obsolete) # Order optimisation parameters (arbitrary order in input file) # Ensures consistency and makes output comparisons more straightforward @@ -487,124 +487,6 @@ def append_input(self): mfile_file.write("***********************************************") mfile_file.writelines(input_lines) - def validate_input(self, replace_obsolete: bool = False): - """Checks the input IN.DAT file for any obsolete variables in the OBS_VARS dict - contained within obsolete_variables.py. - If obsolete variables are found, and if `replace_obsolete` is set to True, - they are either removed or replaced by their updated names as specified - in the OBS_VARS dictionary. - - Raises - ------ - ValueError - If obsolete variables are present in the input file. - """ - obsolete_variables = ov.OBS_VARS - obsolete_vars_help_message = ov.OBS_VARS_HELP - - filename = self.input_file - variables_in_in_dat = [] - modified_lines = [] - changes_made = [] # To store details of the changes - - with open(filename) as file: - for line in file: - # Skip comment lines or lines without an assignment - if line.startswith("*") or "=" not in line: - modified_lines.append(line) - continue - - # Extract the variable name before the separator - raw_variable_name = line.split("=", 1)[0].strip() - # handle cases where the variable name might have parentheses - variable_name = ( - raw_variable_name.split("(", 1)[0] - if "(" in raw_variable_name - else raw_variable_name - ) - - # Check if the variable is obsolete and needs replacing - if variable_name in obsolete_variables: - replacement = obsolete_variables.get(variable_name) - if replace_obsolete: - # Prepare replacement or removal - if replacement is None: - # If no replacement is defined, comment out the line - modified_lines.append(f"* Obsolete: {line}") - changes_made.append( - f"Commented out obsolete variable: {variable_name}" - ) - else: - if isinstance(replacement, list): - # Raise an error if replacement is a list - replacement_str = ", ".join(replacement) - raise ValueError( - f"The variable '{variable_name}' is obsolete and " - "should be replaced by the following variables: " - f"{replacement_str}. " - "Please set their values accordingly." - ) - # Replace obsolete variable - modified_line = line.replace(variable_name, replacement, 1) - modified_lines.append( - f"* Replaced '{variable_name}' with " - f"'{replacement}'\n{modified_line}" - ) - changes_made.append( - f"Replaced '{variable_name}' with '{replacement}'" - ) - variables_in_in_dat.append(variable_name) - else: - # If replacement is False, add the line as-is - modified_lines.append(line) - variables_in_in_dat.append(variable_name) - else: - modified_lines.append(line) - - obs_vars_in_in_dat = [ - var for var in variables_in_in_dat if var in obsolete_variables - ] - - if obs_vars_in_in_dat: - if replace_obsolete: - # If replace_obsolete is True, write the modified content to the file - with open(filename, "w") as file: - file.writelines(modified_lines) - print( - "The IN.DAT file has been updated to replace or " - "comment out obsolete variables." - ) - print("Summary of changes made:") - for change in changes_made: - print(f" - {change}") - else: - # Only print the report if replace_obsolete is False - message = ( - "The IN.DAT file contains obsolete variables " - "from the OBS_VARS dictionary. " - "The obsolete variables in your IN.DAT file are: " - f"{obs_vars_in_in_dat}. " - "Either remove these or replace them with " - "their updated variable names. " - ) - for obs_var in obs_vars_in_in_dat: - replacement = obsolete_variables.get(obs_var) - if replacement is None: - message += ( - f"\n\n{obs_var} is an obsolete variable " - "and needs to be removed." - ) - else: - message += ( - f"\n\n{obs_var} is an obsolete variable " - f"and needs to be replaced by {replacement}." - ) - message += f" {obsolete_vars_help_message.get(obs_var, '')}" - raise ValueError(message) - - else: - print("The IN.DAT file does not contain any obsolete variables.") - def validate_user_model(self): """Checks that a user-created model has been injected correctly From f3a1d500033bea09f201bb8b4ee9a12c8f14adcb Mon Sep 17 00:00:00 2001 From: Clair Mould <86794332+clmould@users.noreply.github.com> Date: Thu, 27 Aug 2026 16:44:12 +0100 Subject: [PATCH 2/6] Fix test_main.py failures from moving obsolete var check to InDat class --- tests/unit/test_main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 4f77df69ec..7c4e63167d 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -36,6 +36,7 @@ def single_run(monkeypatch, input_file, tmp_path): single_run.filepath = tmp_path single_run.models = None single_run.data = DataStructure() + single_run.update_obsolete = False single_run.set_filenames(None) single_run.initialise() return single_run From 6d5141bf66782871ba34de896539bf4226f7fe1f Mon Sep 17 00:00:00 2001 From: Clair Mould <86794332+clmould@users.noreply.github.com> Date: Thu, 27 Aug 2026 16:45:47 +0100 Subject: [PATCH 3/6] check_process unused arg _inputs --- process/core/init.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/process/core/init.py b/process/core/init.py index 422776be68..210bc059eb 100644 --- a/process/core/init.py +++ b/process/core/init.py @@ -70,7 +70,7 @@ def init_process(data: DataStructure, update_obsolete: bool = False): # InDat reads in and updates obsolete variables if requested in_dat = InDat(filename=filename, update_obsolete=update_obsolete) # noqa: F841 - inputs = parse_input_file(data) + _inputs = parse_input_file(data) # Set active constraints set_active_constraints(data) @@ -79,7 +79,7 @@ def init_process(data: DataStructure, update_obsolete: bool = False): set_device_type(data) # Check input data for errors/ambiguities - check_process(inputs, data) + check_process(data, _inputs) run_summary(data) @@ -251,7 +251,7 @@ def run_summary(data: DataStructure): ) -def check_process(inputs, data): # noqa: ARG001 +def check_process(data, _inputs): """Routine to reset specific variables if certain options are being used From d4d920cd66ce3d72b5b3402f24f05c9cdc5751f8 Mon Sep 17 00:00:00 2001 From: Clair Mould <86794332+clmould@users.noreply.github.com> Date: Thu, 27 Aug 2026 17:08:55 +0100 Subject: [PATCH 4/6] use fileprefix instead of output_prefix --- process/core/init.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/process/core/init.py b/process/core/init.py index 210bc059eb..ce517fae42 100644 --- a/process/core/init.py +++ b/process/core/init.py @@ -66,7 +66,7 @@ def init_process(data: DataStructure, update_obsolete: bool = False): # Creating and open the files MFile and OUTFile process_output.OutputFileManager.open_files(data.globals.output_prefix) - filename = data.globals.output_prefix + "IN.DAT" + filename = data.globals.fileprefix # InDat reads in and updates obsolete variables if requested in_dat = InDat(filename=filename, update_obsolete=update_obsolete) # noqa: F841 From 29dd82686d3c2b6a7211fc6d2ae413a6fcf856b0 Mon Sep 17 00:00:00 2001 From: Clair Mould <86794332+clmould@users.noreply.github.com> Date: Wed, 2 Sep 2026 13:54:06 +0100 Subject: [PATCH 5/6] fix test --- tests/unit/core/test_init.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/core/test_init.py b/tests/unit/core/test_init.py index 491612932e..36ee5e9f0c 100644 --- a/tests/unit/core/test_init.py +++ b/tests/unit/core/test_init.py @@ -16,7 +16,7 @@ def _validation_error_message(data): on whether an error was raised. """ try: - check_process(None, data) + check_process(data, None) except ProcessValidationError as error: return str(error) return "" @@ -32,7 +32,7 @@ def test_zero_thickness_superconducting_tf_is_rejected(): data.build.dr_tf_inboard = 0.0 with pytest.raises(ProcessValidationError, match="dr_tf_inboard"): - check_process(None, data) + check_process(data, None) def test_explicit_tf_thickness_is_accepted(): @@ -65,7 +65,7 @@ def test_zero_thickness_resistive_tf_is_rejected(): data.build.dr_tf_inboard = 0.0 with pytest.raises(ProcessValidationError, match="dr_tf_inboard"): - check_process(None, data) + check_process(data, None) def test_explicit_thickness_resistive_tf_is_accepted(): @@ -92,7 +92,7 @@ def test_thickness_iteration_variable_does_not_exempt(bad_value): data.numerics.ixc[0] = 13 with pytest.raises(ProcessValidationError, match="dr_tf_inboard"): - check_process(None, data) + check_process(data, None) def test_stellarator_is_not_checked(): From 1def96a8e05f5f530c2e00828d137d36341af0c2 Mon Sep 17 00:00:00 2001 From: Clair Mould <86794332+clmould@users.noreply.github.com> Date: Wed, 2 Sep 2026 13:56:46 +0100 Subject: [PATCH 6/6] remove unused import --- process/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/process/main.py b/process/main.py index 21cb530d1c..456b653750 100644 --- a/process/main.py +++ b/process/main.py @@ -39,7 +39,6 @@ import process # noqa: F401 from process.core import constants, init -from process.core.data_structure import obsolete_vars as ov from process.core.data_structure.base import DataStructure from process.core.io.cli_tools import LazyGroup, help_opt, indat_opt from process.core.io.mfile import MFile