5 import matplotlib.pyplot
as plt
6 import matplotlib
as mpl
7 import matplotlib.patches
as patches
8 import matplotlib.patheffects
as PathEffects
9 from matplotlib.collections
import PatchCollection
11 from matplotlib
import cm
12 from matplotlib.patches
import Arrow, FancyBboxPatch
13 from matplotlib.colors
import LogNorm, SymLogNorm
14 from matplotlib.colors
import ListedColormap, LinearSegmentedColormap
15 from matplotlib.animation
import FuncAnimation
16 from matplotlib.widgets
import Slider, Button
17 from wreader
import wreader
19 from nucleus_multiple_class
import nucleus_multiple
20 from ngamma_eq
import ngamma_eq
21 from winvn_class
import winvn
27 Class to create an animation of the abundances and their flow for the nuclear reaction network WinNet.
41 separate_fission = True,
42 fission_minflow = 1e-10,
44 flow_adapt_prange = True,
46 flow_adapt_width = True,
47 flow_maxArrowWidth= 2.0,
48 flow_minArrowWidth= 0.3,
49 cmapNameFlow = 'viridis',
54 cmapNameX = 'inferno',
58 addMassBinLabels = True,
60 cmapNameMassBins = 'jet',
61 massBins = [[1,1],[2,71],[72,93],[94,110],[111,144],[145,169],[170,187],[188,205],[206,252],[253,337]],
62 massBinLabels = ['','','1st peak','','2nd peak','rare earths', '', '3rd peak', '', 'fissioning'],
66 additional_plot = 'none',
68 trackedrange = (1e-8, 1),
70 amainoutrange = (5e-10, 1),
72 energyrange = (1e10, 1e20),
74 timescalerange = (1e-12, 1e10),
75 timerange = (1e-5 , 1e5),
78 densityrange = (1e-5, 1e12),
79 temperaturerange = (0, 10),
80 yerange = (0.0, 0.55),
82 indicate_r_path =
False,
90 Path to the WinNet data.
91 fig : matplotlib.figure.Figure
92 Figure to plot the animation on.
94 Folder to save the frames in, default is path/frames.
96 Plot the flow of the abundances.
98 Minimum value for the flow.
100 Maximum value for the flow.
101 separate_fission : bool
102 Separate the fissioning region from the fission products in the flow plot.
103 fission_minflow : float
104 Minimum value to indicate a fission region.
106 Plot the colorbar for the flow.
107 flow_adapt_prange : bool
108 Adapt the color range of the flow to the data.
110 Range (in log10) of the flow.
111 flow_adapt_width : bool
112 Adapt the width of the flow arrows to the flow.
113 flow_maxArrowWidth : float
114 Maximum width of the flow arrows.
115 flow_minArrowWidth : float
116 Minimum width of the flow arrows.
118 Name of the colormap for the flow.
120 Plot the colorbar for the mass fractions.
122 Minimum value for the mass fractions.
124 Maximum value for the mass fractions.
126 Name of the colormap for the mass fractions.
128 Plot the average mass number.
131 addMassBinLabels : bool
132 Add labels to the mass bins.
133 alphaMassBins : float
134 Transparency of the mass bins.
135 cmapNameMassBins : str
136 Name of the colormap for the mass bin color.
140 List of labels for the mass bins.
142 Plot the magic numbers in the abundance plot.
143 additional_plot : str
144 Additional plot to be made, possible values: 'None', 'timescales', 'energy', 'tracked'.
146 Range of the tracked nuclei plot.
147 amainoutrange : tuple
148 Range of the additional mainout plot.
150 Range of the energy axis.
151 timescalerange : tuple
152 Range of the timescales.
154 Range of the time axis.
156 Plot the mainout data.
158 Range of the density axis in the mainout plot.
159 temperaturerange : tuple
160 Range of the temperature axis in the mainout plot.
162 Range of the electron fraction axis in the mainout plot.
164 Plot the WinNet logo.
165 indicate_r_path : bool
166 Indicate the r-process path.
168 Path to the winvn file in case r-process path should be indicated.
170 Enable interactive mode.
175 if (mpl.__version__ <
'3.8.0'):
176 print(
'Using old version of Matplotlib ('+str(mpl.__version__)+
'), some features may not work.')
177 print(
'Need 3.8 or higher.')
183 self.
__script_path = os.path.dirname(os.path.abspath(__file__))
186 if frame_dir
is None:
265 raise ValueError(f
"Additional plot {self.additional_plot} not recognized. Possible values: 'None', 'timescales', 'energy', 'tracked'")
271 self.N_stab, self.
Z_stab = np.loadtxt(os.path.join(self.
__data_path,
'../../../class_files/data/stableiso.dat'),
272 unpack=
True, usecols=(1, 2), dtype=int)
275 sunet_path = os.path.join(self.
__data_path,
"sunet_really_complete")
276 nuclei_names = np.loadtxt(sunet_path,dtype=str)
286 self.
nMagic = [8, 20, 28, 50, 82, 126, 184]
287 self.
zMagic = [8, 20, 28, 50, 82, 114]
292 self.
timescale_entries = [[
'ag',
'ga'],[
'ng',
'gn'],[
'an',
'na'],[
'np',
'pn'],[
'pg',
'gp'],[
'ap',
'pa'],[
"beta"],[
"bfiss"],[
"nfiss"],[
"sfiss"]]
293 self.
timescale_labels = [
r"$\tau_{\alpha,\gamma}$",
r"$\tau_{n,\gamma}$",
r"$\tau_{\alpha,n}$",
r"$\tau_{n,p}$",
r"$\tau_{p,\gamma}$",
294 r"$\tau_{\alpha,p}$",
r"$\tau_{\beta}$",
r"$\tau_{\rm{bfiss}}$",
r"$\tau_{\rm{nfiss}}$",
r"$\tau_{\rm{sfiss}}$"]
297 self.
energy_colors = [
"k",
"C2",
"C3",
"C4",
"C5",
"C6",
"C7",
"C8",
"C9",
"C0"]
298 self.
energy_entries = [
'tot',
'ag_ga',
'ng_gn',
'an_na',
'np_pn',
'pg_gp',
'ap_pa',
'beta',
'fiss']
299 self.
energy_labels = [
'Total',
r"$( \alpha,\gamma )$",
r"$( n,\gamma )$",
r"$( \alpha,n )$",
r"$( n,p )$",
300 r"$( p,\gamma )$",
r"$( \alpha,p )$",
r"$\beta$",
r"$\rm{fission}$"]
305 self.
flow_norm = LogNorm(flow_min, flow_max, clip=
True)
343 self.
winvn.calculate_Sn()
344 df = self.
winvn.get_dataframe()
348 df.set_index([
'N',
'Z'], inplace=
True)
349 self.
winvn.set_dataframe(df)
360 Initialize the ngamma_eq class.
369 sunet_path = self.
wreader.template[
'net_source']
370 nuclei_names = np.loadtxt(sunet_path,dtype=str)
375 for Z
in np.unique(nm.Z):
381 mask = np.where(diff > 1)[0]
384 line = self.
ax.plot([np.min(Ns)-0.5, np.min(Ns)-0.5], [Z-0.5, Z+0.5], color=
'red', zorder=1000, lw=1)
386 line = self.
ax.plot([np.max(Ns)+0.5, np.max(Ns)+0.5], [Z-0.5, Z+0.5], color=
'red', zorder=1000, lw=1)
390 line = self.
ax.plot([Ns[i]+0.5, Ns[i]+0.5], [Z-0.5, Z+0.5], color=
'red', zorder=1000, lw=1)
392 line = self.
ax.plot([Ns[i+1]-0.5, Ns[i+1]-0.5], [Z-0.5, Z+0.5], color=
'red', zorder=1000, lw=1)
396 for N
in np.unique(nm.N):
402 mask = np.where(diff > 1)[0]
405 line = self.
ax.plot([N-0.5, N+0.5], [np.min(Zs)-0.5, np.min(Zs)-0.5], color=
'red', zorder=1000, lw=1)
407 line = self.
ax.plot([N-0.5, N+0.5], [np.max(Zs)+0.5, np.max(Zs)+0.5], color=
'red', zorder=1000, lw=1)
411 line = self.
ax.plot([N-0.5, N+0.5], [Zs[i]+0.5, Zs[i]+0.5], color=
'red', zorder=1000, lw=1)
413 line = self.
ax.plot([N-0.5, N+0.5], [Zs[i+1]-0.5, Zs[i+1]-0.5], color=
'red', zorder=1000, lw=1)
427 Initialize the axes and everything figure related of the plot.
430 mpl.rcParams[
'hatch.linewidth'] = 0.8
434 self.
ax.set_aspect(
'equal')
474 self.
ax.add_patch(patches.Rectangle((0.3, 0.84), 0.8, 0.15, fill=
True, color=
'w', zorder=100, transform=self.
fig.transFigure))
475 self.
ax.add_patch(patches.Rectangle((0.15, 0.745), 0.395, 0.2, fill=
True, color=
'w', zorder=100, transform=self.
fig.transFigure))
481 Initialize the axes and everything figure related of the nuclear chart.
484 self.
ax.xaxis.set_visible(
False)
485 self.
ax.yaxis.set_visible(
False)
486 self.
ax.spines[[
'right',
'top',
"bottom",
"left"]].set_visible(
False)
491 Initialize the axes and everything figure related of the mass fraction plot.
494 self.
axAbund = plt.axes([0.15,0.78,0.35,0.15])
495 self.
axAbund.set_xlabel(
r'Mass number $A$')
496 self.
axAbund.set_ylabel(
r'X(A)')
501 self.
axAbund.axvline(np.nan, color=
'tab:red',label=
r"$\bar{A}$")
502 self.
axAbund.legend(loc=
'upper right')
513 Initialize the axes and everything figure related of the timescales plot.
525 Initialize the axes and everything figure related of the tracked nuclei plot.
529 self.
axTracked.set_ylabel(
'Mass fractions')
537 Initialize the axes and everything figure related of the energy plot.
540 self.
axEnergy.set_xlabel(
'Time [s]')
541 self.
axEnergy.set_ylabel(
'Energy [erg/g/s]')
549 Initialize the axes and everything figure related of the energy plot.
561 Initialize the axes and everything figure related of the mainout plot.
563 def make_patch_spines_invisible(ax):
564 ax.set_frame_on(
True)
565 ax.patch.set_visible(
False)
566 for sp
in ax.spines.values():
567 sp.set_visible(
False)
571 self.
axMainout.set_ylabel(
r'Density [g/cm$^3$]')
596 self.
axMainout_ye.yaxis.set_tick_params(colors=
"tab:blue")
601 Initialize the axes and everything figure related of the WinNet logo.
603 self.
axLogo = plt.axes([0.75,0.45,0.15,0.15])
609 self.
ax_slider = plt.axes([0.18, 0.08, 0.72, 0.02], facecolor=
'lightgoldenrodyellow')
610 self.
ax_button = plt.axes([0.18-0.018, 0.08, 0.012, 0.022])
621 min_val = 1-self.
wreader.mainout[
'yn']/self.
wreader.mainout[
'yheavy']
622 nfreezeout = np.argmin(abs(min_val))
624 if (min_val[nfreezeout-1] < 0)
and (min_val[nfreezeout+1] > 0):
626 self.
ax_slider.axvline(nfreezeout, color=
'tab:red', linestyle=
'-', linewidth=1)
631 if self.
wreader.check_existence(
'tracked_nuclei') !=0:
633 self.
toggle_buttons.append(Button(plt.axes([0.18-0.018, 0.05, 0.012, 0.022]),
"⚛"))
639 if self.
wreader.check_existence(
'timescales') !=0:
642 self.
toggle_buttons.append(Button(plt.axes([0.18-0.018+0.015*amount_buttons, 0.05, 0.012, 0.022]),
r"$\tau$"))
647 if self.
wreader.check_existence(
'energy') !=0:
650 self.
toggle_buttons.append(Button(plt.axes([0.18-0.018+0.015*amount_buttons, 0.05, 0.012, 0.022]),
"⚡"))
655 if self.
wreader.check_existence(
'mainout') !=0:
658 self.
toggle_buttons.append(Button(plt.axes([0.18-0.018+0.015*amount_buttons, 0.05, 0.012, 0.022]),
"m"))
672 supath = self.
wreader.template[
'net_source']
674 if os.path.exists(supath):
680 if self.
wreader.check_existence(
'mainout') !=0:
704 self.
flow_buttons.append(Button(plt.axes([0.869, 0.905, 0.01, 0.02]),
"-"))
705 self.
flow_buttons.append(Button(plt.axes([0.7605, 0.905, 0.01, 0.02]),
"+"))
706 self.
flow_buttons.append(Button(plt.axes([0.75, 0.905, 0.01, 0.02]),
"-"))
708 self.
flow_buttons.append(Button(plt.axes([0.771, 0.905, 0.01, 0.02]),
"⟲"))
710 self.
flow_buttons.append(Button(plt.axes([0.858, 0.905, 0.01, 0.02]),
"○"))
713 f.label.set_fontsize(12)
714 f.label.set_color(
'k')
719 self.
mafra_buttons.append(Button(plt.axes([0.699, 0.905, 0.01, 0.02]),
"-"))
720 self.
mafra_buttons.append(Button(plt.axes([0.5905, 0.905, 0.01, 0.02]),
"+"))
721 self.
mafra_buttons.append(Button(plt.axes([0.58, 0.905, 0.01, 0.02]),
"-"))
723 self.
mafra_buttons.append(Button(plt.axes([0.601, 0.905, 0.01, 0.02]),
"⟲"))
725 self.
mafra_buttons.append(Button(plt.axes([0.688, 0.905, 0.01, 0.02]),
"○"))
728 self.
mafra_buttons = [Button(plt.axes([0.88, 0.905, 0.01, 0.02]),
"+")]
729 self.
mafra_buttons.append(Button(plt.axes([0.869, 0.905, 0.01, 0.02]),
"-"))
730 self.
mafra_buttons.append(Button(plt.axes([0.7605, 0.905, 0.01, 0.02]),
"+"))
731 self.
mafra_buttons.append(Button(plt.axes([0.75, 0.905, 0.01, 0.02]),
"-"))
733 self.
mafra_buttons.append(Button(plt.axes([0.771, 0.905, 0.01, 0.02]),
"⟲"))
735 self.
mafra_buttons.append(Button(plt.axes([0.858, 0.905, 0.01, 0.02]),
"○"))
738 f.label.set_fontsize(12)
739 f.label.set_color(
'k')
754 self.
cb_bg.ax.set_visible(
False)
755 self.
logo.set_visible(
True)
759 self.
cb_bg.set_label(
'BE/A [MeV]')
763 self.
cb_bg.ax.set_visible(
True)
765 self.
logo.set_visible(
False)
770 original_cmap = cm.nipy_spectral
774 colors = original_cmap(np.linspace(0, 1, original_cmap.N))
775 colors[:, -1] = alpha
776 transparent_cmap = ListedColormap(colors)
779 self.
cb_bg.set_label(
'Sn [MeV]')
783 self.
cb_bg.ax.set_visible(
True)
785 self.
logo.set_visible(
False)
792 self.
fig.canvas.draw_idle()
820 self.
fig.canvas.draw_idle()
846 self.
fig.canvas.draw_idle()
858 self.
fig.canvas.draw_idle()
865 self.
fig.canvas.draw_idle()
878 if event.inaxes == button.ax:
883 for t
in [
'top',
'right',
'bottom',
'left']:
888 for t
in [
'top',
'right',
'bottom',
'left']:
889 button.ax.spines[t].set_color(
'red')
890 button.ax.spines[t].set_linewidth(2)
905 for t
in [
'top',
'right',
'bottom',
'left']:
906 button.ax.spines[t].set_color(
'k')
907 button.ax.spines[t].set_linewidth(0.5)
916 self.
fig.canvas.draw_idle()
922 Initialize the data for the plot.
973 amount_mass_bins = len(self.
massBins)
974 massbin_colors = massbin_colormap(np.linspace(0, 1, amount_mass_bins))
980 self.
values = np.linspace(0, 1,num=amount_mass_bins,endpoint=
True)
984 background_Y[:,:] = np.nan
985 for index, mbin
in enumerate(self.
massBins):
1010 Initialize the plots.
1014 vmax=(max(self.
values)),linewidth=0.0,edgecolor=
"face")
1019 vmax=(np.log10(self.
X_max)),linewidth=0.0,edgecolor=
"face")
1029 cmap=self.
cmapNameFlow, angles=
'xy', scale_units=
'xy', scale=1,
1030 units=
'xy', width=0.1, headwidth=3, headlength=4
1036 with np.errstate(divide=
'ignore'):
1037 arrowwidth = (np.log10(self.
flow)-np.log10(self.
flow_min))*width
1041 flow_arrows = [Arrow(self.
flow_N[i],self.
flow_Z[i],self.
flow_dn[i],self.
flow_dz[i],width=arrowwidth[i],color=
'k')
for i
in range(len(self.
flow))]
1043 a.set_array(self.
flow)
1050 fisspos,hatch=
'//////', edgecolor=
'tab:red',facecolor=
'none',
1051 linewidth=0.0,zorder=1000
1058 fissneg,hatch=
'//////', edgecolor=
'tab:blue',facecolor=
'none',
1059 linewidth=0.0,zorder=1000
1064 edgecolors = np.full((max(self.
n+1),max(self.
z+1)),
"none")
1065 edgecolors[self.N_stab, self.
Z_stab] =
"k"
1066 edgecolors = edgecolors.T.ravel()
1068 facecolor=
"none",linewidth=0.5,edgecolor=edgecolors)
1082 for i,b
in enumerate(self.
massBins):
1085 axis_to_data = self.
axAbund.transAxes + self.
axAbund.transData.inverted()
1086 data_to_axis = axis_to_data.inverted()
1087 trans = data_to_axis.transform((px,py))
1089 ha=
'left',va=
'center',clip_on=
False, fontsize=8,color=self.
massbin_colors[i])
1090 txt.set_path_effects([PathEffects.withStroke(linewidth=0.5, foreground=
'k')])
1103 self.
axMainout.legend(lines, [l.get_label()
for l
in lines],loc=
'upper right')
1106 left_side =
"$t$"+
"\n"+rf
"$\rho$"+
"\n"+rf
"$T_9$"+
"\n"+rf
"$Y_e$"
1107 right_side = f
"= {self.format_time(self.mainout_time[-1])[0]}\n"+\
1108 f
"= {self.to_latex_exponent(self.mainout_density[-1])}\n"+\
1109 f
"= {self.to_latex_exponent(self.mainout_temperature[-1])}\n"+\
1110 f
"= {self.mainout_ye[-1]:.3f}"
1111 units = f
"{self.format_time(self.mainout_time[-1])[1]}\n"+\
1118 rect = patches.FancyBboxPatch((0.35, y_pos-0.01), 0.17, 0.135, transform=self.
ax.transAxes, boxstyle=
"round,pad=0.01", ec=
"k", fc=
"lightgrey", zorder=1,alpha=0.5)
1119 self.
ax.add_patch(rect)
1122 self.
ax.text(0.35, y_pos, left_side, transform=self.
ax.transAxes, fontsize=12,)
1123 self.
Mainout_text = self.
ax.text(0.37, y_pos, right_side, transform=self.
ax.transAxes, fontsize=12,)
1129 self.
ax.arrow(-8, -8, arrowLength, 0, head_width=2, head_length=2, fc=
'k', ec=
'k')
1130 self.
ax.arrow(-8, -8, 0, arrowLength, head_width=2, head_length=2, fc=
'k', ec=
'k')
1131 self.
ax.text(arrowLength-8+3,0-8,
'N',horizontalalignment=
'left',verticalalignment=
'center',fontsize=14,clip_on=
True)
1132 self.
ax.text(0-8,arrowLength-8+3,
'Z',horizontalalignment=
'center',verticalalignment=
'bottom',fontsize=14,clip_on=
True)
1159 self.
ax.text(n,zmin-self.
magic_excess-1,int(n),ha=
'center',va=
'top',clip_on=
True)
1169 self.
ax.text(nmin-self.
magic_excess-1,z,int(z),ha=
'right',va=
'center',clip_on=
True)
1174 ratio = self.
fig.get_figwidth()/self.
fig.get_figheight()
1175 self.
ax.add_patch(mpl.patches.Rectangle((0.88, 0.70), 0.01, 0.01*ratio, fill=
False, transform=self.
ax.transAxes, hatch=
"////", edgecolor=
'tab:blue', lw=1))
1176 self.
ax.text(0.9, 0.70,
'Fissioning region', transform=self.
ax.transAxes, fontsize=8, verticalalignment=
'bottom', horizontalalignment=
'left')
1177 self.
ax.add_patch(mpl.patches.Rectangle((0.88, 0.67), 0.01, 0.01*ratio, fill=
False, transform=self.
ax.transAxes, hatch=
"////", edgecolor=
'tab:red', lw=1))
1178 self.
ax.text(0.9, 0.67,
'Fission products', transform=self.
ax.transAxes, fontsize=8, verticalalignment=
'bottom', horizontalalignment=
'left')
1187 self.
axTimescales.legend(loc=
'upper right', ncol=2, bbox_to_anchor=(1.3, 1.0), frameon=
True, facecolor=
'white', edgecolor=
'black', framealpha=1.0, fontsize=8)
1193 self.
axAddMainout.legend(loc=
'upper right', ncol=1, bbox_to_anchor=(1.15, 1.0), frameon=
True, facecolor=
'white', edgecolor=
'black', framealpha=1.0, fontsize=8)
1200 self.
axEnergy.legend(loc=
'upper right', ncol=2, bbox_to_anchor=(1.3, 1.0), frameon=
True, facecolor=
'white', edgecolor=
'black', framealpha=1.0, fontsize=8)
1206 self.
axTracked.legend(loc=
'upper right', ncol=2, bbox_to_anchor=(1.3, 1.0), frameon=
True, facecolor=
'white', edgecolor=
'black', framealpha=1.0, fontsize=8)
1212 Initialize the colorbars.
1215 n_cbars = abun_cbar + flow_cbar
1220 self.
cax = [self.
fig.add_axes([0.75, 0.88, 0.14, 0.02])]
1222 self.
cax = [self.
fig.add_axes([0.58, 0.88, 0.14, 0.02]),
1223 self.
fig.add_axes([0.75, 0.88, 0.14, 0.02])]
1232 cax=self.
cax[ii], orientation=
'horizontal', label=
'')
1233 self.
abun_cbar.ax.set_title(
'Mass fraction')
1241 orientation=
'horizontal',
1251 self.
cb_bg.set_label(
'BE/A [MeV]')
1253 self.
cb_bg.ax.set_visible(
False)
1259 Update the data for the plot.
1266 self.
abun[:,:] = np.nan
1268 self.
abun[self.
N[mask], self.
Z[mask]] = np.log10(xx[mask])
1271 self.
Abar = 1.0/np.sum(yy)
1273 for i
in range(len(self.
massBins)):
1275 self.
Xbins[i] = np.sum(xx[mask])
1304 fpath=f
'{self.path}/WinNet_data.h5'
1306 nin = flow_dict[
'n_in']
1307 zin = flow_dict[
'p_in']
1308 nout = flow_dict[
'n_out']
1309 zout = flow_dict[
'p_out']
1310 flow = flow_dict[
'flow']
1318 self.
flow = flow[mask]
1338 self.
addmainout_label = [
r"Y$_n$",
r"Y$_p$",
r"Y$_\alpha$",
r"Y$_{\text{heavy}}$",
r"Y$_{\text{light}}$"]
1355 if ii == -1
or force_label_init:
1360 Separate the fissioning region from the fission products in the flow plot.
1364 fis_N = self.
flow_N[fis_mask]
1365 fis_Z = self.
flow_Z[fis_mask]
1366 fis_dn = self.
flow_dn[fis_mask]
1367 fis_dz = self.
flow_dz[fis_mask]
1368 fis_flow = self.
flow[fis_mask]
1376 self.
flow = self.
flow[((~fis_mask) & mask)]
1379 for nn, zz, dn, dz, ff
in zip(fis_N, fis_Z, fis_dn, fis_dz, fis_flow):
1412 right_side = f
"= {self.to_latex_exponent(self.format_time(self.mainout_time[-1])[0])}\n"+\
1413 f
"= {self.to_latex_exponent(self.mainout_density[-1])}\n"+\
1414 f
"= {self.to_latex_exponent(self.mainout_temperature[-1])}\n"+\
1415 f
"= {self.mainout_ye[-1]:.3f}"
1417 units = f
"{self.format_time(self.mainout_time[-1])[1]}\n"+\
1439 self.
flow_cbar.mappable.set_clim(vmin=10**lminflow, vmax=10**lmaxflow)
1454 with np.errstate(divide=
'ignore'):
1456 flow_arrows = [Arrow(self.
flow_N[i],self.
flow_Z[i],self.
flow_dn[i],self.
flow_dz[i],width=arrowwidth[i],color=
'k')
for i
in range(len(self.
flow))]
1458 a.set_array(self.
flow)
1487 text = text.split(
"\n")
1488 N = int(text[1].split(
" = ")[1])
1489 Z = int(text[2].split(
" = ")[1])
1490 X = 10**self.
abun[N,Z]
1493 text[-1] = f
"X = {X:.2e}"
1500 plt.savefig(f
'{self.frame_dir}/frame_{ii}.png',
1501 dpi=300, bbox_inches=
'tight')
1508 frames = frames[::100]
1511 frames=frames, **kwargs)
1542 new_seq = list(range(ii, self.
frames[-1])) + list(range(self.
frames[0], ii))
1543 self.
animation._iter_gen =
lambda: iter(new_seq)
1564 elif event.inaxes == self.
ax:
1565 toolbar = plt.get_current_fig_manager().toolbar
1566 active_tool = toolbar.mode
1567 if active_tool ==
'':
1572 x, y = event.xdata, event.ydata
1573 nucl_n = int(np.round(x))
1574 nucl_z = int(np.round(y))
1597 patches.Rectangle((nucl_n-0.5, nucl_z-0.5), 1, 1, linewidth=1, edgecolor=
'r', facecolor=
'none'))
1600 df = self.
winvn.get_dataframe()
1602 nucl_name = df.loc[(nucl_n, nucl_z),
'name']
1603 text = nucl_name.capitalize() +
"\n"
1604 text+= f
"N = {nucl_n}\nZ = {nucl_z}\nA = {nucl_n+nucl_z}"
1606 X = 10**self.
abun[nucl_n, nucl_z]
1610 text+= f
"\nX = {X:.2e}"
1615 horizontalalignment=
'left',
1616 verticalalignment=
'bottom', bbox=dict(facecolor=
'white',
1617 edgecolor=
'black', boxstyle=
'round,pad=0.5'))
1618 self.
fig.canvas.draw_idle()
1628 if event.key
in [
'left',
'down']:
1633 elif event.key
in [
'right',
'up']:
1638 elif event.key ==
" ":
1640 elif ((event.key ==
"t")
or (event.key ==
"e")
1641 or (event.key ==
'n')
or (event.key ==
'd')
1642 or (event.key ==
'm')):
1647 if event.key ==
"t":
1650 elif event.key ==
'e':
1653 elif event.key ==
'n':
1656 elif event.key ==
'm':
1659 elif event.key ==
"d":
1667 self.
fig.canvas.draw_idle()
1755 self.
ax.set_xlim(-5.5, 100)
1756 self.
ax.set_ylim(-6.5, 50)
1758 self.
fig.canvas.draw_idle()
1765 Convert a number to a latex string with exponent.
1769 exponent = np.floor(np.log10(x))
1770 mantissa = x / 10**exponent
1771 return f
'{mantissa:.2f}'+
r'$\times 10^{'+f
'{int(exponent)}'+
r'}$'
1777 Format a time in seconds to a more human readable format.
1780 return time*1000,
'ms'
1784 return time/60,
'min'
1786 return time/3600,
'h'
1787 if time < 3600*24*365:
1788 return time/86400,
'd'
1789 if time < 3600*24*365*1000:
1790 return time/31536000,
'y'
1791 if time < 3600*24*365*1000*1000:
1792 return time/31536000/1000,
'ky'
1794 return time/31536000_000_000,
'My'
1797 A_unique = np.arange(max(A)+1)
1798 X_sum = np.zeros_like(A_unique,dtype=float)
1799 for a, x
in zip(A,X):
1801 return A_unique, X_sum
1807 if __name__ ==
"__main__":
1808 run_path =
"../Example_NSM_dyn_ejecta_rosswog_hdf5"
1809 fig = plt.figure(figsize=(15, 8))
1815 ani = anim.get_funcanimation(interval=10, frames=range(1, anim.n_timesteps))