Skip to content

bug fix for min/max values not working on discontinuous problem - #1226

Merged
jhdark merged 17 commits into
festim-dev:mainfrom
ee-nn:1225-min-max-export-fix
Sep 8, 2026
Merged

jhdark merged 17 commits into
festim-dev:mainfrom
ee-nn:1225-min-max-export-fix

Conversation

@ee-nn

@ee-nn ee-nn commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Description

Provides a fix for issue #1225.

Summary

Essentially #1225 was the result of two separate problems:

  1. initialise_exports() of HydrogenTransportProblemDiscontinuous overrides the base method from HydrogenTransportProblem, but it never initializes its own export.volume_meshtags or export.facet_meshtags. Since it doesn't invoke super() either, these attributes of export aren't carried over to the discontinuous class.

  2. Even with the meshtags attached, compute() in Min/MaxVolume and Min/MaxSurface read self.field.post_processing_solution, which stays None in the discontinuous case because the solution lives per-submesh in species.subdomain_to_post_processing_solution[subdomain].

Motivation and Context

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

Specifically, new system test for the discontinuous case (which is essentially the MWE listed in #1225 - not sure if this is overkill?), and some smaller assertion tests to ensure that meshtags are always defined.

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

@codecov

codecov Bot commented Aug 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.89%. Comparing base (4746e2d) to head (93773dc).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1226      +/-   ##
==========================================
+ Coverage   95.70%   95.89%   +0.19%     
==========================================
  Files          57       57              
  Lines        4955     5017      +62     
==========================================
+ Hits         4742     4811      +69     
+ Misses        213      206       -7     
Flag Coverage Δ
dolfinx-nightly 95.63% <100.00%> (+0.20%) ⬆️
dolfinx-v0.10.0 92.92% <100.00%> (+0.22%) ⬆️
dolfinx-v0.11.0 95.77% <100.00%> (+0.19%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 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.

Comment on lines +2138 to +2164
# the extrema exports read the solution on the submesh of the volume
# subdomain, so check that the species is actually defined there.
# a field that is not a Species with a list of subdomains (a bare name,
# or a species generated from a trap) is skipped: it fails earlier, for
# unrelated reasons
is_extremum = isinstance(
export,
exports.MaximumVolume
| exports.MinimumVolume
| exports.MaximumSurface
| exports.MinimumSurface,
)
if is_extremum and isinstance(export.field, _species.Species):
if isinstance(export, exports.SurfaceQuantity):
volume = self.surface_to_volume[export.surface]
location = f"surface {export.surface.id}"
else:
volume = export.volume
location = f"volume {volume.id}"
subdomains = export.field.subdomains
if isinstance(subdomains, list) and volume not in subdomains:
raise ValueError(
f"Cannot compute {export.title}: species "
f"{export.field.name} is not defined in the volume subdomain "
f"{volume.id} that {location} belongs to"
)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This just provides some error catching with nice-looking raises so could be deleted if desired

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think it's good to catch this early

Comment on lines +471 to +482
bot_min = F.MinimumVolume(
field=H, volume=bottom_volume, filename=f"{tmpdir}/bot_min.csv"
)
bot_max = F.MaximumVolume(field=H, volume=bottom_volume)
top_min = F.MinimumVolume(field=H, volume=top_volume)
top_max = F.MaximumVolume(field=H, volume=top_volume)
bot_surf_min = F.MinimumSurface(
field=H, surface=bottom_surface, filename=f"{tmpdir}/bot_surf_min.csv"
)
bot_surf_max = F.MaximumSurface(field=H, surface=bottom_surface)
top_surf_min = F.MinimumSurface(field=H, surface=top_surface)
top_surf_max = F.MaximumSurface(field=H, surface=top_surface)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Might be a little redundant, perhaps only the bottom domain could be checked?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think it's ok to look out for everything

@RemDelaporteMathurin RemDelaporteMathurin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for this @ee-nn !!! A first pass of comments before I go more in depth

Comment thread src/festim/exports/maximum_surface.py Outdated
meshtags = self.facet_meshtags if facet_meshtags is None else facet_meshtags

if meshtags is None:
raise ValueError(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could be an assertion instead with a message if false

values = solution.x.array[dofs]

# a process may hold no dof of the surface at all, np.max would then raise
local_max = np.max(values) if values.size > 0 else -np.inf

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Good catch

Comment on lines +2138 to +2164
# the extrema exports read the solution on the submesh of the volume
# subdomain, so check that the species is actually defined there.
# a field that is not a Species with a list of subdomains (a bare name,
# or a species generated from a trap) is skipped: it fails earlier, for
# unrelated reasons
is_extremum = isinstance(
export,
exports.MaximumVolume
| exports.MinimumVolume
| exports.MaximumSurface
| exports.MinimumSurface,
)
if is_extremum and isinstance(export.field, _species.Species):
if isinstance(export, exports.SurfaceQuantity):
volume = self.surface_to_volume[export.surface]
location = f"surface {export.surface.id}"
else:
volume = export.volume
location = f"volume {volume.id}"
subdomains = export.field.subdomains
if isinstance(subdomains, list) and volume not in subdomains:
raise ValueError(
f"Cannot compute {export.title}: species "
f"{export.field.name} is not defined in the volume subdomain "
f"{volume.id} that {location} belongs to"
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think it's good to catch this early

Comment on lines +471 to +482
bot_min = F.MinimumVolume(
field=H, volume=bottom_volume, filename=f"{tmpdir}/bot_min.csv"
)
bot_max = F.MaximumVolume(field=H, volume=bottom_volume)
top_min = F.MinimumVolume(field=H, volume=top_volume)
top_max = F.MaximumVolume(field=H, volume=top_volume)
bot_surf_min = F.MinimumSurface(
field=H, surface=bottom_surface, filename=f"{tmpdir}/bot_surf_min.csv"
)
bot_surf_max = F.MaximumSurface(field=H, surface=bottom_surface)
top_surf_min = F.MinimumSurface(field=H, surface=top_surface)
top_surf_max = F.MaximumSurface(field=H, surface=top_surface)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think it's ok to look out for everything

Comment thread test/test_minimum_surface.py Outdated
)

with pytest.raises(ValueError, match="facet meshtags are required"):
my_export.compute()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If meshtags are always required why are they not a mandatory argument of compute?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good point, I can think about this more and update

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Pushed an implementation, let me know what you think

ee-nn added a commit to ee-nn/FESTIM that referenced this pull request Aug 10, 2026
ee-nn added a commit to ee-nn/FESTIM that referenced this pull request Aug 11, 2026
Comment thread src/festim/exports/minimum_volume.py Outdated
Comment thread src/festim/exports/minimum_volume.py Outdated
Comment thread src/festim/exports/minimum_volume.py Outdated
Comment thread src/festim/exports/minimum_volume.py Outdated
Comment thread src/festim/exports/minimum_volume.py Outdated
@ee-nn ee-nn mentioned this pull request Aug 11, 2026
15 tasks
@RemDelaporteMathurin RemDelaporteMathurin added the bug Something isn't working label Sep 1, 2026
@RemDelaporteMathurin

Copy link
Copy Markdown
Collaborator

Let's merge the stack #1216 first and then fix any conflicts here

@ee-nn

ee-nn commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator Author

@RemDelaporteMathurin Ok I think this should be good to merge now

@ee-nn ee-nn self-assigned this Sep 8, 2026
@jhdark
jhdark merged commit fdfffe8 into festim-dev:main Sep 8, 2026
11 checks passed
@ee-nn
ee-nn deleted the 1225-min-max-export-fix branch September 8, 2026 18:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants