Skip to content

Commit

Permalink
Add dashboard for elliptic executables
Browse files Browse the repository at this point in the history
  • Loading branch information
nilsvu committed Jul 3, 2024
1 parent 598525e commit 53f1077
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 11 deletions.
20 changes: 9 additions & 11 deletions src/Visualization/Python/PlotEllipticConvergence.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,15 @@ def split_iteration_sequence(data: pd.DataFrame) -> List[pd.DataFrame]:

def plot_elliptic_convergence(
h5_file,
axes=None,
fig=None,
linear_residuals_subfile_name="GmresResiduals.dat",
nonlinear_residuals_subfile_name="NewtonRaphsonResiduals.dat",
):
"""Plot elliptic solver convergence
Arguments:
h5_file: The H5 reductions file.
axes: Optional. The matplotlib axes to plot on. Should be a tuple of two
axes, the first for the residuals and the second for the walltime.
fig: Optional. The matplotlib figure to plot in.
linear_residuals_subfile_name: The name of the subfile containing the
linear solver residuals.
nonlinear_residuals_subfile_name: The name of the subfile containing the
Expand Down Expand Up @@ -82,12 +81,11 @@ def plot_elliptic_convergence(
else linear_residuals
)[0]["Residual"].iloc[0]
# Plot nonlinear solver residuals
if axes is None:
fig, (ax_residual, ax_time) = plt.subplots(
nrows=2, ncols=1, sharex=True, gridspec_kw={"height_ratios": [3, 1]}
)
else:
ax_residual, ax_time = axes
if fig is None:
fig = plt.figure()
ax_residual, ax_time = fig.subplots(
nrows=2, ncols=1, sharex=True, gridspec_kw={"height_ratios": [3, 1]}
)
if nonlinear_residuals is not None:
m = 0
for i, residuals in enumerate(nonlinear_residuals):
Expand Down Expand Up @@ -150,7 +148,6 @@ def plot_elliptic_convergence(
)

# Configure the axes
ax_residual.set_title("Elliptic solver convergence")
ax_residual.set_yscale("log")
ax_residual.grid()
ax_residual.legend()
Expand All @@ -161,6 +158,7 @@ def plot_elliptic_convergence(
ax_time.set_xlabel("Cumulative linear solver iteration")
# Allow only integer ticks for the x-axis
ax_time.xaxis.set_major_locator(MaxNLocator(integer=True))
return fig


@click.command(name="elliptic-convergence")
Expand All @@ -185,7 +183,7 @@ def plot_elliptic_convergence(
def plot_elliptic_convergence_command(**kwargs):
"""Plot elliptic solver convergence"""
_rich_traceback_guard = True # Hide traceback until here
plot_elliptic_convergence(**kwargs)
return plot_elliptic_convergence(**kwargs)


if __name__ == "__main__":
Expand Down
22 changes: 22 additions & 0 deletions tools/Status/ExecutableStatus/ExecutableStatus.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@

import logging
import os
from pathlib import Path
from typing import Any, Dict, List, Optional

import h5py
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

Expand Down Expand Up @@ -334,3 +336,23 @@ def format(self, field, value):
elif field in ["Residual"]:
return f"{value:.1e}"
raise ValueError

def render_solver_convergence(self, job: dict, input_file: dict):
import streamlit as st
import plotly.express as px

from spectre.Visualization.PlotEllipticConvergence import (
plot_elliptic_convergence,
)

st.subheader("Elliptic solver convergence")
fig = plt.figure(figsize=(8, 6), layout="tight")
plot_elliptic_convergence(
Path(job["WorkDir"])
/ (input_file["Observers"]["ReductionFileName"] + ".h5"),
fig=fig,
)
st.pyplot(fig)

def render_dashboard(self, job: dict, input_file: dict):
return self.render_solver_convergence(job, input_file)
59 changes: 59 additions & 0 deletions tools/Status/ExecutableStatus/SolveXcts.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@

import logging
import os
from pathlib import Path

import h5py
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

from .ExecutableStatus import EllipticStatus

Expand Down Expand Up @@ -49,3 +53,58 @@ def format(self, field, value):
return super().format("Iteration", value)
elif "residual" in field:
return super().format("Residual", value)

def render_dashboard(self, job: dict, input_file: dict):
import streamlit as st
import plotly.express as px

super().render_solver_convergence(job, input_file)

# Parameter control
control_outfile = Path(job["WorkDir"]) / "ControlParamsData.txt"
if control_outfile.exists():
st.subheader("Parameter control")
control_data = np.loadtxt(control_outfile, delimiter=",")
df = pd.DataFrame(
control_data,
columns=[
"Iteration",
"Mass A",
"Mass B",
"Spin A x",
"Spin A y",
"Spin A z",
"Spin B x",
"Spin B y",
"Spin B z",
"Residual Mass A",
"Residual Mass B",
"Residual Spin A x",
"Residual Spin A y",
"Residual Spin A z",
"Residual Spin B x",
"Residual Spin B y",
"Residual Spin B z",
],
).set_index("Iteration")
# Print current params
params = df.iloc[-1]
mass_A, mass_B = params["Mass A"], params["Mass B"]
spin_A, spin_B = [
np.linalg.norm(params[[f"Spin {AB} {xyz}" for xyz in "xyz"]])
for AB in "AB"
]
for label, val, col in zip(
["Mass A", "Mass B", "Spin A", "Spin B"],
[mass_A, mass_B, spin_A, spin_B],
st.columns(4),
):
col.metric(label, f"{val:.4g}")
# Plot convergence
fig = px.line(
np.abs(df[[col for col in df.columns if "Residual" in col]]),
log_y=True,
markers=True,
)
fig.update_yaxes(exponentformat="e", title=None)
st.plotly_chart(fig)

0 comments on commit 53f1077

Please sign in to comment.