Class Galaxy

Nested Relationships

Nested Types

Class Documentation

class Galaxy

A class to represent a galaxy.

Unlike Cluster, which represents a single mono-age population, a Galaxy represents an entire, continuously star-forming system: advance() draws new clusters from sfr()/SimControls::cmf() as time passes, advances every cluster formed so far (whether still bound or already disrupted), and sums their individual spectra, photometry, and bolometric luminosities into this Galaxy’s own spec()/specExtinct()/phot()/photExtinct()/lbol().

Public Types

using SFRVar = std::variant<pdfs::PDF, std::reference_wrapper<const pdfs::PDF>>

Holds this galaxy’s own resolved star formation rate.

Either a reference to SimControls::sfr() itself (when galaxy.sfr was given, so every Galaxy built from the same SimControls shares the identical rate) or a PDF this Galaxy owns outright (when galaxy.sfr_dist was given instead, so each Galaxy draws its own, independent constant rate – see the constructor’s own comment). Mirrors Cluster::Track2DVar’s identical shared-vs-owned pattern exactly – see its own comment for the rationale.

Public Functions

explicit Galaxy(const io::SimControls &controls)

Initialize a galaxy.

curTime_, lbol_, targetMass_, and actualMass_ all start at 0, and clusters_/disruptedClusters_/fieldStars_/deadFieldStars_/ spec_/specExtinct_/specNeb_/specNebExtinct_/lineLum_/lineLumExtinct_/phot_/ photExtinct_/photNeb_/photNebExtinct_ all start empty – no clusters or field stars exist, and no spectrum/photometry has been computed, until the first call to advance().

Also resolves sfr_ here, once, for the rest of this Galaxy’s own lifetime: if controls.sfr() is valid (galaxy.sfr was given), sfr_ becomes a reference to that same PDF, read live exactly as every other SimControls-derived quantity is elsewhere in this class. Otherwise, if controls.sfrDist() is valid instead (galaxy.sfr_dist was given – SimControls’s own constructor guarantees exactly one of the two for a galaxy-type simulation), sfr_ instead becomes a PDF this Galaxy owns outright, built by SimControls::buildConstantSFR() from a single value drawn from controls.sfrDist() – a constant rate, but an independent one per Galaxy, so multiple Galaxy instances built from the same SimControls (e.g. separate trials) each get their own draw. If neither is valid (a Galaxy built from a non-galaxy-type SimControls, e.g. the pybind default, sim_type = “cluster”, which never sets either), sfr_ is simply left invalid, exactly as sc.sfr() itself would have been – any code that actually needs a valid sfr() fails the same way it always did.

Parameters:

controls – Simulation controls (physics settings and control-flow/integrator-tolerance settings together); stored by reference, so it must outlive this Galaxy – see controls_’s own comment

inline auto sfr() const -> const pdfs::PDF&

Return this galaxy’s own resolved star formation rate.

Returns:

A const reference to the PDF sfr_ holds – see its own comment for how this galaxy resolved it, once, at construction

inline auto curTime() const

Return the galaxy’s current time.

Returns:

Current simulation time, in yr

inline auto clusters() -> auto&

Return the galaxy’s currently-alive (non-disrupted) clusters.

Not const: callers commonly need to call a lazily-computed getter (spec()/phot()/lbol()/etc. – see spec()’s own comment) on the individual Cluster elements this returns, which are themselves non-const for the same reason.

Returns:

A reference to the list of clusters formed so far that have not yet disrupted

inline auto disruptedClusters() -> auto&

Return the galaxy’s disrupted clusters.

Not const – see clusters()’s own comment.

Returns:

A reference to the list of clusters formed so far that have already disrupted

inline auto fieldStars() const -> const auto&

Return the galaxy’s currently-alive field stars.

Const, unlike clusters()/disruptedClusters(): a FieldStar is a plain data struct with no lazily-computed getter of its own to trigger.

Returns:

A const reference to the list of individually-tracked (field) stars formed so far – see FieldStar’s own comment – that have not yet died, sorted by formTime_ (see advance()’s own comment for why that ordering is guaranteed)

inline auto deadFieldStars() const -> const auto&

Return the galaxy’s dead field stars.

Returns:

A const reference to the list of individually-tracked (field) stars that have died as of curTime() – see advance()’s own comment for how a field star moves from fieldStars() to here

inline auto spec() -> const auto&

Return the galaxy’s continuously-sampled spectrum.

Computed lazily: if advance() has run since spec_/specExtinct_ were last computed, this triggers computeSpec() (which computes spec_/specExtinct_/specNeb_/specNebExtinct_/lineLum_/lineLumExtinct_ together) before returning, so the result is always current as of the last advance() – see specCurrent_’s own comment. Not const, since it may need to run that computation.

Returns:

A const reference to the sum of spec() over every cluster in clusters() and disruptedClusters(), on the wavelength grid of the simulation’s spectral synthesizer, or an empty vector if no spectral synthesizer was requested (SimControls::specsyn() is null)

inline auto specExtinct() -> const auto&

Return the galaxy’s extincted spectrum.

Computed lazily – see spec()’s own comment; all share the same specCurrent_ flag, since a single computeSpec() call computes them together.

Returns:

A const reference to the sum of specExtinct() over every cluster in clusters() and disruptedClusters(), on the wavelength grid returned by SimControls::extinct()’s own wl(); an empty vector if no extinction curve was requested (SimControls::extinct() is null) or spec() itself is empty (no spectral synthesizer was requested)

inline auto phot() -> const auto&

Return the galaxy’s photometry.

Computed lazily – see spec()’s own comment; computePhot() itself calls spec()/specExtinct() (rather than reading spec_/specExtinct_ directly), so calling phot() alone, with no prior call to spec(), still computes a spectrum current as of the last advance() first – which in turn computes each individual cluster’s own spectrum, but not its photometry (see Cluster::computePhot()’s own comment).

Returns:

A const reference to the photometric value computed from spec() by each filter in SimControls::filters(), in the same order as FilterCollection::filterNames()/ filterUnits(), or an empty vector if no filter collection was requested (SimControls::filters() is null)

inline auto photExtinct() -> const auto&

Return the galaxy’s extincted photometry.

Computed lazily – see phot()’s own comment; all share the same photCurrent_ flag, since a single computePhot() call computes them together. A filter whose passband extends past the edge of the extinction curve’s wavelength coverage is NaN – see FilterCollection::phot()’s three-argument overload.

Returns:

A const reference to the photometric value computed from specExtinct() by each filter in SimControls::filters(), in the same order as phot(); an empty vector if no extinction curve was requested (SimControls::extinct() is null) or no filter collection was requested (SimControls::filters() is null)

inline auto specNeb() -> const auto&

Return the galaxy’s stellar + nebular spectrum.

Computed lazily – see spec()’s own comment; shares the same specCurrent_ flag, since a single computeSpec() call computes spec_/specExtinct_/specNeb_/specNebExtinct_/lineLum_/lineLumExtinct_ together.

Returns:

A const reference to the sum of specNeb() over every cluster in clusters() and disruptedClusters(), plus the continuous population’s own nebular-reprocessed contribution (see addContinuousSpec()’s own comment), on the wavelength grid of the simulation’s spectral synthesizer; an empty vector if no nebular emission grid was requested (SimControls::nebular() is null) or spec() itself is empty (no spectral synthesizer was requested)

inline auto specNebExtinct() -> const auto&

Return the galaxy’s extincted stellar + nebular spectrum.

Computed lazily – see specNeb()’s own comment.

Returns:

A const reference to the sum of specNebExtinct() over every cluster in clusters() and disruptedClusters(), plus the continuous population’s own extincted, nebular- reprocessed contribution, on the wavelength grid returned by SimControls::extinct()’s own wl(); an empty vector if no extinction curve was requested (SimControls::extinct() is null), no nebular emission grid was requested (SimControls::nebular() is null), or spec() itself is empty

inline auto lineLum() -> const auto&

Return the galaxy’s nebular emission line luminosities.

Computed lazily – see specNeb()’s own comment.

Returns:

A const reference to the sum of lineLum() over every cluster in clusters() and disruptedClusters(), plus the continuous population’s own contribution, in erg/s, in the same order as SimControls::nebular()’s own lineWl(); an empty vector if no nebular emission grid was requested (SimControls::nebular() is null) or spec() itself is empty

inline auto lineLumExtinct() -> const auto&

Return the galaxy’s extincted nebular emission line luminosities.

Computed lazily – see lineLum()’s own comment.

Returns:

A const reference to lineLum(), attenuated by extinction; an empty vector if no extinction curve was requested (SimControls::extinct() is null) or lineLum() itself is empty (no nebular emission grid or no spectral synthesizer was requested)

inline auto photNeb() -> const auto&

Return the galaxy’s stellar + nebular photometry.

Computed lazily – see phot()’s own comment; shares the same photCurrent_ flag, since a single computePhot() call computes phot_/photExtinct_/photNeb_/photNebExtinct_ together.

Returns:

A const reference to the photometric value computed from specNeb() by each filter in SimControls::filters(), in the same order as phot(); an empty vector if no nebular emission grid was requested (SimControls::nebular() is null) or no filter collection was requested (SimControls::filters() is null)

inline auto photNebExtinct() -> const auto&

Return the galaxy’s extincted stellar + nebular photometry.

Computed lazily – see photNeb()’s own comment. A filter whose passband extends past the edge of the extinction curve’s wavelength coverage is NaN – see FilterCollection::phot()’s three-argument overload.

Returns:

A const reference to the photometric value computed from specNebExtinct() by each filter in SimControls::filters(), in the same order as phot(); an empty vector if no extinction curve was requested (SimControls::extinct() is null), no nebular emission grid was requested (SimControls::nebular() is null), or no filter collection was requested (SimControls::filters() is null)

inline auto lbol()

Return the galaxy’s bolometric luminosity.

Computed lazily – see spec()’s own comment. computeLbol() itself is a no-op (leaving lbol_ at 0) unless SimControls::computeLbol() is true, mirroring computeSpec()/ computePhot()’s own null-guards.

Returns:

The sum of lbol() over every cluster in clusters() and disruptedClusters(), in Lsun, at the current time, or 0 if SimControls::computeLbol() is false (Lbol was never requested)

inline auto yields() const -> const auto&

Return this galaxy’s total nucleosynthetic yield of each isotope.

Unlike spec()/phot()/lbol(), not computed lazily here – mirrors Cluster::yields() exactly (see its own comment): advance() itself calls computeYields() eagerly at the end of every call, so yields_ is already current by the time this is called, and stays at the all-zero vector it was sized to at construction (see its own comment) until advance() has run at least once.

Returns:

A const reference to yields_ – see Cluster::yields()’s own comment for the general shape/ layout this mirrors

inline auto cumSNe() const -> double

Return the cumulative number of supernovae in this galaxy up to the current time.

Like Cluster::cumSNe() (see its own comment), a double rather than an integer type, since the non-stochastic contributions are in general non-integer, and not computed lazily here: advance() itself updates every cluster’s own count, and calls computeFeedback() to update fieldStarStochSNe_ and fieldStarNonStochSNe_, eagerly at every call.

Returns:

Number of supernovae that have occurred in this galaxy from its formation up to the current time: the sum of Cluster::cumSNe() over every cluster in clusters() and disruptedClusters(), plus fieldStarStochSNe_ (from the individually-sampled field stars) and fieldStarNonStochSNe_ (from the continuously-sampled field population)

inline auto mDotWind() -> double

Return the galaxy’s total stellar wind mass flux.

Computed lazily, like lbol() and Cluster::mDotWind(): advance() only marks the wind fluxes stale (windsCurrent_), and the first of mDotWind()/pDotWind()/eDotWind() called afterward recomputes all three via computeWinds() – see its own comment for how.

Returns:

The summed mass loss rate, in Msun/yr, at the current time, of every cluster in clusters() and disruptedClusters() (via Cluster::mDotWind()), every currently-alive field star, and the purely continuous (non-clustered, non-stochastic) population; 0 until advance() has run

inline auto pDotWind() -> double

Return the galaxy’s total stellar wind momentum flux.

See mDotWind()’s own comment.

Returns:

The summed wind momentum flux, in g cm s^-2, at the current time, over the same stars as mDotWind()

inline auto eDotWind() -> double

Return the galaxy’s total stellar wind energy flux.

See mDotWind()’s own comment.

Returns:

The summed wind kinetic energy flux, in erg s^-1, at the current time, over the same stars as mDotWind()

auto yieldsRate(double t, double feh) const -> std::vector<double>

The instantaneous rate at which the continuous population returns each isotope, at a given time and [Fe/H].

deathRate() with each dying star’s own yields as its quantity, radioactive decay forward to curTime_ as its hook, and a fixed [Fe/H] – see continuousDeathQuantity()’s own comment. The decay uses dtDecay = curTime_ - t, so assumes t <= curTime_: on a Galaxy not yet advance()d past t it would run decay backward in time, which Yields::applyDecay() does not reject but which is not physically meaningful.

Parameters:
  • t – Simulation time, in yr, since this galaxy’s own formation (time 0) – not necessarily curTime(); this is a standalone calculation, independent of advance()/curTime()

  • feh – [Fe/H] to evaluate at

Returns:

The total rate (Msun/yr) at which the continuously sampled population – over this galaxy’s own star formation history sfr() from 0 to t, and all at [Fe/H] feh – returns each isotope to the ISM at t, laid out as yields() (one channel-major row per isotope if controls().yieldsChannelDecomposed(), or isotopes().size() combined entries otherwise), decayed forward to curTime_ unless controls().noDecay(). Excludes the stochastically sampled (cluster and field star) population, and the 1 - fCluster() share applied by computeYields(). An empty vector if controls().yields() is null.

auto yieldsRate(double t) const -> std::vector<double>

The instantaneous rate at which the continuous population returns each isotope, at a given time, averaged over [Fe/H].

deathRate() as in the (t, feh) overload, but with its [Fe/H] integral innermost (fehAveragedDeathIntegrand()), so that decay is applied once, to the [Fe/H]-averaged rate – see continuousDeathQuantity()’s own comment. For a degenerate fehDist(), identical to the (t, feh) overload at its value.

Parameters:

t – Simulation time, in yr – see the (t, feh) overload’s own comment

Returns:

As the (t, feh) overload, but averaged over SimControls::fehDist() rather than at a single [Fe/H]

template<class F, class G = NoDeathRateHook>
inline auto continuousDeathQuantity(const F &f, const std::size_t n, const double tLast, const double tNow, const G &g = G{}) const -> std::vector<double>

The cumulative amount of a quantity released at stellar death by the continuous population between two times.

Integrates, from the outermost integral in:

  • over simulation time t in (tLast, tNow], of the rate at which the quantity is released at t (deathRate()), after g(t, rate) – weighted by a flat distribution over the step, so that utils::integrateScaled() applies unchanged – then scales by the step’s own length and by 1 - SimControls::fCluster(), the continuous population’s own share of the star formation rate;

  • over stellar age, weighted by the star formation rate at t minus that age (deathRate());

  • over [Fe/H], weighted by SimControls::fehDist(), unless that is degenerate (fehAveragedDeathIntegrand());

  • innermost, the rate at which stars of that age and [Fe/H] die, each releasing f(m, feh) (deathIntegrand()).

Integrating time outermost and [Fe/H] innermost means g is applied once per time point, after the [Fe/H] integral (the time and [Fe/H] integrals commute), rather than at every [Fe/H] point – the expensive step for radioactive decay. Every integral makes its integrand dimensionless first – see utils::integrateScaled()’s own comment.

Template Parameters:
  • F – Callable as f(m, feh), m a stellar mass in Msun and feh its [Fe/H], returning a std::vector<double> of n values: the amount of each quantity one star of that mass and [Fe/H] releases when it dies (e.g. its yields, or 1 or 0 for whether it produces a supernova)

  • G – Callable as g(t, rate), t a simulation time in yr and rate a std::vector<double>& holding the instantaneous rate of release at t, modifying rate in place before it is integrated over time – e.g. radioactive decay forward to tNow; NoDeathRateHook (the default) for none

Parameters:
  • f – The quantity released per star

  • n – The number of values f returns

  • tLast – Start of the time interval, in yr since this galaxy’s own formation

  • tNow – End of the time interval, in yr

  • g – The per-time hook

Returns:

The total of each quantity released by the continuous population’s own stars that died during (tLast, tNow], for this galaxy’s own star formation history; all zero if tNow <= tLast, or if there is no continuous population (SimControls::minStochMass() == 0 or fCluster() == 1)

inline auto targetMass() const

Return the total target stellar mass formed so far.

Returns:

The sum, over every advance() call so far, of the total stellar mass (sfr().integral() over that call’s own (curTime(), t] step) that should have formed – both the stochastically-treated (clustered) and continuous (non-clustered) parts together – in Msun – may differ from actualMass() due to stochastic sampling from SimControls::cmf() (see Galaxy::advance())

inline auto actualMass() const

Return the total actual stellar mass formed so far.

Returns:

The sum, over every advance() call so far, of (1) the target mass (Cluster::targetMass()) of every cluster actually drawn from SimControls::cmf() during that call, (2) that same call’s own individually-drawn field-star mass (see FieldStar’s own comment), and (3) that same call’s own purely continuous (non-clustered, below SimControls::minStochMass()) population mass, (1 - SimControls::fCluster()) * (1 - SimControls:: fracStochMass()) times that call’s own total target stellar mass, in Msun

void advance(double t)

Advance the galaxy in time.

Draws and forms new clusters over (curTime(), t] from sfr()/SimControls::cmf(), scaled down to the stochastically-treated fraction SimControls::fCluster() of the step’s own total target mass. Of the remaining (1 - fCluster()), the share at or above SimControls::minStochMass() – SimControls::fracStochMass() of it – is drawn individually too, from SimControls::imf() over [minStochMass(), imf().getMax()], as new FieldStar entries (mass_, feh_ from fehDist(), formTime_, deathTime_, and aV_ from SimControls::avDistField() – an independent draw per star, unlike a bound cluster’s own single, shared aV_) appended to fieldStars() (see FieldStar’s own comment); the rest is folded directly into actualMass(), as the purely continuous, non-clustered population’s own share, with no individual Cluster or FieldStar of its own. Accumulates the step’s own target and actual mass into targetMass()/actualMass(), advances every cluster formed so far (in clusters() and disruptedClusters()) to t, moves any cluster that disrupted during this step from clusters() to disruptedClusters(), moves any field star that has died as of t from fieldStars() to deadFieldStars(), updates curTime() to t and caches every surviving field star’s own properties in fieldStarProps_ (see its own comment), then marks spec_/specExtinct_/specNeb_/specNebExtinct_/lineLum_/lineLumExtinct_/phot_/ photExtinct_/photNeb_/photNebExtinct_/lbol_/lbolCts_ as stale (see specCurrent_/photCurrent_/lbolCurrent_/ lbolCtsCurrent_’s own comments) rather than recomputing them itself – they are instead recomputed lazily, on demand, the next time spec()/specExtinct()/specNeb()/specNebExtinct()/ lineLum()/lineLumExtinct()/phot()/photExtinct()/photNeb()/ photNebExtinct()/lbol() is actually called. Finally updates the yields and feedback quantities eagerly, via computeYields() and computeFeedback().

Parameters:

t – Time to which to advance, in yr; must be >= curTime()

struct FieldStar

A single non-clustered, individually-tracked (field) star.

Represents one star drawn from the stochastically-treated share of the non-clustered population – mass at or above SimControls::minStochMass(), but never folded into any individual Cluster object, since it formed outside SimControls::fCluster()’s own clustered fraction – see Galaxy::advance()’s own comment for how these are drawn, alongside the purely continuous (below minStochMass) share Specsyn::specCts()/specAndLbolCts() integrate directly, with no individual FieldStar of its own.

Public Members

double mass_

Initial (birth) mass, in Msun

double feh_

[Fe/H]

double formTime_

Formation time, in yr

double deathTime_

Time this star dies, in yr (formTime_ + SimControls::tracks()’s own starLifetime(mass_, feh_))

double aV_

V-band extinction, in magnitudes, drawn from SimControls::avDistField() at formation (see Galaxy::advance()’s own comment) – unlike a bound cluster (Cluster::aV_, shared by every star in it), each field star draws its own, independent value, since field stars are not physically clustered together; not currently read for spec()/specExtinct(), which instead attenuate every field star by the same expected extinction as the purely continuous population (see addContinuousSpec()’s own comment)

struct NoDeathRateHook

A per-time hook for continuousDeathQuantity() that leaves each instantaneous rate unchanged.

The default for continuousDeathQuantity()’s own G – for a quantity (e.g. a supernova count) that needs no processing between its release and the end of the integration, unlike radioactive yields, whose hook applies decay.

Public Functions

inline void operator()(double, std::vector<double>&) const

Leave rate unchanged.

Parameters:
  • t – Simulation time, in yr, at which rate applies

  • rate – The instantaneous rate at t