Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
f9317c1
temp
fabiencasenave Sep 23, 2025
e5c5834
save
casenave Sep 23, 2025
3129bc7
temp
casenave Sep 24, 2025
abdcb0e
temp
casenave Sep 24, 2025
f2265ee
continue implementation
casenave Sep 24, 2025
9473bae
add data
casenave Sep 24, 2025
899032b
continue
casenave Sep 24, 2025
8db24fd
continue
casenave Sep 24, 2025
ec13b83
merge
casenave Sep 24, 2025
2a615b7
continue
casenave Sep 25, 2025
11c9667
continue
casenave Sep 25, 2025
ea12770
continue
casenave Sep 25, 2025
447c8d1
continue
casenave Sep 25, 2025
49f5df4
continue
casenave Sep 25, 2025
389d79b
feat(features.py/global) improve docstrings
casenave Sep 25, 2025
51548a7
solve conflict
casenave Sep 25, 2025
84b6667
(sample) add loading of old sample and timeseries files + deprecation…
xroynard Sep 26, 2025
a71a7c2
Merge branch 'main' into globals
fabiencasenave Sep 26, 2025
a779cd4
fix(sample.py) coverage
fabiencasenave Sep 26, 2025
2c1a040
cov and rename delegate -> delegate_methods
fabiencasenave Sep 26, 2025
88a2012
add old_time_series
fabiencasenave Sep 26, 2025
18bfd53
formatting
fabiencasenave Sep 26, 2025
f385d0c
Merge branch 'main' into globals
casenave Sep 26, 2025
8e5f092
start
fabiencasenave Sep 26, 2025
d7377ba
feat(links/paths) remove links/paths and CGNS linking capacities
fabiencasenave Sep 26, 2025
d024328
remove additional argument by reworking new globals mechanism
casenave Oct 5, 2025
a0f9a78
continue
casenave Oct 5, 2025
6ad9ed8
Merge branch 'globals' into datasets_roadmap
casenave Oct 5, 2025
de31e73
update CHANGELOG
casenave Oct 5, 2025
c188ad0
merge
fabiencasenave Oct 7, 2025
a93fc57
merge
fabiencasenave Oct 7, 2025
1e813ae
merge
fabiencasenave Oct 7, 2025
03a24f9
merge
fabiencasenave Oct 7, 2025
7860e3d
merge
fabiencasenave Oct 7, 2025
b9e8a79
merge
fabiencasenave Oct 7, 2025
afe6d4b
continue
casenave Oct 9, 2025
e582adb
merge
casenave Oct 9, 2025
ce1d290
continue
casenave Oct 9, 2025
06a33fa
continue
casenave Oct 9, 2025
5d8f588
continue
casenave Oct 9, 2025
6021b9a
continue
casenave Oct 9, 2025
209f923
continue
casenave Oct 9, 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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Removed

- (sample/features) cgns links and paths, as well as args `mesh_base_name` and `mesh_zone_name`
- (sample) time_series support, now handle directly globals at time steps.


## [0.1.9] - 2025-09-24

### Added
Expand Down
14 changes: 2 additions & 12 deletions examples/containers/sample_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def show_sample(sample: Sample):
# %% [markdown]
# ## Section 1: Initializing an Empty Sample and Adding Data
#
# This section demonstrates how to initialize an empty Sample and add scalars, and meshes / CGNS trees.
# This section demonstrates how to initialize an empty Sample and add scalars, time series data, and meshes / CGNS trees.

# %% [markdown]
# ### Create and display CGNS tree from an unstructured mesh
Expand Down Expand Up @@ -146,17 +146,7 @@ def show_sample(sample: Sample):
# Set meshes in the Sample
new_sample_mult_mesh.features.set_meshes(meshes_dict)

print(f"{new_sample_mult_mesh.features.get_all_mesh_times() = }")

# %% [markdown]
# ### Link tree from another sample

# %%
path_linked_sample = Path.cwd() / "dataset/samples/sample_000000000/meshes/mesh_000000000.cgns"
new_sample_mult_mesh.link_tree(
path_linked_sample, linked_sample=sample, linked_time=0.0, time=1.5
)
print(f"{new_sample_mult_mesh.features.get_all_mesh_times() = }")
print(f"{new_sample_mult_mesh.get_all_mesh_times() = }")

# %% [markdown]
# ## Section 2: Accessing and Modifying Sample Data
Expand Down
29 changes: 10 additions & 19 deletions src/plaid/bridges/huggingface_bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,30 +132,21 @@ def to_plaid_sample(hf_sample: dict[str, bytes]) -> Sample:
return Sample.model_validate(pickled_hf_sample)

except ValidationError:
# If it fails, try to build the sample from its components
try:
scalars = pickled_hf_sample["scalars"]
meshes = pickled_hf_sample["meshes"]

features = SampleFeatures(
data=meshes,
mesh_base_name=pickled_hf_sample.get("mesh_base_name"),
mesh_zone_name=pickled_hf_sample.get("mesh_zone_name"),
links=pickled_hf_sample.get("links"),
paths=pickled_hf_sample.get("paths"),
)
features = SampleFeatures(
data=pickled_hf_sample.get("meshes"),
)

sample = Sample(
path=pickled_hf_sample.get("path"),
features=features,
)
sample = Sample(
path=pickled_hf_sample.get("path"),
features=features,
)

scalars = pickled_hf_sample.get("scalars")
if scalars:
for sn, val in scalars.items():
sample.add_scalar(sn, val)

return Sample.model_validate(sample)
except KeyError as e:
raise KeyError(f"Missing key {e!s} in HF data.") from e
return Sample.model_validate(sample)


def generate_huggingface_description(
Expand Down
88 changes: 7 additions & 81 deletions src/plaid/containers/features.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
"""Module for implementing collections of features within a Sample."""

import copy
import logging
from pathlib import Path
from typing import Optional

import CGNS.MAP as CGM
import CGNS.PAT.cgnskeywords as CGK
import CGNS.PAT.cgnslib as CGL
import CGNS.PAT.cgnsutils as CGU
Expand All @@ -20,7 +17,7 @@
_check_names,
_read_index,
)
from plaid.types import Array, CGNSLink, CGNSNode, CGNSPath, CGNSTree, Field
from plaid.types import Array, CGNSNode, CGNSTree, Field
from plaid.utils import cgns_helper as CGH

logger = logging.getLogger(__name__)
Expand All @@ -31,31 +28,18 @@ class SampleFeatures:

Args:
data (dict[float, CGNSTree], optional): A dictionary mapping time steps to CGNSTrees. Defaults to None.
mesh_base_name (str, optional): The base name for the mesh. Defaults to 'Base'.
mesh_zone_name (str, optional): The zone name for the mesh. Defaults to 'Zone'.
links (dict[float, list[CGNSLink]], optional): A dictionary mapping time steps to lists of links. Defaults to None.
paths (dict[float, list[CGNSPath]], optional): A dictionary mapping time steps to lists of paths. Defaults to None.
"""

def __init__(
self,
data: Optional[dict[float, CGNSTree]] = None,
mesh_base_name: str = "Base",
mesh_zone_name: str = "Zone",
links: Optional[dict[float, list[CGNSLink]]] = None,
paths: Optional[dict[float, list[CGNSPath]]] = None,
):
self.data: dict[float, CGNSTree] = data if data is not None else {}
self._links = links if links is not None else {}
self._paths = paths if paths is not None else {}

self._default_active_base: Optional[str] = None
self._default_active_zone: Optional[str] = None
self._default_active_time: Optional[float] = None

self._mesh_base_name: str = mesh_base_name
self._mesh_zone_name: str = mesh_zone_name

# -------------------------------------------------------------------------#

def set_default_base(self, base_name: str, time: Optional[float] = None) -> None:
Expand Down Expand Up @@ -160,6 +144,8 @@ def set_default_zone_base(

self._default_active_zone = zone_name

# -------------------------------------------------------------------------#

def set_default_time(self, time: float) -> None:
"""Set the default time for the system.

Expand Down Expand Up @@ -327,36 +313,16 @@ def init_tree(self, time: Optional[float] = None) -> CGNSTree:

if not self.data:
self.data = {time: CGL.newCGNSTree()}
self._links = {time: None}
self._paths = {time: None}
elif time not in self.data:
self.data[time] = CGL.newCGNSTree()
self._links[time] = None
self._paths[time] = None

return self.data[time]

def get_links(self, time: Optional[float] = None) -> list[CGNSLink]:
"""Retrieve the CGNS links for a specified time step, if available.

Args:
time (float, optional): The time step for which to retrieve the CGNS links. If a specific time is not provided, the method will display the links for the default time step.

Returns:
list: The CGNS links for the specified time step if available; otherwise, returns None.
"""
time = self.get_time_assignment(time)
return self._links[time] if (self._links) else None

def get_mesh(
self, time: Optional[float] = None, apply_links: bool = False, in_memory=False
) -> Optional[CGNSTree]:
def get_mesh(self, time: Optional[float] = None) -> Optional[CGNSTree]:
"""Retrieve the CGNS tree structure for a specified time step, if available.

Args:
time (float, optional): The time step for which to retrieve the CGNS tree structure. If a specific time is not provided, the method will display the tree structure for the default time step.
apply_links (bool, optional): Activates the following of the CGNS links to reconstruct the complete CGNS tree - in this case, a deepcopy of the tree is made to prevent from modifying the existing tree.
in_memory (bool, optional): Active if apply_links == True, ONLY WORKING if linked mesh is in the current sample. This option follows the link in memory from current sample.

Returns:
CGNSTree: The CGNS tree structure for the specified time step if available; otherwise, returns None.
Expand All @@ -365,28 +331,7 @@ def get_mesh(
return None

time = self.get_time_assignment(time)
tree = self.data[time]

links = self.get_links(time)
if not apply_links or links is None:
return tree

tree = copy.deepcopy(tree)
for link in links:
if not in_memory:
subtree, _, _ = CGM.load(str(Path(link[0]) / link[1]), subtree=link[2])
else:
linked_timestep = int(link[1].split(".cgns")[0].split("_")[1])
linked_timestamp = list(self.data.keys())[linked_timestep]
subtree = self.get_mesh(linked_timestamp)
node_path = "/".join(link[2].split("/")[:-1])
node_to_append = CGU.getNodeByPath(tree, node_path)
assert node_to_append is not None, (
f"nodepath {node_path} not present in tree, cannot apply link"
)
node_to_append[2].append(CGU.getNodeByPath(subtree, link[2]))

return tree
return self.data[time]

def set_meshes(self, meshes: dict[float, CGNSTree]) -> None:
"""Set all meshes with their corresponding time step.
Expand All @@ -399,11 +344,6 @@ def set_meshes(self, meshes: dict[float, CGNSTree]) -> None:
"""
if not self.data:
self.data = meshes
self._links = {}
self._paths = {}
for time in self.data.keys():
self._links[time] = None
self._paths[time] = None
else:
raise KeyError(
"meshes is already set, you cannot overwrite it, delete it first or extend it with `Sample.add_tree`"
Expand Down Expand Up @@ -432,12 +372,8 @@ def add_tree(self, tree: CGNSTree, time: Optional[float] = None) -> CGNSTree:

if not self.data:
self.data = {time: tree}
self._links = {time: None}
self._paths = {time: None}
elif time not in self.data:
self.data[time] = tree
self._links[time] = None
self._paths[time] = None
else:
# TODO: gérer le cas où il y a des bases de mêmes noms... + merge
# récursif des nœuds
Expand Down Expand Up @@ -481,8 +417,6 @@ def del_tree(self, time: float) -> CGNSTree:
if time not in self.data:
raise KeyError(f"There is no CGNS tree for time {time}.")

self._links.pop(time, None)
self._paths.pop(time, None)
return self.data.pop(time)

# -------------------------------------------------------------------------#
Expand Down Expand Up @@ -557,13 +491,7 @@ def init_base(
time = self.get_time_assignment(time)

if base_name is None:
base_name = (
self._mesh_base_name
+ "_"
+ str(topological_dim)
+ "_"
+ str(physical_dim)
)
base_name = "Base_" + str(topological_dim) + "_" + str(physical_dim)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you want to hard code this value ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's only if base_name is not specified, it's the same behavior as before (except I removed the parameter from the classe)


self.init_tree(time)
if not (self.has_base(base_name, time)):
Expand Down Expand Up @@ -662,7 +590,6 @@ def has_globals(self, time: Optional[float] = None) -> bool:
Returns:
bool: `True` if the CGNS tree has a Base called `Globals`, else return `False`.
"""
# print(">>>>>>>>>>>", self.get_base_names(time=time))
return "Global" in self.get_base_names(time=time)

def get_base(
Expand Down Expand Up @@ -725,8 +652,7 @@ def init_zone(

zone_name = self.get_zone_assignment(zone_name, base_name, time)
if zone_name is None:
zone_name = self._mesh_zone_name

zone_name = "Zone"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you want to hard code this value ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's only if zone_name is not specified, it's the same behavior as before (except I removed the parameter from the classe)

zone_node = CGL.newZone(base_node, zone_name, zone_shape, zone_type)
return zone_node

Expand Down
Loading
Loading