Skip to content

GEOPY-2779: Add Leroi as option for engine inside Plate Simulation application#378

Open
benk-mira wants to merge 50 commits intodevelopfrom
GEOPY-2779
Open

GEOPY-2779: Add Leroi as option for engine inside Plate Simulation application#378
benk-mira wants to merge 50 commits intodevelopfrom
GEOPY-2779

Conversation

@benk-mira
Copy link
Copy Markdown
Contributor

@benk-mira benk-mira commented Apr 15, 2026

GEOPY-2779 - Add Leroi as option for engine inside Plate Simulation application

Copilot AI review requested due to automatic review settings April 15, 2026 22:10
@github-actions github-actions Bot changed the title Geopy 2779 GEOPY-2779: Add Leroi as option for engine inside Plate Simulation application Apr 15, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds an initial (toggleable) path for running plate simulations via a LeroiAir-based workflow, alongside early interface/options scaffolding and related tests/UI wiring.

Changes:

  • Introduces a use_leroi option (Python + UI JSON) intended to switch plate simulation execution to a LeroiAir driver for TEM forward runs.
  • Adds initial leroi_air options/interface/driver modules under simpeg_drivers/plate_simulation/leroi_air/.
  • Adds new test scaffolding and a pytest fixture to help resolve executables on PATH in IDE-driven runs.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 15 comments.

Show a summary per file
File Description
tests/plate_simulation/runtest/leroi_test.py Adds Leroi-related runtime tests (currently incomplete).
tests/plate_simulation/runtest/conftest.py Adds session fixture to prepend conda Scripts/ to PATH.
tests/plate_simulation/leroi_air/options_test.py Adds assertions around newly introduced LeroiAir options behavior.
tests/plate_simulation/leroi_air/interface_test.py Placeholder for interface tests (header-only).
tests/plate_simulation/leroi_air/driver_test.py Placeholder for driver tests (currently incomplete).
tests/plate_simulation/leroi_air/init.py Provides helper to generate LeroiAirOptions for tests.
simpeg_drivers/plate_simulation/options.py Adds use_leroi flag to plate simulation options.
simpeg_drivers/plate_simulation/leroi_air/options.py Introduces LeroiAirOptions dataclass and derived properties.
simpeg_drivers/plate_simulation/leroi_air/interface.py Adds CFL formatting/writing interface for LeroiAir (scaffolding).
simpeg_drivers/plate_simulation/leroi_air/driver.py Adds a LeroiAir driver stub (currently inconsistent with options/interface).
simpeg_drivers/plate_simulation/leroi_air/init.py Initializes the new leroi_air package (header-only).
simpeg_drivers/plate_simulation/driver.py Wires use_leroi toggle into driver selection logic.
simpeg_drivers-assets/uijson/plate_simulation.ui.json Adds UI control for use_leroi.
Comments suppressed due to low confidence (1)

tests/plate_simulation/runtest/leroi_test.py:22

  • test_leroi_run has no function body, which will raise an IndentationError during test collection. Add a body (e.g., implement assertions or at least pass/pytest.skip(...)) so the test module imports cleanly.
def test_leroi_run(tmp_path):


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread simpeg_drivers/plate_simulation/driver.py
Comment thread simpeg_drivers/plate_simulation/leroi_air/interface.py
Comment thread simpeg_drivers/plate_simulation/leroi_air/interface.py Outdated
Comment thread tests/plate_simulation/runtest/leroi_test.py Outdated
Comment thread simpeg_drivers/plate_simulation/leroi_air/options.py Outdated
Comment thread simpeg_drivers/plate_simulation/driver.py Outdated
Comment thread simpeg_drivers/plate_simulation/leroi_air/options.py Outdated
Comment thread simpeg_drivers/plate_simulation/leroi_air/interface.py Outdated
Comment thread simpeg_drivers/plate_simulation/leroi_air/driver.py Outdated
Comment thread simpeg_drivers-assets/uijson/plate_simulation.ui.json Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 9 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/plate_simulation/runtest/leroi_test.py
Comment thread tests/plate_simulation/runtest/leroi_test.py
Comment thread simpeg_drivers/plate_simulation/leroi_air/options.py Outdated
Comment thread simpeg_drivers/plate_simulation/leroi_air/options.py
Comment thread simpeg_drivers/plate_simulation/leroi_air/interface.py Outdated
Comment on lines +58 to +65
def run(self) -> None:
"""Write input, run LeroiAir, and save simulated data to geoh5."""
self.interface.write_cfl_file(self.project_path / "LeroiAir.cfl")
self.run_leroi()
self.interface.save_to_geoh5(
outfile=self.project_path / "LeroiAir.out",
out_group=self.out_group,
)
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

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

LeroiAirDriver.run() passes self.out_group into save_to_geoh5, but out_group defaults to None. If a caller forgets to set it, this will fail with a non-obvious attribute error inside save_to_geoh5. Add an explicit check (and raise a clear exception) when out_group is not set.

Copilot uses AI. Check for mistakes.
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.

To Copilot's comment, you should validate/set the out_group in the init of the driver. Check the simpeg_drivers.BaseDriver mechanics...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is out of date. I'm passing self.options.out_group now which is treated the same as in simpeg_drivers.BaseDriver (ie: it can't be none).

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.

self.options.out_group can be None, it is typed as such. The base driver uses self.out_group which is validated/created in the init

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ok, I changed the typing of Options.out_group. In the context of a plate-simulation run, the out group can never be None since it is initialized in plate-simulation driver:
image
and passed into the options class on construction:
image

Comment thread simpeg_drivers/plate_simulation/options.py Outdated
Comment thread simpeg_drivers/plate_simulation/leroi_air/options.py Outdated
Comment thread simpeg_drivers/plate_simulation/leroi_air/interface.py Outdated
benk-mira and others added 6 commits April 21, 2026 13:02
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Collaborator

@domfournier domfournier left a comment

Choose a reason for hiding this comment

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

Good progress. See comments, but also check the runtest for the gravity. Looks like the model is being generated inside the mesh of the forward. The forward driver should not do operation on his own.

Image

Comment thread simpeg_drivers/components/meshes.py Outdated
Comment thread simpeg_drivers/plate_simulation/leroi_air/__init__.py Outdated
Comment thread simpeg_drivers/plate_simulation/leroi_air/interface.py
Comment thread simpeg_drivers/plate_simulation/leroi_air/interface.py
Comment thread simpeg_drivers/plate_simulation/leroi_air/interface.py
Comment thread simpeg_drivers/plate_simulation/leroi_air/interface.py Outdated
Comment thread simpeg_drivers/plate_simulation/leroi_air/options.py Outdated
Comment thread simpeg_drivers/plate_simulation/driver.py Outdated
benk-mira and others added 3 commits April 24, 2026 08:13
Co-authored-by: domfournier <dominiquef@mirageoscience.com>
# Conflicts:
#	simpeg_drivers/plate_simulation/driver.py
#	simpeg_drivers/plate_simulation/options.py
#	simpeg_drivers/utils/utils.py
…aved on plate-simulation mesh and starting_model saved on forward mesh. Fix mesh naming
@benk-mira
Copy link
Copy Markdown
Contributor Author

Good progress. See comments, but also check the runtest for the gravity. Looks like the model is being generated inside the mesh of the forward. The forward driver should not do operation on his own.

Image

Fixed the structure.

@benk-mira benk-mira closed this Apr 24, 2026
@benk-mira benk-mira reopened this Apr 24, 2026
Copy link
Copy Markdown
Collaborator

@domfournier domfournier left a comment

Choose a reason for hiding this comment

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

Getting there.
Can you review the comments, but also take a look at the failing test. We should NOT try to run the exe for now, just do it locally.

Comment thread simpeg_drivers-assets/uijson/plate_simulation.ui.json Outdated
Comment thread simpeg_drivers/plate_simulation/leroi_air/interface.py
Comment thread simpeg_drivers/plate_simulation/leroi_air/interface.py Outdated
Comment thread simpeg_drivers/plate_simulation/leroi_air/options.py Outdated
Comment thread simpeg_drivers/plate_simulation/leroi_air/options.py Outdated
"""Extract channel data for a single component from a LeroiAir .out file."""
lines = Path(outfile).read_text(encoding="utf-8", errors="replace").splitlines()
data_lines = self._slice_data_lines(lines, self._COMPONENT_ANCHORS[component])
return np.array([line.split() for line in data_lines], dtype=float)[:, 4:]
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.

This is where the unit conversion should occur if needed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not sure what you intended here. The unit conversion is in time. I handled this by adding back in properties on the LeroiAirOptions for channels, timing_mark, and waveform, and applying the conversion to milliseconds that LeroiAir expects.

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.

As long as it comes back in SI like SimPEG, then you can ignore.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

SI, with a nT -> T required:
image

Comment thread simpeg_drivers/plate_simulation/leroi_air/options.py Outdated
layer_thicknesses=[model.overburden_options.thickness, 9999],
plate_resistivities=[model.plate_options.plate_property],
plate_geometries=[model.plate_options.geometry],
magnetic_field="dBdt" if "dBdt" in simulation.data_units else "B",
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.

Why not preserving the name and values of data_units?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

it's just a flag to run leroi air in STEP=0 or 1 so I didn't think it was necessary, I simplified to just take a bool.

Comment on lines +58 to +65
def run(self) -> None:
"""Write input, run LeroiAir, and save simulated data to geoh5."""
self.interface.write_cfl_file(self.project_path / "LeroiAir.cfl")
self.run_leroi()
self.interface.save_to_geoh5(
outfile=self.project_path / "LeroiAir.out",
out_group=self.out_group,
)
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.

To Copilot's comment, you should validate/set the out_group in the init of the driver. Check the simpeg_drivers.BaseDriver mechanics...

Comment thread .pre-commit-config.yaml Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 28, 2026

Codecov Report

❌ Patch coverage is 92.04545% with 28 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.36%. Comparing base (749ec6c) to head (5191acb).

Files with missing lines Patch % Lines
...eg_drivers/plate_simulation/leroi_air/interface.py 92.36% 9 Missing and 2 partials ⚠️
simpeg_drivers/plate_simulation/driver.py 84.21% 6 Missing and 3 partials ⚠️
...mpeg_drivers/plate_simulation/leroi_air/options.py 95.79% 5 Missing ⚠️
...impeg_drivers/plate_simulation/leroi_air/driver.py 85.00% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop     #378      +/-   ##
===========================================
+ Coverage    90.26%   90.36%   +0.10%     
===========================================
  Files          132      135       +3     
  Lines         6469     6765     +296     
  Branches       814      822       +8     
===========================================
+ Hits          5839     6113     +274     
- Misses         420      439      +19     
- Partials       210      213       +3     
Files with missing lines Coverage Δ
simpeg_drivers/plate_simulation/options.py 100.00% <100.00%> (+12.12%) ⬆️
simpeg_drivers/utils/utils.py 66.77% <ø> (ø)
...impeg_drivers/plate_simulation/leroi_air/driver.py 85.00% <85.00%> (ø)
...mpeg_drivers/plate_simulation/leroi_air/options.py 95.79% <95.79%> (ø)
simpeg_drivers/plate_simulation/driver.py 88.81% <84.21%> (-4.16%) ⬇️
...eg_drivers/plate_simulation/leroi_air/interface.py 92.36% <92.36%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment on lines +58 to +65
def run(self) -> None:
"""Write input, run LeroiAir, and save simulated data to geoh5."""
self.interface.write_cfl_file(self.project_path / "LeroiAir.cfl")
self.run_leroi()
self.interface.save_to_geoh5(
outfile=self.project_path / "LeroiAir.out",
out_group=self.out_group,
)
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.

self.options.out_group can be None, it is typed as such. The base driver uses self.out_group which is validated/created in the init

Comment on lines +165 to +166
def record_2(self) -> str:
return self.format_line(["TDFD", "DO3D", "PRFL", "ISTOP"])
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.

Would you mind adding docstrings for all of these. Just a translation of the code from the docs.

"""Extract channel data for a single component from a LeroiAir .out file."""
lines = Path(outfile).read_text(encoding="utf-8", errors="replace").splitlines()
data_lines = self._slice_data_lines(lines, self._COMPONENT_ANCHORS[component])
return np.array([line.split() for line in data_lines], dtype=float)[:, 4:]
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.

As long as it comes back in SI like SimPEG, then you can ignore.

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.

4 participants