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
@@ -0,0 +1,188 @@
1
+ # fv_options_writer.py
2
+
3
+ """
4
+ Finite Volume Options Writer for OpenFOAM cases.
5
+
6
+ This writer handles the creation of fvOptions files with support for various
7
+ source terms, constraints, and modifications including momentum, thermal,
8
+ species, turbulence, and pressure sources.
9
+ """
10
+
11
+ from ..foam_writer import FoamWriter
12
+
13
+
14
+ class FvOptionsWriter(FoamWriter):
15
+ """
16
+ A class to write an OpenFOAM fvOptions file.
17
+
18
+ This writer supports comprehensive finite volume source terms and constraints
19
+ for various simulation types including momentum, thermal, species, and
20
+ multiphase flow simulations.
21
+ """
22
+
23
+ def __init__(self, file_path, momentum_sources=None, thermal_sources=None,
24
+ species_sources=None, turbulence_sources=None, pressure_sources=None,
25
+ volume_fraction_sources=None, advanced_sources=None):
26
+ """
27
+ Initialize the FvOptionsWriter.
28
+
29
+ Args:
30
+ file_path (str): The full path to the output file 'fvOptions'.
31
+ momentum_sources (dict, optional): Momentum sources configuration.
32
+ thermal_sources (dict, optional): Thermal sources configuration.
33
+ species_sources (dict, optional): Species sources configuration.
34
+ turbulence_sources (dict, optional): Turbulence sources configuration.
35
+ pressure_sources (dict, optional): Pressure sources configuration.
36
+ volume_fraction_sources (dict, optional): Volume fraction sources configuration.
37
+ advanced_sources (dict, optional): Advanced sources configuration.
38
+ """
39
+ super().__init__(file_path, foam_class="dictionary", foam_object="fvOptions")
40
+ self.momentum_sources = momentum_sources or {}
41
+ self.thermal_sources = thermal_sources or {}
42
+ self.species_sources = species_sources or {}
43
+ self.turbulence_sources = turbulence_sources or {}
44
+ self.pressure_sources = pressure_sources or {}
45
+ self.volume_fraction_sources = volume_fraction_sources or {}
46
+ self.advanced_sources = advanced_sources or {}
47
+
48
+ def validate_sources(self):
49
+ """
50
+ Validate the sources configuration.
51
+
52
+ Returns:
53
+ bool: True if validation passes, False otherwise.
54
+ """
55
+ valid = True
56
+
57
+ # Validate momentum sources
58
+ if self.momentum_sources.get('enabled', False):
59
+ sources = self.momentum_sources.get('sources', [])
60
+ for source in sources:
61
+ if 'type' not in source or 'fields' not in source:
62
+ print(f"Warning: Momentum source missing required fields: {source.get('name', 'unnamed')}")
63
+ valid = False
64
+
65
+ # Validate thermal sources
66
+ if self.thermal_sources.get('enabled', False):
67
+ sources = self.thermal_sources.get('sources', [])
68
+ for source in sources:
69
+ if 'type' not in source or 'fields' not in source:
70
+ print(f"Warning: Thermal source missing required fields: {source.get('name', 'unnamed')}")
71
+ valid = False
72
+
73
+ # Validate species sources
74
+ if self.species_sources.get('enabled', False):
75
+ sources = self.species_sources.get('sources', [])
76
+ for source in sources:
77
+ if 'type' not in source or 'fields' not in source:
78
+ print(f"Warning: Species source missing required fields: {source.get('name', 'unnamed')}")
79
+ valid = False
80
+
81
+ return valid
82
+
83
+ def _write_dict_recursively(self, data_dict, indent_level):
84
+ """
85
+ Recursively writes a dictionary's contents, handling nested dictionaries.
86
+
87
+ Args:
88
+ data_dict (dict): The dictionary to write.
89
+ indent_level (int): The current level of indentation.
90
+ """
91
+ indent = " " * indent_level
92
+ # Calculate padding for alignment within the current dictionary level
93
+ max_key_len = max((len(k) for k in data_dict.keys()), default=0)
94
+
95
+ for key, value in data_dict.items():
96
+ if isinstance(value, dict):
97
+ # If the value is another dictionary, start a new block
98
+ self.file_handle.write(f"{indent}{key}\n")
99
+ self.file_handle.write(f"{indent}{{\n")
100
+ self._write_dict_recursively(value, indent_level + 1)
101
+ self.file_handle.write(f"{indent}}}\n\n")
102
+ elif isinstance(value, (list, tuple)):
103
+ # Write lists and tuples
104
+ if isinstance(value, list) and value and isinstance(value[0], str):
105
+ # Quote string items
106
+ val_str = ' '.join([f'"{v}"' for v in value])
107
+ else:
108
+ # Standard numbers
109
+ val_str = ' '.join(map(str, value))
110
+
111
+ self.file_handle.write(f"{indent}{key:<{max_key_len}} ({val_str});\n")
112
+ elif isinstance(value, bool):
113
+ # Write booleans as 'true' or 'false'
114
+ val_str = "true" if value else "false"
115
+ self.file_handle.write(f"{indent}{key:<{max_key_len}} {val_str};\n")
116
+ else:
117
+ # Otherwise, it's a simple key-value pair
118
+ padded_key = f"{key:<{max_key_len}}"
119
+ self.file_handle.write(f"{indent}{padded_key} {value};\n")
120
+
121
+ def _write_source_section(self, section_name, sources_dict):
122
+ """
123
+ Write a source section (momentum sources, thermal sources, etc.).
124
+
125
+ Args:
126
+ section_name (str): Name of the source section.
127
+ sources_dict (dict): Dictionary containing source configurations.
128
+ """
129
+ if not sources_dict.get('enabled', False):
130
+ return
131
+
132
+ sources = sources_dict.get('sources', [])
133
+ if not sources:
134
+ return
135
+
136
+ for source in sources:
137
+ source_name = source.get('name', 'unnamed')
138
+ self.file_handle.write(f"{source_name}\n")
139
+ self.file_handle.write("{\n")
140
+
141
+ # Write source properties
142
+ for key, value in source.items():
143
+ if key != 'name':
144
+ if isinstance(value, dict):
145
+ self.file_handle.write(f" {key}\n")
146
+ self.file_handle.write(" {\n")
147
+ self._write_dict_recursively(value, indent_level=2)
148
+ self.file_handle.write(" }\n")
149
+ elif isinstance(value, (list, tuple)):
150
+ if isinstance(value, list) and value and isinstance(value[0], str):
151
+ val_str = ' '.join([f'"{v}"' for v in value])
152
+ else:
153
+ val_str = ' '.join(map(str, value))
154
+ self.file_handle.write(f" {key:<20} ({val_str});\n")
155
+ elif isinstance(value, bool):
156
+ val_str = "true" if value else "false"
157
+ self.file_handle.write(f" {key:<20} {val_str};\n")
158
+ else:
159
+ self.file_handle.write(f" {key:<20} {value};\n")
160
+
161
+ self.file_handle.write("}\n\n")
162
+
163
+ def _write_properties(self):
164
+ """Writes the main content of the fvOptions file."""
165
+ self._write_source_section("Momentum Sources", self.momentum_sources)
166
+ self._write_source_section("Thermal Sources", self.thermal_sources)
167
+ self._write_source_section("Species Sources", self.species_sources)
168
+ self._write_source_section("Turbulence Sources", self.turbulence_sources)
169
+ self._write_source_section("Pressure Sources", self.pressure_sources)
170
+ self._write_source_section("Volume Fraction Sources", self.volume_fraction_sources)
171
+ self._write_source_section("Advanced Sources", self.advanced_sources)
172
+
173
+ def write(self):
174
+ """
175
+ Writes the complete fvOptions file.
176
+ """
177
+ print(f"Writing fvOptions to: {self.file_path}")
178
+ with open(self.file_path, 'w') as f:
179
+ self.file_handle = f
180
+ self._write_header()
181
+ self._write_foamfile_dict()
182
+ self._write_separator()
183
+
184
+ self._write_properties()
185
+
186
+ self._write_footer()
187
+ self.file_handle = None
188
+ print("...Done")
@@ -0,0 +1,155 @@
1
+ # fv_schemes_writer.py
2
+
3
+ """
4
+ Finite Volume Schemes Writer for OpenFOAM cases.
5
+
6
+ This writer handles the creation of fvSchemes files with support for various
7
+ discretization schemes for time derivatives, gradients, divergence, Laplacian,
8
+ interpolation, and surface normal gradients.
9
+ """
10
+
11
+ from ..foam_writer import FoamWriter
12
+
13
+
14
+ class FvSchemesWriter(FoamWriter):
15
+ """
16
+ A class to write an OpenFOAM fvSchemes file.
17
+
18
+ This writer supports comprehensive finite volume discretization schemes
19
+ for various simulation types including laminar, turbulent, and multiphase flows.
20
+ """
21
+
22
+ def __init__(self, file_path, ddt_schemes, grad_schemes, div_schemes,
23
+ laplacian_schemes, interpolation_schemes, sn_grad_schemes,
24
+ flux_required=None):
25
+ """
26
+ Initialize the FvSchemesWriter.
27
+
28
+ Args:
29
+ file_path (str): The full path to the output file 'fvSchemes'.
30
+ ddt_schemes (dict): Time derivative schemes configuration.
31
+ grad_schemes (dict): Gradient schemes configuration.
32
+ div_schemes (dict): Divergence schemes configuration.
33
+ laplacian_schemes (dict): Laplacian schemes configuration.
34
+ interpolation_schemes (dict): Interpolation schemes configuration.
35
+ sn_grad_schemes (dict): Surface normal gradient schemes configuration.
36
+ flux_required (dict, optional): Flux required schemes configuration.
37
+ """
38
+ super().__init__(file_path, foam_class="dictionary", foam_object="fvSchemes")
39
+ self.ddt_schemes = ddt_schemes
40
+ self.grad_schemes = grad_schemes
41
+ self.div_schemes = div_schemes
42
+ self.laplacian_schemes = laplacian_schemes
43
+ self.interpolation_schemes = interpolation_schemes
44
+ self.sn_grad_schemes = sn_grad_schemes
45
+ self.flux_required = flux_required or {}
46
+
47
+ def validate_schemes(self):
48
+ """
49
+ Validate the schemes configuration.
50
+
51
+ Returns:
52
+ bool: True if validation passes, False otherwise.
53
+ """
54
+ valid = True
55
+
56
+ # Validate ddt schemes
57
+ valid_ddt = ['Euler', 'backward', 'CrankNicolson', 'localEuler',
58
+ 'steadyState', 'localSteadyState']
59
+ if self.ddt_schemes.get('default') not in valid_ddt:
60
+ print(f"Warning: Invalid ddt scheme '{self.ddt_schemes.get('default')}'")
61
+ valid = False
62
+
63
+ # Validate grad schemes
64
+ valid_grad = ['Gauss linear', 'Gauss linearUpwind', 'Gauss linearUpwind grad',
65
+ 'Gauss pointCellsLeastSquares', 'Gauss cellMDLimited',
66
+ 'Gauss faceMDLimited', 'leastSquares']
67
+ if self.grad_schemes.get('default') not in valid_grad:
68
+ print(f"Warning: Invalid grad scheme '{self.grad_schemes.get('default')}'")
69
+ valid = False
70
+
71
+ # Validate laplacian schemes
72
+ valid_laplacian = ['Gauss linear', 'Gauss linear corrected', 'Gauss linear limited',
73
+ 'Gauss linear limited corrected', 'Gauss linear uncorrected',
74
+ 'Gauss linear orthogonal']
75
+ if self.laplacian_schemes.get('default') not in valid_laplacian:
76
+ print(f"Warning: Invalid laplacian scheme '{self.laplacian_schemes.get('default')}'")
77
+ valid = False
78
+
79
+ # Validate interpolation schemes
80
+ valid_interpolation = ['linear', 'linearUpwind', 'skewCorrected linear', 'cubic',
81
+ 'upwind', 'midPoint', 'harmonic', 'pointCellsLeastSquares']
82
+ if self.interpolation_schemes.get('default') not in valid_interpolation:
83
+ print(f"Warning: Invalid interpolation scheme '{self.interpolation_schemes.get('default')}'")
84
+ valid = False
85
+
86
+ # Validate snGrad schemes
87
+ valid_sn_grad = ['corrected', 'uncorrected', 'limited', 'orthogonal', 'limited corrected']
88
+ if self.sn_grad_schemes.get('default') not in valid_sn_grad:
89
+ print(f"Warning: Invalid snGrad scheme '{self.sn_grad_schemes.get('default')}'")
90
+ valid = False
91
+
92
+ return valid
93
+
94
+ def _write_scheme_section(self, section_name, schemes_dict):
95
+ """
96
+ Write a scheme section (ddtSchemes, gradSchemes, etc.).
97
+
98
+ Args:
99
+ section_name (str): Name of the scheme section.
100
+ schemes_dict (dict): Dictionary containing scheme configurations.
101
+ """
102
+ self.file_handle.write(f"{section_name}\n")
103
+ self.file_handle.write("{\n")
104
+
105
+ # Write default scheme
106
+ default_scheme = schemes_dict.get('default', 'none')
107
+ self.file_handle.write(f" default {default_scheme};\n")
108
+
109
+ # Write field-specific schemes
110
+ field_specific = schemes_dict.get('fieldSpecific', {})
111
+ for field, scheme in field_specific.items():
112
+ self.file_handle.write(f" {field:<20} {scheme};\n")
113
+
114
+ self.file_handle.write("}\n\n")
115
+
116
+ def _write_flux_required(self):
117
+ """Write fluxRequired section if enabled."""
118
+ if not self.flux_required.get('enabled', False):
119
+ return
120
+
121
+ self.file_handle.write("fluxRequired\n")
122
+ self.file_handle.write("{\n")
123
+
124
+ fields = self.flux_required.get('fields', [])
125
+ for field in fields:
126
+ self.file_handle.write(f" {field};\n")
127
+
128
+ self.file_handle.write("}\n\n")
129
+
130
+ def _write_properties(self):
131
+ """Writes the main content of the fvSchemes file."""
132
+ self._write_scheme_section("ddtSchemes", self.ddt_schemes)
133
+ self._write_scheme_section("gradSchemes", self.grad_schemes)
134
+ self._write_scheme_section("divSchemes", self.div_schemes)
135
+ self._write_scheme_section("laplacianSchemes", self.laplacian_schemes)
136
+ self._write_scheme_section("interpolationSchemes", self.interpolation_schemes)
137
+ self._write_scheme_section("snGradSchemes", self.sn_grad_schemes)
138
+ self._write_flux_required()
139
+
140
+ def write(self):
141
+ """
142
+ Writes the complete fvSchemes file.
143
+ """
144
+ print(f"Writing fvSchemes to: {self.file_path}")
145
+ with open(self.file_path, 'w') as f:
146
+ self.file_handle = f
147
+ self._write_header()
148
+ self._write_foamfile_dict()
149
+ self._write_separator()
150
+
151
+ self._write_properties()
152
+
153
+ self._write_footer()
154
+ self.file_handle = None
155
+ print("...Done")
@@ -0,0 +1,191 @@
1
+ # set_fields_writer.py
2
+
3
+ """
4
+ Set Fields Writer for OpenFOAM cases.
5
+
6
+ This writer handles the creation of setFieldsDict files with support for
7
+ initializing field values in specific regions of the domain including
8
+ cell sets, cell zones, and geometric regions.
9
+ """
10
+
11
+ from ..foam_writer import FoamWriter
12
+
13
+
14
+ class SetFieldsWriter(FoamWriter):
15
+ """
16
+ A class to write an OpenFOAM setFieldsDict file.
17
+
18
+ This writer supports comprehensive field initialization for various
19
+ simulation types including multiphase flows, turbulent flows, heat transfer,
20
+ and species transport.
21
+ """
22
+
23
+ def __init__(self, file_path, default_field_values=None, regions=None):
24
+ """
25
+ Initialize the SetFieldsWriter.
26
+
27
+ Args:
28
+ file_path (str): The full path to the output file 'setFieldsDict'.
29
+ default_field_values (dict, optional): Default field values configuration.
30
+ regions (dict, optional): Regions configuration.
31
+ """
32
+ super().__init__(file_path, foam_class="dictionary", foam_object="setFieldsDict")
33
+ self.default_field_values = default_field_values or {}
34
+ self.regions = regions or {}
35
+
36
+ def validate_field_values(self):
37
+ """
38
+ Validate the field values configuration.
39
+
40
+ Returns:
41
+ bool: True if validation passes, False otherwise.
42
+ """
43
+ valid = True
44
+
45
+ # Validate default field values
46
+ if self.default_field_values:
47
+ fields = self.default_field_values.get('fields', [])
48
+ for field in fields:
49
+ if 'type' not in field or 'field' not in field or 'value' not in field:
50
+ print(f"Warning: Default field value missing required fields: {field}")
51
+ valid = False
52
+
53
+ # Validate regions
54
+ if self.regions:
55
+ regions_list = self.regions.get('regions', [])
56
+ for region in regions_list:
57
+ if 'type' not in region or 'fieldValues' not in region:
58
+ print(f"Warning: Region missing required fields: {region.get('name', 'unnamed')}")
59
+ valid = False
60
+
61
+ # Validate field values in region
62
+ field_values = region.get('fieldValues', [])
63
+ for field_value in field_values:
64
+ if 'type' not in field_value or 'field' not in field_value or 'value' not in field_value:
65
+ print(f"Warning: Field value in region missing required fields: {field_value}")
66
+ valid = False
67
+
68
+ return valid
69
+
70
+ def _write_dict_recursively(self, data_dict, indent_level):
71
+ """
72
+ Recursively writes a dictionary's contents, handling nested dictionaries.
73
+
74
+ Args:
75
+ data_dict (dict): The dictionary to write.
76
+ indent_level (int): The current level of indentation.
77
+ """
78
+ indent = " " * indent_level
79
+ # Calculate padding for alignment within the current dictionary level
80
+ max_key_len = max((len(k) for k in data_dict.keys()), default=0)
81
+
82
+ for key, value in data_dict.items():
83
+ if isinstance(value, dict):
84
+ # If the value is another dictionary, start a new block
85
+ self.file_handle.write(f"{indent}{key}\n")
86
+ self.file_handle.write(f"{indent}{{\n")
87
+ self._write_dict_recursively(value, indent_level + 1)
88
+ self.file_handle.write(f"{indent}}}\n\n")
89
+ elif isinstance(value, (list, tuple)):
90
+ # Write lists and tuples
91
+ if isinstance(value, list) and value and isinstance(value[0], str):
92
+ # Quote string items
93
+ val_str = ' '.join([f'"{v}"' for v in value])
94
+ else:
95
+ # Standard numbers
96
+ val_str = ' '.join(map(str, value))
97
+
98
+ self.file_handle.write(f"{indent}{key:<{max_key_len}} ({val_str});\n")
99
+ elif isinstance(value, bool):
100
+ # Write booleans as 'true' or 'false'
101
+ val_str = "true" if value else "false"
102
+ self.file_handle.write(f"{indent}{key:<{max_key_len}} {val_str};\n")
103
+ else:
104
+ # Otherwise, it's a simple key-value pair
105
+ padded_key = f"{key:<{max_key_len}}"
106
+ self.file_handle.write(f"{indent}{padded_key} {value};\n")
107
+
108
+ def _write_field_value(self, field_value):
109
+ """
110
+ Write a single field value entry.
111
+
112
+ Args:
113
+ field_value (dict): Field value configuration.
114
+ """
115
+ field_type = field_value.get('type', 'volScalarFieldValue')
116
+ field_name = field_value.get('field', 'unknown')
117
+ field_value_val = field_value.get('value', 0.0)
118
+
119
+ self.file_handle.write(f" {field_type:<25} {field_name:<15} {field_value_val};\n")
120
+
121
+ def _write_default_field_values(self):
122
+ """Write default field values section."""
123
+ if not self.default_field_values:
124
+ return
125
+
126
+ fields = self.default_field_values.get('fields', [])
127
+ if not fields:
128
+ return
129
+
130
+ self.file_handle.write("defaultFieldValues\n")
131
+ self.file_handle.write("(\n")
132
+
133
+ for field in fields:
134
+ self._write_field_value(field)
135
+
136
+ self.file_handle.write(")\n\n")
137
+
138
+ def _write_regions(self):
139
+ """Write regions section."""
140
+ if not self.regions:
141
+ return
142
+
143
+ regions_list = self.regions.get('regions', [])
144
+ if not regions_list:
145
+ return
146
+
147
+ self.file_handle.write("regions\n")
148
+ self.file_handle.write("(\n")
149
+
150
+ for region in regions_list:
151
+ region_name = region.get('name', 'unnamed')
152
+ region_type = region.get('type', 'cellToCell')
153
+ region_set = region.get('set', 'unknown')
154
+ field_values = region.get('fieldValues', [])
155
+
156
+ self.file_handle.write(f" // {region_name}\n")
157
+ self.file_handle.write(f" {region_type}\n")
158
+ self.file_handle.write(" {\n")
159
+ self.file_handle.write(f" set {region_set};\n\n")
160
+ self.file_handle.write(" fieldValues\n")
161
+ self.file_handle.write(" (\n")
162
+
163
+ for field_value in field_values:
164
+ self._write_field_value(field_value)
165
+
166
+ self.file_handle.write(" );\n")
167
+ self.file_handle.write(" }\n\n")
168
+
169
+ self.file_handle.write(")\n\n")
170
+
171
+ def _write_properties(self):
172
+ """Writes the main content of the setFieldsDict file."""
173
+ self._write_default_field_values()
174
+ self._write_regions()
175
+
176
+ def write(self):
177
+ """
178
+ Writes the complete setFieldsDict file.
179
+ """
180
+ print(f"Writing setFieldsDict to: {self.file_path}")
181
+ with open(self.file_path, 'w') as f:
182
+ self.file_handle = f
183
+ self._write_header()
184
+ self._write_foamfile_dict()
185
+ self._write_separator()
186
+
187
+ self._write_properties()
188
+
189
+ self._write_footer()
190
+ self.file_handle = None
191
+ print("...Done")
@@ -0,0 +1,123 @@
1
+ # snappy_hex_mesh_writer.py
2
+
3
+ import os
4
+ from typing import Dict, Any, List, Optional
5
+ from ..foam_writer import FoamWriter
6
+
7
+ class SnappyHexMeshWriter(FoamWriter):
8
+ """
9
+ A writer class for OpenFOAM 'snappyHexMeshDict' files.
10
+ Handles complex mesh generation with geometry snapping and boundary layers.
11
+ """
12
+
13
+ def __init__(self, file_path: str, geometry: Dict[str, Any],
14
+ castellated_mesh_controls: Dict[str, Any],
15
+ snap_controls: Dict[str, Any],
16
+ add_layers_controls: Optional[Dict[str, Any]] = None,
17
+ mesh_quality_controls: Optional[Dict[str, Any]] = None,
18
+ merged_patches: Optional[List[str]] = None,
19
+ write_flags: Optional[Dict[str, Any]] = None):
20
+ """
21
+ Initializes the SnappyHexMeshWriter.
22
+
23
+ Args:
24
+ file_path (str): The full path to the snappyHexMeshDict file.
25
+ geometry (Dict[str, Any]): Dictionary containing geometry definitions.
26
+ castellated_mesh_controls (Dict[str, Any]): Controls for initial mesh generation.
27
+ snap_controls (Dict[str, Any]): Controls for mesh snapping to geometry.
28
+ add_layers_controls (Optional[Dict[str, Any]]): Controls for boundary layer generation.
29
+ mesh_quality_controls (Optional[Dict[str, Any]]): Controls for mesh quality optimization.
30
+ merged_patches (Optional[List[str]]): List of patches to be merged.
31
+ write_flags (Optional[Dict[str, Any]]): Control what files are written.
32
+ """
33
+ super().__init__(file_path, foam_class="dictionary", foam_object="snappyHexMeshDict")
34
+ self.geometry = geometry
35
+ self.castellated_mesh_controls = castellated_mesh_controls
36
+ self.snap_controls = snap_controls
37
+ self.add_layers_controls = add_layers_controls or {}
38
+ self.mesh_quality_controls = mesh_quality_controls or {}
39
+ self.merged_patches = merged_patches or []
40
+ self.write_flags = write_flags or {}
41
+
42
+ def write(self) -> None:
43
+ """
44
+ Writes the snappyHexMeshDict to the specified file.
45
+ """
46
+ with open(self.file_path, 'w') as f:
47
+ self.file_handle = f
48
+ self._write_header()
49
+
50
+ # Write geometry
51
+ f.write("geometry\n")
52
+ f.write("{\n")
53
+ self._write_dict_recursively(f, self.geometry, indent_level=1)
54
+ f.write("};\n\n")
55
+
56
+ # Write castellated mesh controls
57
+ f.write("castellatedMeshControls\n")
58
+ f.write("{\n")
59
+ self._write_dict_recursively(f, self.castellated_mesh_controls, indent_level=1)
60
+ f.write("};\n\n")
61
+
62
+ # Write snap controls
63
+ f.write("snapControls\n")
64
+ f.write("{\n")
65
+ self._write_dict_recursively(f, self.snap_controls, indent_level=1)
66
+ f.write("};\n\n")
67
+
68
+ # Write add layers controls (if provided)
69
+ if self.add_layers_controls:
70
+ f.write("addLayersControls\n")
71
+ f.write("{\n")
72
+ self._write_dict_recursively(f, self.add_layers_controls, indent_level=1)
73
+ f.write("};\n\n")
74
+
75
+ # Write mesh quality controls
76
+ if self.mesh_quality_controls:
77
+ f.write("meshQualityControls\n")
78
+ f.write("{\n")
79
+ self._write_dict_recursively(f, self.mesh_quality_controls, indent_level=1)
80
+ f.write("};\n\n")
81
+
82
+ # Write merged patches (if any)
83
+ if self.merged_patches:
84
+ f.write("mergePatchPairs\n")
85
+ f.write("(\n")
86
+ for patch in self.merged_patches:
87
+ f.write(f" {patch}\n")
88
+ f.write(");\n\n")
89
+
90
+ # Write flags
91
+ if self.write_flags:
92
+ self._write_dict_recursively(f, self.write_flags, indent_level=0)
93
+
94
+ self._write_footer()
95
+
96
+ def _write_dict_recursively(self, f, data_dict: Dict[str, Any], indent_level: int) -> None:
97
+ """
98
+ Recursively writes dictionary contents to the file with proper indentation.
99
+ """
100
+ indent = " " * indent_level
101
+ for key, value in data_dict.items():
102
+ if isinstance(value, dict):
103
+ f.write(f"{indent}{key}\n")
104
+ f.write(f"{indent}{{\n")
105
+ self._write_dict_recursively(f, value, indent_level + 1)
106
+ f.write(f"{indent}}}\n")
107
+ elif isinstance(value, (list, tuple)):
108
+ if isinstance(value[0], dict):
109
+ # Handle list of dictionaries (e.g., features)
110
+ f.write(f"{indent}{key}\n")
111
+ f.write(f"{indent}(\n")
112
+ for item in value:
113
+ f.write(f"{indent} {{\n")
114
+ self._write_dict_recursively(f, item, indent_level + 2)
115
+ f.write(f"{indent} }}\n")
116
+ f.write(f"{indent});\n")
117
+ else:
118
+ # Handle simple lists/tuples
119
+ f.write(f"{indent}{key} (")
120
+ f.write(" ".join(map(str, value)))
121
+ f.write(");\n")
122
+ else:
123
+ f.write(f"{indent}{key} {value};\n")
@@ -0,0 +1,24 @@
1
+ # zero/__init__.py
2
+ """
3
+ Zero directory writer modules.
4
+
5
+ This package contains writer modules for OpenFOAM 0 directory field files:
6
+ - p (pressure field)
7
+ - U (velocity field)
8
+ - f (force field)
9
+ - lambda (scalar field)
10
+ """
11
+
12
+ from . import p_field_writer
13
+ from . import u_field_writer
14
+ from . import f_field_writer
15
+ from . import lambda_field_writer
16
+ from . import zero_field_factory
17
+
18
+ __all__ = [
19
+ 'p_field_writer',
20
+ 'u_field_writer',
21
+ 'f_field_writer',
22
+ 'lambda_field_writer',
23
+ 'zero_field_factory'
24
+ ]