Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 36 additions & 2 deletions flow360/component/simulation/meshing_param/volume_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,41 @@ class RotationCylinder(RotationVolume):
entities: EntityList[Cylinder] = pd.Field()


class AutomatedFarfield(Flow360BaseModel):
class _FarfieldBase(Flow360BaseModel):
"""Base class for farfield parameters."""

domain_type: Optional[Literal["half_body_positive_y", "half_body_negative_y", "full_body"]] = (
pd.Field( # In the future, we will support more flexible half model types and full model via Union.
None,
description="""
- half_body_positive_y: Trim to a half-model by slicing with the global Y=0 plane; keep the '+y' side for meshing and simulation.
- half_body_negative_y: Trim to a half-model by slicing with the global Y=0 plane; keep the '-y' side for meshing and simulation.
- full_body: Keep the full body for meshing and simulation without attempting to add symmetry planes.

Warning: When using AutomatedFarfield, setting `domain_type` overrides the 'auto' symmetry plane behavior.
""",
)
)

@pd.field_validator("domain_type", mode="after")
@classmethod
def _validate_only_in_beta_mesher(cls, value):
"""
Ensure that domain_type is only used with the beta mesher and GAI.
"""
validation_info = get_validation_info()
if validation_info is None:
return value
if not value or (
validation_info.use_geometry_AI is True and validation_info.is_beta_mesher is True
):
return value
raise ValueError(
"`domain_type` is only supported when using both GAI surface mesher and beta volume mesher."
)


class AutomatedFarfield(_FarfieldBase):
"""
Settings for automatic farfield volume zone generation.

Expand Down Expand Up @@ -412,7 +446,7 @@ def _validate_quasi_3d_periodic_only_in_legacy_mesher(cls, values):
return values


class UserDefinedFarfield(Flow360BaseModel):
class UserDefinedFarfield(_FarfieldBase):
"""
Setting for user defined farfield zone generation.
This means the "farfield" boundaries are coming from the supplied geometry file
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,17 @@ def _get_surface_refinements(refinement_list: list[dict]):
]


def _get_volume_zones(volume_zones_list: list[dict]):
"""
Get the volume zones from the input_params.
"""
return [
item
for item in volume_zones_list
if item["type"] in ("AutomatedFarfield", "UserDefinedFarfield")
]


GAI_SETTING_WHITELIST = {
"meshing": {
"defaults": {
Expand All @@ -159,6 +170,7 @@ def _get_surface_refinements(refinement_list: list[dict]):
"surface_max_adaptation_iterations": None,
},
"refinements": _get_surface_refinements,
"volume_zones": _get_volume_zones,
},
"private_attribute_asset_cache": {
"project_entity_info": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ def get_volume_meshing_json(input_params: SimulationParams, mesh_units):
for zone in input_params.meshing.volume_zones:
if isinstance(zone, UserDefinedFarfield):
translated["farfield"] = {"type": "user-defined"}
if zone.domain_type is not None:
translated["farfield"]["domainType"] = zone.domain_type
break

if isinstance(zone, AutomatedFarfield):
Expand All @@ -272,6 +274,9 @@ def get_volume_meshing_json(input_params: SimulationParams, mesh_units):
translated["farfield"]["periodic"] = {"type": "translational"}
else:
translated["farfield"]["type"] = zone.method

if zone.domain_type is not None:
translated["farfield"]["domainType"] = zone.domain_type
break

if "farfield" not in translated:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
non_beta_mesher_context = ParamsValidationInfo({}, [])
non_beta_mesher_context.is_beta_mesher = False

non_gai_context = ParamsValidationInfo({}, [])
non_gai_context.use_geometry_AI = False

beta_mesher_context = ParamsValidationInfo({}, [])
beta_mesher_context.is_beta_mesher = True

Expand Down Expand Up @@ -454,3 +457,21 @@ def test_quasi_3d_periodic_only_in_legacy_mesher():
# does not raise with legacy mesher on
with ValidationContext(VOLUME_MESH, non_beta_mesher_context):
my_farfield = AutomatedFarfield(method="quasi-3d-periodic")


def test_enforced_half_model_only_in_beta_mesher():
# raises when beta mesher is off
with pytest.raises(
pd.ValidationError,
match=r"`domain_type` is only supported when using both GAI surface mesher and beta volume mesher.",
):
with ValidationContext(VOLUME_MESH, non_beta_mesher_context):
AutomatedFarfield(domain_type="half_body_positive_y")

# raise when GAI is off
with pytest.raises(
pd.ValidationError,
match=r"`domain_type` is only supported when using both GAI surface mesher and beta volume mesher.",
):
with ValidationContext(VOLUME_MESH, non_gai_context):
AutomatedFarfield(domain_type="full_body")
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,15 @@
"units": "flow360_length_unit"
}
}
],
"volume_zones": [
{
"domain_type": "half_body_positive_y",
"type": "AutomatedFarfield",
"name": "Automated Farfield",
"method": "auto",
"relative_size": 50.0
}
]
},
"private_attribute_asset_cache": {
Expand Down Expand Up @@ -631,4 +640,4 @@
]
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ def test_gai_surface_mesher_refinements():
)
geometry["cube-holes.egads"].transformation = transformation
geometry["cylinder.stl"].transformation = transformation
farfield = AutomatedFarfield()
farfield = AutomatedFarfield(domain_type="half_body_positive_y")
params = SimulationParams(
meshing=MeshingParams(
defaults=MeshingDefaults(
Expand Down
4 changes: 2 additions & 2 deletions tests/simulation/translator/test_volume_meshing_translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ def get_test_param():
Surface(name="interface2"),
],
),
UserDefinedFarfield(),
UserDefinedFarfield(domain_type="half_body_negative_y"),
RotationVolume(
name="we_do_not_use_this_anyway",
entities=inner_cylinder,
Expand Down Expand Up @@ -248,7 +248,7 @@ def test_param_to_json(get_test_param, get_surface_mesh):

ref_dict = {
"refinementFactor": 1.45,
"farfield": {"type": "user-defined"},
"farfield": {"type": "user-defined", "domainType": "half_body_negative_y"},
"volume": {
"firstLayerThickness": 1.35e-06,
"growthRate": 1.04,
Expand Down
Loading