1414from .bopdmd import BOPDMD
1515from .hankeldmd import HankelDMD
1616from .havok import HAVOK
17+ from .preprocessing import PrePostProcessingDMD
1718
1819mpl .rcParams ["figure.max_open_warning" ] = 0
1920
@@ -545,6 +546,11 @@ def plot_summary(
545546 mode_color = "k" ,
546547 mode_cmap = "bwr" ,
547548 dynamics_color = "tab:blue" ,
549+ sval_ms = 8 ,
550+ max_eig_ms = 10 ,
551+ max_sval_plot = 50 ,
552+ title_fontsize = 14 ,
553+ label_fontsize = 12 ,
548554 plot_semilogy = False ,
549555 remove_cmap_ticks = False ,
550556):
@@ -564,7 +570,7 @@ def plot_summary(
564570 are continuous-time. If `False`, the eigenvalues are assumed to be the
565571 discrete-time eigenvalues. If `True`, the eigenvalues are taken to be
566572 the continuous-time eigenvalues. Note that `continuous` is
567- automatically assumed to be true if a `BOPDMD` model is given.
573+ automatically assumed to be `True` if a `BOPDMD` model is given.
568574 :type continuous: bool
569575 :param snapshots_shape: Shape of the snapshots. If not provided, the shape
570576 of the snapshots and modes is assumed to be the flattened space dim of
@@ -607,24 +613,25 @@ def plot_summary(
607613 :type mode_cmap: str
608614 :param dynamics_color: Color used to plot the dynamics.
609615 :type dynamics_color: str
616+ :param sval_ms: Marker size of all singular values.
617+ :type sval_ms: int
618+ :param max_eig_ms: Marker size of the most prominent eigenvalue.
619+ :type max_eig_ms: int
620+ :param max_sval_plot: Maximum number of singular values to plot.
621+ :type max_sval_plot: int
622+ :param title_fontsize: Fontsize used for subplot titles.
623+ :type title_fontsize: int
624+ :param label_fontsize: Fontsize used for axis labels.
625+ :type label_fontsize: int
610626 :param plot_semilogy: Whether or not to plot the singular values on a
611627 semilogy plot. If `True`, a semilogy plot is used.
612628 :type plot_semilogy: bool
613629 :param remove_cmap_ticks: Whether or not to include the ticks on 2D mode
614630 plots. If `True`, ticks are removed from all 2D mode plots.
615631 :type remove_cmap_ticks: bool
616632 """
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
627633
634+ # This plotting method is inappropriate for plotting HAVOK results.
628635 if isinstance (dmd , HAVOK ):
629636 raise ValueError ("You should use HAVOK.plot_summary() instead." )
630637
@@ -656,9 +663,9 @@ def plot_summary(
656663 elif not isinstance (index_modes , list ) or len (index_modes ) > 3 :
657664 raise ValueError ("index_modes must be a list of length at most 3." )
658665 # Indices cannot go past the total number of available or plottable modes.
659- elif np .any (np .array (index_modes ) >= min (len (dmd .eigs ), max_plot )):
666+ elif np .any (np .array (index_modes ) >= min (len (dmd .eigs ), max_sval_plot )):
660667 raise ValueError (
661- f"Cannot view past mode { min (len (dmd .eigs ), max_plot )} ."
668+ f"Cannot view past mode { min (len (dmd .eigs ), max_sval_plot )} ."
662669 )
663670
664671 # Sort eigenvalues, modes, and dynamics according to amplitude magnitude.
@@ -667,26 +674,34 @@ def plot_summary(
667674 lead_modes = dmd .modes [:, mode_order ]
668675 lead_dynamics = dmd .dynamics [mode_order ]
669676
670- # Get time step information for eigenvalue conversions.
671- if isinstance (dmd , BOPDMD ):
677+ # Get time information for eigenvalue conversions.
678+ if isinstance (dmd , BOPDMD ) or (
679+ isinstance (dmd , PrePostProcessingDMD )
680+ and isinstance (dmd .pre_post_processed_dmd , BOPDMD )
681+ ):
672682 # BOPDMD models store time in the time attribute.
683+ # BOPDMD models also always compute continuous-time eigenvalues.
684+ cont_eigs = lead_eigs
673685 time = dmd .time
674686 dt = dmd .time [1 ] - dmd .time [0 ]
675687 if not np .allclose (dmd .time [1 :] - dmd .time [:- 1 ], dt ):
676- print (
688+ warnings . warn (
677689 "Time step is not uniform. "
678690 "No discrete-time eigenvalues to plot..."
679691 )
680692 disc_eigs = None
681693 else :
682694 disc_eigs = np .exp (lead_eigs * dt )
683- cont_eigs = lead_eigs
684695 else :
696+ # For all other dmd models, go to the TimeDict for time information.
685697 try :
686- time = dmd .original_timesteps
698+ time = dmd .dmd_timesteps
687699 dt = dmd .original_time ["dt" ]
688700 except AttributeError :
689- warnings .warn ("No time step information available. Using dt = 1." )
701+ warnings .warn (
702+ "No time step information available. "
703+ "Using dt = 1 and t0 = 0."
704+ )
690705 time = np .arange (dmd .snapshots .shape [- 1 ])
691706 dt = 1.0
692707
@@ -714,14 +729,21 @@ def plot_summary(
714729 )
715730
716731 # PLOT 1: Plot the singular value spectrum.
717- s_var_plot = s_var [:max_plot ]
718- eig_axes [0 ].set_title ("Singular Values" )
719- eig_axes [0 ].set_ylabel ("% variance" )
720- t = np .arange (len (s_var_plot )) + 1
721- eig_axes [0 ].plot (t , s_var_plot , "o" , c = main_colors [- 1 ], ms = sval_ms , mec = "k" )
732+ s_var_plot = s_var [:max_sval_plot ]
733+ eig_axes [0 ].set_title ("Singular Values" , fontsize = title_fontsize )
734+ eig_axes [0 ].set_ylabel ("% variance" , fontsize = label_fontsize )
735+ s_t = np .arange (len (s_var_plot )) + 1
736+ eig_axes [0 ].plot (
737+ s_t , s_var_plot , "o" , c = main_colors [- 1 ], ms = sval_ms , mec = "k"
738+ )
722739 for i , idx in enumerate (index_modes ):
723740 eig_axes [0 ].plot (
724- t [idx ], s_var_plot [idx ], "o" , c = main_colors [i ], ms = sval_ms , mec = "k"
741+ s_t [idx ],
742+ s_var_plot [idx ],
743+ "o" ,
744+ c = main_colors [i ],
745+ ms = sval_ms ,
746+ mec = "k" ,
725747 )
726748 if plot_semilogy :
727749 eig_axes [0 ].semilogy ()
@@ -731,44 +753,46 @@ def plot_summary(
731753 ms_vals = max_eig_ms * np .sqrt (s_var / s_var [0 ])
732754 for i , (ax , eigs ) in enumerate (zip (eig_axes [1 :], [disc_eigs , cont_eigs ])):
733755 # Plot the complex plane axes.
734- ax .axvline (x = 0 , c = "k" )
735- ax .axhline (y = 0 , c = "k" )
756+ ax .axvline (x = 0 , c = "k" , lw = 1 )
757+ ax .axhline (y = 0 , c = "k" , lw = 1 )
736758 ax .axis ("equal" )
737- # PLOT 2: Plot the discrete-time eigenvalues with the unit circle.
759+ # PLOT 2: Plot the discrete-time eigenvalues on the unit circle.
738760 if i == 0 :
739- ax .set_title ("Discrete-time Eigenvalues" )
761+ ax .set_title ("Discrete-time Eigenvalues" , fontsize = title_fontsize )
740762 t = np .linspace (0 , 2 * np .pi , 100 )
741763 ax .plot (np .cos (t ), np .sin (t ), c = "tab:blue" , ls = "--" )
742- ax .set_xlabel ("Real" )
743- ax .set_ylabel ("Imag" )
764+ ax .set_xlabel (r"$Re(\lambda)$" , fontsize = label_fontsize )
765+ ax .set_ylabel (r"$Im(\lambda)$" , fontsize = label_fontsize )
744766 # PLOT 3: Plot the continuous-time eigenvalues.
745767 else :
746- ax .set_title ("Continuous-time Eigenvalues" )
747- ax .set_xlabel ("Imag" )
748- ax .set_ylabel ("Real" )
749- # Plot the eigenvalues.
750- for idx , eig in enumerate (eigs ):
751- if idx in index_modes :
752- color = main_colors [index_modes .index (idx )]
753- else :
754- color = main_colors [- 1 ]
755- if i == 0 :
756- ax .plot (eig .real , eig .imag , "o" , c = color , ms = ms_vals [idx ])
757- else :
758- ax .plot (eig .imag , eig .real , "o" , c = color , ms = ms_vals [idx ])
768+ ax .set_title ("Continuous-time Eigenvalues" , fontsize = title_fontsize )
769+ ax .set_xlabel (r"$Im(\omega)$" , fontsize = label_fontsize )
770+ ax .set_ylabel (r"$Re(\omega)$" , fontsize = label_fontsize )
771+ # Plot the eigenvalues (discrete or continuous).
772+ if eigs is not None :
773+ for idx , eig in enumerate (eigs ):
774+ if idx in index_modes :
775+ color = main_colors [index_modes .index (idx )]
776+ else :
777+ color = main_colors [- 1 ]
778+ if i == 0 :
779+ ax .plot (eig .real , eig .imag , "o" , c = color , ms = ms_vals [idx ])
780+ else :
781+ ax .plot (eig .imag , eig .real , "o" , c = color , ms = ms_vals [idx ])
759782
760783 # PLOTS 4-6: Plot the DMD modes.
761- for i , idx in enumerate (index_modes ):
762- ax = mode_axes [i ]
763- ax .set_title (f"Mode { idx + 1 } " , c = main_colors [i ])
784+ for i , (ax , idx ) in enumerate (zip (mode_axes , index_modes )):
785+ ax .set_title (
786+ f"Mode { idx + 1 } " , c = main_colors [i ], fontsize = title_fontsize
787+ )
764788 # Plot modes in 1D.
765789 if len (snapshots_shape ) == 1 :
766790 ax .plot (lead_modes [:, idx ].real , c = mode_color )
767791 # Plot modes in 2D.
768792 else :
769793 mode = lead_modes [:, idx ].reshape (* snapshots_shape , order = order )
770- # Multiply by factor of vmax_scale to intensify the plotted image.
771- vmax = vmax_scale * np .abs (mode .real ).max ()
794+ # Multiply by factor of 0.9 to intensify the plotted image.
795+ vmax = 0.9 * np .abs (mode .real ).max ()
772796 im = ax .imshow (mode .real , vmax = vmax , vmin = - vmax , cmap = mode_cmap )
773797 # Align the colorbar with the plotted image.
774798 divider = make_axes_locatable (ax )
@@ -779,11 +803,15 @@ def plot_summary(
779803 ax .set_yticks ([])
780804
781805 # PLOTS 7-9: Plot the DMD mode dynamics.
782- for i , idx in enumerate (index_modes ):
783- ax = dynamics_axes [i ]
784- ax .set_title ("Mode Dynamics" , c = main_colors [i ])
785- ax .plot (time , lead_dynamics [idx ].real , c = dynamics_color )
786- ax .set_xlabel ("Time" )
806+ for i , (ax , idx ) in enumerate (zip (dynamics_axes , index_modes )):
807+ dynamics_data = lead_dynamics [idx ].real
808+ ax .set_title ("Mode Dynamics" , c = main_colors [i ], fontsize = title_fontsize )
809+ ax .plot (time , dynamics_data , c = dynamics_color )
810+ ax .set_xlabel ("Time" , fontsize = label_fontsize )
811+ dynamics_range = dynamics_data .max () - dynamics_data .min ()
812+ # Re-adjust ylim if dynamics oscillations are extremely small.
813+ if dynamics_range / np .abs (np .average (dynamics_data )) < 1e-4 :
814+ ax .set_ylim ([0.0 , 2 * np .average (dynamics_data )])
787815
788816 # Padding between elements.
789817 if tight_layout_kwargs is None :
0 commit comments