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.
- pycphy/__init__.py +11 -0
- pycphy/cli.py +145 -0
- pycphy/config_manager.py +373 -0
- pycphy/foamCaseDeveloper/__init__.py +60 -41
- pycphy/foamCaseDeveloper/config/__init__.py +69 -26
- pycphy/foamCaseDeveloper/config/cad_mesh_config.py +62 -0
- pycphy/foamCaseDeveloper/config/config_hfdibdem.py +193 -0
- pycphy/foamCaseDeveloper/config/constant/__init__.py +23 -0
- pycphy/foamCaseDeveloper/config/constant/dynamic_mesh_config.py +208 -0
- pycphy/foamCaseDeveloper/config/constant/gravity_field_config.py +379 -0
- pycphy/foamCaseDeveloper/config/constant/transport_properties_config.py +225 -0
- pycphy/foamCaseDeveloper/config/constant/turbulence_config.py +617 -0
- pycphy/foamCaseDeveloper/config/csv_boundary_reader.py +219 -0
- pycphy/foamCaseDeveloper/config/system/__init__.py +31 -0
- pycphy/foamCaseDeveloper/config/system/block_mesh_config.py +184 -0
- pycphy/foamCaseDeveloper/config/{control_config.py → system/control_config.py} +113 -1
- pycphy/foamCaseDeveloper/config/system/decompose_par_config.py +525 -0
- pycphy/foamCaseDeveloper/config/system/fv_options_config.py +575 -0
- pycphy/foamCaseDeveloper/config/system/fv_schemes_config.py +363 -0
- pycphy/foamCaseDeveloper/config/system/set_fields_config.py +640 -0
- pycphy/foamCaseDeveloper/config/system/snappy_hex_mesh_config.py +241 -0
- pycphy/foamCaseDeveloper/config/zero/U_config.py +135 -0
- pycphy/foamCaseDeveloper/config/zero/__init__.py +22 -0
- pycphy/foamCaseDeveloper/config/zero/f_config.py +140 -0
- pycphy/foamCaseDeveloper/config/zero/lambda_config.py +157 -0
- pycphy/foamCaseDeveloper/config/zero/p_config.py +97 -0
- pycphy/foamCaseDeveloper/core/__init__.py +30 -18
- pycphy/foamCaseDeveloper/core/block_mesh_developer.py +1 -1
- pycphy/foamCaseDeveloper/core/cad_block_mesh_developer.py +463 -0
- pycphy/foamCaseDeveloper/core/case_builder.py +1217 -0
- pycphy/foamCaseDeveloper/core/foam_case_manager.py +370 -111
- pycphy/foamCaseDeveloper/develop_case.py +640 -0
- pycphy/foamCaseDeveloper/main.py +260 -260
- pycphy/foamCaseDeveloper/utils/myAutoCAD.py +418 -0
- pycphy/foamCaseDeveloper/writers/__init__.py +37 -4
- pycphy/foamCaseDeveloper/writers/constant/__init__.py +25 -0
- pycphy/foamCaseDeveloper/writers/constant/dynamic_mesh_dict_writer.py +75 -0
- pycphy/foamCaseDeveloper/writers/constant/gravity_field_writer.py +88 -0
- pycphy/foamCaseDeveloper/writers/constant/hfdibdem_dict_writer.py +81 -0
- pycphy/foamCaseDeveloper/writers/constant/transport_properties_writer.py +202 -0
- pycphy/foamCaseDeveloper/writers/{turbulence_properties_writer.py → constant/turbulence_properties_writer.py} +49 -1
- pycphy/foamCaseDeveloper/writers/system/__init__.py +31 -0
- pycphy/foamCaseDeveloper/writers/{block_mesh_writer.py → system/block_mesh_writer.py} +1 -1
- pycphy/foamCaseDeveloper/writers/{control_dict_writer.py → system/control_dict_writer.py} +37 -1
- pycphy/foamCaseDeveloper/writers/system/decompose_par_writer.py +228 -0
- pycphy/foamCaseDeveloper/writers/system/fv_options_writer.py +188 -0
- pycphy/foamCaseDeveloper/writers/system/fv_schemes_writer.py +155 -0
- pycphy/foamCaseDeveloper/writers/system/set_fields_writer.py +191 -0
- pycphy/foamCaseDeveloper/writers/system/snappy_hex_mesh_writer.py +123 -0
- pycphy/foamCaseDeveloper/writers/zero/__init__.py +24 -0
- pycphy/foamCaseDeveloper/writers/zero/f_field_writer.py +89 -0
- pycphy/foamCaseDeveloper/writers/zero/lambda_field_writer.py +84 -0
- pycphy/foamCaseDeveloper/writers/zero/p_field_writer.py +89 -0
- pycphy/foamCaseDeveloper/writers/zero/u_field_writer.py +96 -0
- pycphy/foamCaseDeveloper/writers/zero/zero_field_factory.py +388 -0
- {pycphy-0.1.0.dist-info → pycphy-0.2.0.dist-info}/METADATA +154 -6
- pycphy-0.2.0.dist-info/RECORD +63 -0
- pycphy-0.2.0.dist-info/entry_points.txt +3 -0
- pycphy/foamCaseDeveloper/config/block_mesh_config.py +0 -90
- pycphy/foamCaseDeveloper/config/turbulence_config.py +0 -187
- pycphy/foamCaseDeveloper/core/control_dict_writer.py +0 -55
- pycphy/foamCaseDeveloper/core/turbulence_properties_writer.py +0 -68
- pycphy-0.1.0.dist-info/RECORD +0 -24
- pycphy-0.1.0.dist-info/entry_points.txt +0 -2
- {pycphy-0.1.0.dist-info → pycphy-0.2.0.dist-info}/WHEEL +0 -0
- {pycphy-0.1.0.dist-info → pycphy-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {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
|
+
]
|