Skip to content

Commit

Permalink
manual cherry-pick completed (#749)
Browse files Browse the repository at this point in the history
  • Loading branch information
benflexcompute authored Feb 20, 2025
1 parent c6f3563 commit d1d5e6a
Show file tree
Hide file tree
Showing 27 changed files with 1,550 additions and 69 deletions.
6 changes: 4 additions & 2 deletions flow360/component/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,9 +341,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 @@ -18,6 +18,7 @@
ProjectInterface,
VolumeMeshInterfaceV2,
)
from flow360.component.project_utils import replace_ghost_surfaces
from flow360.component.resource_base import Flow360Resource
from flow360.component.simulation.entity_info import GeometryEntityInfo
from flow360.component.simulation.outputs.output_entities import (
Expand Down Expand Up @@ -639,6 +640,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
63 changes: 63 additions & 0 deletions flow360/component/project_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""
Support class and functions for project interface.
"""

from flow360.component.simulation.framework.base_model import Flow360BaseModel
from flow360.component.simulation.framework.entity_base import EntityList
from flow360.component.simulation.primitives import GhostSurface
from flow360.component.simulation.simulation_params import SimulationParams
from flow360.exceptions import Flow360ConfigurationError


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
13 changes: 12 additions & 1 deletion flow360/component/simulation/framework/updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@

import re

from flow360.component.simulation.framework.updater_functions import (
fix_ghost_sphere_schema,
)

from ....exceptions import Flow360NotImplementedError, Flow360RuntimeError
from .entity_base import generate_uuid
from .updater_utils import compare_dicts
Expand Down Expand Up @@ -55,10 +59,17 @@ def _24_11_6_to_24_11_7_update(params_as_dict):
return params_as_dict


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


UPDATE_MAP = [
("24.11.([0-5])$", "24.11.6", _no_update),
("24.11.6", "24.11.7", _24_11_6_to_24_11_7_update),
("24.11.7", "24.11.*", _no_update),
("24.11.7", "24.11.9", _no_update),
("24.11.9", "24.11.10", _24_11_9_to_24_11_10_update),
("24.11.10", "24.11.*", _no_update),
]


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)
4 changes: 2 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 @@ -163,7 +163,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: 35 additions & 18 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 @@ -43,7 +44,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 @@ -261,21 +265,21 @@ 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 @@ -289,10 +293,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 @@ -402,20 +405,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 @@ -430,6 +442,12 @@ 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 @@ -439,8 +457,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
18 changes: 10 additions & 8 deletions flow360/component/simulation/outputs/outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@
SurfaceFieldNames,
VolumeFieldNames,
)
from flow360.component.simulation.primitives import GhostSurface, Surface
from flow360.component.simulation.primitives import (
GhostCircularPlane,
GhostSphere,
GhostSurface,
Surface,
)
from flow360.component.simulation.unit_system import LengthType


Expand Down Expand Up @@ -132,10 +137,9 @@ class SurfaceOutput(_AnimationAndFileFormatSettings):
# TODO: entities is None --> use all surfaces. This is not implemented yet.

name: Optional[str] = pd.Field("Surface output", description="Name of the `SurfaceOutput`.")
entities: EntityList[Surface, GhostSurface] = pd.Field(
entities: EntityList[Surface, GhostSurface, GhostCircularPlane, GhostSphere] = pd.Field(
alias="surfaces",
description="List of output :class:`~flow360.Surface`/"
+ ":class:`~flow360.GhostSurface` entities. ",
description="List of boundaries where output is generated.",
)
write_single_file: bool = pd.Field(
default=False,
Expand Down Expand Up @@ -392,11 +396,9 @@ class SurfaceIntegralOutput(Flow360BaseModel):
"""

name: str = pd.Field(description="Name of integral.")
entities: EntityList[Surface, GhostSurface] = pd.Field(
entities: EntityList[Surface, GhostSurface, GhostCircularPlane, GhostSphere] = pd.Field(
alias="surfaces",
description="List of :class:`~flow360.component.simulation.primitives.Surface`/"
+ ":class:`~flow360.component.simulation.primitives.GhostSurface` entities on which "
+ "the surface integral will be calculated.",
description="List of boundaries where the surface integral will be calculated.",
)
output_fields: UniqueItemList[str] = pd.Field(
description="List of output variables, only the :class:`UserDefinedField` is allowed."
Expand Down
Loading

0 comments on commit d1d5e6a

Please sign in to comment.