Skip to content

Add SU2_MON history file monitor - #2988

Open
bigfooted wants to merge 16 commits into
developfrom
feature_monitor
Open

bigfooted wants to merge 16 commits into
developfrom
feature_monitor

Conversation

@bigfooted

@bigfooted bigfooted commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Proposed Changes

Give a brief overview of your contribution here in a few sentences.

SU2_MON, a python based monitor to view residuals, in case you do not want to view the real time residuals in paraview.
[and my most recent pre-commit is also fixing a bunch of empty lines in unrelated python files]

related to #2879

PR Checklist

Put an X by all that apply. You can fill this out after submitting the PR. If you have any questions, don't hesitate to ask! We want to help. These are a guide for you to know what the reviewers will be looking for in your contribution.

  • I am submitting my contribution to the develop branch.
  • My contribution generates no new compiler warnings (try with --warnlevel=3 when using meson).
  • My contribution is commented and consistent with SU2 style (https://su2code.github.io/docs_v7/Style-Guide/).
  • I used the pre-commit hook to prevent dirty commits and used pre-commit run --all to format old commits.
  • I have added a test case that demonstrates my contribution, if necessary.
  • I have updated appropriate documentation (Tutorials, Docs Page, config_template.cpp), if necessary.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Comment thread bin/monitor.py Fixed
@bigfooted

Copy link
Copy Markdown
Contributor Author
image

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
@bigfooted
bigfooted requested a review from rois1995 October 19, 2026 16:51
@Syphonicc

Copy link
Copy Markdown
Contributor

Nice addition! This complements the terminal tool from #2879 (su2live) well: SU2_MON covers the windowed case, and su2live covers headless runs over SSH, where there’s no display and often no PyQt5/matplotlib. It also classifies the run (converging with an ETA, stalled, limit-cycling, diverging).

Once this is in, would you be open to a follow-up PR adding a terminal counterpart in bin/? I could do it as a single stdlib-only script next to monitor.py, or as a --terminal mode of SU2_MON, whichever you prefer.

@rois1995 rois1995 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please address the three inline findings before merging.

The monitor also needs installation entries: neither SU2_MON nor monitor.py is included in the Meson install paths, so the normal prefix/bin installation does not contain the new command. Please install both with the launcher’s executable permission and relative module path preserved.

Please add small manufactured-history tests covering the header formats, sparse iteration values, reset/cache behavior, incremental append, and incomplete lines, plus a short dependency and launch note.

Validation: the exact reader and plotting methods were exercised with a real Matplotlib Agg canvas. Ten checks passed; four failing behavior assertions reproduced the three inline issues. Installation was checked statically.

Comment thread bin/monitor.py Resolved
Comment thread bin/monitor.py Resolved
Comment thread bin/monitor.py Resolved
bigfooted and others added 6 commits October 23, 2026 16:22
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Parse the separate VARIABLES line of TECPLOT histories, plot and filter
against the iteration column, advance the reader version on reset, add
manufactured-history tests, and install SU2_MON and monitor.py with meson.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Comment thread SU2_PY/tests/test_monitor.py Fixed
…ith 'import' and 'import from''

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
@bigfooted

Copy link
Copy Markdown
Contributor Author

Meson: monitor.py and SU2_MON are installed to bindir from SU2_PY/meson.build, and the launcher keeps its executable bit and relative path.
Tests: header formats, incomplete header and lines, sparse iterations, incremental append, reset with and without complete rows, and Time_Iter. They stub PyQt5, so they run without a display.
Dependencies: the header comment of monitor.py lists PyQt5, matplotlib, numpy and pandas and says how to launch.

@bigfooted

Copy link
Copy Markdown
Contributor Author

@rois1995 anything left?

@rois1995 rois1995 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The original reproductions now pass. Please address the remaining P2 inline findings before approval; the P3 comments are smaller plotting corrections.

Comment thread bin/monitor.py
Comment on lines +398 to +399
self.btn_time.setEnabled(has)
self.btn_time.setChecked(has)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P2] Do not auto-select a constant Time_Iter column

Steady SU2 histories also contain Time_Iter, with every value equal to zero. After the idle timer disables the checkbox, selecting such a file makes this code check “Time iterations” automatically. With the default start=1, both plots are empty. An existing solver-generated steady history gives 0 points in time mode and 10 in inner mode.

Please keep inner iterations as the default, or require an advancing time counter before selecting time mode automatically. The reproducer exercises the real reader/plot methods and this method with a checkbox stand-in.

"PyQt5.QtWidgets",
"matplotlib.backends.backend_qt5agg",
):
sys.modules.setdefault(module, unittest.mock.MagicMock())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P2] Import unittest.mock explicitly before using it

A fresh python3 SU2_PY/tests/test_monitor.py exits here with AttributeError: module 'unittest' has no attribute 'mock', before running any tests. Importing unittest alone does not load its mock submodule.

Please add import unittest.mock. Preloading that import in the runner lets all seven tests pass, so this has a one-line fix.

Comment thread bin/monitor.py


def cleanName(name):
return name.replace("VARIABLES=", "").replace('"', "").replace(" ", "")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P2] Remove the VARIABLES prefix after normalizing whitespace

For the supported inline legacy header VARIABLES = "Inner_Iter","rms[Rho]",..., the first replacement does not match because there are spaces around =. The final cleaned name is VARIABLES=Inner_Iter, so the reader misses the counter and silently falls back to row indices. Recorded iterations 0,10,20 become 0,1,2 again.

Please remove spaces and quotes before removing the VARIABLES= prefix. Add a sparse-iteration assertion to the existing legacy-header fixture.

Comment thread bin/monitor.py
if zone.startswith(b"ZONE") and zone.endswith(b"\n"):
offset = f.tell()
elif zone and not zone.endswith(b"\n") and b"ZONE".startswith(zone):
return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P3] Wait for the full optional ZONE line

If the optional line currently ends at ZONE T="Conv without a newline, b"ZONE".startswith(zone) is false because zone is longer than ZONE. The reader commits its header offset before this metadata line. Completing the line and appending numeric rows then leaves the reader stuck at the ZONE text, with zero rows loaded.

Please defer any incomplete line that begins with ZONE, while preserving the existing check for a partially written keyword.

Comment thread bin/monitor.py
if byTime and self.hasTime():
keep[:-1] &= counter[1:] != counter[:-1]
rows = np.flatnonzero(keep)
rows = rows[rows >= 1]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P3] Keep the first record when it passes the iteration filter

This unconditional row-index filter drops a valid first record even when its recorded iteration meets start. For an unsteady restart with one row at each Time_Iter=100,101, rowsFrom(1, byTime=True) returns only 101. A one-row restarted history stays empty.

Please remove the rows >= 1 filter. The preceding counter >= start already implements the requested iteration threshold.

Comment thread bin/monitor.py
Comment on lines +292 to +294
ax.set_xlabel(
"time iterations" if byTime and self.reader.hasTime() else "iterations"
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P3] Update the x-axis label when the counter mode changes

Switching “Time iterations” changes the x values, but it does not change the selected series, so rebuild remains false. The x-axis label therefore stays “iterations” after switching to time mode, or “time iterations” after switching back.

Please set the x-axis label outside the series-rebuild conditional so it follows the current counter mode.

@rois1995

rois1995 commented Nov 8, 2026

Copy link
Copy Markdown
Contributor

Thanks, the original reproductions now pass, and the installation entries and dependency/launch note are present. I found three remaining issues to fix before approval and detailed them inline, along with three lower-priority corrections.

Please also add the monitor tests to an automated test command so the standalone import failure is caught. The four GitHub Actions workflows are green; CodeFactor still reports failure, whose cause I have not established.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants