pycphy 0.1.0__py3-none-any.whl → 0.2.0__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 (67) hide show
  1. pycphy/__init__.py +11 -0
  2. pycphy/cli.py +145 -0
  3. pycphy/config_manager.py +373 -0
  4. pycphy/foamCaseDeveloper/__init__.py +60 -41
  5. pycphy/foamCaseDeveloper/config/__init__.py +69 -26
  6. pycphy/foamCaseDeveloper/config/cad_mesh_config.py +62 -0
  7. pycphy/foamCaseDeveloper/config/config_hfdibdem.py +193 -0
  8. pycphy/foamCaseDeveloper/config/constant/__init__.py +23 -0
  9. pycphy/foamCaseDeveloper/config/constant/dynamic_mesh_config.py +208 -0
  10. pycphy/foamCaseDeveloper/config/constant/gravity_field_config.py +379 -0
  11. pycphy/foamCaseDeveloper/config/constant/transport_properties_config.py +225 -0
  12. pycphy/foamCaseDeveloper/config/constant/turbulence_config.py +617 -0
  13. pycphy/foamCaseDeveloper/config/csv_boundary_reader.py +219 -0
  14. pycphy/foamCaseDeveloper/config/system/__init__.py +31 -0
  15. pycphy/foamCaseDeveloper/config/system/block_mesh_config.py +184 -0
  16. pycphy/foamCaseDeveloper/config/{control_config.py → system/control_config.py} +113 -1
  17. pycphy/foamCaseDeveloper/config/system/decompose_par_config.py +525 -0
  18. pycphy/foamCaseDeveloper/config/system/fv_options_config.py +575 -0
  19. pycphy/foamCaseDeveloper/config/system/fv_schemes_config.py +363 -0
  20. pycphy/foamCaseDeveloper/config/system/set_fields_config.py +640 -0
  21. pycphy/foamCaseDeveloper/config/system/snappy_hex_mesh_config.py +241 -0
  22. pycphy/foamCaseDeveloper/config/zero/U_config.py +135 -0
  23. pycphy/foamCaseDeveloper/config/zero/__init__.py +22 -0
  24. pycphy/foamCaseDeveloper/config/zero/f_config.py +140 -0
  25. pycphy/foamCaseDeveloper/config/zero/lambda_config.py +157 -0
  26. pycphy/foamCaseDeveloper/config/zero/p_config.py +97 -0
  27. pycphy/foamCaseDeveloper/core/__init__.py +30 -18
  28. pycphy/foamCaseDeveloper/core/block_mesh_developer.py +1 -1
  29. pycphy/foamCaseDeveloper/core/cad_block_mesh_developer.py +463 -0
  30. pycphy/foamCaseDeveloper/core/case_builder.py +1217 -0
  31. pycphy/foamCaseDeveloper/core/foam_case_manager.py +370 -111
  32. pycphy/foamCaseDeveloper/develop_case.py +640 -0
  33. pycphy/foamCaseDeveloper/main.py +260 -260
  34. pycphy/foamCaseDeveloper/utils/myAutoCAD.py +418 -0
  35. pycphy/foamCaseDeveloper/writers/__init__.py +37 -4
  36. pycphy/foamCaseDeveloper/writers/constant/__init__.py +25 -0
  37. pycphy/foamCaseDeveloper/writers/constant/dynamic_mesh_dict_writer.py +75 -0
  38. pycphy/foamCaseDeveloper/writers/constant/gravity_field_writer.py +88 -0
  39. pycphy/foamCaseDeveloper/writers/constant/hfdibdem_dict_writer.py +81 -0
  40. pycphy/foamCaseDeveloper/writers/constant/transport_properties_writer.py +202 -0
  41. pycphy/foamCaseDeveloper/writers/{turbulence_properties_writer.py → constant/turbulence_properties_writer.py} +49 -1
  42. pycphy/foamCaseDeveloper/writers/system/__init__.py +31 -0
  43. pycphy/foamCaseDeveloper/writers/{block_mesh_writer.py → system/block_mesh_writer.py} +1 -1
  44. pycphy/foamCaseDeveloper/writers/{control_dict_writer.py → system/control_dict_writer.py} +37 -1
  45. pycphy/foamCaseDeveloper/writers/system/decompose_par_writer.py +228 -0
  46. pycphy/foamCaseDeveloper/writers/system/fv_options_writer.py +188 -0
  47. pycphy/foamCaseDeveloper/writers/system/fv_schemes_writer.py +155 -0
  48. pycphy/foamCaseDeveloper/writers/system/set_fields_writer.py +191 -0
  49. pycphy/foamCaseDeveloper/writers/system/snappy_hex_mesh_writer.py +123 -0
  50. pycphy/foamCaseDeveloper/writers/zero/__init__.py +24 -0
  51. pycphy/foamCaseDeveloper/writers/zero/f_field_writer.py +89 -0
  52. pycphy/foamCaseDeveloper/writers/zero/lambda_field_writer.py +84 -0
  53. pycphy/foamCaseDeveloper/writers/zero/p_field_writer.py +89 -0
  54. pycphy/foamCaseDeveloper/writers/zero/u_field_writer.py +96 -0
  55. pycphy/foamCaseDeveloper/writers/zero/zero_field_factory.py +388 -0
  56. {pycphy-0.1.0.dist-info → pycphy-0.2.0.dist-info}/METADATA +154 -6
  57. pycphy-0.2.0.dist-info/RECORD +63 -0
  58. pycphy-0.2.0.dist-info/entry_points.txt +3 -0
  59. pycphy/foamCaseDeveloper/config/block_mesh_config.py +0 -90
  60. pycphy/foamCaseDeveloper/config/turbulence_config.py +0 -187
  61. pycphy/foamCaseDeveloper/core/control_dict_writer.py +0 -55
  62. pycphy/foamCaseDeveloper/core/turbulence_properties_writer.py +0 -68
  63. pycphy-0.1.0.dist-info/RECORD +0 -24
  64. pycphy-0.1.0.dist-info/entry_points.txt +0 -2
  65. {pycphy-0.1.0.dist-info → pycphy-0.2.0.dist-info}/WHEEL +0 -0
  66. {pycphy-0.1.0.dist-info → pycphy-0.2.0.dist-info}/licenses/LICENSE +0 -0
  67. {pycphy-0.1.0.dist-info → pycphy-0.2.0.dist-info}/top_level.txt +0 -0
@@ -1,19 +1,36 @@
1
1
  # foam_case_manager.py
2
2
 
3
3
  import os
4
- from .block_mesh_developer import BlockMeshDeveloper
5
- from .control_dict_writer import ControlDictWriter
6
- from .turbulence_properties_writer import TurbulencePropertiesWriter
4
+ from typing import Dict, Any
5
+ from .case_builder import (
6
+ GeometryComponent,
7
+ ControlComponent,
8
+ TurbulenceComponent,
9
+ DynamicMeshComponent,
10
+ HFDIBDEMComponent,
11
+ TransportPropertiesComponent,
12
+ FvSchemesComponent,
13
+ FvOptionsComponent,
14
+ GravityFieldComponent,
15
+ SetFieldsComponent,
16
+ DecomposeParComponent,
17
+ SnappyHexMeshComponent,
18
+ PFieldComponent,
19
+ UFieldComponent,
20
+ FFieldComponent,
21
+ LambdaFieldComponent
22
+ )
7
23
 
8
24
  class FoamCaseManager:
9
25
  """
10
26
  A comprehensive manager for creating and setting up OpenFOAM cases.
11
27
 
12
28
  This class provides a high-level interface for creating complete OpenFOAM
13
- case directories with all necessary configuration files.
29
+ case directories with all necessary configuration files using a modular
30
+ component-based approach.
14
31
  """
15
32
 
16
- def __init__(self, case_name):
33
+ def __init__(self, case_name: str):
17
34
  """
18
35
  Initialize the FoamCaseManager.
19
36
 
@@ -24,11 +41,50 @@ class FoamCaseManager:
24
41
  self.system_dir = os.path.join(case_name, "system")
25
42
  self.constant_dir = os.path.join(case_name, "constant")
26
43
 
44
+ # Initialize components
45
+ self.geometry = GeometryComponent()
46
+ self.control = ControlComponent()
47
+ self.turbulence = TurbulenceComponent()
48
+ self.dynamic_mesh = DynamicMeshComponent()
49
+ self.hfdibdem = HFDIBDEMComponent()
50
+ self.transport_properties = TransportPropertiesComponent()
51
+ self.fv_schemes = FvSchemesComponent()
52
+ self.fv_options = FvOptionsComponent()
53
+ self.gravity_field = GravityFieldComponent()
54
+ self.set_fields = SetFieldsComponent()
55
+ self.decompose_par = DecomposeParComponent()
56
+ self.snappy_hex_mesh = SnappyHexMeshComponent()
57
+ self.p_field = PFieldComponent()
58
+ self.u_field = UFieldComponent()
59
+ self.f_field = FFieldComponent()
60
+ self.lambda_field = LambdaFieldComponent()
61
+
62
+ # List of all components for easy iteration
63
+ self.components = [
64
+ self.geometry,
65
+ self.control,
66
+ self.turbulence,
67
+ self.dynamic_mesh,
68
+ self.hfdibdem,
69
+ self.transport_properties,
70
+ self.fv_schemes,
71
+ self.fv_options,
72
+ self.gravity_field,
73
+ self.set_fields,
74
+ self.decompose_par,
75
+ self.snappy_hex_mesh,
76
+ self.p_field,
77
+ self.u_field,
78
+ self.f_field,
79
+ self.lambda_field
80
+ ]
81
+
27
82
  # Ensure directories exist
28
83
  os.makedirs(self.system_dir, exist_ok=True)
29
84
  os.makedirs(self.constant_dir, exist_ok=True)
30
85
 
31
- def setup_geometry(self, p0, p1, cells, patch_names, scale=1.0):
86
+ def setup_geometry(self, p0: tuple, p1: tuple, cells: tuple,
87
+ patch_names: Dict[str, str], scale: float = 1.0) -> bool:
32
88
  """
33
89
  Set up the geometry and mesh configuration.
34
90
 
@@ -36,127 +92,282 @@ class FoamCaseManager:
36
92
  p0 (tuple): The minimum corner of the cube (x0, y0, z0).
37
93
  p1 (tuple): The maximum corner of the cube (x1, y1, z1).
38
94
  cells (tuple): Number of cells in each direction (nx, ny, nz).
39
- patch_names (dict): A dictionary mapping face identifiers to custom names.
95
+ patch_names (Dict): A dictionary mapping face identifiers to custom names.
40
96
  scale (float): The scaling factor for the mesh.
97
+
98
+ Returns:
99
+ bool: True if setup successful, False otherwise.
100
+ """
101
+ return self.geometry.configure(p0=p0, p1=p1, cells=cells,
102
+ patch_names=patch_names, scale=scale)
103
+
104
+ def mark_geometry_ready(self, mesh_summary: Dict[str, Any] = None) -> bool:
105
+ """
106
+ Mark the geometry as configured when using CAD-based or external mesh generation.
107
+
108
+ Args:
109
+ mesh_summary (Dict): Optional summary of mesh information
110
+
111
+ Returns:
112
+ bool: True if successful, False otherwise.
41
113
  """
42
- self.geometry_config = {
43
- 'p0': p0,
44
- 'p1': p1,
45
- 'cells': cells,
46
- 'patch_names': patch_names,
47
- 'scale': scale
48
- }
114
+ try:
115
+ # Mark geometry as configured
116
+ self.geometry.is_configured = True
117
+
118
+ # Store mesh summary if provided
119
+ if mesh_summary:
120
+ self.geometry.geometry_config = mesh_summary
121
+
122
+ return True
123
+ except Exception as e:
124
+ print(f"Error marking geometry as ready: {e}")
125
+ return False
49
126
 
50
- def setup_control(self, control_params):
127
+ def setup_control(self, control_params: Dict[str, Any]) -> bool:
51
128
  """
52
129
  Set up the control dictionary parameters.
53
130
 
54
131
  Args:
55
- control_params (dict): Dictionary containing control parameters.
132
+ control_params (Dict): Dictionary containing control parameters.
133
+
134
+ Returns:
135
+ bool: True if setup successful, False otherwise.
56
136
  """
57
- self.control_config = control_params
137
+ return self.control.configure(control_params=control_params)
58
138
 
59
- def setup_turbulence(self, simulation_type, model_properties):
139
+ def setup_turbulence(self, simulation_type: str, model_properties: Dict[str, Any]) -> bool:
60
140
  """
61
141
  Set up the turbulence model configuration.
62
142
 
63
143
  Args:
64
144
  simulation_type (str): The simulation type ('RAS', 'LES', 'laminar').
65
- model_properties (dict): Properties for the turbulence model.
145
+ model_properties (Dict): Properties for the turbulence model.
146
+
147
+ Returns:
148
+ bool: True if setup successful, False otherwise.
66
149
  """
67
- self.turbulence_config = {
68
- 'simulation_type': simulation_type,
69
- 'model_properties': model_properties
70
- }
150
+ return self.turbulence.configure(simulation_type=simulation_type,
151
+ model_properties=model_properties)
71
152
 
72
- def create_blockmesh_dict(self):
153
+ def setup_dynamic_mesh(self, write_dynamic_mesh_dict: bool, mesh_type: str,
154
+ mesh_properties: Dict[str, Any]) -> bool:
73
155
  """
74
- Create the blockMeshDict file.
156
+ Set up the dynamic mesh configuration.
75
157
 
158
+ Args:
159
+ write_dynamic_mesh_dict (bool): Whether to write dynamicMeshDict.
160
+ mesh_type (str): The type of dynamic mesh ('solidBodyMotion', 'multiBodyOverset', etc.).
161
+ mesh_properties (Dict): Properties for the dynamic mesh configuration.
162
+
76
163
  Returns:
77
- bool: True if successful, False otherwise.
164
+ bool: True if setup successful, False otherwise.
78
165
  """
79
- if not hasattr(self, 'geometry_config'):
80
- print("Error: Geometry configuration not set. Call setup_geometry() first.")
81
- return False
166
+ return self.dynamic_mesh.configure(write_dynamic_mesh_dict=write_dynamic_mesh_dict,
167
+ mesh_type=mesh_type, mesh_properties=mesh_properties)
168
+
169
+ def setup_hfdibdem(self, write_hfdibdem_dict: bool,
170
+ hfdibdem_properties: Dict[str, Any]) -> bool:
171
+ """
172
+ Set up the HFDIBDEM configuration.
82
173
 
83
- try:
84
- developer = BlockMeshDeveloper(
85
- p0=self.geometry_config['p0'],
86
- p1=self.geometry_config['p1'],
87
- cells=self.geometry_config['cells'],
88
- patch_names=self.geometry_config['patch_names'],
89
- scale=self.geometry_config.get('scale', 1.0)
90
- )
91
-
92
- bmd_path = os.path.join(self.system_dir, "blockMeshDict")
93
- developer.create_blockmesh_dict(file_path=bmd_path)
94
- return True
174
+ Args:
175
+ write_hfdibdem_dict (bool): Whether to write HFDIBDEMDict.
176
+ hfdibdem_properties (Dict): Properties for the HFDIBDEM configuration.
95
177
 
96
- except Exception as e:
97
- print(f"Error creating blockMeshDict: {e}")
98
- return False
178
+ Returns:
179
+ bool: True if setup successful, False otherwise.
180
+ """
181
+ return self.hfdibdem.configure(write_hfdibdem_dict=write_hfdibdem_dict,
182
+ hfdibdem_properties=hfdibdem_properties)
99
183
 
100
- def create_control_dict(self):
184
+ def validate_all_components(self) -> bool:
101
185
  """
102
- Create the controlDict file.
186
+ Validate all configured components.
103
187
 
104
188
  Returns:
105
- bool: True if successful, False otherwise.
189
+ bool: True if all required components are valid, False otherwise.
106
190
  """
107
- if not hasattr(self, 'control_config'):
108
- print("Error: Control configuration not set. Call setup_control() first.")
109
- return False
191
+ all_valid = True
110
192
 
111
- try:
112
- cd_path = os.path.join(self.system_dir, "controlDict")
113
- control_dict = ControlDictWriter(file_path=cd_path, params=self.control_config)
114
-
115
- # Validate parameters before writing
116
- if not control_dict.validate_params():
117
- print("Warning: Control parameters validation failed, but proceeding anyway.")
118
-
119
- control_dict.write()
120
- return True
121
-
122
- except Exception as e:
123
- print(f"Error creating controlDict: {e}")
124
- return False
193
+ for component in self.components:
194
+ if component.is_required and component.is_configured:
195
+ if not component.validate():
196
+ print(f"Validation failed for {component.name}")
197
+ all_valid = False
198
+ elif component.is_required and not component.is_configured:
199
+ print(f"Required component {component.name} is not configured")
200
+ all_valid = False
201
+
202
+ return all_valid
125
203
 
126
- def create_turbulence_properties(self):
204
+ def get_component_status(self) -> Dict[str, Dict[str, Any]]:
127
205
  """
128
- Create the turbulenceProperties file.
206
+ Get the status of all components.
129
207
 
130
208
  Returns:
131
- bool: True if successful, False otherwise.
209
+ Dict containing status information for each component.
132
210
  """
133
- if not hasattr(self, 'turbulence_config'):
134
- print("Error: Turbulence configuration not set. Call setup_turbulence() first.")
135
- return False
136
-
137
- try:
138
- tp_path = os.path.join(self.constant_dir, "turbulenceProperties")
139
- turbulence_writer = TurbulencePropertiesWriter(
140
- file_path=tp_path,
141
- simulation_type=self.turbulence_config['simulation_type'],
142
- model_properties=self.turbulence_config['model_properties']
143
- )
144
-
145
- # Validate configuration before writing
146
- if not turbulence_writer.validate_simulation_type():
147
- print("Warning: Simulation type validation failed, but proceeding anyway.")
148
-
149
- if not turbulence_writer.validate_model_properties():
150
- print("Warning: Model properties validation failed, but proceeding anyway.")
151
-
152
- turbulence_writer.write()
153
- return True
154
-
155
- except Exception as e:
156
- print(f"Error creating turbulenceProperties: {e}")
157
- return False
211
+ status = {}
212
+ for component in self.components:
213
+ status[component.name] = component.get_status()
214
+ return status
215
+
216
+ def print_component_status(self):
217
+ """Print the status of all components."""
218
+ print(f"\n=== Component Status for '{self.case_name}' ===")
219
+ for component in self.components:
220
+ status = component.get_status()
221
+ status_icon = "✓" if status['is_valid'] else "✗" if status['is_configured'] else "-"
222
+ required_icon = "!" if status['is_required'] else ""
223
+ print(f"{status_icon}{required_icon} {component.name}: {component.description}")
224
+ if component.is_configured:
225
+ print(f" Configured: {status['is_configured']}, Valid: {status['is_valid']}")
226
+ else:
227
+ print(f" Configured: {status['is_configured']}")
228
+ print("=" * 50)
229
+
230
+ def setup_transport_properties(self, write_transport_properties: bool, transport_model: str,
231
+ model_properties: Dict[str, Any], thermal_properties: Dict[str, Any] = None,
232
+ species_properties: Dict[str, Any] = None, advanced_properties: Dict[str, Any] = None) -> bool:
233
+ """Set up transport properties configuration."""
234
+ return self.transport_properties.configure(
235
+ write_transport_properties=write_transport_properties,
236
+ transport_model=transport_model,
237
+ model_properties=model_properties,
238
+ thermal_properties=thermal_properties,
239
+ species_properties=species_properties,
240
+ advanced_properties=advanced_properties
241
+ )
158
242
 
159
- def create_full_case(self):
243
+ def setup_fv_schemes(self, write_fv_schemes: bool, ddt_schemes: Dict[str, Any],
244
+ grad_schemes: Dict[str, Any], div_schemes: Dict[str, Any],
245
+ laplacian_schemes: Dict[str, Any], interpolation_schemes: Dict[str, Any],
246
+ sn_grad_schemes: Dict[str, Any], flux_required: Dict[str, Any] = None) -> bool:
247
+ """Set up finite volume schemes configuration."""
248
+ return self.fv_schemes.configure(
249
+ write_fv_schemes=write_fv_schemes,
250
+ ddt_schemes=ddt_schemes,
251
+ grad_schemes=grad_schemes,
252
+ div_schemes=div_schemes,
253
+ laplacian_schemes=laplacian_schemes,
254
+ interpolation_schemes=interpolation_schemes,
255
+ sn_grad_schemes=sn_grad_schemes,
256
+ flux_required=flux_required
257
+ )
258
+
259
+ def setup_fv_options(self, write_fv_options: bool, momentum_sources: Dict[str, Any] = None,
260
+ thermal_sources: Dict[str, Any] = None, species_sources: Dict[str, Any] = None,
261
+ turbulence_sources: Dict[str, Any] = None, pressure_sources: Dict[str, Any] = None,
262
+ volume_fraction_sources: Dict[str, Any] = None, advanced_sources: Dict[str, Any] = None) -> bool:
263
+ """Set up finite volume options configuration."""
264
+ return self.fv_options.configure(
265
+ write_fv_options=write_fv_options,
266
+ momentum_sources=momentum_sources,
267
+ thermal_sources=thermal_sources,
268
+ species_sources=species_sources,
269
+ turbulence_sources=turbulence_sources,
270
+ pressure_sources=pressure_sources,
271
+ volume_fraction_sources=volume_fraction_sources,
272
+ advanced_sources=advanced_sources
273
+ )
274
+
275
+ def setup_gravity_field(self, write_gravity_field: bool, gravity_value: tuple,
276
+ dimensions: list = None) -> bool:
277
+ """Set up gravity field configuration."""
278
+ return self.gravity_field.configure(
279
+ write_gravity_field=write_gravity_field,
280
+ gravity_value=gravity_value,
281
+ dimensions=dimensions
282
+ )
283
+
284
+ def setup_set_fields(self, write_set_fields_dict: bool, default_field_values: Dict[str, Any] = None,
285
+ regions: Dict[str, Any] = None) -> bool:
286
+ """Set up setFields configuration."""
287
+ return self.set_fields.configure(
288
+ write_set_fields_dict=write_set_fields_dict,
289
+ default_field_values=default_field_values,
290
+ regions=regions
291
+ )
292
+
293
+ def setup_decompose_par(self, write_decompose_par_dict: bool, number_of_subdomains: int,
294
+ method: str, coeffs: Dict[str, Any] = None, options: Dict[str, Any] = None,
295
+ fields: list = None, preserve_patches: list = None, preserve_cell_zones: list = None,
296
+ preserve_face_zones: list = None, preserve_point_zones: list = None) -> bool:
297
+ """Set up decomposePar configuration."""
298
+ return self.decompose_par.configure(
299
+ write_decompose_par_dict=write_decompose_par_dict,
300
+ number_of_subdomains=number_of_subdomains,
301
+ method=method,
302
+ coeffs=coeffs,
303
+ options=options,
304
+ fields=fields,
305
+ preserve_patches=preserve_patches,
306
+ preserve_cell_zones=preserve_cell_zones,
307
+ preserve_face_zones=preserve_face_zones,
308
+ preserve_point_zones=preserve_point_zones
309
+ )
310
+
311
+ def setup_snappy_hex_mesh(self, write_snappy_hex_mesh_dict: bool, geometry: Dict[str, Any],
312
+ castellated_mesh_controls: Dict[str, Any], snap_controls: Dict[str, Any],
313
+ add_layers_controls: Dict[str, Any] = None, mesh_quality_controls: Dict[str, Any] = None,
314
+ merged_patches: list = None, write_flags: Dict[str, Any] = None) -> bool:
315
+ """Set up snappyHexMesh configuration."""
316
+ return self.snappy_hex_mesh.configure(
317
+ write_snappy_hex_mesh_dict=write_snappy_hex_mesh_dict,
318
+ geometry=geometry,
319
+ castellated_mesh_controls=castellated_mesh_controls,
320
+ snap_controls=snap_controls,
321
+ add_layers_controls=add_layers_controls,
322
+ mesh_quality_controls=mesh_quality_controls,
323
+ merged_patches=merged_patches,
324
+ write_flags=write_flags
325
+ )
326
+
327
+ def setup_p_field(self, write_p_field: bool, internal_pressure: float,
328
+ boundary_conditions: Dict[str, Any], ref_pressure_cell: int = 0,
329
+ ref_pressure_value: float = 0.0, pressure_dimensions: list = None) -> bool:
330
+ """Set up pressure field configuration."""
331
+ return self.p_field.configure(
332
+ write_p_field=write_p_field,
333
+ internal_pressure=internal_pressure,
334
+ boundary_conditions=boundary_conditions,
335
+ ref_pressure_cell=ref_pressure_cell,
336
+ ref_pressure_value=ref_pressure_value,
337
+ pressure_dimensions=pressure_dimensions
338
+ )
339
+
340
+ def setup_u_field(self, write_u_field: bool, internal_velocity: tuple,
341
+ boundary_conditions: Dict[str, Any], velocity_dimensions: list = None) -> bool:
342
+ """Set up velocity field configuration."""
343
+ return self.u_field.configure(
344
+ write_u_field=write_u_field,
345
+ internal_velocity=internal_velocity,
346
+ boundary_conditions=boundary_conditions,
347
+ velocity_dimensions=velocity_dimensions
348
+ )
349
+
350
+ def setup_f_field(self, write_f_field: bool, internal_force: tuple,
351
+ boundary_conditions: Dict[str, Any], force_dimensions: list = None) -> bool:
352
+ """Set up force field configuration."""
353
+ return self.f_field.configure(
354
+ write_f_field=write_f_field,
355
+ internal_force=internal_force,
356
+ boundary_conditions=boundary_conditions,
357
+ force_dimensions=force_dimensions
358
+ )
359
+
360
+ def setup_lambda_field(self, write_lambda_field: bool, internal_lambda: float,
361
+ boundary_conditions: Dict[str, Any], lambda_dimensions: list = None) -> bool:
362
+ """Set up lambda field configuration."""
363
+ return self.lambda_field.configure(
364
+ write_lambda_field=write_lambda_field,
365
+ internal_lambda=internal_lambda,
366
+ boundary_conditions=boundary_conditions,
367
+ lambda_dimensions=lambda_dimensions
368
+ )
369
+
370
+ def create_full_case(self) -> bool:
160
371
  """
161
372
  Create a complete OpenFOAM case with all configuration files.
162
373
 
@@ -165,31 +376,79 @@ class FoamCaseManager:
165
376
  """
166
377
  print(f"--- Starting full OpenFOAM case setup for '{self.case_name}' ---")
167
378
 
168
- success = True
379
+ # Validate all components first
380
+ if not self.validate_all_components():
381
+ print("Component validation failed. Cannot proceed with case creation.")
382
+ self.print_component_status()
383
+ return False
169
384
 
170
- # Create blockMeshDict
171
- print("\n[Step 1/3] Creating blockMeshDict...")
172
- if not self.create_blockmesh_dict():
173
- success = False
385
+ print("\nAll components validated successfully. Proceeding with case creation...")
174
386
 
175
- # Create controlDict
176
- print("\n[Step 2/3] Creating controlDict...")
177
- if not self.create_control_dict():
178
- success = False
387
+ success = True
388
+ step = 1
389
+ total_steps = len([c for c in self.components if c.is_configured])
179
390
 
180
- # Create turbulenceProperties
181
- print("\n[Step 3/3] Creating turbulenceProperties...")
182
- if not self.create_turbulence_properties():
183
- success = False
391
+ # Build each configured component
392
+ for component in self.components:
393
+ if component.is_configured:
394
+ print(f"\n[Step {step}/{total_steps}] Creating {component.name}...")
395
+ if not component.build(self.case_name):
396
+ print(f"Failed to build {component.name}")
397
+ success = False
398
+ step += 1
184
399
 
185
400
  if success:
186
401
  print(f"\n--- Case setup complete! ---")
187
- print(f"Files written in '{self.case_name}'")
188
- print(f" - {os.path.join(self.case_name, 'system', 'blockMeshDict')}")
189
- print(f" - {os.path.join(self.case_name, 'system', 'controlDict')}")
190
- print(f" - {os.path.join(self.case_name, 'constant', 'turbulenceProperties')}")
402
+ print(f"Files written in '{self.case_name}':")
403
+
404
+ # List all created files
405
+ created_files = []
406
+
407
+ # System files
408
+ if self.geometry.is_configured:
409
+ created_files.append(os.path.join(self.case_name, 'system', 'blockMeshDict'))
410
+ if self.control.is_configured:
411
+ created_files.append(os.path.join(self.case_name, 'system', 'controlDict'))
412
+
413
+ # Constant files
414
+ if self.turbulence.is_configured:
415
+ created_files.append(os.path.join(self.case_name, 'constant', 'turbulenceProperties'))
416
+ if self.dynamic_mesh.is_configured and self.dynamic_mesh.write_dynamic_mesh_dict:
417
+ created_files.append(os.path.join(self.case_name, 'constant', 'dynamicMeshDict'))
418
+ if self.hfdibdem.is_configured and self.hfdibdem.write_hfdibdem_dict:
419
+ created_files.append(os.path.join(self.case_name, 'constant', 'HFDIBDEMDict'))
420
+ if self.transport_properties.is_configured and self.transport_properties.write_transport_properties:
421
+ created_files.append(os.path.join(self.case_name, 'constant', 'transportProperties'))
422
+ if self.gravity_field.is_configured and self.gravity_field.write_gravity_field:
423
+ created_files.append(os.path.join(self.case_name, 'constant', 'g'))
424
+
425
+ # Additional system files
426
+ if self.fv_schemes.is_configured and self.fv_schemes.write_fv_schemes:
427
+ created_files.append(os.path.join(self.case_name, 'system', 'fvSchemes'))
428
+ if self.fv_options.is_configured and self.fv_options.write_fv_options:
429
+ created_files.append(os.path.join(self.case_name, 'system', 'fvOptions'))
430
+ if self.set_fields.is_configured and self.set_fields.write_set_fields_dict:
431
+ created_files.append(os.path.join(self.case_name, 'system', 'setFieldsDict'))
432
+ if self.decompose_par.is_configured and self.decompose_par.write_decompose_par_dict:
433
+ created_files.append(os.path.join(self.case_name, 'system', 'decomposeParDict'))
434
+ if self.snappy_hex_mesh.is_configured and self.snappy_hex_mesh.write_snappy_hex_mesh_dict:
435
+ created_files.append(os.path.join(self.case_name, 'system', 'snappyHexMeshDict'))
436
+
437
+ # Zero directory files
438
+ if self.p_field.is_configured and self.p_field.write_p_field:
439
+ created_files.append(os.path.join(self.case_name, '0', 'p'))
440
+ if self.u_field.is_configured and self.u_field.write_u_field:
441
+ created_files.append(os.path.join(self.case_name, '0', 'U'))
442
+ if self.f_field.is_configured and self.f_field.write_f_field:
443
+ created_files.append(os.path.join(self.case_name, '0', 'f'))
444
+ if self.lambda_field.is_configured and self.lambda_field.write_lambda_field:
445
+ created_files.append(os.path.join(self.case_name, '0', 'lambda'))
446
+
447
+ for file_path in created_files:
448
+ print(f" - {file_path}")
449
+
191
450
  else:
192
451
  print(f"\n--- Case setup failed! ---")
193
- print("Some files could not be created. Check error messages above.")
452
+ print("Some components could not be built. Check error messages above.")
194
453
 
195
454
  return success