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
6 changes: 4 additions & 2 deletions flow360/component/simulation/meshing_param/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,10 +396,12 @@ def _check_no_reused_volume_entities(self) -> Self:
return self

@property
def automated_farfield_method(self):
"""Returns the automated farfield method used."""
def farfield_method(self):
"""Returns the farfield method used."""
if self.volume_zones:
for zone in self.volume_zones: # pylint: disable=not-an-iterable
if isinstance(zone, AutomatedFarfield):
return zone.method
if isinstance(zone, UserDefinedFarfield):
return "user-defined"
return None
9 changes: 9 additions & 0 deletions flow360/component/simulation/meshing_param/volume_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,3 +462,12 @@ class UserDefinedFarfield(_FarfieldBase):

type: Literal["UserDefinedFarfield"] = pd.Field("UserDefinedFarfield", frozen=True)
name: Optional[str] = pd.Field(None)

@property
def symmetry_plane(self) -> GhostSurface:
"""
Returns the symmetry plane boundary surface.

Warning: This should only be used when using GAI and beta mesher.
"""
return GhostSurface(name="symmetric")
9 changes: 8 additions & 1 deletion flow360/component/simulation/models/solver_numerics.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,14 @@ class NavierStokesSolver(GenericSolverSettings):
limit_velocity: bool = pd.Field(False, description="Limiter for velocity")
limit_pressure_density: bool = pd.Field(False, description="Limiter for pressure and density.")

type_name: Literal["Compressible"] = pd.Field("Compressible", frozen=True)
type_name: Literal["Compressible", "CompressibleIsentropic"] = pd.Field(
"Compressible",
description="The type of Navier-Stokes equations to solve. "
+ "The default is the compressible conservation laws. "
+ "CompressibleIsentropic is recommended for low mach number applications to speed up the solver. "
+ "It will apply mass and momentum conservation along with the isentropic assumption for low-speed flow."
+ "CompressibleIsentropic is used for LiquidOperatingCondition.",
)

low_mach_preconditioner: bool = pd.Field(
False, description="Use preconditioning for accelerating low Mach number flows."
Expand Down
2 changes: 2 additions & 0 deletions flow360/component/simulation/models/surface_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
check_deleted_surface_in_entity_list,
check_deleted_surface_pair,
check_symmetric_boundary_existence,
check_user_defined_farfield_symmetry_existence,
)

# pylint: disable=fixme
Expand All @@ -59,6 +60,7 @@ class EntityListAllowingGhost(EntityList):
@classmethod
def ghost_entity_validator(cls, value):
"""Run all validators"""
check_user_defined_farfield_symmetry_existence(value)
return check_symmetric_boundary_existence(value)


Expand Down
2 changes: 1 addition & 1 deletion flow360/component/simulation/primitives.py
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,7 @@ def _get_existence_dependency(self, validation_info):
return y_min, y_max, tolerance, largest_dimension

def exists(self, validation_info) -> bool:
"""Mesher logic for symmetric plane existence."""
"""For automated farfield, check mesher logic for symmetric plane existence."""

if self.name != "symmetric":
# Quasi-3D mode, no need to check existence.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,12 +323,12 @@ def _check_complete_boundary_condition_and_unknown_surface(
if not validation_info:
return params

asset_boundary_entities = params.private_attribute_asset_cache.boundaries
asset_boundary_entities = params.private_attribute_asset_cache.boundaries # Persistent ones

# Filter out the ones that will be deleted by mesher
automated_farfield_method = params.meshing.automated_farfield_method if params.meshing else None
farfield_method = params.meshing.farfield_method if params.meshing else None

if automated_farfield_method:
if farfield_method:
if validation_info.at_least_one_body_transformed:
# If transformed then `_will_be_deleted_by_mesher()` will no longer be accurate
# since we do not know the final bounding box for each surface and global model.
Expand All @@ -342,26 +342,33 @@ def _check_complete_boundary_condition_and_unknown_surface(
for item in asset_boundary_entities
if item._will_be_deleted_by_mesher(
at_least_one_body_transformed=validation_info.at_least_one_body_transformed,
farfield_method=automated_farfield_method,
farfield_method=farfield_method,
global_bounding_box=validation_info.global_bounding_box,
planar_face_tolerance=validation_info.planar_face_tolerance,
half_model_symmetry_plane_center_y=validation_info.half_model_symmetry_plane_center_y,
quasi_3d_symmetry_planes_center_y=validation_info.quasi_3d_symmetry_planes_center_y,
)
is False
]
if automated_farfield_method == "auto":
if farfield_method == "auto":
asset_boundary_entities += [
item
for item in params.private_attribute_asset_cache.project_entity_info.ghost_entities
if item.name not in ("symmetric-1", "symmetric-2") and item.exists(validation_info)
]
elif automated_farfield_method == "quasi-3d":
elif farfield_method == "quasi-3d":
asset_boundary_entities += [
item
for item in params.private_attribute_asset_cache.project_entity_info.ghost_entities
if item.name != "symmetric"
]
elif farfield_method == "user-defined":
if validation_info.use_geometry_AI and validation_info.is_beta_mesher:
asset_boundary_entities += [
item
for item in params.private_attribute_asset_cache.project_entity_info.ghost_entities
if item.name == "symmetric"
]

potential_zone_zone_interfaces = set()
if validation_info.farfield_method == "user-defined":
Expand Down
32 changes: 31 additions & 1 deletion flow360/component/simulation/validation/validation_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,38 @@ def check_deleted_surface_pair(value):
return value


def check_user_defined_farfield_symmetry_existence(stored_entities):
"""
Ensure that when:
1. UserDefinedFarfield is used
2. GhostSurface(name="symmetric") is assigned

That:
1. GAI and beta mesher is used.
"""
validation_info = get_validation_info()

if validation_info is None:
return stored_entities

if validation_info.farfield_method != "user-defined":
return stored_entities

for item in stored_entities:
if (
item.private_attribute_entity_type_name != "GhostCircularPlane"
or item.name != "symmetric"
):
continue
if not validation_info.use_geometry_AI or not validation_info.is_beta_mesher:
raise ValueError(
"Symmetry plane of user defined farfield will only be generated when both GAI and beta mesher are used."
)
return stored_entities


def check_symmetric_boundary_existence(stored_entities):
"""Check according to the criteria if the symmetric plane exists."""
"""For automated farfield, check according to the criteria if the symmetric plane exists."""
validation_info = get_validation_info()

if validation_info is None:
Expand Down
106 changes: 72 additions & 34 deletions tests/simulation/params/test_automated_farfield.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
MeshingDefaults,
MeshingParams,
)
from flow360.component.simulation.meshing_param.volume_params import AutomatedFarfield
from flow360.component.simulation.meshing_param.volume_params import (
AutomatedFarfield,
UserDefinedFarfield,
)
from flow360.component.simulation.models.surface_models import (
Freestream,
SlipWall,
Expand All @@ -39,6 +42,39 @@ def change_test_dir(request, monkeypatch):
monkeypatch.chdir(request.fspath.dirname)


@pytest.fixture()
def surface_mesh():
sm = SurfaceMeshV2.from_local_storage(
local_storage_path="data/surface_mesh",
meta_data=SurfaceMeshMetaV2(
**local_metadata_builder(
id="aaa",
name="aaa",
cloud_path_prefix="aaa",
)
),
)
return sm


def _run_validation(params, surface_mesh_obj, use_beta_mesher=True, use_geometry_AI=False):
params = set_up_params_for_uploading(
params=params,
root_asset=surface_mesh_obj,
length_unit=1 * u.m,
use_beta_mesher=use_beta_mesher,
use_geometry_AI=use_geometry_AI,
)

_, errors, _ = services.validate_model(
params_as_dict=params.model_dump(exclude_none=True),
validated_by=services.ValidationCalledBy.LOCAL,
root_item_type="SurfaceMesh",
validation_level="All",
)
return errors


def test_automated_farfield_surface_usage():
# Test use of GhostSurface in meshing
with pytest.raises(
Expand Down Expand Up @@ -144,35 +180,8 @@ def test_automated_farfield_import_export():
assert isinstance(meshing.volume_zones[0], AutomatedFarfield)


def test_symmetric_existence():
def _run_validation(params):
params = set_up_params_for_uploading(
params=params,
root_asset=sm,
length_unit=1 * u.m,
use_beta_mesher=True,
use_geometry_AI=False,
)
def test_symmetric_existence(surface_mesh):

_, errors, _ = services.validate_model(
params_as_dict=params.model_dump(exclude_none=True),
validated_by=services.ValidationCalledBy.LOCAL,
root_item_type="SurfaceMesh",
validation_level="All",
)

return errors

sm = SurfaceMeshV2.from_local_storage(
local_storage_path="data/surface_mesh",
meta_data=SurfaceMeshMetaV2(
**local_metadata_builder(
id="aaa",
name="aaa",
cloud_path_prefix="aaa",
)
),
)
farfield = AutomatedFarfield()
with SI_unit_system:
params = SimulationParams(
Expand All @@ -185,25 +194,25 @@ def _run_validation(params):
volume_zones=[farfield],
),
models=[
Wall(surfaces=sm["*"]),
Wall(surfaces=surface_mesh["*"]),
Freestream(surfaces=[farfield.farfield]),
],
)

# Valid Symmetric but did not use it
errors = _run_validation(params)
errors = _run_validation(params, surface_mesh)
assert len(errors) == 1
assert (
"The following boundaries do not have a boundary condition: symmetric." in errors[0]["msg"]
)

params.models.append(SymmetryPlane(surfaces=[farfield.symmetry_planes]))
errors = _run_validation(params)
errors = _run_validation(params, surface_mesh)
assert errors is None

# Invalid Symmetric
params.meshing.defaults.planar_face_tolerance = 1e-100
errors = _run_validation(params)
errors = _run_validation(params, surface_mesh)
assert len(errors) == 1
assert (
"`symmetric` boundary will not be generated: model spans: [-4.1e-05, 1.2e+03], tolerance = 1e-100 x 2.5e+03 = 2.5e-97."
Expand All @@ -212,7 +221,36 @@ def _run_validation(params):

# Invalid Symmetric but did not use it
params.models.pop()
errors = _run_validation(params)
errors = _run_validation(params, surface_mesh)
assert errors is None


def test_user_defined_farfield_symmetry_plane(surface_mesh):
farfield = UserDefinedFarfield()

with SI_unit_system:
params = SimulationParams(
operating_condition=AerospaceCondition(velocity_magnitude=1),
meshing=MeshingParams(
defaults=MeshingDefaults(
boundary_layer_first_layer_thickness=0.001,
boundary_layer_growth_rate=1.1,
),
volume_zones=[farfield],
),
models=[
Wall(surfaces=surface_mesh["*"]),
SymmetryPlane(surfaces=farfield.symmetry_plane),
],
)
errors = _run_validation(params, surface_mesh, use_beta_mesher=True, use_geometry_AI=False)
assert errors[0]["loc"] == ("models", 1, "entities", "stored_entities")
assert (
errors[0]["msg"]
== "Value error, Symmetry plane of user defined farfield will only be generated when both GAI and beta mesher are used."
)
params.meshing.defaults.geometry_accuracy = 1 * u.mm
errors = _run_validation(params, surface_mesh, use_beta_mesher=True, use_geometry_AI=True)
assert errors is None


Expand Down
4 changes: 2 additions & 2 deletions tests/simulation/translator/ref/Flow360_porous_media_box.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
},
"lowMachPreconditioner": false,
"maxForceJacUpdatePhysicalSteps": 0,
"modelType": "Compressible",
"modelType": "CompressibleIsentropic",
"numericalDissipationFactor": 1.0,
"orderOfAccuracy": 2,
"relativeTolerance": 0.0,
Expand Down Expand Up @@ -201,4 +201,4 @@
"shouldCheckStopCriterion": false,
"externalProcessMonitorOutput": false
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
},
"lowMachPreconditioner": false,
"maxForceJacUpdatePhysicalSteps": 0,
"modelType": "Compressible",
"modelType": "CompressibleIsentropic",
"numericalDissipationFactor": 1.0,
"orderOfAccuracy": 2,
"relativeTolerance": 0.0,
Expand Down Expand Up @@ -192,4 +192,4 @@
"shouldCheckStopCriterion": false,
"externalProcessMonitorOutput": false
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def _create_porous_media_param(slip_wall_list, inflow, outflow, porous_zone):
models=[
Fluid(
navier_stokes_solver=NavierStokesSolver(
type_name="CompressibleIsentropic",
absolute_tolerance=1e-10,
kappa_MUSCL=0.01,
linear_solver=LinearSolver(max_iterations=25),
Expand Down
Loading