From 29f57b3a376219c08579c20e2f9a0228575ad3a3 Mon Sep 17 00:00:00 2001 From: Timothy Nunn Date: Fri, 19 Jun 2026 09:18:57 +0100 Subject: [PATCH 1/4] Remove IXC_SIMPLE dict --- process/core/io/data_structure_dicts.py | 11 ----------- process/core/io/in_dat/base.py | 10 ++-------- process/core/io/vary_run/tools.py | 9 ++++----- 3 files changed, 6 insertions(+), 24 deletions(-) diff --git a/process/core/io/data_structure_dicts.py b/process/core/io/data_structure_dicts.py index bc2995fd30..2a16273551 100644 --- a/process/core/io/data_structure_dicts.py +++ b/process/core/io/data_structure_dicts.py @@ -161,16 +161,6 @@ def dict_ixc_bounds(): return ixc_bounds -def dict_ixc_simple(): - # Returns dictionary mapping ixc no to iteration variable name - ixc_simple = {} - ixc_full = output_dict["DICT_IXC_FULL"] - for key, value in ixc_full.items(): - ixc_simple[key] = value["name"] - - return ixc_simple - - # cache the output of get_dicts so that it is never re-calculated in a given # process run. @cache @@ -195,7 +185,6 @@ def get_dicts(): SourceDictionary("DICT_VAR_TYPE", dict_var_type), SourceDictionary("DICT_IXC_FULL", dict_ixc_full), SourceDictionary("DICT_IXC_BOUNDS", dict_ixc_bounds), - SourceDictionary("DICT_IXC_SIMPLE", dict_ixc_simple), ]) # Make individual dicts within dict objects, process, then add to output_dict diff --git a/process/core/io/in_dat/base.py b/process/core/io/in_dat/base.py index 0360b504e5..e31229de3e 100644 --- a/process/core/io/in_dat/base.py +++ b/process/core/io/in_dat/base.py @@ -16,6 +16,7 @@ from process.core.exceptions import ProcessValidationError from process.core.io.data_structure_dicts import get_dicts from process.core.solver.constraints import ConstraintManager +from process.core.solver.iteration_variables import ITERATION_VARIABLES from process.data_structure.numerics import PROCESSRunMode @@ -322,10 +323,6 @@ def get_iteration_variables(data: dict): variable number, comment, upper and/or lower bounds if present """ variables = {} - - # Load dicts from dicts JSON file - dicts = get_dicts() - # List of variable numbers in IN.DAT variable_numbers = data["ixc"].value @@ -333,10 +330,7 @@ def get_iteration_variables(data: dict): for variable_number in variable_numbers: variable = {} - comment = dicts["DICT_IXC_SIMPLE"][ - str(variable_number).replace(",", ";").replace(".", ";").replace(":", ";") - ] - variable["comment"] = comment + variable["comment"] = ITERATION_VARIABLES[int(variable_number)].name # Set bounds if there are any if str(variable_number) in data["bounds"].value: diff --git a/process/core/io/vary_run/tools.py b/process/core/io/vary_run/tools.py index 9d871213a7..b6c4aa6bf9 100644 --- a/process/core/io/vary_run/tools.py +++ b/process/core/io/vary_run/tools.py @@ -11,6 +11,7 @@ from process.core.io.in_dat import InDat from process.core.io.mfile import MFile from process.core.model import DataStructure +from process.core.solver.iteration_variables import ITERATION_VARIABLES logger = logging.getLogger(__name__) @@ -19,8 +20,6 @@ def get_neqns_itervars(in_dat, wdir="."): """Returns the number of equations and a list of variable names of all iteration variables """ - # Load dicts from dicts JSON file - dicts = get_dicts() in_dat = InDat(Path(wdir, in_dat)) ixc_list = in_dat.data["ixc"].get_value @@ -28,7 +27,7 @@ def get_neqns_itervars(in_dat, wdir="."): itervars = [] for var in ixc_list: if var != "": - itervars += [dicts["DICT_IXC_SIMPLE"][str(var)]] + itervars += [ITERATION_VARIABLES[int(var)].name] if in_dat.number_of_itvars != len(itervars): raise ValueError("Number of iteration vars is not consistent") @@ -47,7 +46,7 @@ def update_ixc_bounds(indat, wdir="."): bounds = in_dat.data["bounds"].get_value for key, value in bounds.items(): - name = dicts["DICT_IXC_SIMPLE"][key] + name = ITERATION_VARIABLES[int(key)].name if "l" in value: dicts["DICT_IXC_BOUNDS"][name]["lb"] = float(value["l"]) @@ -151,7 +150,7 @@ def check_in_dat(filename): ixc_list = in_dat.data["ixc"].get_value for itervarno in ixc_list: - itervarname = dicts["DICT_IXC_SIMPLE"][str(itervarno)] + itervarname = ITERATION_VARIABLES[int(itervarno)].name try: lowerinputbound = dicts["DICT_INPUT_BOUNDS"][itervarname]["lb"] except KeyError as err: From 7cf6d82113252872975167ce623934266b4fc9c4 Mon Sep 17 00:00:00 2001 From: Timothy Nunn Date: Wed, 24 Jun 2026 13:19:45 +0100 Subject: [PATCH 2/4] Remove DICT_INPUT_BOUNDS dict --- process/core/input.py | 24 ++++++++++++++ process/core/io/data_structure_dicts.py | 30 ------------------ process/core/io/vary_run/tools.py | 42 ++++++++++--------------- 3 files changed, 41 insertions(+), 55 deletions(-) diff --git a/process/core/input.py b/process/core/input.py index 301d8b60d6..fd93a08972 100644 --- a/process/core/input.py +++ b/process/core/input.py @@ -107,6 +107,30 @@ def __post_init__(self): stacklevel=2, ) + def bounds(self) -> tuple[NumberType | None, NumberType | None]: + """Returns the upper and lower bounds of the input. + + Returns + ------- + lb: + The lower bound or None if no bound is available. + ub: + The upper bound or None if no bound is available/ + + Notes + ----- + Precedence is given to the `range` if both it and `choices` are specified. + """ + lb, ub = None, None + if self.range is not None: + lb, ub = self.range + + elif self.choices is not None and self.type in {int, float}: + lb = min(self.choices) + ub = max(self.choices) + + return lb, ub + INPUT_VARIABLES = { "runtitle": InputVariable("globals", str), diff --git a/process/core/io/data_structure_dicts.py b/process/core/io/data_structure_dicts.py index 2a16273551..239b6d7ba2 100644 --- a/process/core/io/data_structure_dicts.py +++ b/process/core/io/data_structure_dicts.py @@ -106,35 +106,6 @@ def dict_var_type(): return di -def dict_input_bounds(): - """Returns a dictionary matching variable names to dictionary containing - upper and lower bounds that PROCESS checks variable lies between when - reading IN.DAT. Looks in input.f90 for parse_real_variable and - parse_int_variable. - - Example of a line we are looking for: - call parse_real_variable('BETA', beta, 0.0D0, 1.0D0, & - - Example dictionary entry: - DICT_INPUT_BOUNDS['beta'] = {'lb' : 0.0, 'ub' : 1.0} - """ - di = {} - - for var_name, config in INPUT_VARIABLES.items(): - lb, ub = None, None - if config.range is not None: - lb, ub = config.range - - elif config.choices is not None and config.type in {int, float}: - lb = min(config.choices) - ub = max(config.choices) - - if lb is not None: - di[var_name] = {"lb": lb, "ub": ub} - - return di - - def dict_ixc_full(): """Function to return a dictionary matching str(ixc_no) to a dictionary containing the name, lower and upper bounds of that variable. @@ -181,7 +152,6 @@ def get_dicts(): HardcodedDictionary("DICT_DEFAULT", {}), HardcodedDictionary("DICT_MODULE", {}), HardcodedDictionary("DICT_DESCRIPTIONS", {}), - SourceDictionary("DICT_INPUT_BOUNDS", dict_input_bounds), SourceDictionary("DICT_VAR_TYPE", dict_var_type), SourceDictionary("DICT_IXC_FULL", dict_ixc_full), SourceDictionary("DICT_IXC_BOUNDS", dict_ixc_bounds), diff --git a/process/core/io/vary_run/tools.py b/process/core/io/vary_run/tools.py index b6c4aa6bf9..6f20891325 100644 --- a/process/core/io/vary_run/tools.py +++ b/process/core/io/vary_run/tools.py @@ -5,8 +5,8 @@ import logging import sys from pathlib import Path -from time import sleep +from process.core.input import INPUT_VARIABLES from process.core.io.data_structure_dicts import get_dicts from process.core.io.in_dat import InDat from process.core.io.mfile import MFile @@ -150,52 +150,44 @@ def check_in_dat(filename): ixc_list = in_dat.data["ixc"].get_value for itervarno in ixc_list: - itervarname = ITERATION_VARIABLES[int(itervarno)].name - try: - lowerinputbound = dicts["DICT_INPUT_BOUNDS"][itervarname]["lb"] - except KeyError as err: - # arrays do not have input bound checks - if "(" in itervarname: - continue - - print("Error in check_in_dat():") - print("There seems to be some information missing from the dicts.") - print("Please flag this up for a developer to investigate!") - print(itervarname, err) - print(dicts["DICT_INPUT_BOUNDS"][itervarname]) - sys.exit() + itvar = ITERATION_VARIABLES[int(itervarno)] + + # array variables do not support bounds + if "(" in itvar.name: + continue - if dicts["DICT_IXC_BOUNDS"][itervarname]["lb"] < lowerinputbound: + if itvar.name not in INPUT_VARIABLES: + error_msg = f"{itvar.name} does not have a corresponding input variable." + raise RuntimeError(error_msg) + + lowerinputbound, upperinputbound = INPUT_VARIABLES[itvar.name].bounds() + if dicts["DICT_IXC_BOUNDS"][itvar.name]["lb"] < lowerinputbound: print( "Warning: boundl for ", - itervarname, + itvar.name, " lies out of allowed input range!\n Reset boundl(", itervarno, ") to ", lowerinputbound, file=sys.stderr, ) - dicts["DICT_IXC_BOUNDS"][itervarname]["lb"] = lowerinputbound + dicts["DICT_IXC_BOUNDS"][itvar.name]["lb"] = lowerinputbound set_variable_in_indat( in_dat, "boundl(" + str(itervarno) + ")", lowerinputbound ) - sleep(1) - - upperinputbound = dicts["DICT_INPUT_BOUNDS"][itervarname]["ub"] - if dicts["DICT_IXC_BOUNDS"][itervarname]["ub"] > upperinputbound: + if dicts["DICT_IXC_BOUNDS"][itvar.name]["ub"] > upperinputbound: print( "Warning: boundu for", - itervarname, + itvar.name, f"lies out of allowed input range!\n Reset boundu({itervarno}) to", upperinputbound, file=sys.stderr, ) - dicts["DICT_IXC_BOUNDS"][itervarname]["ub"] = upperinputbound + dicts["DICT_IXC_BOUNDS"][itvar.name]["ub"] = upperinputbound set_variable_in_indat( in_dat, "boundu(" + str(itervarno) + ")", upperinputbound ) - sleep(1) in_dat.write_in_dat(output_filename=filename) From 2ba162243539a61b7e7eaa39678f42916375b6c9 Mon Sep 17 00:00:00 2001 From: Timothy Nunn Date: Thu, 25 Jun 2026 10:26:00 +0100 Subject: [PATCH 3/4] Remove NON_F_VALUES dict --- process/core/io/data_structure_dicts.py | 8 ---- process/core/io/vary_run/tools.py | 54 ++++++++++++------------- 2 files changed, 27 insertions(+), 35 deletions(-) diff --git a/process/core/io/data_structure_dicts.py b/process/core/io/data_structure_dicts.py index 239b6d7ba2..802547f9d4 100644 --- a/process/core/io/data_structure_dicts.py +++ b/process/core/io/data_structure_dicts.py @@ -20,13 +20,6 @@ INPUT_TYPE_MAP = {int: "int", float: "real", str: "string"} -NON_F_VALUES = [ - "f_j_cs_start_pulse_end_flat_top", - "f_c_plasma_non_inductive", - "feffcd", - "f_a_tf_turn_cable_copper", -] - logger = logging.getLogger(__name__) output_dict = {} @@ -148,7 +141,6 @@ def get_dicts(): # Some dicts depend on other dicts already existing in output_dicts, so # be careful if changing the order! dict_objects.extend([ - HardcodedDictionary("NON_F_VALUES", NON_F_VALUES), HardcodedDictionary("DICT_DEFAULT", {}), HardcodedDictionary("DICT_MODULE", {}), HardcodedDictionary("DICT_DESCRIPTIONS", {}), diff --git a/process/core/io/vary_run/tools.py b/process/core/io/vary_run/tools.py index 6f20891325..3a2e05dbb8 100644 --- a/process/core/io/vary_run/tools.py +++ b/process/core/io/vary_run/tools.py @@ -3,6 +3,7 @@ """ import logging +import re import sys from pathlib import Path @@ -77,7 +78,6 @@ def get_variable_range(itervars, factor, indat, data: DataStructure, wdir="."): (Default value = ".") """ # Load dicts from dicts JSON file - dicts = get_dicts() in_dat = InDat(Path(wdir, indat)) lbs = [] @@ -89,35 +89,29 @@ def get_variable_range(itervars, factor, indat, data: DataStructure, wdir="."): iteration_variable_index = iteration_variables.index(varname) lb = data.numerics.boundl[iteration_variable_index] ub = data.numerics.boundu[iteration_variable_index] - # for f-values we set the same range as in process - if varname[0] == "f" and (varname not in dicts["NON_F_VALUES"]): - lbs += [lb] - ubs += [ub] - # for non-f-values we modify the range with the factor - else: - value = get_from_indat_or_default(in_dat, varname) + value = get_from_indat_or_default(in_dat, varname) - if value is None: - print(f"Error: Iteration variable {varname} has None value!") - sys.exit() + if value is None: + print(f"Error: Iteration variable {varname} has None value!") + sys.exit() - # to allow the factor to have some influence - if value == 0.0: # noqa: RUF069 - value = 1.0 + # to allow the factor to have some influence + if value == 0.0: # noqa: RUF069 + value = 1.0 - # assure value is within bounds! - if value < lb: - value = lb - elif value > ub: - value = ub + # assure value is within bounds! + if value < lb: + value = lb + elif value > ub: + value = ub - if value > 0: - lbs += [max(value / factor, lb)] - ubs += [min(value * factor, ub)] - else: - lbs += [min(value / factor, lb)] - ubs += [max(value * factor, ub)] + if value > 0: + lbs += [max(value / factor, lb)] + ubs += [min(value * factor, ub)] + else: + lbs += [min(value / factor, lb)] + ubs += [max(value * factor, ub)] if lbs[-1] > ubs[-1]: print( @@ -323,10 +317,16 @@ def get_from_indat_or_default(in_dat, varname): or PROCESS default value """ dicts = get_dicts() + if "(" in varname: + name, index = re.match(r"([a-zA-Z0-9_]+)\(([0-9]+)\)", varname.strip()).groups() + + if varname in in_dat.data: + return in_dat.data[name].get_value[int(index) - 1] + + return dicts["DICT_DEFAULT"][name][int(index) - 1] + if varname in in_dat.data: return in_dat.data[varname].get_value - # Load dicts from dicts JSON file - dicts = get_dicts() return dicts["DICT_DEFAULT"][varname] From 81deab0445b13e2f79338d14a8945fac0fa81096 Mon Sep 17 00:00:00 2001 From: Timothy Nunn Date: Fri, 26 Jun 2026 08:52:38 +0100 Subject: [PATCH 4/4] Remove parameter group from IN.DAT --- process/core/io/in_dat/base.py | 53 +++++----------------------------- tests/unit/core/test_indat.py | 5 ++-- 2 files changed, 10 insertions(+), 48 deletions(-) diff --git a/process/core/io/in_dat/base.py b/process/core/io/in_dat/base.py index e31229de3e..8ce7e7f737 100644 --- a/process/core/io/in_dat/base.py +++ b/process/core/io/in_dat/base.py @@ -200,29 +200,6 @@ def find_line_type(line): return "Parameter" -def find_parameter_group(name): - """Function to find the module which the parameter belongs to. - - Parameters - ---------- - name: - Parameter name - - Returns - ------- - : - Return the module the parameter belongs to - """ - # Load dicts from dicts JSON file - dicts = get_dicts() - - # Search DICT_MODULES for parameter - for key in dicts["DICT_MODULE"]: - if name in dicts["DICT_MODULE"][key]: - return key - return None # Explicit return - - def write_title(title, out_file): """Function to write title line to file with fixed width @@ -646,7 +623,6 @@ def add_parameter(data, parameter_name, parameter_value): # Check that the parameter is not already in the dictionary if parameter_name not in data: - parameter_group = find_parameter_group(parameter_name) if "f_nd_impurity_electrons" in parameter_name: comment = dicts["DICT_DESCRIPTIONS"]["f_nd_impurity_electrons"] else: @@ -661,9 +637,7 @@ def add_parameter(data, parameter_name, parameter_value): ) comment = "" - param_data = INVariable( - parameter_name, parameter_value, "Parameter", parameter_group, comment - ) + param_data = INVariable(parameter_name, parameter_value, "Parameter", comment) data[parameter_name] = param_data @@ -989,7 +963,7 @@ def variable_bound_check(bound_number, bound_type): class INVariable: """Class to stores the information of a single variable from the - IN.DAT file + IN.DAT file. Parameters ---------- @@ -999,17 +973,14 @@ class INVariable: Item value v_type: Type of item - parameter_group: - PROCESS variable group item belongs to comment: Comment for item """ - def __init__(self, name, value, v_type, parameter_group, comment): + def __init__(self, name, value, v_type, comment): self.name = name self.value = value self.v_type = v_type - self.parameter_group = parameter_group self.comment = comment def __eq__(self, value): @@ -1018,16 +989,15 @@ def __eq__(self, value): self.name == value.name and self.value == value.value and self.v_type == value.v_type - and self.parameter_group == value.parameter_group ) def __hash__(self): - return hash((self.name, self.value, self.v_type, self.parameter_group)) + return hash((self.name, self.value, self.v_type)) def __repr__(self): return ( f"{type(self).__name__}(name={self.name!r}, value={self.value!r}, v_type={self.v_type!r}, " - f"parameter_group={self.parameter_group!r}, comment={self.comment!r})" + f"comment={self.comment!r})" ) @property @@ -1125,7 +1095,7 @@ def process_line(self, line_type, line): # Create bound variable class using INVariable class if the bounds entry # doesn't exist if "bounds" not in self.data: - self.data["bounds"] = INVariable("bounds", {}, "Bound", "Bound", "Bounds") + self.data["bounds"] = INVariable("bounds", {}, "Bound", "Bounds") # Constraint equations if line_type == "Constraint Equation": @@ -1159,8 +1129,6 @@ def process_line(self, line_type, line): empty_array = [] if array_name not in self.data: - parameter_group = find_parameter_group(array_name) - # Get parameter comment/description from dictionary comment = ( dicts["DICT_DESCRIPTIONS"][array_name] @@ -1175,7 +1143,7 @@ def process_line(self, line_type, line): # DICT_DEFAULT; don't want changes to data to change the # defaults self.data[array_name] = INVariable( - array_name, empty_array_copy, array_name, parameter_group, comment + array_name, empty_array_copy, array_name, comment ) self.process_array(line, empty_array) @@ -1228,9 +1196,6 @@ def process_parameter(self, line): ) sys.exit() - # Find group of variables the parameter belongs to - parameter_group = find_parameter_group(name) - # Get parameter comment/description from dictionary comment = ( dicts["DICT_DESCRIPTIONS"][name] @@ -1245,7 +1210,7 @@ def process_parameter(self, line): self.add_duplicate_variable(name) # Populate the IN.DAT dictionary with the information - self.data[name] = INVariable(name, value, "Parameter", parameter_group, comment) + self.data[name] = INVariable(name, value, "Parameter", comment) def process_constraint_equation(self, line): """Function to process constraint equation entry in IN.DAT @@ -1280,7 +1245,6 @@ def process_constraint_equation(self, line): "icc", value, "Constraint Equation", - "Constraint Equation", "Constraint Equations", ) @@ -1329,7 +1293,6 @@ def process_iteration_variables(self, line): "ixc", value, "Iteration Variable", - "Iteration Variable", "Iteration Variables", ) diff --git a/tests/unit/core/test_indat.py b/tests/unit/core/test_indat.py index 51758cd27e..44c4b95a43 100644 --- a/tests/unit/core/test_indat.py +++ b/tests/unit/core/test_indat.py @@ -13,10 +13,9 @@ def test_invariable_equality(value): """A test to check equality between INVariable's""" name = "test" v_type = "Parameter" - parameter_group = "test_group" - v1 = INVariable(name, value, v_type, parameter_group, "") - v2 = INVariable(name, value, v_type, parameter_group, "different comment") + v1 = INVariable(name, value, v_type, "") + v2 = INVariable(name, value, v_type, "different comment") assert v1 == v2