primat — top-level facade¶
main.py¶
Main class for primat.
Design¶
PRIMAT.__init__(params)accepts an optional dict of parameters, builds aPRIMATConfig, loads all data files (thermodynamics tables and nuclear rate tables), and pre-computes the thermal background.PRIMAT.solve()runs the full nuclear network ODE integration and returns the BBN predictions.PRIMAT.primat_results()callssolve()and returns the result dict (for backwards compatibility).
- class primat.main.PRIMAT(params=None, extra_rho=None, custom_network=None, background=None)[source]¶
Bases:
objectMain primat class.
- Parameters:
params (dict, optional) – Run-time parameters overriding defaults (see
config.DEFAULT_PARAMS).extra_rho (list of callable, optional) –
Extra contributions to the total energy density entering the Friedmann equation. Each element is a function
rho(Tg) -> MeV^4of the photon temperatureTg[MeV], summed intorho_totbyprimat.background.StandardBackground.Hubble(). This is the generic plug-in point for “dark sector” components; Early Dark Energy (cfg.fEDE > 0) is implemented as the first such plug-in (seeprimat.background.StandardBackground._setup_EDE()) and is appended automatically – callers do not need to include it here.Example: a constant extra radiation density of dRho [MeV^4]:
>>> PRIMAT({"network": "small"}, extra_rho=[lambda Tg: dRho])
Ignored (with a warning) if
backgroundis supplied: a caller providing a fullBackgroundinstance is expected to fold any extra energy density into it directly.background (primat.background.Background, optional) –
A pre-built background instance to drive the nuclear network with, in place of the standard
cfg.custom_background-driven dispatch below. This is the seam for a fully custom expansion history (e.g. a non-standard cosmology that needs more thanextra_rhocan express): subclassprimat.background.Background– whose docstring lists the compulsory (T_of_t,t_of_T,rhoB_BBN,weak_nTOp_frwrd/weak_nTOp_bkwrd) and optional methods – and pass an instance here. SinceBackground.__init__already takes(cfg, plasma, extra_rho),PRIMATtakesself.cfg/self.plasmafrom the supplied instance (background.cfg/background.plasma) rather than building its own, so the nuclear network and the background always agree on which config/plasma drove them – build the instance with your ownPRIMATConfig/Plasmafirst, then hand it toPRIMAT.None(default) preserves today’scfg.custom_background-based dispatch (StandardBackgroundorCustomBackground) usingparams/extra_rhoas usual. Mutually exclusive withparams/extra_rho/cfg.custom_background, which only make sense for the default dispatch; supplyingbackgroundtogether withparamsorextra_rhoemits a warning, and the suppliedbackgroundinstance wins.Example: drive the network with a hand-built background:
>>> from primat import Background >>> from primat.config import PRIMATConfig >>> from primat.plasma import Plasma >>> cfg = PRIMATConfig({"network": "small"}) >>> plasma = Plasma(cfg) >>> class MyBackground(Background): ... ... # implement T_of_t, t_of_T, rhoB_BBN, weak_nTOp_* >>> PRIMAT(background=MyBackground(cfg, plasma))
custom_network (dict, optional) –
GUI/scripting “Customise Reactions” override, forwarded verbatim to
primat.network_data.UpdateNuclearRates(see its docstring for the{"removed": [...], "replaced": {...}, "added": {...}}schema).None(default) uses the standardcfg.networkreaction list unchanged. Not aPRIMATConfigfield: it carries bulk table data rather than a fingerprintable scalar, so it does not participate in any rate cache fingerprint.Example: drop one reaction, override another’s rate table, and add a brand-new reaction (its stoichiometry is read from the name):
>>> PRIMAT({"network": "small"}, custom_network={ ... "removed": ["d_d__t_p"], ... "replaced": {"n_p__d_g": "0.001 1.2e3\n10.0 4.5e1\n"}, ... "added": {"t_t__He4_n_n": "0.001 1.0e2\n10.0 1.0e2\n"}, ... })
- solve(progress=None)[source]¶
Integrate the nuclear network over the three temperature eras and return a dict of BBN observables.
Delegates the ODE integration to
primat.nuclear_network.NuclearNetwork.solve()(Class 2), which is driven byself.background(Class 1, seeprimat.background) and populatesself.nuclear.Y_final,self.nuclear.abundance_namesandself.nuclear.Y_of_t. The “final observables” – light-element ratios fromY_final, plusNeff/Omeganurel/OneOverOmeganunrfrom the background’s optional neutrino-sector hooks (Background.rho_nu_total_final(),Background.N_eff(),Background.Omeganuh2_relnu()/Background.Omeganuh2_nrnu()) – are assembled here, intoself.results, which is whatget_quantity/__getitem__/Neff()/YPBBN()/… andprimat_results()read.The neutrino-sector keys (
Neff,Omeganurel,OneOverOmeganunr) are only added to the dict if the background actually provides that information (Nonereturned from the corresponding hook) – a minimal background with no neutrino-sector model simply omits them.- Parameters:
progress (bool | None) – bool, optional.
None(default) defers tocfg.show_progress(aDEFAULT_PARAMSkey, defaultTrue). Pass an explicitTrue/Falseto override the config for this call regardless ofshow_progress(e.g.Falseinside MC workers to avoid per-sample spam).- Return type:
- property a_of_T: Callable[[Any], Any]¶
Scale factor a(T_γ), available after initialisation.
afollows the same normalisation as the internal a(T) ODE (primat.background.StandardBackground._setup_background_and_cosmo()): entropy conservationa^3 * spl(T) = constfixed so thata * T -> T0CMB[MeV] asT -> 0, i.e.a = 1today up to the small entropy-injection correction from e+e- annihilation encoded inspl(T).Example
>>> p.a_of_T(1.0) # scale factor at T_gamma = 1 MeV
- property T_of_a: Callable[[Any], Any]¶
T_γ(a) interpolator [MeV], available after initialisation.
Inverse of
a_of_T; same normalisation convention fora.
- property a_of_t: Callable[[Any], Any]¶
Scale factor a(t), available after initialisation.
Same normalisation as
a_of_T;tis the cosmic time [s] used byT_of_t/t_of_T.
- property t_of_a: Callable[[Any], Any]¶
Cosmic time t(a) [s], available after initialisation.
Inverse of
a_of_t; same normalisation convention fora.
- property abundance_names: list[str]¶
Tracked nuclide names, in abundance-vector order (solves if needed).
For the large network this is the full ~59-nuclide list; accessing it also guarantees
self.A/N/Zcover every species (handy for plottingA_i Y_ifor all nuclides).
- property evolution: EvolutionResult | None¶
The unified time-evolution result (
primat.evolution.EvolutionResult,Noneunlesscfg.output_time_evolution=True), solving first if needed. Thin alias forself.nuclear.evolutionso callers that don’t care whether they hold a livePRIMATor a backend-agnosticprimat.gui.run_view.GuiRun(which has no.nuclearat all) can readrun.evolutionuniformly either way.
- primat.main.mc_uncertainty(num_mc, quantity, params=None, n_jobs=-1, seed=0, prev=None, custom_network=None, progress=None)[source]¶
Estimate nuclear-rate and neutron-lifetime uncertainties on BBN observables via Monte Carlo.
Each MC sample draws all active nuclear rate offsets p_* independently from N(0,1), plus the neutron lifetime
tau_n ~ N(cfg.tau_n, cfg.std_tau_n)(used whencfg.tau_n_flag=True, the default), and runs a full primat solve. By default all reactions in the selected network are varied.- Parameters:
num_mc (int) – Number of MC samples.
quantity (str or list of str) – A key from the result dict (‘YPBBN’, ‘DoH’, ‘He3oH’, ‘Li7oH’, ‘Neff’, ‘YPCMB’, …) or a nuclide name (‘H2’, ‘He4’, ‘Li7’, …) for the final mass fraction Y. Pass a list to evaluate multiple quantities in one MC pass (more efficient than separate calls). This only controls which quantities are guaranteed present and validated strictly (an unknown name raises); the returned
MCResultalways additionally contains every tracked nuclide’s final Y and every standard observable in_DEFAULT_MC_OBSERVABLESthat this network/custom_network actually produces (Neff,YPBBN,YPCMB,DoH,He3oH,He3oHe4,Li7oH,Li6oLi7,YCNO), at no extra solving cost (each MC sample already runs a full solve). This keeps a TSV dump (primat.backend.dump_mc_samples) complete even when the caller only asked for one or two quantities for display purposes.params (dict, optional) – Base parameters for PRIMAT (e.g. Omegabh2, is_small, network).
n_jobs (int) – Number of parallel workers passed to joblib.Parallel (-1 = all CPUs).
n_jobs=1runs the samples serially in-process and does not import joblib at all, so a lean core install (pip install primat, nomc/recommendedextra) can still do Monte-Carlo this way; any other value needs joblib and raises an actionable ImportError if it is missing.seed (int) – Base random seed; sample i uses seed + i for reproducibility. When evaluating on a parameter grid (e.g. scanning Ω_b h²), use the same seed at every grid point so that sample i draws the same rate vector p_* everywhere. This correlates the MC noise across the grid, making any finite-sample bias cancel when comparing predictions at different parameter values.
prev (MCResult, optional) – A previously computed result to extend rather than recompute from scratch. Because sample
iis fully determined byseed + i, the firstmin(len(prev), num_mc)samples are identical toprevas long as the seed, the set/order of quantities,paramsandcustom_networkall match, so only the missing samples (seed + n_prev .. seed + num_mc - 1) are actually solved. This makes it cheap to refine an estimate – e.g. going from 30 to 50 samples only runs the 20 new ones.previs silently ignored (full recompute) if itsseed, quantities,params,custom_networkorbackend(this function only reuses aprevwhosebackendis"python"or unset – a C-backend result has incompatible RNG samples, seeprimat.backend.run_mc) differ from this call; ifnum_mcis smaller thanlen(prev), the result is justprevtruncated tonum_mcsamples (nothing is solved).custom_network (dict, optional) – “Customise Reactions” override, forwarded to every
PRIMATinstance built here (seePRIMAT’s docstring for the{"removed": [...], "replaced": {...}}schema). Reactions listed under"removed"are also excluded from the set of varied rate offsets (rate_keys) below, since they no longer exist in the network; reactions listed under"replaced"stay inrate_keysand are varied using the replacement table’s own error column (UpdateNuclearRatesbuildsexpsigmafrom it), so a custom rate’s uncertainty is honoured automatically.None(default) uses the standard, uncustomised network.progress (bool, optional) – When truthy, print a running
N/total (XX%)counter to stderr as samples complete, so the user can track advancement during long MC runs. One update per parallel worker chunk – forn_jobs=-1(all CPUs) this gives roughlycpu_countupdates over the full run.None(default) defers toparams['show_progress'](DEFAULT_PARAMSdefaultTrue); pass an explicitTrue/Falseto override it for this call.
- Returns:
MCResult – Dict-like object indexed by quantity name. Each value is an
MCQuantityResultwith attributescentral,mean,std, andvalues.- Return type:
MCResult
Example
>>> mc = mc_uncertainty(100, ['YPBBN', 'DoH'], params={'Omegabh2': 0.022}) >>> mc['YPBBN'].central >>> mc['YPBBN'].std >>> mc['DoH'].values # full sample array