Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
074e5a6
general snappy framework, does not work
piotrkluba Jul 9, 2025
a95f5e7
basic translator functionality
piotrkluba Jul 10, 2025
712b515
tests repaired
piotrkluba Jul 10, 2025
5717d7a
reversed unnecessary changes
piotrkluba Jul 10, 2025
2465712
reversed unnecessary changes again
piotrkluba Jul 10, 2025
be7d991
reversed unnecessary changes yet again
piotrkluba Jul 10, 2025
c6e41e0
points in mesh added
piotrkluba Jul 10, 2025
9893ea2
added strictRegionSnap
piotrkluba Jul 11, 2025
bf593c9
implemented initial validators
piotrkluba Jul 11, 2025
757ed1f
corrected errors with validators and included angle
piotrkluba Jul 11, 2025
4499e0f
always include strictFeatureSnap
piotrkluba Jul 11, 2025
645adbf
pulled snappy clases to main namespace
piotrkluba Jul 11, 2025
5ecbaed
repaired handlin volume meshing params and empty edge lists
piotrkluba Jul 11, 2025
8c44b58
fixed volume meshing in modular workflow
piotrkluba Jul 14, 2025
253e538
fixed Self import error
piotrkluba Jul 14, 2025
7687b49
added a test
piotrkluba Jul 15, 2025
b531409
fixes to translator
piotrkluba Jul 15, 2025
bb6d9ae
fixed empty regions and bodies n edge refinement
piotrkluba Jul 15, 2025
1e5d3bf
additional fixe with more tests coverage
piotrkluba Jul 15, 2025
3f44994
draft change
piotrkluba Jul 16, 2025
e4a8953
fixed turning off edges at smoothing
piotrkluba Jul 16, 2025
3c1af03
added validators to quality metrics
piotrkluba Jul 16, 2025
b8db661
added more validators
piotrkluba Jul 17, 2025
9600a3b
Use original edges for smoothing - snappy (#1304)
piotrkluba Jul 29, 2025
89673f2
Added UniformRefinement to snappy (#1331)
piotrkluba Aug 6, 2025
f75697b
Merge rc/25.6 to snappy main branch (#1347)
piotrkluba Aug 13, 2025
4bc6ee1
bumped version for beta publish
maciej-flexcompute Aug 18, 2025
54fa8bc
Squashed commit of the following:
piotrkluba Aug 28, 2025
ebc9e18
merge addition
piotrkluba Aug 28, 2025
b3b6911
merging kinks repaired
piotrkluba Aug 28, 2025
a09705a
Revert "Ensure the geometry tolerance and the planar tolerance are co…
benflexcompute Aug 27, 2025
ddca991
Skip boundaries during translation if the boundary is not found in vo…
benflexcompute Aug 28, 2025
100e1bb
validation context extracts also from ModularMeshingWorkflow
piotrkluba Aug 29, 2025
b3d86ef
validation of volume zones corrected for different meshing shemes
piotrkluba Sep 1, 2025
34ecc84
bump version to 25.8.0b2, formatter changes
piotrkluba Sep 2, 2025
a159f21
fixed planar_face_tolerance context bug
piotrkluba Sep 9, 2025
c375ce8
Python meshing refactor (#1415)
piotrkluba Sep 25, 2025
4ea8698
user defined farfield no longer required when using custom volume or …
piotrkluba Sep 26, 2025
81cf82a
Rebased snappy to main (#1449)
piotrkluba Sep 26, 2025
7eca1e1
revert fix of translating missing boundaries as it was done in main
piotrkluba Sep 29, 2025
7aa255d
Merge branch 'develop' into snappy-main
piotrkluba Oct 1, 2025
abe9e77
formatter
piotrkluba Oct 1, 2025
b53f2dd
unnecessary warnings removed
piotrkluba Oct 1, 2025
e87dffd
added SeedpointZone to rotation
piotrkluba Oct 3, 2025
2f6b5e0
formatter
piotrkluba Oct 3, 2025
957a663
Renaming on _get_boundary_full_name
benflexcompute Oct 3, 2025
517624d
Some renaming
benflexcompute Oct 6, 2025
c87cfd6
Merge pull request #1469 from flexcompute/main
benflexcompute Oct 8, 2025
28f623b
Merge pull request #1473 from flexcompute/main
angranl-flex Oct 8, 2025
f32de54
changed type to type_name as a discriminator in added types
piotrkluba Oct 8, 2025
6302aee
Merge branch 'snappy-main' of github.com:flexcompute/Flow360 into sna…
piotrkluba Oct 8, 2025
85c576d
farfield cosmetic changes
piotrkluba Oct 8, 2025
085c865
Merge pull request #1479 from flexcompute/main
angranl-flex Oct 8, 2025
da831f8
Merge pull request #1481 from flexcompute/main
benflexcompute Oct 8, 2025
cf9a46f
revertet some weird change to base model
piotrkluba Oct 9, 2025
0c8c532
Rebase to newest develop (#1485)
piotrkluba Oct 9, 2025
735cf9f
Added the base_spacing parameter to control snappy octree (#1495)
piotrkluba Oct 14, 2025
de73fbe
exposed octree spacing
piotrkluba Oct 14, 2025
9976667
inverted the spacing convention
piotrkluba Oct 14, 2025
9ae168c
changed warning formatting
piotrkluba Oct 14, 2025
9369ca0
add condition for SnappySurfaceEdgeRefinement validation
piotrkluba Oct 14, 2025
587165c
removed the optional annotation from boundary first layer thickness
piotrkluba Oct 14, 2025
6dfde38
Fix spacings not being translated in list (#1500)
piotrkluba Oct 16, 2025
d3ae923
Two level structure for geometry referencing snappy (#1465)
piotrkluba Oct 16, 2025
2af7eaf
removed unnecessary printing
piotrkluba Oct 24, 2025
4c17b19
Snappy code refactor (#1540)
piotrkluba Nov 4, 2025
50e9563
bumped version to 25.8.0b4
piotrkluba Nov 4, 2025
6698bcd
Merge branch 'develop' into snappy-main
piotrkluba Nov 5, 2025
da033cc
Merge branch 'develop' into snappy-main
piotrkluba Nov 6, 2025
50b9c93
changes to adjust the merge
piotrkluba Nov 6, 2025
2003aeb
validation context correction
piotrkluba Nov 6, 2025
c001f13
reverted poetry.lock changes
piotrkluba Nov 7, 2025
fe639af
Merge branch 'develop' into snappy-main
piotrkluba Nov 7, 2025
1f1e897
formatter
piotrkluba Nov 7, 2025
c29746c
sorting within the snappy translator
piotrkluba Nov 7, 2025
2f7744a
formatter
piotrkluba Nov 7, 2025
6241b3e
Merge branch 'develop' into snappy-main
benflexcompute Nov 10, 2025
3e79542
Applied reviewer suggestions part 1
piotrkluba Nov 11, 2025
fbf804e
added test and removed optional from retain on smoothing
piotrkluba Nov 12, 2025
3ccb0c8
merged regions and bodies into one "entities" parameter in snappy.Sur…
piotrkluba Nov 12, 2025
38e2859
apply PR suggestions
piotrkluba Nov 12, 2025
edadbe3
Merge branch 'main' into snappy-main
piotrkluba Nov 12, 2025
3e1c6a2
Merge remote-tracking branch 'origin/main' into snappy-main
benflexcompute Nov 14, 2025
fd08bcb
Merge branch 'main' into snappy-main
benflexcompute Nov 14, 2025
1ddf84a
Ben: Try using the old ndarray seralizer
benflexcompute Nov 14, 2025
81855cf
Fix lint
benflexcompute Nov 14, 2025
3bd1c61
review suggestions applied
piotrkluba Nov 17, 2025
8c798d7
updated quality descriptions
piotrkluba Nov 17, 2025
3d132ce
Changed SeedpointZones to SeedpointVolumes and it is included in Cust…
piotrkluba Nov 17, 2025
ae3b345
to_be_generated_custom_volumes account for SeedpointVolumes
piotrkluba Nov 17, 2025
cb3e9f6
added a validator for project_to_surface option of UniformRefinement
piotrkluba Nov 18, 2025
02493a8
Merge branch 'main' into snappy-main
piotrkluba Nov 18, 2025
504d0c7
linter
piotrkluba Nov 18, 2025
a14ec2b
linter again
piotrkluba Nov 18, 2025
c07a7e4
linter 3
piotrkluba Nov 18, 2025
8c128de
Added self reminder
benflexcompute Nov 18, 2025
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
438 changes: 438 additions & 0 deletions examples/basic_simulations/meshing/cube_snappy.ipynb

Large diffs are not rendered by default.

16 changes: 15 additions & 1 deletion flow360/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from flow360.component.simulation import units as u
from flow360.component.simulation.entity_operation import Transformation
from flow360.component.simulation.folder import Folder
from flow360.component.simulation.meshing_param import snappy
from flow360.component.simulation.meshing_param.edge_params import (
AngleBasedRefinement,
AspectRatioBasedRefinement,
Expand All @@ -24,9 +25,15 @@
PassiveSpacing,
SurfaceRefinement,
)
from flow360.component.simulation.meshing_param.params import (
from flow360.component.simulation.meshing_param.meshing_specs import (
MeshingDefaults,
OctreeSpacing,
VolumeMeshingDefaults,
)
from flow360.component.simulation.meshing_param.params import (
MeshingParams,
ModularMeshingWorkflow,
VolumeMeshingParams,
)
from flow360.component.simulation.meshing_param.volume_params import (
AutomatedFarfield,
Expand Down Expand Up @@ -146,6 +153,7 @@
Cylinder,
ImportedSurface,
ReferenceGeometry,
SeedpointVolume,
)
from flow360.component.simulation.run_control.run_control import RunControl
from flow360.component.simulation.run_control.stopping_criterion import (
Expand Down Expand Up @@ -314,12 +322,18 @@
"math",
"solution",
"report",
"snappy",
"ModularMeshingWorkflow",
"SeedpointVolume",
"VolumeMeshingParams",
"VolumeMeshingDefaults",
"get_user_variable",
"show_user_variables",
"remove_user_variable",
"StoppingCriterion",
"MovingStatistic",
"ImportedSurface",
"OctreeSpacing",
"RunControl",
]

Expand Down
2 changes: 1 addition & 1 deletion flow360/component/case.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ def to_case(self) -> Case:
@before_submit_only
def submit(self, force_submit: bool = False) -> Case:
"""
submits case to cloud for running
Submits case to cloud for running.
"""
assert self.name
assert self.volume_mesh_id or self.other_case or self.parent_id or self.parent_case
Expand Down
30 changes: 30 additions & 0 deletions flow360/component/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,11 @@ class Geometry(AssetBase):
_entity_info_class = GeometryEntityInfo
_cloud_resource_type_name = "Geometry"

# pylint: disable=redefined-builtin
def __init__(self, id: Union[str, None]):
super().__init__(id)
self.snappy_body_registry = None

@property
def face_group_tag(self):
"getter for face_group_tag"
Expand Down Expand Up @@ -263,6 +268,16 @@ def body_group_tag(self):
def body_group_tag(self, new_value: str):
raise SyntaxError("Cannot set body_group_tag, use group_bodies_by_tag() instead.")

@property
def snappy_bodies(self):
"""Getter for the snappy registry."""
if self.snappy_body_registry is None:
raise Flow360ValueError(
"The faces in geometry are not grouped for snappy."
"Please use `group_faces_for_snappy` function to group them first."
)
return self.snappy_body_registry

def get_dynamic_default_settings(self, simulation_dict: dict):
"""Get the default geometry settings from the simulation dict"""

Expand Down Expand Up @@ -412,10 +427,25 @@ def group_bodies_by_tag(self, tag_name: str) -> None:
"body", tag_name, self.internal_registry
)

def group_faces_for_snappy(self) -> None:
"""
Group faces according to body::region convention for snappyHexMesh.
"""
# pylint: disable=protected-access,no-member
self.internal_registry = self._entity_info._group_entity_by_tag(
"face", "faceId", self.internal_registry
)
# pylint: disable=protected-access
self.snappy_body_registry = self._entity_info._group_faces_by_snappy_format()

def reset_face_grouping(self) -> None:
"""Reset the face grouping"""
# pylint: disable=protected-access,no-member
self.internal_registry = self._entity_info._reset_grouping("face", self.internal_registry)
if self.snappy_body_registry is not None:
self.snappy_body_registry = self.snappy_body._reset_grouping(
"face", self.snappy_body_registry
)

def reset_edge_grouping(self) -> None:
"""Reset the edge grouping"""
Expand Down
2 changes: 1 addition & 1 deletion flow360/component/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -1462,7 +1462,7 @@ def _run(
destination_id = draft.run_up_to_target_asset(
target,
source_item_type=source_item_type,
use_beta_mesher=use_beta_mesher,
use_beta_mesher=params.private_attribute_asset_cache.use_inhouse_mesher,
use_geometry_AI=use_geometry_AI,
start_from=start_from,
)
Expand Down
43 changes: 37 additions & 6 deletions flow360/component/simulation/entity_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
import pydantic as pd

from flow360.component.simulation.framework.base_model import Flow360BaseModel
from flow360.component.simulation.framework.entity_registry import EntityRegistry
from flow360.component.simulation.framework.entity_registry import (
EntityRegistry,
SnappyBodyRegistry,
)
from flow360.component.simulation.outputs.output_entities import (
Point,
PointArray,
Expand All @@ -24,6 +27,7 @@
GeometryBodyGroup,
GhostCircularPlane,
GhostSphere,
SnappyBody,
Surface,
)
from flow360.component.simulation.unit_system import LengthType
Expand Down Expand Up @@ -152,7 +156,7 @@ class GeometryEntityInfo(EntityInfoModel):

def group_in_registry(
self,
entity_type_name: Literal["face", "edge", "body"],
entity_type_name: Literal["face", "edge", "body", "snappy_body"],
attribute_name: str,
registry: EntityRegistry,
) -> EntityRegistry:
Expand All @@ -165,15 +169,31 @@ def group_in_registry(
known_frozen_hashes = registry.fast_register(item, known_frozen_hashes)
return registry

def _get_snappy_bodies(self) -> List[SnappyBody]:

snappy_body_mapping = {}
for patch in self.grouped_faces[self.face_attribute_names.index("faceId")]:
name_components = patch.name.split("::")
body_name = name_components[0]
if body_name not in snappy_body_mapping:
snappy_body_mapping[body_name] = []
if patch not in snappy_body_mapping[body_name]:
snappy_body_mapping[body_name].append(patch)

return [
SnappyBody(name=snappy_body, surfaces=body_entities)
for snappy_body, body_entities in snappy_body_mapping.items()
]

def _get_list_of_entities(
self,
attribute_name: Union[str, None] = None,
entity_type_name: Literal["face", "edge", "body"] = None,
) -> Union[List[Surface], List[Edge], List[GeometryBodyGroup]]:
entity_type_name: Literal["face", "edge", "body", "snappy_body"] = None,
) -> Union[List[Surface], List[Edge], List[GeometryBodyGroup], List[SnappyBody]]:
# Validations
if entity_type_name is None:
raise ValueError("Entity type name is required.")
if entity_type_name not in ["face", "edge", "body"]:
if entity_type_name not in ["face", "edge", "body", "snappy_body"]:
raise ValueError(
f"Invalid entity type name, expected 'body, 'face' or 'edge' but got {entity_type_name}."
)
Expand All @@ -185,10 +205,12 @@ def _get_list_of_entities(
entity_attribute_names = self.edge_attribute_names
entity_full_list = self.grouped_edges
specified_attribute_name = self.edge_group_tag
else:
elif entity_type_name == "body":
entity_attribute_names = self.body_attribute_names
entity_full_list = self.grouped_bodies
specified_attribute_name = self.body_group_tag
else:
return self._get_snappy_bodies()

# Use the supplied one if not None
if attribute_name is not None:
Expand Down Expand Up @@ -358,11 +380,20 @@ def _group_entity_by_tag(

return registry

def _group_faces_by_snappy_format(self):
registry = SnappyBodyRegistry()

registry = self.group_in_registry("snappy_body", attribute_name="faceId", registry=registry)

return registry

@pd.validate_call
def _reset_grouping(
self, entity_type_name: Literal["face", "edge", "body"], registry: EntityRegistry
) -> EntityRegistry:
if entity_type_name == "face":
registry.clear(Surface)
registry.clear(SnappyBody)
with model_attribute_unlock(self, "face_group_tag"):
self.face_group_tag = None
elif entity_type_name == "edge":
Expand Down
77 changes: 77 additions & 0 deletions flow360/component/simulation/framework/entity_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,37 @@
from flow360.component.simulation.framework.base_model import Flow360BaseModel
from flow360.component.simulation.framework.entity_base import EntityBase
from flow360.component.utils import _naming_pattern_handler
from flow360.exceptions import Flow360ValueError


class StringIndexableList(list):
"""
An extension of a list that allows accessing elements inside it through a string key.
"""

def __getitem__(self, key: Union[str, slice, int]):
if isinstance(key, str):
returned_items = []
for item in self:
try:
item_ret_value = item[key]
except KeyError:
item_ret_value = []
except Exception as e:
raise ValueError(
f"Trying to access something in {item} through string indexing, which is not allowed."
) from e
if isinstance(item_ret_value, list):
returned_items += item_ret_value
else:
returned_items.append(item_ret_value)
if not returned_items:
raise ValueError(
"No entity found in registry for parent entities: "
+ f"{', '.join([f'{entity.name}' for entity in self])} with given name/naming pattern: '{key}'."
)
return returned_items
return super().__getitem__(key)


class EntityRegistryBucket:
Expand Down Expand Up @@ -224,3 +255,49 @@ def find_by_asset_id(self, *, entity_id: str, entity_class: type[EntityBase]):
def is_empty(self):
"""Return True if the registry is empty, False otherwise."""
return not self.internal_registry


class SnappyBodyRegistry(EntityRegistry):
"""
Extension of :class:`EntityRegistry` to be used with :class:`SnappyBody`, allows double indexing
for accessing the boundaries under certain :class:`SnappyBody`.
"""

def find_by_naming_pattern(
self, pattern: str, enforce_output_as_list: bool = True, error_when_no_match: bool = False
) -> StringIndexableList[EntityBase]:
"""
Finds all registered entities whose names match a given pattern.

Parameters:
pattern (str): A naming pattern, which can include '*' as a wildcard.

Returns:
List[EntityBase]: A list of entities whose names match the pattern.
"""
matched_entities = StringIndexableList()
regex = _naming_pattern_handler(pattern=pattern)
# pylint: disable=no-member
for entity_list in self.internal_registry.values():
matched_entities.extend(filter(lambda x: regex.match(x.name), entity_list))

if not matched_entities and error_when_no_match is True:
raise ValueError(
f"No entity found in registry with given name/naming pattern: '{pattern}'."
)
if enforce_output_as_list is False and len(matched_entities) == 1:
return matched_entities[0]

return matched_entities

def __getitem__(self, key):
"""
Get the entity by name.
`key` is the name of the entity or the naming pattern if wildcard is used.
"""
if isinstance(key, str) is False:
raise Flow360ValueError(f"Entity naming pattern: {key} is not a string.")

return self.find_by_naming_pattern(
key, enforce_output_as_list=False, error_when_no_match=True
)
23 changes: 20 additions & 3 deletions flow360/component/simulation/framework/param_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ def register_entity_list(model: Flow360BaseModel, registry: EntityRegistry) -> N
register_entity_list(field, registry)


# pylint: disable=too-many-branches
def _update_entity_full_name(
model: Flow360BaseModel,
target_entity_type: Union[type[_SurfaceEntityBase], type[_VolumeEntityBase]],
Expand All @@ -167,20 +168,36 @@ def _update_entity_full_name(
field._update_entity_info_with_metadata(volume_mesh_meta_data)

if isinstance(field, EntityList):
added_entities = []
for entity in field.stored_entities:
if isinstance(entity, target_entity_type):
# pylint: disable=protected-access
entity._update_entity_info_with_metadata(volume_mesh_meta_data)
partial_additions = entity._update_entity_info_with_metadata(
volume_mesh_meta_data
)
if partial_additions is not None:
added_entities.extend(partial_additions)
field.stored_entities.extend(added_entities)

elif isinstance(field, (list, tuple)):
added_entities = []
for item in field:
if isinstance(item, target_entity_type):
item._update_entity_info_with_metadata( # pylint: disable=protected-access
volume_mesh_meta_data
partial_additions = (
item._update_entity_info_with_metadata( # pylint: disable=protected-access
volume_mesh_meta_data
)
)
if partial_additions is not None:
added_entities.extend(partial_additions)
elif isinstance(item, Flow360BaseModel):
_update_entity_full_name(item, target_entity_type, volume_mesh_meta_data)

if isinstance(field, list):
field.extend(added_entities)
if isinstance(field, tuple):
field += tuple(added_entities)

elif isinstance(field, Flow360BaseModel):
_update_entity_full_name(field, target_entity_type, volume_mesh_meta_data)

Expand Down
10 changes: 10 additions & 0 deletions flow360/component/simulation/framework/updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,15 @@ def _to_25_7_7(params_as_dict):
return params_as_dict


def _to_25_8_0(params_as_dict):
# new method of specifying meshing was added, as well as the method discriminator
meshing = params_as_dict.get("meshing")
if meshing:
meshing["type_name"] = "MeshingParams"

return params_as_dict


VERSION_MILESTONES = [
(Flow360Version("24.11.1"), _to_24_11_1),
(Flow360Version("24.11.7"), _to_24_11_7),
Expand All @@ -463,6 +472,7 @@ def _to_25_7_7(params_as_dict):
(Flow360Version("25.7.2"), _to_25_7_2),
(Flow360Version("25.7.6"), _to_25_7_6),
(Flow360Version("25.7.7"), _to_25_7_7),
(Flow360Version("25.8.0b4"), _to_25_8_0),
] # A list of the Python API version tuple with there corresponding updaters.


Expand Down
4 changes: 3 additions & 1 deletion flow360/component/simulation/framework/updater_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ def compare_dicts(dict1, dict2, atol=1e-15, rtol=1e-10, ignore_keys=None):
dict2_filtered = {k: v for k, v in dict2.items() if k not in ignore_keys}

if dict1_filtered.keys() != dict2_filtered.keys():
print(f"dict keys not equal, dict1 {dict1_filtered.keys()}, dict2 {dict2_filtered.keys()}")
print(
f"dict keys not equal, dict1 {sorted(dict1_filtered.keys())}, dict2 {sorted(dict2_filtered.keys())}"
)
return False

for key in dict1_filtered:
Expand Down
Loading
Loading