Skip to content

Comments

Fix: prevent duplicate exports by popping matched time in is_it_time_…#1073

Merged
RemDelaporteMathurin merged 3 commits intofestim-dev:fenicsxfrom
AdriaLlealS:duplicate-times-fix
Feb 23, 2026
Merged

Fix: prevent duplicate exports by popping matched time in is_it_time_…#1073
RemDelaporteMathurin merged 3 commits intofestim-dev:fenicsxfrom
AdriaLlealS:duplicate-times-fix

Conversation

@AdriaLlealS
Copy link

@AdriaLlealS AdriaLlealS commented Feb 23, 2026

Description

Summary

This PR updates is_it_time_to_export so that when a timestep matches a desired export time (within the np.isclose tolerances), the corresponding entry in times is popped. This prevents repeated exports for the same target time.

Example:

import numpy as np

import festim as F

model = F.HydrogenTransportProblem()
model.mesh = F.Mesh1D(np.linspace(0, 1, 10))

mat = F.Material(D_0=1.0, E_D=0.0)
vol = F.VolumeSubdomain1D(id=1, material=mat, borders=[0, 1])

model.subdomains = [vol]

H = F.Species("H")
model.species = [H]

model.temperature = 300

model.settings = F.Settings(atol=1e-12, rtol=1e-12, final_time=0.9e8 + 20)
model.settings.stepsize = F.Stepsize(1, milestones=[0.9e8])


model.exports = [
    F.Profile1DExport(H, subdomain=vol, times=[0.9e8]),
    F.Profile1DExport(H, subdomain=vol, times=None),
]

model.initialise()
model.t.value = 0.9e8 - 10

model.run()

assert len(model.exports[0].data) == 1, len(model.exports[0].data)
print(f"Found {len(model.exports[0].data)} profiles at times {model.exports[0].t}")
print(f"Total number of timesteps: {len(model.exports[1].data)}")

Related Issues

Motivation and Context

Previously, multiple consecutive timesteps inside the tolerance window could all return True, causing repeated exports for a single requested export time, especially at high simulation times where the relative tolerance (rtol) allows larger absolute differences. This change ensures a 1‑to‑1 mapping between requested and actual exports, provided the simulation reaches timesteps sufficiently close to the target times.

Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 🔨 Code refactoring (no functional changes, no API changes)
  • 📝 Documentation update
  • ✅ Test update (adding missing tests or correcting existing tests)
  • 🔧 Build/CI configuration change

Testing

  • All existing tests pass locally (pytest)
  • I have added new tests that prove my fix is effective or that my feature works

Code Quality Checklist

  • My code follows the code style of this project (Ruff formatted: ruff format .)
  • My code passes linting checks (ruff check .)
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas

Documentation

  • [] I have updated the documentation accordingly (if applicable)
  • I have added docstrings to new functions/classes following the project conventions

Breaking Changes

Screenshots/Examples

Additional Notes

Even though duplicates are now removed, very large simulation times can still cause the matched timestep to be several seconds away from the desired export time. Exposing tolerances (e.g. reducing rtol) cannot fully solve this issue: tightening rtol only delays it and may prevent matching early in the simulation, while relying on a small atol may fail when the timestep becomes larger near an export event.

If precise export timings are required, one way to mitigate this limitation is to use a small atol and ensure that the desired export times are included in the solver’s milestones so that the simulation visits them closely enough.

AdriaLlealS and others added 3 commits February 23, 2026 11:47
…to_export

This updates `is_it_time_to_export` so that when a timestep matches a desired export time (within the `np.isclose` tolerances), the corresponding entry in `times` is popped.

Previously, multiple consecutive timesteps within the tolerance window could all trigger `True`, leading to repeated exports for the same target time, especially at high simulation times where the relative tolerance (rtol) allows larger absolute differences.

With this change:
- only the first matching timestep triggers an export
- the matched time is removed, preventing any subsequent exports for it

This ensures a 1-to-1 mapping between requested and actual exports, provided the simulation reaches timesteps sufficiently close to the target times.
@codecov
Copy link

codecov bot commented Feb 23, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.77%. Comparing base (d7d8372) to head (74c28a6).
⚠️ Report is 4 commits behind head on fenicsx.

Additional details and impacted files
@@           Coverage Diff            @@
##           fenicsx    #1073   +/-   ##
========================================
  Coverage    94.77%   94.77%           
========================================
  Files           44       44           
  Lines         3174     3175    +1     
========================================
+ Hits          3008     3009    +1     
  Misses         166      166           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@RemDelaporteMathurin RemDelaporteMathurin merged commit b09927e into festim-dev:fenicsx Feb 23, 2026
10 of 11 checks passed
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.

2 participants