def test_pythonize_name_dict(self): names = ["energy", "rho[0]"] pyth_dict = ad.pythonize_name_dict(names) self.assertEqual(pyth_dict.energy, "energy") self.assertEqual(pyth_dict.rho[0], "rho[0]")
def __init__(self, allfiles, reduction_type): """Constructor. :param allfiles: List of all the files :type allfiles: list of str :param reduction_type: Type of reduction. :type reduction_type: str """ self.reduction_type = str(reduction_type) # TODO: Is it necessary to have the folder level? # Probably not, so remove it # _vars_readers is like _vars_readers['variable']['folder'] -> # OneScalar(f) _vars_readers['variable'] is a dictionary with as keys # the folders where to find the files associated to the variable and the # reduction reduction_type # # OneScalar objects act as readers. self._vars_readers = {} for file_ in allfiles: # We only save those that variables are well-behaved try: cactusascii_file = OneScalar(file_) if cactusascii_file.reduction_type == reduction_type: for var in list(cactusascii_file.keys()): # We add to the _vars_readers dictionary the mapping: # [var][folder] to OneScalar(f) folder = cactusascii_file.folder self._vars_readers.setdefault( var, {})[folder] = cactusascii_file except RuntimeError: try: cactusascii_file = TwoScalar(file_) if cactusascii_file.reduction_type == reduction_type: for var in list(cactusascii_file.keys()): # We add to the _vars dictionary the mapping: # [var][folder] to OneScalar(f) folder = cactusascii_file.folder self._vars_readers.setdefault( var, {})[folder] = cactusascii_file except RuntimeError: pass # We cache the results in _vars, a dictionary with keys the variables # and values the timeseries. self._vars = {} # What pythonize_name_dict does is to make the various variables # accessible as attributes, e.g. self.fields.rho self.fields = pythonize_name_dict(list(self.keys()), self.__getitem__)
def __init__(self, sd): """Constructor. :param sd: Simulation directory. :type sd: :py:class:`~.SimDir` """ # self._vars is a dictionary. For _vars_ascii, the keys are the # variables and the items are sets of tuples of the form # (multipole_l, multipole_m, radius, filename) for text files. # # For _vars_h5 they are sets (since we have to read the content # to find the l and m) self._vars_ascii = {} self._vars_h5 = {} # First, we need to find the multipole files. # There are text files and h5 files # # We use a regular expressions on the name # The structure is like mp_Psi4_l_m2_r110.69.asc # # Let's understand the regexp: # 0. ^ and $ means that we match the entire name # 1. We match mp_ followed my the variable name, which is # any combination of characters # 2. We match _l with a number # 3. We match _m with possibly a minus sign and a number # 4. We match _r with any combination of numbers with possibly # dots # 5. We possibly match a compression rx_ascii = re.compile( r"""^ mp_([a-zA-Z0-9\[\]_]+) _l(\d+) _m([-]?\d+) _r([0-9.]+) .asc (?:.bz2|.gz)? $""", re.VERBOSE, ) # For h5 files is easy: it is just the var name rx_h5 = re.compile(r"^mp_([a-zA-Z0-9\[\]_]+).h5$") for f in sd.allfiles: filename = os.path.split(f)[1] matched_h5 = rx_h5.match(filename) matched_ascii = rx_ascii.match(filename) if matched_h5 is not None: variable_name = matched_h5.group(1).lower() var_list = self._vars_h5.setdefault(variable_name, set()) # We are flagging that this h5 var_list.add(f) elif matched_ascii is not None: variable_name = matched_ascii.group(1).lower() mult_l = int(matched_ascii.group(2)) mult_m = int(matched_ascii.group(3)) radius = float(matched_ascii.group(4)) var_list = self._vars_ascii.setdefault(variable_name, set()) var_list.add((mult_l, mult_m, radius, f)) # What pythonize_name_dict does is to make the various variables # accessible as attributes, e.g. self.fields.rho self.fields = pythonize_name_dict(list(self.keys()), self.__getitem__)
def __init__(self, qlm_vars, ah_vars, shape_files): """Constructor. :param qlm_vars: Dictionary that maps the name of the QLM variable with the associated :py:class:`~.TimeSeries`. :type qlm_vars: dict :param ah_vars: Dictionary that maps the name of the AH variable with the associated :py:class:`~.TimeSeries`. :type ah_vars: dict :param shape_files: Dictionary that maps the iteration to the files where to find the shape at that iteration. :type shape_files: dict """ self._qlm_vars = qlm_vars self._ah_vars = ah_vars # We turn the var_dictionary into attributes for var, timeseries in self._qlm_vars.items(): # With this we can access properties in the following way # horizon.mass setattr(self, var, timeseries) # Here we compute some interesting and useful quantities, if we have # qlm data if self._qlm_vars: self.mass_final = self.mass.y[-1] self.spin_final = self.spin.y[-1] self.dimensionless_spin_final = (self.spin_final / self.mass_final**2) else: self.mass_final = None self.spin_final = None self.dimensionless_spin_final = None # We put the AH vars under the ah attribute, they are accessed in the # same way as qlm_vars (as attribute). This is achieved using # pythonize_name_dict. The second argument is how we access the data. # Here we use the method get_ah_property that peeks into self._ah_vars. self.ah = pythonize_name_dict(ah_vars, self.get_ah_property) # We read the formation time from a variable in AH if self._ah_vars: self.formation_time = self.ah.area.tmin else: self.formation_time = None # Now we deal with the shape. Shape files is a dictionary that maps # iteration to the associated file self._shape_files = shape_files self.shape_available = self._shape_files != {} if self.shape_available: # We sort the iterations self.shape_iterations = np.array( sorted(s for s in self._shape_files)) self.shape_iteration_min = self.shape_iterations[0] self.shape_iteration_max = self.shape_iterations[-1] # To convert between time and iteration, we need the AH data If we # don't have that, we will assume time = iteration. # if self._ah_vars: # Now we find the associated times using ah.cctk_iteration # self.ah.cctk_iteration is a function time vs iteration, we # want the opposite. We define a new timeseries in which we swap # t and y times_iterations = TimeSeries(self.ah.cctk_iteration.y, self.ah.cctk_iteration.t) self.shape_times = times_iterations(self.shape_iterations) self.shape_time_min = self.shape_times[0] self.shape_time_max = self.shape_times[-1] else: warnings.warn( "AH data not found, so it is impossible to convert" " between iteration number to time.\nManually set" " shape_times or methods involving shape and time" " will not work") self.shape_times = None self.shape_time_min = None self.shape_time_max = None # We will save all the shape patches and their origin that we read in # this dictionary self._patches = {}
def __init__(self, sd): """Constructor. :param sd: Simulation directory. :type sd: :py:class:`~.SimDir` """ # self._vars_*_files are dictionary. For _vars_ascii_files, the keys are # the variables and the items are sets of tuples of the form # (multipole_l, multipole_m, radius, filename) for text files. # # For example: # self._vars_ascii_files = # {'psi4': {(2, 2, 110.69, 'output-0000/mp_Psi4_l_m2_r110.69.asc'), # (2, 2, 110.69, 'output-0001/mp_Psi4_l_m2_r110.69.asc')}} self._vars_ascii_files = {} # For _vars_h5_files, the keys are still the variables, but values are # sets with only the files (and not tuples), since we have to read the # content to find the l and m # # For example # self._vars_h5_files = # {'psi4': {'output-0000/mp_Psi4.h5', 'output-0001/mp_Psi4.h5'}} self._vars_h5_files = {} # For _vars_IL_files, I did things similarly to in the ascii case, # because I didn't appreciate the fact that the h5 files were really # closer to what I needed for the IL code files, since in both cases # one must open the files in order to know what modes it contains. # The only difference from the ascii case is that this time the whole # set of multipoles contains the *same* file. I then overload the # reading method to figure out which column of the file to pull from # when reading. Hacky & inelegant, but hey, it works! self._vars_IL_files = {} # self._vars is the dictionary where we cache the results. The keys are # the variables, the values are the corresponding MultipoleAllDets # objects. We fill this with __getitem__ self._vars = {} # First, we need to find the multipole files. # There are text files and h5 files # # We use a regular expressions on the name # The structure is like mp_Psi4_l_m2_r110.69.asc # # Let's understand the regexp: # 0. ^ and $ means that we match the entire name # 1. We match mp_ followed my the variable name, which is # any combination of characters # 2. We match _l with a number # 3. We match _m with possibly a minus sign and a number # 4. We match _r with any combination of numbers with possibly # dots # 5. We possibly match a compression rx_ascii = re.compile( r"""^ mp_([a-zA-Z0-9\[\]_]+) _l(\d+) _m([-]?\d+) _r([0-9.]+) .asc (?:.bz2|.gz)? $""", re.VERBOSE, ) # For h5 files is easy: it is just the var name rx_h5 = re.compile(r"^mp_([a-zA-Z0-9\[\]_]+).h5$") # For IL code Psi4 files, it is even easier: it's just these files, rx_IL = re.compile('^Psi4_rad\.mon\.([0-9]+)$') for f in sd.allfiles: filename = os.path.split(f)[1] matched_h5 = rx_h5.match(filename) matched_ascii = rx_ascii.match(filename) matched_IL = rx_IL.match(filename) if matched_h5 is not None: variable_name = matched_h5.group(1).lower() var_list = self._vars_h5_files.setdefault(variable_name, set()) # We are flagging that this h5 var_list.add(f) elif matched_ascii is not None: variable_name = matched_ascii.group(1).lower() mult_l = int(matched_ascii.group(2)) mult_m = int(matched_ascii.group(3)) radius = float(matched_ascii.group(4)) var_list = self._vars_ascii_files.setdefault( variable_name, set()) var_list.add((mult_l, mult_m, radius, f)) elif matched_IL is not None: variable_name = "psi4" # all keys must be lower-case var_list = self._vars_IL_files.setdefault(variable_name, set()) data = np.genfromtxt( f ) # Unfortunately there's no way to check this without opening the file radius = data[0, -4] if (data.shape[1] - 5) % 2 != 0: raise RuntimeError('Wrong format') nmodes = (data.shape[1] - 5) // 2 # Loop through all the modes in the file l = 2 i = 1 while (i <= nmodes): m = l while (m >= -l and i <= nmodes): var_list.add((l, m, radius, f)) m = m - 1 i = i + 1 l = l + 1 # What pythonize_name_dict does is to make the various variables # accessible as attributes, e.g. self.fields.rho self.fields = pythonize_name_dict(list(self.keys()), self.__getitem__)