From b11f8f70165279a6cd96a41a9675c5515517e631 Mon Sep 17 00:00:00 2001 From: Orlando Date: Sat, 8 Nov 2025 10:47:45 +0100 Subject: [PATCH 1/8] Update .gitignore include macOS specific file --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 9989d6d..e69b617 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,6 @@ pip-log.txt pip-delete-this-directory.txt .tox + +# macOS +.DS_Store \ No newline at end of file From 6aee7e9c3068b6393ab815da941667f56b4f756b Mon Sep 17 00:00:00 2001 From: Orlando Date: Sat, 8 Nov 2025 16:40:44 +0100 Subject: [PATCH 2/8] move node enums to seperate file --- precice_config_graph/enums.py | 141 ++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 precice_config_graph/enums.py diff --git a/precice_config_graph/enums.py b/precice_config_graph/enums.py new file mode 100644 index 0000000..3b4df34 --- /dev/null +++ b/precice_config_graph/enums.py @@ -0,0 +1,141 @@ +""" +This graph is intended for the preCICE logical-checker https://github.com/precice-forschungsprojekt/config-checker. + +You can find documentation under README.md, docs/Nodes.md and docs/Edges.md. + +This graph was developed by Simon Wazynski, Alexander Hutter and Orlando Ackermann as part of https://github.com/precice-forschungsprojekt. +""" + +from __future__ import annotations + +from enum import Enum + + +class MappingMethod(Enum): + NEAREST_NEIGHBOR = "nearest-neighbor" + NEAREST_PROJECTION = "nearest-projection" + NEAREST_NEIGHBOR_GRADIENT = "nearest-neighbor-gradient" + LINEAR_CELL_INTERPOLATION = "linear-cell-interpolation" + RBF_GLOBAL_ITERATIVE = "rbf-global-iterative" + RBF_GLOBAL_DIRECT = "rbf-global-direct" + RBF_PUM_DIRECT = "rbf-pum-direct" + RBF = "rbf" + AXIAL_GEOMETRIC_MULTISCALE = "axial-geometric-multiscale" + RADIAL_GEOMETRIC_MULTISCALE = "radial-geometric-multiscale" + + +class MappingConstraint(Enum): + CONSERVATIVE = "conservative" + CONSISTENT = "consistent" + SCALED_CONSISTENT_SURFACE = "scaled-consistent-surface" + SCALED_CONSISTENT_VOLUME = "scaled-consistent-volume" + + +class M2NType(Enum): + SOCKETS = "sockets" + MPI = "mpi" + MPI_MULTIPLE_PORTS = "mpi-multiple-ports" + + +class Direction(Enum): + READ = "read" + WRITE = "write" + + +class DataType(Enum): + SCALAR = "scalar" + VECTOR = "vector" + + +class TimingType(Enum): + WRITE_MAPPING_POST = "write-mapping-post" + READ_MAPPING_POST = "read-mapping-post" + + +class CouplingSchemeType(Enum): + SERIAL_EXPLICIT = "serial-explicit" + PARALLEL_EXPLICIT = "parallel-explicit" + SERIAL_IMPLICIT = "serial-implicit" + PARALLEL_IMPLICIT = "parallel-implicit" + # This enum does not include coupling-scheme:multi, since it is modeled with a different node type + + +class ActionType(Enum): + MULTIPLY_BY_AREA = "multiply-by-area" + DIVIDE_BY_AREA = "divide-by-area" + SUMMATION = "summation" + PYTHON = "python" + RECORDER = "recorder" + + +class ExportFormat(Enum): + VTK = "vtk" + VTU = "vtu" + VTP = "vtp" + CSV = "csv" + + +class AccelerationType(Enum): + AITKEN = "aitken" + IQN_ILS = "IQN-ILS" + IQN_IMVJ = "IQN-IMVJ" + CONSTANT = "constant" + + +class ConvergenceMeasureType(Enum): + ABSOLUTE = "absolute" + ABSOLUTE_OR_RELATIVE = "absolute-or-relative" + RELATIVE = "relative" + RESIDUAL_RELATIVE = "residual-relative" + + +class MappingExecutorType(Enum): + CPU = "cpu" + CUDA = "cuda" + OPENMP = "openmp" + HIP = "hip" + + +class PreconditionerType(Enum): + CONSTANT = "constant" + VALUE = "value" + RESIDUAL = "residual" + RESIDUAL_SUM = "residual-sum" + + +class AccelerationFilterType(Enum): + QR1 = "QR1" + QR1ABSOLUTE = "QR1-absolute" + QR2 = "QR2" + QR3 = "QR3" + + +class MappingBasisFunctionType(Enum): + COMPACT_POLYNOMIAL_C0 = "compact-polynomial-c0" + COMPACT_POLYNOMIAL_C2 = "compact-polynomial-c2" + COMPACT_POLYNOMIAL_C4 = "compact-polynomial-c4" + COMPACT_POLYNOMIAL_C6 = "compact-polynomial-c6" + COMPACT_POLYNOMIAL_C8 = "compact-polynomial-c8" + COMPACT_TPS_C2 = "compact-tps-c2" + MULTIQUADRICS = "multiquadrics" + INVERSE_MULTIQUADRICS = "inverse-multiquadrics" + GAUSSIAN = "gaussian" + THIN_PLATE_SPLINE = "thin-plate-spline" + VOLUME_SPLINE = "volume-spline" + + +class MappingMultiscaleType(Enum): + SPREAD = "spread" + COLLECT = "collect" + + +class MappingMultiscaleAxis(Enum): + X = "x" + Y = "y" + Z = "z" + + +class MappingPolynomialType(Enum): + SEPARATE = "separate" + ON = "on" + OFF = "off" From 4ab0fc6632e301ad65958119263ce41634b7791d Mon Sep 17 00:00:00 2001 From: Orlando Date: Sat, 8 Nov 2025 16:41:15 +0100 Subject: [PATCH 3/8] adjust tests to new enum file and graph building --- precice_config_graph/graph.py | 63 ++++++++++--------- tests/example-configs/actions/actions_test.py | 2 +- .../multi-coupling/multi-coupling_test.py | 48 +++++++------- .../simple-good/simple-good_test.py | 2 +- 4 files changed, 55 insertions(+), 60 deletions(-) diff --git a/precice_config_graph/graph.py b/precice_config_graph/graph.py index 96c6785..8a53035 100644 --- a/precice_config_graph/graph.py +++ b/precice_config_graph/graph.py @@ -14,6 +14,7 @@ from . import nodes as n from .edges import Edge +from . import enums as e from .xml_processing import convert_string_to_bool LINK_GRAPH_ISSUES: str = "'\033[1;36mhttps://github.com/precice-forschungsprojekt/config-graph/issues\033[0m'" @@ -105,12 +106,12 @@ def get_attribute(e: etree.Element, key: str): for (data_el, kind) in find_all_with_prefix(root, "data"): name = get_attribute(data_el, "name") try: - type = n.DataType(kind) + type = e.DataType(kind) except ValueError: - possible_types_list = get_enum_values(n.DataType) + possible_types_list = get_enum_values(e.DataType) error_unknown_type(data_el, kind, possible_types_list) line: int = data_el.sourceline - node = n.DataNode(name, type, line) + node = n.DataNode(name, type, line=line) data_nodes[name] = node # Meshes – @@ -149,7 +150,7 @@ def get_attribute(e: etree.Element, key: str): mesh = mesh_nodes[mesh_name] line: int = write_data_el.sourceline - write_data = n.WriteDataNode(participant, data, mesh, line) + write_data = n.WriteDataNode(participant, data, mesh, line=line) participant.write_data.append(write_data) write_data_nodes.append(write_data) @@ -162,7 +163,7 @@ def get_attribute(e: etree.Element, key: str): mesh = mesh_nodes[mesh_name] line: int = read_data_el.sourceline - read_data = n.ReadDataNode(participant, data, mesh, line) + read_data = n.ReadDataNode(participant, data, mesh, line=line) participant.read_data.append(read_data) read_data_nodes.append(read_data) @@ -177,9 +178,9 @@ def get_attribute(e: etree.Element, key: str): to_mesh = mesh_nodes[to_mesh_name] if to_mesh_name else None try: - method = n.MappingMethod(kind) + method = e.MappingMethod(kind) except ValueError: - possible_method_list = get_enum_values(n.MappingMethod) + possible_method_list = get_enum_values(e.MappingMethod) possible_methods: str = list_to_string(possible_method_list) message: str = ( 'Unknown method "' @@ -190,7 +191,7 @@ def get_attribute(e: etree.Element, key: str): + possible_methods ) error(message) - constraint = n.MappingConstraint(get_attribute(mapping_el, "constraint")) + constraint = e.MappingConstraint(get_attribute(mapping_el, "constraint")) if not from_mesh and not to_mesh: error_missing_attribute(mapping_el, 'from" or "to') @@ -199,13 +200,13 @@ def get_attribute(e: etree.Element, key: str): mapping = n.MappingNode( participant, - n.Direction(direction), + e.Direction(direction), just_in_time, method, constraint, from_mesh, to_mesh, - line, + line=line, ) participant.mappings.append(mapping) @@ -215,19 +216,19 @@ def get_attribute(e: etree.Element, key: str): # for (_, kind) in find_all_with_prefix(participant_el, "export"): try: - type = n.ExportFormat(kind) + type = e.ExportFormat(kind) except ValueError: - possible_types_list = get_enum_values(n.ExportFormat) + possible_types_list = get_enum_values(e.ExportFormat) error_unknown_type(_, kind, possible_types_list) line: int = _.sourceline - export = n.ExportNode(participant, type, line) + export = n.ExportNode(participant, type, line=line) export_nodes.append(export) # Actions # for (action_el, kind) in find_all_with_prefix(participant_el, "action"): mesh = mesh_nodes[get_attribute(action_el, "mesh")] - timing = n.TimingType(get_attribute(action_el, "timing")) + timing = e.TimingType(get_attribute(action_el, "timing")) target_data = None if kind in ["multiply-by-area", "divide-by-area", "summation", "python"]: @@ -244,14 +245,14 @@ def get_attribute(e: etree.Element, key: str): ) try: - type = n.ActionType(kind) + type = e.ActionType(kind) except ValueError: - possible_types_list = get_enum_values(n.ActionType) + possible_types_list = get_enum_values(e.ActionType) error_unknown_type(action_el, kind, possible_types_list) line: int = action_el.sourceline action = n.ActionNode( - participant, type, mesh, timing, target_data, source_data, line + participant, type, mesh, timing, target_data, source_data, line=line ) action_nodes.append(action) @@ -262,7 +263,7 @@ def get_attribute(e: etree.Element, key: str): mesh = mesh_nodes[get_attribute(watch_point_el, "mesh")] line: int = watch_point_el.sourceline - watch_point = n.WatchPointNode(point_name, participant, mesh, line) + watch_point = n.WatchPointNode(point_name, participant, mesh, line=line) watch_point_nodes.append(watch_point) # Watch-Integral @@ -272,7 +273,7 @@ def get_attribute(e: etree.Element, key: str): mesh = mesh_nodes[get_attribute(watch_integral_el, "mesh")] line: int = watch_integral_el.sourceline - watch_integral = n.WatchIntegralNode(integral_name, participant, mesh, line) + watch_integral = n.WatchIntegralNode(integral_name, participant, mesh, line=line) watch_integral_nodes.append(watch_integral) # Now that participant_node is completely built, add it and children to the graph and our dictionary @@ -303,7 +304,7 @@ def get_attribute(e: etree.Element, key: str): line: int = receive_mesh_el.sourceline receive_mesh = n.ReceiveMeshNode( - participant, mesh, from_participant, api_access, line + participant, mesh, from_participant, api_access, line=line ) participant.receive_meshes.append(receive_mesh) receive_mesh_nodes.append(receive_mesh) @@ -331,7 +332,7 @@ def get_attribute(e: etree.Element, key: str): second_participant_name = get_attribute(participants, "second") second_participant = participant_nodes[second_participant_name] - type = n.CouplingSchemeType(kind) + type = e.CouplingSchemeType(kind) coupling_scheme = n.CouplingSchemeNode( type, first_participant, second_participant, line=line @@ -389,7 +390,7 @@ def get_attribute(e: etree.Element, key: str): line: int = exchange_el.sourceline exchange = n.ExchangeNode( - coupling_scheme, data, mesh, from_participant, to_participant, line + coupling_scheme, data, mesh, from_participant, to_participant, line=line ) coupling_scheme.exchanges.append(exchange) exchange_nodes.append(exchange) @@ -409,9 +410,9 @@ def get_attribute(e: etree.Element, key: str): error(message) try: - type = n.AccelerationType(a_kind) + type = e.AccelerationType(a_kind) except ValueError: - possible_types_list = get_enum_values(n.AccelerationType) + possible_types_list = get_enum_values(e.AccelerationType) error_unknown_type(acceleration_el, a_kind, possible_types_list) line: int = acceleration_el.sourceline @@ -435,7 +436,7 @@ def get_attribute(e: etree.Element, key: str): a_mesh_name = get_attribute(a_data, "mesh") mesh = mesh_nodes[a_mesh_name] line: int = a_data.sourceline - a_data_node = n.AccelerationDataNode(acceleration, data, mesh, line) + a_data_node = n.AccelerationDataNode(acceleration, data, mesh, line=line) acceleration.data.append(a_data_node) acceleration_data_nodes.append(a_data_node) @@ -448,9 +449,9 @@ def get_attribute(e: etree.Element, key: str): match kind: case "serial-implicit" | "parallel-implicit" | "multi": try: - type = n.ConvergenceMeasureType(c_kind) + type = e.ConvergenceMeasureType(c_kind) except ValueError: - possible_types_list = get_enum_values(n.ConvergenceMeasureType) + possible_types_list = get_enum_values(e.ConvergenceMeasureType) error_unknown_type( convergence_measure_el, c_kind, possible_types_list ) @@ -462,7 +463,7 @@ def get_attribute(e: etree.Element, key: str): line: int = convergence_measure_el.sourceline convergence_measure = n.ConvergenceMeasureNode( - coupling_scheme, type, c_data, c_mesh, line + coupling_scheme, type, c_data, c_mesh, line=line ) coupling_scheme.convergence_measures.append(convergence_measure) convergence_measure_nodes.append(convergence_measure) @@ -487,9 +488,9 @@ def get_attribute(e: etree.Element, key: str): # M2N – for (m2n_el, kind) in find_all_with_prefix(root, "m2n"): try: - type = n.M2NType(kind) + type = e.M2NType(kind) except ValueError: - possible_types_list = get_enum_values(n.M2NType) + possible_types_list = get_enum_values(e.M2NType) error_unknown_type(m2n_el, kind, possible_types_list) acceptor_name = get_attribute(m2n_el, "acceptor") acceptor = participant_nodes[acceptor_name] @@ -497,7 +498,7 @@ def get_attribute(e: etree.Element, key: str): connector = participant_nodes[connector_name] line: int = m2n_el.sourceline - m2n = n.M2NNode(type, acceptor, connector, line) + m2n = n.M2NNode(type, acceptor, connector, line=line) m2n_nodes.append(m2n) # BUILD GRAPH diff --git a/tests/example-configs/actions/actions_test.py b/tests/example-configs/actions/actions_test.py index e6f5264..6b9d6bc 100644 --- a/tests/example-configs/actions/actions_test.py +++ b/tests/example-configs/actions/actions_test.py @@ -3,7 +3,7 @@ from precice_config_graph import graph, xml_processing from precice_config_graph import nodes as n from precice_config_graph.edges import Edge -from precice_config_graph.nodes import ( +from precice_config_graph.enums import ( DataType, Direction, TimingType, diff --git a/tests/example-configs/multi-coupling/multi-coupling_test.py b/tests/example-configs/multi-coupling/multi-coupling_test.py index fc3096d..5ece324 100644 --- a/tests/example-configs/multi-coupling/multi-coupling_test.py +++ b/tests/example-configs/multi-coupling/multi-coupling_test.py @@ -3,13 +3,7 @@ from precice_config_graph import graph, xml_processing from precice_config_graph import nodes as n from precice_config_graph.edges import Edge -from precice_config_graph.nodes import ( - DataType, - Direction, - M2NType, - MappingMethod, - MappingConstraint, -) +from precice_config_graph import enums as e def test_graph(): @@ -21,12 +15,12 @@ def test_graph(): edges = [] # Data - n_data_forces1 = n.DataNode("Forces1", DataType.VECTOR) - n_data_forces2 = n.DataNode("Forces2", DataType.VECTOR) - n_data_forces3 = n.DataNode("Forces3", DataType.VECTOR) - n_data_displacements1 = n.DataNode("Displacements1", DataType.VECTOR) - n_data_displacements2 = n.DataNode("Displacements2", DataType.VECTOR) - n_data_displacements3 = n.DataNode("Displacements3", DataType.VECTOR) + n_data_forces1 = n.DataNode("Forces1", e.DataType.VECTOR) + n_data_forces2 = n.DataNode("Forces2", e.DataType.VECTOR) + n_data_forces3 = n.DataNode("Forces3", e.DataType.VECTOR) + n_data_displacements1 = n.DataNode("Displacements1", e.DataType.VECTOR) + n_data_displacements2 = n.DataNode("Displacements2", e.DataType.VECTOR) + n_data_displacements3 = n.DataNode("Displacements3", e.DataType.VECTOR) # Meshes n_mesh_nastin1 = n.MeshNode("NASTIN_Mesh1", [n_data_forces1]) @@ -131,10 +125,10 @@ def test_graph(): n_participant_nastin.mappings = [ n.MappingNode( parent_participant=n_participant_nastin, - direction=Direction.WRITE, + direction=e.Direction.WRITE, just_in_time=False, - method=MappingMethod.NEAREST_NEIGHBOR, - constraint=MappingConstraint.CONSERVATIVE, + method=e.MappingMethod.NEAREST_NEIGHBOR, + constraint=e.MappingConstraint.CONSERVATIVE, from_mesh=from_mesh, to_mesh=to_mesh, ) @@ -240,13 +234,13 @@ def test_graph(): # M2N nodes m2n_nastin_solidz1 = n.M2NNode( - M2NType.SOCKETS, n_participant_nastin, n_participant_solizd1 + e.M2NType.SOCKETS, n_participant_nastin, n_participant_solizd1 ) m2n_nastin_solidz2 = n.M2NNode( - M2NType.SOCKETS, n_participant_nastin, n_participant_solizd2 + e.M2NType.SOCKETS, n_participant_nastin, n_participant_solizd2 ) m2n_nastin_solidz3 = n.M2NNode( - M2NType.SOCKETS, n_participant_nastin, n_participant_solizd3 + e.M2NType.SOCKETS, n_participant_nastin, n_participant_solizd3 ) edges += [ @@ -353,8 +347,8 @@ def test_graph(): ] ) - acceleration = n.AccelerationNode(n_coupling_scheme, n.AccelerationType.IQN_ILS) - n_coupling_scheme.accelerations.append(acceleration) + acceleration = n.AccelerationNode(n_coupling_scheme, e.AccelerationType.IQN_ILS) + n_coupling_scheme.accelerations.append(acceleration) # TODO acceleration acceleration_data1 = n.AccelerationDataNode( acceleration, n_data_forces1, n_mesh_solidz1 ) @@ -397,37 +391,37 @@ def test_graph(): convergence_measures = [ n.ConvergenceMeasureNode( n_coupling_scheme, - n.ConvergenceMeasureType.RELATIVE, + e.ConvergenceMeasureType.RELATIVE, n_data_displacements1, n_mesh_solidz1, ), n.ConvergenceMeasureNode( n_coupling_scheme, - n.ConvergenceMeasureType.RELATIVE, + e.ConvergenceMeasureType.RELATIVE, n_data_displacements2, n_mesh_solidz2, ), n.ConvergenceMeasureNode( n_coupling_scheme, - n.ConvergenceMeasureType.RELATIVE, + e.ConvergenceMeasureType.RELATIVE, n_data_displacements3, n_mesh_solidz3, ), n.ConvergenceMeasureNode( n_coupling_scheme, - n.ConvergenceMeasureType.RELATIVE, + e.ConvergenceMeasureType.RELATIVE, n_data_forces1, n_mesh_solidz1, ), n.ConvergenceMeasureNode( n_coupling_scheme, - n.ConvergenceMeasureType.RELATIVE, + e.ConvergenceMeasureType.RELATIVE, n_data_forces2, n_mesh_solidz2, ), n.ConvergenceMeasureNode( n_coupling_scheme, - n.ConvergenceMeasureType.RELATIVE, + e.ConvergenceMeasureType.RELATIVE, n_data_forces3, n_mesh_solidz3, ), diff --git a/tests/example-configs/simple-good/simple-good_test.py b/tests/example-configs/simple-good/simple-good_test.py index 6ef77a9..bd14854 100644 --- a/tests/example-configs/simple-good/simple-good_test.py +++ b/tests/example-configs/simple-good/simple-good_test.py @@ -3,7 +3,7 @@ from precice_config_graph import graph, xml_processing from precice_config_graph import nodes as n from precice_config_graph.edges import Edge -from precice_config_graph.nodes import ( +from precice_config_graph.enums import ( DataType, Direction, CouplingSchemeType, From f2efa7baec565195442836855c340497a60f7a2e Mon Sep 17 00:00:00 2001 From: Orlando Date: Sat, 8 Nov 2025 16:42:16 +0100 Subject: [PATCH 4/8] add to_xml() and new parameters all of these parameters are NOT needed for graph generation. They will be needed when creating a config.xml from the graph --- precice_config_graph/nodes.py | 595 ++++++++++++++++++++++++---------- 1 file changed, 422 insertions(+), 173 deletions(-) diff --git a/precice_config_graph/nodes.py b/precice_config_graph/nodes.py index ab4862e..779acd7 100644 --- a/precice_config_graph/nodes.py +++ b/precice_config_graph/nodes.py @@ -8,101 +8,23 @@ from __future__ import annotations -from enum import Enum - - -class MappingMethod(Enum): - NEAREST_NEIGHBOR = "nearest-neighbor" - NEAREST_PROJECTION = "nearest-projection" - NEAREST_NEIGHBOR_GRADIENT = "nearest-neighbor-gradient" - LINEAR_CELL_INTERPOLATION = "linear-cell-interpolation" - RBF_GLOBAL_ITERATIVE = "rbf-global-iterative" - RBF_GLOBAL_DIRECT = "rbf-global-direct" - RBF_PUM_DIRECT = "rbf-pum-direct" - RBF = "rbf" - AXIAL_GEOMETRIC_MULTISCALE = "axial-geometric-multiscale" - RADIAL_GEOMETRIC_MULTISCALE = "radial-geometric-multiscale" - - -class MappingConstraint(Enum): - CONSERVATIVE = "conservative" - CONSISTENT = "consistent" - SCALED_CONSISTENT_SURFACE = "scaled-consistent-surface" - SCALED_CONSISTENT_VOLUME = "scaled-consistent-volume" - - -class M2NType(Enum): - SOCKETS = "sockets" - MPI = "mpi" - MPI_MULTIPLE_PORTS = "mpi-multiple-ports" - - -class Direction(Enum): - READ = "read" - WRITE = "write" - - -class DataType(Enum): - SCALAR = "scalar" - VECTOR = "vector" - - -class TimingType(Enum): - WRITE_MAPPING_POST = "write-mapping-post" - READ_MAPPING_POST = "read-mapping-post" - - -class CouplingSchemeType(Enum): - SERIAL_EXPLICIT = "serial-explicit" - PARALLEL_EXPLICIT = "parallel-explicit" - SERIAL_IMPLICIT = "serial-implicit" - PARALLEL_IMPLICIT = "parallel-implicit" - # This enum does not include coupling-scheme:multi, since it is modeled with a different node type - - -class ActionType(Enum): - MULTIPLY_BY_AREA = "multiply-by-area" - DIVIDE_BY_AREA = "divide-by-area" - SUMMATION = "summation" - PYTHON = "python" - RECORDER = "recorder" - - -class ExportFormat(Enum): - VTK = "vtk" - VTU = "vtu" - VTP = "vtp" - CSV = "csv" - - -class AccelerationType(Enum): - AITKEN = "aitken" - IQN_ILS = "IQN-ILS" - IQN_IMVJ = "IQN-IMVJ" - CONSTANT = "constant" - - -class ConvergenceMeasureType(Enum): - ABSOLUTE = "absolute" - ABSOLUTE_OR_RELATIVE = "absolute-or-relative" - RELATIVE = "relative" - RESIDUAL_RELATIVE = "residual-relative" +from . import enums as e class ParticipantNode: def __init__( - self, - name: str, - write_data: list[WriteDataNode] = None, - read_data: list[ReadDataNode] = None, - receive_meshes: list[ReceiveMeshNode] = None, - provide_meshes: list[MeshNode] = None, - mappings: list[MappingNode] = None, - exports: list[ExportNode] = None, - actions: list[ActionNode] = None, - watchpoints: list[WatchPointNode] = None, - watch_integrals: list[WatchIntegralNode] = None, - line: int = None, + self, + name: str, + write_data: list[WriteDataNode] = None, + read_data: list[ReadDataNode] = None, + receive_meshes: list[ReceiveMeshNode] = None, + provide_meshes: list[MeshNode] = None, + mappings: list[MappingNode] = None, + exports: list[ExportNode] = None, + actions: list[ActionNode] = None, + watchpoints: list[WatchPointNode] = None, + watch_integrals: list[WatchIntegralNode] = None, + line: int = None, ): self.name = name @@ -153,27 +75,59 @@ def __init__( self.line = line + def to_xml(self): + xml_str: str = f"\n" + for provide_mesh in self.provide_meshes: + xml_str += f" \n" + for receive_mesh in self.receive_meshes: + xml_str += f" {receive_mesh.to_xml()}\n" + for write_data in self.write_data: + xml_str += f" {write_data.to_xml()}\n" + for read_data in self.read_data: + xml_str += f" {read_data.to_xml()}\n" + for mapping in self.mappings: + xml_str += f" {mapping.to_xml()}\n" + + for action in self.actions: + xml_str += f" {action.to_xml()}\n" + for export in self.exports: + xml_str += f" {export.to_xml()}\n" + for watchpoint in self.watchpoints: + xml_str += f" {watchpoint.to_xml()}\n" + for watch_integral in self.watch_integrals: + xml_str += f" {watch_integral.to_xml()}\n" + + xml_str += f"\n" + return xml_str + class MeshNode: - def __init__(self, name: str, use_data: list[DataNode] = None, line: int = None): + def __init__(self, name: str, use_data: list[DataNode] = None, line: int = None, dimensions: int = 3): self.name = name if use_data is None: self.use_data = [] else: self.use_data = use_data - + self.dimensions = dimensions self.line = line + def to_xml(self): + xml_str: str = f"\n" + for data in self.use_data: + xml_str += f" \n" + xml_str += f"\n" + return xml_str + class ReceiveMeshNode: def __init__( - self, - participant: ParticipantNode, - mesh: MeshNode, - from_participant: ParticipantNode, - api_access: bool, - line: int = None, + self, + participant: ParticipantNode, + mesh: MeshNode, + from_participant: ParticipantNode, + api_access: bool, + line: int = None, ): self.participant = participant self.mesh = mesh @@ -181,17 +135,24 @@ def __init__( self.api_access = api_access self.line = line + def to_xml(self): + api_access_str: str = "api-access=\"true\" " if self.api_access else "" + xml_str: str = f"" + return xml_str + class CouplingSchemeNode: def __init__( - self, - type: CouplingSchemeType, - first_participant: ParticipantNode, - second_participant: ParticipantNode, - exchanges: list[ExchangeNode] = None, - accelerations: list[AccelerationNode] = None, - convergence_measures: list[ConvergenceMeasureNode] = None, - line: int = None, + self, + type: e.CouplingSchemeType, + first_participant: ParticipantNode, + second_participant: ParticipantNode, + exchanges: list[ExchangeNode] = None, + accelerations: list[AccelerationNode] = None, + convergence_measures: list[ConvergenceMeasureNode] = None, + line: int = None, + max_time_windows: int = 10, + time_window_size: float = 1e-1, ): self.type = type self.first_participant = first_participant @@ -213,17 +174,33 @@ def __init__( self.convergence_measures = convergence_measures self.line = line + self.max_time_windows = max_time_windows + self.time_window_size = time_window_size + + def to_xml(self): + xml_str: str = f"\n" + xml_str += f" \n" + xml_str += f" \n" + xml_str += f" \n" + for exchange in self.exchanges: + xml_str += f" {exchange.to_xml()}\n" + for acceleration in self.accelerations: + xml_str += f" {acceleration.to_xml()}\n" + for convergence in self.convergence_measures: + xml_str += f" {convergence.to_xml()}\n" + xml_str += f"\n" + return xml_str class MultiCouplingSchemeNode: def __init__( - self, - control_participant: ParticipantNode, - participants: list[ParticipantNode] = None, - exchanges: list[ExchangeNode] = None, - accelerations: list[AccelerationNode] = None, - convergence_measures: list[ConvergenceMeasureNode] = None, - line: int = None, + self, + control_participant: ParticipantNode, + participants: list[ParticipantNode] = None, + exchanges: list[ExchangeNode] = None, + accelerations: list[AccelerationNode] = None, + convergence_measures: list[ConvergenceMeasureNode] = None, + line: int = None, ): self.control_participant = control_participant @@ -249,25 +226,58 @@ def __init__( self.line = line + def to_xml(self): + xml_str: str = f"\n" + for participant in self.participants: + if participant == self.control_participant: + xml_str += f" \n" + else: + xml_str += f" \n" + for exchange in self.exchanges: + xml_str += f" {exchange.to_xml()}\n" + for acceleration in self.accelerations: + xml_str += f" {acceleration.to_xml()}\n" + for convergence in self.convergence_measures: + xml_str += f" {convergence.to_xml()}\n" + + xml_str += f"\n" + return xml_str + class DataNode: - def __init__(self, name: str, data_type: DataType, line: int = None): + def __init__(self, name: str, data_type: e.DataType, line: int = None): self.name = name self.data_type = data_type self.line = line + def to_xml(self) -> str: + return f"" + class MappingNode: def __init__( - self, - parent_participant: ParticipantNode, - direction: Direction, - just_in_time: bool, - method: MappingMethod, - constraint: MappingConstraint, - from_mesh: MeshNode | None = None, - to_mesh: MeshNode | None = None, - line: int = None, + self, + parent_participant: ParticipantNode, + direction: e.Direction, + just_in_time: bool, + method: e.MappingMethod, + constraint: e.MappingConstraint, + from_mesh: MeshNode | None = None, + to_mesh: MeshNode | None = None, + polynomial: e.MappingPolynomialType = e.MappingPolynomialType.SEPARATE, + x_dead: bool = False, + y_dead: bool = False, + z_dead: bool = False, + solver_rtol: float = 1e-9, + vertices_per_cluster: int = 50, + relative_overlap: float = 0.15, + project_to_input: bool = True, + multiscale_type: e.MappingMultiscaleType = e.MappingMultiscaleType.COLLECT, + multiscale_axis: e.MappingMultiscaleAxis = e.MappingMultiscaleAxis.X, + multiscale_radius: float = 1, + basisfunction: MappingBasisFunctionNode = None, + executor: MappingExecutorNode = None, + line: int = None, ): self.parent_participant = parent_participant self.direction = direction @@ -276,46 +286,112 @@ def __init__( self.constraint = constraint self.from_mesh = from_mesh self.to_mesh = to_mesh + + self.polynomial = polynomial + self.x_dead = x_dead + self.y_dead = y_dead + self.z_dead = z_dead + self.solver_rtol = solver_rtol + self.vertices_per_cluster = vertices_per_cluster + self.relative_overlap = relative_overlap + self.project_to_input = project_to_input + self.multiscale_type = multiscale_type + self.multiscale_axis = multiscale_axis + self.multiscale_radius = multiscale_radius + if basisfunction is None: + self.basisfunction = MappingBasisFunctionNode(mapping=self, + type=e.MappingBasisFunctionType.COMPACT_POLYNOMIAL_C0) + else: + self.basisfunction = basisfunction + if executor is None: + self.executor = MappingExecutorNode(mapping=self, type=e.MappingExecutorType.CPU) + else: + self.executor = executor + self.line = line + def to_xml(self) -> str: + # General tags + xml_str: str = f"\n" # close opening element brace + xml_str += f" {self.executor.to_xml()}\n" + xml_str += f" {self.basisfunction.to_xml()}\n" + xml_str += f"" + else: + xml_str += f"/>" + return xml_str + class WriteDataNode: def __init__( - self, - participant: ParticipantNode, - data: DataNode, - mesh: MeshNode, - line: int = None, + self, + participant: ParticipantNode, + data: DataNode, + mesh: MeshNode, + line: int = None, ): self.participant = participant self.data = data self.mesh = mesh self.line = line + def to_xml(self) -> str: + xml_str: str = f"" + return xml_str + class ReadDataNode: def __init__( - self, - participant: ParticipantNode, - data: DataNode, - mesh: MeshNode, - line: int = None, + self, + participant: ParticipantNode, + data: DataNode, + mesh: MeshNode, + line: int = None, ): self.participant = participant self.data = data self.mesh = mesh self.line = line + def to_xml(self) -> str: + xml_str: str = f"" + return xml_str + class ExchangeNode: def __init__( - self, - coupling_scheme: CouplingSchemeNode | MultiCouplingSchemeNode, - data: DataNode, - mesh: MeshNode, - from_participant: ParticipantNode, - to_participant: ParticipantNode, - line: int = None, + self, + coupling_scheme: CouplingSchemeNode | MultiCouplingSchemeNode, + data: DataNode, + mesh: MeshNode, + from_participant: ParticipantNode, + to_participant: ParticipantNode, + line: int = None, ): self.coupling_scheme = coupling_scheme self.data = data @@ -324,26 +400,37 @@ def __init__( self.to_participant = to_participant self.line = line + def to_xml(self) -> str: + xml_str: str = (f" ") + return xml_str + class ExportNode: def __init__( - self, participant: ParticipantNode, format: ExportFormat, line: int = None + self, participant: ParticipantNode, format: e.ExportFormat, line: int = None, directory: str = "." ): self.participant = participant self.format = format self.line = line + self.directory = directory + + def to_xml(self) -> str: + xml_str: str = f"" + return xml_str class ActionNode: def __init__( - self, - participant: ParticipantNode, - type: ActionType, - mesh: MeshNode, - timing: TimingType, - target_data: DataNode | None = None, - source_data: list[DataNode] = None, - line: int = None, + self, + participant: ParticipantNode, + type: e.ActionType, + mesh: MeshNode, + timing: e.TimingType, + target_data: DataNode | None = None, + source_data: list[DataNode] = None, + python_module_name: str = "", + line: int = None, ): self.participant = participant self.type = type @@ -356,64 +443,104 @@ def __init__( else: self.source_data = source_data + self.python_module_name = python_module_name + self.line = line + def to_xml(self) -> str: + xml_str: str = f"\n" + if self.type != e.ActionType.RECORDER and self.target_data is not None: + xml_str += f" \n" + if self.type == e.ActionType.PYTHON: + xml_str += f" \n" + for source_data in self.source_data: + xml_str += f" \n" + xml_str += f"\n" + return xml_str + class WatchPointNode: def __init__( - self, name: str, participant: ParticipantNode, mesh: MeshNode, line: int = None + self, name: str, participant: ParticipantNode, mesh: MeshNode, line: int = None, + coordinate: list[float] = None ): self.name = name self.participant = participant self.mesh = mesh + if coordinate is None: + self.coordinate = [0] * self.mesh.dimensions # create a 2-d or 3-d coordinate + elif len(coordinate) != self.mesh.dimensions: + self.coordinate = [0] * self.mesh.dimensions # create a 2-d or 3-d coordinate + else: + self.coordinate = coordinate self.line = line + def to_xml(self) -> str: + coordinate = ";".join(map(str, self.coordinate)) # Coordinate of the form 0;1;2 ... + xml_str: str = f"" + return xml_str + class WatchIntegralNode: def __init__( - self, name: str, participant: ParticipantNode, mesh: MeshNode, line: int = None + self, name: str, participant: ParticipantNode, mesh: MeshNode, scale_with_connectivity: bool = False, + line: int = None ): self.name = name self.participant = participant self.mesh = mesh + self.scale_with_connectivity = scale_with_connectivity self.line = line + def to_xml(self) -> str: + xml_str: str = f"" + return xml_str + class M2NNode: def __init__( - self, - type: M2NType, - acceptor: ParticipantNode, - connector: ParticipantNode, - line: int = None, + self, + type: e.M2NType, + acceptor: ParticipantNode, + connector: ParticipantNode, + line: int = None, ): self.type = type self.acceptor = acceptor self.connector = connector self.line = line + def to_xml(self) -> str: + return f"\n" + class AccelerationDataNode: def __init__( - self, - acceleration: AccelerationNode, - data: DataNode, - mesh: MeshNode, - line: int = None, + self, + acceleration: AccelerationNode, + data: DataNode, + mesh: MeshNode, + line: int = None, ): self.acceleration = acceleration self.data = data self.mesh = mesh self.line = line + def to_xml(self) -> str: + xml_str: str = f"" + return xml_str + class AccelerationNode: def __init__( - self, - coupling_scheme: CouplingSchemeNode | MultiCouplingSchemeNode, - type: AccelerationType, - data: list[AccelerationDataNode] = None, - line: int = None, + self, + coupling_scheme: CouplingSchemeNode | MultiCouplingSchemeNode, + type: e.AccelerationType, + data: list[AccelerationDataNode] = None, + preconditioner: PreconditionerNode = None, + filter: AccelerationFilterNode = None, + line: int = None, ): self.coupling_scheme = coupling_scheme self.type = type @@ -423,20 +550,142 @@ def __init__( else: self.data = data + self.preconditioner = preconditioner + self.filter = filter + self.line = line + def to_xml(self) -> str: + xml_str: str = f"\n" + if self.type == e.AccelerationType.CONSTANT: + xml_str += f" \n" + for accelerated_data in self.data: + xml_str += f" {accelerated_data.to_xml()}\n" + if self.preconditioner is not None: + xml_str += f" {self.preconditioner.to_xml()}\n" + if self.filter is not None: + xml_str += f" {self.filter.to_xml()}" + xml_str += f"" + return xml_str + class ConvergenceMeasureNode: def __init__( - self, - coupling_scheme: CouplingSchemeNode | MultiCouplingSchemeNode, - type: ConvergenceMeasureType, - data: DataNode, - mesh: MeshNode, - line: int = None, + self, + coupling_scheme: CouplingSchemeNode | MultiCouplingSchemeNode, + type: e.ConvergenceMeasureType, + data: DataNode, + mesh: MeshNode, + line: int = None, + limit: float = 0.1, # functions as absolute and residual limit (they cannot appear together) + rel_limit: float = 0.1, ): self.type = type self.coupling_scheme = coupling_scheme self.data = data self.mesh = mesh self.line = line + self.limit = limit + self.rel_limit = rel_limit + + def to_xml(self) -> str: + limit_str: str = "" + if self.type == e.ConvergenceMeasureType.ABSOLUTE: + limit_str = f"limit=\"{self.limit}\" " + elif self.type == e.ConvergenceMeasureType.RELATIVE: + limit_str = f"limit=\"{self.rel_limit}\" " + elif self.type == e.ConvergenceMeasureType.ABSOLUTE_OR_RELATIVE: + limit_str = f"abs-limit=\"{self.limit}\" rel-limit=\"{self.rel_limit}\" " + elif self.type == e.ConvergenceMeasureType.RESIDUAL_RELATIVE: + limit_str = f"limit=\"{self.limit}\" " + xml_str: str = f"<{self.type.value}-convergence-measure data=\"{self.data.name}\" mesh=\"{self.mesh.name}\" {limit_str}/>" + return xml_str + + +class PreconditionerNode: + def __init__( + self, + type: e.PreconditionerType, + acceleration: AccelerationNode, + freeze_after: int = -1, + update_on_threshold: bool = True + ): + self.type = type + self.acceleration = acceleration + self.freeze_after = freeze_after + self.update_on_threshold = update_on_threshold + + def to_xml(self) -> str: + threshold_str: str = "" + if self.acceleration.type != e.AccelerationType.AITKEN: + threshold_str = f"update-on-threshold=\"{self.update_on_threshold}\" " + + xml_str: str = f"" + return xml_str + + +class AccelerationFilterNode: + def __init__( + self, + acceleration: AccelerationNode, + type: e.AccelerationFilterType = e.AccelerationFilterType.QR3, + limit: float = 1e-16, + ): + self.acceleration = acceleration + self.type = type + self.limit = limit + + def to_xml(self) -> str: + xml_str: str = f"" + return xml_str + + +class MappingBasisFunctionNode: + def __init__( + self, + type: e.MappingBasisFunctionType, + mapping: MappingNode, + support_radius: float = 0.5, + shape_parameter: float = 1, + ): + self.type = type + self.mapping = mapping + self.support_radius = support_radius + self.shape_parameter = shape_parameter + + def to_xml(self) -> str: + support_radius: str = "" + if self.type in [e.MappingBasisFunctionType.COMPACT_POLYNOMIAL_C0, + e.MappingBasisFunctionType.COMPACT_POLYNOMIAL_C2, + e.MappingBasisFunctionType.COMPACT_POLYNOMIAL_C4, + e.MappingBasisFunctionType.COMPACT_POLYNOMIAL_C6, + e.MappingBasisFunctionType.COMPACT_POLYNOMIAL_C8, + e.MappingBasisFunctionType.COMPACT_TPS_C2, + e.MappingBasisFunctionType.GAUSSIAN, + ]: + support_radius += f"support-radius=\"{self.support_radius}\" " + shape_parameter_str: str = "" + if self.type in [e.MappingBasisFunctionType.MULTIQUADRICS, + e.MappingBasisFunctionType.INVERSE_MULTIQUADRICS, + e.MappingBasisFunctionType.GAUSSIAN]: + shape_parameter_str = f"shape-parameter=\"{self.shape_parameter}\" " + xml_str: str = f"" + return xml_str + + +class MappingExecutorNode: + def __init__(self, type: e.MappingExecutorType, mapping: MappingNode, gpu_device_id: int = 0, n_threads: int = 0): + self.type = type + self.mapping = mapping + self.gpu_device_id = gpu_device_id + self.n_threads = n_threads + + def to_xml(self) -> str: + gpu_device_id_str: str = "" + if self.type in [e.MappingExecutorType.CUDA, e.MappingExecutorType.HIP]: + gpu_device_id_str += f"gpu-device-id=\"{self.gpu_device_id}\" " + n_threads_str: str = "" + if self.type == e.MappingExecutorType.OPENMP: + n_threads_str += f"n-threads=\"{self.n_threads}\" " + xml_str: str = f"" + return xml_str From a113423bee69d66d753b178209aff1cfc35342aa Mon Sep 17 00:00:00 2001 From: Orlando Date: Sun, 9 Nov 2025 06:39:01 +0100 Subject: [PATCH 5/8] Update nodes.py Changed the parameter "list(accelerations)" to just "acceleration" as there can only ever be one of them --- precice_config_graph/nodes.py | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/precice_config_graph/nodes.py b/precice_config_graph/nodes.py index 779acd7..992f041 100644 --- a/precice_config_graph/nodes.py +++ b/precice_config_graph/nodes.py @@ -148,7 +148,7 @@ def __init__( first_participant: ParticipantNode, second_participant: ParticipantNode, exchanges: list[ExchangeNode] = None, - accelerations: list[AccelerationNode] = None, + acceleration: AccelerationNode = None, convergence_measures: list[ConvergenceMeasureNode] = None, line: int = None, max_time_windows: int = 10, @@ -163,10 +163,7 @@ def __init__( else: self.exchanges = exchanges - if accelerations is None: - self.accelerations = [] - else: - self.accelerations = accelerations + self.acceleration = acceleration if convergence_measures is None: self.convergence_measures = [] @@ -184,8 +181,8 @@ def to_xml(self): xml_str += f" \n" for exchange in self.exchanges: xml_str += f" {exchange.to_xml()}\n" - for acceleration in self.accelerations: - xml_str += f" {acceleration.to_xml()}\n" + if self.acceleration is not None: + xml_str += f" {self.acceleration.to_xml()}\n" for convergence in self.convergence_measures: xml_str += f" {convergence.to_xml()}\n" xml_str += f"\n" @@ -198,7 +195,7 @@ def __init__( control_participant: ParticipantNode, participants: list[ParticipantNode] = None, exchanges: list[ExchangeNode] = None, - accelerations: list[AccelerationNode] = None, + acceleration: AccelerationNode = None, convergence_measures: list[ConvergenceMeasureNode] = None, line: int = None, ): @@ -214,10 +211,7 @@ def __init__( else: self.exchanges = exchanges - if accelerations is None: - self.accelerations = [] - else: - self.accelerations = accelerations + self.acceleration = acceleration if convergence_measures is None: self.convergence_measures = [] @@ -235,8 +229,8 @@ def to_xml(self): xml_str += f" \n" for exchange in self.exchanges: xml_str += f" {exchange.to_xml()}\n" - for acceleration in self.accelerations: - xml_str += f" {acceleration.to_xml()}\n" + if self.acceleration is not None: + xml_str += f" {self.acceleration.to_xml()}\n" for convergence in self.convergence_measures: xml_str += f" {convergence.to_xml()}\n" From dd90620a792f866bf060f3c2b648e07a06aebbbe Mon Sep 17 00:00:00 2001 From: Orlando Date: Sun, 9 Nov 2025 06:39:39 +0100 Subject: [PATCH 6/8] updated tests and graph gen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit to use new “acceleration” attribute --- precice_config_graph/graph.py | 2 +- .../multi-coupling/multi-coupling_test.py | 282 +++++++++--------- 2 files changed, 142 insertions(+), 142 deletions(-) diff --git a/precice_config_graph/graph.py b/precice_config_graph/graph.py index 8a53035..c9dbb75 100644 --- a/precice_config_graph/graph.py +++ b/precice_config_graph/graph.py @@ -440,7 +440,7 @@ def get_attribute(e: etree.Element, key: str): acceleration.data.append(a_data_node) acceleration_data_nodes.append(a_data_node) - coupling_scheme.accelerations.append(acceleration) + coupling_scheme.acceleration = acceleration acceleration_nodes.append(acceleration) for (convergence_measure_el, c_kind) in find_all_with_postfix( diff --git a/tests/example-configs/multi-coupling/multi-coupling_test.py b/tests/example-configs/multi-coupling/multi-coupling_test.py index 5ece324..0b970de 100644 --- a/tests/example-configs/multi-coupling/multi-coupling_test.py +++ b/tests/example-configs/multi-coupling/multi-coupling_test.py @@ -153,83 +153,83 @@ def test_graph(): ] edges += ( - [ - (read_data, participant, Edge.READ_DATA__PARTICIPANT__BELONGS_TO) - for participant in participants - for read_data in participant.read_data - ] - + [ - (read_data, read_data.data, Edge.READ_DATA__DATA_READ_BY) - for participant in participants - for read_data in participant.read_data - ] - + [ - (read_data, read_data.mesh, Edge.READ_DATA__MESH_READ_BY) - for participant in participants - for read_data in participant.read_data - ] + [ + (read_data, participant, Edge.READ_DATA__PARTICIPANT__BELONGS_TO) + for participant in participants + for read_data in participant.read_data + ] + + [ + (read_data, read_data.data, Edge.READ_DATA__DATA_READ_BY) + for participant in participants + for read_data in participant.read_data + ] + + [ + (read_data, read_data.mesh, Edge.READ_DATA__MESH_READ_BY) + for participant in participants + for read_data in participant.read_data + ] ) edges += ( - [ - ( - receive_mesh, - n_participant_nastin, - Edge.RECEIVE_MESH__PARTICIPANT__BELONGS_TO, - ) - for participant in participants - for receive_mesh in participant.receive_meshes - ] - + [ - (receive_mesh, receive_mesh.mesh, Edge.RECEIVE_MESH__MESH) - for participant in participants - for receive_mesh in participant.receive_meshes - ] - + [ - ( - receive_mesh, - receive_mesh.from_participant, - Edge.RECEIVE_MESH__PARTICIPANT_RECEIVED_FROM, - ) - for participant in participants - for receive_mesh in participant.receive_meshes - ] + [ + ( + receive_mesh, + n_participant_nastin, + Edge.RECEIVE_MESH__PARTICIPANT__BELONGS_TO, + ) + for participant in participants + for receive_mesh in participant.receive_meshes + ] + + [ + (receive_mesh, receive_mesh.mesh, Edge.RECEIVE_MESH__MESH) + for participant in participants + for receive_mesh in participant.receive_meshes + ] + + [ + ( + receive_mesh, + receive_mesh.from_participant, + Edge.RECEIVE_MESH__PARTICIPANT_RECEIVED_FROM, + ) + for participant in participants + for receive_mesh in participant.receive_meshes + ] ) edges += ( - [ - (write_data, participant, Edge.WRITE_DATA__PARTICIPANT__BELONGS_TO) - for participant in participants - for write_data in participant.write_data - ] - + [ - (write_data, write_data.data, Edge.WRITE_DATA__WRITES_TO_DATA) - for participant in participants - for write_data in participant.write_data - ] - + [ - (write_data, write_data.mesh, Edge.WRITE_DATA__WRITES_TO_MESH) - for participant in participants - for write_data in participant.write_data - ] + [ + (write_data, participant, Edge.WRITE_DATA__PARTICIPANT__BELONGS_TO) + for participant in participants + for write_data in participant.write_data + ] + + [ + (write_data, write_data.data, Edge.WRITE_DATA__WRITES_TO_DATA) + for participant in participants + for write_data in participant.write_data + ] + + [ + (write_data, write_data.mesh, Edge.WRITE_DATA__WRITES_TO_MESH) + for participant in participants + for write_data in participant.write_data + ] ) edges += ( - [ - (mapping, n_participant_nastin, Edge.MAPPING__PARTICIPANT__BELONGS_TO) - for participant in participants - for mapping in participant.mappings - ] - + [ - (mapping, mapping.from_mesh, Edge.MAPPING__FROM_MESH) - for participant in participants - for mapping in participant.mappings - ] - + [ - (mapping, mapping.to_mesh, Edge.MAPPING__TO_MESH) - for participant in participants - for mapping in participant.mappings - ] + [ + (mapping, n_participant_nastin, Edge.MAPPING__PARTICIPANT__BELONGS_TO) + for participant in participants + for mapping in participant.mappings + ] + + [ + (mapping, mapping.from_mesh, Edge.MAPPING__FROM_MESH) + for participant in participants + for mapping in participant.mappings + ] + + [ + (mapping, mapping.to_mesh, Edge.MAPPING__TO_MESH) + for participant in participants + for mapping in participant.mappings + ] ) # M2N nodes @@ -268,15 +268,15 @@ def test_graph(): ) edges += [ - (n_coupling_scheme, participant, Edge.MULTI_COUPLING_SCHEME__PARTICIPANT) - for participant in n_coupling_scheme.participants - ] + [ - ( - n_coupling_scheme, - n_participant_nastin, - Edge.MULTI_COUPLING_SCHEME__PARTICIPANT__CONTROL, - ) - ] + (n_coupling_scheme, participant, Edge.MULTI_COUPLING_SCHEME__PARTICIPANT) + for participant in n_coupling_scheme.participants + ] + [ + ( + n_coupling_scheme, + n_participant_nastin, + Edge.MULTI_COUPLING_SCHEME__PARTICIPANT__CONTROL, + ) + ] n_exchange_forces1 = n.ExchangeNode( coupling_scheme=n_coupling_scheme, @@ -331,24 +331,24 @@ def test_graph(): n_coupling_scheme.exchanges = exchanges edges += ( - [ - (exchange, n_coupling_scheme, Edge.EXCHANGE__COUPLING_SCHEME__BELONGS_TO) - for exchange in exchanges - ] - + [(exchange, exchange.data, Edge.EXCHANGE__DATA) for exchange in exchanges] - + [(exchange, exchange.mesh, Edge.EXCHANGE__MESH) for exchange in exchanges] - + [ - (exchange, exchange.from_participant, Edge.EXCHANGE__EXCHANGED_FROM) - for exchange in exchanges - ] - + [ - (exchange, exchange.to_participant, Edge.EXCHANGE__EXCHANGES_TO) - for exchange in exchanges - ] + [ + (exchange, n_coupling_scheme, Edge.EXCHANGE__COUPLING_SCHEME__BELONGS_TO) + for exchange in exchanges + ] + + [(exchange, exchange.data, Edge.EXCHANGE__DATA) for exchange in exchanges] + + [(exchange, exchange.mesh, Edge.EXCHANGE__MESH) for exchange in exchanges] + + [ + (exchange, exchange.from_participant, Edge.EXCHANGE__EXCHANGED_FROM) + for exchange in exchanges + ] + + [ + (exchange, exchange.to_participant, Edge.EXCHANGE__EXCHANGES_TO) + for exchange in exchanges + ] ) acceleration = n.AccelerationNode(n_coupling_scheme, e.AccelerationType.IQN_ILS) - n_coupling_scheme.accelerations.append(acceleration) # TODO acceleration + n_coupling_scheme.acceleration = acceleration acceleration_data1 = n.AccelerationDataNode( acceleration, n_data_forces1, n_mesh_solidz1 ) @@ -363,29 +363,29 @@ def test_graph(): acceleration.data.append(acceleration_data3) edges += ( - [ - ( - acceleration, - acceleration.coupling_scheme, - Edge.ACCELERATION__COUPLING_SCHEME__BELONGS_TO, - ) - ] - + [ - ( - data_node, - data_node.acceleration, - Edge.ACCELERATION_DATA__ACCELERATION__BELONGS_TO, - ) - for data_node in acceleration.data - ] - + [ - (data_node, data_node.data, Edge.ACCELERATION_DATA__DATA) - for data_node in acceleration.data - ] - + [ - (data_node, data_node.mesh, Edge.ACCELERATION_DATA__MESH) - for data_node in acceleration.data - ] + [ + ( + acceleration, + acceleration.coupling_scheme, + Edge.ACCELERATION__COUPLING_SCHEME__BELONGS_TO, + ) + ] + + [ + ( + data_node, + data_node.acceleration, + Edge.ACCELERATION_DATA__ACCELERATION__BELONGS_TO, + ) + for data_node in acceleration.data + ] + + [ + (data_node, data_node.data, Edge.ACCELERATION_DATA__DATA) + for data_node in acceleration.data + ] + + [ + (data_node, data_node.mesh, Edge.ACCELERATION_DATA__MESH) + for data_node in acceleration.data + ] ) convergence_measures = [ @@ -428,30 +428,30 @@ def test_graph(): ] edges += ( - [ - ( - convergence_measure, - convergence_measure.coupling_scheme, - Edge.CONVERGENCE_MEASURE__COUPLING_SCHEME__BELONGS_TO, - ) - for convergence_measure in convergence_measures - ] - + [ - ( - convergence_measure, - convergence_measure.data, - Edge.CONVERGENCE_MEASURE__DATA, - ) - for convergence_measure in convergence_measures - ] - + [ - ( - convergence_measure, - convergence_measure.mesh, - Edge.CONVERGENCE_MEASURE__MESH, - ) - for convergence_measure in convergence_measures - ] + [ + ( + convergence_measure, + convergence_measure.coupling_scheme, + Edge.CONVERGENCE_MEASURE__COUPLING_SCHEME__BELONGS_TO, + ) + for convergence_measure in convergence_measures + ] + + [ + ( + convergence_measure, + convergence_measure.data, + Edge.CONVERGENCE_MEASURE__DATA, + ) + for convergence_measure in convergence_measures + ] + + [ + ( + convergence_measure, + convergence_measure.mesh, + Edge.CONVERGENCE_MEASURE__MESH, + ) + for convergence_measure in convergence_measures + ] ) G_expected = nx.Graph() @@ -467,8 +467,8 @@ def edge_match(edge_a, edge_b): assert nx.is_isomorphic( G_expected, G_actual, node_match=node_match, edge_match=edge_match ), ( - f"Graphs did not match. Some stats: Expected: (num nodes: {len(G_expected.nodes)}, num edges: {len(G_expected.edges)}), " - + f"Actual: (num nodes: {len(G_actual.nodes)}, num edges: {len(G_actual.edges)})" + f"Graphs did not match. Some stats: Expected: (num nodes: {len(G_expected.nodes)}, num edges: {len(G_expected.edges)}), " + + f"Actual: (num nodes: {len(G_actual.nodes)}, num edges: {len(G_actual.edges)})" ) print("\nGraphs are isomorphic.") From 6b49f3e7d8246281fbde1d3508920e7c59390abb Mon Sep 17 00:00:00 2001 From: Orlando Date: Sun, 9 Nov 2025 06:39:51 +0100 Subject: [PATCH 7/8] updated docs --- docs/Nodes.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/Nodes.md b/docs/Nodes.md index 96dffbb..1a904f0 100644 --- a/docs/Nodes.md +++ b/docs/Nodes.md @@ -63,8 +63,9 @@ A coupling-scheme node represents a coupling-scheme element of the XML file. - `first_participant`: The participant of the coupling-scheme that gets referred to as `first=“”`. - `second_participant`: The participant of the coupling-scheme that gets referred to as `second=“”`. - `exchanges`: A list of exchanges between the participants. This links to `Exchange` nodes for further references. -- `accelerations`: A list of accelerations that are intended to accelerate data in exchanges. -- `convergence_measures`: A list of convergence-measure. Defines the convergence criterion of certain data of a mesh in a coupling-scheme. +- `accelerations`: The acceleration node that will contain references to all data-accelerations of the coupling-scheme. +- `convergence_measures`: A list of convergence-measure. Defines the convergence criterion of certain data of a mesh in + a coupling-scheme. - `line`: The line number where the coupling-scheme is defined in the config.xml. ## MultiCouplingScheme @@ -76,8 +77,9 @@ coupling-scheme to allow for more than two participants. - `participants`: A list of all participants taking part in the multi-coupling-scheme. This does _not_ include the control participant. - `exchanges`: A list of all exchanges being used to exchange data in this multi-coupling-scheme. -- `accelerations`: A list of accelerations that are intended to accelerate data in exchanges. -- `convergence_measures`: A list of convergence-measure. Defines the convergence criterion of certain data of a mesh in a multi-coupling-scheme. +- `accelerations`: The acceleration node that will contain references to all data-accelerations of the coupling-scheme. +- `convergence_measures`: A list of convergence-measure. Defines the convergence criterion of certain data of a mesh in + a multi-coupling-scheme. - `line`: The line number where the multi-coupling-scheme is defined in the config.xml. ## Data @@ -206,7 +208,8 @@ Data is accelerated in a mesh in an exchange in the same coupling scheme. Defines the convergence criterion of certain data of a mesh in a coupling-scheme. - `coupling_scheme`: The coupling-scheme who the convergence-measure belongs to. -- `type`: The type of the convergence-measure node. Possible values are `absolute`, `absolute-or-relative`, `relative` and `residual-relative`. +- `type`: The type of the convergence-measure node. Possible values are `absolute`, `absolute-or-relative`, `relative` + and `residual-relative`. - `data`: Which data should converge by type. - `mesh`: In which mesh the data is to be converged according to the type. - `line`: The line number where the convergence-measure is defined in the config.xml. From ae5c8a2e8d8a1844aca554bc25a5c0189beecb08 Mon Sep 17 00:00:00 2001 From: Orlando Date: Sun, 9 Nov 2025 06:52:14 +0100 Subject: [PATCH 8/8] updated docs To mention the new attributes and method --- docs/Nodes.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/docs/Nodes.md b/docs/Nodes.md index 1a904f0..f17da4e 100644 --- a/docs/Nodes.md +++ b/docs/Nodes.md @@ -1,16 +1,24 @@ # Nodes -This project builds a graph based on a precice-config.xml file. The nodes in the graph correspond to the specified XML +This project builds a graph based on a `precice-config.xml` file. The nodes in the graph correspond to the specified XML elements and tags.
-The documentation for preCICE's config tags can be read -at [preCICE XML reference](https://precice.org/configuration-XML-reference.html) +The documentation for preCICE's config tags can be read at +the [preCICE XML reference](https://precice.org/configuration-XML-reference.html). > [!NOTE] -> This graph is built for the [preCICE logic checker](https://github.com/precice-forschungsprojekt/config-checker). This -> means that there may be redundancies or “inconsistencies” in the building of the graph itself. +> This graph is built for both [preCICE config check](https://github.com/precice/config-check/) +> and [preCICE case-generate](https://github.com/precice/case-generate). +> This means that there may be redundancies or “inconsistencies” in the building of the graph itself. Here you will find a list with brief explanations of each node and its parameters. +> [!NOTE] +> Please note that since version 1.1, each node additionally has a method `to_xml()`, +> to convert it back to a `precice-config.xml` element (string).
+> To allow this conversion to be as thorough as possible, additional, _optional_ attributes were added to many nodes, +> which are not listed here and _not_ required for using the graph. Their meaning can be inferred from their name or +> the [preCICE XML reference](https://precice.org/configuration-xml-reference.html). + ## Participant The participant is the center of the graph. It saves references to all connected meshes, written and read data,