From 1d781dbeea1acc832c4918d932ebeef808025c34 Mon Sep 17 00:00:00 2001 From: Benjamin Johnson Date: Tue, 28 Sep 2021 16:21:10 -0400 Subject: [PATCH 1/7] Enable alpha enhanced isochrones and spectra in FSPS/alpha-mc Add extra afe index to SSP arrays; Add afeindex and afe parameters and surface number of afe grid points; Update fsps/afe submodule to 3486349. --- src/fsps/CMakeLists.txt | 2 - src/fsps/fsps.f90 | 183 +++++++++++++++++++++++----------------- src/fsps/fsps.py | 44 ++++++++-- src/fsps/libfsps | 2 +- tests/simple.py | 2 - 5 files changed, 141 insertions(+), 92 deletions(-) diff --git a/src/fsps/CMakeLists.txt b/src/fsps/CMakeLists.txt index cd32399b..eee0aab2 100644 --- a/src/fsps/CMakeLists.txt +++ b/src/fsps/CMakeLists.txt @@ -48,7 +48,6 @@ set( "${CMAKE_CURRENT_SOURCE_DIR}/libfsps/src/locate.f90" "${CMAKE_CURRENT_SOURCE_DIR}/libfsps/src/mod_gb.f90" "${CMAKE_CURRENT_SOURCE_DIR}/libfsps/src/mod_hb.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/libfsps/src/pz_convol.f90" "${CMAKE_CURRENT_SOURCE_DIR}/libfsps/src/sbf.f90" "${CMAKE_CURRENT_SOURCE_DIR}/libfsps/src/setup_tabular_sfh.f90" "${CMAKE_CURRENT_SOURCE_DIR}/libfsps/src/sfh_weight.f90" @@ -56,7 +55,6 @@ set( "${CMAKE_CURRENT_SOURCE_DIR}/libfsps/src/sfhlimit.f90" "${CMAKE_CURRENT_SOURCE_DIR}/libfsps/src/sfhstat.f90" "${CMAKE_CURRENT_SOURCE_DIR}/libfsps/src/smoothspec.f90" - "${CMAKE_CURRENT_SOURCE_DIR}/libfsps/src/spec_bin.f90" "${CMAKE_CURRENT_SOURCE_DIR}/libfsps/src/sps_setup.f90" "${CMAKE_CURRENT_SOURCE_DIR}/libfsps/src/ssp_gen.f90" "${CMAKE_CURRENT_SOURCE_DIR}/libfsps/src/tsum.f90" diff --git a/src/fsps/fsps.f90 b/src/fsps/fsps.f90 index b00972f1..9273e670 100644 --- a/src/fsps/fsps.f90 +++ b/src/fsps/fsps.f90 @@ -14,10 +14,10 @@ module driver integer :: is_setup=0 !f2py intent(hide) has_ssp - integer, dimension(nz) :: has_ssp=0 + integer, dimension(nz,nafe) :: has_ssp=0 !f2py intent(hide) has_ssp_age - integer, dimension(nz,nt) :: has_ssp_age=0 + integer, dimension(nz,nafe,nt) :: has_ssp_age=0 contains @@ -88,8 +88,8 @@ subroutine set_ssp_params(imf_type0,imf_upper_limit0,imf_lower_limit0,& pset%evtype=evtype pset%frac_xrb=frac_xrb - has_ssp(:) = 0 - has_ssp_age(:,:) = 0 + has_ssp(:,:) = 0 + has_ssp_age(:,:,:) = 0 end subroutine @@ -97,9 +97,10 @@ subroutine set_csp_params(smooth_velocity0,redshift_colors0,& compute_light_ages0,nebemlineinspec0,& dust_type0,add_dust_emission0,add_neb_emission0,& add_neb_continuum0,cloudy_dust0,add_igm_absorption0,& - zmet,sfh,wgp1,wgp2,wgp3,tau,& - const,tage,fburst,tburst,dust1,dust2,dust3,& - logzsol,zred,pmetals,dust_clumps,frac_nodust,& + zmet,afeindx,sfh,wgp1,wgp2,wgp3,& + tau,const,tage,fburst,tburst,& + dust1,dust2,dust3,& + logzsol,afe,zred,pmetals,dust_clumps,frac_nodust,& dust_index,dust_tesc,frac_obrun,uvb,mwr,& dust1_index,sf_start,sf_trunc,sf_slope,& duste_gamma,duste_umin,duste_qpah,& @@ -114,10 +115,10 @@ subroutine set_csp_params(smooth_velocity0,redshift_colors0,& compute_light_ages0,nebemlineinspec0,& dust_type0,add_dust_emission0,add_neb_emission0,& add_neb_continuum0,cloudy_dust0,add_igm_absorption0,& - zmet,sfh,wgp1,wgp2,wgp3 + zmet,afeindx,sfh,wgp1,wgp2,wgp3 double precision, intent(in) :: tau,& const,tage,fburst,tburst,dust1,dust2,dust3,& - logzsol,zred,pmetals,dust_clumps,frac_nodust,& + logzsol,afe,zred,pmetals,dust_clumps,frac_nodust,& dust_index,dust_tesc,frac_obrun,uvb,mwr,& dust1_index,sf_start,sf_trunc,sf_slope,& duste_gamma,duste_umin,duste_qpah,& @@ -136,6 +137,7 @@ subroutine set_csp_params(smooth_velocity0,redshift_colors0,& add_igm_absorption=add_igm_absorption0 pset%zmet=zmet + pset%afeindx=afeindx pset%sfh=sfh pset%wgp1=wgp1 pset%wgp2=wgp2 @@ -150,6 +152,7 @@ subroutine set_csp_params(smooth_velocity0,redshift_colors0,& pset%dust2=dust2 pset%dust3=dust3 pset%logzsol=logzsol + pset%afe=afe pset%zred=zred pset%pmetals=pmetals pset%dust_clumps=dust_clumps @@ -182,26 +185,29 @@ subroutine ssps ! Loop over the metallicity grid and compute all the SSPs. implicit none - integer :: zi + integer :: zi, ai do zi=1,nz - call ssp(zi) + do ai=1,nafe + call ssp(zi, ai) + enddo enddo end subroutine - subroutine ssp(zi) + subroutine ssp(zi,ai) - ! Compute a SSP at a single metallicity. + ! Compute a SSP at a single metallicity gridpoint. implicit none - integer, intent(in) :: zi + integer, intent(in) :: zi, ai pset%zmet = zi - call ssp_gen(pset, mass_ssp_zz(:,zi),lbol_ssp_zz(:,zi),& - spec_ssp_zz(:,:,zi)) + pset%afeindx = ai + call ssp_gen(pset, mass_ssp_zz(:,zi,ai),lbol_ssp_zz(:,zi,ai),& + spec_ssp_zz(:,:,zi,ai)) if (minval(pset%ssp_gen_age) .eq. 1) then - has_ssp(zi) = 1 + has_ssp(zi,ai) = 1 endif - has_ssp_age(zi,:) = pset%ssp_gen_age + has_ssp_age(zi,ai,:) = pset%ssp_gen_age end subroutine @@ -214,60 +220,70 @@ subroutine compute_zdep(ns,n_age,ztype) integer, intent(in) :: ns,n_age,ztype double precision, dimension(ns,n_age) :: spec double precision, dimension(n_age) :: mass,lbol - integer :: zlo,zmet - double precision :: zpos + integer :: zlo,zmet,alo,afeidx + double precision :: zpos, apos character(100) :: outfile if (ztype .eq. 0) then ! Build the SSP for one metallicity, then feed to compsp zmet = pset%zmet - if (has_ssp(zmet) .eq. 0) then - call ssp(zmet) + afeidx = pset%afeindx + if (has_ssp(zmet,afeidx) .eq. 0) then + call ssp(zmet,afeidx) endif !mass = mass_ssp_zz(:,zmet) !lbol = lbol_ssp_zz(:,zmet) !spec = spec_ssp_zz(:,:,zmet) - call compsp(0,1,outfile,mass_ssp_zz(:,zmet),lbol_ssp_zz(:,zmet),& - spec_ssp_zz(:,:,zmet),pset,ocompsp) + call compsp(0,1,outfile,mass_ssp_zz(:,zmet,afeidx),& + lbol_ssp_zz(:,zmet,afeidx),& + spec_ssp_zz(:,:,zmet,afeidx),pset,ocompsp) endif if (ztype .eq. 1) then zpos = pset%logzsol + apos = pset%afe ! Find the bracketing metallicity indices and generate ssps if ! necessary, then interpolate, and feed the result to compsp zlo = max(min(locate(log10(zlegend/zsol),zpos),nz-1),1) + alo = max(min(locate(afe_val,apos),nafe-1),1) do zmet=zlo,zlo+1 - if (has_ssp(zmet) .eq. 0) then - call ssp(zmet) - endif + do afeidx=alo,alo+1 + if ((has_ssp(zmet,afeidx) .eq. 0).and.(afeidx.le.nafe)) then + call ssp(zmet,afeidx) + endif + enddo enddo - call ztinterp(zpos,spec,lbol,mass) + call ztinterp(zpos,apos,spec,lbol,mass) call compsp(0,1,outfile,mass,lbol,spec,pset,ocompsp) endif if (ztype .eq. 2) then zpos = pset%logzsol + apos = pset%afe ! Build the SSPs for *every* metallicity if necessary, then ! comvolve with the MDF, and then feed to compsp do zmet=1,nz - if (has_ssp(zmet) .eq. 0) then - call ssp(zmet) - endif + do afeidx=1,nafe + if (has_ssp(zmet,afeidx) .eq. 0) then + call ssp(zmet,afeidx) + endif + enddo enddo - call ztinterp(zpos,spec,lbol,mass,zpow=pset%pmetals) + call ztinterp(zpos,apos,spec,lbol,mass,zpow=pset%pmetals) call compsp(0,1,outfile,mass,lbol,spec,pset,ocompsp) endif if (ztype .eq. 3) then ! Build the SSPs for *every* metallicity and feed all of them to compsp ! for z-dependent tabular + afeidx = pset%afeindx do zmet=1,nz - if (has_ssp(zmet) .eq. 0) then - call ssp(zmet) + if (has_ssp(zmet,afeidx) .eq. 0) then + call ssp(zmet,afeidx) endif enddo - call compsp(0,nz,outfile,mass_ssp_zz,lbol_ssp_zz,& - spec_ssp_zz,pset,ocompsp) + call compsp(0,nz,outfile,mass_ssp_zz(:,:,afeidx),lbol_ssp_zz(:,:,afeidx),& + spec_ssp_zz(:,:,:,afeidx),pset,ocompsp) endif @@ -305,40 +321,40 @@ subroutine get_mags(ns,n_age,n_bands,z_red,mc,mags) end subroutine - subroutine interp_ssp(ns,zpos,tpos,spec,mass,lbol) +! subroutine interp_ssp(ns,zpos,apos,tpos,spec,mass,lbol) ! Return the SSPs interpolated to the target metallicity - !(zpos) and target age (tpos) + !(zpos and apos) and target age (tpos) - implicit none +! implicit none - integer, intent(in) :: ns - double precision, intent(in) :: zpos - double precision, intent(in) :: tpos +! integer, intent(in) :: ns +! double precision, intent(in) :: zpos,apos +! double precision, intent(in) :: tpos - double precision, dimension(ns,1), intent(inout) :: spec - double precision, dimension(1), intent(inout) :: mass,lbol +! double precision, dimension(ns,1), intent(inout) :: spec +! double precision, dimension(1), intent(inout) :: mass,lbol - double precision, dimension(nt) :: time +! double precision, dimension(nt) :: time - integer :: zlo,zmet,tlo +! integer :: zlo,zmet,tlo - zlo = max(min(locate(log10(zlegend/0.0190),zpos),nz-1),1) - time = timestep_isoc(zlo,:) - tlo = max(min(locate(time,tpos),nt-1),1) +! zlo = max(min(locate(log10(zlegend/0.0190),zpos),nz-1),1) +! time = timestep_isoc(zlo,:) +! tlo = max(min(locate(time,tpos),nt-1),1) - do zmet=zlo,zlo+1 - if ((has_ssp_age(zmet,tlo) .eq. 0) .or. (has_ssp_age(zmet,tlo+1) .eq. 0)) then - pset%ssp_gen_age = 0 - pset%ssp_gen_age(tlo:tlo+1) = 1 - call ssp(zmet) - pset%ssp_gen_age = 1 - endif - enddo +! do zmet=zlo,zlo+1 +! if ((has_ssp_age(zmet,tlo) .eq. 0) .or. (has_ssp_age(zmet,tlo+1) .eq. 0)) then +! pset%ssp_gen_age = 0 +! pset%ssp_gen_age(tlo:tlo+1) = 1 +! call ssp(zmet) +! pset%ssp_gen_age = 1 +! endif +! enddo - call ztinterp(zpos,spec,lbol,mass,tpos=tpos) +! call ztinterp(zpos,apos,spec,lbol,mass,tpos=tpos) - end subroutine +! end subroutine subroutine smooth_spectrum(ns,wave,spec,sigma_broad,minw,maxw) @@ -392,28 +408,27 @@ subroutine get_csp_components(ns, csp1, csp2) end subroutine - - subroutine get_ssp_spec(ns,n_age,n_z,ssp_spec_out,ssp_mass_out,ssp_lbol_out) + ! subroutine get_ssp_spec(ns,n_age,n_z,ssp_spec_out,ssp_mass_out,ssp_lbol_out) ! Return the contents of the ssp spectral array, ! regenerating the ssps if necessary - implicit none - integer, intent(in) :: ns,n_age,n_z - integer :: zi - double precision, dimension(ns,n_age,n_z), intent(inout) :: ssp_spec_out - double precision, dimension(n_age,n_z), intent(inout) :: ssp_mass_out, ssp_lbol_out - do zi=1,nz - if (has_ssp(zi) .eq. 0) then - call ssp(zi) - endif - enddo - - ssp_spec_out = spec_ssp_zz - ssp_mass_out = mass_ssp_zz - ssp_lbol_out = lbol_ssp_zz - - end subroutine +! implicit none +! integer, intent(in) :: ns,n_age,n_z +! integer :: zi +! double precision, dimension(ns,n_age,n_z), intent(inout) :: ssp_spec_out +! double precision, dimension(n_age,n_z), intent(inout) :: ssp_mass_out, ssp_lbol_out +! do zi=1,nz +! if (has_ssp(zi) .eq. 0) then +! call ssp(zi) +! endif +! enddo + +! ssp_spec_out = spec_ssp_zz +! ssp_mass_out = mass_ssp_zz +! ssp_lbol_out = lbol_ssp_zz +! +! end subroutine subroutine set_sfh_tab(ntab, age, sfr, met) @@ -480,6 +495,16 @@ subroutine get_zsol(z_sol) end subroutine + subroutine get_nafe(n_afe) + + ! get the number of afe grid points + + implicit none + integer, intent(out) :: n_afe + n_afe = nafe + + end subroutine + subroutine get_timefull(n_age,timefull) ! Get the actual time steps of the SSPs. @@ -571,7 +596,7 @@ subroutine get_libraries(isocname,specname,dustname) implicit none character(4), intent(out) :: isocname - character(5), intent(out) :: specname + character(7), intent(out) :: specname character(6), intent(out) :: dustname isocname = isoc_type specname = spec_type @@ -597,7 +622,7 @@ subroutine get_nmass_isochrone(zz, tt, nmass) ! Get the number of masses included in a specific isochrone. integer, intent(in) :: zz,tt integer, intent(out) :: nmass - nmass = nmass_isoc(zz,tt) + nmass = nmass_isoc(zz,1,tt) end subroutine diff --git a/src/fsps/fsps.py b/src/fsps/fsps.py index fd7a1b8d..d124a947 100644 --- a/src/fsps/fsps.py +++ b/src/fsps/fsps.py @@ -179,13 +179,24 @@ class StellarPopulation(object): (Z/Z_\odot)`. Only used if ``zcontinuous > 0``. :param pmetals: (default: 2.0) - The power for the metallicty distribution function. The MDF is given by - :math:`(Z \, e^{-Z})^{\mathrm{pmetals}}` where :math:`Z = - z/(z_\odot \, 10^{\mathrm{logzsol}})` and z is the metallicity in - linear units (i.e., :math:`z_\odot = 0.019`). Using a negative value - will result in smoothing of the SSPs by a three-point triangular kernel - before linear interpolation (in :math:`\log Z`) to the requested - metallicity. Only used if ``zcontinuous = 2``. + The power for the metallicty distribution function. The MDF is given by + :math:`(Z \, e^{-Z})^{\mathrm{pmetals}}` where :math:`Z = + z/(z_\odot \, 10^{\mathrm{logzsol}})` and z is the metallicity in + linear units (i.e., :math:`z_\odot = 0.019`). Using a negative value + will result in smoothing of the SSPs by a three-point triangular kernel + before linear interpolation (in :math:`\log Z`) to the requested + metallicity. Only used if ``zcontinuous = 2``. + + :param afeindx: (default: 1) + The alpha enhancement specified as integer ranging from 1 to nafe. If + ``zcontinuous > 0`` then this parameter is ignored. Only takes effect if + (python-)FSPS was compiled with AFE_FLAG=1 and uses the MIST isochrones + and C3K spectra. + + :param afe: (default: 0.0) + The value of [alpha/Fe] for the spectra and isochrones. Only takes + effect if (python-)FSPS was compiled with AFE_FLAG=1 and uses the MIST + isochrones. Generally should be -0.2 < afe < 0.6 :param imf_type: (default: 2) Common variable defining the IMF type: @@ -477,6 +488,8 @@ def __init__( zmet=1, logzsol=0.0, pmetals=2.0, + afeindx=1, + afe=0.0, imf_type=2, imf_upper_limit=120, imf_lower_limit=0.08, @@ -567,6 +580,7 @@ def _update_params(self): def _compute_csp(self): self._update_params() + assert self._zcontinuous < 2, "Cannot use MDF with afe enhancement" NSPEC = driver.get_nspec() NTFULL = driver.get_ntfull() @@ -709,6 +723,9 @@ def _ztinterp(self, zpos, tpos, peraa=False): :param zpos: The metallicity, in units of :math:`\log(Z/Z_\odot)` + :param apos: + The alpha-enhancement, in units of :math:`[\alpha/Fe]` + :param tpos: The desired age, in Gyr. @@ -725,13 +742,14 @@ def _ztinterp(self, zpos, tpos, peraa=False): :returns lbol: The bolometric luminosity of the returned SSP. """ + raise NotImplementedError("Bindings disabled for afe enhancement") if self.params.dirtiness == 2: self._update_params() NSPEC = driver.get_nspec() spec, mass, lbol = np.zeros(NSPEC), np.zeros(1), np.zeros(1) logt_yrs = np.log10(tpos * 1e9) - driver.interp_ssp(zpos, logt_yrs, spec, mass, lbol) + driver.interp_ssp(zpos, apos, logt_yrs, spec, mass, lbol) if peraa: wavegrid = self.wavelengths @@ -763,6 +781,8 @@ def _all_ssp_spec(self, update=True, peraa=False): The bolometric luminosity of the SSPs, having shape (ntfull, nz). """ + raise NotImplementedError("Bindings disabled for afe") + if (self.params.dirtiness == 2) and update: self._update_params() @@ -1113,6 +1133,12 @@ def solar_metallicity(self): self._solar_metallicity = driver.get_zsol() return self._solar_metallicity + @property + def n_afe(self): + r"""The number of [a/Fe] gridpoints.""" + nafe = driver.get_nafe() + return nafe + @property def ssp_ages(self): r"""The age grid of the SSPs, in log(years), used by FSPS.""" @@ -1304,6 +1330,7 @@ class ParameterSet(object): "cloudy_dust", "add_igm_absorption", "zmet", + "afeindx", "sfh", "wgp1", "wgp2", @@ -1317,6 +1344,7 @@ class ParameterSet(object): "dust2", "dust3", "logzsol", + "afe", "zred", "pmetals", "dust_clumps", diff --git a/src/fsps/libfsps b/src/fsps/libfsps index 5b87175e..34863491 160000 --- a/src/fsps/libfsps +++ b/src/fsps/libfsps @@ -1 +1 @@ -Subproject commit 5b87175ed9ed5a8abf6af87f9b987517e6a624d9 +Subproject commit 34863491895fe0974497ed170f01ed904b3ca17c diff --git a/tests/simple.py b/tests/simple.py index 8e5a42c6..113e3f75 100644 --- a/tests/simple.py +++ b/tests/simple.py @@ -2,5 +2,3 @@ pop = StellarPopulation() pop.get_spectrum() - -print("success") From ecff8490d5c437e44358451a02bbaf0399d10c24 Mon Sep 17 00:00:00 2001 From: Benjamin Johnson Date: Tue, 28 Jul 2026 23:56:41 -0400 Subject: [PATCH 2/7] Expose the 'use_lw_tpagb' variable --- src/fsps/fsps.f90 | 5 +++-- src/fsps/fsps.py | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/fsps/fsps.f90 b/src/fsps/fsps.f90 index 9273e670..fd66b777 100644 --- a/src/fsps/fsps.f90 +++ b/src/fsps/fsps.f90 @@ -45,7 +45,7 @@ subroutine set_ssp_params(imf_type0,imf_upper_limit0,imf_lower_limit0,& imf1,imf2,imf3,vdmc,mdave,dell,& delt,sbss,fbhb,pagb,add_stellar_remnants0,& tpagb_norm_type0,add_agb_dust_model0,agb_dust,& - redgb,agb,masscut,fcstar,evtype,use_wr_spectra0,& + redgb,agb,masscut,fcstar,evtype,use_wr_spectra0,use_lw_tpagb0,& logt_wmb_hot0,add_xrb_emission0,frac_xrb,smooth_lsf0) ! Set the parameters that affect the SSP computation. @@ -53,7 +53,7 @@ subroutine set_ssp_params(imf_type0,imf_upper_limit0,imf_lower_limit0,& implicit none integer, intent(in) :: imf_type0,add_stellar_remnants0,tpagb_norm_type0,& - add_agb_dust_model0,use_wr_spectra0,add_xrb_emission0,smooth_lsf0 + add_agb_dust_model0,use_wr_spectra0,use_lw_tpagb0,add_xrb_emission0,smooth_lsf0 double precision, intent(in) :: imf_upper_limit0, imf_lower_limit0,& imf1,imf2,imf3,vdmc,mdave,dell,& delt,sbss,fbhb,pagb,agb_dust,& @@ -67,6 +67,7 @@ subroutine set_ssp_params(imf_type0,imf_upper_limit0,imf_lower_limit0,& tpagb_norm_type=tpagb_norm_type0 add_agb_dust_model=add_agb_dust_model0 use_wr_spectra=use_wr_spectra0 + use_lw_tpagb=use_lw_tpagb0 logt_wmb_hot=logt_wmb_hot0 smooth_lsf=smooth_lsf0 add_xrb_emission=add_xrb_emission0 diff --git a/src/fsps/fsps.py b/src/fsps/fsps.py index d124a947..bcdea26b 100644 --- a/src/fsps/fsps.py +++ b/src/fsps/fsps.py @@ -249,6 +249,10 @@ class StellarPopulation(object): Turn on/off the WR spectral library. If off (0), will use the main default library instead + :param use_lw_tpagb: (default: 0) + Turn on/off the use of the Lancon & Wood (2002) O-type TP-AGB library. + If off (0), will use the main default library instead. + :param logt_wmb_hot: (default: 0.0) Use the Eldridge (2017) WMBasic hot star library above this value of :math:`\log T_\mathrm{eff}` or 25,000K, whichever is larger. @@ -500,6 +504,7 @@ def __init__( mdave=0.5, evtype=-1, use_wr_spectra=1, + use_lw_tpagb=0, logt_wmb_hot=0.0, add_xrb_emission=0, masscut=150.0, @@ -1312,6 +1317,7 @@ class ParameterSet(object): "fcstar", "evtype", "use_wr_spectra", + "use_lw_tpagb", "logt_wmb_hot", "add_xrb_emission", "frac_xrb", From ee53407d54c3a7ce0880b8fea533754668423824 Mon Sep 17 00:00:00 2001 From: Benjamin Johnson Date: Thu, 30 Jul 2026 14:08:52 -0400 Subject: [PATCH 3/7] update docs for new libraries. --- docs/installation.rst | 9 +++++---- docs/stellarpop_api.rst | 2 +- src/fsps/fsps.py | 22 +++++++++++----------- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 236827e0..57488838 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -52,8 +52,9 @@ in more detail in the FSPS documentation, but their names are: * Stellar spectral libraries - - `MILES` (default) - - `BASEL` + - `C3K_LR` (default) + - `MILES` + - `C3K_HR` * Dust emission libraries @@ -63,12 +64,12 @@ in more detail in the FSPS documentation, but their names are: Changing any of these libraries requires switching off the relevant default, which for isochrones is `MIST` and for spectra is `MILES`, and switching on the desired library. As an example, you can change to Padova isochrones and the -BaSeL low resolution synthetic stellar library by re-installing: +MILES empirical stellar library by re-installing: .. code-block:: bash pip uninstall fsps - FFLAGS="-DMIST=0 -DPADOVA=1 -DMILES=0 -DBASEL=1" python -m pip install fsps --no-binary fsps + FFLAGS="-DMIST=0 -DPADOVA=1 -DC3K_LR=0 -DMILES=1" python -m pip install fsps --no-binary fsps where the `--no-binary fsps` flag is required to force building from source. diff --git a/docs/stellarpop_api.rst b/docs/stellarpop_api.rst index 36bf7f5e..fba3d270 100644 --- a/docs/stellarpop_api.rst +++ b/docs/stellarpop_api.rst @@ -15,7 +15,7 @@ and some dust with a Calzetti et al (2000) extinction curve:: >>> sp = fsps.StellarPopulation(compute_vega_mags=False, zcontinuous=1, sfh=0, logzsol=0.0, dust_type=2, dust2=0.2) >>> sp.libraries - ('mist', 'miles', 'DL07') + (b'mist', b'c3k_lr', b'DL07') The last line indicates that we are using the MIST isochrones and MILES spectral library. These can be changed only by reinstalling python-FSPS with appropriate diff --git a/src/fsps/fsps.py b/src/fsps/fsps.py index bcdea26b..84cb9ff4 100644 --- a/src/fsps/fsps.py +++ b/src/fsps/fsps.py @@ -124,23 +124,23 @@ class StellarPopulation(object): * 2: Villaume, Conroy, Johnson 2015 normalization :param dell: (default: 0.0) - Shift in :math:`\log L_\mathrm{bol}` of the TP-AGB isochrones. Note - that the meaning of this parameter and the one below has changed to - reflect the updated calibrations presented in Conroy & Gunn (2009). - That is, these parameters now refer to a modification about the - calibrations presented in that paper. Only has effect if - ``tpagb_norm_type=1``. + Shift in :math:`\log L_\mathrm{bol}` of the TP-AGB isochrones for Padova + models. Note that the meaning of this parameter and the one below + has changed to reflect the updated calibrations presented in Conroy & + Gunn (2009). That is, these parameters now refer to a modification about + the calibrations presented in that paper. Only has effect if + ``tpagb_norm_type=1`` and the PDVA isochrones are used. :param delt: (default: 0.0) - Shift in :math:`\log T_\mathrm{eff}` of the TP-AGB isochrones. Only - has effect if ``tpagb_norm_type=1``. + Shift in :math:`\log T_\mathrm{eff}` of the TP-AGB isochrones for Padova + models. Only has effect if ``tpagb_norm_type=1`` and the PDVA + isochrones are used. :param redgb: (default: 1.0) - Modify weight given to RGB. Only available with BaSTI isochrone set. + Modify weight given to RGB. Does not have effect for the Padova isochrones. :param agb: (default: 1.0) - Modify weight given to TP-AGB. This only has effect for FSPS v3.1 or - higher. + Modify weight given to TP-AGB (for all isochrones with a TP-AGB phase). :param fcstar: (default: 1.0) Fraction of stars that the Padova isochrones identify as Carbon stars From 42635d17f96ddc820379dc9489f6b2c417086bad Mon Sep 17 00:00:00 2001 From: Benjamin Johnson Date: Thu, 30 Jul 2026 14:28:39 -0400 Subject: [PATCH 4/7] Make cloudy_dust a setup parameter instead of a variable; closes #230 --- src/fsps/fsps.f90 | 16 +++++++++------- src/fsps/fsps.py | 20 ++++++++++++-------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/fsps/fsps.f90 b/src/fsps/fsps.f90 index fd66b777..867e1dd6 100644 --- a/src/fsps/fsps.f90 +++ b/src/fsps/fsps.f90 @@ -22,17 +22,19 @@ module driver contains - subroutine setup(compute_vega_mags0, vactoair_flag0) + subroutine setup(compute_vega_mags0, vactoair_flag0, cloudy_dust0) ! Load all the data files/templates into memory. implicit none - integer, intent(in) :: compute_vega_mags0, vactoair_flag0 + integer, intent(in) :: compute_vega_mags0, vactoair_flag0, cloudy_dust0 compute_vega_mags = compute_vega_mags0 vactoair_flag = vactoair_flag0 + cloudy_dust = cloudy_dust0 + call sps_setup(-1) is_setup = 1 @@ -97,7 +99,7 @@ subroutine set_ssp_params(imf_type0,imf_upper_limit0,imf_lower_limit0,& subroutine set_csp_params(smooth_velocity0,redshift_colors0,& compute_light_ages0,nebemlineinspec0,& dust_type0,add_dust_emission0,add_neb_emission0,& - add_neb_continuum0,cloudy_dust0,add_igm_absorption0,& + add_neb_continuum0,add_igm_absorption0,& zmet,afeindx,sfh,wgp1,wgp2,wgp3,& tau,const,tage,fburst,tburst,& dust1,dust2,dust3,& @@ -115,7 +117,7 @@ subroutine set_csp_params(smooth_velocity0,redshift_colors0,& integer, intent(in) :: smooth_velocity0,redshift_colors0,& compute_light_ages0,nebemlineinspec0,& dust_type0,add_dust_emission0,add_neb_emission0,& - add_neb_continuum0,cloudy_dust0,add_igm_absorption0,& + add_neb_continuum0,add_igm_absorption0,& zmet,afeindx,sfh,wgp1,wgp2,wgp3 double precision, intent(in) :: tau,& const,tage,fburst,tburst,dust1,dust2,dust3,& @@ -134,7 +136,6 @@ subroutine set_csp_params(smooth_velocity0,redshift_colors0,& add_dust_emission=add_dust_emission0 add_neb_emission=add_neb_emission0 add_neb_continuum=add_neb_continuum0 - cloudy_dust=cloudy_dust0 add_igm_absorption=add_igm_absorption0 pset%zmet=zmet @@ -459,12 +460,13 @@ subroutine set_ssp_lsf(nsv, sigma, wlo, whi) end subroutine - subroutine get_setup_vars(cvms, vta_flag) + subroutine get_setup_vars(cvms, vta_flag, cd_flag) implicit none - integer, intent(out) :: cvms, vta_flag + integer, intent(out) :: cvms, vta_flag, cd_flag cvms = compute_vega_mags vta_flag = vactoair_flag + cd_flag = cloudy_dust end subroutine diff --git a/src/fsps/fsps.py b/src/fsps/fsps.py index 84cb9ff4..c5530461 100644 --- a/src/fsps/fsps.py +++ b/src/fsps/fsps.py @@ -37,6 +37,10 @@ class StellarPopulation(object): If ``True``, output wavelengths in air (rather than vac). Can only be changed during initialization. + :param cloudy_dust: (default: False) + Switch to include dust in the Cloudy tables. Can only be changed during + initialization. + :param zcontinuous: (default: 0) Flag specifying how interpolation in metallicity of the simple stellar populations (SSPs) is performed before computing composite stellar @@ -110,9 +114,6 @@ class StellarPopulation(object): spread function. See the ``set_lsf()`` method for details. Only takes effect if ``smooth_velocity`` is True. - :param cloudy_dust: (default: False) - Switch to include dust in the Cloudy tables. - :param agb_dust: (default: 1.0) Scales the circumstellar AGB dust emission. @@ -461,7 +462,11 @@ class StellarPopulation(object): """ def __init__( - self, compute_vega_mags=False, vactoair_flag=False, zcontinuous=0, **kwargs + self, compute_vega_mags=False, + vactoair_flag=False, + zcontinuous=0, + cloudy_dust=False, + **kwargs ): # Set up the parameters to their default values. self.params = ParameterSet( @@ -476,7 +481,6 @@ def __init__( nebemlineinspec=True, smooth_velocity=True, smooth_lsf=False, - cloudy_dust=False, agb_dust=1.0, tpagb_norm_type=2, dell=0.0, @@ -559,11 +563,12 @@ def __init__( # Before the first time we interact with the FSPS driver, we need to # run the ``setup`` method. if not driver.is_setup: - driver.setup(compute_vega_mags, vactoair_flag) + driver.setup(compute_vega_mags, vactoair_flag, cloudy_dust) else: - cvms, vtaflag = driver.get_setup_vars() + cvms, vtaflag, cd_flag = driver.get_setup_vars() assert compute_vega_mags == bool(cvms) assert vactoair_flag == bool(vtaflag) + assert cloudy_dust == bool(cd_flag) self._zcontinuous = zcontinuous # Caching. self._wavelengths = None @@ -1333,7 +1338,6 @@ class ParameterSet(object): "add_dust_emission", "add_neb_emission", "add_neb_continuum", - "cloudy_dust", "add_igm_absorption", "zmet", "afeindx", From 5ceeee4f20e1a708f0c23451119b542662d8a862 Mon Sep 17 00:00:00 2001 From: Benjamin Johnson Date: Fri, 31 Jul 2026 16:33:58 -0400 Subject: [PATCH 5/7] Updating FSPS to v4.0 --- src/fsps/libfsps | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsps/libfsps b/src/fsps/libfsps index 34863491..05b5e550 160000 --- a/src/fsps/libfsps +++ b/src/fsps/libfsps @@ -1 +1 @@ -Subproject commit 34863491895fe0974497ed170f01ed904b3ca17c +Subproject commit 05b5e550ddd7ffb1e71102b75cfbfdf057c211fd From e17b910e2b5d0f341f86008a22b2b8123f5de796 Mon Sep 17 00:00:00 2001 From: Benjamin Johnson Date: Fri, 31 Jul 2026 17:40:40 -0400 Subject: [PATCH 6/7] Add some scripts for demonstrating features, and comparing across different libraries. --- demos/compare_lib.py | 72 +++++++++ demos/feature_demo.py | 62 +++++--- demos/test_and_compare_lib.sh | 63 ++++++++ demos/test_lib.py | 278 ++++++++++++++++++++++++++++++++++ 4 files changed, 455 insertions(+), 20 deletions(-) create mode 100644 demos/compare_lib.py create mode 100644 demos/test_and_compare_lib.sh create mode 100644 demos/test_lib.py diff --git a/demos/compare_lib.py b/demos/compare_lib.py new file mode 100644 index 00000000..e040d931 --- /dev/null +++ b/demos/compare_lib.py @@ -0,0 +1,72 @@ +import os, glob, argparse +from astropy.table import Table +import matplotlib.pyplot as pl + +pl.style.use("via") +pl.rcParams["font.size"] = 14 + +MARKERS = {"MILES": "o", "C3K_LR": "s", "C3K_HR": "^"} +COLORS = {"MIST": "k", "PDVA": "firebrick", "PRSC": "dodgerblue", "BPSS": "orange"} + +def get_parser(): + parser = argparse.ArgumentParser(description="Run pyFSPS AFE tests") + parser.add_argument("--dir", type=str, default="./lib_tests/sps_home-aa77c3_pyfsps-42635d_pyfslib-aa77c3", + help="Output directory for test results") + parser.add_argument("--kind", type=str, default="constneb", choices=["const", "constneb", "tau1"], + help="Kind of SFH to plot spectrum of.") + parser.add_argument("--filters", type=str, nargs=2, default=["bessell_B", "bessell_V"], + help="Two filters to plot color evolution for.") + + return parser + + +if __name__ == "__main__": + parser = get_parser() + args = parser.parse_args() + + dirn = args.dir + f1, f2 = args.filters + + + tbls = glob.glob(f"{dirn}/color_evol_*.csv") + tags = [os.path.basename(t).replace("color_evol_", "").replace(".csv", "") for t in tbls] + #tags = [f"{t}_sfh1" for t in tags] + tags = sorted(tags) + print(tags) + kind = args.kind + + fig, ax = pl.subplots(figsize=(8, 6)) + sfig, sax = pl.subplots(figsize=(8, 6)) + for tag in tags: + isoc, slib, afe = tag.split("+") + marker = MARKERS.get(slib.upper(), "o") + color = COLORS.get(isoc.upper(), "k") + color = "cyan" if afe == "afe1" else color + color = "magenta" if afe == "afe1_afe0.3" else color + try: + table = Table.read(f"{dirn}/color_evol_{tag}.csv", format="csv") + ax.plot(table["log_age"], table[f1] - table[f2], f"-{marker}", color=color, mec=color, alpha=0.5, label=f"{tag}, [a/Fe]=0.0") + spec = Table.read(f"{dirn}/spectrum_{tag}_{kind}.csv", format="csv") + sax.plot(spec["wave"], spec["spec"], "-", alpha=0.5, label=f"{tag}") + except Exception as e: + print(f"Error plotting {tag}: {e}") + pass + + ax.set_xlabel("SSP Age (log yr)") + ax.set_ylabel("Color (B-V)") + ax.set_ylim(-0.6, 1.8) + ax.set_xlim(4.8, 10.4) + ax.grid(True) + # split the legend into two columns + ax.legend(loc="upper left", frameon=True, framealpha=0.5, fontsize="small", ncol=2) + + sax.set_title("Spectra for different tags: constant SFH") + sax.set_xlabel("Wavelength (AA)") + sax.set_ylabel("Flux") + sax.set_xlim(3000, 10000) + sax.set_yscale("log") + sax.set_ylim(5e-5, 5e-3) + sax.grid(True) + sax.legend(loc="upper right", frameon=True, framealpha=0.5, fontsize="small") + fig.savefig(f"{dirn}/color_evol.png", dpi=300) + sfig.savefig(f"{dirn}/spectrum_{kind}.png", dpi=300) \ No newline at end of file diff --git a/demos/feature_demo.py b/demos/feature_demo.py index 98a74dec..e45f61c2 100644 --- a/demos/feature_demo.py +++ b/demos/feature_demo.py @@ -6,7 +6,7 @@ value of some variables. """ -import os +import os, time import matplotlib.pyplot as pl from matplotlib.backends.backend_pdf import PdfPages @@ -38,20 +38,42 @@ def prettify(fig, ax, label=None): return fig, ax +def get_githash(dir): + """ + Get the current git hash for a repo + """ + import subprocess + cmd = ["git", "rev-parse", "HEAD"] + hash = subprocess.check_output(cmd, cwd=dir).decode("utf-8").strip() + return hash + + if __name__ == "__main__": - pl.rc("text", usetex=True) - pl.rc("font", family="serif") - pl.rc("axes", grid=False) - pl.rc("xtick", direction="in") - pl.rc("ytick", direction="in") - pl.rc("xtick", top=True) - pl.rc("ytick", right=True) + + pl.rcParams["font.family"] = "serif" + pl.rcParams["font.serif"] = ["cmr10"] + pl.rcParams["mathtext.fontset"] = "cm" + pl.rcParams["axes.formatter.use_mathtext"] = True + pl.rcParams["axes.grid"] = True + pl.rcParams["grid.alpha"] = 0.5 + pl.rcParams["xtick.direction"] = "in" + pl.rcParams["ytick.direction"] = "in" + + + pyfsps_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) + fsps_hash = get_githash(os.environ["SPS_HOME"]) + pyfsps_hash = get_githash(os.path.dirname(__file__)) + pyfsps_lib_hash = get_githash(os.path.join(pyfsps_dir, "src", "fsps", "libfsps")) + print(f"FSPS git hash: {fsps_hash}") + print(f"pyFSPS git hash: {pyfsps_hash}") + print(f"pyFSPS lib git hash: {pyfsps_lib_hash}") + hashtag = f"sps_home-{fsps_hash[:6]}_pyfsps-{pyfsps_hash[:6]}_pyfslib-{pyfsps_lib_hash[:6]}" sps = fsps.StellarPopulation(zcontinuous=1) - ilib, slib, dlib = sps.libraries + ilib, slib, dlib = [s.decode("utf-8") for s in sps.libraries] print(ilib, slib) - os.makedirs("./figures", exist_ok=True) - pdf = PdfPages("./figures/features.pdf") + os.makedirs(f"./figures/{hashtag}", exist_ok=True) + pdf = PdfPages(f"./figures/{hashtag}/features_{ilib}-{slib}-{dlib}.pdf") # Basic spectrum sps.params["sfh"] = 4 @@ -61,42 +83,42 @@ def prettify(fig, ax, label=None): sps.params["imf_type"] = 2 # kroupa sps.params["imf3"] = 2.3 fig, ax, spec = makefig(sps) - fig, ax = prettify(fig, ax, label=r"$\tau=5$, Age$=13.7$,\\n$\log Z/Z_\odot=0.0$") + fig, ax = prettify(fig, ax, label=r"$\tau=5$, Age$=13.7$,"+"\n" + r"$\log Z/Z_\odot=0.0$") pdf.savefig(fig) pl.close(fig) # change IMF sps.params["imf3"] = 2.5 fig, ax, spec = makefig(sps, oldspec=spec) - fig, ax = prettify(fig, ax, label=r"IMF slope") + fig, ax = prettify(fig, ax, label=f"IMF slope") pdf.savefig(fig) # Attenuate sps.params["add_dust_emission"] = False sps.params["dust2"] = 0.2 fig, ax, spec = makefig(sps, oldspec=spec) - fig, ax = prettify(fig, ax, label=r"Dust Attenuation") + fig, ax = prettify(fig, ax, label=f"Dust Attenuation") pdf.savefig(fig) pl.close(fig) # Dust emission sps.params["add_dust_emission"] = True fig, ax, spec = makefig(sps, oldspec=spec) - fig, ax = prettify(fig, ax, label=r"Dust Emission") + fig, ax = prettify(fig, ax, label=f"Dust Emission") pdf.savefig(fig) pl.close(fig) # Dust temperature sps.params["duste_umin"] = 10 fig, ax, spec = makefig(sps, oldspec=spec) - fig, ax = prettify(fig, ax, label=r"Dust SED\\n({})".format(dlib)) + fig, ax = prettify(fig, ax, label=f"Dust SED\n({dlib})") pdf.savefig(fig) pl.close(fig) # AGN emission sps.params["fagn"] = 0.3 fig, ax, spec = makefig(sps, oldspec=spec) - fig, ax = prettify(fig, ax, label=r"AGN dust\\n(Nenkova)") + fig, ax = prettify(fig, ax, label=f"AGN dust\n(Nenkova)") pdf.savefig(fig) pl.close(fig) @@ -104,12 +126,12 @@ def prettify(fig, ax, label=None): sps.params["add_neb_emission"] = True sps.params["gas_logu"] = -3.5 fig, ax, spec = makefig(sps, oldspec=spec) - fig, ax = prettify(fig, ax, label=r"Neb. emission\\n(Byler)") + fig, ax = prettify(fig, ax, label=f"Neb. emission\n(Byler)") pdf.savefig(fig) pl.close(fig) # change logu - sps.params["gas_logu"] = -1.0 + sps.params["gas_logu"] = -1.5 fig, ax, spec = makefig(sps, oldspec=spec) fig, ax = prettify(fig, ax, label=r"Change U$_{neb}$") pdf.savefig(fig) @@ -127,7 +149,7 @@ def prettify(fig, ax, label=None): sps.params["zred"] = 6.0 sps.params["add_igm_absorption"] = True fig, ax, spec = makefig(sps, oldspec=spec) - fig, ax = prettify(fig, ax, label=r"IGM attenuation\\n(Madau, $z=6$)") + fig, ax = prettify(fig, ax, label=f"IGM attenuation\n" + r"(Madau, $z=6$)") pdf.savefig(fig) pl.close(fig) diff --git a/demos/test_and_compare_lib.sh b/demos/test_and_compare_lib.sh new file mode 100644 index 00000000..da83601a --- /dev/null +++ b/demos/test_and_compare_lib.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +# This script cycles through the different FSPS libraries and runs afe.py to run +# tests and generate the color evolution plots and data. + + +PYFSPS_DIR=`realpath ../` +TESTDIR=$PYFSPS_DIR/demos +SPECLIBS=( C3K_LR C3K_HR MILES ) +ISOCS=( MIST PADOVA PARSEC ) + +# Do AFE_FLAG=1 for MIST + C3K_LR (the defaults) +cd $PYFSPS_DIR +FFLAGS="-DAFE_FLAG=1" +echo "Recompiling with flags: ${FFLAGS}" +python -m pip uninstall -y fsps +FFLAGS=${FFLAGS} python -m pip install . --no-binary fsps +cd $TESTDIR +python test_lib.py + +# Now do the other combinations of isochrones and spectral libraries. We will +# always use AFE_FLAG=0 (the default), but we will switch the isochrone and +# spectral library. +for iso in "${ISOCS[@]}"; do + for lib in "${SPECLIBS[@]}"; do + echo "Running tests for ${lib} and ${iso}" + cd $PYFSPS_DIR + + FFLAGS="" + + # Switch the isochrone + if [[ ${iso} != "MIST" ]]; then + FFLAGS=${FFLAGS}" -DMIST=0" + FFLAGS=${FFLAGS}" -D${iso}=1" + fi + + # Switch the spectral library + if [[ ${lib} != "C3K_LR" ]]; then + FFLAGS=${FFLAGS}" -DC3K_LR=0" + FFLAGS=${FFLAGS}" -D${lib}=1" + fi + + echo $FFLAGS + + # Recompile with flags + echo "Recompiling with flags: ${FFLAGS}" + python -m pip uninstall -y fsps + FFLAGS=${FFLAGS} python -m pip install . --no-binary fsps + + # run the tests + cd $TESTDIR + python test_lib.py + done +done + +# Now do BPASS, which has no spectral library attached +cd $PYFSPS_DIR +FFLAGS="-DMIST=0 -DC3K_LR=0 -DBPASS=1" +echo "Recompiling with flags: ${FFLAGS}" +python -m pip uninstall -y fsps +FFLAGS=${FFLAGS} python -m pip install . --no-binary fsps +cd $TESTDIR +python test_lib.py \ No newline at end of file diff --git a/demos/test_lib.py b/demos/test_lib.py new file mode 100644 index 00000000..9e34f347 --- /dev/null +++ b/demos/test_lib.py @@ -0,0 +1,278 @@ +# We want to run a few basic tests. These should check that +# x1. The code is importable +# x2. The SP object instantiates and the libraries attrbute is correct +# x3. The returned spectrum has the right shape +# x4. The attributes have the right shape +# x5. The spectrum is not all zeros +# x6. Nebular emission works for young populations when add_neb_emission=True +# Then we have a few things to look at for different compiler options +# x1. The SSP color evolution for afe=0 makes sense between C3K_LR, C3K_LR+alpha, and MILES +# x2. The C3K_LR+alpha spectra are different from the C3K_LR spectra for afe>0 +# x3. The C3K_LR spectra are the same as the C3K_LR+alpha spectra for afe=0 + +import sys, os, argparse +import numpy as np +from astropy.table import Table +from sedpy.observate import load_filters, getSED + + +filternames = ['bessell_U', 'bessell_B', 'bessell_V', 'bessell_R', 'twomass_J', 'twomass_Ks'] +filternames += ['sdss_u0', 'sdss_g0', 'sdss_r0', 'sdss_i0', 'sdss_z0'] + +FILTERS = load_filters(filternames) +#FILTERS = load_filters(['sdss_u0', 'sdss_g0', 'sdss_r0', 'sdss_i0', 'sdss_z0']) + +def get_githash(dir): + """ + Get the current git hash for a repo + """ + import subprocess + cmd = ["git", "rev-parse", "HEAD"] + hash = subprocess.check_output(cmd, cwd=dir).decode("utf-8").strip() + return hash + + +def test_import_and_instantiate(zcontinuous=1): + import fsps + from fsps import StellarPopulation + sp = StellarPopulation(zcontinuous=zcontinuous) + print(sp.libraries) + assert "afeindx" in sp.params.csp_params + assert "afe" in sp.params.csp_params + print(f"Number of afe gridpoints: {sp.n_afe}") + print(f'afe index: {sp.params["afeindx"]}') + print(f'afe value: {sp.params["afe"]}') + sp.params["use_lw_tpagb"] = True + + default_params = dict([(k, sp.params[k]) for k in sp.params.all_params]) + return sp, default_params + + +def _reset_default_params(pop, params, zcontinuous=1): + pop._zcontinuous = zcontinuous + for k in pop.params.all_params: + pop.params[k] = params[k] + + +def test_spectrum_shape(sp): + wave, spec = sp.get_spectrum(tage=1.0, peraa=False) + print(len(wave)) + assert spec.shape == (len(wave),) + assert not np.all(spec == 0) + wave, spec = sp.get_spectrum(peraa=True) + assert spec.shape == (len(sp.ssp_ages), len(wave),) + assert spec.shape[0] > 1 + return None + + +def test_attributes(sp, sfh=3): + + sp.params["sfh"] = sfh + if sfh == 3: + sp.set_tabular_sfh(np.linspace(0, 10, 10), + np.random.uniform(0, 1, 10)) + wave, spec = sp.get_spectrum(tage=1.0, peraa=True) + young, old = sp._csp_young_old + assert young.shape == old.shape + assert young.shape == (len(wave),) + + w = sp.wavelengths + res = sp.resolutions + assert w.shape == res.shape + assert w.shape == (len(wave),) + + zleg = sp.zlegend + n_afe = sp.n_afe + ages = sp.ssp_ages + weights = sp._ssp_weights + assert weights.shape == (len(ages), len(zleg)) + assert np.any(weights > 0) + assert n_afe >= 1 + + age = sp.log_age + mstar = sp.stellar_mass + lbol = sp.log_lbol + sfr = sp.sfr + mdust = sp.dust_mass + mform = sp.formed_mass + assert np.isscalar(age) + assert np.isscalar(mstar) + assert np.isscalar(lbol) + assert np.isscalar(sfr) + assert np.isscalar(mdust) + assert np.isscalar(mform) + assert age > 0 + assert mstar > 0 + assert mform > 0 + assert lbol > -33 + assert mstar/mform < 1 + + ewave = sp.emline_wavelengths + elum = sp.emline_luminosity + assert ewave.shape == elum.shape + assert ewave.size > 0 + + return None + + +def test_sfhs(sp): + + for sfh in [0, 1, 2, 3, 4, 5]: + try: + stat = test_attributes(sp, sfh=sfh) + except Exception as e: + print(f"Error testing sfh={sfh}: {e}") + raise + return None + + +def test_neb(sp, tage=0.003, gas_logz=-0.5, gas_logu=-2.0): + current = sp.params["add_neb_emission"] + sp.params["add_neb_emission"] = True + sp.params["logzsol"] = gas_logz + sp.params["gas_logu"] = gas_logu + sp.params["gas_logz"] = gas_logz + sp.params["sfh"] = 0 + wave, spec = sp.get_spectrum(tage=tage, peraa=True) + eline_waves = sp.emline_wavelengths + eline_lums = sp.emline_luminosity + assert eline_waves.shape == eline_lums.shape + assert np.any(eline_lums > 0) + sp.params["add_neb_emission"] = current + return None + + +def color_evol(sp, afe=0, plot=False, filterlist=FILTERS): + fn = (1, 2) # B-V + sp.params["sfh"] = 0 + sp.params["afe"] = afe + ssp_ages = sp.ssp_ages.copy() + 0.02 + mags = np.zeros((len(ssp_ages), len(filterlist))) + for it, log_age in enumerate(ssp_ages): + age = 10**(log_age-9) + wave, spec = sp.get_spectrum(tage=age, peraa=True) + mags[it] = getSED(wave, spec, filterlist=filterlist, linear_flux=False) + #print(f"Age: {age:.2f} Gyr, AFE: {afe}, mags: {mags}") + + # make an astropy table of the mangitudes and ages + colnames = [f"{filt.name}" for filt in filterlist] + colnames.insert(0, "log_age") + t = Table(data=np.column_stack((ssp_ages, mags)), names=colnames) + + if plot: + import matplotlib.pyplot as pl + fig, ax = pl.subplots() + ax.plot(ssp_ages, mags[:, fn[0]] - mags[:, fn[1]], "-o") + ax.set_xlabel("SSP Age (log yr)") + ax.set_ylabel(f"Color ({filterlist[fn[0]].name} - {filterlist[fn[1]].name})") + ax.set_ylim(-1.4, 1.8) + ax.set_xlim(4.8, 10.4) + return t, fig + else: + return t, None + + +def get_parser(): + parser = argparse.ArgumentParser(description="Run pyFSPS AFE tests") + parser.add_argument("--outdir", type=str, default="./lib_tests", + help="Output directory for test results") + parser.add_argument("--show_hashes", action="store_true", + help="Only show git hashes for FSPS and pyFSPS") + parser.add_argument("--logzsol", type=float, default=0.0, + help="Metallicity for the tests") + parser.add_argument("--plot", type=int, default=1, + help="Plot the color evolution (1) or not (0)") + parser.add_argument("--write", type=int, default=1, + help="Write the results to disk (1) or not (0)") + return parser + + +if __name__ == "__main__": + + parser = get_parser() + args = parser.parse_args() + outdir = args.outdir + + pyfsps_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) + fsps_hash = get_githash(os.environ["SPS_HOME"]) + pyfsps_hash = get_githash(os.path.dirname(__file__)) + pyfsps_lib_hash = get_githash(os.path.join(pyfsps_dir, "src", "fsps", "libfsps")) + print(f"FSPS git hash: {fsps_hash}") + print(f"pyFSPS git hash: {pyfsps_hash}") + print(f"pyFSPS lib git hash: {pyfsps_lib_hash}") + + hashtag = f"sps_home-{fsps_hash[:6]}_pyfsps-{pyfsps_hash[:6]}_pyfslib-{pyfsps_lib_hash[:6]}" + if args.show_hashes: + sys.exit() + + print(f"writing results to {outdir}/{hashtag}") + if args.write | args.plot: + os.makedirs(f"{outdir}/{hashtag}", exist_ok=True) + + sp, defaults = test_import_and_instantiate(zcontinuous=1) + defaults["logzsol"] = args.logzsol + + libs, nafe = [s.decode("utf-8") for s in sp.libraries], sp.n_afe + tag = f"{libs[0]}+{libs[1]}+afe{int(nafe > 1)}" + + test_spectrum_shape(sp) + _reset_default_params(sp, defaults, zcontinuous=1) + test_attributes(sp, sfh=0) + _reset_default_params(sp, defaults, zcontinuous=1) + test_sfhs(sp) + _reset_default_params(sp, defaults, zcontinuous=1) + test_neb(sp) + _reset_default_params(sp, defaults, zcontinuous=1) + + # get all the color data + table, fig = color_evol(sp, afe=0, plot=True) + + if args.write: + table.write(f"{outdir}/{hashtag}/color_evol_{tag}.csv", format="csv", overwrite=True) + if args.plot: + fig.axes[0].grid(True) + fig.axes[0].set_title(f"{tag}") + fig.savefig(f"{outdir}/{hashtag}/color_evol_{tag}.png", dpi=300) + if nafe > 1: + # just run one alpha_enhancement + t3, fig3 = color_evol(sp, afe=0.3, plot=args.plot) + if args.write: + t3.write(f"{outdir}/{hashtag}/color_evol_{tag}_afe0.3.csv", format="csv", overwrite=True) + if args.plot: + fig3.axes[0].set_title(f"{tag}, afe=0.3") + fig3.axes[0].grid(True) + fig3.axes[0].plot(table["log_age"], table["bessell_B"] - table["bessell_V"], "-o", alpha=0.5, label="[a/Fe]=0.0") + fig3.axes[0].legend() + fig3.savefig(f"{outdir}/{hashtag}/color_evol_{tag}_afe0.3.png", dpi=300) + _reset_default_params(sp, defaults, zcontinuous=1) + + # -- make spectra for different SFHs -- + sp.params["logzsol"] = args.logzsol + sp.params["sfh"] = 1 + sp.params["const"] = 1.0 + wave, spec = sp.get_spectrum(tage=1, peraa=True) + tbl = Table(data=np.column_stack((wave, spec)), names=["wave", "spec"]) + if args.write: + tbl.write(f"{outdir}/{hashtag}/spectrum_{tag}_const.csv", format="csv", overwrite=True) + _reset_default_params(sp, defaults, zcontinuous=1) + + sp.params["logzsol"] = args.logzsol + sp.params["sfh"] = 1 + sp.params["const"] = 1.0 + sp.params["add_neb_emission"] = True + wave, spec = sp.get_spectrum(tage=1, peraa=True) + tbl = Table(data=np.column_stack((wave, spec)), names=["wave", "spec"]) + if args.write: + tbl.write(f"{outdir}/{hashtag}/spectrum_{tag}_constneb.csv", format="csv", overwrite=True) + _reset_default_params(sp, defaults, zcontinuous=1) + + sp.params["logzsol"] = args.logzsol + sp.params["sfh"] = 1 + sp.params["const"] = 0.0 + sp.params["tau"] = 1.0 + wave, spec = sp.get_spectrum(tage=1, peraa=True) + tbl = Table(data=np.column_stack((wave, spec)), names=["wave", "spec"]) + if args.write: + tbl.write(f"{outdir}/{hashtag}/spectrum_{tag}_tau1.csv", format="csv", overwrite=True) + _reset_default_params(sp, defaults, zcontinuous=1) + From 5e5b31213677f5e4570215fffe97d6eb542cda40 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2026 21:52:11 +0000 Subject: [PATCH 7/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- demos/compare_lib.py | 50 ++++++++++++----- demos/feature_demo.py | 6 +- demos/test_and_compare_lib.sh | 2 +- demos/test_lib.py | 101 ++++++++++++++++++++++++---------- src/fsps/fsps.py | 7 ++- 5 files changed, 117 insertions(+), 49 deletions(-) diff --git a/demos/compare_lib.py b/demos/compare_lib.py index e040d931..3651a514 100644 --- a/demos/compare_lib.py +++ b/demos/compare_lib.py @@ -8,14 +8,29 @@ MARKERS = {"MILES": "o", "C3K_LR": "s", "C3K_HR": "^"} COLORS = {"MIST": "k", "PDVA": "firebrick", "PRSC": "dodgerblue", "BPSS": "orange"} + def get_parser(): parser = argparse.ArgumentParser(description="Run pyFSPS AFE tests") - parser.add_argument("--dir", type=str, default="./lib_tests/sps_home-aa77c3_pyfsps-42635d_pyfslib-aa77c3", - help="Output directory for test results") - parser.add_argument("--kind", type=str, default="constneb", choices=["const", "constneb", "tau1"], - help="Kind of SFH to plot spectrum of.") - parser.add_argument("--filters", type=str, nargs=2, default=["bessell_B", "bessell_V"], - help="Two filters to plot color evolution for.") + parser.add_argument( + "--dir", + type=str, + default="./lib_tests/sps_home-aa77c3_pyfsps-42635d_pyfslib-aa77c3", + help="Output directory for test results", + ) + parser.add_argument( + "--kind", + type=str, + default="constneb", + choices=["const", "constneb", "tau1"], + help="Kind of SFH to plot spectrum of.", + ) + parser.add_argument( + "--filters", + type=str, + nargs=2, + default=["bessell_B", "bessell_V"], + help="Two filters to plot color evolution for.", + ) return parser @@ -27,10 +42,11 @@ def get_parser(): dirn = args.dir f1, f2 = args.filters - tbls = glob.glob(f"{dirn}/color_evol_*.csv") - tags = [os.path.basename(t).replace("color_evol_", "").replace(".csv", "") for t in tbls] - #tags = [f"{t}_sfh1" for t in tags] + tags = [ + os.path.basename(t).replace("color_evol_", "").replace(".csv", "") for t in tbls + ] + # tags = [f"{t}_sfh1" for t in tags] tags = sorted(tags) print(tags) kind = args.kind @@ -45,9 +61,17 @@ def get_parser(): color = "magenta" if afe == "afe1_afe0.3" else color try: table = Table.read(f"{dirn}/color_evol_{tag}.csv", format="csv") - ax.plot(table["log_age"], table[f1] - table[f2], f"-{marker}", color=color, mec=color, alpha=0.5, label=f"{tag}, [a/Fe]=0.0") + ax.plot( + table["log_age"], + table[f1] - table[f2], + f"-{marker}", + color=color, + mec=color, + alpha=0.5, + label=f"{tag}, [a/Fe]=0.0", + ) spec = Table.read(f"{dirn}/spectrum_{tag}_{kind}.csv", format="csv") - sax.plot(spec["wave"], spec["spec"], "-", alpha=0.5, label=f"{tag}") + sax.plot(spec["wave"], spec["spec"], "-", alpha=0.5, label=f"{tag}") except Exception as e: print(f"Error plotting {tag}: {e}") pass @@ -60,7 +84,7 @@ def get_parser(): # split the legend into two columns ax.legend(loc="upper left", frameon=True, framealpha=0.5, fontsize="small", ncol=2) - sax.set_title("Spectra for different tags: constant SFH") + sax.set_title("Spectra for different tags: constant SFH") sax.set_xlabel("Wavelength (AA)") sax.set_ylabel("Flux") sax.set_xlim(3000, 10000) @@ -69,4 +93,4 @@ def get_parser(): sax.grid(True) sax.legend(loc="upper right", frameon=True, framealpha=0.5, fontsize="small") fig.savefig(f"{dirn}/color_evol.png", dpi=300) - sfig.savefig(f"{dirn}/spectrum_{kind}.png", dpi=300) \ No newline at end of file + sfig.savefig(f"{dirn}/spectrum_{kind}.png", dpi=300) diff --git a/demos/feature_demo.py b/demos/feature_demo.py index e45f61c2..50999f07 100644 --- a/demos/feature_demo.py +++ b/demos/feature_demo.py @@ -43,6 +43,7 @@ def get_githash(dir): Get the current git hash for a repo """ import subprocess + cmd = ["git", "rev-parse", "HEAD"] hash = subprocess.check_output(cmd, cwd=dir).decode("utf-8").strip() return hash @@ -59,7 +60,6 @@ def get_githash(dir): pl.rcParams["xtick.direction"] = "in" pl.rcParams["ytick.direction"] = "in" - pyfsps_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) fsps_hash = get_githash(os.environ["SPS_HOME"]) pyfsps_hash = get_githash(os.path.dirname(__file__)) @@ -83,7 +83,9 @@ def get_githash(dir): sps.params["imf_type"] = 2 # kroupa sps.params["imf3"] = 2.3 fig, ax, spec = makefig(sps) - fig, ax = prettify(fig, ax, label=r"$\tau=5$, Age$=13.7$,"+"\n" + r"$\log Z/Z_\odot=0.0$") + fig, ax = prettify( + fig, ax, label=r"$\tau=5$, Age$=13.7$," + "\n" + r"$\log Z/Z_\odot=0.0$" + ) pdf.savefig(fig) pl.close(fig) diff --git a/demos/test_and_compare_lib.sh b/demos/test_and_compare_lib.sh index da83601a..301135ce 100644 --- a/demos/test_and_compare_lib.sh +++ b/demos/test_and_compare_lib.sh @@ -60,4 +60,4 @@ echo "Recompiling with flags: ${FFLAGS}" python -m pip uninstall -y fsps FFLAGS=${FFLAGS} python -m pip install . --no-binary fsps cd $TESTDIR -python test_lib.py \ No newline at end of file +python test_lib.py diff --git a/demos/test_lib.py b/demos/test_lib.py index 9e34f347..db1ff14c 100644 --- a/demos/test_lib.py +++ b/demos/test_lib.py @@ -1,4 +1,4 @@ -# We want to run a few basic tests. These should check that +# We want to run a few basic tests. These should check that # x1. The code is importable # x2. The SP object instantiates and the libraries attrbute is correct # x3. The returned spectrum has the right shape @@ -15,18 +15,26 @@ from astropy.table import Table from sedpy.observate import load_filters, getSED - -filternames = ['bessell_U', 'bessell_B', 'bessell_V', 'bessell_R', 'twomass_J', 'twomass_Ks'] -filternames += ['sdss_u0', 'sdss_g0', 'sdss_r0', 'sdss_i0', 'sdss_z0'] +filternames = [ + "bessell_U", + "bessell_B", + "bessell_V", + "bessell_R", + "twomass_J", + "twomass_Ks", +] +filternames += ["sdss_u0", "sdss_g0", "sdss_r0", "sdss_i0", "sdss_z0"] FILTERS = load_filters(filternames) -#FILTERS = load_filters(['sdss_u0', 'sdss_g0', 'sdss_r0', 'sdss_i0', 'sdss_z0']) +# FILTERS = load_filters(['sdss_u0', 'sdss_g0', 'sdss_r0', 'sdss_i0', 'sdss_z0']) + def get_githash(dir): """ Get the current git hash for a repo """ import subprocess + cmd = ["git", "rev-parse", "HEAD"] hash = subprocess.check_output(cmd, cwd=dir).decode("utf-8").strip() return hash @@ -35,6 +43,7 @@ def get_githash(dir): def test_import_and_instantiate(zcontinuous=1): import fsps from fsps import StellarPopulation + sp = StellarPopulation(zcontinuous=zcontinuous) print(sp.libraries) assert "afeindx" in sp.params.csp_params @@ -60,8 +69,11 @@ def test_spectrum_shape(sp): assert spec.shape == (len(wave),) assert not np.all(spec == 0) wave, spec = sp.get_spectrum(peraa=True) - assert spec.shape == (len(sp.ssp_ages), len(wave),) - assert spec.shape[0] > 1 + assert spec.shape == ( + len(sp.ssp_ages), + len(wave), + ) + assert spec.shape[0] > 1 return None @@ -69,8 +81,7 @@ def test_attributes(sp, sfh=3): sp.params["sfh"] = sfh if sfh == 3: - sp.set_tabular_sfh(np.linspace(0, 10, 10), - np.random.uniform(0, 1, 10)) + sp.set_tabular_sfh(np.linspace(0, 10, 10), np.random.uniform(0, 1, 10)) wave, spec = sp.get_spectrum(tage=1.0, peraa=True) young, old = sp._csp_young_old assert young.shape == old.shape @@ -105,7 +116,7 @@ def test_attributes(sp, sfh=3): assert mstar > 0 assert mform > 0 assert lbol > -33 - assert mstar/mform < 1 + assert mstar / mform < 1 ewave = sp.emline_wavelengths elum = sp.emline_luminosity @@ -143,16 +154,16 @@ def test_neb(sp, tage=0.003, gas_logz=-0.5, gas_logu=-2.0): def color_evol(sp, afe=0, plot=False, filterlist=FILTERS): - fn = (1, 2) # B-V + fn = (1, 2) # B-V sp.params["sfh"] = 0 sp.params["afe"] = afe ssp_ages = sp.ssp_ages.copy() + 0.02 mags = np.zeros((len(ssp_ages), len(filterlist))) for it, log_age in enumerate(ssp_ages): - age = 10**(log_age-9) + age = 10 ** (log_age - 9) wave, spec = sp.get_spectrum(tage=age, peraa=True) mags[it] = getSED(wave, spec, filterlist=filterlist, linear_flux=False) - #print(f"Age: {age:.2f} Gyr, AFE: {afe}, mags: {mags}") + # print(f"Age: {age:.2f} Gyr, AFE: {afe}, mags: {mags}") # make an astropy table of the mangitudes and ages colnames = [f"{filt.name}" for filt in filterlist] @@ -161,6 +172,7 @@ def color_evol(sp, afe=0, plot=False, filterlist=FILTERS): if plot: import matplotlib.pyplot as pl + fig, ax = pl.subplots() ax.plot(ssp_ages, mags[:, fn[0]] - mags[:, fn[1]], "-o") ax.set_xlabel("SSP Age (log yr)") @@ -174,16 +186,26 @@ def color_evol(sp, afe=0, plot=False, filterlist=FILTERS): def get_parser(): parser = argparse.ArgumentParser(description="Run pyFSPS AFE tests") - parser.add_argument("--outdir", type=str, default="./lib_tests", - help="Output directory for test results") - parser.add_argument("--show_hashes", action="store_true", - help="Only show git hashes for FSPS and pyFSPS") - parser.add_argument("--logzsol", type=float, default=0.0, - help="Metallicity for the tests") - parser.add_argument("--plot", type=int, default=1, - help="Plot the color evolution (1) or not (0)") - parser.add_argument("--write", type=int, default=1, - help="Write the results to disk (1) or not (0)") + parser.add_argument( + "--outdir", + type=str, + default="./lib_tests", + help="Output directory for test results", + ) + parser.add_argument( + "--show_hashes", + action="store_true", + help="Only show git hashes for FSPS and pyFSPS", + ) + parser.add_argument( + "--logzsol", type=float, default=0.0, help="Metallicity for the tests" + ) + parser.add_argument( + "--plot", type=int, default=1, help="Plot the color evolution (1) or not (0)" + ) + parser.add_argument( + "--write", type=int, default=1, help="Write the results to disk (1) or not (0)" + ) return parser @@ -228,7 +250,9 @@ def get_parser(): table, fig = color_evol(sp, afe=0, plot=True) if args.write: - table.write(f"{outdir}/{hashtag}/color_evol_{tag}.csv", format="csv", overwrite=True) + table.write( + f"{outdir}/{hashtag}/color_evol_{tag}.csv", format="csv", overwrite=True + ) if args.plot: fig.axes[0].grid(True) fig.axes[0].set_title(f"{tag}") @@ -237,11 +261,21 @@ def get_parser(): # just run one alpha_enhancement t3, fig3 = color_evol(sp, afe=0.3, plot=args.plot) if args.write: - t3.write(f"{outdir}/{hashtag}/color_evol_{tag}_afe0.3.csv", format="csv", overwrite=True) + t3.write( + f"{outdir}/{hashtag}/color_evol_{tag}_afe0.3.csv", + format="csv", + overwrite=True, + ) if args.plot: fig3.axes[0].set_title(f"{tag}, afe=0.3") fig3.axes[0].grid(True) - fig3.axes[0].plot(table["log_age"], table["bessell_B"] - table["bessell_V"], "-o", alpha=0.5, label="[a/Fe]=0.0") + fig3.axes[0].plot( + table["log_age"], + table["bessell_B"] - table["bessell_V"], + "-o", + alpha=0.5, + label="[a/Fe]=0.0", + ) fig3.axes[0].legend() fig3.savefig(f"{outdir}/{hashtag}/color_evol_{tag}_afe0.3.png", dpi=300) _reset_default_params(sp, defaults, zcontinuous=1) @@ -253,7 +287,9 @@ def get_parser(): wave, spec = sp.get_spectrum(tage=1, peraa=True) tbl = Table(data=np.column_stack((wave, spec)), names=["wave", "spec"]) if args.write: - tbl.write(f"{outdir}/{hashtag}/spectrum_{tag}_const.csv", format="csv", overwrite=True) + tbl.write( + f"{outdir}/{hashtag}/spectrum_{tag}_const.csv", format="csv", overwrite=True + ) _reset_default_params(sp, defaults, zcontinuous=1) sp.params["logzsol"] = args.logzsol @@ -263,7 +299,11 @@ def get_parser(): wave, spec = sp.get_spectrum(tage=1, peraa=True) tbl = Table(data=np.column_stack((wave, spec)), names=["wave", "spec"]) if args.write: - tbl.write(f"{outdir}/{hashtag}/spectrum_{tag}_constneb.csv", format="csv", overwrite=True) + tbl.write( + f"{outdir}/{hashtag}/spectrum_{tag}_constneb.csv", + format="csv", + overwrite=True, + ) _reset_default_params(sp, defaults, zcontinuous=1) sp.params["logzsol"] = args.logzsol @@ -273,6 +313,7 @@ def get_parser(): wave, spec = sp.get_spectrum(tage=1, peraa=True) tbl = Table(data=np.column_stack((wave, spec)), names=["wave", "spec"]) if args.write: - tbl.write(f"{outdir}/{hashtag}/spectrum_{tag}_tau1.csv", format="csv", overwrite=True) + tbl.write( + f"{outdir}/{hashtag}/spectrum_{tag}_tau1.csv", format="csv", overwrite=True + ) _reset_default_params(sp, defaults, zcontinuous=1) - diff --git a/src/fsps/fsps.py b/src/fsps/fsps.py index c5530461..aa3e82a0 100644 --- a/src/fsps/fsps.py +++ b/src/fsps/fsps.py @@ -39,7 +39,7 @@ class StellarPopulation(object): :param cloudy_dust: (default: False) Switch to include dust in the Cloudy tables. Can only be changed during - initialization. + initialization. :param zcontinuous: (default: 0) Flag specifying how interpolation in metallicity of the simple stellar @@ -462,11 +462,12 @@ class StellarPopulation(object): """ def __init__( - self, compute_vega_mags=False, + self, + compute_vega_mags=False, vactoair_flag=False, zcontinuous=0, cloudy_dust=False, - **kwargs + **kwargs, ): # Set up the parameters to their default values. self.params = ParameterSet(