winnet_movie.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # Authors: M. Jacobi, J. Kuske, M. Reichert
3 # Slider option added by H. Rose.
4 # Movie script inspired by Skynet (J. Lippuner)
5 import sys
6 import os
7 # Get the path of the script and add it to the path (necessary for the imports)
8 script_path = os.path.dirname(os.path.realpath(__file__))
9 sys.path.append(os.path.join(script_path,'src_files/'))
10 from src_files.FlowAnimation import FlowAnimation
11 from src_files.wreader import wreader
12 from tqdm import tqdm
13 import matplotlib.pyplot as plt
14 import optparse
15 
16 
17 #--- define options ----------------------------------------------------------
18 p = optparse.OptionParser()
19 p.add_option("-i","--input", action="store", dest="rundir", default='.', \
20  help="Simulation directory to visualize (default: current directory)")
21 p.add_option("--disable_flow", action="store_true", dest="plot_flow", default=False, \
22  help="Whether or not to plot the flow arrows.")
23 p.add_option("--flow_min", action="store", dest="flow_min", default="", \
24  help="Lower limit of the flow.")
25 p.add_option("--flow_max", action="store", dest="flow_max", default="", \
26  help="Upper limit of the flow.")
27 p.add_option("--fix_flows", action="store_true", dest="fix_flows", default=False, \
28  help="Whether or not the flows are adapted to the data or lie between flow_min and flow_max.")
29 p.add_option("--flow_range", action="store", dest="flow_range", default="", \
30  help="Log range of the flows in case that they are not fixed.")
31 p.add_option("--fix_flow_arrow_width", action="store_true", dest="fix_flow_arrow_width", default=False, \
32  help="Fix the width of the flow arrows to constant width.")
33 p.add_option("--flow_cmap", action="store", dest="flow_cmap", default="", \
34  help="Colormap of the flows.")
35 p.add_option("--separate_fission", action="store_true", dest="separate_fission", default=False, \
36  help="Whether or not to show arrows also for fission. If not present, hatched areas will be plotted.")
37 p.add_option("--fission_minflow", action="store", dest="fission_minflow", default="", \
38  help="Minimum flow to get indicated as fission region in case the separate fission flag is not given.")
39 p.add_option("--x_min", action="store", dest="x_min", default="", \
40  help="Lower limit of the mass fraction.")
41 p.add_option("--x_max", action="store", dest="x_max", default="", \
42  help="Upper limit of the mass fraction.")
43 p.add_option("--x_cmap", action="store", dest="x_cmap", default="", \
44  help="Colormap of the mass fractions.")
45 p.add_option("--disable_abar", action="store_true", dest="disable_abar", default=False, \
46  help="Whether or not disabling the indication of Abar.")
47 p.add_option("--mass_bins_cmap", action="store", dest="mass_bins_cmap", default="", \
48  help="Colormap of the background colors.")
49 p.add_option("--disable_magic", action="store_true", dest="disable_magic", default=False, \
50  help="Whether or not disabling the indication for the magic number.")
51 p.add_option("--additional_plot", action="store", dest="additional_plot", default="", \
52  help="Whether or not to show an additional plot in the top left corner. "+
53  "Possible options are 'timescales', 'tracked', 'mainout', or 'energy'"+
54  " for plotting average timescales, mass fractions of tracked nuclei, additional mainout data"+
55  ", or nuclear energy generation.")
56 p.add_option("--tau_min", action="store", dest="tau_min", default="", \
57  help="Lower limit of the average timescales.")
58 p.add_option("--tau_max", action="store", dest="tau_max", default="", \
59  help="Upper limit of the average timescales.")
60 p.add_option("--engen_min", action="store", dest="engen_min", default="", \
61  help="Lower limit of the Energy.")
62 p.add_option("--engen_max", action="store", dest="engen_max", default="", \
63  help="Upper limit of the Energy.")
64 p.add_option("--tracked_min", action="store", dest="tracked_min", default="", \
65  help="Lower limit of the tracked nuclei mass fractions.")
66 p.add_option("--tracked_max", action="store", dest="tracked_max", default="", \
67  help="Upper limit of the tracked nuclei mass fractions.")
68 p.add_option("--amainout_min", action="store", dest="amainout_min", default="", \
69  help="Lower limit of the additional mainout abundances.")
70 p.add_option("--amainout_max", action="store", dest="amainout_max", default="", \
71  help="Upper limit of the additional mainout abundances.")
72 p.add_option("--time_min", action="store", dest="t_min", default="", \
73  help="Lower limit of the time.")
74 p.add_option("--time_max", action="store", dest="t_max", default="", \
75  help="Upper limit of the time.")
76 p.add_option("--disable_mainout", action="store_true", dest="disable_mainout", default=False, \
77  help="Whether or not disabling the mainout plot.")
78 p.add_option("--density_min", action="store", dest="density_min", default="", \
79  help="Lower limit of the density.")
80 p.add_option("--density_max", action="store", dest="density_max", default="", \
81  help="Upper limit of the density.")
82 p.add_option("--temperature_min", action="store", dest="temperature_min", default="", \
83  help="Lower limit of the temperatures.")
84 p.add_option("--temperature_max", action="store", dest="temperature_max", default="", \
85  help="Upper limit of the temperature.")
86 p.add_option("--ye_min", action="store", dest="ye_min", default="", \
87  help="Lower limit of the electron fraction.")
88 p.add_option("--ye_max", action="store", dest="ye_max", default="", \
89  help="Upper limit of the electron fraction.")
90 p.add_option("--indicate_r_path", action="store_true", dest="indicate_r_path", default=False, \
91  help="Whether or not to indicate a theoretical r-process path that has been calculated assuming "+\
92  "(n,gamma)(gamma,n) equilibrium.")
93 p.add_option("--frame_min", action="store", dest="frame_min", default="", \
94  help="Value of the first frame (default = 1).")
95 p.add_option("--frame_max", action="store", dest="frame_max", default="", \
96  help="Value of the last frame (default = end of the simulation).")
97 p.add_option("--save", action="store_true", dest="save", default=False, \
98  help="Whether or not saving the movie.")
99 p.add_option("--save_frames", action="store_true", dest="save_frames", default=False, \
100  help="Whether or not saving the frames into a folder.")
101 p.add_option("--output", action="store", dest="output_name", default='flow_movie.mp4', \
102  help="Output name of the movie.")
103 p.add_option('--parallel_save', action='store_true', dest='parallel_save', default=False, \
104  help='Whether or not to save the movie in parallel.')
105 p.add_option('--parallel_cpus', action='store', dest='parallel_cpus', default='5', \
106  help='Number of CPUs to use for parallel saving.')
107 p.add_option("--interval", action="store", dest="interval", default='10', \
108  help="Interval of the movie (larger value equals slower).")
109 p.add_option("--mpirun_path", action="store", dest="mpirun_path", default='', \
110  help="Path of the mpirun command to use for parallel saving.")
111 p.add_option("--interactive", action="store_true", default=False, \
112  help="Whether to show the movie in interactive mode.")
113 p.set_usage("""
114  Visualize a WinNet simulation. Ensure that at least
115  snapshot_every or h_snapshot_every parameter was enabled in the
116  parameter file. To plot timescales, energy, tracked nuclei, mainout,
117  or reaction flows, the necessary parameters have to be enabled in the
118  parameter file.
119 
120  Usage: ./winnet_movie.py -i <rundir>
121  Example: ./winnet_movie.py -i runs/test""")
122 
123 
124 #--- parse options -----------------------------------------------------------
125 (options,args) = p.parse_args()
126 run_path = options.rundir
127 
128 kwargs = {}
129 kwargs['interactive'] = options.interactive
130 kwargs['timescalerange'] = (1e-12, 1e10)
131 kwargs['trackedrange'] = (1e-8, 1e0)
132 kwargs['energyrange'] = (1e10, 1e20)
133 kwargs['timerange'] = (1e-5 , 1e5)
134 kwargs['densityrange'] = (1e-5, 1e12)
135 kwargs['temperaturerange'] = (0, 10)
136 kwargs['yerange'] = (0.0, 0.55)
137 kwargs['amainoutrange'] = (5e-10,1e0)
138 
139 if options.flow_min: kwargs['flow_min'] = float(options.flow_min)
140 if options.flow_max: kwargs['flow_max'] = float(options.flow_max)
141 if options.plot_flow: kwargs['plot_flow'] = False if options.plot_flow else True
142 if options.separate_fission: kwargs['separate_fission'] = True
143 if options.fission_minflow: kwargs['fission_minflow'] = float(options.fission_minflow)
144 if options.fix_flows: kwargs['flow_adapt_prange'] = False
145 if options.flow_range: kwargs['flow_prange'] = float(options.flow_range)
146 if options.fix_flow_arrow_width: kwargs['flow_adapt_width'] = True
147 if options.flow_cmap: kwargs['cmapNameFlow'] = options.flow_cmap
148 if options.x_min: kwargs['X_min'] = float(options.x_min)
149 if options.x_max: kwargs['X_max'] = float(options.x_max)
150 if options.x_cmap: kwargs['cmapNameX'] = options.x_cmap
151 if options.disable_abar: kwargs['plot_abar'] = (not options.disable_abar)
152 if options.mass_bins_cmap: kwargs['cmapNameMassBins'] = options.mass_bins_cmap
153 if options.disable_magic: kwargs['plot_magic'] = (not options.disable_magic)
154 if options.additional_plot: kwargs['additional_plot'] = options.additional_plot.lower().strip()
155 if options.tau_min: kwargs['timescalerange'] = (float(options.tau_min),kwargs['timescalerange'][1])
156 if options.tau_max: kwargs['timescalerange'] = (kwargs['timescalerange'][0], float(options.tau_max))
157 if options.engen_min: kwargs['energyrange'] = (float(options.engen_min), kwargs['energyrange'][1])
158 if options.engen_max: kwargs['energyrange'] = (kwargs['energyrange'][0], float(options.engen_max))
159 if options.tracked_min: kwargs['trackedrange'] = (float(options.tracked_min), kwargs['trackedrange'][1])
160 if options.tracked_max: kwargs['trackedrange'] = (kwargs['trackedrange'][0], float(options.tracked_max))
161 if options.t_min: kwargs['timerange'] = (float(options.t_min), kwargs['timerange'][1])
162 if options.t_max: kwargs['timerange'] = (kwargs['timerange'][0], float(options.t_max))
163 if options.disable_mainout: kwargs['plot_mainout'] = (not options.disable_mainout)
164 if options.density_min: kwargs['densityrange'] = (float(options.density_min), kwargs['densityrange'][1])
165 if options.density_max: kwargs['densityrange'] = (kwargs['densityrange'][0], float(options.density_max))
166 if options.temperature_min: kwargs['temperaturerange'] = (float(options.temperature_min), kwargs['temperaturerange'][1])
167 if options.temperature_max: kwargs['temperaturerange'] = (kwargs['temperaturerange'][0], float(options.temperature_max))
168 if options.ye_min: kwargs['yerange'] = (float(options.ye_min), kwargs['yerange'][1])
169 if options.ye_max: kwargs['yerange'] = (kwargs['yerange'][0], float(options.ye_max))
170 if options.amainout_min: kwargs['amainoutrange'] = (float(options.amainout_min), kwargs['amainoutrange'][1])
171 if options.amainout_max: kwargs['amainoutrange'] = (kwargs['amainoutrange'][0], float(options.amainout_max))
172 if options.indicate_r_path: kwargs['indicate_r_path'] = True
173 
174 
175 
176 # Sanity checks
177 w = wreader(run_path)
178 
179 # Check if the run has snapshots
180 value = w.check_existence('snapshot')
181 if value == 0:
182  raise ValueError('No snapshots found. Please enable snapshots in the parameter file.')
183 
184 # Sanity for timescales and so on, disable if not found
185 if options.additional_plot:
186  if options.additional_plot == 'timescales':
187  value = w.check_existence('timescales')
188  if value == 0:
189  print('No timescales found. Disabling timescales. Remove --additional_plot to disable this message.')
190  kwargs['additional_plot'] = 'none'
191  elif options.additional_plot == 'energy':
192  value = w.check_existence('energy')
193  if value == 0:
194  print('No energy found. Disabling energy. Remove --additional_plot to disable this message.')
195  kwargs['additional_plot'] = 'none'
196  elif options.additional_plot == 'tracked':
197  value = w.check_existence('tracked_nuclei')
198  if value == 0:
199  print('No tracked nuclei found. Disabling tracked nuclei. Remove --additional_plot to disable this message.')
200  kwargs['additional_plot'] = 'none'
201  elif options.additional_plot == 'mainout':
202  value = w.check_existence('mainout')
203  if value == 0:
204  print('No mainout found. Disabling mainout. Remove --additional_plot to disable this message.')
205  kwargs['additional_plot'] = 'none'
206 if options.indicate_r_path:
207  value = w.check_existence('mainout')
208  if value == 0:
209  print('No mainout found. Disabling r-process path. Remove --indicate_r_path to disable this message.')
210  kwargs['indicate_r_path'] = False
211 if options.indicate_r_path or options.interactive:
212  # Check if the winvn.dat file is present
213  winvn_path = w.template['isotopes_file']
214  if not os.path.exists(os.path.join(run_path, winvn_path)):
215  print('No winvn.dat found. Falling back to default winvn path.')
216  # relative to the file here
217  winvn_path = os.path.join(script_path, '../../data/winvne_v2.0.dat')
218  kwargs['winvn_path'] = winvn_path
219 if options.interactive:
220  kwargs['additional_plot'] = 'none'
221 if not options.disable_mainout:
222  value = w.check_existence('mainout')
223  if value == 0:
224  print('No mainout found. Disabling mainout. Set --disable_mainout to disable this message.')
225  kwargs['plot_mainout'] = False
226 if not options.plot_flow:
227  value = w.check_existence('flows')
228  if value == 0:
229  print('No flow found. Disabling flow. Set --disable_flow to disable this message.')
230  kwargs['plot_flow'] = False
231 
232 
233 if options.frame_min: frame_min = int(options.frame_min)
234 else: frame_min = 1
235 if options.frame_max: frame_max = int(options.frame_max)
236 else: frame_max = w.nr_of_snaps
237 
238 # Ensure that not both is chosen
239 if options.save_frames and options.save:
240  raise ValueError('Cannot save frames and movie at the same time. Please choose one of them.')
241 
242 # Check if frames should be saved into a folder
243 if options.save_frames:
244  # Create the output folder if it doesnt exist yet
245  if options.output_name:
246  os.mkdir(options.output_name)
247 
248  if not options.parallel_save:
249  # Create figure
250  fig = plt.figure(figsize=(15, 8))
251  if options.output_name:
252  anim = FlowAnimation(run_path, fig, frame_dir=options.output_name, **kwargs)
253  else:
254  anim = FlowAnimation(run_path, fig, **kwargs)
255 
256  # Save the frames
257  for i in tqdm(range(frame_min, frame_max)):
258  anim.save_frame(i)
259  else: # parallel saving
260  # Sanity check, does mpi4py exist?
261  try:
262  from mpi4py import MPI
263  except ImportError:
264  raise ImportError('mpi4py not found. Please install it to use parallel saving.')
265 
266  # Next check, is ffmpeg installed?
267  if os.system('ffmpeg -version > /dev/null') != 0:
268  raise ImportError('ffmpeg not found. Please install it to use parallel saving.')
269 
270  # Get folder location of this script
271  script_path = os.path.dirname(os.path.realpath(__file__))
272  # The options have to be passed to the parallel_save.py script
273  # Therefore save them
274  # try to import pickle
275  try:
276  import pickle
277  except ImportError:
278  raise ImportError('pickle not found. Please install it to use parallel saving.')
279 
280  option_dict_path = os.path.join(script_path, 'src_files/data/options.pkl')
281  with open(option_dict_path, 'wb') as f:
282  pickle.dump(kwargs, f)
283 
284  # Get the path to the parallel_save.py script
285  parallel_save_path = os.path.join(script_path, 'src_files', 'parallel_save.py')
286 
287  # Check if the mpirun path is given
288  if not options.mpirun_path:
289  # Get the correct mpirun
290  mpirun = os.popen('whereis mpirun').read().strip().split()
291  # Get the mpirun that has oneapi in the path
292  mpirun = [m for m in mpirun if 'oneapi' in m]
293  if not mpirun:
294  raise ImportError('No mpirun with oneapi found. Please install it to use parallel saving.')
295  mpirun = mpirun[0]
296  else:
297  # Take the given mpirun path
298  mpirun = options.mpirun_path
299 
300  # Test if the mpirun path is correct
301  if os.system(f'{mpirun} -version > /dev/null') != 0:
302  raise ImportError('mpirun not found or wrong path. Please install it to use parallel saving.')
303 
304  # Run the parallel saving
305  os.system(f'{mpirun} -n {options.parallel_cpus} python {parallel_save_path} {run_path} {frame_min} {frame_max} {options.interval} TRUE')
306 
307  # Remove the options file
308  os.remove(option_dict_path)
309 
310  # Move to the output destination
311  if options.output_name:
312  os.system(f'mv {run_path}/frames/* {options.output_name}')
313 
314  print('Finished saving frames!')
315 
316 # Check if things should be saved or shown
317 elif options.save:
318  if not options.parallel_save:
319  # Funcanimation
320  fig = plt.figure(figsize=(15, 8))
321 
322  # Animate the flows
323  anim = FlowAnimation(run_path, fig, **kwargs)
324 
325  ani = anim.get_funcanimation(frames=range(frame_min, frame_max))
326  ani.save(options.output_name, fps=int(options.interval))
327  else: # Parallel saving
328  # Sanity check, does mpi4py exist?
329  try:
330  from mpi4py import MPI
331  except ImportError:
332  raise ImportError('mpi4py not found. Please install it to use parallel saving.')
333 
334  # Next check, is ffmpeg installed?
335  if os.system('ffmpeg -version > /dev/null') != 0:
336  raise ImportError('ffmpeg not found. Please install it to use parallel saving.')
337 
338  # Get folder location of this script
339  script_path = os.path.dirname(os.path.realpath(__file__))
340  # The options have to be passed to the parallel_save.py script
341  # Therefore save them
342  # try to import pickle
343  try:
344  import pickle
345  except ImportError:
346  raise ImportError('pickle not found. Please install it to use parallel saving.')
347 
348  option_dict_path = os.path.join(script_path, 'src_files/data/options.pkl')
349  with open(option_dict_path, 'wb') as f:
350  pickle.dump(kwargs, f)
351 
352  # Get the path to the parallel_save.py script
353  parallel_save_path = os.path.join(script_path, 'src_files', 'parallel_save.py')
354 
355  # Check if the mpirun path is given
356  if not options.mpirun_path:
357  # Get the correct mpirun
358  mpirun = os.popen('whereis mpirun').read().strip().split()
359  # Get the mpirun that has oneapi in the path
360  mpirun = [m for m in mpirun if 'oneapi' in m]
361  if not mpirun:
362  raise ImportError('No mpirun with oneapi found. Please install it to use parallel saving.')
363  mpirun = mpirun[0]
364  else:
365  # Take the given mpirun path
366  mpirun = options.mpirun_path
367 
368  # Test if the mpirun path is correct
369  if os.system(f'{mpirun} -version > /dev/null') != 0:
370  raise ImportError('mpirun not found or wrong path. Please install it to use parallel saving.')
371 
372  # Run the parallel saving
373  os.system(f'{mpirun} -n {options.parallel_cpus} python {parallel_save_path} {run_path} {frame_min} {frame_max} {options.interval}')
374 
375  # Remove the options file
376  os.remove(option_dict_path)
377 
378  print('Finished saving movie!')
379 else:
380  # Funcanimation
381  fig = plt.figure(figsize=(15, 8))
382 
383  # Animate the flows
384  anim = FlowAnimation(run_path, fig, **kwargs)
385  ani = anim.get_funcanimation(interval=int(options.interval), frames=range(frame_min, frame_max))
386  plt.show()
src_files.wreader
Definition: wreader.py:1
src_files.FlowAnimation
Definition: FlowAnimation.py:1
src_files.wreader.wreader
Definition: wreader.py:12
src_files.FlowAnimation.FlowAnimation
Definition: FlowAnimation.py:25