Skip to content

Commit 0e0a0c5

Browse files
committed
More features
1 parent 12bfc24 commit 0e0a0c5

File tree

1 file changed

+71
-33
lines changed

1 file changed

+71
-33
lines changed

pydmd/plotter.py

Lines changed: 71 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
from .bopdmd import BOPDMD
1515
from .hankeldmd import HankelDMD
16+
from .havok import HAVOK
1617

1718
mpl.rcParams["figure.max_open_warning"] = 0
1819

@@ -539,38 +540,38 @@ def plot_summary(
539540
order="C",
540541
figsize=(12, 8),
541542
dpi=200,
543+
tight_layout_kwargs=None,
542544
main_colors=("r", "b", "g", "gray"),
543-
max_plot=50,
544-
max_marker_size=10,
545-
vmax_scale=0.9,
545+
mode_color="k",
546546
mode_cmap="bwr",
547-
mode_color="tab:orange",
548547
dynamics_color="tab:blue",
548+
plot_semilogy=False,
549+
remove_cmap_ticks=False,
549550
):
550551
"""
551-
Generate a 3x3 summarizing plot that contains the following components:
552+
Generate a 3 x 3 summarizing plot that contains the following components:
552553
- the singular value spectrum of the data
553-
- the discrete-time and continuous-time dmd eigenvalues
554-
- the three dmd modes specified by the index_modes parameter
554+
- the discrete-time and continuous-time DMD eigenvalues
555+
- the three DMD modes specified by the `index_modes` parameter
555556
- the dynamics corresponding with each plotted mode
556557
Eigenvalues, modes, and dynamics are ordered according to the magnitude of
557558
their corresponding amplitude value. Singular values and eigenvalues that
558559
are associated with plotted modes and dynamics are also highlighted.
559560
560561
:param dmd: DMD instance.
561562
:type dmd: pydmd.DMDBase
562-
:param continuous: whether or not the eigenvalues of the given DMD instance
563+
:param continuous: Whether or not the eigenvalues of the given DMD instance
563564
are continuous-time. If `False`, the eigenvalues are assumed to be the
564565
discrete-time eigenvalues. If `True`, the eigenvalues are taken to be
565-
the continuous-time eigenvalues. Note that `BOPDMD` models always
566-
compute continuous-time eigenvalues.
566+
the continuous-time eigenvalues. Note that `continuous` is
567+
automatically assumed to be true if a `BOPDMD` model is given.
567568
:type continuous: bool
568569
:param snapshots_shape: Shape of the snapshots. If not provided, the shape
569570
of the snapshots and modes is assumed to be the flattened space dim of
570571
the snapshot data.
571572
:type snapshots_shape: tuple(int, int)
572-
:param index_modes: The indices of the modes to plot. By default, the first
573-
three leading modes are plotted.
573+
:param index_modes: A list of the indices of the modes to plot. By default,
574+
the first three leading modes are plotted.
574575
:type index_modes: list
575576
:param filename: If specified, the plot is saved at `filename`.
576577
:type filename: str
@@ -588,16 +589,44 @@ def plot_summary(
588589
"C" is used by default.
589590
:type order: {"C", "F", "A"}
590591
:param figsize: Tuple in inches defining the figure size.
591-
Deafult is (12, 8).
592592
:type figsize: tuple(int, int)
593+
:param dpi: Figure resolution.
594+
:type dpi: int
595+
:param tight_layout_kwargs: Optional dictionary of
596+
`matplotlib.pyplot.tight_layout()` parameters.
597+
:type tight_layout_kwargs: dict
593598
:param main_colors: Tuple of strings defining the colors used to denote
594599
eigenvalue, mode, dynamics associations. The first three colors are
595600
used to highlight the singular values and eigenvalues associated with
596601
the plotted modes and dynamics, while the fourth color is used to
597-
denote all other singular values and eigenvalues. Default colors are
598-
("r","b","g","gray").
599-
:type main_colors: tuple(str,str,str,str)
602+
denote all other values.
603+
:type main_colors: tuple(str, str, str, str)
604+
:param mode_color: Color used to plot the modes, if modes are 1D.
605+
:type mode_color: str
606+
:param mode_cmap: Colormap used to plot the modes, if modes are 2D.
607+
:type mode_cmap: str
608+
:param dynamics_color: Color used to plot the dynamics.
609+
:type dynamics_color: str
610+
:param plot_semilogy: Whether or not to plot the singular values on a
611+
semilogy plot. If `True`, a semilogy plot is used.
612+
:type plot_semilogy: bool
613+
:param remove_cmap_ticks: Whether or not to include the ticks on 2D mode
614+
plots. If `True`, ticks are removed from all 2D mode plots.
615+
:type remove_cmap_ticks: bool
600616
"""
617+
# Other potential parameters to consider:
618+
# - ylims on the dynamics plots
619+
# - fontsizes
620+
621+
# All other potentially customizable values.
622+
# For now, we take them to be constants.
623+
max_plot = 50
624+
sval_ms = 8 # singular value marker size
625+
max_eig_ms = 10 # marker size of the most prominent eigenvalue
626+
vmax_scale = 0.9
627+
628+
if isinstance(dmd, HAVOK):
629+
raise ValueError("You should use HAVOK.plot_summary() instead.")
601630

602631
# Check that the DMD instance has been fitted.
603632
if dmd.modes is None:
@@ -629,7 +658,7 @@ def plot_summary(
629658
# Indices cannot go past the total number of available or plottable modes.
630659
elif np.any(np.array(index_modes) >= min(len(dmd.eigs), max_plot)):
631660
raise ValueError(
632-
"Cannot view past mode {}.".format(min(len(dmd.eigs), max_plot))
661+
f"Cannot view past mode {min(len(dmd.eigs), max_plot)}."
633662
)
634663

635664
# Sort eigenvalues, modes, and dynamics according to amplitude magnitude.
@@ -641,6 +670,7 @@ def plot_summary(
641670
# Get time step information for eigenvalue conversions.
642671
if isinstance(dmd, BOPDMD):
643672
# BOPDMD models store time in the time attribute.
673+
time = dmd.time
644674
dt = dmd.time[1] - dmd.time[0]
645675
if not np.allclose(dmd.time[1:] - dmd.time[:-1], dt):
646676
print(
@@ -651,12 +681,13 @@ def plot_summary(
651681
else:
652682
disc_eigs = np.exp(lead_eigs * dt)
653683
cont_eigs = lead_eigs
654-
# For all other DMD instances, access dt using the time dictionaries.
655684
else:
656685
try:
686+
time = dmd.original_timesteps
657687
dt = dmd.original_time["dt"]
658688
except AttributeError:
659689
warnings.warn("No time step information available. Using dt = 1.")
690+
time = np.arange(dmd.snapshots.shape[-1])
660691
dt = 1.0
661692

662693
if continuous:
@@ -682,33 +713,35 @@ def plot_summary(
682713
3, 3, figsize=figsize, dpi=dpi
683714
)
684715

685-
# Plot 1: Plot the singular value spectrum.
716+
# PLOT 1: Plot the singular value spectrum.
686717
s_var_plot = s_var[:max_plot]
687718
eig_axes[0].set_title("Singular Values")
688719
eig_axes[0].set_ylabel("% variance")
689720
t = np.arange(len(s_var_plot)) + 1
690-
eig_axes[0].plot(t, s_var_plot, "o", c=main_colors[-1], ms=8, mec="k")
721+
eig_axes[0].plot(t, s_var_plot, "o", c=main_colors[-1], ms=sval_ms, mec="k")
691722
for i, idx in enumerate(index_modes):
692723
eig_axes[0].plot(
693-
t[idx], s_var_plot[idx], "o", c=main_colors[i], ms=8, mec="k"
724+
t[idx], s_var_plot[idx], "o", c=main_colors[i], ms=sval_ms, mec="k"
694725
)
726+
if plot_semilogy:
727+
eig_axes[0].semilogy()
695728

696-
# Plots 2-3: Plot the eigenvalues (discrete-time and continuous-time).
729+
# PLOTS 2-3: Plot the eigenvalues (discrete-time and continuous-time).
697730
# Scale marker sizes to reflect the amount of variance captured.
698-
ms_vals = max_marker_size * np.sqrt(s_var / s_var[0])
731+
ms_vals = max_eig_ms * np.sqrt(s_var / s_var[0])
699732
for i, (ax, eigs) in enumerate(zip(eig_axes[1:], [disc_eigs, cont_eigs])):
700733
# Plot the complex plane axes.
701-
ax.axvline(x=0, c="k", lw=1)
702-
ax.axhline(y=0, c="k", lw=1)
734+
ax.axvline(x=0, c="k")
735+
ax.axhline(y=0, c="k")
703736
ax.axis("equal")
704-
# Plot 2: Plot the discrete-time eigenvalues with the unit circle.
737+
# PLOT 2: Plot the discrete-time eigenvalues with the unit circle.
705738
if i == 0:
706739
ax.set_title("Discrete-time Eigenvalues")
707740
t = np.linspace(0, 2 * np.pi, 100)
708741
ax.plot(np.cos(t), np.sin(t), c="tab:blue", ls="--")
709742
ax.set_xlabel("Real")
710743
ax.set_ylabel("Imag")
711-
# Plot 3: Plot the continuous-time eigenvalues
744+
# PLOT 3: Plot the continuous-time eigenvalues.
712745
else:
713746
ax.set_title("Continuous-time Eigenvalues")
714747
ax.set_xlabel("Imag")
@@ -724,10 +757,10 @@ def plot_summary(
724757
else:
725758
ax.plot(eig.imag, eig.real, "o", c=color, ms=ms_vals[idx])
726759

727-
# Plots 4-6: Plot the DMD modes.
760+
# PLOTS 4-6: Plot the DMD modes.
728761
for i, idx in enumerate(index_modes):
729762
ax = mode_axes[i]
730-
ax.set_title(f"Mode {idx + 1}", c=main_colors[i], fontsize=15)
763+
ax.set_title(f"Mode {idx + 1}", c=main_colors[i])
731764
# Plot modes in 1D.
732765
if len(snapshots_shape) == 1:
733766
ax.plot(lead_modes[:, idx].real, c=mode_color)
@@ -741,16 +774,21 @@ def plot_summary(
741774
divider = make_axes_locatable(ax)
742775
cax = divider.append_axes("right", size="3%", pad=0.05)
743776
fig.colorbar(im, cax=cax)
777+
if remove_cmap_ticks:
778+
ax.set_xticks([])
779+
ax.set_yticks([])
744780

745-
# Plots 7-9: Plot the DMD mode dynamics.
781+
# PLOTS 7-9: Plot the DMD mode dynamics.
746782
for i, idx in enumerate(index_modes):
747783
ax = dynamics_axes[i]
748-
ax.set_title("Mode Dynamics", c=main_colors[i], fontsize=12)
749-
ax.plot(lead_dynamics[idx].real, c=dynamics_color)
784+
ax.set_title("Mode Dynamics", c=main_colors[i])
785+
ax.plot(time, lead_dynamics[idx].real, c=dynamics_color)
750786
ax.set_xlabel("Time")
751787

752788
# Padding between elements.
753-
plt.tight_layout()
789+
if tight_layout_kwargs is None:
790+
tight_layout_kwargs = {}
791+
plt.tight_layout(**tight_layout_kwargs)
754792

755793
# Save plot if filename is provided.
756794
if filename:

0 commit comments

Comments
 (0)