Skip to content

[SCFD-5659] force distribution grouping capabilities. #1340

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
75 changes: 74 additions & 1 deletion flow360/component/results/base_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import tempfile
import time
import uuid
from collections import defaultdict
from itertools import chain, product
from typing import Callable, Dict, List, Optional

Expand All @@ -19,8 +20,11 @@
CloudFileNotFoundError,
get_local_filename_and_create_folders,
)
from flow360.component.simulation.entity_info import GeometryEntityInfo
from flow360.component.simulation.models.surface_models import BoundaryBase
from flow360.component.simulation.simulation_params import SimulationParams
from flow360.component.v1.flow360_params import Flow360Params
from flow360.exceptions import Flow360ValueError
from flow360.log import log

# pylint: disable=consider-using-with
Expand Down Expand Up @@ -555,6 +559,15 @@ class PerEntityResultCSVModel(ResultCSVModel):
_x_columns: List[str] = []
_filter_when_zero = []
_entities: List[str] = None
_entity_groups: dict = pd.PrivateAttr()

@classmethod
# pylint: disable=arguments-differ
def from_dict(cls, data: dict, group: dict):
obj = super().from_dict(data)
# pylint: disable=protected-access
obj._entity_groups = group
return obj

@property
def values(self):
Expand All @@ -574,7 +587,7 @@ def values(self):
@property
def entities(self):
"""
Returns list of entities (boundary names) available for this result
Returns list of entity names available for this result
"""
if self._entities is None:
pattern = re.compile(rf"(.*)_({'|'.join(self._variables)})$")
Expand Down Expand Up @@ -638,3 +651,63 @@ def _filtered_sum(self):
regex_pattern = rf"^(?!total).*{variable}$"
df[new_col_name] = list(df.filter(regex=regex_pattern).sum(axis=1))
self.update(df)

def _create_forces_group(self, entity_groups: Dict[str, List[str]]) -> PerEntityResultCSVModel:
"""
Create new CSV model for the given entity groups.
"""
raw_values = {}
for x_column in self._x_columns:
raw_values[x_column] = np.array(self.raw_values[x_column])
for name, entities in entity_groups.items():
self.filter(include=entities)
for variable in self._variables:
if f"{name}_{variable}" not in raw_values:
raw_values[f"{name}_{variable}"] = np.array(self.values[f"total{variable}"])
continue
raw_values[f"{name}_{variable}"] += np.array(self.values[f"total{variable}"])

raw_values = {key: val.tolist() for key, val in raw_values.items()}
entity_groups = {key: sorted(val) for key, val in entity_groups.items()}

return self.from_dict(data=raw_values, group=entity_groups)

def by_boundary_condition(self, params: SimulationParams) -> PerEntityResultCSVModel:
"""
Group entities by boundary condition's name and create a
SurfaceForcesGroupResultCSVModel.
Forces from different boundaries but with the same type and name will be summed together.
"""

entity_groups = defaultdict(list)
for model in params.models:
if not isinstance(model, BoundaryBase):
continue
boundary_name = model.name if model.name is not None else model.type
entity_groups[boundary_name].extend(
[entity.name for entity in model.entities.stored_entities]
)
return self._create_forces_group(entity_groups=entity_groups)

def by_body_group(self, params: SimulationParams) -> PerEntityResultCSVModel:
"""
Group entities by body group's name and create a
SurfaceForcesGroupResultCSVModel
"""
if not isinstance(
params.private_attribute_asset_cache.project_entity_info, GeometryEntityInfo
):
raise Flow360ValueError(
"Group surface forces by body group is only supported for case starting from geometry."
)
entity_info = params.private_attribute_asset_cache.project_entity_info
if (
not hasattr(entity_info, "body_attribute_names")
or "groupByBodyId" not in entity_info.face_attribute_names
):
raise Flow360ValueError(
"The geometry in this case does not contain the necessary body group information, "
"please upgrade the project to the latest version and re-run the case."
)
entity_groups = entity_info.get_body_group_to_face_group_name_map()
return self._create_forces_group(entity_groups=entity_groups)
75 changes: 1 addition & 74 deletions flow360/component/results/case_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from __future__ import annotations

import re
from collections import defaultdict
from enum import Enum
from typing import Callable, Dict, List, Optional

Expand All @@ -20,8 +19,6 @@
ResultTarGZModel,
)
from flow360.component.simulation.conversion import unit_converter as unit_converter_v2
from flow360.component.simulation.entity_info import GeometryEntityInfo
from flow360.component.simulation.models.surface_models import BoundaryBase
from flow360.component.simulation.simulation_params import SimulationParams
from flow360.component.simulation.unit_system import (
Flow360UnitSystem,
Expand Down Expand Up @@ -221,82 +218,12 @@ def _preprocess(self, filter_physical_steps_only: bool = True, include_time: boo
def reload_data(self, filter_physical_steps_only: bool = True, include_time: bool = True):
return super().reload_data(filter_physical_steps_only, include_time)

def _create_surface_forces_group(
self, entity_groups: Dict[str, List[str]]
) -> SurfaceForcesGroupResultCSVModel:
"""
Create the SurfaceForcesGroupResultCSVModel for the given entity groups.
"""
raw_values = {}
for x_column in self._x_columns:
raw_values[x_column] = np.array(self.raw_values[x_column])
for name, entities in entity_groups.items():
self.filter(include=entities)
for variable in self._variables:
if f"{name}_{variable}" not in raw_values:
raw_values[f"{name}_{variable}"] = np.array(self.values[f"total{variable}"])
continue
raw_values[f"{name}_{variable}"] += np.array(self.values[f"total{variable}"])

raw_values = {key: val.tolist() for key, val in raw_values.items()}
entity_groups = {key: sorted(val) for key, val in entity_groups.items()}

return SurfaceForcesGroupResultCSVModel.from_dict(data=raw_values, group=entity_groups)

def by_boundary_condition(self, params: SimulationParams) -> SurfaceForcesGroupResultCSVModel:
"""
Group entities by boundary condition's name and create a
SurfaceForcesGroupResultCSVModel.
Forces from different boundaries but with the same type and name will be summed together.
"""

entity_groups = defaultdict(list)
for model in params.models:
if not isinstance(model, BoundaryBase):
continue
boundary_name = model.name if model.name is not None else model.type
entity_groups[boundary_name].extend(
[entity.name for entity in model.entities.stored_entities]
)
return self._create_surface_forces_group(entity_groups=entity_groups)

def by_body_group(self, params: SimulationParams) -> SurfaceForcesGroupResultCSVModel:
"""
Group entities by body group's name and create a
SurfaceForcesGroupResultCSVModel
"""
if not isinstance(
params.private_attribute_asset_cache.project_entity_info, GeometryEntityInfo
):
raise Flow360ValueError(
"Group surface forces by body group is only supported for case starting from geometry."
)
entity_info = params.private_attribute_asset_cache.project_entity_info
if (
not hasattr(entity_info, "body_attribute_names")
or "groupByBodyId" not in entity_info.face_attribute_names
):
raise Flow360ValueError(
"The geometry in this case does not contain the necessary body group information, "
"please upgrade the project to the latest version and re-run the case."
)
entity_groups = entity_info.get_body_group_to_face_group_name_map()
return self._create_surface_forces_group(entity_groups=entity_groups)


class SurfaceForcesGroupResultCSVModel(SurfaceForcesResultCSVModel):
"""SurfaceForcesGroupResultCSVModel"""

remote_file_name: str = pd.Field(None, frozen=True) # Unused dummy field
_entity_groups: dict = pd.PrivateAttr()

@classmethod
# pylint: disable=arguments-differ
def from_dict(cls, data: dict, group: dict):
obj = super().from_dict(data)
# pylint: disable=protected-access
obj._entity_groups = group
return obj



class LegacyForceDistributionResultCSVModel(ResultCSVModel):
Expand Down
Loading
Loading