Analyzing Multiple Datasets with the EnzoSimulation Class
We generally analyze numerical simulations a single dataset at a time, but quite often what we really want to do is analyze the entire simulation all at once. This is particularly necessary when studying anything time dependent. This is usually accomplished by writing some disposable wrapper for the analysis routine that loops over similarly named, indexed datasets. If you’re particularly lazy, you may end up just calling your code over and over and changing the arguments by hand. When running a cosmology simulation, you will likely have datasets output at specific redshifts, in addition to data coming out at equally-spaced time intervals. This can make time-sorting of datasets a little trickier, or at least a little more tedious.
To make this a little easier, we have the EnzoSimulation class (yt/extensions/EnzoSimulation.py). To instantiate an EnzoSimulation object, you only need to provide the parameter file use to run the simulation.
>>> from yt.extensions.EnzoSimulation import *
>>> ES = EnzoSimulation("128Mpc256grid_SFFB.param")
The EnzoSimulation class doesn’t give you much, but what it does give you is nice. The EnzoSimulation object we just made has one main attribute, a list called allOutputs. This is a time-sorted list of all datasets in the entire simulation. Each item in the list is a dictionary containing entries for the path to the dataset parameter file, the time associated with that dataset, and if this is a cosmological simulation, the associated redshift.
>>> w.allOutputs[0]
{'filename': '/Users/britton/EnzoRuns/cool_core_unreasonable/RD0000/RD0000',
'time': 0.81631644849936602, 'redshift': 99.0}
Who wants to analyze a dataset at redshift 99, though? We can limit time interval over which datasets are kept in the list with keywords, initial_time, initial_redshift, final_time, and final_redshift, given when instantiating the EnzoSimulation object.
>>> ES = EnzoSimulation("128Mpc256grid_SFFB.param",
initial_redshift=1,final_redshift=0)
Time and redshift keywords can also be mixed together. One other keyword can be specified. If links=True is thrown as instantiation, all items in the allOutputs list will also have entries, previous and next, that point to the previous and next items on the list. This is useful if you’re buried in a loop somewhere without index information and you need to know what the nearest datasets are. Now you’re disposable wrapper becomes:
from yt.extensions.EnzoSimulation import *
ES = EnzoSimulation("128Mpc256grid_SFFB.param",initial_redshift=5)
for output in ES.allOutputs:
do_analysis_on_this_dataset(output['filename'])
You can use the EnzoSimulation class as a superclass for your own analysis subclasses. A good example of this is yt/extensions/SimulationHaloProfiler.py. This uses the EnzoSimulation class to run the HaloProfiler over multiple datasets. Here’s the entire source:
import yt.extensions.HaloProfiler as HP
from EnzoSimulation import *
class SimulationHaloProfiler(EnzoSimulation):
def __init__(self,EnzoParameterFile,HaloProfilerParameterFile,**kwargs):
EnzoSimulation.__init__(self,EnzoParameterFile,**kwargs)
self.HaloProfilerParameterFile = HaloProfilerParameterFile
def runHaloProfiler(self):
for output in self.allOutputs:
halo_profiler = HP.HaloProfiler(output['filename'],self.HaloProfilerParameterFile)
halo_profiler.makeProfiles()
halo_profiler.makeProjections()
del halo_profiler
The call to EnzoSimulation.__init__ ensures that all of the EnzoSimulation object attributes, namely allOutputs, gets inherited by the SimulationHaloProfiler object. Without that call, all we would get are the EnzoSimulation object methods, which aren’t very useful on their own. Using this subclass, all that needs to be done to run the HaloProfiler over the entire simulation is this:
from yt.extensions.SimulationHaloProfiler import *
SHP = SimulationHaloProfiler("128Mpc256grid_SFFB.param",
"halo_profiler_parameters.par",
initial_redshift=5)
SHP.runHaloProfiler()
Simple as that.