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
2 changes: 1 addition & 1 deletion flow360/component/simulation/meshing_param/face_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from flow360.component.simulation.validation.validation_context import (
get_validation_info,
)
from flow360.component.simulation.validation_utils import (
from flow360.component.simulation.validation.validation_utils import (
check_deleted_surface_in_entity_list,
)

Expand Down
76 changes: 46 additions & 30 deletions flow360/component/simulation/meshing_param/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
UniformRefinement,
UserDefinedFarfield,
)
from flow360.component.simulation.primitives import Cylinder
from flow360.component.simulation.unit_system import AngleType, LengthType
from flow360.component.simulation.validation.validation_context import (
SURFACE_MESH,
Expand All @@ -29,6 +28,7 @@
ContextField,
get_validation_info,
)
from flow360.component.simulation.validation.validation_utils import EntityUsageMap

RefinementTypes = Annotated[
Union[
Expand Down Expand Up @@ -221,48 +221,64 @@ def _check_volume_zones_has_farfied(cls, v):
return v

@pd.model_validator(mode="after")
def _check_no_reused_cylinder(self) -> Self:
def _check_no_reused_volume_entities(self) -> Self:
"""
Check that the RoatatoinCylinder, AxisymmetricRefinement, and UniformRefinement
do not share the same cylinder.
Meshing entities reuse check.
+------------------------+------------------------+------------------------+------------------------+
| | RotationCylinder | AxisymmetricRefinement | UniformRefinement |
+------------------------+------------------------+------------------------+------------------------+
| RotationCylinder | NO | -- | -- |
+------------------------+------------------------+------------------------+------------------------+
| AxisymmetricRefinement | NO | NO | -- |
+------------------------+------------------------+------------------------+------------------------+
| UniformRefinement | YES | NO | NO |
+------------------------+------------------------+------------------------+------------------------+

"""

class CylinderUsageMap(dict):
"""A customized dict to store the cylinder name and its usage."""

def __setitem__(self, key, value):
if key in self:
if self[key] != value:
raise ValueError(
f"The same cylinder named `{key}` is used in both {self[key]} and {value}."
)
raise ValueError(
f"The cylinder named `{key}` is used multiple times in {value}."
)
super().__setitem__(key, value)
usage = EntityUsageMap()

cylinder_name_to_usage_map = CylinderUsageMap()
for volume_zone in self.volume_zones if self.volume_zones is not None else []:
if isinstance(volume_zone, RotationCylinder):
# pylint: disable=protected-access
for cylinder in [
item
_ = [
usage.add_entity_usage(item, volume_zone.type)
for item in volume_zone.entities._get_expanded_entities(create_hard_copy=False)
if isinstance(item, Cylinder)
]:
cylinder_name_to_usage_map[cylinder.name] = RotationCylinder.model_fields[
"type"
].default
]

for refinement in self.refinements if self.refinements is not None else []:
if isinstance(refinement, (UniformRefinement, AxisymmetricRefinement)):
# pylint: disable=protected-access
for cylinder in [
item
_ = [
usage.add_entity_usage(item, refinement.refinement_type)
for item in refinement.entities._get_expanded_entities(create_hard_copy=False)
if isinstance(item, Cylinder)
]:
cylinder_name_to_usage_map[cylinder.name] = refinement.refinement_type
]

error_msg = ""
for entity_type, entity_model_map in usage.dict_entity.items():
for entity_info in entity_model_map.values():
if len(entity_info["model_list"]) == 1 or sorted(
entity_info["model_list"]
) == sorted(["RotationCylinder", "UniformRefinement"]):
# RotationCylinder and UniformRefinement are allowed to be used together
continue

model_set = set(entity_info["model_list"])
if len(model_set) == 1:
error_msg += (
f"{entity_type} entity `{entity_info['entity_name']}` "
+ f"is used multiple times in `{model_set.pop()}`."
)
else:
model_string = ", ".join(f"`{x}`" for x in sorted(model_set))
error_msg += (
f"Using {entity_type} entity `{entity_info['entity_name']}` "
+ f"in {model_string} at the same time is not allowed."
)

if error_msg:
raise ValueError(error_msg)

return self

@property
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
Surface,
)
from flow360.component.simulation.unit_system import LengthType
from flow360.component.simulation.validation_utils import (
from flow360.component.simulation.validation.validation_utils import (
check_deleted_surface_in_entity_list,
)

Expand Down
2 changes: 1 addition & 1 deletion flow360/component/simulation/models/surface_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
from flow360.component.simulation.validation.validation_context import (
get_validation_info,
)
from flow360.component.simulation.validation_utils import (
from flow360.component.simulation.validation.validation_utils import (
check_deleted_surface_in_entity_list,
check_deleted_surface_pair,
)
Expand Down
2 changes: 1 addition & 1 deletion flow360/component/simulation/models/volume_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
from flow360.component.simulation.validation.validation_context import (
get_validation_info,
)
from flow360.component.simulation.validation_utils import (
from flow360.component.simulation.validation.validation_utils import (
_validator_append_instance_name,
)

Expand Down
2 changes: 1 addition & 1 deletion flow360/component/simulation/outputs/outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
get_validation_info,
get_validation_levels,
)
from flow360.component.simulation.validation_utils import (
from flow360.component.simulation.validation.validation_utils import (
check_deleted_surface_in_entity_list,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from flow360.component.simulation.validation.validation_context import (
get_validation_info,
)
from flow360.component.simulation.validation_utils import (
from flow360.component.simulation.validation.validation_utils import (
check_deleted_surface_in_entity_list,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

from typing import get_args

from flow360.component.simulation.entity_info import DraftEntityTypes
from flow360.component.simulation.models.solver_numerics import NoneSolver
from flow360.component.simulation.models.surface_models import (
Inflow,
Expand All @@ -21,17 +20,14 @@
TimeAverageOutputTypes,
VolumeOutput,
)
from flow360.component.simulation.primitives import (
_SurfaceEntityBase,
_VolumeEntityBase,
)
from flow360.component.simulation.time_stepping.time_stepping import Steady, Unsteady
from flow360.component.simulation.validation.validation_context import (
ALL,
CASE,
get_validation_info,
get_validation_levels,
)
from flow360.component.simulation.validation.validation_utils import EntityUsageMap


def _check_consistency_wall_function_and_surface_output(v):
Expand Down Expand Up @@ -63,56 +59,32 @@ def _check_consistency_wall_function_and_surface_output(v):


def _check_duplicate_entities_in_models(params):
models = params.models
if not params.models:
return params

dict_entity = {"Surface": {}, "Volume": {}}

def _get_entity_key(entity):
draft_entity_types = get_args(get_args(DraftEntityTypes)[0])
if isinstance(entity, draft_entity_types):
return entity.private_attribute_id
return entity.name

def register_single_entity(entity, model_type, dict_entity):
entity_type = None
if isinstance(entity, _SurfaceEntityBase):
entity_type = "Surface"
elif isinstance(entity, _VolumeEntityBase):
entity_type = "Volume"
else:
raise ValueError(
f"[Internal Error] Entity `{entity.name}` in the {model_type} model "
f"cannot be registered as a valid Surface or Volume entity."
)
entity_key = _get_entity_key(entity=entity)
entity_log = dict_entity[entity_type].get(
entity_key, {"entity_name": entity.name, "model_list": []}
)
entity_log["model_list"].append(model_type)
dict_entity[entity_type][entity_key] = entity_log
return dict_entity
models = params.models
usage = EntityUsageMap()

if models:
for model in models:
if hasattr(model, "entities"):
# pylint: disable = protected-access
expanded_entities = model.entities._get_expanded_entities(create_hard_copy=False)
for entity in expanded_entities:
dict_entity = register_single_entity(entity, model.type, dict_entity)
for model in models:
if hasattr(model, "entities"):
# pylint: disable = protected-access
expanded_entities = model.entities._get_expanded_entities(create_hard_copy=False)
for entity in expanded_entities:
usage.add_entity_usage(entity, model.type)

error_msg = ""
for entity_type, entity_model_map in dict_entity.items():
for entity_type, entity_model_map in usage.dict_entity.items():
for entity_info in entity_model_map.values():
if len(entity_info["model_list"]) > 1:
model_set = set(entity_info["model_list"])
model_string = ", ".join(f"`{x}`" for x in sorted(model_set))
model_string += " models.\n" if len(model_set) > 1 else " model.\n"
error_msg += (
f"{entity_type} entity `{entity_info['entity_name']}` appears "
f"multiple times in {model_string}"
f"{entity_type} entity `{entity_info['entity_name']}` "
+ f"appears multiple times in {model_string}"
)

if error_msg != "":
if error_msg:
raise ValueError(error_msg)

return params
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@
"""

from functools import wraps
from typing import get_args

from flow360.component.simulation.primitives import Surface
from flow360.component.simulation.entity_info import DraftEntityTypes
from flow360.component.simulation.primitives import (
Surface,
_SurfaceEntityBase,
_VolumeEntityBase,
)
from flow360.component.simulation.validation.validation_context import (
get_validation_info,
)
Expand Down Expand Up @@ -105,3 +111,44 @@ def check_deleted_surface_pair(value):
)

return value


class EntityUsageMap: # pylint:disable=too-few-public-methods
"""
A customized dict to store the entity name and its usage.
{"$EntityID": [$UsedInWhatModel]}
"""

def __init__(self):
self.dict_entity = {"Surface": {}, "Volume": {}}

@classmethod
def _get_entity_key(cls, entity) -> str:
"""
Get unique identifier for the entity.
"""
draft_entity_types = get_args(get_args(DraftEntityTypes)[0])
if isinstance(entity, draft_entity_types):
return entity.private_attribute_id
return entity.name

def add_entity_usage(self, entity, model_type):
"""
Add the entity usage to the dictionary.
"""
entity_type = None
if isinstance(entity, _SurfaceEntityBase):
entity_type = "Surface"
elif isinstance(entity, _VolumeEntityBase):
entity_type = "Volume"
else:
raise ValueError(
f"[Internal Error] Entity `{entity.name}` in the {model_type} model "
f"cannot be registered as a valid Surface or Volume entity."
)
entity_key = self._get_entity_key(entity=entity)
entity_log = self.dict_entity[entity_type].get(
entity_key, {"entity_name": entity.name, "model_list": []}
)
entity_log["model_list"].append(model_type)
self.dict_entity[entity_type][entity_key] = entity_log
Loading