예제 #1
0
 def __init__(
     self,
     hubble_constant,
     omega_matter,
     omega_lambda,
     omega_radiation,
     omega_curvature,
     initial_redshift,
     unit_registry=None,
 ):
     Cosmology.__init__(
         self,
         hubble_constant=hubble_constant,
         omega_matter=omega_matter,
         omega_lambda=omega_lambda,
         omega_radiation=omega_radiation,
         omega_curvature=omega_curvature,
         unit_registry=unit_registry,
     )
     self.initial_redshift = initial_redshift
     self.initial_time = self.t_from_z(self.initial_redshift)
     # time units = 1 / sqrt(4 * pi * G rho_0 * (1 + z_i)**3),
     # rho_0 = (3 * Omega_m * h**2) / (8 * pi * G)
     self.time_unit = (
         (
             1.5
             * self.omega_matter
             * self.hubble_constant**2
             * (1 + self.initial_redshift) ** 3
         )
         ** -0.5
     ).in_units("s")
     self.time_unit.units.registry = self.unit_registry
예제 #2
0
 def _set_units(self):
     self.unit_registry = UnitRegistry()
     self.time_unit = self.quan(1.0, "s")
     if self.cosmological_simulation:
         # Instantiate Cosmology object for units and time conversions.
         self.cosmology = \
           Cosmology(hubble_constant=self.hubble_constant,
                     omega_matter=self.omega_matter,
                     omega_lambda=self.omega_lambda,
                     unit_registry=self.unit_registry)
         self.unit_registry.modify("h", self.hubble_constant)
         # Comoving lengths
         for my_unit in ["m", "pc", "AU", "au"]:
             new_unit = "%scm" % my_unit
             # technically not true, but should be ok
             self.unit_registry.add(new_unit,
                                    self.unit_registry.lut[my_unit][0],
                                    dimensions.length,
                                    "\\rm{%s}/(1+z)" % my_unit)
         self.length_unit = self.quan(self.unit_base["UnitLength_in_cm"],
                                      "cmcm / h",
                                      registry=self.unit_registry)
         self.box_size *= self.length_unit.in_units("Mpccm / h")
     else:
         # Read time from file for non-cosmological sim
         self.time_unit = self.quan(
             self.unit_base["UnitLength_in_cm"]/ \
                 self.unit_base["UnitVelocity_in_cm_per_s"], "s")
         self.unit_registry.add("code_time", 1.0, dimensions.time)
         self.unit_registry.modify("code_time", self.time_unit)
         # Length
         self.length_unit = self.quan(self.unit_base["UnitLength_in_cm"],
                                      "cm")
         self.unit_registry.add("code_length", 1.0, dimensions.length)
         self.unit_registry.modify("code_length", self.length_unit)
예제 #3
0
def find_radius_mass(m_r, delta, z=0.0, cosmo=None):
    """
    Given a mass profile and an overdensity, find the radius 
    and mass (e.g. M200, r200)

    Parameters
    ----------
    m_r : RadialProfile
        The mass profile.
    delta : float
        The overdensity to compute the mass and radius for.
    z : float, optional
        The redshift of the halo formation. Default: 0.0
    cosmo : yt ``Cosmology`` object
        The cosmology to be used when computing the critical
        density. If not supplied, a default one from yt will 
        be used.
    """
    from yt.utilities.cosmology import Cosmology
    from scipy.optimize import bisect
    if cosmo is None:
        cosmo = Cosmology()
    rho_crit = cosmo.critical_density(z).to_value("Msun/kpc**3")
    f = lambda r: 3.0*m_r(r)/(4.*np.pi*r**3) - delta*rho_crit
    r_delta = bisect(f, 0.01, 10000.0)
    return r_delta, m_r(r_delta)
예제 #4
0
    def _parse_parameter_file(self):
        with open(self.parameter_filename, "rb") as f:
            hvals = fpu.read_cattrs(f, header_dt)
            hvals.pop("unused")
        self.dimensionality = 3
        self.refine_by = 2
        prefix = ".".join(self.parameter_filename.rsplit(".", 2)[:-2])
        self.filename_template = "%s.%%(num)s%s" % (prefix, self._suffix)
        self.file_count = len(glob.glob(prefix + ".*" + self._suffix))

        # Now we can set up things we already know.
        self.cosmological_simulation = 1
        self.current_redshift = (1.0 / hvals["scale"]) - 1.0
        self.hubble_constant = hvals["h0"]
        self.omega_lambda = hvals["Ol"]
        self.omega_matter = hvals["Om"]
        cosmo = Cosmology(
            hubble_constant=self.hubble_constant,
            omega_matter=self.omega_matter,
            omega_lambda=self.omega_lambda,
        )
        self.current_time = cosmo.lookback_time(self.current_redshift,
                                                1e6).in_units("s")
        self.periodicity = (True, True, True)
        self.particle_types = "halos"
        self.particle_types_raw = "halos"

        self.domain_left_edge = np.array([0.0, 0.0, 0.0])
        self.domain_right_edge = np.array([hvals["box_size"]] * 3)

        self.domain_dimensions = np.ones(3, "int32")
        self.parameters.update(hvals)
예제 #5
0
    def _parse_parameter_file(self):
        # Read all parameters.
        simu = self._read_log_simu()
        param = self._read_parameter()

        # Set up general information.
        self.filename_template = self.parameter_filename
        self.file_count = 1
        self.parameters.update(param)
        self.particle_types = ('halos')
        self.particle_types_raw = ('halos')

        # Set up geometrical information.
        self.refine_by = 2
        self.dimensionality = 3
        nz = 1 << self.over_refine_factor
        self.domain_dimensions = np.ones(self.dimensionality, "int32") * nz
        self.domain_left_edge = np.array([0.0, 0.0, 0.0])
        # Note that boxsize is in Mpc but particle positions are in kpc.
        self.domain_right_edge = np.array([simu['boxsize']] * 3) * 1000
        self.periodicity = (True, True, True)

        # Set up cosmological information.
        self.cosmological_simulation = 1
        self.current_redshift = param['z']
        self.omega_lambda = simu['lambda0']
        self.omega_matter = simu['omega0']
        cosmo = Cosmology(hubble_constant=self.hubble_constant,
                          omega_matter=self.omega_matter,
                          omega_lambda=self.omega_lambda)
        self.current_time = cosmo.lookback_time(param['z'], 1e6).in_units('s')
예제 #6
0
    def _load_star_data(self, select='all'):
        """If star is present load Metallicity if present"""
        if self.obj.simulation.nstar == 0:
            return

        if select is 'all': 
            flag = [True]*self.obj.simulation.nstar
        else:
            flag = (select>=0)

        if has_property(self.obj, 'star', 'metallicity'):
            self.sZ  = get_property(self.obj, 'metallicity', 'star')[flag]
        elif has_property(self.obj, 'star', 'met_tng'):  # try Illustris/TNG alias
            self.sZ  = get_property(self.obj, 'met_tng', 'star')[flag]  
            #self.sZ  = np.sum(self.sZ.T[2:],axis=0)  # first two are H,He; the rest sum to give metallicity
            #self.sZ[self.sZ<0] = 0.  # some (very small) negative values, set to 0
        else:
            mylog.warning('Metallicity not found: setting all stars to solar=0.0134')
            self.sZ = 0.0134*np.ones(self.obj.simulation.nstar,dtype=MY_DTYPE)

        ds = self.obj.yt_dataset
        if has_property(self.obj, 'star', 'aform'):
            self.age  = get_property(self.obj, 'aform', 'star')[flag]  # a_exp at time of formation
        elif has_property(self.obj, 'star', 'aform_tng'):  # try Illustris/TNG alias
            self.age  = get_property(self.obj, 'aform_tng', 'star')[flag]  
            self.age  = abs(self.age)  # some negative values here too; not sure what to do?
        else:
            self.age = np.zeros(self.obj.simulation.nstar,dtype=MY_DTYPE)
            mylog.warning('Stellar age not found -- photometry will be incorrect!')
        if ds.cosmological_simulation:
            from yt.utilities.cosmology import Cosmology
            co = Cosmology(hubble_constant=ds.hubble_constant, omega_matter=ds.omega_matter, omega_lambda=ds.omega_lambda)
            self.age = (ds.current_time - co.t_from_z(1./self.age-1.)).in_units('Gyr').astype(MY_DTYPE)  # age at time of snapshot 
예제 #7
0
    def _parse_parameter_file(self):

        hvals = self._get_hvals()

        self.dimensionality = 3
        self.refine_by = 2
        self.parameters["HydroMethod"] = "sph"
        self.unique_identifier = \
            int(os.stat(self.parameter_filename)[stat.ST_CTIME])
        # Set standard values

        # We may have an overridden bounding box.
        if self.domain_left_edge is None:
            self.domain_left_edge = np.zeros(3, "float64")
            self.domain_right_edge = np.ones(3, "float64") * hvals["BoxSize"]
        nz = 1 << self.over_refine_factor
        self.domain_dimensions = np.ones(3, "int32") * nz
        self.periodicity = (True, True, True)

        self.cosmological_simulation = 1

        self.current_redshift = hvals["Redshift"]
        self.omega_lambda = hvals["OmegaLambda"]
        self.omega_matter = hvals["Omega0"]
        self.hubble_constant = hvals["HubbleParam"]
        # According to the Gadget manual, OmegaLambda will be zero for
        # non-cosmological datasets.  However, it may be the case that
        # individuals are running cosmological simulations *without* Lambda, in
        # which case we may be doing something incorrect here.
        # It may be possible to deduce whether ComovingIntegration is on
        # somehow, but opinions on this vary.
        if self.omega_lambda == 0.0:
            mylog.info("Omega Lambda is 0.0, so we are turning off Cosmology.")
            self.hubble_constant = 1.0  # So that scaling comes out correct
            self.cosmological_simulation = 0
            self.current_redshift = 0.0
            # This may not be correct.
            self.current_time = hvals["Time"]
        else:
            # Now we calculate our time based on the cosmology, because in
            # ComovingIntegration hvals["Time"] will in fact be the expansion
            # factor, not the actual integration time, so we re-calculate
            # global time from our Cosmology.
            cosmo = Cosmology(self.hubble_constant,
                              self.omega_matter, self.omega_lambda)
            self.current_time = cosmo.hubble_time(self.current_redshift)
            mylog.info("Calculating time from %0.3e to be %0.3e seconds",
                       hvals["Time"], self.current_time)
        self.parameters = hvals

        prefix = os.path.abspath(
            os.path.join(os.path.dirname(self.parameter_filename),
                         os.path.basename(self.parameter_filename).split(".", 1)[0]))

        if hvals["NumFiles"] > 1:
            self.filename_template = "%s.%%(num)s%s" % (prefix, self._suffix)
        else:
            self.filename_template = self.parameter_filename

        self.file_count = hvals["NumFiles"]
예제 #8
0
    def _parse_parameter_file(self):
        with h5py.File(self.parameter_filename, mode="r") as f:
            self.parameters = \
              dict((str(field), val)
                   for field, val in f["Header"].attrs.items())

        self.dimensionality = 3
        self.refine_by = 2

        # Set standard values
        self.domain_left_edge = np.zeros(3, "float64")
        self.domain_right_edge = np.ones(3, "float64") * \
          self.parameters["BoxSize"]
        self.domain_dimensions = np.ones(3, "int32")
        self.cosmological_simulation = 1
        self.periodicity = (True, True, True)
        self.current_redshift = self.parameters["Redshift"]
        self.omega_lambda = self.parameters["OmegaLambda"]
        self.omega_matter = self.parameters["Omega0"]
        self.hubble_constant = self.parameters["HubbleParam"]
        cosmology = Cosmology(hubble_constant=self.hubble_constant,
                              omega_matter=self.omega_matter,
                              omega_lambda=self.omega_lambda)
        self.current_time = cosmology.t_from_z(self.current_redshift)

        prefix = os.path.abspath(
            os.path.join(os.path.dirname(self.parameter_filename), 
                         os.path.basename(self.parameter_filename).split(".", 1)[0]))
        suffix = self.parameter_filename.rsplit(".", 1)[-1]
        self.filename_template = "%s.%%(num)i.%s" % (prefix, suffix)
        self.file_count = self.parameters["NumFiles"]
        self.particle_types = ("Group", "Subhalo")
        self.particle_types_raw = ("Group", "Subhalo")
예제 #9
0
def find_virial_radius(ds,center):
    z = ds.current_redshift

    co = Cosmology(hubble_constant=ds.hubble_constant, omega_matter=ds.omega_matter,
                   omega_lambda=ds.omega_lambda, omega_curvature=0.0)
    rho_c = co.critical_density(z=z)
    
    r = 0
    rs = []
    densities = []
    density = np.inf
    while density > 200*rho_c:
        r+=5
        rs.append(r)
        sp = ds.sphere(center,(r,'kpc'))
        cell_mass,particle_mass = sp.quantities.total_mass()
        volume = 4/3*np.pi*ds.arr(r,'kpc')**3
        new_density = (cell_mass+particle_mass)/volume
        if new_density > density:
            print('density not decreasing!')
            break
        density = new_density.in_units('g/cm**3')
        densities.append(density)
        toprint = (r,cell_mass+particle_mass,(cell_mass+particle_mass).units,particle_mass,particle_mass.units,cell_mass,cell_mass.units)
    rs = np.array(rs)
    densities = np.array(densities)
    Rvir = np.interp(200*rho_c,np.flip(densities),np.flip(rs))
    Mvir = cell_mass+particle_mass
    return ds.arr(Rvir,'kpc'),ds.arr(Mvir,'g')
    def _parse_parameter_file(self):
        with open(self.parameter_filename, "rb") as f:
            hvals = fpu.read_cattrs(f, header_dt)
            hvals.pop("unused")
        self.dimensionality = 3
        self.refine_by = 2
        self.unique_identifier = int(os.stat(self.parameter_filename)[stat.ST_CTIME])
        prefix = ".".join(self.parameter_filename.rsplit(".", 2)[:-2])
        self.filename_template = "%s.%%(num)s%s" % (prefix, self._suffix)
        self.file_count = len(glob.glob(prefix + ".*" + self._suffix))

        # Now we can set up things we already know.
        self.cosmological_simulation = 1
        self.current_redshift = (1.0 / hvals["scale"]) - 1.0
        self.hubble_constant = hvals["h0"]
        self.omega_lambda = hvals["Ol"]
        self.omega_matter = hvals["Om"]
        cosmo = Cosmology(self.hubble_constant, self.omega_matter, self.omega_lambda)
        self.current_time = cosmo.hubble_time(self.current_redshift).in_units("s")
        self.periodicity = (True, True, True)
        self.particle_types = "halos"
        self.particle_types_raw = "halos"

        self.domain_left_edge = np.array([0.0, 0.0, 0.0])
        self.domain_right_edge = np.array([hvals["box_size"]] * 3)

        nz = 1 << self.over_refine_factor
        self.domain_dimensions = np.ones(3, "int32") * nz
        self.parameters.update(hvals)
예제 #11
0
def nfw_scale_density(conc, z=0.0, delta=200.0, cosmo=None):
    """
    Compute a scale density parameter for an NFW profile
    given a concentration parameter, and optionally
    a redshift, overdensity, and cosmology.

    Parameters
    ----------
    conc : float
        The concentration parameter for the halo, which should 
        correspond the selected overdensity (which has a default
        of 200). 
    z : float, optional
        The redshift of the halo formation. Default: 0.0
    delta : float, optional
        The overdensity parameter for which the concentration
        is defined. Default: 200.0
    cosmo : yt Cosmology object
        The cosmology to be used when computing the critical
        density. If not supplied, a default one from yt will 
        be used.
    """
    from yt.utilities.cosmology import Cosmology
    if cosmo is None:
        cosmo = Cosmology()
    rho_crit = cosmo.critical_density(z).to_value("Msun/kpc**3")
    rho_s = delta*rho_crit*conc**3*_nfw_factor(conc)/3.
    return rho_s
예제 #12
0
def read_caesar(c_file, selected_gals):
    print('Reading caesar file...')
    sim = caesar.load(c_file, LoadHalo=False)
    redshift = np.round(sim.simulation.redshift, decimals=2)
    h = sim.simulation.hubble_constant
    cosmo = Cosmology(hubble_constant=sim.simulation.hubble_constant,
                      omega_matter=sim.simulation.omega_matter,
                      omega_lambda=sim.simulation.omega_lambda,
                      omega_curvature=0)
    thubble = cosmo.hubble_time(redshift).in_units("Gyr")
    H0 = (100 * sim.simulation.hubble_constant) * km / s / Mpc
    rhocrit = 3. * H0.to('1/s')**2 / (8 * np.pi * sim.simulation.G)
    mlim = 32 * rhocrit.to('Msun/kpc**3') * sim.simulation.boxsize.to(
        'kpc'
    )**3 * sim.simulation.omega_baryon / sim.simulation.effective_resolution**3 / sim.simulation.scale_factor**3  # galaxy mass resolution limit: 32 gas particle masses

    gals = np.array([])
    # read galaxy particle data for the selected galaxies
    if isinstance(selected_gals, np.ndarray):
        gals = np.asarray([
            i for i in sim.galaxies
            if i.GroupID in selected_gals and i.masses['stellar'] > mlim
        ])  # select resolved galaxies
    print('Galaxy data from caesar file extracted and saved.')
    return sim, redshift, h, cosmo, thubble, H0, rhocrit, mlim, gals
예제 #13
0
    def _parse_parameter_file(self):
        self.dimensionality = 3
        self._periodicity = (True, True, True)
        self.refine_by = 2
        self.unique_identifier = \
            int(os.stat(self.parameter_filename)[stat.ST_CTIME])
        self.file_count = 1

        ct_file = CTHLMiniFile(self, self.parameter_filename)
        self.parameters.update(ct_file.parameters)

        for par in [
                'hubble_constant', 'omega_matter', 'omega_lambda',
                'current_redshift'
        ]:
            setattr(self, par, self.parameters.get(par))

        self.cosmological_simulation = 1
        co = Cosmology(hubble_constant=self.hubble_constant,
                       omega_matter=self.omega_matter,
                       omega_lambda=self.omega_lambda)
        self.current_time = co.t_from_z(self.current_redshift)
        self.domain_left_edge = np.zeros(self.dimensionality)
        self.domain_right_edge = float(self.parameters['box_size'][0]) * \
          np.ones(self.dimensionality)
        self.domain_dimensions = np.ones(self.dimensionality, "int32")
예제 #14
0
    def _parse_parameter_file(self):
        with open(self.parameter_filename, "rb") as f:
            hvals = fpu.read_cattrs(f, header_dt)
            hvals.pop("unused")
        self.dimensionality = 3
        self.refine_by = 2
        self.unique_identifier = \
            int(os.stat(self.parameter_filename)[stat.ST_CTIME])
        prefix = ".".join(self.parameter_filename.rsplit(".", 2)[:-2])
        self.filename_template = "%s.%%(num)s%s" % (prefix, self._suffix)
        self.file_count = len(glob.glob(prefix + ".*" + self._suffix))

        # Now we can set up things we already know.
        self.cosmological_simulation = 1
        self.current_redshift = (1.0 / hvals['scale']) - 1.0
        self.hubble_constant = hvals['h0']
        self.omega_lambda = hvals['Ol']
        self.omega_matter = hvals['Om']
        cosmo = Cosmology(self.hubble_constant, self.omega_matter,
                          self.omega_lambda)
        self.current_time = cosmo.hubble_time(
            self.current_redshift).in_units("s")
        self.periodicity = (True, True, True)
        self.particle_types = ("halos")
        self.particle_types_raw = ("halos")

        self.domain_left_edge = np.array([0.0, 0.0, 0.0])
        self.domain_right_edge = np.array([hvals['box_size']] * 3)

        nz = 1 << self.over_refine_factor
        self.domain_dimensions = np.ones(3, "int32") * nz
        self.parameters.update(hvals)
예제 #15
0
    def _set_code_unit_attributes(self):
        # First try to set units based on parameter file
        if self.cosmological_simulation:
            mu = self.parameters.get("dMsolUnit", 1.0)
            self.mass_unit = self.quan(mu, "Msun")
            lu = self.parameters.get("dKpcUnit", 1000.0)
            # In cosmological runs, lengths are stored as length*scale_factor
            self.length_unit = self.quan(lu, "kpc") * self.scale_factor
            density_unit = self.mass_unit / (self.length_unit / self.scale_factor) ** 3
            if "dHubble0" in self.parameters:
                # Gasoline's internal hubble constant, dHubble0, is stored in
                # units of proper code time
                self.hubble_constant *= np.sqrt(G * density_unit)
                # Finally, we scale the hubble constant by 100 km/s/Mpc
                self.hubble_constant /= self.quan(100, "km/s/Mpc")
                # If we leave it as a YTQuantity, the cosmology object
                # used below will add units back on.
                self.hubble_constant = self.hubble_constant.to_value("")
        else:
            mu = self.parameters.get("dMsolUnit", 1.0)
            self.mass_unit = self.quan(mu, "Msun")
            lu = self.parameters.get("dKpcUnit", 1.0)
            self.length_unit = self.quan(lu, "kpc")

        # If unit base is defined by the user, override all relevant units
        if self._unit_base is not None:
            for my_unit in ["length", "mass", "time"]:
                if my_unit in self._unit_base:
                    my_val = self._unit_base[my_unit]
                    my_val = (
                        self.quan(*my_val)
                        if isinstance(my_val, tuple)
                        else self.quan(my_val)
                    )
                    setattr(self, f"{my_unit}_unit", my_val)

        # Finally, set the dependent units
        if self.cosmological_simulation:
            cosmo = Cosmology(
                hubble_constant=self.hubble_constant,
                omega_matter=self.omega_matter,
                omega_lambda=self.omega_lambda,
            )
            self.current_time = cosmo.lookback_time(self.current_redshift, 1e6)
            # mass units are rho_crit(z=0) * domain volume
            mu = (
                cosmo.critical_density(0.0)
                * (1 + self.current_redshift) ** 3
                * self.length_unit ** 3
            )
            self.mass_unit = self.quan(mu.in_units("Msun"), "Msun")
            density_unit = self.mass_unit / (self.length_unit / self.scale_factor) ** 3
            # need to do this again because we've modified the hubble constant
            self.unit_registry.modify("h", self.hubble_constant)
        else:
            density_unit = self.mass_unit / self.length_unit ** 3

        if not hasattr(self, "time_unit"):
            self.time_unit = 1.0 / np.sqrt(density_unit * G)
예제 #16
0
    def __init__(self,
                 ds,
                 data_source=None,
                 star_mass=None,
                 star_creation_time=None,
                 bins=300,
                 volume=None,
                 star_filter=None):
        self._ds = ds
        self._data_source = data_source
        self._filter = star_filter
        self.ds_provided = self._data_source is not None
        self.filter_provided = self._filter is not None
        self.bin_count = bins

        # Set up for time conversion.
        self.cosm = Cosmology(hubble_constant=self._ds.hubble_constant,
                              omega_matter=self._ds.omega_matter,
                              omega_lambda=self._ds.omega_lambda)
        # Find the time right now.
        self.time_now = self._ds.current_time

        if not self.ds_provided:
            # Check to make sure we have the right set of informations.
            if star_mass is None or star_creation_time is None \
                    or volume is None:
                mylog.error("""
                    If data_source is not provided, all of these parameters
                    need to be set:
                        star_mass (array, Msun),
                        star_creation_time (array, code units),
                        volume (float, cMpc**3).""")
                return None

            if isinstance(star_mass, YTArray):
                assert star_mass.units.same_dimensions_as(g.units)
            elif star_mass is not None:
                star_mass = YTArray(star_mass, 'Msun')
            self.star_mass = star_mass

            if isinstance(star_creation_time, YTArray):
                assert star_creation_time.units.same_dimensions_as(s.units)
            elif star_creation_time is not None:
                star_creation_time = self._ds.arr(star_creation_time,
                                                  'code_time')
            self.star_creation_time = star_creation_time

            if isinstance(volume, YTQuantity):
                assert volume.units.same_dimensions_as(
                    self._ds.quan(1.0, 'Mpccm**3').units)
            elif volume is not None:
                volume = self._ds.quan(volume, 'Mpccm**3')
            self.volume = volume

        # Build the distribution.
        self.build_dist()
        # Attach some convenience arrays.
        self.attach_arrays()
예제 #17
0
def test_z_t_analytic():
    """
    Test z/t conversions against analytic solutions.
    """

    cosmos = (
        {
            "hubble_constant": 0.7,
            "omega_matter": 0.3,
            "omega_lambda": 0.7
        },
        {
            "hubble_constant": 0.7,
            "omega_matter": 1.0,
            "omega_lambda": 0.0
        },
        {
            "hubble_constant": 0.7,
            "omega_matter": 0.3,
            "omega_lambda": 0.0
        },
    )

    for cosmo in cosmos:
        omega_curvature = 1 - cosmo["omega_matter"] - cosmo["omega_lambda"]
        co = Cosmology(omega_curvature=omega_curvature, **cosmo)
        # random sample in log(a) from -6 to 6
        my_random = np.random.RandomState(10132324)
        la = 12 * my_random.random_sample(1000) - 6
        z = 1 / np.power(10, la) - 1

        t_an = t_from_z_analytic(z, **cosmo).to("Gyr")
        t_co = co.t_from_z(z).to("Gyr")

        assert_rel_equal(
            t_an,
            t_co,
            4,
            err_msg=
            f"t_from_z does not match analytic version for cosmology {cosmo}.",
        )

        # random sample in log(t/t0) from -3 to 1
        t0 = np.power(10, 4 * my_random.random_sample(1000) - 3)
        t = (t0 / co.hubble_constant).to("Gyr")

        z_an = z_from_t_analytic(t, **cosmo)
        z_co = co.z_from_t(t)

        # compare scale factors since z approaches 0
        assert_rel_equal(
            1 / (1 + z_an),
            1 / (1 + z_co),
            5,
            err_msg=
            f"z_from_t does not match analytic version for cosmology {cosmo}.",
        )
예제 #18
0
 def _set_units(self):
     self.unit_registry = UnitRegistry()
     self.time_unit = self.quan(1.0, "s")
     if self.cosmological_simulation:
         # Instantiate Cosmology object for units and time conversions.
         self.cosmology = Cosmology(
             hubble_constant=self.hubble_constant,
             omega_matter=self.omega_matter,
             omega_lambda=self.omega_lambda,
             unit_registry=self.unit_registry,
         )
         if "h" in self.unit_registry:
             self.unit_registry.modify("h", self.hubble_constant)
         else:
             self.unit_registry.add("h", self.hubble_constant,
                                    dimensions.dimensionless)
         # Comoving lengths
         for my_unit in ["m", "pc", "AU"]:
             new_unit = f"{my_unit}cm"
             # technically not true, but should be ok
             self.unit_registry.add(
                 new_unit,
                 self.unit_registry.lut[my_unit][0],
                 dimensions.length,
                 "\\rm{%s}/(1+z)" % my_unit,
                 prefixable=True,
             )
         self.length_unit = self.quan(
             self.unit_base["UnitLength_in_cm"],
             "cmcm / h",
             registry=self.unit_registry,
         )
         self.mass_unit = self.quan(self.unit_base["UnitMass_in_g"],
                                    "g / h",
                                    registry=self.unit_registry)
         self.box_size = self.box_size * self.length_unit
         self.domain_left_edge = self.domain_left_edge * self.length_unit
         self.domain_right_edge = self.domain_right_edge * self.length_unit
         self.unit_registry.add(
             "unitary",
             float(self.box_size.in_base()),
             self.length_unit.units.dimensions,
         )
     else:
         # Read time from file for non-cosmological sim
         self.time_unit = self.quan(
             self.unit_base["UnitLength_in_cm"] /
             self.unit_base["UnitVelocity_in_cm_per_s"],
             "s",
         )
         self.unit_registry.add("code_time", 1.0, dimensions.time)
         self.unit_registry.modify("code_time", self.time_unit)
         # Length
         self.length_unit = self.quan(self.unit_base["UnitLength_in_cm"],
                                      "cm")
         self.unit_registry.add("code_length", 1.0, dimensions.length)
         self.unit_registry.modify("code_length", self.length_unit)
    def __init__(self, parameter_filename, simulation_type, find_outputs=False):
        self.parameter_filename = parameter_filename
        self.simulation_type = simulation_type
        self.simulation = simulation(parameter_filename, simulation_type, 
                                     find_outputs=find_outputs)

        self.cosmology = Cosmology(
            hubble_constant=(self.simulation.hubble_constant),
            omega_matter=self.simulation.omega_matter,
            omega_lambda=self.simulation.omega_lambda)
예제 #20
0
def test_hubble_time():
    """
    Make sure hubble_time and t_from_z functions agree.

    """

    for i in range(10):
        co = Cosmology()
        # random sample over interval (-1,100]
        z = -101 * np.random.random() + 100
        assert_rel_equal(co.hubble_time(z), co.t_from_z(z), 5)
예제 #21
0
def test_dark_factor():
    """
    Test that dark factor returns same value for when not
    being used and when w_0 = -1 and w_z = 0.
    """

    co = Cosmology(w_0=-1, w_a=0, use_dark_factor=False)

    assert_equal(co.get_dark_factor(0), 1.0)
    co.use_dark_factor = True
    assert_equal(co.get_dark_factor(0), 1.0)
def test_hubble_time():
    """
    Make sure hubble_time and t_from_z functions agree.

    """

    for i in range(10):
        co = Cosmology()
        # random sample over interval (-1,100]
        z = -101 * np.random.random() + 100
        yield assert_rel_equal, co.hubble_time(z), co.t_from_z(z), 5
def test_z_t_conversion():
    """
    Make sure t_from_z and z_from_t are consistent.

    """

    for i in range(10):
        co = Cosmology()
        # random sample over interval (-1,100]
        z1 = -101 * np.random.random() + 100
        t = co.t_from_z(z1)
        z2 = co.z_from_t(t)
        yield assert_rel_equal, z1, z2, 10
예제 #24
0
def test_z_t_conversion():
    """
    Make sure t_from_z and z_from_t are consistent.

    """

    for i in range(10):
        co = Cosmology()
        # random sample over interval (-1,100]
        z1 = -101 * np.random.random() + 100
        t = co.t_from_z(z1)
        z2 = co.z_from_t(t)
        assert_rel_equal(z1, z2, 10)
 def __init__(self, hubble_constant, omega_matter, omega_lambda,
              omega_curvature, initial_redshift, unit_registry = None):
     Cosmology.__init__(self,
                        hubble_constant=hubble_constant,
                        omega_matter=omega_matter,
                        omega_lambda=omega_lambda,
                        omega_curvature=omega_curvature,
                        unit_registry=unit_registry)
     self.initial_redshift = initial_redshift
     # time units = 1 / sqrt(4 * pi * G rho_0 * (1 + z_i)**3),
     # rho_0 = (3 * Omega_m * h**2) / (8 * pi * G)
     self.time_unit = ((1.5 * self.omega_matter * self.hubble_constant**2 * 
                        (1 + self.initial_redshift)**3)**-0.5).in_units("s")
     self.time_unit.units.registry = self.unit_registry
예제 #26
0
def test_dataset_cosmology_calculator():
    """
    Test datasets's cosmology calculator against standalone.
    """

    ds = data_dir_load(enzotiny)

    co = Cosmology(hubble_constant=ds.hubble_constant,
                   omega_matter=ds.omega_matter,
                   omega_lambda=ds.omega_lambda)

    v1 = ds.cosmology.comoving_radial_distance(1, 5).to('Mpccm').v
    v2 = co.comoving_radial_distance(1, 5).to('Mpccm').v
    assert_equal(v1, v2)
예제 #27
0
def test_z_t_roundtrip():
    """
    Make sure t_from_z and z_from_t are consistent.

    """

    co = Cosmology()
    # random sample in log(a) from -6 to 6
    my_random = np.random.RandomState(6132305)
    la = 12 * my_random.random_sample(10000) - 6
    z1 = 1 / np.power(10, la) - 1
    t = co.t_from_z(z1)
    z2 = co.z_from_t(t)
    assert_rel_equal(z1, z2, 4)
예제 #28
0
def z_to_Time(z,cosmo=None,verbose=False):
    if cosmo is None:
        #use Planck 2015 from last column (TT, TE, EE+lowP+lensing+ext) of Table 4 from http://arxiv.org/pdf/1502.01589v2.pdf
        from yt.utilities.cosmology import Cosmology
        h = 0.6774
        om = 0.3089
        ol = 0.6911
        #behroozi parameters
        # h=.7
        # om=.27
        # ol=1-om
        if verbose: print "Assuming a Planck 2015 cosmology (H0 = {0}, Om0 = {1}, OL = {2})".format(h*100,om,ol)
        cosmo = Cosmology(hubble_constant=h,omega_matter=om,omega_lambda=ol)

    return cosmo.t_from_z(z).in_units('yr')
예제 #29
0
    def _set_units(self):
        """
        Set "cm" units for explicitly comoving.
        Note, we are using comoving units all the time since
        we are dealing with data at multiple redshifts.
        """
        for my_unit in ["m", "pc", "AU"]:
            new_unit = f"{my_unit}cm"
            self.unit_registry.add(
                new_unit, self.unit_registry.lut[my_unit][0],
                length, self.unit_registry.lut[my_unit][3])

        setup = True
        for attr in ["hubble_constant",
                     "omega_matter",
                     "omega_lambda"]:
            if getattr(self, attr) is None:
                setup = False
                ytreeLogger.warning(
                    f"{attr} missing from data. "
                    "Arbor will have no cosmology calculator.")

        if setup:
            self.cosmology = Cosmology(
                hubble_constant=self.hubble_constant,
                omega_matter=self.omega_matter,
                omega_lambda=self.omega_lambda,
                omega_radiation=self.omega_radiation,
                unit_registry=self.unit_registry)
 def _set_units(self):
     self.unit_registry = UnitRegistry()
     self.time_unit = self.quan(1.0, "s")
     if self.cosmological_simulation:
         # Instantiate Cosmology object for units and time conversions.
         self.cosmology = \
           Cosmology(hubble_constant=self.hubble_constant,
                     omega_matter=self.omega_matter,
                     omega_lambda=self.omega_lambda,
                     unit_registry=self.unit_registry)
         self.unit_registry.modify("h", self.hubble_constant)
         # Comoving lengths
         for my_unit in ["m", "pc", "AU", "au"]:
             new_unit = "%scm" % my_unit
             # technically not true, but should be ok
             self.unit_registry.add(
                 new_unit, self.unit_registry.lut[my_unit][0],
                 dimensions.length, "\\rm{%s}/(1+z)" % my_unit)
         self.length_unit = self.quan(self.unit_base["UnitLength_in_cm"],
                                      "cmcm / h", registry=self.unit_registry)
         self.box_size *= self.length_unit.in_units("Mpccm / h")
     else:
         # Read time from file for non-cosmological sim
         self.time_unit = self.quan(
             self.unit_base["UnitLength_in_cm"]/ \
                 self.unit_base["UnitVelocity_in_cm_per_s"], "s")
         self.unit_registry.add("code_time", 1.0, dimensions.time)
         self.unit_registry.modify("code_time", self.time_unit)
         # Length
         self.length_unit = self.quan(
             self.unit_base["UnitLength_in_cm"],"cm")
         self.unit_registry.add("code_length", 1.0, dimensions.length)
         self.unit_registry.modify("code_length", self.length_unit)
예제 #31
0
def realistic_ds(fields, particle_fields, nprocs):
    np.random.seed(int(0x4d3d3d3))
    units = [base_ds._get_field_info(*f).units for f in fields]
    punits = [base_ds._get_field_info('io', f).units for f in particle_fields]
    fields = [_strip_ftype(f) for f in fields]

    ds = fake_random_ds(16,
                        fields=fields,
                        units=units,
                        nprocs=nprocs,
                        particle_fields=particle_fields,
                        particle_field_units=punits,
                        particles=base_ds.stream_handler.particle_count[0][0])

    ds.parameters["HydroMethod"] = "streaming"
    ds.parameters["EOSType"] = 1.0
    ds.parameters["EOSSoundSpeed"] = 1.0
    ds.conversion_factors["Time"] = 1.0
    ds.conversion_factors.update(dict((f, 1.0) for f in fields))
    ds.gamma = 5.0 / 3.0
    ds.current_redshift = 0.0001
    ds.cosmological_simulation = 1
    ds.hubble_constant = 0.7
    ds.omega_matter = 0.27
    ds.omega_lambda = 0.73
    ds.cosmology = Cosmology(hubble_constant=ds.hubble_constant,
                             omega_matter=ds.omega_matter,
                             omega_lambda=ds.omega_lambda,
                             unit_registry=ds.unit_registry)
    return ds
예제 #32
0
    def __init__(self,
                 ds,
                 bcdir="",
                 model="chabrier",
                 time_now=None,
                 star_filter=None):
        self._ds = ds
        if not os.path.isdir(bcdir):
            bcdir = os.path.join(ytcfg.get("yt", "test_data_dir"), bcdir)
            if not os.path.isdir(bcdir):
                raise RuntimeError("Failed to locate %s" % bcdir)
        self.bcdir = bcdir
        self._filter = star_filter
        self.filter_provided = self._filter is not None
        if model == "chabrier":
            self.model = CHABRIER
        elif model == "salpeter":
            self.model = SALPETER
        # Set up for time conversion.
        self.cosm = Cosmology(hubble_constant=self._ds.hubble_constant,
                              omega_matter=self._ds.omega_matter,
                              omega_lambda=self._ds.omega_lambda)
        # Find the time right now.

        if time_now is None:
            self.time_now = self._ds.current_time
        else:
            self.time_now = time_now

        # Read the tables.
        self.read_bclib()
예제 #33
0
def test_cosmology_calculator_answers():
    """
    Test cosmology calculator functions against previously calculated values.
    """

    fn = os.path.join(local_dir, 'cosmology_answers.yml')
    data = yaml.load(open(fn, 'r'), Loader=yaml.FullLoader)

    cosmologies = data['cosmologies']
    functions = data['functions']

    for cname, copars in cosmologies.items():
        omega_curvature = 1 - copars['omega_matter'] - \
          copars['omega_lambda'] - copars['omega_radiation']

        cosmology = Cosmology(omega_curvature=omega_curvature, **copars)

        for fname, finfo in functions.items():
            func = getattr(cosmology, fname)
            args = finfo.get('args', [])
            val = func(*args)
            units = finfo.get('units')
            if units is not None:
                val.convert_to_units(units)
            val = float(val)

            err_msg = '%s answer has changed for %s cosmology, old: %f, new: %f.' % \
              (fname, cname, finfo['answers'][cname], val)
            assert_almost_equal(
                val, finfo['answers'][cname], 10,
                err_msg=err_msg)
예제 #34
0
def get_base_ds(nprocs):
    fields, units = [], []

    for fname, (code_units, aliases, dn) in StreamFieldInfo.known_other_fields:
        fields.append(("gas", fname))
        units.append(code_units)

    ds = fake_random_ds(4,
                        fields=fields,
                        units=units,
                        particles=20,
                        nprocs=nprocs)
    ds.parameters["HydroMethod"] = "streaming"
    ds.parameters["EOSType"] = 1.0
    ds.parameters["EOSSoundSpeed"] = 1.0
    ds.conversion_factors["Time"] = 1.0
    ds.conversion_factors.update(dict((f, 1.0) for f in fields))
    ds.gamma = 5.0 / 3.0
    ds.current_redshift = 0.0001
    ds.cosmological_simulation = 1
    ds.hubble_constant = 0.7
    ds.omega_matter = 0.27
    ds.omega_lambda = 0.73
    ds.cosmology = Cosmology(hubble_constant=ds.hubble_constant,
                             omega_matter=ds.omega_matter,
                             omega_lambda=ds.omega_lambda,
                             unit_registry=ds.unit_registry)
    # ensures field errors are raised during testing
    # see FieldInfoContainer.check_derived_fields
    ds._field_test_dataset = True
    ds.index
    return ds
예제 #35
0
    def set_units(self):
        """
        Creates the unit registry for this dataset.

        """
        from yt.units.dimensions import length
        if hasattr(self, "cosmological_simulation") \
           and getattr(self, "cosmological_simulation"):
            # this dataset is cosmological, so add cosmological units.
            self.unit_registry.modify("h", self.hubble_constant)
            # Comoving lengths
            for my_unit in ["m", "pc", "AU", "au"]:
                new_unit = "%scm" % my_unit
                self.unit_registry.add(
                    new_unit, self.unit_registry.lut[my_unit][0] /
                    (1 + self.current_redshift), length,
                    "\\rm{%s}/(1+z)" % my_unit)

        self.set_code_units()

        if hasattr(self, "cosmological_simulation") \
           and getattr(self, "cosmological_simulation"):
            # this dataset is cosmological, add a cosmology object
            setattr(
                self, "cosmology",
                Cosmology(hubble_constant=self.hubble_constant,
                          omega_matter=self.omega_matter,
                          omega_lambda=self.omega_lambda,
                          unit_registry=self.unit_registry))
            setattr(self, "critical_density",
                    self.cosmology.critical_density(self.current_redshift))
예제 #36
0
def z_to_Time(z, cosmo=None, verbose=False):
    if cosmo is None:
        #use Planck 2015 from last column (TT, TE, EE+lowP+lensing+ext) of Table 4 from http://arxiv.org/pdf/1502.01589v2.pdf
        from yt.utilities.cosmology import Cosmology
        h = 0.6774
        om = 0.3089
        ol = 0.6911
        #behroozi parameters
        # h=.7
        # om=.27
        # ol=1-om
        if verbose:
            print "Assuming a Planck 2015 cosmology (H0 = {0}, Om0 = {1}, OL = {2})".format(
                h * 100, om, ol)
        cosmo = Cosmology(hubble_constant=h, omega_matter=om, omega_lambda=ol)

    return cosmo.t_from_z(z).in_units('yr')
    def __init__(self, parameter_filename, simulation_type, find_outputs=False):
        self.parameter_filename = parameter_filename
        self.simulation_type = simulation_type
        self.simulation = simulation(parameter_filename, simulation_type, 
                                     find_outputs=find_outputs)

        self.cosmology = Cosmology(
            hubble_constant=(self.simulation.hubble_constant),
            omega_matter=self.simulation.omega_matter,
            omega_lambda=self.simulation.omega_lambda)
    def __init__(self, ds, data_source=None, star_mass=None,
                 star_creation_time=None, bins=300, volume=None,
                 star_filter=None):
        self._ds = ds
        self._data_source = data_source
        self._filter = star_filter
        self.ds_provided = self._data_source is not None
        self.filter_provided = self._filter is not None
        self.bin_count = bins

        # Set up for time conversion.
        self.cosm = Cosmology(
            hubble_constant=self._ds.hubble_constant,
            omega_matter=self._ds.omega_matter,
            omega_lambda=self._ds.omega_lambda)
        # Find the time right now.
        self.time_now = self._ds.current_time

        if not self.ds_provided:
            # Check to make sure we have the right set of informations.
            if star_mass is None or star_creation_time is None \
                    or volume is None:
                mylog.error("""
                    If data_source is not provided, all of these parameters
                    need to be set:
                        star_mass (array, Msun),
                        star_creation_time (array, code units),
                        volume (float, cMpc**3).""")
                return None

            if isinstance(star_mass, YTArray):
                assert star_mass.units.same_dimensions_as(g.units)
            elif star_mass is not None:
                star_mass = YTArray(star_mass, 'Msun')
            self.star_mass = star_mass

            if isinstance(star_creation_time, YTArray):
                assert star_creation_time.units.same_dimensions_as(s.units)
            elif star_creation_time is not None:
                star_creation_time = self._ds.arr(star_creation_time,
                                                  'code_time')
            self.star_creation_time = star_creation_time

            if isinstance(volume, YTQuantity):
                assert volume.units.same_dimensions_as(
                    self._ds.quan(1.0, 'Mpccm**3').units
                )
            elif volume is not None:
                volume = self._ds.quan(volume, 'Mpccm**3')
            self.volume = volume

        # Build the distribution.
        self.build_dist()
        # Attach some convenience arrays.
        self.attach_arrays()
    def _parse_parameter_file(self):
        handle = h5py.File(self.parameter_filename, mode="r")
        hvals = {}
        hvals.update((str(k), v) for k, v in handle["/Header"].attrs.items())
        hvals["NumFiles"] = hvals["NumFiles"]

        self.dimensionality = 3
        self.refine_by = 2
        self.unique_identifier = \
            int(os.stat(self.parameter_filename)[stat.ST_CTIME])

        # Set standard values
        self.domain_left_edge = np.zeros(3, "float64")
        self.domain_right_edge = np.ones(3, "float64") * hvals["BoxSize"]
        nz = 1 << self.over_refine_factor
        self.domain_dimensions = np.ones(3, "int32") * nz
        self.cosmological_simulation = 1
        self.periodicity = (True, True, True)
        self.current_redshift = hvals["Redshift"]
        self.omega_lambda = hvals["OmegaLambda"]
        self.omega_matter = hvals["Omega0"]
        self.hubble_constant = hvals["HubbleParam"]

        cosmology = Cosmology(hubble_constant=self.hubble_constant,
                              omega_matter=self.omega_matter,
                              omega_lambda=self.omega_lambda)
        self.current_time = cosmology.t_from_z(self.current_redshift)

        self.parameters = hvals
        prefix = os.path.abspath(
            os.path.join(os.path.dirname(self.parameter_filename), 
                         os.path.basename(self.parameter_filename).split(".", 1)[0]))
        
        suffix = self.parameter_filename.rsplit(".", 1)[-1]
        self.filename_template = "%s.%%(num)i.%s" % (prefix, suffix)
        self.file_count = len(glob.glob(prefix + "*" + self._suffix))
        if self.file_count == 0:
            raise YTException(message="No data files found.", ds=self)
        self.particle_types = ("Group", "Subhalo")
        self.particle_types_raw = ("Group", "Subhalo")
        
        handle.close()
 def _set_code_unit_attributes(self):
     if self.cosmological_simulation:
         mu = self.parameters.get('dMsolUnit', 1.)
         lu = self.parameters.get('dKpcUnit', 1000.)
         # In cosmological runs, lengths are stored as length*scale_factor
         self.length_unit = self.quan(lu, 'kpc')*self.scale_factor
         self.mass_unit = self.quan(mu, 'Msun')
         density_unit = self.mass_unit/ (self.length_unit/self.scale_factor)**3
         # Gasoline's hubble constant, dHubble0, is stored units of proper code time.
         self.hubble_constant *= np.sqrt(G.in_units('kpc**3*Msun**-1*s**-2')*density_unit).value/(3.2407793e-18)  
         cosmo = Cosmology(self.hubble_constant,
                           self.omega_matter, self.omega_lambda)
         self.current_time = cosmo.hubble_time(self.current_redshift)
     else:
         mu = self.parameters.get('dMsolUnit', 1.0)
         self.mass_unit = self.quan(mu, 'Msun')
         lu = self.parameters.get('dKpcUnit', 1.0)
         self.length_unit = self.quan(lu, 'kpc')
         density_unit = self.mass_unit / self.length_unit**3
     self.time_unit = 1.0 / np.sqrt(G * density_unit)
예제 #41
0
def time_to_z(t,cosmo=None,verbose=False):            # H0=YTQuantity(70.2,'km/s/Mpc')):
    from yt import YTQuantity,YTArray
    if cosmo is None:
        #use Planck 2015 from last column (TT, TE, EE+lowP+lensing+ext) of Table 4 from http://arxiv.org/pdf/1502.01589v2.pdf
        from yt.utilities.cosmology import Cosmology
        h = 0.6774
        om = 0.3089
        ol = 0.6911
        #behroozi parameters
        # h=.7
        # om=.27
        # ol=1-om
        if verbose: print "Assuming a Planck 2015 cosmology (H0 = {0}, Om0 = {1}, OL = {2})".format(h*100,om,ol)
        cosmo = Cosmology(hubble_constant=h,omega_matter=om,omega_lambda=ol)

    if type(t) != type(YTQuantity(1,'Gyr')) and type(t) != type(YTArray([1,2,3],'Gyr')):
        #then I need to figure out units and wrap in a yt object
        if type(t) == type(1.23):   #single float
            if t < 15:  #assume Gyr
                t = YTArray(t,'Gyr')
                if verbose: print "Assuming time in Gyr"
            elif t < 1e11:  #assume yr
                t = YTArray(t,'yr')
                if verbose: print "Assuming time in yr"
            else:   #then it's probably in seconds
                t = YTArray(t,'s')
                if verbose: print "Assuming time in seconds"
        else:
            from numpy import array
            t = array(t)
            if (t < 15).all():
                t = YTArray(t,'Gyr')
                if verbose: print "Assuming time in Gyr"
            elif (t < 1e11).all():  #assume yr
                t = YTArray(t,'yr')
                if verbose: print "Assuming time in yr"
            else:   #then it's probably in seconds
                t = YTArray(t,'s')
                if verbose: print "Assuming time in seconds"

    return cosmo.z_from_t(t)
예제 #42
0
    def __init__(self, parameter_filename, simulation_type=None,
                 near_redshift=None, far_redshift=None,
                 use_minimum_datasets=True, deltaz_min=0.0,
                 minimum_coherent_box_fraction=0.0,
                 time_data=True, redshift_data=True,
                 find_outputs=False, load_kwargs=None):

        self.near_redshift = near_redshift
        self.far_redshift = far_redshift
        self.use_minimum_datasets = use_minimum_datasets
        self.deltaz_min = deltaz_min
        self.minimum_coherent_box_fraction = minimum_coherent_box_fraction
        self.parameter_filename = parameter_filename
        if load_kwargs is None:
            self.load_kwargs = {}
        else:
            self.load_kwargs = load_kwargs
        self.light_ray_solution = []
        self._data = {}

        # Make a light ray from a single, given dataset.        
        if simulation_type is None:
            ds = load(parameter_filename, **self.load_kwargs)
            if ds.cosmological_simulation:
                redshift = ds.current_redshift
                self.cosmology = Cosmology(
                    hubble_constant=ds.hubble_constant,
                    omega_matter=ds.omega_matter,
                    omega_lambda=ds.omega_lambda,
                    unit_registry=ds.unit_registry)
            else:
                redshift = 0.
            self.light_ray_solution.append({"filename": parameter_filename,
                                            "redshift": redshift})

        # Make a light ray from a simulation time-series.
        else:
            # Get list of datasets for light ray solution.
            CosmologySplice.__init__(self, parameter_filename, simulation_type,
                                     find_outputs=find_outputs)
            self.light_ray_solution = \
              self.create_cosmology_splice(self.near_redshift, self.far_redshift,
                                           minimal=self.use_minimum_datasets,
                                           deltaz_min=self.deltaz_min,
                                           time_data=time_data,
                                           redshift_data=redshift_data)
class CosmologySplice(object):
    """
    Class for splicing together datasets to extend over a
    cosmological distance.
    """

    def __init__(self, parameter_filename, simulation_type, find_outputs=False):
        self.parameter_filename = parameter_filename
        self.simulation_type = simulation_type
        self.simulation = simulation(parameter_filename, simulation_type, 
                                     find_outputs=find_outputs)

        self.cosmology = Cosmology(
            hubble_constant=(self.simulation.hubble_constant),
            omega_matter=self.simulation.omega_matter,
            omega_lambda=self.simulation.omega_lambda)

    def create_cosmology_splice(self, near_redshift, far_redshift,
                                minimal=True, deltaz_min=0.0,
                                time_data=True, redshift_data=True):
        r"""Create list of datasets capable of spanning a redshift
        interval.

        For cosmological simulations, the physical width of the simulation
        box corresponds to some \Delta z, which varies with redshift.
        Using this logic, one can stitch together a series of datasets to
        create a continuous volume or length element from one redshift to
        another. This method will return such a list

        Parameters
        ----------
        near_redshift : float
            The nearest (lowest) redshift in the cosmology splice list.
        far_redshift : float
            The furthest (highest) redshift in the cosmology splice list.
        minimal : bool
            If True, the minimum number of datasets is used to connect the
            initial and final redshift.  If false, the list will contain as
            many entries as possible within the redshift
            interval.
            Default: True.
        deltaz_min : float
            Specifies the minimum delta z between consecutive datasets
            in the returned
            list.
            Default: 0.0.
        time_data : bool
            Whether or not to include time outputs when gathering
            datasets for time series.
            Default: True.
        redshift_data : bool
            Whether or not to include redshift outputs when gathering
            datasets for time series.
            Default: True.

        Examples
        --------

        >>> co = CosmologySplice("enzo_tiny_cosmology/32Mpc_32.enzo", "Enzo")
        >>> cosmo = co.create_cosmology_splice(1.0, 0.0)

        """

        if time_data and redshift_data:
            self.splice_outputs = self.simulation.all_outputs
        elif time_data:
            self.splice_outputs = self.simulation.all_time_outputs
        elif redshift_data:
            self.splice_outputs = self.simulation.all_redshift_outputs
        else:
            mylog.error('Both time_data and redshift_data are False.')
            return

        # Link datasets in list with pointers.
        # This is used for connecting datasets together.
        for i, output in enumerate(self.splice_outputs):
            if i == 0:
                output['previous'] = None
                output['next'] = self.splice_outputs[i + 1]
            elif i == len(self.splice_outputs) - 1:
                output['previous'] = self.splice_outputs[i - 1]
                output['next'] = None
            else:
                output['previous'] = self.splice_outputs[i - 1]
                output['next'] = self.splice_outputs[i + 1]

        # Calculate maximum delta z for each data dump.
        self._calculate_deltaz_max()

        # Calculate minimum delta z for each data dump.
        self._calculate_deltaz_min(deltaz_min=deltaz_min)

        cosmology_splice = []
 
        if near_redshift == far_redshift:
            self.simulation.get_time_series(redshifts=[near_redshift])
            cosmology_splice.append({'time': self.simulation[0].current_time,
                                     'redshift': self.simulation[0].current_redshift,
                                     'filename': os.path.join(self.simulation[0].fullpath,
                                                              self.simulation[0].basename),
                                     'next': None})
            mylog.info("create_cosmology_splice: Using %s for z = %f ." %
                       (cosmology_splice[0]['filename'], near_redshift))
            return cosmology_splice
        
        # Use minimum number of datasets to go from z_i to z_f.
        if minimal:

            z_Tolerance = 1e-3
            z = far_redshift

            # fill redshift space with datasets
            while ((z > near_redshift) and
                   (np.abs(z - near_redshift) > z_Tolerance)):

                # For first data dump, choose closest to desired redshift.
                if (len(cosmology_splice) == 0):
                    # Sort data outputs by proximity to current redshift.
                    self.splice_outputs.sort(key=lambda obj:np.fabs(z - \
                        obj['redshift']))
                    cosmology_splice.append(self.splice_outputs[0])

                # Move forward from last slice in stack until z > z_max.
                else:
                    current_slice = cosmology_splice[-1]
                    while current_slice['next'] is not None and \
                            (z < current_slice['next']['redshift'] or \
                                 np.abs(z - current_slice['next']['redshift']) <
                                 z_Tolerance):
                        current_slice = current_slice['next']

                    if current_slice is cosmology_splice[-1]:
                        near_redshift = cosmology_splice[-1]['redshift'] - \
                          cosmology_splice[-1]['dz_max']
                        mylog.error("Cosmology splice incomplete due to insufficient data outputs.")
                        break
                    else:
                        cosmology_splice.append(current_slice)

                z = cosmology_splice[-1]['redshift'] - \
                  cosmology_splice[-1]['dz_max']

        # Make light ray using maximum number of datasets (minimum spacing).
        else:
            # Sort data outputs by proximity to current redsfhit.
            self.splice_outputs.sort(key=lambda obj:np.abs(far_redshift -
                                                           obj['redshift']))
            # For first data dump, choose closest to desired redshift.
            cosmology_splice.append(self.splice_outputs[0])

            nextOutput = cosmology_splice[-1]['next']
            while (nextOutput is not None):
                if (nextOutput['redshift'] <= near_redshift):
                    break
                if ((cosmology_splice[-1]['redshift'] - nextOutput['redshift']) >
                    cosmology_splice[-1]['dz_min']):
                    cosmology_splice.append(nextOutput)
                nextOutput = nextOutput['next']
            if (cosmology_splice[-1]['redshift'] -
                cosmology_splice[-1]['dz_max']) > near_redshift:
                mylog.error("Cosmology splice incomplete due to insufficient data outputs.")
                near_redshift = cosmology_splice[-1]['redshift'] - \
                  cosmology_splice[-1]['dz_max']

        mylog.info("create_cosmology_splice: Used %d data dumps to get from z = %f to %f." %
                   (len(cosmology_splice), far_redshift, near_redshift))
        
        # change the 'next' and 'previous' pointers to point to the correct outputs for the created
        # splice
        for i, output in enumerate(cosmology_splice):
            if len(cosmology_splice) == 1:
                output['previous'] = None
                output['next'] = None
            elif i == 0:
                output['previous'] = None
                output['next'] = cosmology_splice[i + 1]
            elif i == len(cosmology_splice) - 1:
                output['previous'] = cosmology_splice[i - 1]
                output['next'] = None
            else:
                output['previous'] = cosmology_splice[i - 1]
                output['next'] = cosmology_splice[i + 1]
        
        self.splice_outputs.sort(key=lambda obj: obj['time'])
        return cosmology_splice

    def plan_cosmology_splice(self, near_redshift, far_redshift,
                              decimals=3, filename=None,
                              start_index=0):
        r"""Create imaginary list of redshift outputs to maximally
        span a redshift interval.

        If you want to run a cosmological simulation that will have just
        enough data outputs to create a cosmology splice,
        this method will calculate a list of redshifts outputs that will
        minimally connect a redshift interval.

        Parameters
        ----------
        near_redshift : float
            The nearest (lowest) redshift in the cosmology splice list.
        far_redshift : float
            The furthest (highest) redshift in the cosmology splice list.
        decimals : int
            The decimal place to which the output redshift will be rounded.
            If the decimal place in question is nonzero, the redshift will
            be rounded up to
            ensure continuity of the splice.  Default: 3.
        filename : string
            If provided, a file will be written with the redshift outputs in
            the form in which they should be given in the enzo dataset.
            Default: None.
        start_index : int
            The index of the first redshift output.  Default: 0.

        Examples
        --------
        >>> from yt.analysis_modules.api import CosmologySplice
        >>> my_splice = CosmologySplice('enzo_tiny_cosmology/32Mpc_32.enzo', 'Enzo')
        >>> my_splice.plan_cosmology_splice(0.0, 0.1, filename='redshifts.out')

        """

        z = far_redshift
        outputs = []

        while z > near_redshift:
            rounded = np.round(z, decimals=decimals)
            if rounded - z < 0:
                rounded += np.power(10.0, (-1.0*decimals))
            z = rounded

            deltaz_max = self._deltaz_forward(z, self.simulation.box_size)
            outputs.append({'redshift': z, 'dz_max': deltaz_max})
            z -= deltaz_max

        mylog.info("%d data dumps will be needed to get from z = %f to %f." %
                   (len(outputs), near_redshift, far_redshift))

        if filename is not None:
            self.simulation._write_cosmology_outputs(filename, outputs,
                                                     start_index,
                                                     decimals=decimals)
        return outputs

    def _calculate_deltaz_max(self):
        r"""Calculate delta z that corresponds to full box length going
        from z to (z - delta z).
        """

        d_Tolerance = 1e-4
        max_Iterations = 100

        target_distance = self.simulation.box_size

        for output in self.splice_outputs:
            z = output['redshift']

            # Calculate delta z that corresponds to the length of the box
            # at a given redshift using Newton's method.
            z1 = z
            z2 = z1 - 0.1 # just an initial guess
            distance1 = self.simulation.quan(0.0, "Mpccm / h")
            distance2 = self.cosmology.comoving_radial_distance(z2, z)
            iteration = 1

            while ((np.abs(distance2-target_distance)/distance2) > d_Tolerance):
                m = (distance2 - distance1) / (z2 - z1)
                z1 = z2
                distance1 = distance2
                z2 = ((target_distance - distance2) / m.in_units("Mpccm / h")) + z2
                distance2 = self.cosmology.comoving_radial_distance(z2, z)
                iteration += 1
                if (iteration > max_Iterations):
                    mylog.error("calculate_deltaz_max: Warning - max iterations " +
                                "exceeded for z = %f (delta z = %f)." %
                                (z, np.abs(z2 - z)))
                    break
            output['dz_max'] = np.abs(z2 - z)
            
    def _calculate_deltaz_min(self, deltaz_min=0.0):
        r"""Calculate delta z that corresponds to a single top grid pixel
        going from z to (z - delta z).
        """

        d_Tolerance = 1e-4
        max_Iterations = 100

        target_distance = self.simulation.box_size / \
          self.simulation.domain_dimensions[0]

        for output in self.splice_outputs:
            z = output['redshift']

            # Calculate delta z that corresponds to the length of a
            # top grid pixel at a given redshift using Newton's method.
            z1 = z
            z2 = z1 - 0.01 # just an initial guess
            distance1 = self.simulation.quan(0.0, "Mpccm / h")
            distance2 = self.cosmology.comoving_radial_distance(z2, z)
            iteration = 1

            while ((np.abs(distance2 - target_distance) / distance2) > d_Tolerance):
                m = (distance2 - distance1) / (z2 - z1)
                z1 = z2
                distance1 = distance2
                z2 = ((target_distance - distance2) / m.in_units("Mpccm / h")) + z2
                distance2 = self.cosmology.comoving_radial_distance(z2, z)
                iteration += 1
                if (iteration > max_Iterations):
                    mylog.error("calculate_deltaz_max: Warning - max iterations " +
                                "exceeded for z = %f (delta z = %f)." %
                                (z, np.abs(z2 - z)))
                    break
            # Use this calculation or the absolute minimum specified by the user.
            output['dz_min'] = max(np.abs(z2 - z), deltaz_min)

    def _deltaz_forward(self, z, target_distance):
        r"""Calculate deltaz corresponding to moving a comoving distance
        starting from some redshift.
        """

        d_Tolerance = 1e-4
        max_Iterations = 100

        # Calculate delta z that corresponds to the length of the
        # box at a given redshift.
        z1 = z
        z2 = z1 - 0.1 # just an initial guess
        distance1 = self.cosmology.quan(0.0, "Mpccm / h")
        distance2 = self.cosmology.comoving_radial_distance(z2, z)
        iteration = 1

        while ((np.abs(distance2 - target_distance)/distance2) > d_Tolerance):
            m = (distance2 - distance1) / (z2 - z1)
            z1 = z2
            distance1 = distance2
            z2 = ((target_distance - distance2) / m.in_units("Mpccm / h")) + z2
            distance2 = self.cosmology.comoving_radial_distance(z2, z)
            iteration += 1
            if (iteration > max_Iterations):
                mylog.error("deltaz_forward: Warning - max iterations " +
                            "exceeded for z = %f (delta z = %f)." %
                            (z, np.abs(z2 - z)))
                break
        return np.abs(z2 - z)
예제 #44
0
class LightRay(CosmologySplice):
    """
    LightRay(parameter_filename, simulation_type=None,
             near_redshift=None, far_redshift=None,
             use_minimum_datasets=True, deltaz_min=0.0,
             minimum_coherent_box_fraction=0.0,
             time_data=True, redshift_data=True,
             find_outputs=False, load_kwargs=None):

    Create a LightRay object.  A light ray is much like a light cone,
    in that it stacks together multiple datasets in order to extend a
    redshift interval.  Unlike a light cone, which does randomly
    oriented projections for each dataset, a light ray consists of
    randomly oriented single rays.  The purpose of these is to create
    synthetic QSO lines of sight.

    Light rays can also be made from single datasets.
    
    Once the LightRay object is set up, use LightRay.make_light_ray to
    begin making rays.  Different randomizations can be created with a
    single object by providing different random seeds to make_light_ray.

    Parameters
    ----------
    parameter_filename : string
        The path to the simulation parameter file or dataset.
    simulation_type : optional, string
        The simulation type.  If None, the first argument is assumed to 
        refer to a single dataset.
        Default: None
    near_redshift : optional, float
        The near (lowest) redshift for a light ray containing multiple 
        datasets.  Do not use is making a light ray from a single 
        dataset.
        Default: None
    far_redshift : optional, float
        The far (highest) redshift for a light ray containing multiple 
        datasets.  Do not use is making a light ray from a single 
        dataset.
        Default: None
    use_minimum_datasets : optional, bool
        If True, the minimum number of datasets is used to connect the
        initial and final redshift.  If false, the light ray solution
        will contain as many entries as possible within the redshift
        interval.
        Default: True.
    deltaz_min : optional, float
        Specifies the minimum :math:`\Delta z` between consecutive
        datasets in the returned list.
        Default: 0.0.
    minimum_coherent_box_fraction : optional, float
        Used with use_minimum_datasets set to False, this parameter
        specifies the fraction of the total box size to be traversed
        before rerandomizing the projection axis and center.  This
        was invented to allow light rays with thin slices to sample
        coherent large scale structure, but in practice does not work
        so well.  Try setting this parameter to 1 and see what happens.
        Default: 0.0.
    time_data : optional, bool
        Whether or not to include time outputs when gathering
        datasets for time series.
        Default: True.
    redshift_data : optional, bool
        Whether or not to include redshift outputs when gathering
        datasets for time series.
        Default: True.
    find_outputs : optional, bool
        Whether or not to search for datasets in the current 
        directory.
        Default: False.
    load_kwargs : optional, dict
        Optional dictionary of kwargs to be passed to the "load" 
        function, appropriate for use of certain frontends.  E.g.
        Tipsy using "bounding_box"
        Gadget using "unit_base", etc.
        Default : None

    """
    def __init__(self, parameter_filename, simulation_type=None,
                 near_redshift=None, far_redshift=None,
                 use_minimum_datasets=True, deltaz_min=0.0,
                 minimum_coherent_box_fraction=0.0,
                 time_data=True, redshift_data=True,
                 find_outputs=False, load_kwargs=None):

        self.near_redshift = near_redshift
        self.far_redshift = far_redshift
        self.use_minimum_datasets = use_minimum_datasets
        self.deltaz_min = deltaz_min
        self.minimum_coherent_box_fraction = minimum_coherent_box_fraction
        self.parameter_filename = parameter_filename
        if load_kwargs is None:
            self.load_kwargs = {}
        else:
            self.load_kwargs = load_kwargs
        self.light_ray_solution = []
        self._data = {}

        # Make a light ray from a single, given dataset.        
        if simulation_type is None:
            ds = load(parameter_filename, **self.load_kwargs)
            if ds.cosmological_simulation:
                redshift = ds.current_redshift
                self.cosmology = Cosmology(
                    hubble_constant=ds.hubble_constant,
                    omega_matter=ds.omega_matter,
                    omega_lambda=ds.omega_lambda,
                    unit_registry=ds.unit_registry)
            else:
                redshift = 0.
            self.light_ray_solution.append({"filename": parameter_filename,
                                            "redshift": redshift})

        # Make a light ray from a simulation time-series.
        else:
            # Get list of datasets for light ray solution.
            CosmologySplice.__init__(self, parameter_filename, simulation_type,
                                     find_outputs=find_outputs)
            self.light_ray_solution = \
              self.create_cosmology_splice(self.near_redshift, self.far_redshift,
                                           minimal=self.use_minimum_datasets,
                                           deltaz_min=self.deltaz_min,
                                           time_data=time_data,
                                           redshift_data=redshift_data)

    def _calculate_light_ray_solution(self, seed=None, 
                                      start_position=None, end_position=None,
                                      trajectory=None, filename=None):
        "Create list of datasets to be added together to make the light ray."

        # Calculate dataset sizes, and get random dataset axes and centers.
        np.random.seed(seed)

        # If using only one dataset, set start and stop manually.
        if start_position is not None:
            if len(self.light_ray_solution) > 1:
                raise RuntimeError("LightRay Error: cannot specify start_position " + \
                                   "if light ray uses more than one dataset.")
            if not ((end_position is None) ^ (trajectory is None)):
                raise RuntimeError("LightRay Error: must specify either end_position " + \
                                   "or trajectory, but not both.")
            self.light_ray_solution[0]['start'] = np.array(start_position)
            if end_position is not None:
                self.light_ray_solution[0]['end'] = np.array(end_position)
            else:
                # assume trajectory given as r, theta, phi
                if len(trajectory) != 3:
                    raise RuntimeError("LightRay Error: trajectory must have length 3.")
                r, theta, phi = trajectory
                self.light_ray_solution[0]['end'] = self.light_ray_solution[0]['start'] + \
                  r * np.array([np.cos(phi) * np.sin(theta),
                                np.sin(phi) * np.sin(theta),
                                np.cos(theta)])
            self.light_ray_solution[0]['traversal_box_fraction'] = \
              vector_length(self.light_ray_solution[0]['start'], 
                            self.light_ray_solution[0]['end'])

        # the normal way (random start positions and trajectories for each dataset)
        else:
            
            # For box coherence, keep track of effective depth travelled.
            box_fraction_used = 0.0

            for q in range(len(self.light_ray_solution)):
                if (q == len(self.light_ray_solution) - 1):
                    z_next = self.near_redshift
                else:
                    z_next = self.light_ray_solution[q+1]['redshift']

                # Calculate fraction of box required for a depth of delta z
                self.light_ray_solution[q]['traversal_box_fraction'] = \
                    self.cosmology.comoving_radial_distance(z_next, \
                        self.light_ray_solution[q]['redshift']).in_units("Mpccm / h") / \
                        self.simulation.box_size

                # Simple error check to make sure more than 100% of box depth
                # is never required.
                if (self.light_ray_solution[q]['traversal_box_fraction'] > 1.0):
                    mylog.error("Warning: box fraction required to go from z = %f to %f is %f" %
                                (self.light_ray_solution[q]['redshift'], z_next,
                                 self.light_ray_solution[q]['traversal_box_fraction']))
                    mylog.error("Full box delta z is %f, but it is %f to the next data dump." %
                                (self.light_ray_solution[q]['dz_max'],
                                 self.light_ray_solution[q]['redshift']-z_next))

                # Get dataset axis and center.
                # If using box coherence, only get start point and vector if
                # enough of the box has been used,
                # or if box_fraction_used will be greater than 1 after this slice.
                if (q == 0) or (self.minimum_coherent_box_fraction == 0) or \
                        (box_fraction_used >
                         self.minimum_coherent_box_fraction) or \
                        (box_fraction_used +
                         self.light_ray_solution[q]['traversal_box_fraction'] > 1.0):
                    # Random start point
                    self.light_ray_solution[q]['start'] = np.random.random(3)
                    theta = np.pi * np.random.random()
                    phi = 2 * np.pi * np.random.random()
                    box_fraction_used = 0.0
                else:
                    # Use end point of previous segment and same theta and phi.
                    self.light_ray_solution[q]['start'] = \
                      self.light_ray_solution[q-1]['end'][:]

                self.light_ray_solution[q]['end'] = \
                  self.light_ray_solution[q]['start'] + \
                    self.light_ray_solution[q]['traversal_box_fraction'] * \
                    np.array([np.cos(phi) * np.sin(theta),
                              np.sin(phi) * np.sin(theta),
                              np.cos(theta)])
                box_fraction_used += \
                  self.light_ray_solution[q]['traversal_box_fraction']

        if filename is not None:
            self._write_light_ray_solution(filename,
                extra_info={'parameter_filename':self.parameter_filename,
                            'random_seed':seed,
                            'far_redshift':self.far_redshift,
                            'near_redshift':self.near_redshift})

    def make_light_ray(self, seed=None,
                       start_position=None, end_position=None,
                       trajectory=None,
                       fields=None, setup_function=None,
                       solution_filename=None, data_filename=None,
                       get_los_velocity=True, redshift=None,
                       njobs=-1):
        """
        make_light_ray(seed=None, start_position=None, end_position=None,
                       trajectory=None, fields=None, setup_function=None,
                       solution_filename=None, data_filename=None,
                       get_los_velocity=True, redshift=None,
                       njobs=-1)

        Create a light ray and get field values for each lixel.  A light
        ray consists of a list of field values for cells intersected by
        the ray and the path length of the ray through those cells.
        Light ray data can be written out to an hdf5 file.

        Parameters
        ----------
        seed : optional, int
            Seed for the random number generator.
            Default: None.
        start_position : optional, list of floats
            Used only if creating a light ray from a single dataset.
            The coordinates of the starting position of the ray.
            Default: None.
        end_position : optional, list of floats
            Used only if creating a light ray from a single dataset.
            The coordinates of the ending position of the ray.
            Default: None.
        trajectory : optional, list of floats
            Used only if creating a light ray from a single dataset.
            The (r, theta, phi) direction of the light ray.  Use either 
            end_position or trajectory, not both.
            Default: None.
        fields : optional, list
            A list of fields for which to get data.
            Default: None.
        setup_function : optional, callable, accepts a ds
            This function will be called on each dataset that is loaded 
            to create the light ray.  For, example, this can be used to 
            add new derived fields.
            Default: None.
        solution_filename : optional, string
            Path to a text file where the trajectories of each
            subray is written out.
            Default: None.
        data_filename : optional, string
            Path to output file for ray data.
            Default: None.
        get_los_velocity : optional, bool
            If True, the line of sight velocity is calculated for
            each point in the ray.
            Default: True.
        redshift : optional, float
            Used with light rays made from single datasets to specify a 
            starting redshift for the ray.  If not used, the starting 
            redshift will be 0 for a non-cosmological dataset and 
            the dataset redshift for a cosmological dataset.
            Default: None.
        njobs : optional, int
            The number of parallel jobs over which the segments will 
            be split.  Choose -1 for one processor per segment.
            Default: -1.

        Examples
        --------

        Make a light ray from multiple datasets:
        
        >>> import yt
        >>> from yt.analysis_modules.cosmological_observation.light_ray.api import \
        ...     LightRay
        >>> my_ray = LightRay("enzo_tiny_cosmology/32Mpc_32.enzo", "Enzo",
        ...                   0., 0.1, time_data=False)
        ...
        >>> my_ray.make_light_ray(seed=12345,
        ...                       solution_filename="solution.txt",
        ...                       data_filename="my_ray.h5",
        ...                       fields=["temperature", "density"],
        ...                       get_los_velocity=True)

        Make a light ray from a single dataset:

        >>> import yt
        >>> from yt.analysis_modules.cosmological_observation.light_ray.api import \
        ...     LightRay
        >>> my_ray = LightRay("IsolatedGalaxy/galaxy0030/galaxy0030")
        ...
        >>> my_ray.make_light_ray(start_position=[0., 0., 0.],
        ...                       end_position=[1., 1., 1.],
        ...                       solution_filename="solution.txt",
        ...                       data_filename="my_ray.h5",
        ...                       fields=["temperature", "density"],
        ...                       get_los_velocity=True)
        
        """

        # Calculate solution.
        self._calculate_light_ray_solution(seed=seed, 
                                           start_position=start_position, 
                                           end_position=end_position,
                                           trajectory=trajectory,
                                           filename=solution_filename)

        # Initialize data structures.
        self._data = {}
        if fields is None: fields = []
        data_fields = fields[:]
        all_fields = fields[:]
        all_fields.extend(['dl', 'dredshift', 'redshift'])
        if get_los_velocity:
            all_fields.extend(['velocity_x', 'velocity_y',
                               'velocity_z', 'velocity_los'])
            data_fields.extend(['velocity_x', 'velocity_y', 'velocity_z'])

        all_ray_storage = {}
        for my_storage, my_segment in parallel_objects(self.light_ray_solution,
                                                       storage=all_ray_storage,
                                                       njobs=njobs):

            # Load dataset for segment.
            ds = load(my_segment['filename'], **self.load_kwargs)

            my_segment['unique_identifier'] = ds.unique_identifier
            if redshift is not None:
                if ds.cosmological_simulation and redshift != ds.current_redshift:
                    mylog.warn("Generating light ray with different redshift than " +
                               "the dataset itself.")
                my_segment["redshift"] = redshift

            if setup_function is not None:
                setup_function(ds)

            if start_position is not None:
                my_segment["start"] = ds.arr(my_segment["start"], "code_length")
                my_segment["end"] = ds.arr(my_segment["end"], "code_length")
            else:
                my_segment["start"] = ds.domain_width * my_segment["start"] + \
                  ds.domain_left_edge
                my_segment["end"] = ds.domain_width * my_segment["end"] + \
                  ds.domain_left_edge

            if not ds.cosmological_simulation:
                next_redshift = my_segment["redshift"]
            elif self.near_redshift == self.far_redshift:
                next_redshift = my_segment["redshift"] - \
                  self._deltaz_forward(my_segment["redshift"], 
                                       ds.domain_width[0].in_units("Mpccm / h") *
                                       my_segment["traversal_box_fraction"])
            elif my_segment.get("next", None) is None:
                next_redshift = self.near_redshift
            else:
                next_redshift = my_segment['next']['redshift']

            mylog.info("Getting segment at z = %s: %s to %s." %
                       (my_segment['redshift'], my_segment['start'],
                        my_segment['end']))

            # Break periodic ray into non-periodic segments.
            sub_segments = periodic_ray(my_segment['start'], my_segment['end'],
                                        left=ds.domain_left_edge,
                                        right=ds.domain_right_edge)

            # Prepare data structure for subsegment.
            sub_data = {}
            sub_data['segment_redshift'] = my_segment['redshift']
            for field in all_fields:
                sub_data[field] = []

            # Get data for all subsegments in segment.
            for sub_segment in sub_segments:
                mylog.info("Getting subsegment: %s to %s." %
                           (list(sub_segment[0]), list(sub_segment[1])))
                sub_ray = ds.ray(sub_segment[0], sub_segment[1])
                asort = np.argsort(sub_ray["t"])
                sub_data['dl'].extend(sub_ray['dts'][asort] *
                                      vector_length(sub_ray.start_point,
                                                    sub_ray.end_point))
                for field in data_fields:
                    sub_data[field].extend(sub_ray[field][asort])

                if get_los_velocity:
                    line_of_sight = sub_segment[1] - sub_segment[0]
                    line_of_sight /= ((line_of_sight**2).sum())**0.5
                    sub_vel = ds.arr([sub_ray['velocity_x'],
                                      sub_ray['velocity_y'],
                                      sub_ray['velocity_z']])
                    sub_data['velocity_los'].extend((np.rollaxis(sub_vel, 1) *
                                                     line_of_sight).sum(axis=1)[asort])
                    del sub_vel

                sub_ray.clear_data()
                del sub_ray, asort

            for key in sub_data:
                sub_data[key] = ds.arr(sub_data[key]).in_cgs()

            # Get redshift for each lixel.  Assume linear relation between l and z.
            sub_data['dredshift'] = (my_segment['redshift'] - next_redshift) * \
                (sub_data['dl'] / vector_length(my_segment['start'], 
                                                my_segment['end']).in_cgs())
            sub_data['redshift'] = my_segment['redshift'] - \
              sub_data['dredshift'].cumsum() + sub_data['dredshift']

            # Remove empty lixels.
            sub_dl_nonzero = sub_data['dl'].nonzero()
            for field in all_fields:
                sub_data[field] = sub_data[field][sub_dl_nonzero]
            del sub_dl_nonzero

            # Add to storage.
            my_storage.result = sub_data

            del ds

        # Reconstruct ray data from parallel_objects storage.
        all_data = [my_data for my_data in all_ray_storage.values()]
        # This is now a list of segments where each one is a dictionary
        # with all the fields.
        all_data.sort(key=lambda a:a['segment_redshift'], reverse=True)
        # Flatten the list into a single dictionary containing fields
        # for the whole ray.
        all_data = _flatten_dict_list(all_data, exceptions=['segment_redshift'])

        if data_filename is not None:
            self._write_light_ray(data_filename, all_data)

        self._data = all_data
        return all_data

    @parallel_root_only
    def _write_light_ray(self, filename, data):
        """
        _write_light_ray(filename, data)

        Write light ray data to hdf5 file.
        """

        mylog.info("Saving light ray data to %s." % filename)
        output = h5py.File(filename, 'w')
        for field in data.keys():
            # if the field is a tuple, only use the second part of the tuple
            # in the hdf5 output (i.e. ('gas', 'density') -> 'density')
            if isinstance(field, tuple):
                fieldname = field[1]
            else:
                fieldname = field
            output.create_dataset(fieldname, data=data[field])
            output[fieldname].attrs["units"] = str(data[field].units)
        output.close()

    @parallel_root_only
    def _write_light_ray_solution(self, filename, extra_info=None):
        """
        _write_light_ray_solution(filename, extra_info=None)

        Write light ray solution to a file.
        """

        mylog.info("Writing light ray solution to %s." % filename)
        f = open(filename, 'w')
        if extra_info is not None:
            for par, val in extra_info.items():
                f.write("%s = %s\n" % (par, val))
        f.write("\nSegment Redshift dl/box    Start x       y             " + \
                "z             End x         y             z            Dataset\n")
        for q, my_segment in enumerate(self.light_ray_solution):
            f.write("%04d    %.6f %.6f % .10f % .10f % .10f % .10f % .10f % .10f %s\n" % \
                    (q, my_segment['redshift'], my_segment['traversal_box_fraction'],
                     my_segment['start'][0], my_segment['start'][1], my_segment['start'][2],
                     my_segment['end'][0], my_segment['end'][1], my_segment['end'][2],
                     my_segment['filename']))
        f.close()
class GadgetSimulation(SimulationTimeSeries):
    r"""
    Initialize an Gadget Simulation object.

    Upon creation, the parameter file is parsed and the time and redshift
    are calculated and stored in all_outputs.  A time units dictionary is
    instantiated to allow for time outputs to be requested with physical
    time units.  The get_time_series can be used to generate a
    DatasetSeries object.

    parameter_filename : str
        The simulation parameter file.
    find_outputs : bool
        If True, the OutputDir directory is searched for datasets.  
        Time and redshift information are gathered by temporarily 
        instantiating each dataset.  This can be used when simulation 
        data was created in a non-standard way, making it difficult 
        to guess the corresponding time and redshift information.
        Default: False.

    Examples
    --------
    >>> import yt
    >>> gs = yt.simulation("my_simulation.par", "Gadget")
    >>> gs.get_time_series()
    >>> for ds in gs:
    ...     print ds.current_time

    """

    def __init__(self, parameter_filename, find_outputs=False):
        self.simulation_type = "particle"
        self.dimensionality = 3
        SimulationTimeSeries.__init__(self, parameter_filename,
                                      find_outputs=find_outputs)

    def _set_units(self):
        self.unit_registry = UnitRegistry()
        self.time_unit = self.quan(1.0, "s")
        if self.cosmological_simulation:
            # Instantiate Cosmology object for units and time conversions.
            self.cosmology = \
              Cosmology(hubble_constant=self.hubble_constant,
                        omega_matter=self.omega_matter,
                        omega_lambda=self.omega_lambda,
                        unit_registry=self.unit_registry)
            self.unit_registry.modify("h", self.hubble_constant)
            # Comoving lengths
            for my_unit in ["m", "pc", "AU", "au"]:
                new_unit = "%scm" % my_unit
                # technically not true, but should be ok
                self.unit_registry.add(
                    new_unit, self.unit_registry.lut[my_unit][0],
                    dimensions.length, "\\rm{%s}/(1+z)" % my_unit)
            self.length_unit = self.quan(self.unit_base["UnitLength_in_cm"],
                                         "cmcm / h", registry=self.unit_registry)
            self.box_size *= self.length_unit.in_units("Mpccm / h")
        else:
            # Read time from file for non-cosmological sim
            self.time_unit = self.quan(
                self.unit_base["UnitLength_in_cm"]/ \
                    self.unit_base["UnitVelocity_in_cm_per_s"], "s")
            self.unit_registry.add("code_time", 1.0, dimensions.time)
            self.unit_registry.modify("code_time", self.time_unit)
            # Length
            self.length_unit = self.quan(
                self.unit_base["UnitLength_in_cm"],"cm")
            self.unit_registry.add("code_length", 1.0, dimensions.length)
            self.unit_registry.modify("code_length", self.length_unit)

    def get_time_series(self, initial_time=None, final_time=None,
                        initial_redshift=None, final_redshift=None,
                        times=None, redshifts=None, tolerance=None,
                        parallel=True, setup_function=None):

        """
        Instantiate a DatasetSeries object for a set of outputs.

        If no additional keywords given, a DatasetSeries object will be
        created with all potential datasets created by the simulation.

        Outputs can be gather by specifying a time or redshift range
        (or combination of time and redshift), with a specific list of
        times or redshifts), or by simply searching all subdirectories 
        within the simulation directory.

        initial_time : tuple of type (float, str)
            The earliest time for outputs to be included.  This should be 
            given as the value and the string representation of the units.
            For example, (5.0, "Gyr").  If None, the initial time of the 
            simulation is used.  This can be used in combination with 
            either final_time or final_redshift.
            Default: None.
        final_time : tuple of type (float, str)
            The latest time for outputs to be included.  This should be 
            given as the value and the string representation of the units.
            For example, (13.7, "Gyr"). If None, the final time of the 
            simulation is used.  This can be used in combination with either 
            initial_time or initial_redshift.
            Default: None.
        times : tuple of type (float array, str)
            A list of times for which outputs will be found and the units 
            of those values.  For example, ([0, 1, 2, 3], "s").
            Default: None.
        initial_redshift : float
            The earliest redshift for outputs to be included.  If None,
            the initial redshift of the simulation is used.  This can be
            used in combination with either final_time or
            final_redshift.
            Default: None.
        final_redshift : float
            The latest redshift for outputs to be included.  If None,
            the final redshift of the simulation is used.  This can be
            used in combination with either initial_time or
            initial_redshift.
            Default: None.
        redshifts : array_like
            A list of redshifts for which outputs will be found.
            Default: None.
        tolerance : float
            Used in combination with "times" or "redshifts" keywords,
            this is the tolerance within which outputs are accepted
            given the requested times or redshifts.  If None, the
            nearest output is always taken.
            Default: None.
        parallel : bool/int
            If True, the generated DatasetSeries will divide the work
            such that a single processor works on each dataset.  If an
            integer is supplied, the work will be divided into that
            number of jobs.
            Default: True.
        setup_function : callable, accepts a ds
            This function will be called whenever a dataset is loaded.

        Examples
        --------

        >>> import yt
        >>> gs = yt.simulation("my_simulation.par", "Gadget")
        
        >>> gs.get_time_series(initial_redshift=10, final_time=(13.7, "Gyr"))

        >>> gs.get_time_series(redshifts=[3, 2, 1, 0])

        >>> # after calling get_time_series
        >>> for ds in gs.piter():
        ...     p = ProjectionPlot(ds, "x", "density")
        ...     p.save()

        >>> # An example using the setup_function keyword
        >>> def print_time(ds):
        ...     print ds.current_time
        >>> gs.get_time_series(setup_function=print_time)
        >>> for ds in gs:
        ...     SlicePlot(ds, "x", "Density").save()

        """

        if (initial_redshift is not None or \
            final_redshift is not None) and \
            not self.cosmological_simulation:
            raise InvalidSimulationTimeSeries(
                "An initial or final redshift has been given for a " +
                "noncosmological simulation.")

        my_all_outputs = self.all_outputs
        if not my_all_outputs:
            DatasetSeries.__init__(self, outputs=[], parallel=parallel,
                                   unit_base=self.unit_base)
            mylog.info("0 outputs loaded into time series.")
            return

        # Apply selection criteria to the set.
        if times is not None:
            my_outputs = self._get_outputs_by_key("time", times,
                                                  tolerance=tolerance,
                                                  outputs=my_all_outputs)

        elif redshifts is not None:
            my_outputs = self._get_outputs_by_key("redshift",
                                                  redshifts, tolerance=tolerance,
                                                  outputs=my_all_outputs)

        else:
            if initial_time is not None:
                if isinstance(initial_time, float):
                    initial_time = self.quan(initial_time, "code_time")
                elif isinstance(initial_time, tuple) and len(initial_time) == 2:
                    initial_time = self.quan(*initial_time)
                elif not isinstance(initial_time, YTArray):
                    raise RuntimeError(
                        "Error: initial_time must be given as a float or " +
                        "tuple of (value, units).")
            elif initial_redshift is not None:
                my_initial_time = self.cosmology.t_from_z(initial_redshift)
            else:
                my_initial_time = self.initial_time

            if final_time is not None:
                if isinstance(final_time, float):
                    final_time = self.quan(final_time, "code_time")
                elif isinstance(final_time, tuple) and len(final_time) == 2:
                    final_time = self.quan(*final_time)
                elif not isinstance(final_time, YTArray):
                    raise RuntimeError(
                        "Error: final_time must be given as a float or " +
                        "tuple of (value, units).")
                my_final_time = final_time.in_units("s")
            elif final_redshift is not None:
                my_final_time = self.cosmology.t_from_z(final_redshift)
            else:
                my_final_time = self.final_time

            my_initial_time.convert_to_units("s")
            my_final_time.convert_to_units("s")
            my_times = np.array([a["time"] for a in my_all_outputs])
            my_indices = np.digitize([my_initial_time, my_final_time], my_times)
            if my_initial_time == my_times[my_indices[0] - 1]: my_indices[0] -= 1
            my_outputs = my_all_outputs[my_indices[0]:my_indices[1]]

        init_outputs = []
        for output in my_outputs:
            if os.path.exists(output["filename"]):
                init_outputs.append(output["filename"])
        if len(init_outputs) == 0 and len(my_outputs) > 0:
            mylog.warn("Could not find any datasets.  " +
                       "Check the value of OutputDir in your parameter file.")
            
        DatasetSeries.__init__(self, outputs=init_outputs, parallel=parallel,
                                setup_function=setup_function,
                                unit_base=self.unit_base)
        mylog.info("%d outputs loaded into time series.", len(init_outputs))

    def _parse_parameter_file(self):
        """
        Parses the parameter file and establishes the various
        dictionaries.
        """

        self.unit_base = {}

        # Let's read the file
        lines = open(self.parameter_filename).readlines()
        comments = ["%", ";"]
        for line in (l.strip() for l in lines):
            for comment in comments:
                if comment in line: line = line[0:line.find(comment)]
            if len(line) < 2: continue
            param, vals = (i.strip() for i in line.split(None, 1))
            # First we try to decipher what type of value it is.
            vals = vals.split()
            # Special case approaching.
            if "(do" in vals: vals = vals[:1]
            if len(vals) == 0:
                pcast = str # Assume NULL output
            else:
                v = vals[0]
                # Figure out if it's castable to floating point:
                try:
                    float(v)
                except ValueError:
                    pcast = str
                else:
                    if any("." in v or "e" in v for v in vals):
                        pcast = float
                    elif v == "inf":
                        pcast = str
                    else:
                        pcast = int
            # Now we figure out what to do with it.
            if param.startswith("Unit"):
                self.unit_base[param] = float(vals[0])
            if len(vals) == 0:
                vals = ""
            elif len(vals) == 1:
                vals = pcast(vals[0])
            else:
                vals = np.array([pcast(i) for i in vals])

            self.parameters[param] = vals

        if self.parameters["ComovingIntegrationOn"]:
            cosmo_attr = {"box_size": "BoxSize",
                          "omega_lambda": "OmegaLambda",
                          "omega_matter": "Omega0",
                          "hubble_constant": "HubbleParam"}
            self.initial_redshift = 1.0 / self.parameters["TimeBegin"] - 1.0
            self.final_redshift = 1.0 / self.parameters["TimeMax"] - 1.0
            self.cosmological_simulation = 1
            for a, v in cosmo_attr.items():
                if not v in self.parameters:
                    raise MissingParameter(self.parameter_filename, v)
                setattr(self, a, self.parameters[v])
        else:
            self.cosmological_simulation = 0
            self.omega_lambda = self.omega_matter = \
                self.hubble_constant = 0.0

    def _snapshot_format(self, index=None):
        """
        The snapshot filename for a given index.  Modify this for different 
        naming conventions.
        """

        if self.parameters["OutputDir"].startswith("/"):
            data_dir = self.parameters["OutputDir"]
        else:
            data_dir = os.path.join(self.directory,
                                    self.parameters["OutputDir"])
        if self.parameters["NumFilesPerSnapshot"] > 1:
            suffix = ".0"
        else:
            suffix = ""
        if self.parameters["SnapFormat"] == 3:
            suffix += ".hdf5"
        if index is None:
            count = "*"
        else:
            count = "%03d" % index
        filename = "%s_%s%s" % (self.parameters["SnapshotFileBase"],
                                count, suffix)
        return os.path.join(data_dir, filename)
                
    def _get_all_outputs(self, find_outputs=False):
        """
        Get all potential datasets and combine into a time-sorted list.
        """

        # Create the set of outputs from which further selection will be done.
        if find_outputs:
            self._find_outputs()
        else:
            if self.parameters["OutputListOn"]:
                a_values = [float(a) for a in 
                           file(self.parameters["OutputListFilename"], "r").readlines()]
            else:
                a_values = [float(self.parameters["TimeOfFirstSnapshot"])]
                time_max = float(self.parameters["TimeMax"])
                while a_values[-1] < time_max:
                    if self.cosmological_simulation:
                        a_values.append(
                            a_values[-1] * self.parameters["TimeBetSnapshot"])
                    else:
                        a_values.append(
                            a_values[-1] + self.parameters["TimeBetSnapshot"])
                if a_values[-1] > time_max:
                    a_values[-1] = time_max

            if self.cosmological_simulation:
                self.all_outputs = \
                  [{"filename": self._snapshot_format(i),
                    "redshift": (1. / a - 1)}
                   for i, a in enumerate(a_values)]
                
                # Calculate times for redshift outputs.
                for output in self.all_outputs:
                    output["time"] = self.cosmology.t_from_z(output["redshift"])
            else:
                self.all_outputs = \
                  [{"filename": self._snapshot_format(i),
                    "time": self.quan(a, "code_time")}
                   for i, a in enumerate(a_values)]

            self.all_outputs.sort(key=lambda obj:obj["time"].to_ndarray())

    def _calculate_simulation_bounds(self):
        """
        Figure out the starting and stopping time and redshift for the simulation.
        """

        # Convert initial/final redshifts to times.
        if self.cosmological_simulation:
            self.initial_time = self.cosmology.t_from_z(self.initial_redshift)
            self.initial_time.units.registry = self.unit_registry
            self.final_time = self.cosmology.t_from_z(self.final_redshift)
            self.final_time.units.registry = self.unit_registry

        # If not a cosmology simulation, figure out the stopping criteria.
        else:
            if "TimeBegin" in self.parameters:
                self.initial_time = self.quan(self.parameters["TimeBegin"], "code_time")
            else:
                self.initial_time = self.quan(0., "code_time")

            if "TimeMax" in self.parameters:
                self.final_time = self.quan(self.parameters["TimeMax"], "code_time")
            else:
                self.final_time = None
            if not "TimeMax" in self.parameters:
                raise NoStoppingCondition(self.parameter_filename)

    def _find_outputs(self):
        """
        Search for directories matching the data dump keywords.
        If found, get dataset times py opening the ds.
        """

        potential_outputs = glob.glob(self._snapshot_format())
        self.all_outputs = self._check_for_outputs(potential_outputs)
        self.all_outputs.sort(key=lambda obj: obj["time"])
        only_on_root(mylog.info, "Located %d total outputs.", len(self.all_outputs))

        # manually set final time and redshift with last output
        if self.all_outputs:
            self.final_time = self.all_outputs[-1]["time"]
            if self.cosmological_simulation:
                self.final_redshift = self.all_outputs[-1]["redshift"]

    def _check_for_outputs(self, potential_outputs):
        r"""
        Check a list of files to see if they are valid datasets.
        """

        only_on_root(mylog.info, "Checking %d potential outputs.", 
                     len(potential_outputs))

        my_outputs = {}
        for my_storage, output in parallel_objects(potential_outputs, 
                                                   storage=my_outputs):
            if os.path.exists(output):
                try:
                    ds = load(output)
                    if ds is not None:
                        my_storage.result = {"filename": output,
                                             "time": ds.current_time.in_units("s")}
                        if ds.cosmological_simulation:
                            my_storage.result["redshift"] = ds.current_redshift
                except YTOutputNotIdentified:
                    mylog.error("Failed to load %s", output)
        my_outputs = [my_output for my_output in my_outputs.values() \
                      if my_output is not None]
        return my_outputs

    def _write_cosmology_outputs(self, filename, outputs, start_index,
                                 decimals=3):
        r"""
        Write cosmology output parameters for a cosmology splice.
        """

        mylog.info("Writing redshift output list to %s.", filename)
        f = open(filename, "w")
        for output in outputs:
            f.write("%f\n" % (1. / (1. + output["redshift"])))
        f.close()
class StarFormationRate(object):

    r"""Calculates the star formation rate for a given population of
    star particles.

    Parameters
    ----------
    ds : EnzoDataset object
    data_source : AMRRegion object, optional
        The region from which stars are extracted for analysis. If this
        is not supplied, the next three must be, otherwise the next
        three do not need to be specified.
    star_mass : Ordered array or list of floats
        The mass of the stars to be analyzed in units of Msun.
    star_creation_time : Ordered array or list of floats
        The creation time for the stars in code units.
    volume : Float
        The comoving volume of the region for the specified list of stars.
    bins : Integer
        The number of time bins used for binning the stars. Default = 300.
    star_filter : A user-defined filtering rule for stars.
        See: http://yt-project.org/docs/dev/analyzing/filtering.html
        Default: ct>0

    Examples
    --------

    >>> import yt
    >>> from yt.analysis_modules.star_analysis.api import StarFormationRate
    >>> ds = yt.load("Enzo_64/RD0006/RedshiftOutput0006")
    >>> sp = ds.sphere([0.5, 0.5, 0.5], 0.1)
    >>> sfr = StarFormationRate(ds, sp)
    """

    def __init__(self, ds, data_source=None, star_mass=None,
                 star_creation_time=None, bins=300, volume=None,
                 star_filter=None):
        self._ds = ds
        self._data_source = data_source
        self._filter = star_filter
        self.ds_provided = self._data_source is not None
        self.filter_provided = self._filter is not None
        self.bin_count = bins

        # Set up for time conversion.
        self.cosm = Cosmology(
            hubble_constant=self._ds.hubble_constant,
            omega_matter=self._ds.omega_matter,
            omega_lambda=self._ds.omega_lambda)
        # Find the time right now.
        self.time_now = self._ds.current_time

        if not self.ds_provided:
            # Check to make sure we have the right set of informations.
            if star_mass is None or star_creation_time is None \
                    or volume is None:
                mylog.error("""
                    If data_source is not provided, all of these parameters
                    need to be set:
                        star_mass (array, Msun),
                        star_creation_time (array, code units),
                        volume (float, cMpc**3).""")
                return None

            if isinstance(star_mass, YTArray):
                assert star_mass.units.same_dimensions_as(g.units)
            elif star_mass is not None:
                star_mass = YTArray(star_mass, 'Msun')
            self.star_mass = star_mass

            if isinstance(star_creation_time, YTArray):
                assert star_creation_time.units.same_dimensions_as(s.units)
            elif star_creation_time is not None:
                star_creation_time = self._ds.arr(star_creation_time,
                                                  'code_time')
            self.star_creation_time = star_creation_time

            if isinstance(volume, YTQuantity):
                assert volume.units.same_dimensions_as(
                    self._ds.quan(1.0, 'Mpccm**3').units
                )
            elif volume is not None:
                volume = self._ds.quan(volume, 'Mpccm**3')
            self.volume = volume

        # Build the distribution.
        self.build_dist()
        # Attach some convenience arrays.
        self.attach_arrays()

    def build_dist(self):
        """
        Build the data for plotting.
        """
        # Pick out the stars.
        if self.filter_provided:
            ct = self._filter['creation_time']
            mass_stars = self._data_source[self._filter, "particle_mass"]
        else:
            if self.ds_provided:
                ct = self._data_source['creation_time']
                if ct is None:
                    errmsg = 'data source must have particle_age!'
                    mylog.error(errmsg)
                    raise RuntimeError(errmsg)
                mask = ct > 0
                if not any(mask):
                    errmsg = 'all particles have age < 0'
                    mylog.error(errmsg)
                    raise RuntimeError(errmsg)
                # type = self._data_source['particle_type']
                ct_stars = ct[mask]
                mass_stars = self._data_source[
                    'particle_mass'][mask].in_units('Msun')
                del mask
            else:
                ct_stars = self.star_creation_time
                mass_stars = self.star_mass
        # Find the oldest stars in units of code time.
        tmin = ct_stars.min().in_units("s")
        # Multiply the end to prevent numerical issues.
        self.time_bins = np.linspace(
            tmin * 1.01, self._ds.current_time.in_units("s"),
            num=self.bin_count + 1)
        # Figure out which bins the stars go into.
        inds = np.digitize(ct_stars.in_units("s"), self.time_bins) - 1
        # Sum up the stars created in each time bin.
        self.mass_bins = YTArray(
            np.zeros(self.bin_count + 1, dtype='float64'), "Msun"
        )
        for index in np.unique(inds):
            self.mass_bins[index] += (mass_stars[inds == index]).sum()
        # We will want the time taken between bins.
        self.time_bins_dt = self.time_bins[1:] - self.time_bins[:-1]

    def attach_arrays(self):
        """
        Attach convenience arrays to the class for easy access.
        """
        if self.ds_provided:
            try:
                vol = self._data_source[
                    'cell_volume'].in_units('Mpccm ** 3').sum()
            except AttributeError:
                # If we're here, this is probably a HOPHalo object, and we
                # can get the volume this way.
                ds = self._data_source.get_sphere()
                vol = ds['cell_volume'].in_units('Mpccm ** 3').sum()
        else:
            vol = self.volume.in_units('Mpccm ** 3')

        # Use the center of the time_bin, not the left edge.
        self.time = 0.5 * \
            (self.time_bins[1:] + self.time_bins[:-1]).in_units('yr')
        self.lookback_time = self.time_now - self.time  # now in code_time...
        self.redshift = self.cosm.z_from_t(self.time)

        self.Msol_yr = (
            self.mass_bins[:-1] / self.time_bins_dt[:]).in_units('Msun/yr')
        # changed vol from mpc to mpccm used in literature
        self.Msol_yr_vol = self.Msol_yr / vol

        self.Msol = self.mass_bins[:-1].in_units("Msun")
        self.Msol_cumulative = self.Msol.cumsum()

    def write_out(self, name="StarFormationRate.out"):
        r"""Write out the star analysis to a text file *name*. The columns are in
        order.

        The columns in the output file are:
           1. Time (yrs)
           2. Look-back time (yrs)
           3. Redshift
           4. Star formation rate in this bin per year (Msol/yr)
           5. Star formation rate in this bin per year
              per Mpc**3 (Msol/yr/Mpc**3)
           6. Stars formed in this time bin (Msol)
           7. Cumulative stars formed up to this time bin (Msol)

        Parameters
        ----------
        name : String
            The name of the file to write to. Default = StarFormationRate.out.

        Examples
        --------
        >>> import yt
        >>> from yt.analysis_modules.star_analysis.api import StarFormationRate
        >>> ds = yt.load("Enzo_64/RD0006/RedshiftOutput0006")
        >>> sp = ds.sphere([0.5, 0.5, 0.5], 0.1)
        >>> sfr = StarFormationRate(ds, sp)
        >>> sfr.write_out("stars-SFR.out")
        """
        fp = open(name, "w")
        fp.write(
            "#time\tlookback\tredshift\tMsol/yr\tMsol/yr/Mpc3\tMsol\tcumMsol\t\n")
        for i, time in enumerate(self.time):
            line = "%1.5e %1.5e %1.5e %1.5e %1.5e %1.5e %1.5e\n" % \
                (time.in_units("yr"),  # Time
                 self.lookback_time[i].in_units('yr'),  # Lookback time
                 self.redshift[i],  # Redshift
                 self.Msol_yr[i].in_units("Msun/yr"),
                 self.Msol_yr_vol[i],
                 self.Msol[i].in_units("Msun"),  # Msol in bin
                 self.Msol_cumulative[i].in_units("Msun"))  # cumulative
            fp.write(line)
        fp.close()
예제 #47
0
    def from_data_source(cls, data_source, redshift, area,
                         exp_time, source_model, point_sources=False,
                         parameters=None, center=None, dist=None, 
                         cosmology=None, velocity_fields=None):
        r"""
        Initialize a :class:`~pyxsim.photon_list.PhotonList` from a yt data
        source. The redshift, collecting area, exposure time, and cosmology
        are stored in the *parameters* dictionary which is passed to the
        *source_model* function.

        Parameters
        ----------
        data_source : :class:`~yt.data_objects.data_containers.YTSelectionContainer`
            The data source from which the photons will be generated.
        redshift : float
            The cosmological redshift for the photons.
        area : float, (value, unit) tuple, :class:`~yt.units.yt_array.YTQuantity`, or :class:`~astropy.units.Quantity`
            The collecting area to determine the number of photons. If units are
            not specified, it is assumed to be in cm^2.
        exp_time : float, (value, unit) tuple, :class:`~yt.units.yt_array.YTQuantity`, or :class:`~astropy.units.Quantity`
            The exposure time to determine the number of photons. If units are
            not specified, it is assumed to be in seconds.
        source_model : :class:`~pyxsim.source_models.SourceModel`
            A source model used to generate the photons.
        point_sources : boolean, optional
            If True, the photons will be assumed to be generated from the exact
            positions of the cells or particles and not smeared around within
            a volume. Default: False
        parameters : dict, optional
            A dictionary of parameters to be passed for the source model to use,
            if necessary.
        center : string or array_like, optional
            The origin of the photon spatial coordinates. Accepts "c", "max", or
            a coordinate. If not specified, pyxsim attempts to use the "center"
            field parameter of the data_source.
        dist : float, (value, unit) tuple, :class:`~yt.units.yt_array.YTQuantity`, or :class:`~astropy.units.Quantity`
            The angular diameter distance, used for nearby sources. This may be
            optionally supplied instead of it being determined from the
            *redshift* and given *cosmology*. If units are not specified, it is
            assumed to be in kpc. To use this, the redshift must be set to zero.
        cosmology : :class:`~yt.utilities.cosmology.Cosmology`, optional
            Cosmological information. If not supplied, we try to get the
            cosmology from the dataset. Otherwise, LCDM with the default yt 
            parameters is assumed.
        velocity_fields : list of fields
            The yt fields to use for the velocity. If not specified, the 
            following will be assumed:
            ['velocity_x', 'velocity_y', 'velocity_z'] for grid datasets
            ['particle_velocity_x', 'particle_velocity_y', 'particle_velocity_z'] for particle datasets

        Examples
        --------
        >>> thermal_model = ThermalSourceModel(apec_model, Zmet=0.3)
        >>> redshift = 0.05
        >>> area = 6000.0 # assumed here in cm**2
        >>> time = 2.0e5 # assumed here in seconds
        >>> sp = ds.sphere("c", (500., "kpc"))
        >>> my_photons = PhotonList.from_data_source(sp, redshift, area,
        ...                                          time, thermal_model)
        """
        ds = data_source.ds

        if parameters is None:
            parameters = {}
        if cosmology is None:
            if hasattr(ds, 'cosmology'):
                cosmo = ds.cosmology
            else:
                cosmo = Cosmology()
        else:
            cosmo = cosmology
        if dist is None:
            if redshift <= 0.0:
                msg = "If redshift <= 0.0, you must specify a distance to the " \
                      "source using the 'dist' argument!"
                mylog.error(msg)
                raise ValueError(msg)
            D_A = cosmo.angular_diameter_distance(0.0, redshift).in_units("Mpc")
        else:
            D_A = parse_value(dist, "kpc")
            if redshift > 0.0:
                mylog.warning("Redshift must be zero for nearby sources. "
                              "Resetting redshift to 0.0.")
                redshift = 0.0

        if isinstance(center, string_types):
            if center == "center" or center == "c":
                parameters["center"] = ds.domain_center
            elif center == "max" or center == "m":
                parameters["center"] = ds.find_max("density")[-1]
        elif iterable(center):
            if isinstance(center, YTArray):
                parameters["center"] = center.in_units("code_length")
            elif isinstance(center, tuple):
                if center[0] == "min":
                    parameters["center"] = ds.find_min(center[1])[-1]
                elif center[0] == "max":
                    parameters["center"] = ds.find_max(center[1])[-1]
                else:
                    raise RuntimeError
            else:
                parameters["center"] = ds.arr(center, "code_length")
        elif center is None:
            if hasattr(data_source, "left_edge"):
                parameters["center"] = 0.5*(data_source.left_edge+data_source.right_edge)
            else:
                parameters["center"] = data_source.get_field_parameter("center")

        parameters["fid_exp_time"] = parse_value(exp_time, "s")
        parameters["fid_area"] = parse_value(area, "cm**2")
        parameters["fid_redshift"] = redshift
        parameters["fid_d_a"] = D_A
        parameters["hubble"] = cosmo.hubble_constant
        parameters["omega_matter"] = cosmo.omega_matter
        parameters["omega_lambda"] = cosmo.omega_lambda

        if redshift > 0.0:
            mylog.info("Cosmology: h = %g, omega_matter = %g, omega_lambda = %g" %
                       (cosmo.hubble_constant, cosmo.omega_matter, cosmo.omega_lambda))
        else:
            mylog.info("Observing local source at distance %s." % D_A)

        D_A = parameters["fid_d_a"].in_cgs()
        dist_fac = 1.0/(4.*np.pi*D_A.value*D_A.value*(1.+redshift)**2)
        spectral_norm = parameters["fid_area"].v*parameters["fid_exp_time"].v*dist_fac

        source_model.setup_model(data_source, redshift, spectral_norm)

        p_fields, v_fields, w_field = determine_fields(ds,
                                                       source_model.source_type,
                                                       point_sources)

        if velocity_fields is not None:
            v_fields = velocity_fields

        if p_fields[0] == ("index", "x"):
            parameters["data_type"] = "cells"
        else:
            parameters["data_type"] = "particles"

        citer = data_source.chunks([], "io")

        photons = defaultdict(list)

        for chunk in parallel_objects(citer):

            chunk_data = source_model(chunk)

            if chunk_data is not None:
                ncells, number_of_photons, idxs, energies = chunk_data
                photons["num_photons"].append(number_of_photons)
                photons["energy"].append(energies)
                photons["pos"].append(np.array([chunk[p_fields[0]].d[idxs],
                                                chunk[p_fields[1]].d[idxs],
                                                chunk[p_fields[2]].d[idxs]]))
                photons["vel"].append(np.array([chunk[v_fields[0]].d[idxs],
                                                chunk[v_fields[1]].d[idxs],
                                                chunk[v_fields[2]].d[idxs]]))
                if w_field is None:
                    photons["dx"].append(np.zeros(ncells))
                else:
                    photons["dx"].append(chunk[w_field].d[idxs])

        source_model.cleanup_model()

        photon_units = {"pos": ds.field_info[p_fields[0]].units,
                        "vel": ds.field_info[v_fields[0]].units,
                        "energy": "keV"}
        if w_field is None:
            photon_units["dx"] = "kpc"
        else:
            photon_units["dx"] = ds.field_info[w_field].units

        concatenate_photons(ds, photons, photon_units)

        c = parameters["center"].to("kpc")

        if sum(ds.periodicity) > 0:
            # Fix photon coordinates for regions crossing a periodic boundary
            dw = ds.domain_width.to("kpc")
            le, re = find_object_bounds(data_source)
            for i in range(3):
                if ds.periodicity[i] and photons["pos"].shape[0] > 0:
                    tfl = photons["pos"][:,i] < le[i]
                    tfr = photons["pos"][:,i] > re[i]
                    photons["pos"][tfl,i] += dw[i]
                    photons["pos"][tfr,i] -= dw[i]

        # Re-center all coordinates
        if photons["pos"].shape[0] > 0:
            photons["pos"] -= c

        mylog.info("Finished generating photons.")
        mylog.info("Number of photons generated: %d" % int(np.sum(photons["num_photons"])))
        mylog.info("Number of cells with photons: %d" % photons["dx"].size)

        return cls(photons, parameters, cosmo)