flowgraph 0.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. flowgraph/__init__.py +9 -0
  2. flowgraph/__main__.py +6 -0
  3. flowgraph/adapters/__init__.py +141 -0
  4. flowgraph/adapters/controls.py +96 -0
  5. flowgraph/adapters/cosapp.py +195 -0
  6. flowgraph/adapters/data_types.py +59 -0
  7. flowgraph/adapters/documentation.py +45 -0
  8. flowgraph/adapters/field_ops.py +121 -0
  9. flowgraph/adapters/file_conversion.py +393 -0
  10. flowgraph/adapters/files.py +191 -0
  11. flowgraph/adapters/filters.py +173 -0
  12. flowgraph/adapters/image_tools.py +394 -0
  13. flowgraph/adapters/mesh_creation_tools.py +431 -0
  14. flowgraph/adapters/mesh_generations.py +58 -0
  15. flowgraph/adapters/mesh_ops.py +362 -0
  16. flowgraph/adapters/paraview.py +39 -0
  17. flowgraph/adapters/plaid_bridge.py +205 -0
  18. flowgraph/adapters/readers.py +215 -0
  19. flowgraph/adapters/remote_files.py +118 -0
  20. flowgraph/adapters/simple_sources.py +199 -0
  21. flowgraph/adapters/sinks.py +80 -0
  22. flowgraph/adapters/user_code.py +196 -0
  23. flowgraph/adapters/workflow.py +176 -0
  24. flowgraph/adapters/workflow_interfaces.py +78 -0
  25. flowgraph/adapters/writers.py +159 -0
  26. flowgraph/application/__init__.py +1 -0
  27. flowgraph/application/node_registry.py +14 -0
  28. flowgraph/application/port_conversions.py +63 -0
  29. flowgraph/application/wasm_execution.py +132 -0
  30. flowgraph/application/workflow_cli.py +417 -0
  31. flowgraph/application/workflow_core.py +1193 -0
  32. flowgraph/application/workflow_history.py +93 -0
  33. flowgraph/application/workflow_io.py +393 -0
  34. flowgraph/application/workflow_layout.py +170 -0
  35. flowgraph/application/workflow_web_catalog.py +143 -0
  36. flowgraph/application/workflows.py +7 -0
  37. flowgraph/core_cli.py +81 -0
  38. flowgraph/domain/__init__.py +5 -0
  39. flowgraph/domain/mesh_document.py +91 -0
  40. flowgraph/examples/AllFilters.py +19 -0
  41. flowgraph/examples/convert_format.py +21 -0
  42. flowgraph/resources.py +29 -0
  43. flowgraph/testdata/AllFilters.json +390 -0
  44. flowgraph/testdata/FlowGraph.h5 +0 -0
  45. flowgraph/testdata/FlowGraph.stl +0 -0
  46. flowgraph/testdata/FlowGraph.xdmf +22 -0
  47. flowgraph/testdata/SimpleReadMesh.json +70 -0
  48. flowgraph/testdata/SpherePoints.csv +83 -0
  49. flowgraph/testdata/muscat-muscat-mesh-conversion.json +101 -0
  50. flowgraph/version.py +3 -0
  51. flowgraph-0.1.2.dist-info/METADATA +150 -0
  52. flowgraph-0.1.2.dist-info/RECORD +56 -0
  53. flowgraph-0.1.2.dist-info/WHEEL +5 -0
  54. flowgraph-0.1.2.dist-info/entry_points.txt +2 -0
  55. flowgraph-0.1.2.dist-info/licenses/LICENSE +29 -0
  56. flowgraph-0.1.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,393 @@
1
+ """Workflow node for converting mesh files between supported formats."""
2
+
3
+ from collections.abc import Mapping
4
+ from pathlib import Path
5
+ from typing import Any, ClassVar
6
+
7
+ from flowgraph.application.workflow_core import (
8
+ NodeDefinition,
9
+ ParameterDefinition,
10
+ ParameterOption,
11
+ PortDefinition,
12
+ PortDirection,
13
+ )
14
+
15
+ from .data_types import FILE, ParameterKind
16
+
17
+
18
+ class MeshFileConversionError(RuntimeError):
19
+ """Raised when a mesh file conversion cannot be completed."""
20
+
21
+
22
+ class DelayInitialization:
23
+ """Lazily resolve the reader or writer classes in a conversion catalog."""
24
+
25
+ _namespace: ClassVar[dict[str, Any]] = {}
26
+
27
+ def __init__(self, delayed_init_struct: Mapping[str, str]) -> None:
28
+ self.entries: dict[str, type[Any]] = {}
29
+ self.delayed_init_struct = dict(delayed_init_struct)
30
+
31
+ def __getitem__(self, key: str) -> type[Any]:
32
+ """Return the class registered under *key*, importing it on first use."""
33
+ if key not in self.delayed_init_struct:
34
+ raise KeyError(f"Unknown conversion format: {key!r}")
35
+ if key in self.entries:
36
+ return self.entries[key]
37
+
38
+ namespace: dict[str, Any] = {}
39
+ try:
40
+ exec(self.delayed_init_struct[key], self._namespace, namespace) # noqa: S102
41
+ resolved = namespace.get("res")
42
+ except Exception as error:
43
+ raise MeshFileConversionError(
44
+ f"Could not initialize conversion format {key!r}: {error}"
45
+ ) from error
46
+
47
+ if not isinstance(resolved, type):
48
+ raise MeshFileConversionError(f"Conversion format {key!r} did not resolve to a class")
49
+ self.entries[key] = resolved
50
+ return resolved
51
+
52
+ def keys(self):
53
+ """Return the available conversion format keys."""
54
+ return self.delayed_init_struct.keys()
55
+
56
+ def check(self) -> None:
57
+ """Resolve every registered class and raise the first import error."""
58
+ for key in self.delayed_init_struct:
59
+ try:
60
+ self[key]
61
+ except Exception as error:
62
+ raise RuntimeError(
63
+ f"Could not initialize conversion format {key}",
64
+ ) from error
65
+
66
+ def get_class(self, filename: str | Path, key: str) -> type[Any]:
67
+ """Return an explicitly selected or extension-selected conversion class."""
68
+ if key == "auto":
69
+ suffixes = Path(filename).suffixes
70
+ extension = "".join(suffixes[-2:]).lower() if len(suffixes) > 1 else ""
71
+ extension = extension or Path(filename).suffix.lower()
72
+ muscat_obj = None
73
+ other_obj = None
74
+ for candidate in self.delayed_init_struct:
75
+ rext, backend, *_ = candidate.split(" ")
76
+ if extension == rext and other_obj is None:
77
+ other_obj = candidate
78
+ if extension == rext and muscat_obj is None and backend == "Muscat":
79
+ muscat_obj = candidate
80
+ break
81
+ selected = muscat_obj or other_obj
82
+ if selected is None:
83
+ raise MeshFileConversionError(
84
+ f"No conversion class is registered for extension {extension!r}"
85
+ )
86
+ return self[selected]
87
+ return self[key]
88
+
89
+ GetClass = get_class
90
+
91
+
92
+ ReaderDelayedInitStruct = {
93
+ ".ans Muscat FemmReader": "from Muscat.IO.FemmReader import FemmReader; res = FemmReader",
94
+ ".ansys Muscat AnsysReader": "from Muscat.IO.AnsysReader import AnsysReader; res = AnsysReader",
95
+ ".asc Muscat AscReader": "from Muscat.IO.AscReader import AscReader; res = AscReader",
96
+ ".avs MeshIO avs_avsucd_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_avs_avsucd_Reader; res = MeshIO_avs_avsucd_Reader",
97
+ ".bdf MeshIO bdf_nastran_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_bdf_nastran_Reader; res = MeshIO_bdf_nastran_Reader",
98
+ ".cgns MeshIO cgns_cgns_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_cgns_cgns_Reader; res = MeshIO_cgns_cgns_Reader",
99
+ ".cgns Muscat CGNSReader": "from Muscat.IO.CGNSReader import CGNSReader; res = CGNSReader",
100
+ ".dat MeshIO dat_tecplot_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_dat_tecplot_Reader; res = MeshIO_dat_tecplot_Reader",
101
+ ".dat Muscat DatReader": "from Muscat.IO.SamcefReader import DatReader;res = DatReader",
102
+ ".dato MeshIO dato_permas_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_dato_permas_Reader; res = MeshIO_dato_permas_Reader",
103
+ ".dato.gz MeshIO dato.gz_permas_Reader": """from Muscat.Bridges.MeshIOBridge import readers; res = readers["MeshIO_dato.gz_permas_Reader"]""",
104
+ ".datt Muscat DatReader": "from Muscat.IO.SamcefReader import DatReader; res = DatReader",
105
+ ".dill Muscat DillReader": "from Muscat.IO.DillTools import DillReader; res = DillReader",
106
+ ".ele MeshIO ele_tetgen_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_ele_tetgen_Reader; res = MeshIO_ele_tetgen_Reader",
107
+ ".f3grid MeshIO f3grid_flac3d_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_f3grid_flac3d_Reader; res = MeshIO_f3grid_flac3d_Reader",
108
+ ".fac Muscat SamcefOuputReader": "from Muscat.IO.SamcefOutputReader import SamcefOuputReader; res = SamcefOuputReader ",
109
+ ".fem MeshIO fem_nastran_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_fem_nastran_Reader; res = MeshIO_fem_nastran_Reader",
110
+ ".fem Muscat FemReader": "from Muscat.IO.FemReader import FemReader; res = FemReader",
111
+ ".gcode Muscat GReader": "from Muscat.IO.GReader import GReader; res = GReader",
112
+ ".geo Muscat GeoReader": "from Muscat.IO.GeoReader import GeoReader; res =GeoReader",
113
+ ".geof Muscat GeofReader": "from Muscat.IO.GeofReader import GeofReader; res = GeofReader",
114
+ ".h5m MeshIO h5m_h5m_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_h5m_h5m_Reader; res = MeshIO_h5m_h5m_Reader",
115
+ ".inp MeshIO inp_abaqus_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_inp_abaqus_Reader; res = MeshIO_inp_abaqus_Reader",
116
+ ".inp Muscat InpReader": "from Muscat.IO.InpReader import InpReader; res=InpReader",
117
+ ".mdpa MeshIO mdpa_mdpa_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_mdpa_mdpa_Reader; res = MeshIO_mdpa_mdpa_Reader",
118
+ ".med MeshIO med_med_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_med_med_Reader; res = MeshIO_med_med_Reader",
119
+ ".mesh MeshIO mesh_medit_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_mesh_medit_Reader; res = MeshIO_mesh_medit_Reader",
120
+ ".mesh Muscat MeshReader": "from Muscat.IO.MeshReader import MeshReader; res = MeshReader",
121
+ ".meshb MeshIO meshb_medit_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_meshb_medit_Reader; res = MeshIO_meshb_medit_Reader",
122
+ ".meshb Muscat MeshReader": "from Muscat.IO.MeshReader import MeshReader; res = MeshReader",
123
+ ".msh MeshIO msh_ansys_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_msh_ansys_Reader; res = MeshIO_msh_ansys_Reader",
124
+ ".msh MeshIO msh_gmsh_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_msh_gmsh_Reader; res = MeshIO_msh_gmsh_Reader",
125
+ ".msh Muscat GmshReader": "from Muscat.IO.GmshReader import GmshReader; res = GmshReader",
126
+ ".nas MeshIO nas_nastran_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_nas_nastran_Reader; res = MeshIO_nas_nastran_Reader",
127
+ ".node MeshIO node_tetgen_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_node_tetgen_Reader; res = MeshIO_node_tetgen_Reader",
128
+ ".obj MeshIO obj_obj_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_obj_obj_Reader; res = MeshIO_obj_obj_Reader",
129
+ ".off MeshIO off_off_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_off_off_Reader; res = MeshIO_off_off_Reader",
130
+ ".pickle Muscat PickleReader": "from Muscat.IO.PickleTools import PickleReader; res = PickleReader",
131
+ ".ply MeshIO ply_ply_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_ply_ply_Reader; res = MeshIO_ply_ply_Reader",
132
+ ".post MeshIO post_permas_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_post_permas_Reader; res = MeshIO_post_permas_Reader",
133
+ ".post.gz MeshIO post.gz_permas_Reader": """from Muscat.Bridges.MeshIOBridge import readers; res = readers['MeshIO_post.gz_permas_Reader']""",
134
+ ".pxdmf Muscat XdmfReader": "from Muscat.IO.XdmfReader import XdmfReader; res = XdmfReader",
135
+ ".sdb Muscat SamcefOutputBaconReader": "from Muscat.IO.SamcefOutputReader import SamcefOutputBaconReader; res = SamcefOutputBaconReader",
136
+ ".sol Muscat MeshSolutionReaderWrapper": "from Muscat.IO.MeshReader import MeshSolutionReaderWrapper; res = MeshSolutionReaderWrapper",
137
+ ".solb Muscat MeshSolutionReaderWrapper": "from Muscat.IO.MeshReader import MeshSolutionReaderWrapper; res = MeshSolutionReaderWrapper",
138
+ ".stl Muscat StlReader": "from Muscat.IO.StlReader import StlReader; res = StlReader",
139
+ ".stl MeshIO stl_stl_Reader": """from Muscat.Bridges.MeshIOBridge import InitAllReaders, readers; InitAllReaders(); res = readers["MeshIO_stl_stl_Reader"][1]""",
140
+ ".su2 MeshIO su2_su2_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_su2_su2_Reader; res = MeshIO_su2_su2_Reader",
141
+ ".svg MeshIO svg_svg_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_svg_svg_Reader; res = MeshIO_svg_svg_Reader",
142
+ ".tec MeshIO tec_tecplot_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_tec_tecplot_Reader; res = MeshIO_tec_tecplot_Reader",
143
+ ".ugrid MeshIO ugrid_ugrid_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_ugrid_ugrid_Reader; res = MeshIO_ugrid_ugrid_Reader",
144
+ ".ut Muscat UtReader": "from Muscat.IO.UtReader import UtReader; res = UtReader",
145
+ ".utp Muscat UtReader": "from Muscat.IO.UtReader import UtReader; res = UtReader",
146
+ ".vol MeshIO vol_netgen_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_vol_netgen_Reader; res = MeshIO_vol_netgen_Reader",
147
+ ".vol.gz MeshIO vol.gz_netgen_Reader": """from Muscat.Bridges.MeshIOBridge import readers; res = readers['MeshIO_vol.gz_netgen_Reader']""",
148
+ ".vtk MeshIO vtk_vtk_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_vtk_vtk_Reader; res = MeshIO_vtk_vtk_Reader",
149
+ ".vtu MeshIO vtu_vtu_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_vtu_vtu_Reader; res = MeshIO_vtu_vtu_Reader",
150
+ ".wkt MeshIO wkt_wkt_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_wkt_wkt_Reader; res = MeshIO_wkt_wkt_Reader",
151
+ ".xdmf MeshIO xdmf_xdmf_Reader": "from Muscat.Bridges.MeshIOBridge import readers; res = readers['MeshIO_xdmf_xdmf_Reader']",
152
+ ".xdmf Muscat XdmfReader": "from Muscat.IO.XdmfReader import XdmfReader; res = XdmfReader",
153
+ ".xmf Muscat XdmfReader": "from Muscat.IO.XdmfReader import XdmfReader; res = XdmfReader",
154
+ ".xmf MeshIO xmf_xdmf_Reader": "from Muscat.Bridges.MeshIOBridge import MeshIO_xmf_xdmf_Reader; res = MeshIO_xmf_xdmf_Reader",
155
+ ".xml MeshIO xml_dolfin-xml_Reader": """from Muscat.Bridges.MeshIOBridge import readers; res = readers['MeshIO_xml_dolfin-xml_Reader']""",
156
+ }
157
+ ReaderMapping = DelayInitialization(ReaderDelayedInitStruct)
158
+
159
+
160
+ WriterDelayedInitStruct = {
161
+ ".avs MeshIO avs_avsucd_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_avs_avsucd_Writer"][1]""",
162
+ ".bdf MeshIO bdf_nastran_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_bdf_nastran_Writer"][1]""",
163
+ ".off MeshIO off_off_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_off_off_Writer"][1]""",
164
+ ".cgns MeshIO cgns_cgns_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_cgns_cgns_Writer"][1]""",
165
+ ".nas MeshIO nas_nastran_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_nas_nastran_Writer"][1]""",
166
+ ".mesh Muscat MeshWriter": "from Muscat.IO.MeshWriter import MeshWriter; res = MeshWriter",
167
+ ".dill Muscat DillWriter": "from Muscat.IO.DillTools import DillWriter; res = DillWriter",
168
+ ".stl Muscat StlWriter": "from Muscat.IO.StlWriter import StlWriter; res = StlWriter",
169
+ ".xmf MeshIO xmf_xdmf_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_xmf_xdmf_Writer"][1]""",
170
+ ".med MeshIO med_med_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_med_med_Writer"][1]""",
171
+ ".ply MeshIO ply_ply_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_ply_ply_Writer"][1]""",
172
+ ".tec MeshIO tec_tecplot_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_tec_tecplot_Writer"][1]""",
173
+ ".msh MeshIO msh_gmsh_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_msh_gmsh_Writer"][1]""",
174
+ ".xdmf MeshIO xdmf_xdmf_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_xdmf_xdmf_Writer"][1]""",
175
+ ".cgns Muscat CGNSWriter": "from Muscat.IO.CGNSWriter import CGNSWriter; res = CGNSWriter",
176
+ ".vtk MeshIO vtk_vtk_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_vtk_vtk_Writer"][1]""",
177
+ ".h5m MeshIO h5m_h5m_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_h5m_h5m_Writer"][1]""",
178
+ ".su2 MeshIO su2_su2_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_su2_su2_Writer"][1]""",
179
+ ".vol.gz MeshIO vol.gz_netgen_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_vol.gz_netgen_Writer"][1]""",
180
+ ".ele MeshIO ele_tetgen_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_ele_tetgen_Writer"][1]""",
181
+ ".msh Muscat GmshWriter": "from Muscat.IO.GmshWriter import GmshWriter; res = GmshWriter",
182
+ ".ugrid MeshIO ugrid_ugrid_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_ugrid_ugrid_Writer"][1]""",
183
+ ".node MeshIO node_tetgen_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_node_tetgen_Writer"][1]""",
184
+ ".wkt MeshIO wkt_wkt_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_wkt_wkt_Writer"][1]""",
185
+ ".inp Muscat InpWriter": "from Muscat.IO.InpWriter import InpWriter; res = InpWriter",
186
+ ".dat MeshIO dat_tecplot_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_dat_tecplot_Writer"][1]""",
187
+ ".f3grid MeshIO f3grid_flac3d_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_f3grid_flac3d_Writer"][1]""",
188
+ ".dato.gz MeshIO dato.gz_permas_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_dato.gz_permas_Writer"][1]""",
189
+ ".vtu MeshIO vtu_vtu_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_vtu_vtu_Writer"][1]""",
190
+ ".hmf MeshIO hmf_hmf_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_hmf_hmf_Writer"][1]""",
191
+ ".mesh MeshIO mesh_medit_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_mesh_medit_Writer"][1]""",
192
+ ".xmf Muscat XdmfWriter": "from Muscat.IO.XdmfWriter import XdmfWriter; res = XdmfWriter",
193
+ ".fem MeshIO fem_nastran_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_fem_nastran_Writer"][1]""",
194
+ ".stl MeshIO stl_stl_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_stl_stl_Writer"][1]""",
195
+ ".xdmf Muscat XdmfWriter": "from Muscat.IO.XdmfWriter import XdmfWriter; res = XdmfWriter",
196
+ ".obj MeshIO obj_obj_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_obj_obj_Writer"][1]""",
197
+ ".pickle Muscat PickleWriter": "from Muscat.IO.PickleTools import PickleWriter; res = PickleWriter",
198
+ ".svg MeshIO svg_svg_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_svg_svg_Writer"][1]""",
199
+ ".msh MeshIO msh_ansys_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_msh_ansys_Writer"][1]""",
200
+ ".inp MeshIO inp_abaqus_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_inp_abaqus_Writer"][1]""",
201
+ ".meshb Muscat MeshWriter": "from Muscat.IO.MeshWriter import MeshWriter; res = MeshWriter",
202
+ ".dato MeshIO dato_permas_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_dato_permas_Writer"][1]""",
203
+ ".xml MeshIO xml_dolfin-xml_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_xml_dolfin-xml_Writer"][1]""",
204
+ ".geof Muscat GeofWriter": "from Muscat.IO.GeofWriter import GeofWriter; res = GeofWriter",
205
+ ".post MeshIO post_permas_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_post_permas_Writer"][1]""",
206
+ ".meshb MeshIO meshb_medit_Writer": """from Muscat.Bridges.MeshIOBridge import InitAllWriters, writers; InitAllWriters(); res = writers["MeshIO_meshb_medit_Writer"][1]""",
207
+ ".csv Muscat CsvWriter": "from Muscat.IO.CsvWriter import CsvWriter; res = CsvWriter",
208
+ ".vol MeshIO vol_netgen_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_vol_netgen_Writer"][1]""",
209
+ ".mdpa MeshIO mdpa_mdpa_Writer": """from Muscat.Bridges.MeshIOBridge import writers; res = writers["MeshIO_mdpa_mdpa_Writer"][1]""",
210
+ }
211
+
212
+ WriterMapping = DelayInitialization(WriterDelayedInitStruct)
213
+
214
+ try:
215
+ from Muscat.Bridges.MeshIOBridge import InitAllReaders, InitAllWriters
216
+
217
+ InitAllWriters()
218
+ InitAllReaders()
219
+ except ImportError:
220
+ pass
221
+
222
+
223
+ def mesh_file_conversion(
224
+ inputs: Mapping[str, Any], parameters: Mapping[str, Any]
225
+ ) -> Mapping[str, Any]:
226
+ """Convert a mesh while preserving the explicit reader/writer algorithm.
227
+
228
+ Parameters
229
+ ----------
230
+ inputs : collections.abc.Mapping[str, Any]
231
+ Input and output filenames.
232
+ parameters : collections.abc.Mapping[str, Any]
233
+ Reader, writer, temporal selection, and binary-output settings.
234
+
235
+ Returns
236
+ -------
237
+ collections.abc.Mapping[str, Any]
238
+ The output filename, allowing the node to be chained in a workflow.
239
+
240
+ Raises
241
+ ------
242
+ MeshFileConversionError
243
+ If a path, selector, or reader/writer operation is invalid.
244
+ """
245
+ try:
246
+ inputfilename = Path(parameters["input_filename"]).expanduser()
247
+ outputfilename = Path(parameters["output_filename"]).expanduser()
248
+ except (KeyError, TypeError, ValueError) as error:
249
+ raise MeshFileConversionError(
250
+ "input_filename and output_filename must be valid paths"
251
+ ) from error
252
+
253
+ if not inputfilename.is_file():
254
+ raise MeshFileConversionError(f"Input mesh file does not exist: {inputfilename}")
255
+
256
+ if not outputfilename.parent.is_dir():
257
+ raise MeshFileConversionError(f"Output directory does not exist: {outputfilename.parent}")
258
+
259
+ readername = parameters.get("reader", "auto")
260
+ writername = parameters.get("writer", "auto")
261
+
262
+ if not inputfilename.suffix and not readername == "auto":
263
+ raise MeshFileConversionError("Input mesh filenames must have extensions")
264
+ if not outputfilename.suffix and not writername == "auto":
265
+ raise MeshFileConversionError("Output mesh filenames must have extensions")
266
+
267
+ binary = parameters.get("binary", True)
268
+
269
+ try:
270
+ reader = ReaderMapping.GetClass(inputfilename, readername)()
271
+ writer = WriterMapping.GetClass(outputfilename, writername)()
272
+ except (KeyError, MeshFileConversionError) as error:
273
+ raise MeshFileConversionError(f"Could not select reader or writer: {error}") from error
274
+
275
+ reader.SetFileName(inputfilename)
276
+ timestep_to_read = parameters.get("timestep_to_read", "last")
277
+
278
+ def DoWrite(the_mesh, the_writer, **extras):
279
+ PointFieldsNames = list(the_mesh.nodeFields.keys())
280
+ PointFields = list(the_mesh.nodeFields.values())
281
+ CellFieldsNames = list(the_mesh.elemFields.keys())
282
+ CellFields = list(the_mesh.elemFields.values())
283
+ the_writer.Write(
284
+ the_mesh,
285
+ PointFieldsNames=PointFieldsNames,
286
+ PointFields=PointFields,
287
+ CellFieldsNames=CellFieldsNames,
288
+ CellFields=CellFields,
289
+ **extras,
290
+ )
291
+
292
+ if reader.canHandleTemporal:
293
+ reader.ReadMetaData()
294
+ timesAvailable = reader.GetAvailableTimes()
295
+ writer.SetFileName(outputfilename)
296
+ if hasattr(writer, "SetBinary"):
297
+ writer.SetBinary(binary)
298
+
299
+ if timestep_to_read == "first":
300
+ times = [0]
301
+ elif timestep_to_read == "last":
302
+ times = [timesAvailable[-1]]
303
+ elif timestep_to_read == "all":
304
+ times = timesAvailable
305
+ else:
306
+ from Muscat.Helpers.ParserHelper import ReadInts
307
+
308
+ times = timesAvailable[ReadInts(timestep_to_read)]
309
+
310
+ if writer.canHandleTemporal:
311
+ writer.SetTemporal()
312
+ writer.Open(outputfilename)
313
+ try:
314
+ for t in times:
315
+ reader.SetTimeToRead(t)
316
+ mesh = reader.Read()
317
+ DoWrite(mesh, writer, Time=t)
318
+ finally:
319
+ writer.Close()
320
+ else:
321
+ writer.Open(outputfilename)
322
+ try:
323
+ DoWrite(reader.Read(), writer)
324
+ finally:
325
+ writer.Close()
326
+
327
+ else:
328
+ mesh = reader.Read()
329
+ writer.SetFileName(outputfilename)
330
+ if hasattr(writer, "SetBinary"):
331
+ writer.SetBinary(binary)
332
+ writer.Open()
333
+ try:
334
+ DoWrite(mesh, writer)
335
+ finally:
336
+ writer.Close()
337
+
338
+ return {"converted_filename": str(outputfilename)}
339
+
340
+
341
+ MESH_FILE_CONVERSION = NodeDefinition(
342
+ id="convert-mesh-file-format",
343
+ icon="mdi--file-swap",
344
+ label="Convert Mesh File Format",
345
+ description="Read and write a mesh (and solutions if supported) to a new format file",
346
+ ports=(PortDefinition("converted_filename", PortDirection.OUTPUT, FILE, "Output Filename"),),
347
+ executor=mesh_file_conversion,
348
+ parameters=(
349
+ ParameterDefinition("input_filename", ParameterKind.FILE, "Input Filename", ""),
350
+ ParameterDefinition("output_filename", ParameterKind.FILE, "Output Filename", ""),
351
+ ParameterDefinition(
352
+ "reader",
353
+ ParameterKind.STR_SELECT,
354
+ "Reader",
355
+ "auto",
356
+ options=(ParameterOption("auto", "Auto"),)
357
+ + tuple(ParameterOption(k, k) for k in ReaderMapping.keys()), # noqa: SIM118
358
+ port=False,
359
+ ),
360
+ ParameterDefinition(
361
+ "writer",
362
+ ParameterKind.STR_SELECT,
363
+ "Writer",
364
+ "auto",
365
+ options=(ParameterOption("auto", "Auto"),)
366
+ + tuple(ParameterOption(k, k) for k in WriterMapping.keys()), # noqa: SIM118
367
+ port=False,
368
+ ),
369
+ ParameterDefinition(
370
+ "timestep_to_read",
371
+ ParameterKind.STR_SELECT,
372
+ "Timestep to read",
373
+ "last",
374
+ options=(
375
+ ParameterOption("first", "First time step"),
376
+ ParameterOption("last", "Last time step"),
377
+ ParameterOption("all", "All time steps"),
378
+ # ParameterOption("Selected time step", "selected")
379
+ ),
380
+ port=False,
381
+ ),
382
+ ParameterDefinition(
383
+ "binary", ParameterKind.BOOLEAN, "binary (if available)", True, port=False
384
+ ),
385
+ # ParameterDefinition(
386
+ # "timestep", ParameterKind.LIST_INT, "time step to read", [-1], port=True
387
+ # ),
388
+ ),
389
+ )
390
+
391
+ __all__ = [
392
+ "MESH_FILE_CONVERSION",
393
+ ]
@@ -0,0 +1,191 @@
1
+ """Workflow nodes for selecting files and listing directory contents."""
2
+
3
+ import re
4
+ from collections.abc import Mapping
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from flowgraph.application.workflow_core import (
9
+ NodeDefinition,
10
+ ParameterDefinition,
11
+ PortDefinition,
12
+ PortDirection,
13
+ )
14
+
15
+ from .data_types import LIST_STR, STRING, ParameterKind
16
+
17
+
18
+ def _select_file(_inputs: Mapping[str, Any], parameters: Mapping[str, Any]) -> Mapping[str, Any]:
19
+ """Output the configured local file path."""
20
+ path = parameters.get("path", "")
21
+ if not isinstance(path, str):
22
+ raise TypeError("The selected file path parameter must be text")
23
+ if len(path) == 0:
24
+ raise ValueError("Path can't be empty")
25
+ return {"path": path}
26
+
27
+
28
+ SELECT_FILE = NodeDefinition(
29
+ id="select-file",
30
+ icon="mdi-file-find-outline",
31
+ label="Select file",
32
+ description="Outputs a selected local file path as text without reading the file.",
33
+ ports=(PortDefinition("path", PortDirection.OUTPUT, STRING, "File path"),),
34
+ executor=_select_file,
35
+ parameters=(
36
+ ParameterDefinition(
37
+ "path",
38
+ ParameterKind.FILE,
39
+ "File path",
40
+ "",
41
+ "/path/to/file",
42
+ file_patterns=("*.*",),
43
+ ),
44
+ ),
45
+ )
46
+
47
+
48
+ def _select_server_file(
49
+ _inputs: Mapping[str, Any], parameters: Mapping[str, Any]
50
+ ) -> Mapping[str, Any]:
51
+ """Output the configured uploaded-file path."""
52
+ path = parameters.get("file name", "")
53
+ if not isinstance(path, str):
54
+ raise TypeError("The selected file path parameter must be text")
55
+ if len(path) == 0:
56
+ raise ValueError("Path can't be empty")
57
+ return {"path": path}
58
+
59
+
60
+ SELECT_SERVER_FILE = NodeDefinition(
61
+ id="select-server-file",
62
+ icon="mdi-cloud-upload-outline",
63
+ label="Select uploaded file",
64
+ description="Outputs a selected server file path as text without reading the file.",
65
+ ports=(PortDefinition("path", PortDirection.OUTPUT, STRING, "File path"),),
66
+ executor=_select_server_file,
67
+ parameters=(
68
+ ParameterDefinition(
69
+ "file name", ParameterKind.SELECT_SERVER_FILENAME, "File Name", "", port=False
70
+ ),
71
+ ),
72
+ )
73
+
74
+
75
+ class DirectoryReadError(ValueError):
76
+ """Raised when a directory-file listing node receives an invalid directory path."""
77
+
78
+
79
+ def _compile_filename_patterns(
80
+ patterns: object,
81
+ ) -> tuple[tuple[re.Pattern[str], ...], tuple[re.Pattern[str], ...]]:
82
+ """Parse inclusive and exclusive filename regular expressions from text rules.
83
+
84
+ Each non-empty, non-comment line must start with ``+`` or ``include:`` to
85
+ include matching filenames, or ``-`` or ``exclude:`` to exclude them.
86
+
87
+ Parameters
88
+ ----------
89
+ patterns : object
90
+ Configured pattern-rule text.
91
+
92
+ Returns
93
+ -------
94
+ tuple[tuple[re.Pattern[str], ...], tuple[re.Pattern[str], ...]]
95
+ Compiled inclusive patterns followed by compiled exclusive patterns.
96
+
97
+ Raises
98
+ ------
99
+ TypeError
100
+ If the configured rules are not text.
101
+ ValueError
102
+ If a rule lacks a supported prefix, has no regular expression, or
103
+ contains an invalid regular expression.
104
+ """
105
+ if not isinstance(patterns, str):
106
+ raise TypeError("The filename pattern rules must be text")
107
+
108
+ inclusive: list[re.Pattern[str]] = []
109
+ exclusive: list[re.Pattern[str]] = []
110
+ for line_number, line in enumerate(patterns.splitlines(), start=1):
111
+ rule = line.strip()
112
+ if not rule or rule.startswith("#"):
113
+ continue
114
+
115
+ normalized = rule.casefold()
116
+ if normalized.startswith("include:"):
117
+ target, expression = inclusive, rule[len("include:") :].strip()
118
+ elif normalized.startswith("exclude:"):
119
+ target, expression = exclusive, rule[len("exclude:") :].strip()
120
+ elif rule.startswith("+"):
121
+ target, expression = inclusive, rule[1:].strip()
122
+ elif rule.startswith("-"):
123
+ target, expression = exclusive, rule[1:].strip()
124
+ else:
125
+ raise ValueError(
126
+ f"Filename pattern rule on line {line_number} must start with '+', '-', "
127
+ "'include:', or 'exclude:'"
128
+ )
129
+
130
+ if not expression:
131
+ raise ValueError(
132
+ f"Filename pattern rule on line {line_number} has no regular expression"
133
+ )
134
+ try:
135
+ target.append(re.compile(expression))
136
+ except re.error as error:
137
+ raise ValueError(
138
+ f"Invalid filename regular expression on line {line_number}: {error}"
139
+ ) from error
140
+
141
+ return tuple(inclusive), tuple(exclusive)
142
+
143
+
144
+ def _read_directory_files(
145
+ inputs: Mapping[str, Any], parameters: Mapping[str, Any]
146
+ ) -> Mapping[str, Any]:
147
+ """List immediate regular-file names in a directory after regex filtering."""
148
+ directory = Path(inputs["path"]).expanduser()
149
+ if not directory.is_dir():
150
+ raise DirectoryReadError(f"Directory does not exist or is not a directory: {directory}")
151
+
152
+ inclusive, exclusive = _compile_filename_patterns(parameters.get("patterns", ""))
153
+ filenames = sorted(path.name for path in directory.iterdir() if path.is_file())
154
+ filtered_filenames = [
155
+ filename
156
+ for filename in filenames
157
+ if (not inclusive or any(pattern.search(filename) for pattern in inclusive))
158
+ and not any(pattern.search(filename) for pattern in exclusive)
159
+ ]
160
+ return {"files": filtered_filenames}
161
+
162
+
163
+ READ_DIRECTORY_FILES = NodeDefinition(
164
+ id="read-directory-files",
165
+ icon="mdi-file-find-outline",
166
+ label="Read Directory Files",
167
+ description="Lists immediate file names in a directory with optional regular-expression filters.",
168
+ ports=(
169
+ PortDefinition("path", PortDirection.INPUT, STRING, "Directory path"),
170
+ PortDefinition("files", PortDirection.OUTPUT, LIST_STR, "File names"),
171
+ ),
172
+ executor=_read_directory_files,
173
+ parameters=(
174
+ ParameterDefinition(
175
+ "patterns",
176
+ ParameterKind.TEXT,
177
+ "Filename patterns",
178
+ "",
179
+ "+\\.csv$\n-exclude-this\\.csv$",
180
+ port=False,
181
+ ),
182
+ ),
183
+ )
184
+
185
+
186
+ __all__ = [
187
+ "READ_DIRECTORY_FILES",
188
+ "SELECT_FILE",
189
+ "SELECT_SERVER_FILE",
190
+ "DirectoryReadError",
191
+ ]