Skip to content

Commit

Permalink
Revert "Revert "[SCFD-3769] fix(): Python submited ghost surface not …
Browse files Browse the repository at this point in the history
…recogniz…" (#732)

This reverts commit 8f78b18.

Fixed the optaional setting and also add symeetric back
  • Loading branch information
benflexcompute committed Feb 19, 2025
1 parent 3e34f1d commit baae401
Show file tree
Hide file tree
Showing 29 changed files with 1,546 additions and 70 deletions.
6 changes: 4 additions & 2 deletions flow360/component/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,9 +336,11 @@ def _show_available_entity_groups(
for tag_index, attribute_tag in enumerate(attribute_names):
if ignored_attribute_tags is not None and attribute_tag in ignored_attribute_tags:
continue
log.info(f" >> Tag {tag_index}: {attribute_tag}. Grouping with this tag results in:")
log.info(
f" >> Tag '{tag_index}': {attribute_tag}. Grouping with this tag results in:"
)
for index, entity in enumerate(grouped_items[tag_index]):
log.info(f" >> Group {index}: {entity.name}")
log.info(f" >> Boundary {index}: {entity.name}")
if show_ids_in_each_group is True:
log.info(f" IDs: {entity.private_attribute_sub_components}")

Expand Down
4 changes: 4 additions & 0 deletions flow360/component/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
GeometryFiles,
SurfaceMeshFile,
VolumeMeshFile,
replace_ghost_surfaces,
show_projects_with_keyword_filter,
)
from flow360.component.resource_base import Flow360Resource
Expand Down Expand Up @@ -1027,6 +1028,9 @@ def _run(

with model_attribute_unlock(params.private_attribute_asset_cache, "project_entity_info"):
params.private_attribute_asset_cache.project_entity_info = entity_info
# Replace the ghost surfaces in the SimulationParams by the real ghost ones from asset metadata.
# This has to be done after `project_entity_info` is properly set.
entity_info = replace_ghost_surfaces(params)

draft.update_simulation_params(params)

Expand Down
59 changes: 59 additions & 0 deletions flow360/component/project_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,21 @@

from flow360.cloud.rest_api import RestApi
from flow360.component.interfaces import ProjectInterface
from flow360.component.simulation.framework.base_model import Flow360BaseModel
from flow360.component.simulation.framework.entity_base import EntityList
from flow360.component.simulation.framework.single_attribute_base import (
SingleAttributeModel,
)
from flow360.component.simulation.primitives import GhostSurface
from flow360.component.simulation.simulation_params import SimulationParams
from flow360.component.utils import (
SUPPORTED_GEOMETRY_FILE_PATTERNS,
MeshFileFormat,
MeshNameParser,
match_file_pattern,
parse_datetime,
)
from flow360.exceptions import Flow360ConfigurationError
from flow360.log import log


Expand Down Expand Up @@ -196,3 +201,57 @@ def show_projects_with_keyword_filter(search_keyword: str):
f"but only the latest {MAX_DISPLAYABLE_ITEM_COUNT} will be displayed. "
)
log.info("Total number of matching projects on the cloud: %d", resp["total"])


def replace_ghost_surfaces(params: SimulationParams):
"""
When the `SimulationParam` is constructed with python script on the Python side, the ghost boundaries
will be obtained by for example `automated_farfield.farfield` which returns :class:`GhostSurface`
not :class:`GhostSphere`. This will not be recognized by the webUI causing the assigned farfield being
removed by front end.
"""

def _replace_the_ghost_surface(*, ghost_surface, ghost_entities_from_metadata):
for item in ghost_entities_from_metadata:
if item.name == ghost_surface.name:
return item
raise Flow360ConfigurationError(
f"Unknown ghost surface with name `{ghost_surface.name}` found."
"Please double check the use of ghost surfaces."
)

def _find_ghost_surfaces(*, model, ghost_entities_from_metadata):
for field in model.__dict__.values():
if isinstance(field, GhostSurface):
# pylint: disable=protected-access
field = _replace_the_ghost_surface(
ghost_surface=field, ghost_entities_from_metadata=ghost_entities_from_metadata
)

if isinstance(field, EntityList):
if field.stored_entities:
for entity_index, _ in enumerate(field.stored_entities):
if isinstance(field.stored_entities[entity_index], GhostSurface):
field.stored_entities[entity_index] = _replace_the_ghost_surface(
ghost_surface=field.stored_entities[entity_index],
ghost_entities_from_metadata=ghost_entities_from_metadata,
)

elif isinstance(field, list):
for item in field:
if isinstance(item, Flow360BaseModel):
_find_ghost_surfaces(
model=item, ghost_entities_from_metadata=ghost_entities_from_metadata
)

elif isinstance(field, Flow360BaseModel):
_find_ghost_surfaces(
model=field, ghost_entities_from_metadata=ghost_entities_from_metadata
)

ghost_entities_from_metadata = (
params.private_attribute_asset_cache.project_entity_info.ghost_entities
)
_find_ghost_surfaces(model=params, ghost_entities_from_metadata=ghost_entities_from_metadata)

return params
8 changes: 4 additions & 4 deletions flow360/component/simulation/framework/param_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def boundaries(self):

def register_entity_list(model: Flow360BaseModel, registry: EntityRegistry) -> None:
"""
Registers entities used/occured in a Flow360BaseModel instance to an EntityRegistry.
Registers entities used/occurred in a Flow360BaseModel instance to an EntityRegistry.
This function iterates through the attributes of the given model. If an attribute is an
EntityList, it retrieves the expanded entities and registers each entity in the registry.
Expand Down Expand Up @@ -84,7 +84,7 @@ def _update_entity_full_name(
):
"""
Update Surface/Boundary with zone name from volume mesh metadata.
TODO: Maybe no need to recursivly looping the param and just manipulating the registry may suffice?
TODO: Maybe no need to recursively looping the param and just manipulating the registry may suffice?
"""
for field in model.__dict__.values():
if isinstance(field, target_entity_type):
Expand Down Expand Up @@ -122,13 +122,13 @@ def _update_zone_boundaries_with_metadata(
def _set_boundary_full_name_with_zone_name(
registry: EntityRegistry, naming_pattern: str, give_zone_name: str
) -> None:
"""Set the full name of surfaces that does not have full name spcified."""
"""Set the full name of surfaces that does not have full name specified."""
if registry.find_by_naming_pattern(naming_pattern):
for surface in registry.find_by_naming_pattern(naming_pattern):
if surface.private_attribute_full_name is not None:
# This indicates that full name has been set by mesh metadata because that and this are the
# only two places we set the full name.
# meshmeta data takes precedence as it is the most reliable source.
# mesh meta data takes precedence as it is the most reliable source.
# Note: Currently automated farfield assumes zone name to be "fluid" but the other mesher has "1".
# Note: We need to figure out how to handle this. Otherwise this may result in wrong
# Note: zone name getting prepended.
Expand Down
9 changes: 9 additions & 0 deletions flow360/component/simulation/framework/updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
from typing import Any

from flow360.component.simulation.framework.entity_base import generate_uuid
from flow360.component.simulation.framework.updater_functions import (
fix_ghost_sphere_schema,
)
from flow360.component.simulation.framework.updater_utils import (
Flow360Version,
compare_dicts,
Expand Down Expand Up @@ -137,10 +140,16 @@ def _to_25_2_0(params_as_dict):
return params_as_dict


def _to_25_2_1(params_as_dict):
fix_ghost_sphere_schema(params_as_dict=params_as_dict)
return params_as_dict


VERSION_MILESTONES = [
(Flow360Version("24.11.1"), _to_24_11_1),
(Flow360Version("24.11.7"), _to_24_11_7),
(Flow360Version("25.2.0"), _to_25_2_0),
(Flow360Version("25.2.1"), _to_25_2_1),
] # A list of the Python API version tuple with there corresponding updaters.


Expand Down
33 changes: 33 additions & 0 deletions flow360/component/simulation/framework/updater_functions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""Implementation of the updater functions. The updated.py should just import functions from here."""


def fix_ghost_sphere_schema(*, params_as_dict: dict):
"""
The previous ghost farfield has wrong schema (bug) and therefore needs data alternation.
"""

def i_am_outdated_ghost_sphere(*, data: dict):
"""Identify if the current dict is a outdated ghost sphere."""
if "type_name" in data.keys() and data["type_name"] == "GhostSphere":
return True
return False

def recursive_fix_ghost_surface(*, data):
if isinstance(data, dict):
# 1. Check if this is a ghost sphere instance
if i_am_outdated_ghost_sphere(data=data):
data.pop("type_name")
data["private_attribute_entity_type_name"] = "GhostSphere"

# 2. Otherwise, recurse into each item in the dictionary
for _, val in data.items():
recursive_fix_ghost_surface(
data=val,
)

elif isinstance(data, list):
# Recurse into each item in the list
for _, item in enumerate(data):
recursive_fix_ghost_surface(data=item)

recursive_fix_ghost_surface(data=params_as_dict)
6 changes: 4 additions & 2 deletions flow360/component/simulation/meshing_param/volume_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class UniformRefinement(Flow360BaseModel):


class CylindricalRefinementBase(Flow360BaseModel, metaclass=ABCMeta):
"""Base class for all refinements that requires spacing in axia, radial and circumferential directions."""
"""Base class for all refinements that requires spacing in axial, radial and circumferential directions."""

# pylint: disable=no-member
spacing_axial: LengthType.Positive = pd.Field(description="Spacing along the axial direction.")
Expand Down Expand Up @@ -145,11 +145,13 @@ class AutomatedFarfield(Flow360BaseModel):
@property
def farfield(self):
"""Returns the farfield boundary surface."""
# Make sure the naming is the same here and what the geometry/surface mesh pipeline generates.
return GhostSurface(name="farfield")

@property
def symmetry_planes(self):
"""Returns the symmetry plane boundary surface(s)."""
# Make sure the naming is the same here and what the geometry/surface mesh pipeline generates.
if self.method == "auto":
return GhostSurface(name="symmetric")
if self.method == "quasi-3d":
Expand All @@ -163,7 +165,7 @@ def symmetry_planes(self):
class UserDefinedFarfield(Flow360BaseModel):
"""
Setting for user defined farfield zone generation.
This means the "farfield" boundaires are comming from the supplied geometry file
This means the "farfield" boundaries are coming from the supplied geometry file
and meshing will take place inside this "geometry".
"""

Expand Down
53 changes: 36 additions & 17 deletions flow360/component/simulation/models/surface_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
)
from flow360.component.simulation.primitives import (
GhostCircularPlane,
GhostSphere,
GhostSurface,
Surface,
SurfacePair,
Expand All @@ -45,7 +46,10 @@ class BoundaryBase(Flow360BaseModel, metaclass=ABCMeta):
"""Boundary base"""

type: str = pd.Field()
entities: EntityList[Surface] = pd.Field(alias="surfaces")
entities: EntityList[Surface] = pd.Field(
alias="surfaces",
description="List of boundaries with boundary condition imposed.",
)


class BoundaryBaseWithTurbulenceQuantities(BoundaryBase, metaclass=ABCMeta):
Expand Down Expand Up @@ -332,18 +336,19 @@ class Freestream(BoundaryBaseWithTurbulenceQuantities):
Example
-------
- Define freestream boundary condition with velocity expression:
- Define freestream boundary condition with velocity expression and boundaries from the volume mesh:
>>> fl.Freestream(
... surfaces=[volume_mesh["blk-1/zblocks"],
... volume_mesh["blk-1/xblocks"]],
... surfaces=[volume_mesh["blk-1/freestream-part1"],
... volume_mesh["blk-1/freestream-part2"]],
... velocity = ["min(0.2, 0.2 + 0.2*y/0.5)", "0", "0.1*y/0.5"]
... )
- Define freestream boundary condition with turbulence quantities:
- Define freestream boundary condition with turbulence quantities and automated farfield:
>>> fl.Freestream(
... entities=[volume_mesh['freestream']],
>>> auto_farfield = fl.AutomatedFarfield()
... fl.Freestream(
... entities=[auto_farfield.farfield],
... turbulence_quantities= fl.TurbulenceQuantities(
... modified_viscosity_ratio=10,
... )
Expand All @@ -360,10 +365,9 @@ class Freestream(BoundaryBaseWithTurbulenceQuantities):
+ ":py:attr:`AerospaceCondition.alpha` and :py:attr:`AerospaceCondition.beta` angles. "
+ "Optionally, an expression for each of the velocity components can be specified.",
)
entities: EntityList[Surface, GhostSurface] = pd.Field(
entities: EntityList[Surface, GhostSurface, GhostSphere, GhostCircularPlane] = pd.Field(
alias="surfaces",
description="A list of :class:`Surface` entities with "
+ "the `Freestream` boundary condition imposed.",
description="List of boundaries with the `Freestream` boundary condition imposed.",
)


Expand Down Expand Up @@ -466,20 +470,29 @@ class SlipWall(BoundaryBase):
Example
-------
Define :code:`SlipWall` boundary condition for entities with the naming pattern
- Define :code:`SlipWall` boundary condition for entities with the naming pattern
:code:`"*/slipWall"` in the volume mesh.
>>> fl.SlipWall(entities=volume_mesh["*/slipWall"]
>>> fl.SlipWall(entities=volume_mesh["*/slipWall"]
- Define :code:`SlipWall` boundary condition with automated farfield symmetry plane boundaries:
>>> auto_farfield = fl.AutomatedFarfield()
>>> fl.SlipWall(
... entities=[auto_farfield.symmetry_planes],
... turbulence_quantities= fl.TurbulenceQuantities(
... modified_viscosity_ratio=10,
... )
... )
====
"""

name: Optional[str] = pd.Field(None, description="Name of the `SlipWall` boundary condition.")
type: Literal["SlipWall"] = pd.Field("SlipWall", frozen=True)
entities: EntityList[Surface, GhostSurface] = pd.Field(
entities: EntityList[Surface, GhostSurface, GhostCircularPlane] = pd.Field(
alias="surfaces",
description="A list of :class:`Surface` entities with "
+ "the `SlipWall` boundary condition imposed.",
description="List of boundaries with the :code:`SlipWall` boundary condition imposed.",
)


Expand All @@ -494,6 +507,13 @@ class SymmetryPlane(BoundaryBase):
>>> fl.SymmetryPlane(entities=volume_mesh["fluid/symmetry"])
- Define `SymmetryPlane` boundary condition with automated farfield symmetry plane boundaries:
>>> auto_farfield = fl.AutomatedFarfield()
>>> fl.SymmetryPlane(
... entities=[auto_farfield.symmetry_planes],
... )
====
"""

Expand All @@ -503,8 +523,7 @@ class SymmetryPlane(BoundaryBase):
type: Literal["SymmetryPlane"] = pd.Field("SymmetryPlane", frozen=True)
entities: EntityList[Surface, GhostSurface, GhostCircularPlane] = pd.Field(
alias="surfaces",
description="A list of :class:`Surface` entities with "
+ "the `SymmetryPlane` boundary condition imposed.",
description="List of boundaries with the `SymmetryPlane` boundary condition imposed.",
)


Expand Down
Loading

0 comments on commit baae401

Please # to comment.