def _read_particle_header(self):
        if not self.ds.parameters["particles.write_in_plotfile"]:
            self.pgrid_info = np.zeros((self.num_grids, 3), dtype='int64')
            return
        for fn in ['particle_position_%s' % ax for ax in 'xyz'] + \
                  ['particle_mass'] +  \
                  ['particle_velocity_%s' % ax for ax in 'xyz']:
            self.field_list.append(("io", fn))
        header = open(os.path.join(self.ds.output_dir, "DM", "Header"))
        version = header.readline()
        ndim = header.readline()
        nfields = header.readline()
        ntotalpart = int(header.readline())
        dummy = header.readline() # nextid
        maxlevel = int(header.readline()) # max level

        # Skip over how many grids on each level; this is degenerate
        for i in range(maxlevel + 1): dummy = header.readline()

        grid_info = np.fromiter((int(i) for line in header.readlines()
                                 for i in line.split()),
                                dtype='int64',
                                count=3*self.num_grids).reshape((self.num_grids, 3))
        # we need grid_info in `populate_grid_objects`, so save it to self

        for g, pg in izip(self.grids, grid_info):
            g.particle_filename = os.path.join(self.ds.output_dir, "DM",
                                               "Level_%s" % (g.Level),
                                               "DATA_%04i" % pg[0])
            g.NumberOfParticles = pg[1]
            g._particle_offset = pg[2]

        self.grid_particle_count[:, 0] = grid_info[:, 1]
Exemple #2
0
    def _read_particle_header(self):
        if not self.ds.parameters["particles"]:
            self.pgrid_info = np.zeros((self.num_grids, 3), dtype='int64')
            return
        for fn in ['particle_position_%s' % ax for ax in 'xyz'] + \
                  ['particle_mass'] +  \
                  ['particle_velocity_%s' % ax for ax in 'xyz']:
            self.field_list.append(("io", fn))
        header = open(os.path.join(self.ds.output_dir, "DM", "Header"))
        version = header.readline()  # NOQA
        ndim = header.readline()  # NOQA
        nfields = header.readline()  # NOQA
        ntotalpart = int(header.readline())  # NOQA
        nextid = header.readline()  # NOQA
        maxlevel = int(header.readline())  # NOQA

        # Skip over how many grids on each level; this is degenerate
        for i in range(maxlevel + 1):
            header.readline()

        grid_info = np.fromiter(
            (int(i) for line in header.readlines() for i in line.split()),
            dtype='int64',
            count=3 * self.num_grids).reshape((self.num_grids, 3))
        # we need grid_info in `populate_grid_objects`, so save it to self

        for g, pg in izip(self.grids, grid_info):
            g.particle_filename = os.path.join(self.ds.output_dir, "DM",
                                               "Level_%s" % (g.Level),
                                               "DATA_%04i" % pg[0])
            g.NumberOfParticles = pg[1]
            g._particle_offset = pg[2]

        self.grid_particle_count[:, 0] = grid_info[:, 1]
Exemple #3
0
 def _populate_grid_objects(self):
     for g,f in izip(self.grids, self.filenames):
         g._prepare_grid()
         g._setup_dx()
         g.set_filename(f[0])
     del self.filenames # No longer needed.
     self.max_level = self.grid_levels.max()
Exemple #4
0
 def _populate_grid_objects(self):
     reconstruct = ytcfg.getboolean("yt", "reconstruct_index")
     for g, f in izip(self.grids, self.filenames):
         g._prepare_grid()
         g._setup_dx()
         g.set_filename(f[0])
         if reconstruct:
             if g.Parent is not None: g._guess_properties_from_parent()
     del self.filenames  # No longer needed.
     self.max_level = self.grid_levels.max()
 def _populate_grid_objects(self):
     reconstruct = ytcfg.getboolean("yt","reconstruct_index")
     for g,f in izip(self.grids, self.filenames):
         g._prepare_grid()
         g._setup_dx()
         g.set_filename(f[0])
         if reconstruct:
             if g.Parent is not None: g._guess_properties_from_parent()
     del self.filenames # No longer needed.
     self.max_level = self.grid_levels.max()
Exemple #6
0
    def save(self, name=None, suffix=None, mpl_kwargs=None):
        r"""
        Saves a 1d profile plot.

        Parameters
        ----------
        name : str
            The output file keyword.
        suffix : string
            Specify the image type by its suffix. If not specified, the output
            type will be inferred from the filename. Defaults to PNG.
        mpl_kwargs : dict
            A dict of keyword arguments to be passed to matplotlib.
        """
        if not self._plot_valid:
            self._setup_plots()
        unique = set(self.plots.values())
        if len(unique) < len(self.plots):
            iters = izip(range(len(unique)), sorted(unique))
        else:
            iters = iteritems(self.plots)
        if not suffix:
            suffix = "png"
        suffix = ".%s" % suffix
        fullname = False
        if name is None:
            if len(self.profiles) == 1:
                prefix = self.profiles[0].ds
            else:
                prefix = "Multi-data"
            name = "%s%s" % (prefix, suffix)
        else:
            sfx = get_image_suffix(name)
            if sfx != '':
                suffix = sfx
                prefix = name[:name.rfind(suffix)]
                fullname = True
            else:
                prefix = name
        xfn = self.profiles[0].x_field
        if isinstance(xfn, tuple):
            xfn = xfn[1]
        fns = []
        for uid, plot in iters:
            if isinstance(uid, tuple):
                uid = uid[1]
            if fullname:
                fns.append("%s%s" % (prefix, suffix))
            else:
                fns.append("%s_1d-Profile_%s_%s%s" %
                           (prefix, xfn, uid, suffix))
            mylog.info("Saving %s", fns[-1])
            with matplotlib_style_context():
                plot.save(fns[-1], mpl_kwargs=mpl_kwargs)
        return fns
    def save(self, name=None, suffix=None):
        r"""
        Saves a 1d profile plot.

        Parameters
        ----------
        name : str
            The output file keyword.
        suffix : string
            Specify the image type by its suffix. If not specified, the output
            type will be inferred from the filename. Defaults to PNG.
        """
        if not self._plot_valid:
            self._setup_plots()
        unique = set(self.figures.values())
        if len(unique) < len(self.figures):
            iters = izip(range(len(unique)), sorted(unique))
        else:
            iters = iteritems(self.figures)
        if not suffix:
            suffix = "png"
        suffix = ".%s" % suffix
        if name is None:
            if len(self.profiles) == 1:
                prefix = self.profiles[0].ds
            else:
                prefix = "Multi-data"
            name = "%s%s" % (prefix, suffix)
        else:
            sfx = get_image_suffix(name)
            if sfx != '':
                suffix = sfx
                prefix = name[:name.rfind(suffix)]
            else:
                prefix = name
        xfn = self.profiles[0].x_field
        if isinstance(xfn, tuple):
            xfn = xfn[1]
        canvas_cls = get_canvas(name)
        fns = []
        for uid, fig in iters:
            if isinstance(uid, tuple):
                uid = uid[1]
            canvas = canvas_cls(fig)
            fns.append("%s_1d-Profile_%s_%s%s" % (prefix, xfn, uid, suffix))
            mylog.info("Saving %s", fns[-1])
            canvas.print_figure(fns[-1])
        return fns
Exemple #8
0
    def save(self, name=None, suffix=None):
        r"""
        Saves a 1d profile plot.

        Parameters
        ----------
        name : str
            The output file keyword.
        suffix : string
            Specify the image type by its suffix. If not specified, the output
            type will be inferred from the filename. Defaults to PNG.
        """
        if not self._plot_valid:
            self._setup_plots()
        unique = set(self.figures.values())
        if len(unique) < len(self.figures):
            iters = izip(range(len(unique)), sorted(unique))
        else:
            iters = iteritems(self.figures)
        if not suffix:
            suffix = "png"
        suffix = ".%s" % suffix
        if name is None:
            if len(self.profiles) == 1:
                prefix = self.profiles[0].ds
            else:
                prefix = "Multi-data"
            name = "%s%s" % (prefix, suffix)
        else:
            sfx = get_image_suffix(name)
            if sfx != '':
                suffix = sfx
                prefix = name[:name.rfind(suffix)]
            else:
                prefix = name
        xfn = self.profiles[0].x_field
        if isinstance(xfn, tuple):
            xfn = xfn[1]
        canvas_cls = get_canvas(name)
        fns = []
        for uid, fig in iters:
            if isinstance(uid, tuple):
                uid = uid[1]
            canvas = canvas_cls(fig)
            fns.append("%s_1d-Profile_%s_%s%s" % (prefix, xfn, uid, suffix))
            mylog.info("Saving %s", fns[-1])
            canvas.print_figure(fns[-1])
        return fns
Exemple #9
0
 def _repr_html_(self):
     """Return an html representation of the plot object. Will display as a
     png for each WindowPlotMPL instance in self.plots"""
     ret = ''
     unique = set(self.plots.values())
     if len(unique) < len(self.plots):
         iters = izip(range(len(unique)), sorted(unique))
     else:
         iters = iteritems(self.plots)
     for uid, plot in iters:
         with matplotlib_style_context():
             img = plot._repr_png_()
         img = base64.b64encode(img).decode()
         ret += r'<img style="max-width:100%%;max-height:100%%;" ' \
                r'src="data:image/png;base64,{0}"><br>'.format(img)
     return ret
 def _repr_html_(self):
     """Return an html representation of the plot object. Will display as a
     png for each WindowPlotMPL instance in self.plots"""
     ret = ''
     unique = set(self.figures.values())
     if len(unique) < len(self.figures):
         iters = izip(range(len(unique)), sorted(unique))
     else:
         iters = iteritems(self.figures)
     for uid, fig in iters:
         canvas = mpl.FigureCanvasAgg(fig)
         f = BytesIO()
         canvas.print_figure(f)
         f.seek(0)
         img = base64.b64encode(f.read()).decode()
         ret += r'<img style="max-width:100%%;max-height:100%%;" ' \
                r'src="data:image/png;base64,{0}"><br>'.format(img)
     return ret
Exemple #11
0
 def _repr_html_(self):
     """Return an html representation of the plot object. Will display as a
     png for each WindowPlotMPL instance in self.plots"""
     ret = ''
     unique = set(self.figures.values())
     if len(unique) < len(self.figures):
         iters = izip(range(len(unique)), sorted(unique))
     else:
         iters = iteritems(self.figures)
     for uid, fig in iters:
         canvas = mpl.FigureCanvasAgg(fig)
         f = BytesIO()
         canvas.print_figure(f)
         f.seek(0)
         img = base64.b64encode(f.read()).decode()
         ret += r'<img style="max-width:100%%;max-height:100%%;" ' \
                r'src="data:image/png;base64,{0}"><br>'.format(img)
     return ret
Exemple #12
0
def compare_arbors(a1, a2, groups=None, fields=None):
    """
    Compare all fields for all trees in two arbors.
    """

    if groups is None:
        groups = ["tree", "prog"]

    if fields is None:
        fields = a1.field_list

    for field in fields:
        assert (a1[field] == a2[field]).all()

    i = 0
    ntot = len(a1)
    pbar = get_pbar("Comparing trees", ntot)
    for t1, t2 in izip(a1, a2):
        compare_trees(t1, t2, groups=groups, fields=fields)
        i += 1
        pbar.update(i)
    pbar.finish()
    def calculate_spectrum(self, data_source=None, star_mass=None,
                           star_creation_time=None,
                           star_metallicity_fraction=None,
                           star_metallicity_constant=None,
                           min_age=YTQuantity(0.0, 'yr')):

        r"""For the set of stars, calculate the collective spectrum.
        Attached to the output are several useful objects:

        Attributes
        ----------
        final_spec: array
            The collective spectrum in units of flux binned in wavelength.
        wavelength: array
            The wavelength for the spectrum bins, in Angstroms.
        total_mass: float
            Total mass of all the stars.
        avg_mass: float
            Average mass of all the stars.
        avg_metal: float
            Average metallicity of all the stars.

        Parameters
        ----------
        data_source : AMRRegion object, optional
            The region from which stars are extracted for analysis. If this is
            not specified, the next three parameters must be supplied.
        star_mass : Array or list of floats
            An array of star masses in Msun units.
        star_creation_time : Array or list of floats
            An array of star creation times in code units.
        star_metallicity_fraction : Array or list of floats
            An array of star metallicity fractions, in code
            units (which is not Z/Zsun, rather just Z).
        star_metallicity_constant : Float
            If desired, override the star
            metallicity fraction of all the stars to the given value.
        min_age : Float
            Removes young stars younger than this number (in years)
            from the spectrum. Default: 0 (all stars).

        Examples
        --------
        >>> import yt
        >>> from yt.analysis_modules.star_analysis.api import SpectrumBuilder
        >>> ds = yt.load("Enzo_64/RD0006/RedshiftOutput0006")
        >>> spec = SpectrumBuilder(ds, "bc", model="salpeter")
        >>> sp = ds.sphere([0.5, 0.5, 0.5], 0.1)
        >>> spec.calculate_spectrum(data_source=sp, min_age=1.e6)
        """

        # Initialize values
        self.final_spec = np.zeros(self.wavelength.size, dtype='float64')
        self._data_source = data_source

        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(star_metallicity_fraction, YTArray):
            assert \
                star_metallicity_fraction.units.same_dimensions_as(Zsun.units)
        elif star_metallicity_fraction is not None:
            star_metallicity_fraction = self._ds.arr(
                star_metallicity_fraction, 'code_metallicity'
            )
        self.star_metallicity_fraction = star_metallicity_fraction

        if isinstance(min_age, YTQuantity):
            assert min_age.units.same_dimensions_as(s.units)
        elif min_age is not None:
            min_age = YTQuantity(min_age, 'yr')
        self.min_age = min_age

        # Check to make sure we have the right set of data.
        if data_source is None:
            if self.star_mass is None or self.star_creation_time is None or \
                    (star_metallicity_fraction is None and
                     star_metallicity_constant is None):
                mylog.error(
                    """
                If data_source is not provided, all of these paramters
                need to be set:
                   star_mass (array, Msun),
                   star_creation_time (array, code units),
                And one of:
                   star_metallicity_fraction (array, code units).
                --OR--
                   star_metallicity_constant (float, code units).
                """)
                return None

            if star_metallicity_fraction is not None:
                self.star_metal = star_metallicity_fraction
            else:
                self.star_metal = \
                    self._ds.arr(np.ones_like(self.star_mass) *
                                 star_metallicity_constant, 'Zsun')
        else:
            # Get the data we need.
            if self.filter_provided:
                ct = self._filter['creation_time']
                # mass_stars = self._data_source[self._filter, "particle_mass"]
                if star_metallicity_constant is None:
                    self.star_metal = self._data_source[
                        self._filter, "metallicity_fraction"].in_units('Zsun')
                else:
                    self.star_metal = \
                        self._ds.arr(np.ones_like(
                            self._data_source[self._filter,
                                              "metallicity_fraction"]) *
                        star_metallicity_constant, "Zsun")
            else:
                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']
                self.star_creation_time = ct[mask]
                self.star_mass = self._data_source[
                    'particle_mass'][mask].in_units('Msun')
                if star_metallicity_constant is not None:
                    self.star_metal = self._ds.arr(
                        np.ones_like(self.star_mass) *
                        star_metallicity_constant, 'Zsun')
                else:
                    self.star_metal = self._data_source[
                        "metallicity_fraction"][mask].in_units('Zsun')
        # Age of star in years.
        dt = (self.time_now - self.star_creation_time).in_units('yr')
        dt[dt < 0.0] = 0.0
        # Remove young stars
        sub = dt >= self.min_age
        if len(sub) == 0:
            return
        self.star_metal = self.star_metal[sub]
        dt = dt[sub]
        self.star_creation_time = self.star_creation_time[sub]
        # Figure out which METALS bin the star goes into.
        Mindex = np.digitize(self.star_metal.in_units('Zsun'), METALS)
        # Replace the indices with strings.
        Mname = MtoD[Mindex]
        # Figure out which age bin this star goes into.
        Aindex = np.digitize(dt, self.age)
        # Ratios used for the interpolation.
        ratio1 = (dt - self.age[Aindex - 1]) / \
            (self.age[Aindex] - self.age[Aindex - 1])
        ratio2 = (self.age[Aindex] - dt) / \
            (self.age[Aindex] - self.age[Aindex - 1])
        # Sort the stars by metallicity and then by age, which should reduce
        # memory access time by a little bit in the loop.
        indexes = np.arange(self.star_metal.size)
        sort = np.asarray([indexes[i]
                           for i in np.lexsort([indexes, Aindex, Mname])])
        Mname = Mname[sort]
        Aindex = Aindex[sort]
        ratio1 = ratio1[sort]
        ratio2 = ratio2[sort]
        self.star_mass = self.star_mass[sort]
        self.star_creation_time = self.star_creation_time[sort]
        self.star_metal = self.star_metal[sort]

        # Interpolate the flux for each star, adding to the total by weight.
        pbar = get_pbar("Calculating fluxes", len(self.star_mass))
        for i, star in enumerate(izip(Mname, Aindex, ratio1, ratio2,
                                      self.star_mass)):
            # Pick the right age bin for the right flux array.
            flux = self.flux[star[0]][star[1], :]
            # Get the one just before the one above.
            flux_1 = self.flux[star[0]][star[1] - 1, :]
            # interpolate in log(flux), linear in time.
            int_flux = star[3] * np.log10(flux_1) + star[2] * np.log10(flux)
            # Add this flux to the total, weighted by mass.
            self.final_spec += np.power(10., int_flux) * star[4]
            pbar.update(i)
        pbar.finish()

        # Normalize.
        self.total_mass = self.star_mass.sum()
        self.avg_mass = self.star_mass.mean()
        tot_metal = (self.star_metal * self.star_mass).sum()
        if tot_metal > 0:
            self.avg_metal = math.log10(
                (tot_metal / self.total_mass).in_units('Zsun'))
        else:
            self.avg_metal = -99
Exemple #14
0
    def calculate_spectrum(self,
                           data_source=None,
                           star_mass=None,
                           star_creation_time=None,
                           star_metallicity_fraction=None,
                           star_metallicity_constant=None,
                           min_age=YTQuantity(0.0, 'yr')):
        r"""For the set of stars, calculate the collective spectrum.
        Attached to the output are several useful objects:

        Attributes
        ----------
        final_spec: array
            The collective spectrum in units of flux binned in wavelength.
        wavelength: array
            The wavelength for the spectrum bins, in Angstroms.
        total_mass: float
            Total mass of all the stars.
        avg_mass: float
            Average mass of all the stars.
        avg_metal: float
            Average metallicity of all the stars.

        Parameters
        ----------
        data_source : AMRRegion object, optional
            The region from which stars are extracted for analysis. If this is
            not specified, the next three parameters must be supplied.
        star_mass : Array or list of floats
            An array of star masses in Msun units.
        star_creation_time : Array or list of floats
            An array of star creation times in code units.
        star_metallicity_fraction : Array or list of floats
            An array of star metallicity fractions, in code
            units (which is not Z/Zsun, rather just Z).
        star_metallicity_constant : Float
            If desired, override the star
            metallicity fraction of all the stars to the given value.
        min_age : Float
            Removes young stars younger than this number (in years)
            from the spectrum. Default: 0 (all stars).

        Examples
        --------
        >>> import yt
        >>> from yt.analysis_modules.star_analysis.api import SpectrumBuilder
        >>> ds = yt.load("Enzo_64/RD0006/RedshiftOutput0006")
        >>> spec = SpectrumBuilder(ds, "bc", model="salpeter")
        >>> sp = ds.sphere([0.5, 0.5, 0.5], 0.1)
        >>> spec.calculate_spectrum(data_source=sp, min_age=1.e6)
        """

        # Initialize values
        self.final_spec = np.zeros(self.wavelength.size, dtype='float64')
        self._data_source = data_source

        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(star_metallicity_fraction, YTArray):
            assert \
                star_metallicity_fraction.units.same_dimensions_as(Zsun.units)
        elif star_metallicity_fraction is not None:
            star_metallicity_fraction = self._ds.arr(star_metallicity_fraction,
                                                     'code_metallicity')
        self.star_metallicity_fraction = star_metallicity_fraction

        if isinstance(min_age, YTQuantity):
            assert min_age.units.same_dimensions_as(s.units)
        elif min_age is not None:
            min_age = YTQuantity(min_age, 'yr')
        self.min_age = min_age

        # Check to make sure we have the right set of data.
        if data_source is None:
            if self.star_mass is None or self.star_creation_time is None or \
                    (star_metallicity_fraction is None and
                     star_metallicity_constant is None):
                mylog.error("""
                If data_source is not provided, all of these paramters
                need to be set:
                   star_mass (array, Msun),
                   star_creation_time (array, code units),
                And one of:
                   star_metallicity_fraction (array, code units).
                --OR--
                   star_metallicity_constant (float, code units).
                """)
                return None

            if star_metallicity_fraction is not None:
                self.star_metal = star_metallicity_fraction
            else:
                self.star_metal = \
                    self._ds.arr(np.ones_like(self.star_mass) *
                                 star_metallicity_constant, 'Zsun')
        else:
            # Get the data we need.
            if self.filter_provided:
                ct = self._filter['creation_time']
                # mass_stars = self._data_source[self._filter, "particle_mass"]
                if star_metallicity_constant is None:
                    self.star_metal = self._data_source[
                        self._filter, "metallicity_fraction"].in_units('Zsun')
                else:
                    self.star_metal = \
                        self._ds.arr(np.ones_like(
                            self._data_source[self._filter,
                                              "metallicity_fraction"]) *
                        star_metallicity_constant, "Zsun")
            else:
                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']
                self.star_creation_time = ct[mask]
                self.star_mass = self._data_source['particle_mass'][
                    mask].in_units('Msun')
                if star_metallicity_constant is not None:
                    self.star_metal = self._ds.arr(
                        np.ones_like(self.star_mass) *
                        star_metallicity_constant, 'Zsun')
                else:
                    self.star_metal = self._data_source[
                        "metallicity_fraction"][mask].in_units('Zsun')
        # Age of star in years.
        dt = (self.time_now - self.star_creation_time).in_units('yr')
        dt[dt < 0.0] = 0.0
        # Remove young stars
        sub = dt >= self.min_age
        if len(sub) == 0:
            return
        self.star_metal = self.star_metal[sub]
        dt = dt[sub]
        self.star_creation_time = self.star_creation_time[sub]
        # Figure out which METALS bin the star goes into.
        Mindex = np.digitize(self.star_metal.in_units('Zsun'), METALS)
        # Replace the indices with strings.
        Mname = MtoD[Mindex]
        # Figure out which age bin this star goes into.
        Aindex = np.digitize(dt, self.age)
        # Ratios used for the interpolation.
        ratio1 = (dt - self.age[Aindex - 1]) / \
            (self.age[Aindex] - self.age[Aindex - 1])
        ratio2 = (self.age[Aindex] - dt) / \
            (self.age[Aindex] - self.age[Aindex - 1])
        # Sort the stars by metallicity and then by age, which should reduce
        # memory access time by a little bit in the loop.
        indexes = np.arange(self.star_metal.size)
        sort = np.asarray(
            [indexes[i] for i in np.lexsort([indexes, Aindex, Mname])])
        Mname = Mname[sort]
        Aindex = Aindex[sort]
        ratio1 = ratio1[sort]
        ratio2 = ratio2[sort]
        self.star_mass = self.star_mass[sort]
        self.star_creation_time = self.star_creation_time[sort]
        self.star_metal = self.star_metal[sort]

        # Interpolate the flux for each star, adding to the total by weight.
        pbar = get_pbar("Calculating fluxes", len(self.star_mass))
        for i, star in enumerate(
                izip(Mname, Aindex, ratio1, ratio2, self.star_mass)):
            # Pick the right age bin for the right flux array.
            flux = self.flux[star[0]][star[1], :]
            # Get the one just before the one above.
            flux_1 = self.flux[star[0]][star[1] - 1, :]
            # interpolate in log(flux), linear in time.
            int_flux = star[3] * np.log10(flux_1) + star[2] * np.log10(flux)
            # Add this flux to the total, weighted by mass.
            self.final_spec += np.power(10., int_flux) * star[4]
            pbar.update(i)
        pbar.finish()

        # Normalize.
        self.total_mass = self.star_mass.sum()
        self.avg_mass = self.star_mass.mean()
        tot_metal = (self.star_metal * self.star_mass).sum()
        if tot_metal > 0:
            self.avg_metal = math.log10(
                (tot_metal / self.total_mass).in_units('Zsun'))
        else:
            self.avg_metal = -99