Skip to content

refactor(med): rewrite field writing to support multi-timestep and group by base name#16

Open
fznoussi wants to merge 6 commits into
mainfrom
refactor/multi-timestep-field-write
Open

refactor(med): rewrite field writing to support multi-timestep and group by base name#16
fznoussi wants to merge 6 commits into
mainfrom
refactor/multi-timestep-field-write

Conversation

@fznoussi
Copy link
Copy Markdown
Collaborator

@fznoussi fznoussi commented May 29, 2026

The previous write() implementation iterated over mesh.point_data and mesh.cell_data entries one by one and called _write_data() for each. This had two major problems:

  1. Multi-timestep fields (stored as 'Temperature[0] - 0.0', 'Temperature[1] - 1.0', etc. after read) were written as separate independent MED fields instead of being grouped under a single field group with multiple timestep subgroups, which is the correct MED structure.
  2. The tracker.notify() call for nodal fields was using hardcoded 'MED_POINT1' regardless of the actual field support, causing incorrect bitmask metadata in the output file.

Changes:

  • Added _parse_med_field_name(name): parses 'FieldName[i] - t' into (base_name, index, time) using a regex so multi-timestep fields can be regrouped by their base name
  • Added _write_field_step(): extracted the low-level HDF5 writing of a single timestep (NOE/MAI group, profile subgroup, CO dataset) into a dedicated helper to avoid code duplication between nodal and cell paths
  • write(): replaced the flat loop over point_data/cell_data with two defaultdict(list) accumulators (nodal_groups, cell_groups) that group entries by base field name before writing
  • write(): each base field name now creates exactly one HDF5 field group with one timestep subgroup per entry, matching the MED spec
  • write(): nodal tracker.notify() now uses 'MED_NODE'/'MED_NO_GEOTYPE' and cell tracker.notify() uses med_type_to_entity[med_type] and med_to_geo_type[med_type] for correct bitmask generation
  • write(): added early return with f.close() when there are no fields to write, avoiding creation of an empty CHA group
  • _write_data(): kept as a compatibility shim for _medmulti.py with two new optional parameters (tracker, step_name) so callers can override the hardcoded step name and flush the tracker themselves

Why: without this fix, a MED file with multiple timesteps read and re-written by meshio would produce a structurally invalid file where each timestep appears as a separate field, which Code_Aster and Salome refuse to load or silently misinterpret.

fznoussi added 6 commits May 29, 2026 13:58
…oup by base name

The previous write() implementation iterated over mesh.point_data and
mesh.cell_data entries one by one and called _write_data() for each.
This had two major problems:
  1. Multi-timestep fields (stored as 'Temperature[0] - 0.0',
     'Temperature[1] - 1.0', etc. after read) were written as separate
     independent MED fields instead of being grouped under a single
     field group with multiple timestep subgroups, which is the correct
     MED structure.
  2. The tracker.notify() call for nodal fields was using hardcoded
     'MED_POINT1' regardless of the actual field support, causing
     incorrect bitmask metadata in the output file.

Changes:
- Added _parse_med_field_name(name): parses 'FieldName[i] - t' into
  (base_name, index, time) using a regex so multi-timestep fields can
  be regrouped by their base name
- Added _write_field_step(): extracted the low-level HDF5 writing of a
  single timestep (NOE/MAI group, profile subgroup, CO dataset) into a
  dedicated helper to avoid code duplication between nodal and cell paths
- write(): replaced the flat loop over point_data/cell_data with two
  defaultdict(list) accumulators (nodal_groups, cell_groups) that group
  entries by base field name before writing
- write(): each base field name now creates exactly one HDF5 field group
  with one timestep subgroup per entry, matching the MED spec
- write(): nodal tracker.notify() now uses 'MED_NODE'/'MED_NO_GEOTYPE'
  and cell tracker.notify() uses med_type_to_entity[med_type] and
  med_to_geo_type[med_type] for correct bitmask generation
- write(): added early return with f.close() when there are no fields to
  write, avoiding creation of an empty CHA group
- _write_data(): kept as a compatibility shim for _medmulti.py with two
  new optional parameters (tracker, step_name) so callers can override
  the hardcoded step name and flush the tracker themselves

Why: without this fix, a MED file with multiple timesteps read and
re-written by meshio would produce a structurally invalid file where
each timestep appears as a separate field, which Code_Aster and Salome
refuse to load or silently misinterpret.
…uires both the geometric type (MED_TRIA3, MED_TETRA4, etc.) and the entity type (MED_CELL, MED_NODE_ELEMENT, etc.). Previously the entity type was hardcoded as a string in the write() call sites, which was incorrect for vertex elements (PO1 should be MED_NODE_ELEMENT, not MED_CELL). The new med_type_to_entity dict provides the correct entity type for every supported MED element type and is used in the cell data write loop.
…oup by base name

Previously, write() iterated over mesh.point_data and mesh.cell_data
entries one by one and called _write_data() for each. This had two
major problems:
  1. Multi-timestep fields stored as 'Temperature[0] - 0.0',
     'Temperature[1] - 1.0', etc. were written as separate independent
     MED fields instead of being grouped under a single field group with
     multiple timestep subgroups, which is the correct MED structure.
  2. CHA group was always created even when there were no fields,
     producing an unnecessary empty group.

Changes:
- Added _parse_med_field_name(): parses 'FieldName[i] - t' into
  (base_name, index, time) using a regex so multi-timestep fields
  can be regrouped by their base name
- write(): replaced flat loop with defaultdict(list) accumulators
  (nodal_groups, cell_groups) that group entries by base field name
- write(): each base field name creates exactly one HDF5 field group
  with one timestep subgroup per entry
- write(): added early return with f.close() when there are no fields
  to write, avoiding creation of an empty CHA group
- write(): nodal tracker.notify() now uses MED_NODE/MED_NO_GEOTYPE
  and cell tracker.notify() uses med_to_geo_type for correct bitmask
Add 5 tests to verify that the multi-timestep field write refactor
works correctly.

Tests added:

- test_parse_med_field_name_single:
  Checks that a field name without timestep pattern returns
  (name, None, None). Without PR16 this function did not exist.

- test_parse_med_field_name_multi:
  Checks that 'Temperature[2] - 1.5' is correctly parsed into
  ('Temperature', 2, 1.5).

- test_multi_timestep_grouped_under_single_hdf5_field:
  Checks that multiple timesteps of the same field are written under
  a single HDF5 group in CHA. Without PR16 each 'Temperature[i] - t'
  created a separate group.

- test_no_cha_group_when_no_fields:
  Checks that CHA group is not created when there are no fields.
  Without PR16 CHA was always created even when empty.

- test_multi_timestep_roundtrip_box:
  Checks that a MED file with multiple timesteps survives a full
  read→write cycle with correct values using box.med.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant