Skip to content

Commit 303bb12

Browse files
authored
Add structured box region to volume meshing params (#1463)
1 parent 3154da7 commit 303bb12

File tree

6 files changed

+305
-10
lines changed

6 files changed

+305
-10
lines changed

flow360/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
AxisymmetricRefinement,
3434
RotationCylinder,
3535
RotationVolume,
36+
StructuredBoxRefinement,
3637
UniformRefinement,
3738
UserDefinedFarfield,
3839
)
@@ -198,6 +199,7 @@
198199
"SurfaceRefinement",
199200
"AutomatedFarfield",
200201
"AxisymmetricRefinement",
202+
"StructuredBoxRefinement",
201203
"RotationCylinder",
202204
"RotationVolume",
203205
"UniformRefinement",

flow360/component/simulation/meshing_param/params.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
AxisymmetricRefinement,
2121
RotationCylinder,
2222
RotationVolume,
23+
StructuredBoxRefinement,
2324
UniformRefinement,
2425
UserDefinedFarfield,
2526
)
@@ -42,6 +43,7 @@
4243
BoundaryLayer,
4344
PassiveSpacing,
4445
UniformRefinement,
46+
StructuredBoxRefinement,
4547
AxisymmetricRefinement,
4648
],
4749
pd.Field(discriminator="refinement_type"),
@@ -334,6 +336,14 @@ def _check_no_reused_volume_entities(self) -> Self:
334336
| UniformRefinement | YES | NO | NO |
335337
+------------------------+------------------------+------------------------+------------------------+
336338
339+
+------------------------+------------------------+------------------------+
340+
| |StructuredBoxRefinement | UniformRefinement |
341+
+------------------------+------------------------+------------------------+
342+
|StructuredBoxRefinement | NO | -- |
343+
+------------------------+------------------------+------------------------+
344+
| UniformRefinement | NO | NO |
345+
+------------------------+------------------------+------------------------+
346+
337347
"""
338348

339349
usage = EntityUsageMap()
@@ -347,7 +357,10 @@ def _check_no_reused_volume_entities(self) -> Self:
347357
]
348358

349359
for refinement in self.refinements if self.refinements is not None else []:
350-
if isinstance(refinement, (UniformRefinement, AxisymmetricRefinement)):
360+
if isinstance(
361+
refinement,
362+
(UniformRefinement, AxisymmetricRefinement, StructuredBoxRefinement),
363+
):
351364
# pylint: disable=protected-access
352365
_ = [
353366
usage.add_entity_usage(item, refinement.refinement_type)

flow360/component/simulation/meshing_param/volume_params.py

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,63 @@ class UniformRefinement(Flow360BaseModel):
5252
spacing: LengthType.Positive = pd.Field(description="The required refinement spacing.")
5353

5454

55+
class StructuredBoxRefinement(Flow360BaseModel):
56+
"""
57+
- The mesh inside the :class:`StructuredBoxRefinement` is semi-structured.
58+
- The :class:`StructuredBoxRefinement` cannot enclose/intersect with other objects.
59+
- The spacings along the three box axes can be adjusted independently.
60+
61+
Example
62+
-------
63+
64+
>>> StructuredBoxRefinement(
65+
... entities=[
66+
... Box.from_principal_axes(
67+
... name="boxRefinement",
68+
... center=(0, 1, 1) * fl.u.cm,
69+
... size=(1, 2, 1) * fl.u.cm,
70+
... axes=((2, 2, 0), (-2, 2, 0)),
71+
... )
72+
... ],
73+
... spacing_axis1=7.5*u.cm,
74+
... spacing_axis2=10*u.cm,
75+
... spacing_normal=15*u.cm,
76+
... )
77+
====
78+
"""
79+
80+
# pylint: disable=no-member
81+
# pylint: disable=too-few-public-methods
82+
name: Optional[str] = pd.Field("StructuredBoxRefinement")
83+
refinement_type: Literal["StructuredBoxRefinement"] = pd.Field(
84+
"StructuredBoxRefinement", frozen=True
85+
)
86+
entities: EntityList[Box] = pd.Field()
87+
88+
spacing_axis1: LengthType.Positive = pd.Field(
89+
description="Spacing along the first axial direction."
90+
)
91+
spacing_axis2: LengthType.Positive = pd.Field(
92+
description="Spacing along the second axial direction."
93+
)
94+
spacing_normal: LengthType.Positive = pd.Field(
95+
description="Spacing along the normal axial direction."
96+
)
97+
98+
@pd.model_validator(mode="after")
99+
def _validate_only_in_beta_mesher(self):
100+
"""
101+
Ensure that StructuredBoxRefinement objects are only processed with the beta mesher.
102+
"""
103+
validation_info = get_validation_info()
104+
if validation_info is None:
105+
return self
106+
if validation_info.is_beta_mesher:
107+
return self
108+
109+
raise ValueError("`StructuredBoxRefinement` is only supported with the beta mesher.")
110+
111+
55112
class AxisymmetricRefinementBase(Flow360BaseModel, metaclass=ABCMeta):
56113
"""Base class for all refinements that requires spacing in axial, radial and circumferential directions."""
57114

@@ -149,11 +206,12 @@ class RotationVolume(AxisymmetricRefinementBase):
149206
type: Literal["RotationVolume"] = pd.Field("RotationVolume", frozen=True)
150207
name: Optional[str] = pd.Field("Rotation Volume", description="Name to display in the GUI.")
151208
entities: EntityList[Cylinder, AxisymmetricBody] = pd.Field()
152-
enclosed_entities: Optional[EntityList[Cylinder, Surface, AxisymmetricBody]] = pd.Field(
209+
enclosed_entities: Optional[EntityList[Cylinder, Surface, AxisymmetricBody, Box]] = pd.Field(
153210
None,
154211
description="Entities enclosed by :class:`RotationVolume`. "
155-
+ "Can be `Surface` and/or other :class:`~flow360.Cylinder`(s)"
156-
"and/or other :class:`~flow360.AxisymmetricBody`(s).",
212+
"Can be `Surface` and/or other :class:`~flow360.Cylinder`(s)"
213+
"and/or other :class:`~flow360.AxisymmetricBody`(s)"
214+
"and/or other :class:`~flow360.Box`(s)",
157215
)
158216

159217
@pd.field_validator("entities", mode="after")
@@ -196,6 +254,28 @@ def _validate_cylinder_name_length(cls, values):
196254
)
197255
return values
198256

257+
@pd.field_validator("enclosed_entities", mode="after")
258+
@classmethod
259+
def _validate_enclosed_box_only_in_beta_mesher(cls, values):
260+
"""
261+
Check the name length for the cylinder entities due to the 32-character
262+
limitation of all data structure names and labels in CGNS format.
263+
The current prefix is 'rotatingBlock-' with 14 characters.
264+
"""
265+
validation_info = get_validation_info()
266+
if validation_info is None:
267+
return values
268+
if validation_info.is_beta_mesher:
269+
return values
270+
271+
for entity in values.stored_entities:
272+
if isinstance(entity, Box):
273+
raise ValueError(
274+
"`Box` entity in `RotationVolume.enclosed_entities` is only supported with the beta mesher."
275+
)
276+
277+
return values
278+
199279
@pd.field_validator("entities", mode="after")
200280
@classmethod
201281
def _validate_axisymmetric_only_in_beta_mesher(cls, values):

flow360/component/simulation/translator/volume_meshing_translator.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
AxisymmetricRefinementBase,
1313
RotationCylinder,
1414
RotationVolume,
15+
StructuredBoxRefinement,
1516
UniformRefinement,
1617
UserDefinedFarfield,
1718
)
@@ -31,6 +32,8 @@
3132
from flow360.component.simulation.utils import is_exact_instance
3233
from flow360.exceptions import Flow360TranslationError
3334

35+
# pylint: disable=too-many-locals
36+
3437

3538
def uniform_refinement_translator(obj: UniformRefinement):
3639
"""
@@ -51,6 +54,17 @@ def cylindrical_refinement_translator(obj: AxisymmetricRefinementBase):
5154
}
5255

5356

57+
def box_refinement_translator(obj: StructuredBoxRefinement):
58+
"""
59+
Translate StructuredBoxRefinement spacings
60+
"""
61+
return {
62+
"spacingAxis1": obj.spacing_axis1.value.item(),
63+
"spacingAxis2": obj.spacing_axis2.value.item(),
64+
"spacingNormal": obj.spacing_normal.value.item(),
65+
}
66+
67+
5468
def boundary_layer_translator(obj: BoundaryLayer):
5569
"""
5670
Translate BoundaryLayer.
@@ -100,6 +114,8 @@ def rotation_volume_translator(obj: RotationVolume, rotor_disk_names: list):
100114
setting["enclosedObjects"].append("slidingInterface-" + entity.name)
101115
elif is_exact_instance(entity, AxisymmetricBody):
102116
setting["enclosedObjects"].append("slidingInterface-" + entity.name)
117+
elif is_exact_instance(entity, Box):
118+
setting["enclosedObjects"].append("structuredBox-" + entity.name)
103119
elif is_exact_instance(entity, Surface):
104120
setting["enclosedObjects"].append(entity.name)
105121
return setting
@@ -126,6 +142,25 @@ def refinement_entity_injector(entity_obj):
126142
return {}
127143

128144

145+
def refinement_entity_box_with_axes_injector(entity_obj: Box):
146+
"""Injector for Box entity in StructuredBoxRefinement."""
147+
lengths = list(entity_obj.size.value)
148+
149+
axis1 = entity_obj.axes[0]
150+
axis2 = entity_obj.axes[1]
151+
152+
return {
153+
"name": entity_obj.name,
154+
"type": "box",
155+
"lengthAxis1": lengths[0],
156+
"lengthAxis2": lengths[1],
157+
"lengthNormal": lengths[2],
158+
"axis1": list(axis1),
159+
"axis2": list(axis2),
160+
"center": list(entity_obj.center.value),
161+
}
162+
163+
129164
def rotor_disks_entity_injector(entity: Cylinder):
130165
"""Injector for Cylinder entity in AxisymmetricRefinement."""
131166

@@ -284,6 +319,14 @@ def get_volume_meshing_json(input_params: SimulationParams, mesh_units):
284319
to_list=True,
285320
entity_injection_func=rotor_disks_entity_injector,
286321
)
322+
structured_box_refinement = translate_setting_and_apply_to_all_entities(
323+
meshing_params.refinements,
324+
StructuredBoxRefinement,
325+
box_refinement_translator,
326+
to_list=True,
327+
entity_injection_func=refinement_entity_box_with_axes_injector,
328+
)
329+
287330
if uniform_refinement_list:
288331
translated["refinement"] = []
289332
translated["refinement"].extend(uniform_refinement_list)
@@ -294,6 +337,10 @@ def get_volume_meshing_json(input_params: SimulationParams, mesh_units):
294337
translated["rotorDisks"].extend(rotor_disk_refinement)
295338
rotor_disk_names = [item["name"] for item in rotor_disk_refinement]
296339

340+
if structured_box_refinement:
341+
translated["structuredRegions"] = []
342+
translated["structuredRegions"].extend(structured_box_refinement)
343+
297344
faces_aniso_setting = translate_setting_and_apply_to_all_entities(
298345
meshing_params.refinements,
299346
BoundaryLayer,

0 commit comments

Comments
 (0)