def __call__(self, theta): """Posterior likelihood call.""" # Evaluate prior lnpriors = np.array([self._priors[n](v) for n, v in zip(self._param_names, theta)]) lnprior = np.sum(lnpriors) if ~np.isfinite(lnprior): model_mjy = np.empty(self.n_computed_bands, np.float) model_mjy.fill(np.nan) blob = {"lnpost": -np.inf, "logMstar": np.nan, "logMdust": np.nan, "logLbol": np.nan, "logSFR": np.nan, "logAge": np.nan, "model_sed": model_mjy, "lnpriors": lnpriors} return -np.inf, blob meta1 = np.empty(5, dtype=float) meta2 = np.empty(5, dtype=float) for name, val in zip(self._param_names, theta): if name == 'logmass': logm = val elif name == 'logZZsol': logZZsol = val elif name == 'logtau': self.sp.params['tau'] = 10. ** val elif name in self.sp.params.all_params: # list of parameter names self.sp.params[name] = val zmet1, zmet2 = bracket_logz(logZZsol) # Compute fluxes with low metallicity self.sp.params['zmet'] = zmet1 f1 = abs_ab_mag_to_micro_jy( self.sp.get_mags(tage=13.8, bands=self._compute_bands), self.d) meta1[0] = self.sp.stellar_mass meta1[1] = self.sp.dust_mass meta1[2] = self.sp.log_lbol meta1[3] = self.sp.sfr meta1[4] = self.sp.log_age # Compute fluxes with high metallicity self.sp.params['zmet'] = zmet2 f2 = abs_ab_mag_to_micro_jy( self.sp.get_mags(tage=13.8, bands=self._compute_bands), self.d) meta2[0] = self.sp.stellar_mass meta2[1] = self.sp.dust_mass meta2[2] = self.sp.log_lbol meta2[3] = self.sp.sfr meta2[4] = self.sp.log_age # Interpolate and scale the SED by mass model_mjy = 10. ** logm * interp_logz(zmet1, zmet2, logZZsol, f1, f2) # Interpolate metadata between the two metallicities meta = interp_logz(zmet1, zmet2, logZZsol, meta1, meta2) # Compute likelihood # Automatically ignores pixels with no measurement (Nan) lnL = -0.5 * np.nansum( np.power((model_mjy[self.band_indices] - self._sed) / self._err, 2.)) lnpost = lnprior + lnL if ~np.isfinite(lnpost): lnpost = -np.inf # Scale statistics by the total mass blob = {"lnpost": lnpost, "logMstar": logm + np.log10(meta[0]), # log solar masses, "logMdust": logm + np.log10(meta[1]), # log solar masses "logLbol": logm * meta[2], # log solar luminosities "logSFR": logm * meta[3], # star formation rate, M_sun / yr "logAge": meta[4], "model_sed": model_mjy, "lnpriors": lnpriors} return lnpost, blob
def interp_z_likelihood(args): """Generic single-pixel likeihood function where a single metallicity is linearly interpolated. Note that the SED can contain values of NaN, those bands will be automatically ignored from the likelihood calculation """ global SP theta, theta_names, phi, phi_names, B, area, sed, err, sed_bands, \ compute_bands = args meta1 = np.empty(5, dtype=float) meta2 = np.empty(5, dtype=float) band_indices = np.array([compute_bands.index(b) for b in sed_bands]) # Evaluate the ln-likelihood function by interpolating between # metallicty bracket for name, val in itertools.chain(zip(theta_names, theta), zip(phi_names, phi)): if name == 'logmass': logm = val elif name == 'logZZsol': logZZsol = val elif name == 'logtau': SP.params['tau'] = 10. ** val elif name in SP.params.all_params: # list of parameter names SP.params[name] = val zmet1, zmet2 = bracket_logz(logZZsol) # Compute fluxes with low metallicity SP.params['zmet'] = zmet1 f1 = abs_ab_mag_to_micro_jy( SP.get_mags(tage=13.8, bands=compute_bands), phi[0]) meta1[0] = SP.stellar_mass meta1[1] = SP.dust_mass meta1[2] = SP.log_lbol meta1[3] = SP.sfr meta1[4] = SP.log_age # Compute fluxes with high metallicity SP.params['zmet'] = zmet2 f2 = abs_ab_mag_to_micro_jy( SP.get_mags(tage=13.8, bands=compute_bands), phi[0]) meta2[0] = SP.stellar_mass meta2[1] = SP.dust_mass meta2[2] = SP.log_lbol meta2[3] = SP.sfr meta2[4] = SP.log_age # Interpolate and scale the SED by mass model_mjy = 10. ** logm * interp_logz(zmet1, zmet2, logZZsol, f1, f2) # Interpolate metadata between the two metallicities meta = interp_logz(zmet1, zmet2, logZZsol, meta1, meta2) # Compute likelihood # Automatically ignores pixels with no measurement (Nan) L = -0.5 * np.nansum( np.power((model_mjy[band_indices] + B * area - sed) / err, 2.)) # Scale statistics by the total mass blob = {"logMstar": logm + np.log10(meta[0]), # log solar masses, "logMdust": logm + np.log10(meta[1]), # log solar masses "logLbol": logm * meta[2], # log solar luminosities "logSFR": logm * meta[3], # star formation rate, M_sun / yr "logAge": meta[4], "model_sed": model_mjy} # note: background no included return L, blob
def compute_library_seds(self, bands, age=13.7, default_pset=None): """Compute an SED for each library model instance. Model SEDs are stored as absolute fluxes (µJy at d=10pc), normalized to a 1 Solar mass stellar population. .. todo:: Support parallel computation. Parameters ---------- bands : list List of `FSPS bandpass names <http://dan.iel.fm/python-fsps/current/filters/>`_. default_pset : dict Default Python-FSPS parameters. """ if default_pset is None: default_pset = {} # ALWAYS compute AB mags default_pset['compute_vega_mags'] = False # Solar magnitude in each bandpass solar_mags = [fsps.get_filter(n).msun_ab for n in bands] # Add bands and AB solar mags to the group attr metadata self.group.attrs['bands'] = bands self.group.attrs['msun_ab'] = solar_mags # Build the SED table table_names = ['seds', 'mass_light', 'meta'] for name in table_names: if name in self.group: del self.group[name] # Table for SEDs n_models = len(self.group["params"]) dtype = np.dtype([(n, np.float) for n in bands]) sed_table = self.group.create_dataset("seds", (n_models,), dtype=dtype) # Table for M/L ratios in each bandpass dtype = np.dtype([(n, np.float) for n in bands]) ml_table = self.group.create_dataset("mass_light", (n_models,), dtype=dtype) # Table for metadata (stellar mass, dust mass, etc..) meta_cols = ('logMstar', 'logMdust', 'logLbol', 'logSFR', 'logAge') dtype = np.dtype([(n, np.float) for n in meta_cols]) meta_table = self.group.create_dataset("meta", (n_models,), dtype=dtype) # Iterate on each model # TODO eventually split this work between processors sp_param_names = self.group['params'].dtype.names sp = fsps.StellarPopulation(**default_pset) for i, row in enumerate(self.group["params"]): for n, p in zip(sp_param_names, row): sp.params[n] = float(p) mags = sp.get_mags(tage=age, bands=bands) fluxes = abs_ab_mag_to_micro_jy(mags, 10.) # Fill in SED and ML tables for n, msun, flux in zip(bands, solar_mags, fluxes): # interesting slicing syntax for structured array assignment sed_table[n, i] = flux logL = micro_jy_to_luminosity(flux, msun, 10.) log_ml = np.log10(sp.stellar_mass) - logL ml_table[n, i] = log_ml # Fill in meta data table meta_table['logMstar', i] = np.log10(sp.stellar_mass) meta_table['logMdust', i] = np.log10(sp.dust_mass) meta_table['logLbol', i] = np.log10(sp.log_lbol) meta_table['logSFR', i] = np.log10(sp.sfr) meta_table['logAge', i] = sp.log_age