From baf54bcbf3527054dcd7f91844957d2a27697480 Mon Sep 17 00:00:00 2001 From: "Documenter.jl" Date: Mon, 7 Oct 2024 06:58:55 +0000 Subject: [PATCH] build based on 4fde202 --- dev/.documenter-siteinfo.json | 2 +- dev/binaries/index.html | 4 +- dev/doc_index/index.html | 2 +- dev/examples/index.html | 2 +- dev/fitting/figures/covar_0.svg | 172 +-- dev/fitting/figures/covar_1.svg | 136 +- dev/fitting/figures/covar_m1.svg | 703 +++++----- dev/fitting/figures/sigma_distribution.svg | 557 ++++---- dev/fitting/figures/template_compare.svg | 1414 ++++++++++---------- dev/fitting/fitting_intro/index.html | 6 +- dev/fitting/fixed_amr/index.html | 2 +- dev/fitting/internals/index.html | 14 +- dev/fitting/kernels/index.html | 2 +- dev/fitting/linear_amr/index.html | 52 +- dev/fitting/log_amr/index.html | 12 +- dev/fitting/log_amr_plot.svg | 466 +++---- dev/fitting/unconstrained/index.html | 22 +- dev/helpers/index.html | 18 +- dev/index.html | 2 +- dev/search_index.js | 2 +- dev/sigma_distribution_binaries.svg | 668 ++++----- dev/simulate/index.html | 6 +- dev/template_compare_binaries.svg | 226 ++-- 23 files changed, 2242 insertions(+), 2248 deletions(-) diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index 85efcb5..4bb4de8 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.10.5","generation_timestamp":"2024-09-28T21:35:11","documenter_version":"1.7.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.10.5","generation_timestamp":"2024-10-07T06:58:51","documenter_version":"1.7.0"}} \ No newline at end of file diff --git a/dev/binaries/index.html b/dev/binaries/index.html index 6635e2c..eb99ca0 100644 --- a/dev/binaries/index.html +++ b/dev/binaries/index.html @@ -1,4 +1,4 @@ -Binary Systems · StarFormationHistories.jl

Binary Systems

Here we review the API for including binary systems in our population models. Our Monte Carlo sampling methods supports all three models, while our smooth template modelling procedure only supports NoBinaries and RandomBinaryPairs. A comparison between a Monte Carlo population and a smooth template model for a RandomBinaryPairs model with binary fraction of 70% is shown below. The redward shift of the lower main sequence typical of populations with high binary fractions is clearly evident and robustly modelled.

Comparison of CMD-sampled population with smooth Hess diagram template, with binaries.

Types

StarFormationHistories.AbstractBinaryModelType

StarFormationHistories.AbstractBinaryModel is the abstract supertype for all types that are used to model multi-star systems in the package. All concrete subtypes should implement the following methods to support all features:

Note that all quantities relating to binary populations (e.g., binary_system_fraction) should be defined for the population at birth. As the stars in a binary system evolve, the more massive star may die before the system is observed at present-day. Of course, the stars in single-star systems can also die. If the rate at which binary systems become single-star systems is not equal to the rate at which single-star systems die, then there can be net transfer between these populations over time. Therefore the observed, present-day binary system fraction of an evolved population is not necessarily equal to the fraction at birth, which is the more fundamental quantity.

source
StarFormationHistories.RandomBinaryPairsType
RandomBinaryPairs(fraction::Real)

The RandomBinaryPairs type takes one argument 0 <= fraction::Real <= 1 that denotes the number fraction of stellar systems that are binaries (e.g., 0.3 for 30% binary fraction) and will sample binaries as random pairs of two stars drawn from the same single-star IMF. This model will ONLY generate up to one additional star – it will not generate any 3+ star systems. This model typically incurs a 10–20% speed penalty relative to NoBinaries.

source
StarFormationHistories.BinaryMassRatioType
BinaryMassRatio(fraction::Real,
+Binary Systems · StarFormationHistories.jl

Binary Systems

Here we review the API for including binary systems in our population models. Our Monte Carlo sampling methods supports all three models, while our smooth template modelling procedure only supports NoBinaries and RandomBinaryPairs. A comparison between a Monte Carlo population and a smooth template model for a RandomBinaryPairs model with binary fraction of 70% is shown below. The redward shift of the lower main sequence typical of populations with high binary fractions is clearly evident and robustly modelled.

Comparison of CMD-sampled population with smooth Hess diagram template, with binaries.

Types

StarFormationHistories.AbstractBinaryModelType

StarFormationHistories.AbstractBinaryModel is the abstract supertype for all types that are used to model multi-star systems in the package. All concrete subtypes should implement the following methods to support all features:

Note that all quantities relating to binary populations (e.g., binary_system_fraction) should be defined for the population at birth. As the stars in a binary system evolve, the more massive star may die before the system is observed at present-day. Of course, the stars in single-star systems can also die. If the rate at which binary systems become single-star systems is not equal to the rate at which single-star systems die, then there can be net transfer between these populations over time. Therefore the observed, present-day binary system fraction of an evolved population is not necessarily equal to the fraction at birth, which is the more fundamental quantity.

source
StarFormationHistories.RandomBinaryPairsType
RandomBinaryPairs(fraction::Real)

The RandomBinaryPairs type takes one argument 0 <= fraction::Real <= 1 that denotes the number fraction of stellar systems that are binaries (e.g., 0.3 for 30% binary fraction) and will sample binaries as random pairs of two stars drawn from the same single-star IMF. This model will ONLY generate up to one additional star – it will not generate any 3+ star systems. This model typically incurs a 10–20% speed penalty relative to NoBinaries.

source
StarFormationHistories.BinaryMassRatioType
BinaryMassRatio(fraction::Real,
                 qdist::Distributions.ContinuousUnivariateDistribution =
-                    Distributions.Uniform(0.1, 1.0))

The BinaryMassRatio type takes two arguments; the number fraction of stellar systems that are binaries 0 <= fraction::Real <= 1 and a continuous univariate distribution qdist from which to sample binary mass ratios, defined as the ratio of the secondary mass to the primary mass: $q = \text{M}_S / \text{M}_P$. The provided qdist must have the proper support of (minimum(qdist) >= 0) & (maximum(qdist) <= 1). Users may find the Distributions.truncated method useful for enforcing this support on more general distributions. The default qdist is a uniform distribution from 0.1 to 1, which appears to give reasonably good agreement to observations (see, e.g., Goodwin 2013).

source

Methods

StarFormationHistories.binary_system_fractionFunction
binary_system_fraction(model::T) where T <: AbstractBinaryModel

Returns the number fraction of stellar systems that are binaries for the given concrete subtype T <: AbstractBinaryModel. Has a default implementation of binary_system_fraction(model::AbstractBinaryModel) = model.fraction.

source
StarFormationHistories.binary_mass_fractionFunction
binary_mass_fraction(model::T, imf) where T <: AbstractBinaryModel

Returns the fraction of stellar mass in binary systems for the given concrete subtype T <: AbstractBinaryModel and initial mass function imf. imf must be a properly normalized probability distribution such that the number fraction of stars/systems between mass m1 and m2 is given by the integral of dispatch_imf(imf, x) from m1 to m2.

source
binary_mass_fraction(m::RandomBinaryPairs, imf)

The RandomBinaryPairs model uses a single-star imf. If a system is chosen to be a binary pair, two stars are drawn from the single-star imf and the more massive star is made the primary. Given this model, it can be shown that the expectation value for the mass of a binary system is twice the expectation value for single star systems:

\[2\int_{\text{M}_\text{min}}^{\text{M}_\text{max}} \text{M} \frac{d\text{N} \left( \text{M} \right)}{d\text{M}} d\text{M} = \int_{\text{M}_\text{min}}^{\text{M}_\text{max}} \int_{\text{M}_\text{min}}^{\text{M}_\text{max}} \left( \text{M}_P + \text{M}_S \right) \frac{d\text{N} \left( \text{M}_S \right)}{d\text{M}} \frac{d\text{N} \left( \text{M}_P \right)}{d\text{M}} d\text{M}_S \, d\text{M}_P\]

for primary mass $\text{M}_P$, secondary mass $\text{M}_S$, and single-star IMF $d\text{N} / d\text{M}$. As such, the fraction of total stellar mass in binaries is equal to the number fraction of all stars in binary pairs, which is given by StarFormationHistories.binary_number_fraction.

source
binary_mass_fraction(m::BinaryMassRatio, imf)

This binary model requires an imf that is defined by stellar system mass. If a system with a randomly sampled mass $M$ is is a binary, the primary and secondary mass are determined based on a binary mass ratio $q$ sampled from a user-defined distribution. By definition, the expectation value for the total mass of a binary system is equal to the expectation value for single-star systems. In this case the binary mass fraction is equal the binary system number fraction as given by StarFormationHistories.binary_system_fraction.

source
StarFormationHistories.sample_systemFunction
masses = sample_system(imf, rng::AbstractRNG, binarymodel::StarFormationHistories.AbstractBinaryModel)

Simulates the effects of non-interacting, unresolved stellar companions on stellar photometry. Implementation depends on the choice of binarymodel.

Arguments

  • imf: an object implementing rand(imf) to draw a random mass for a single star or a stellar system (depends on choice of binarymodel)
  • rng::AbstractRNG: the random number generator to use when sampling stars
  • binarymodel::StarFormationHistories.AbstractBinaryModel: an instance of a binary model that determines which implementation will be used; currently provided options are NoBinaries, RandomBinaryPairs, and BinaryMassRatio.

Returns

  • masses::SVector{N,eltype(imf)}: the masses of the individual stars sampled in the system in descending order where N is the maximum number of stars that can be sampled by the provided binarymodel as given by Base.length(binarymodel).
source

Notes

The trickiest part of including binaries in the smooth template modelling procedure is deriving the IMF weights. Let $M_p$ be the sorted list of initial masses for primary stars and $M_s$ be the sorted list of initial masses for secondary stars. Conceptually, the IMF weight for a binary system with primary mass $M_{p,i}$ and secondary mass $M_{s,j}$ should compute the number fraction of binary systems born with primary masses between $M_{p,i}$ and $M_{p,i+1}$ and secondary masses between $M_{s,j}$ and $M_{s,j+1}$ per unit solar mass formed.

In the case of the RandomBinaryPairs model, the IMF weights are calculated as follows, with $dN(M)/dM$ being the IMF for single stars, $\langle M \rangle$ being the mean mass of single stars over the full range of possible initial masses, and the integral in the denominator being over the range of initial masses in the isochrone. The integral in the denominator accounts for losses due to stellar evolution.

\[ w_{\text{IMF},i,j} = \frac{\int_{M_{p,i}}^{M_{p,i+1}} \int_{M_{s,j}}^{M_{s,j+1}} \frac{dN(M_p)}{dM} \frac{dN(M_s)}{dM} \ dM_p \ dM_s}{\langle M \rangle \ \int_{M_{\text{min}}}^{M_{\text{max}}} \frac{dN(M)}{dM} \ dM}\]

+ Distributions.Uniform(0.1, 1.0))

The BinaryMassRatio type takes two arguments; the number fraction of stellar systems that are binaries 0 <= fraction::Real <= 1 and a continuous univariate distribution qdist from which to sample binary mass ratios, defined as the ratio of the secondary mass to the primary mass: $q = \text{M}_S / \text{M}_P$. The provided qdist must have the proper support of (minimum(qdist) >= 0) & (maximum(qdist) <= 1). Users may find the Distributions.truncated method useful for enforcing this support on more general distributions. The default qdist is a uniform distribution from 0.1 to 1, which appears to give reasonably good agreement to observations (see, e.g., Goodwin 2013).

source

Methods

StarFormationHistories.binary_system_fractionFunction
binary_system_fraction(model::T) where T <: AbstractBinaryModel

Returns the number fraction of stellar systems that are binaries for the given concrete subtype T <: AbstractBinaryModel. Has a default implementation of binary_system_fraction(model::AbstractBinaryModel) = model.fraction.

source
StarFormationHistories.binary_mass_fractionFunction
binary_mass_fraction(model::T, imf) where T <: AbstractBinaryModel

Returns the fraction of stellar mass in binary systems for the given concrete subtype T <: AbstractBinaryModel and initial mass function imf. imf must be a properly normalized probability distribution such that the number fraction of stars/systems between mass m1 and m2 is given by the integral of dispatch_imf(imf, x) from m1 to m2.

source
binary_mass_fraction(m::RandomBinaryPairs, imf)

The RandomBinaryPairs model uses a single-star imf. If a system is chosen to be a binary pair, two stars are drawn from the single-star imf and the more massive star is made the primary. Given this model, it can be shown that the expectation value for the mass of a binary system is twice the expectation value for single star systems:

\[2\int_{\text{M}_\text{min}}^{\text{M}_\text{max}} \text{M} \frac{d\text{N} \left( \text{M} \right)}{d\text{M}} d\text{M} = \int_{\text{M}_\text{min}}^{\text{M}_\text{max}} \int_{\text{M}_\text{min}}^{\text{M}_\text{max}} \left( \text{M}_P + \text{M}_S \right) \frac{d\text{N} \left( \text{M}_S \right)}{d\text{M}} \frac{d\text{N} \left( \text{M}_P \right)}{d\text{M}} d\text{M}_S \, d\text{M}_P\]

for primary mass $\text{M}_P$, secondary mass $\text{M}_S$, and single-star IMF $d\text{N} / d\text{M}$. As such, the fraction of total stellar mass in binaries is equal to the number fraction of all stars in binary pairs, which is given by StarFormationHistories.binary_number_fraction.

source
binary_mass_fraction(m::BinaryMassRatio, imf)

This binary model requires an imf that is defined by stellar system mass. If a system with a randomly sampled mass $M$ is is a binary, the primary and secondary mass are determined based on a binary mass ratio $q$ sampled from a user-defined distribution. By definition, the expectation value for the total mass of a binary system is equal to the expectation value for single-star systems. In this case the binary mass fraction is equal the binary system number fraction as given by StarFormationHistories.binary_system_fraction.

source
StarFormationHistories.sample_systemFunction
masses = sample_system(imf, rng::AbstractRNG, binarymodel::StarFormationHistories.AbstractBinaryModel)

Simulates the effects of non-interacting, unresolved stellar companions on stellar photometry. Implementation depends on the choice of binarymodel.

Arguments

  • imf: an object implementing rand(imf) to draw a random mass for a single star or a stellar system (depends on choice of binarymodel)
  • rng::AbstractRNG: the random number generator to use when sampling stars
  • binarymodel::StarFormationHistories.AbstractBinaryModel: an instance of a binary model that determines which implementation will be used; currently provided options are NoBinaries, RandomBinaryPairs, and BinaryMassRatio.

Returns

  • masses::SVector{N,eltype(imf)}: the masses of the individual stars sampled in the system in descending order where N is the maximum number of stars that can be sampled by the provided binarymodel as given by Base.length(binarymodel).
source

Notes

The trickiest part of including binaries in the smooth template modelling procedure is deriving the IMF weights. Let $M_p$ be the sorted list of initial masses for primary stars and $M_s$ be the sorted list of initial masses for secondary stars. Conceptually, the IMF weight for a binary system with primary mass $M_{p,i}$ and secondary mass $M_{s,j}$ should compute the number fraction of binary systems born with primary masses between $M_{p,i}$ and $M_{p,i+1}$ and secondary masses between $M_{s,j}$ and $M_{s,j+1}$ per unit solar mass formed.

In the case of the RandomBinaryPairs model, the IMF weights are calculated as follows, with $dN(M)/dM$ being the IMF for single stars, $\langle M \rangle$ being the mean mass of single stars over the full range of possible initial masses, and the integral in the denominator being over the range of initial masses in the isochrone. The integral in the denominator accounts for losses due to stellar evolution.

\[ w_{\text{IMF},i,j} = \frac{\int_{M_{p,i}}^{M_{p,i+1}} \int_{M_{s,j}}^{M_{s,j+1}} \frac{dN(M_p)}{dM} \frac{dN(M_s)}{dM} \ dM_p \ dM_s}{\langle M \rangle \ \int_{M_{\text{min}}}^{M_{\text{max}}} \frac{dN(M)}{dM} \ dM}\]

diff --git a/dev/doc_index/index.html b/dev/doc_index/index.html index 01c3887..4ac9b19 100644 --- a/dev/doc_index/index.html +++ b/dev/doc_index/index.html @@ -1,2 +1,2 @@ -Index · StarFormationHistories.jl

Index

+Index · StarFormationHistories.jl

Index

diff --git a/dev/examples/index.html b/dev/examples/index.html index 1cb3bfc..19f0589 100644 --- a/dev/examples/index.html +++ b/dev/examples/index.html @@ -1,2 +1,2 @@ -Examples · StarFormationHistories.jl

Examples

We have constructed an example Jupyter notebook that highlights some common use cases supported by the package. It is recommended that you read some of the background in our main documentation before or concurrently with the example. The notebook is available in the repository as examples/fitting1.ipynb and can be viewed in rendered form at this link. It relies on a custom isochrone file which can be made available upon request to the package author.

There are other scripts in the examples directory of the source repository that are used to generate figures for the documentation and provide more targeted examples of usage.

+Examples · StarFormationHistories.jl

Examples

We have constructed an example Jupyter notebook that highlights some common use cases supported by the package. It is recommended that you read some of the background in our main documentation before or concurrently with the example. The notebook is available in the repository as examples/fitting1.ipynb and can be viewed in rendered form at this link. It relies on a custom isochrone file which can be made available upon request to the package author.

There are other scripts in the examples directory of the source repository that are used to generate figures for the documentation and provide more targeted examples of usage.

diff --git a/dev/fitting/figures/covar_0.svg b/dev/fitting/figures/covar_0.svg index 4ece7cb..5168873 100644 --- a/dev/fitting/figures/covar_0.svg +++ b/dev/fitting/figures/covar_0.svg @@ -6,7 +6,7 @@ - 2024-09-28T21:34:31.078322 + 2024-10-07T06:58:14.960084 image/svg+xml @@ -37,37 +37,37 @@ L 61.705464 7.2 z " style="fill: #ffffff"/> - + +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" id="image58702f96e6" transform="matrix(4.839796 0 0 2.8 61.705464 7.2)" style="image-rendering:crisp-edges;image-rendering:pixelated" width="49" height="99"/> - - - + + - - + - + - + - + - + - + @@ -252,17 +252,17 @@ z - - + - + - + - + - + - + @@ -387,12 +387,12 @@ z - + - + @@ -404,12 +404,12 @@ z - + - + @@ -421,12 +421,12 @@ z - + - + @@ -438,12 +438,12 @@ z - + - + @@ -455,12 +455,12 @@ z - + - + @@ -472,12 +472,12 @@ z - + - + - + +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" id="image719255c13b" transform="matrix(4.839796 0 0 2.8 298.855464 7.2)" style="image-rendering:crisp-edges;image-rendering:pixelated" width="49" height="99"/> - - + + - + - + @@ -848,12 +848,12 @@ iVBORw0KGgoAAAANSUhEUgAAADEAAABjCAYAAAAy2ujgAAAL3ElEQVR4nO2b7ZLjuG6GH4CU7O6tPSfn - + - + @@ -863,12 +863,12 @@ iVBORw0KGgoAAAANSUhEUgAAADEAAABjCAYAAAAy2ujgAAAL3ElEQVR4nO2b7ZLjuG6GH4CU7O6tPSfn - + - + @@ -888,63 +888,63 @@ iVBORw0KGgoAAAANSUhEUgAAADEAAABjCAYAAAAy2ujgAAAL3ElEQVR4nO2b7ZLjuG6GH4CU7O6tPSfn - + - + - + - + - + - + - + - + - + @@ -1127,25 +1127,25 @@ L 619.705464 7.2 z " style="fill: #ffffff"/> - + +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" id="imagebbe18a8273" transform="matrix(5.693878 0 0 2.8 619.705464 7.2)" style="image-rendering:crisp-edges;image-rendering:pixelated" width="49" height="99"/> - - + + - + - + @@ -1155,12 +1155,12 @@ iVBORw0KGgoAAAANSUhEUgAAADEAAABjCAYAAAAy2ujgAAAadElEQVR4nL2baahl2XXff3vvM9xzhzfW - + - + @@ -1170,12 +1170,12 @@ iVBORw0KGgoAAAANSUhEUgAAADEAAABjCAYAAAAy2ujgAAAadElEQVR4nL2baahl2XXff3vvM9xzhzfW - + - + @@ -1195,63 +1195,63 @@ iVBORw0KGgoAAAANSUhEUgAAADEAAABjCAYAAAAy2ujgAAAadElEQVR4nL2baahl2XXff3vvM9xzhzfW - + - + - + - + - + - + - + - + - + @@ -1374,18 +1374,18 @@ z " style="fill: #ffffff"/> +iVBORw0KGgoAAAANSUhEUgAAABQAAAGBCAYAAABmVZFJAAAB/klEQVR4nO2cgW0DMQwD7f+M1hG6/yhNh9AJIEFqAKL3pGh/GuT+3N/vAedz7kPqHVbtnPO5z2UFizweHvnwyKzghsuspgHySUSGg327y9NZQIb/QnxTHJATXc5Dbn1Nx8JleeQ8l798faF6DTYwPDIdm41dpgX1kevyXFAemW9sVs8i2HmH1DfPZf1gB96+AuuLXj2LQwoW1HcZf3l0aGxY0AG5Lk8FHZDr8lTQ4Y4NCzq43JvDcIo8nyIDgg7IfdebCuojRx6jsGCRx2OArL/LgfWFfnf6WCB3l+eCgcj6wQ6srwZ7LhiIrB/svF3Wf4YN9nwCg52IfC77YtFdnk+DPZ8Nl9lN0XfZ4OYQeceGGztyl1nBujyfjUNKvbHxYMP/yWywgVmorxMY7CIPxwKZFTSoL32XaVMMkAN3GSa2QOZ3GdVbQH4S66suTwX1XX7gK3Fdnk+R58Mj05ti0Nj0M3RwORBZPth1WU/Q4CpigHz/aEF55Lo8Fnz1kXmX2Wdo0NivwbkcV18Oq5dnSiAyvnrybcMjy58p9BFQ5Pm0vubDI9PlYHAldkCOe/FZ2GV1lxd2Wd3lhWDHuRx44TR4o08MdtwHknxj6wc779O5hWCjehbIqNzOIUULwl/i3DikYMFX/mczH9gUC5dpZAOXWU38GW7UF47MCuLI/3eYjgRi4GjtAAAAAElFTkSuQmCC" id="image71349ce138" transform="scale(1 -1) translate(0 -277.2)" x="535.68" y="-6.48" width="14.4" height="277.2"/> - - + @@ -1403,12 +1403,12 @@ L 3.5 0 - + - + @@ -1421,12 +1421,12 @@ L 3.5 0 - + - + @@ -1439,12 +1439,12 @@ L 3.5 0 - + - + @@ -1457,12 +1457,12 @@ L 3.5 0 - + - + @@ -1475,12 +1475,12 @@ L 3.5 0 - + - + @@ -1506,13 +1506,13 @@ z - + - + - + diff --git a/dev/fitting/figures/covar_1.svg b/dev/fitting/figures/covar_1.svg index ce9fcad..a0ecd87 100644 --- a/dev/fitting/figures/covar_1.svg +++ b/dev/fitting/figures/covar_1.svg @@ -6,7 +6,7 @@ - 2024-09-28T21:34:28.824796 + 2024-10-07T06:58:12.707742 image/svg+xml @@ -37,37 +37,37 @@ L 61.705464 7.2 z " style="fill: #ffffff"/> - + +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" id="imageaba7b468b0" transform="matrix(4.839796 0 0 2.8 61.705464 7.2)" style="image-rendering:crisp-edges;image-rendering:pixelated" width="49" height="99"/> - - - + + - - + - + - + - + - + - + @@ -252,17 +252,17 @@ z - - + - + - + - + - + - + @@ -368,12 +368,12 @@ z - + - + @@ -385,12 +385,12 @@ z - + - + @@ -671,25 +671,25 @@ L 298.855464 7.2 z " style="fill: #ffffff"/> - + +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" id="imageec641b80fc" transform="matrix(4.839796 0 0 2.8 298.855464 7.2)" style="image-rendering:crisp-edges;image-rendering:pixelated" width="49" height="99"/> - - + + - + - + @@ -699,12 +699,12 @@ iVBORw0KGgoAAAANSUhEUgAAADEAAABjCAYAAAAy2ujgAAAJo0lEQVR4nO2b224jxxGGv+qeE0+iRK3W - + - + @@ -714,12 +714,12 @@ iVBORw0KGgoAAAANSUhEUgAAADEAAABjCAYAAAAy2ujgAAAJo0lEQVR4nO2b224jxxGGv+qeE0+iRK3W - + - + @@ -739,35 +739,35 @@ iVBORw0KGgoAAAANSUhEUgAAADEAAABjCAYAAAAy2ujgAAAJo0lEQVR4nO2b224jxxGGv+qeE0+iRK3W - + - + - + - + - + @@ -950,25 +950,25 @@ L 619.705464 7.2 z " style="fill: #ffffff"/> - + +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" id="imagece56464759" transform="matrix(5.693878 0 0 2.8 619.705464 7.2)" style="image-rendering:crisp-edges;image-rendering:pixelated" width="49" height="99"/> - - + + - + - + @@ -978,12 +978,12 @@ iVBORw0KGgoAAAANSUhEUgAAADEAAABjCAYAAAAy2ujgAAARDUlEQVR4nM2ceYxlWV3HP+ece+9b69Wr - + - + @@ -993,12 +993,12 @@ iVBORw0KGgoAAAANSUhEUgAAADEAAABjCAYAAAAy2ujgAAARDUlEQVR4nM2ceYxlWV3HP+ece+9b69Wr - + - + @@ -1018,35 +1018,35 @@ iVBORw0KGgoAAAANSUhEUgAAADEAAABjCAYAAAAy2ujgAAARDUlEQVR4nM2ceYxlWV3HP+ece+9b69Wr - + - + - + - + - + @@ -1205,18 +1205,18 @@ z " style="fill: #ffffff"/> +iVBORw0KGgoAAAANSUhEUgAAABQAAAGBCAYAAABmVZFJAAAB/klEQVR4nO2cgW0DMQwD7f+M1hG6/yhNh9AJIEFqAKL3pGh/GuT+3N/vAedz7kPqHVbtnPO5z2UFizweHvnwyKzghsuspgHySUSGg327y9NZQIb/QnxTHJATXc5Dbn1Nx8JleeQ8l798faF6DTYwPDIdm41dpgX1kevyXFAemW9sVs8i2HmH1DfPZf1gB96+AuuLXj2LQwoW1HcZf3l0aGxY0AG5Lk8FHZDr8lTQ4Y4NCzq43JvDcIo8nyIDgg7IfdebCuojRx6jsGCRx2OArL/LgfWFfnf6WCB3l+eCgcj6wQ6srwZ7LhiIrB/svF3Wf4YN9nwCg52IfC77YtFdnk+DPZ8Nl9lN0XfZ4OYQeceGGztyl1nBujyfjUNKvbHxYMP/yWywgVmorxMY7CIPxwKZFTSoL32XaVMMkAN3GSa2QOZ3GdVbQH4S66suTwX1XX7gK3Fdnk+R58Mj05ti0Nj0M3RwORBZPth1WU/Q4CpigHz/aEF55Lo8Fnz1kXmX2Wdo0NivwbkcV18Oq5dnSiAyvnrybcMjy58p9BFQ5Pm0vubDI9PlYHAldkCOe/FZ2GV1lxd2Wd3lhWDHuRx44TR4o08MdtwHknxj6wc779O5hWCjehbIqNzOIUULwl/i3DikYMFX/mczH9gUC5dpZAOXWU38GW7UF47MCuLI/3eYjgRi4GjtAAAAAElFTkSuQmCC" id="imageb220b89238" transform="scale(1 -1) translate(0 -277.2)" x="535.68" y="-6.48" width="14.4" height="277.2"/> - - + @@ -1233,12 +1233,12 @@ L 3.5 0 - + - + @@ -1250,12 +1250,12 @@ L 3.5 0 - + - + - + - + - + - + + - + - + diff --git a/dev/fitting/figures/covar_m1.svg b/dev/fitting/figures/covar_m1.svg index 8259087..719de8e 100644 --- a/dev/fitting/figures/covar_m1.svg +++ b/dev/fitting/figures/covar_m1.svg @@ -1,12 +1,12 @@ - + - 2024-09-28T21:34:25.581001 + 2024-10-07T06:58:09.558359 image/svg+xml @@ -22,52 +22,52 @@ - - + +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" id="image3d39646686" transform="matrix(4.839796 0 0 2.8 68.533953 7.2)" style="image-rendering:crisp-edges;image-rendering:pixelated" width="49" height="99"/> - - - + + - - + - + - + - + - + - + @@ -179,7 +179,7 @@ z - + - - + - - + + + @@ -292,85 +320,55 @@ z + - + - - - - - + + - + + - + - - + + - @@ -378,143 +376,84 @@ z - + + - + - - - - - - - + + + + - - + + + - + - - + + - + + - + - - + + - + + - + - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - + + - + @@ -522,28 +461,28 @@ z - - - - - + - + - - + +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" id="image3832da34cd" transform="matrix(4.839796 0 0 2.8 305.683953 7.2)" style="image-rendering:crisp-edges;image-rendering:pixelated" width="49" height="99"/> - - + + - + - + - + - + @@ -812,14 +751,14 @@ iVBORw0KGgoAAAANSUhEUgAAADEAAABjCAYAAAAy2ujgAAALx0lEQVR4nO2b23YbSXJFd0RmFS6UZPW0 - + - + - + - + @@ -827,23 +766,23 @@ iVBORw0KGgoAAAANSUhEUgAAADEAAABjCAYAAAAy2ujgAAALx0lEQVR4nO2b23YbSXJFd0RmFS6UZPW0 - + - + - + - + - + - + @@ -851,93 +790,79 @@ iVBORw0KGgoAAAANSUhEUgAAADEAAABjCAYAAAAy2ujgAAALx0lEQVR4nO2b23YbSXJFd0RmFS6UZPW0 + + + + + + + + + + + + + + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - + - + - - + +iVBORw0KGgoAAAANSUhEUgAAADEAAABjCAYAAAAy2ujgAAAVsklEQVR4nO2caYxk13Xff/fet1VVV3VXd09Pz8ohKQ5FURKlyCNQlKM4kgUDybc4CWARCgI4CwIEMOIAcQwEAQI4HxQgiIEE8YcE0gcnjh0ggZ2FsqwElhGLWmiJEkVJHG4zw2W23ru2t9178+G896p6pns2Nsn54AsUqqu76tU992z/8z/ntXroS//acxfLG1B2+nw/LH23H6g3fr8IAPcgxP24/lyI+2X9uRD3y7pjIbyRx/24bitEvXFlp/nhflu3FMJFvskH3oAClK+eZ97zfq9GiBtPWDKyajas6yTn9n7a5Op9104jxI0ZWFnZuNfyuHF5Dd6IFmrtvF/CHGxON/zFK8DLwyvw2k9/roRB+8bMms+9B4Lt2Wr9hd6I2XgNtuXEqR240OMC+b3OFapUKAeqVHgFuhARZgV5LzDWHiHqL6zNSDkw4+otXjarKzNDy7ONvbzX0mjqxiu/29q4ZXSqTcUrcLFHW1AlzWbLtkcXSnxiRgCv5ABqjbzb2rhJCAXNySonZuMSj5kobAQ2lje5yGMyEQAHLhBBbOynWtn3G94DIWAmjFYnrDOFC8DkgPLYyINTeMAZ0CWgaPxC1Q4/o5FZkzps89IHXdSFcpQ2kZMtexYbgS4VPvBTZ285yo5vAoAPvGijOgS8XEvN5MTDNq9gtsxsokrlxGXXiRZiT7htquys0JnGh6KRYKhRBbhIYRMIRkqiWlwFCCXXqoV4N/L7LS023NHiH4VCFwrbE2ldJD6SXDd4oJyrIlRlelCZZGVKqhIGuCmPHIoQtwJ13gAagonkA+qEN1/gA3lPkCrCXY0uxKlt4tGlwoXys428HJUHF/jpdQ9TCDjARrWcnhkrXOCxLU98ORSHdQqdQ3rUki84fLA31Nq4EqQlEUIVcj2dK/CVmR2iIMHsizq01qrXVr646DrwCtdxeAOt1yOUBTPSlIsleaQwY41JFSZVlC1POFSEOwG27XGhCGZQ2MRhUr3H0Q9ViDq0usgL7DDipLqUzRUdiLYV6VGLN55owxBsBhIIqIKRBpNKVNO5omx7fOjRqcJrLwhAicYOS4692EnXmxBVeCVZ2caeMvG01hTp0RIUxOsG2/a0Lys6lyWSmbw+BEl+dSgOBmKbulAz0h6ek+/FTk4eNvFis3WyKxSt6woby+aDgRYT6TjKNuw8VqLHmrLtiXbBrma0rivyIyXKKcqOwyWu0qps3iZ+nx0cghBQ+4VCZ1LsuNCjShg8Ukr8TyAYinnpsabseKJ1g2s5bL9E557u9xNGj2fEV0K89gQViKwjoTNgMgkYe4qswxCiQa6VWQQTCAcKd2ZC51JAPu8oe5bJqZKyI/agzg6xbc/yc5rwWsj4uFwjfDMSU+xagSOpbjCVmkmCXr1zs7rJsb1GcJCBsuNRVuGvJ6KBiaJYtPixId5QhCNFeaULHSi64GKHcor0iKfoO3zomP9xyOi4mI6Lq+8wTJFvlX9qV7mXdaBFmrQqfCyYiYTNousw1yJIHLqE4WlPuuwxKQzOONxyjtfQP3cdVSiSt0N2H7W0ritc5BtSoda0jXwDFPcrge9YiBv5pKaiSyoYHkCxWGLbDlXKZoK1kOFDlmRNEYwU4wcL4gcHtM4ndC/C+g9X8MaT9x2qUAyfSGld1biWpWz5BlTqYi+YvFez0jWfVK/6RJStMFIK0XqADzyuV9J5SxJV92XD+ITj7/6NP6R1KWQyjMn6nskRRXk0R2ea7gWNKqF1PqGY96hMgxbQaFKBMoKKJRne67q5KHJTjAM0odaMNVjF+Lgj3FXsfjRHp4rfeuEz5H1H63wCx1M6lz3tl2M4MSH6hTXsvCXre5I1AYy14Rc9J7mkOjAzkcR6L9rY1xJ1xSXVPgFV5Mo00aYgW5ycZBDaxhyTH7XY+LmM5Kl1jv5+Qvm/lmldDLGLRePQ9ErJ6F25sG0JLkNzz3XGrWvsGdTpOhYWCoK0SlipCJNuJ8Snhix85iqTxyeEb8Skzy5z5a8U7DwieeDsmauMVz1z59YxVyX0husB4UjRuqoJd5UQDrUa7tLJ9U1osgZ/xfR1/R69FjE64cgXPHoxp5y3YDytP+oy+KNVwrhk9TuWZNPTXRjzyMfeZHLS8uoLJzn2LUteGsKBIj9isYmnmHPkfU/Zqep1piXtXQkxq0JXbdZFksickYLHBZ5gK8AFkitWP34VfSmB2NF/LiQaesxf2qT3zBybHwxY/d9voL/eJy1DHvng20Q7mrWPBlinyRc84aYhHGhc4tEFTe3hAj8tZ7lzuB7s90szU50FI4EgaAiHismxkrVvHqM1AOVDdj/gsR1H8vwi81+4TEd5Xl45DSfHjL97jH/8i3/Av984TdkG9ew8YQf48AD72hzttwzpksekmmCkJLkClNX336GP7EueeS3asC1PvmRxsSefd6gSlBdnHzxocQbCgcaMNHj4wqnvsv61E3zi0+dptzP0o0O+fOEp5v7qVdy5XcbHHekDOf3/1sEryBY9Jp+yizqjgSW8U00oB1iFN574WiBYp+tIj1aVmodwV+RPT+Ykb0QU844vfe8XWNz0PPfds7huSWshZefbK9iWhNjiZ8a4ccBoVeMDh4s91sqhxCNN3vMNq1hXinetiVoLEkLFlIquI1tyJGsG388x/YyyLc7oA+j9OCJbtjzwxGV6324x90uX6b2qQcNkNwEFH/uLL5OeG9H7kxZ6EJD3YP7sJmasCSaStdNlhw89ZUsEaEjqe9FEXU/oVDWRwlSwnO0ItTKROjlTlMcz8ocL+knGpWtL6JOe8usnKI56gqSknASkqyXP/+lZXOSZu2yJBpqi5dk80icsqkSnNd74hqB2QZWrgjsDhjdrAhpIUHdYXCB/mHtDU+zEuKgqL1ND/naHrQt9Vv5HTLijWPxJydHvWh755zs8/YnvEOwagrEiWdNsPhbQ/9466RHJ3mYybdCYsUKXsnldys5sVUjZ1g0I4nZCwF44rjMlRFnsyec9qlWSrCniLUV0PSAYKZgv+Oyvf5PJSYv+lWso53n176zyGys/ovvYJsmG5B1n4LUvHiH/2JCy45mcKAmGCpx0nGzicaFUlV6BzmR7Or81GFH7Dag0ScdD2XFEmwYbe1zscS2HTjU4iE6NmHtmjrynGDzk8G3Lf/zslwH41X/79wUBd+UawekR/pU5cPDwz17irZ15yuf6lB8Z4i92KJdK1ERL/jBTM1K+Ii6KgwU5UAhAChamNXeyJnU0CjpvKgYPO+YuaYanPH41JXy9hdeeZFOhCwgHnq0Pwemv5bz+Rei9EJN9akB+tc38S4bds65x4KUHt9h4bRHfK0guxZL41JRtv5Vf7G9OzEDyCi4HE0W+4Jo6ORpK1Tf45IQPnrtIcDHh9NdG5CdznvibL7Ly755l7UnLmWdSLvxtz2P/6FWyvifbbPHw45cJR77KCXJk428uo3NFeCWqVDDlwZp9HZA3DoRaNeq0LS82O6mI5ZGidU0xOK3QGQQXEtb/wwPoUrH5oTYLfxaz/surXP2VpyDwnPvN76E2Il76jQ+ycN7zxGOXeOPZkwxPKtx8iYsdyXWDbXla1xQmV2RLFhdOC7S6Dj8og98SLypXcVBaTCrc1qTLjtEpx+RMjvIQ7SjWzkGyBvm8wuQe+9JrLJ4vwMHvvvAzsJxBr+Da50pe/LMHyZetaHgngMBTzHmCsWJ4tsBrT+uqQWdVqLUztf8B69ZQvKJXqC6irTQbg6Gi+5OIfMnR+strdN7Q7DxuGZwt2HzC8fdeeoXtfzBg5ZsBZ35b4UYh3d6E0/9dk1zXLP7A0P70OivPwYlTG/jAYzLQY4ONIVt0ogmPwJ59ccVthPBVAVbnCtsWX8jnnSBcD8MHHAs/0qxfXKSYAx84lr8d8NiX3ubXnvtFjv/DIcMTigtPA8YTfHWBh/7ZTwkmMDmi2FjvsvZxxfY3VrFtx+4jJTj5rmAsDc5yzhFumikxfTdCzAJBQAhgB/G6wATloXtRs/Xxkt5LYs9hL2f3IWj9TsqJ3w156VdP8q9++cuQGcK1gK/8+r/hT77zOOmy5/Qf7tL7fowPK1MaGNpvBiirCAZSJIHQn3WuUvZgk9o3xDZ/ZOpYIAkrHAp9Y1ueZF0zOlPS/6Fh+1MZ5nJMuVTSWRozGUf4nYgT/xfCoeWtz4Y8/F+2uf7kAsWcomzDkR+UHPknr/PiHz+CsopkDcbHPEVP6oxgJCG9KZIUTdi/rSbqVZuUmJVQ9GXbYxNBsS6AaMOw9VHL0jdikg3FA78Pvd/rsrq8gxlprv71jLUnImzsefvn+4x/fshkxdP65DqjVcP59RWUk/K06EI0UMxdkpPLF20FRW4Nz++omq19Q1uJ63bOki1bsmVH3rcEO4bBA4qs79k5EzI8oSl/5ygLH9ogeb6N/vQWH/i9MeYzm5QX5jAPDtm62Odf/NpX6LVSXOgJB4p4S6CNTUTjZqKlhx7ObHifMHtLc/JmZkhFyWnYrsMMhXsyE0V6vEDlGnoFS0tDdl5cwhnoXlBsf6SEyEEhZ9V6S2CpLmDy+ITet1po6xn83Jju3AT+5xLpkvicjSSs29bUnHQuCePGDd86T1gJsZK15WEGWp5TMYHeT0Ihw67FbL66iEkV4VCRbDm6rwQE6yEnvq5ASxXXvuL54tNfx2/GDB702Fhx9L8muK8uMzmimJyQVrMLfVPb6Ex6Gwc59m3NaU/JGkg/2xnxDSlbK/J5ocRFjmzJSpwPFOVTu5QrObtnDKeeUZTnBux+bsxv/+fPs/iC4tHfvES87dl4esTOJzPmLzh8Ysk+kGITGcUIxmo6cuGn/fW7EqLOGTUHpTMtTZKOYP1wWDGGpSJaSpm7aFCLGUHqWPlKi6VnI+xTO1z/C4Yz/9JSrieMTpeMVxU//aenuP6kJXtzju7zMeUXNlFjw5H/ExMOJayGu6rqvFYWUaib8sVtcuFMo95WJ6IqSqVQ6FyRz1eUf6DglY6ofD1m66wmPLdL+uICympWvme58NfmOf3oFS5dOkKy6Vn+sWd4zJAuSyN/58UlAg/rH/fgPdGOJltyVauMplfuaxRRDRLcMddWRygzVuBEgLJnyVaFZTNjTbylGJ2xBCNFejbF/WmfaFcRhpa8q7ExrH3jOGY7YPcDnrc+r0g2peCKtz2dx7Yo56ZIOVt0RNuaYEJTqjZ7mTngOx8pbaSp+ng5xNcDcFKZ6QKGH8kky2pIXk0Yn3CUCQyvzbHzsObI8570iKP3mpjj/Mkd1j4h7YGsrxi83AeNkGplxX95GbEo5+y9O/bscqEUKr4qVlwk/TqAYtHR+XGMzsVuJ8dK+i8qHv/8yzJKFHryjiI+NSQcwdNPfov2f5pn+QeSncfH67ayJhhq7EIp3NSS4LVo00xb1FUJe09C1CXibEOkmHe4CHSqmKxIWCx6DjVXUvQUP33mrJjah7fxAaTjiNFxxVd/62fZ+aUhW49q8r4nHGiUVRQ9T7yhMJuS4ep85ILplJu+oVS9pyaTctKqwgsDqHPAQ2tNckQwVOi1iPTJIfGW8FPHugMe+VvnYTfERTB4ALJXekS74B8eUXQdrTVF+y3ha33gm/EJmfykAYFe783ct41ONy4PULVwlZ/WwC6W/l0wUtiWx4wVxdU2oxPgeiWv/vAkejVFTwSZll2p6MpP7WJ+1KNcdNhYNpivlCRvhRRznmhLY1senYIPqnSt96KId9QKd4bG48NdjU0c8RYEY9UQCy6G9isR0bam3I3ovQbZgylQjdp9v4fX0HtNAGV6rCTcCMhWLHaxaIiEZlJUT1vVdx2dGk1UMbom1mrr9NWoxOi40JDlyUzaZCmNn0Rrhu3HHWojYuXhDZRV5B8ek62U0nz00Ho7oJi3BDsaMi2d245rdjprVvW6ayH2tL+8aKNu4RZdQaMu9ASXhSnMl4Q4XjiviLcVPnbEG5rRH6+Q9y3+7RbLp7bJe1B0hXnvXAwolkp0Kpqs5w29mjZ/Zhsxd+0Tzarr7rLqso4VvjItM1EUPYElyRVDMIHBGU8xbyEQR3fVpGfrmmLjSBf94Ql2HJCXCpNBdD0ADcWcI1mvGPPZrF2R3vekidnVTAcUirIr4S9fEPjsjW8KKhdAfrQgGGhar0cUcxLnxWkhuhTjtoRvKpcKzEQRjOUgwoEmW5xOSe/HfLxzx6aK5WOZRgPJsPGGzAfaGJk62A4ESiw54k3JB0XXYybgHh1KgAgd7dciJifFZlWppk7saLRbH9A9C7Fn+qAaNKmnEnxY1SCxlzK2I/6gLNh+iW25poKbe3SLYKJwERTbCa0rhvBaJH2P0MkcSKnITuXN7HpNLN9YW9yzY8++btoArsZVohWdKcq2wyWecE2KJ50pBg/B8HyfYCLB4Oj/00xWLfGGQpcKPTIEY4lsZjOoYMtMy+GGdQgjU1NwWHNS+GmmNRPZuJnIKbpY6PuyXzJZcSRrimufK4k3DZMVaQurUlG2hKwLh7qpLF00k70PJTrNCmFqulE1+cMrObmy44k3dCNU0RWnDzcCvIbJigy8uMAT7ShsS65ZN1bKtpOhrxp+e3DGy3dV61A0Udfi8kI6PC6Z3pLgYi9OGchQsC4U8YZAeHsso+xair4jPWYJhlK7m7QizGKHC25ArYcBAA8SpJnGnGh0qhqKxxnJHbZnm5Cb9SXuRxcTlFdEGxozlHE8XSqyvphVsBPgYn8gcXaoQjSrcu6adpFBSCh7juTtUKYSRtLaQkmWDgYaF0HZL6VOqYSue9tmpG+6i8zdaz1xV6ue0XAyNhduaYo5h21L2PWRmIg6msltPqEnuian7o1oqanpY2no1E37G5svhy5EzY40gvipIDoXVg+nMCNxdq7EQt07RTnnG3v34ZQmMqmazgpWI0r3XNndyWoAItP8UQti2zIHGExq+qWKbAUNIWcmMq5K1XCsbzBpru+mbbBm+uewhZhdsxrxgYBEkylcSMOxBiMlZemS+INNPLYts09mIg352s9uXLUgh5InbiuInuYQIeKq8nKm3AzXp1tR1Ti2D6afbzSKkHU1BHnHld2ezZq9z3tWjT5N7ay+GSeVWz+nM1YNJVTOtBb8jAChv6k5f6h5Yvb5xrXHR9x0+N1GXkjraq5DbgWqGjv1WMYM/7pfU/5d84n9bsitt6JmbNxk1Wysl5zhDVON1FDDqQPvgYV30Sf2Q7sw6yPTPFDjIDPRewgyXU+guZnPzqz3JDoduNzeDSlXzW/UkwSGRoB6eW7Wbj2Y/P4IUa267QB7Qd0szPZ6xgzvZaLgvVjeVIPv7MVDzes7GC991/PEfmvPDYoV0q1/3mNmBwyn7PmXA/Z9EmI/s7jdP/jZ72/3zAC+W+ud3I963wjxTtZ9L8SdDPi+70LcbpN3YmbvuxCHcW/2/wewEHOeD8Ur2gAAAABJRU5ErkJggg==" id="image8797f82028" transform="matrix(5.693878 0 0 2.8 626.533953 7.2)" style="image-rendering:crisp-edges;image-rendering:pixelated" width="49" height="99"/> - - + + - + - + - + - + @@ -1119,14 +1044,14 @@ iVBORw0KGgoAAAANSUhEUgAAADEAAABjCAYAAAAy2ujgAAAZLElEQVR4nNWbeYxk13Xef3d5Sy3dXb1M - + - + - + - + @@ -1134,23 +1059,23 @@ iVBORw0KGgoAAAANSUhEUgAAADEAAABjCAYAAAAy2ujgAAAZLElEQVR4nNWbeYxk13Xef3d5Sy3dXb1M - + - + - + - + - + - + @@ -1158,93 +1083,79 @@ iVBORw0KGgoAAAANSUhEUgAAADEAAABjCAYAAAAy2ujgAAAZLElEQVR4nNWbeYxk13Xef3d5Sy3dXb1M - - - - - - - - - - - - - - - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - - - - + - + - +iVBORw0KGgoAAAANSUhEUgAAABMAAAGBCAYAAACEiYowAAAB9ElEQVR4nO2cgW3DQAwD9XZG6wjdf5S4M8g4oAeQGoDomRT/nQY5P+f3GWg+cy5Kazilmfmc63BixVwPizksJidGu8npiTEnBRMM7elubgfGBP8ydAPMmCluZmC2grajdlOLmeHmw1YQptXQvhgWk4wGvZukmBezbu7FtJhs03Ja6tBmHChPhpve0IbcgkIqiFwn9YECinndRF/EzE0Lipkx6+ZWzIxZN7di5jstKGZ2syf6coq5n2K+EDNj9t1pK+bFjDnqQLFirkeM6d3NkArCvv86aszu5l4sBNMb2pAKamj3YiGY3tBm7Kb3mTW0+wkJbQrmHO7y3t3cT0O7H9pNbgO8bopP9Jg7Ldi0MbvJidXN/dAHirVp0dCC/11raF8MXEETEtpiLkeNyYmJK8jrJmmAGDNkN0FKNSa7m5gWjHmlVFDd3Ip53bzAa2jd3E8x98NikhsgblrymZndDMHUhrZu/q+Y+HogxjxfUkyLWTfXYrcXk3WTe2bipr3F52ZEBZnXKcOAEEx0nbStwWJqzwCytou5n1bQflhMctHF11AzZsRLBbybVjfh3bS6CYc2ws2Qy574TTgltBEfxrFN6w1txqdUcGgxLTUmJsUfKKQY+KU9+kABxW7tT+NdoAFqN0lMsZucHvrM6ApCMTkxFPMPeuiOBCY1y2cAAAAASUVORK5CYII=" id="image40a17ff9d0" transform="scale(1 -1) translate(0 -277.2)" x="542.88" y="-6.48" width="13.68" height="277.2"/> - - + + - - + - + - + @@ -1402,45 +1313,15 @@ L 3.5 0 - - + + - + - + - - - - + @@ -1450,15 +1331,15 @@ z - - + + - + - + - + @@ -1468,15 +1349,15 @@ z - - + + - + - + - + @@ -1486,15 +1367,15 @@ z - - + + - + - + - + @@ -1504,15 +1385,15 @@ z - - + + - + - + - + @@ -1522,30 +1403,78 @@ z + + + + + + + + + + + + + + + + + + + + + - - - + + - - + + - - + + diff --git a/dev/fitting/figures/sigma_distribution.svg b/dev/fitting/figures/sigma_distribution.svg index da4f106..2ab5957 100644 --- a/dev/fitting/figures/sigma_distribution.svg +++ b/dev/fitting/figures/sigma_distribution.svg @@ -6,7 +6,7 @@ - 2024-09-28T21:34:09.705641 + 2024-10-07T06:57:53.699188 image/svg+xml @@ -40,213 +40,213 @@ z +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p7e15b49811)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> - - + @@ -293,7 +293,7 @@ z - + @@ -329,7 +329,7 @@ z - + @@ -751,12 +751,12 @@ z - - + @@ -780,12 +780,12 @@ z - + - + - + - + @@ -830,12 +830,12 @@ z - + - + - + - + - + - + - + - + - - + + @@ -1450,7 +1451,7 @@ z - + diff --git a/dev/fitting/figures/template_compare.svg b/dev/fitting/figures/template_compare.svg index ec62600..a55f023 100644 --- a/dev/fitting/figures/template_compare.svg +++ b/dev/fitting/figures/template_compare.svg @@ -6,7 +6,7 @@ - 2024-09-28T21:34:04.982276 + 2024-10-07T06:57:48.945549 image/svg+xml @@ -38,10 +38,10 @@ z " style="fill: #ffffff"/> +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" id="imageaa3970f993" transform="scale(1 -1) translate(0 -264.96)" x="59.04" y="-25.2" width="239.76" height="264.96"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + @@ -319,7 +319,7 @@ z - + @@ -364,7 +364,7 @@ z - + @@ -517,12 +517,12 @@ z - - + @@ -560,7 +560,7 @@ z - + @@ -574,7 +574,7 @@ z - + @@ -616,7 +616,7 @@ z - + @@ -655,7 +655,7 @@ z - + @@ -699,7 +699,7 @@ z - + @@ -1298,225 +1298,225 @@ z " style="fill: #ffffff"/> +iVBORw0KGgoAAAANSUhEUgAAAU0AAAFwCAYAAAA8FCm3AAAut0lEQVR4nO2d2ZNV5dWHXzQ2g6SZbeZmahBQkMEChcKgxqK0cpNUqrjKRSr5d/JHJDHRKiu3saJWyhQKQUAIczfz3MrYoDYtynfD9+Xbaz27z3rP1Of0+T13e9W793nP6dP7/PYaJzx+/DiJtiHyx5oQPI/W1ZN6vmboWiMjI25dV1eXO3FwcLDiC/b09IQ2NjIyElpnuXPnTtWvGcXujT6LVMPnGLlWjVT7fW8oTzX7BYUQop2ZIKXZVjT9l9eqjqDiQAXW1dUV2VvVX8io6rPvgZQnqb6S91T1ughj8ZolRJ5gmqE0m64sLVKaQgiRwU/GegMii3r6BEPXr1atkKqs1lcW8UGWMXnyZGcbHh4e9biMoFJOw8PDFd/n0NCQO2/SpEn0ms5GqpLeQ+Szjapzek/d3d3WVPX3rIZ19X7NikhpCiFEBlKaHUCGX6yeUdOQAqhnJJvUG0WpZ8yYUfFax44dc7bFixdX7W+9ePFi4bi3tzd0Hr0nUHghtVxyrdDfruT69tyoD7IWdVhPv35VPlMpTSGEyEDR8/FH1b/2kYj30NCQO498caRMaJ1VgqQCSS1+/fXXzkasXLmyqtckDh065Gxbt251NqsqU0pp3rx5o+4hJfa/Rv2ckdckMnzWDc18yKCe6rYqP6eUphBCZCClKf4/Fb8MtUR9S/IhKyoHUlGkDun65P+zRFVxNMpeLffv33e227dvO9uOHTucLfJ3qTFvs57+xaZXi9XzXClNIYTIQEozRkvWwCbYFykOIqLAnlCVLyu6Dwv5+qIKjHx4t27dcralS5cWjmmvda6mSdevXx91Dymxn3bOnDnOVpJN4P5O1v9M6pmoc8VRqyjNuilSKU0hhMhAeZoxWkJFRogqyKhyGBwcLOyDVE4ttdsRHj586GwzZ84MnXv37l1ns75Jun5UKU+cONHZSB1OnTp11D2klNKDBw9C1yr5HCMVOw7yF0dzSC3RnNISqlWCzfjfLLyGbpr1o94OaaJu6RbRx09Kg7FEgzL0+GlvOvQ4bW84ZdC5kZvO6dOn3ZolS5Y42zfffBPaB30e9jUWL15c9bXohhv54aIbWPQGGfm+1Nnlk5IadgghRPujQFBjqbeTup5/LHf9gYEBd/2+vr6K51HC+6VLl9wLkuqzkBqlx2d6LP7pT3/qbDYAk1JKs2bNKhxHH8UvXLjgbKtWrXI2esy2apn2T0SVNyXxW0VKSrDGlnJjkfAupSmEEO2EfJqNpZZfxXomDjsuXrzo1kWCN9evX3fnkd/tq6++imzDpQ6Rj23Pnj3ORj5HYsqUKc529uzZwvH06dPdGlJgEaVchvVhfvfdd24NqWyrilPivxOpQ2uj9xRtaFKiPgvfhQzVOuZqsRakNIUQIgP5NKunnq3+q40SVv3HGxgYcDbyeZ0/f75wTP40UmD2vJQ4+fzkyZOFY1J9BKkaSi9atmyZs1nf55EjR9wa2kfU13fq1Cln27ZtW+G4lvQc8vGSP9cms9c4uK3a72M7DfALIaUphBAZyKdZPWPeYCBjVIGzke+QIs229Rkp1CiR/ZIvlNQtXevatWuh61mlNnfuXLfm22+/dTbyOVL0/Pnnn3c2q1IhKwH9nNF2cUePHnW277//vnBco9JsyZzJpBG+QgjR2sin2Vga2hCYIuDkN6SBYHRupDEGKbANGzY4G1XZRBpSXL582a2hCPKVK1ecjVTZ008/7WzWX0m5kOQjpPdO0XlaZ19z4cKFbg3ld1KT46g/OgKpzxpKKxvdnKPeqDWcEEI0GinN+lHX6p+SIWcVa89JoZJyoEjwzZs3nW327NkV19y7d8/ZyJdIuZWHDx8uHP/kJ97NTr5WUpU//PCDs5FKtVH2jRs3ujWk+ghqUUc+Uquoo+M6du3a5WzRCibrI61lnEYE5WkKIYRwdLrSbNm8ssg4XepsQ9Uc/f39zkZddiJRWer0Qz488jk+99xzzmbf0969e92aadOmORspUvLr0X6tIiU1NH/+fGeL+lvffvttZ7N+SFLn9PlQjTr5ISOR91qaEJdQbdctotonNUXPhRCilel0pTkWNDRPkyLlpFppHC1hVeSiRYvcGlvL/WQfzkZ+Thu5jg5Re/bZZ52NPg9SdNa/SGqRVBkpdtrH66+/7mwWquohpUzrSoatVfy7U515tFn0ePRNVouUphBCZCCl2Vhq+XAr+nMoikpqgpQDnWsj5Sl55UdVN1TzTVy9etXZfvzxx4rXoiFkttolpZQePXrkbKSQrHqO+vooB5Zek/yttkqI3ufy5ctD+6BsAhr/Yf3WNXZWb4VxvYRqz4UQopVR7XmMeg59ilb/VPxlJ1VJkMIgtUKRWqssKSpO+ZGrV68O7c2eS3ul3FCKNFMUn6qcrB+ScjLJz0mQqnzmmWeczf6NqTNUtPMR1btH6sozOitVG8kei2wURc+FEKKVkdKM0dBfs2henFUrpKIo/9JW3aTEUd8DBw44m81zJL8e7YNsJ06ccDZbu02+SuqJeebMGWejOnNSweSHtFDUnaC6dfLB2nzXt956K3QtUpD094wozRLfbdQPWU8VWe301ZZASlMIITJQ9Lx+1LX2nCY8Wh8m5S+SmojOyd6/f3/FdeT3pEoZqnOmqhWrSCdM8B8Z+eJIQVLNN/kXraKjjkO0V6qdj87YsT5M8mlGI/H0ZEL18xGaMHmyJdViLUhpCiFEBvJp1o+6zgiiqKa1UfUMKU3qqHPs2DFno4ix9TFSLTdFsm/cuOFspHitsiQFRlUxkeqilFip2V6Z9DlSzipBSpb8oS+//HLhmHJWaS7Rjh07nI2eEiJU271IFJHSFEKIDKQ0Y1T01ZAPsiQHrupZ5TZ6TpFsmhVDUEUKKR3rZzt37pxbQ1FrUoKkZO31L1y44NaQsqIZO6SoX3jhBWezr0FKmdQi5bZu2rTJ2Qj7d6EsB1KC5M8lxR6tK6+BiL9y3PkvCSlNIYTIQNHz1iBUJRSBOvHs2bPH2ch3SGrFqiuKilN+J83Yoe+ajYLT+470xEyJlRX5eK2N+nVGiX6O9qmAck+p0ic6SZR81KCyQ1ka3d3d1fbA7AilqZump1UaEzvsoxo9ttLjHKUJ0WM2NeOIBB3opkbXopSap54qPuxQmSbdmOjmbZt/pMRJ/PbzoP8BSkMi6CZPN2/7HigtidruRX8IgkGeugYra7xe26LHcyGEyECBIE9LqMoE+xgcHCycSyk2FIAhqFyRAimR1m10HqlbUkg23Wfq1KluDak5egQmlwCda18jOiKYgjf0qEzK2+6XEuWjYyyi2Ef7jOYcREeqSkJKUwghMpDSbD6hlCOrKlPySoT8WKTUyH9JSpMUkk29IeVD1yL/IpVbWt+ebeCREvtpSUFa/2hK/BnZ/dL1yadJAaPz588727p165xt8+bNhWNKqI8MR0spXiobbK7c1g2BxwIpTSGEyEBKs/mEfEg9PT3uF3pgYKBwLpUXRhoJp8TlheQntNcjBUkKlXyrkRG79J4ovYj8kPQ+V6xY4Wz2PVFUn/yj5LulxHjyV9pGylSYQEQT+4PRc/kv64CUphBCZKA8zdYglNxu/WCkLk6ePOlsn332mbORYqTr2ZJD8jlSQj3tn/xu1k9IIytIfVLJJzXPoMYha9asKRyTqiQbtZ6jzyOyN0pkp7Eb1PItY5xutcnnHeGbrBYpTSGEyEBKs37U9YMkX5aNNO/bt8+tIf8cKUFSdBRlt35Oyl+khh0UCY60hiPlRueRkqXKIVJvNspO0W5S4qQ0aewu+Xhttc+WLVvcGsrTpO9BSSMOKcEmIaUphBAZKHpeP0K/9NEWcpT/Z22U90hRZVJbpIZI5UWeRCjqTtenURY2Z/L27dtuDSlZuhb5NClKbZUaZRJQzTqpSlKfhM37JPVM+aKkKjNG8YoGIKUphBAZSGk2mWjbrY0bN1aUeGfPnnU28jlSniPVrdM6G40nNUr+P1K3lENq11HUmpQVKUGKspPv074H8r+Sn/PUqVPORj7HV155xdls3io1RyZo/xmqsiNbtzUaKU0hhMhASrNFIT/hl19+WTimmm/yc5J6I/8fjXOweZRU6UMKktRnZPAZRfVp/1QHTn5O6ltplSb5Kkl90j7eeecdZyNOnz5dOKbac9pryZMJPYVUm1upnMxMpDSFECID5WnWj7p+kJGKms8//9ytoZxMGhJGNlI/1mdHXZSo6oaUMvk5rVqmTkXkR6WI98yZM51t7dq1zmbVLPlHyUY13+RH/e1vf+ts1i9LfTgzRuxG1acUYwOQ0hRCiAzk06wfdc3TpGFo1o+3evVqt+bEiRPORt3WSWmSn9A+iRw6dMitidaZR55qqAck+UdJaVLvyatXr1bcB3VkJ+7fv+9sVNlDWBVZQ/14mU2qsklIaQohRAZSmo0lpCqJ7du3O9uHH35YOI52TJ89e7azkW/yk08+cTZby04dfMinSRF18v/Z69P+KSpOOaWk3uhcW6NOPlOaDEmQyj5+/Liz2c7tJdRbQSpPswFIaQohRAaKnjeW0IcbnV9u2b9/v7OR3416bFLOJCnXS5cujXqcEteLk8+RVKSNlpNapJxS8n3Sa1KU2tbnU3ck6shOVTwUZd+wYYOzWcjnS3tNUocth5SmEEJkIJ9mYwlFQ0v6IzpsxcgHH3zg1lBXH3qaIKVGipHm7liiEyQJqyJJ7ZJ/lHyVtI5s1h9KCpWqf86cOeNsJerQYf92GTmZhKLnY4iUphBCZCCl2aKQ+rQVO2+99ZZb88UXXzgb+QRJgZHStP4+8plSt/ioErRVQpRrGenDmRLXi9N7WrZsmbNZKI+VVDyp7EgHdqozr7EiiIj41KVQM9FNs3qqTeeoOgm+t7e3cO6MGTPcGjsILSUuabQjZVPiG4Bt4kGpPtRkg250dFOz0I2DGhpTY2JKraIgj91vNL3ojTfecDYKqA0MDDhbnZPbo+fqhtgA9HguhBAZSGlWT0N/xSlQc+zYsYLqoDZtpCqpCQalOZF6s4/LpDQp7YbWUUDKpiHRYzEFZehRmd4nfR62bJLSqBYsWOBs1HaPVCoVMJDbIYjUYoshpSmEEBlIaTafkI+KFNLKlSsLxxTkIEUTSSpPidWtLX2kfVGAh1QrBYzsa0YDSJERxymxYrTrSMlSmWnE55sSt5ALolSiNkBKUwghMpDSbD5VJ7zblCNKPKeUIFJq5IuLnmshJUvNOShNyNqoPR35OdesWeNslKQeSaOiqDv5aanFnh3NmxI3cw4mwUtVtgFSmkIIkYGUZmsQUhjz5s0rKNLdu3e7Nfv27XM2al/2/vvvOxspNRvxJiVICoyuRRF16/skVUl+WlJzFLWm5hlWGZNSJoVKua3UKi/Y1LjeqlL+0CYhpSmEEBlIaTaW6K9/aJ2tIqEIMkG+T4pSk6KzkeZo9Q9FzylP00bUKZJNbdRI4ZEfkpSrfU/UXIQqdt5++21no2olwqr9rq6uWpShVOUYIqUphBAZSGk2lrrWo1u1Qspw48aNzkbqkHIaCVt5Q76+aJ4mjfC1+aK0hnImCVKy5Fu1uaY0+peaehw9ejS0LjjSpBZlKFU5hkhpCiFEBlKabQxV8JD/j9TWzp07ne3w4cPOZqPUUb8h5WRSizq7jhQqQXmgpKjJn2tzVEkZXrlyxdkoJ5PyLylbAXyk8ku2KVKaQgiRgZRmG9HV1VVQIiMjI6ERwaTACFKHVqUuWbLEraH8RaqyIRVp90b7pwi1HY6WEudbUpTd+oK//vprt4Z8lbR/qqKKVP9QxD463lmMLVKaQgiRgZRm61KxG1K0+/eGDRvctcj3SV3O//znPxeOSbXSPqJ9Ma3iunHjhltDCow6ptPYXcJWNVH3ovnz5zsb+WQpmyCCVGX7IqUphBAZSGm2LpFIKqlRZ6N8TvLjEUuXLi0cUx9Ouj713ezv73c2ez3K+aRoNPkvSd1SDqZVy3StAwcOOBtVBNH1g9HzqivDxNgipSmEEBlIabY3IRXS3d3tFMwLL7zg1lHnILuOVBTNKqKoMnVIshMqSfVF69gpH5U6MNnXIP8i9TOlWUL0nshfrDzN8YOUphBCZCCl2RmE/GdUj759+/bCMfkqyZdIivTYsWPOZiPXFJ2n2UJ0fVJ9VDlko+DRLvNUgUX5qGJ8I6UphBAZSGmK/4N8e1bRUS4nKTzKcySVav1/Z8+edWsoPzJa/UMq2HZSovr0aG4o+W5JkcL15L9sU6Q0hRAiAynNzsUpHaplt1CXI+r+Q/PAad2FCxcKx5TzSWqO8kUpik+KdO3atYVjis5H5wadO3fO2SKVT5TRkKQ+2wLdNMX/ESnLXLhwoftn7+vrC11/7969zmYbb1BQhoI+lF5EASO6Cdu2b3StaNL6ggULnO3kyZPOtnr16sJxSRmlbqRtgB7PhRAiAynNziCqYCqmJpFyIyh5ngIu9tGbmm7YBPiUuB0djQiOpEOtX7/erbl69aqzUWOPf/zjH85mH//pNUtQaWUbIKUphBAZSGl2BnUb4lUSwHBQ4w1Saps2bSocf/HFF27NypUrnY38r9RWjhoY28APJd0vXrzY2chnSsGnbdu2OZuFlOfw8HCoqbQYW6Q0hRAiAylNUQmrfkJ+t2hE3foEKRpt05JS4oYd0XQlu46S58lnSuqZRiZTC7xVq1YVjqk5CjUJyUC+zyYhpSmEEBlIaYpKVKtW3Hk9PT1ODS1fvrxwTD5CgqLs0QbJNkmdEtknTPBvm/I5STHSutOnTxeOafgalV+K1kNKUwghMpDSFPUg5OekSLAtrXzllVfcGlKQn376qbORb5LGetjIO0XYSX3SOmoNR42P9+/fXzh+6aWX3BoiY9Sv/JdNQkpTCCEykNIUjSKkfObMmVNQpNQAg5ojUxWPrSlPifMhz58/XzgmvydFyiliT008qAGIbWr8+eefuzXUds/msf7vVsgISH02AClNIYTIQEpTjCk2N3HZsmVuzZkzZ5yNlCBVCVGbNmsjNWobFafEkX2KstPebKs8Us+2E1JK7NOkKHtJh6pIjq3IREpTCCEykNIUY4pVedQdiVQfjcWgiDeda9UtVf/QYDXq9Un+SxoOZzsw0eiPI0eOONuWLVucTdHzsUVKUwghMpDSFI0iVAttfXHkX6T8y8HBQWcjXyLZ7NA3qk8naNQHnUvq0/YSJV/o0qVLnc1WEqXE0X6Nz2geUppCCJGBlKZoFFWpHIoCR9Ri2TryV9p6caoVp31QP007gjgl7jRvxxxTdRF1ticfL6ns4MwhKc86IKUphBAZSGmKlofUFuVMku/z8uXLzjZ37tyK59GMIIrOU5Sd/Jy2Rp3GGVPnI6pymj59eujcrq4uKcsGIKUphBAZSGmKZhKJ8IaiwFOmTHHrqG590aJFFTdFOZPkHyWlSX5OUq7Wh0kKlXpz0rrdu3c7myqCmoeUphBCZCClKZpJRaVDkWHq+D5r1iy3bsOGDc728ccfO5tVlpR/SbmWpEiptp2i5/PmzSsc0/uk/Es7WyglrlsPolzOOiClKYQQGUhpipaiZCJjaN7Qf/7zH3fiihUrnM1G3kn1Ue7mkiVLnI26tNN8d6p0spDipTxQ2m9J5yOpyAagm6ZoNaoenbF582Zne++995zNpuzQDZIGt9EoYbpZUWNim2JEj/AnTpxwtldffdXZooyMjBQ+N91E64Mez4UQIgMpTTFuoHZxlP5jW7dRYIXKLyk4RCMqaBCcHfBmyypT4oR3Ss4nSlSkVegKBNUBKU0hhMhASlO0A04NDQ0NOdVEfkhq7GHH5/71r38NbYICPOSHJOVqk8+nTp3q1pBCpcFqJ0+epO25z6Ovr4/WiRqR0hRCiAykNEU7EIqeE5QmdPDgwcIxpSWRT5NSfajMkaLxNspOozMoeZ6GylGWgB3clhIqTfkv64CUphBCZCClKdqBUO7m4sWL3SLKhzx27FjhmJr/koKk61PSOqk+qz5JKdNrUgs8akyyc+dOZwMoeh5FKvUJUppCCJGBlKYYN5B6Iz+khaLWtlFxSikdP37c2ahd3MyZM51taGiocEwRdvKFUos6UryEfU2N/q0PUppCCJGBlKYYN1hllRKPgbAq7/z5824NNRem5hwrV650ths3bjibbUJMLeXIp0mVQ+QzpWbL0QwDkYeUphBCZCClKdqVUESdWqvZFmzU0JhUHzU5pkj2Dz/84Gw2T5MGt1HtOUXPbUVTSindunXL2azKLhmJITKR0hRCiAwaoTTdr73NZaNf/xr9LxHVoShh+xLqzlPSiNfZbA05RaipOxKN06XvLVUTWfVJ+aO0V/JpPnjwwNmoSkjKsjFIaQohRAY5SrOigkyJI3t2HUU5bXQxpfhQqf7+fre3F154wZqi1RBSpK1H6G9CPSUPHjzo/u72e0WdiqhKiPIoSc1R7qaFhqiRUibFS37OGlCPzUykNIUQIoOfpBrqUakry0cffeRstmt1f3+/W2O7aafEOXaUF0cdYyzUYbtkiJdoX9x3mSLSX375ZeGYcibXrl3rbKQ0bR17Gfa7Rr5K8vVH5wbR/6Il6vMVoyOlKYQQGUx4/LiWxice6qj9pz/9qXD873//2513+vRpZzt06JCzkSqgX0urUrds2eLWvP32285GuXjRsbKi9RgcHKyYu7lnz57QtT7++GNno6cmqhyyndrJ70nd3Ldv3+5spFJ37drlbODXD+W2lqwTT5DSFEKIDOqep9nd3e1+pX7/+98Xfs0oL466ytjKjZRSun//vrNFumx/8sknbg35Qr/44gtn+81vfuNsvb29ygNtPZxqoqcE+91Yvny5W0O+SsrJpLlE9P2231F6YqL6cfqOUrUSzVq32DnoKWkWejVIaQohRAZNqT23Psef//znofNKcjKdzUbnU/JdakglkLqdN2+es5GStZHInp4e+YbGnpDPzqpP6pNJCo8yPMg3SU9DFqoIouvTtSgjgL63wX6ahL7LoyClKYQQGTSry1HhVwr8gem1115zJ509e9bZLly44GykGG2tL+WoURUS5eLVQPQXW7/sTcT6NKMd0+l7RuqQrmeVK/k0aa46vSZV4pEyrqGfg757oyClKYQQGbRMP02Y0Yw16u+8846zUZ6drZCgX2L6tb948aKz0cwX8ivVgH7Zm4j1lVOWBkXFqWclVeJQXbnNh6bvI80qojrzNWvWOBv19bRA3qaogpa5aRIvvviis82ePdvZpk+f7myHDx8uHFPyPH1x6R+IHsFsm7Cenh7d+FoT93cZHh4u3MHoe0auGypznD9/vrPRqAx7U6NA0L1795yNbsAkJuh/wJYc03nd3d1yDWWix3MhhMhgrJRmtNWXs1EQiRzje/fuLRyTqqR0EXocIvVp133//fduX1u3btUv9tjj/i42KEguGfqbkzqkdZQWZ79/NGIjWtJMI3xpH/b/R8056oOUphBCZNDSPs0oVCr35ptvFo6pmSsFeAYGBpyNHPRPP/104dj6UFNKaenSpaGyviQfUlOxpYP09ELBRfJfkkolRWeDT/REQ9en0RY2ZSolDmpa/z8lwKuMMh8pTSGEyKAdlab7ZaQIoE1hopSmv/zlL85GkXKKmlqlSSlINIiLUDPkhuK+L7ZdHJXrkp+cIL84JbdbRUpPOfTdi6Y+UWL8z372M2cTtSOlKYQQGbSj0iRC6tPyxhtvOBv5kKgVl/VzkjLZt2+fs61evdrZJk+e7PZaUgIn/1M+Ub9yAVJulH9pnzhS4txKGy0nhUpPK7SOnnzIpxlEeZqZSGkKIUQGdR930cK4N0oVEpSL9/e//93Z7MAryrujiD0pSBoWt2PHDmezalaRz8ZBo3+J999/39nIN3n06NHCMeVVkmqliPqSJUucbdu2bc5m/aYZZZRqKjMKUppCCJHBePFpRgj5PUkJUm2y9SGdP3/eraEqJFpHA7Vo3IIdQ6zGx1VTUUXS3/xvf/ubs9kWhCmx0rTfq8jY6TKoKfa1a9eczfpgKb+zpGeCvlejIKUphBAZdJLSJEK/sps2bXKLrO29995zayjKeeTIEWejvDtaZ/2tv/vd79yaJJUQwX4e7jOzPsiU2CdI6wibgUE+TfoeUDYH+TlJ8doMgJKsAX1fMpHSFEKIDDpdadaNX/7yl85GeZrUp5Ei9lSb/MorrxSOye9ZEiGVmhgd91nMnj07VI9O6o36YlofOK2hKiHqe0Aqlca52GbFVCff29vrbGJ0pDSFECIDKU1PVIEVlAjlTK5atcqpFRo3TGNaSZHaoXJ37951a6gyhGqrIxVTJYxHheo+C+oItH37dmcjXzapPpuzu2DBAreGhgbStS5fvuxstF/bsYt88yIfKU0hhMhASrN6KkZgyd9FaoW6IZHCsNFVitxSFJWutW7dOmcj/1aHVB2599jV1RWaEDBt2jRno0owmyNJeZoUFae+B1RBtn79emez/u2SGUHOJkZHSlMIITKQ0qwfoZxP8i+SIp04caKz2dzNGzduuDVnzpxxNoq2koKhnqPJv4dOUJ4IqTJSh8ePH3e2CROKHxtFxUl9kmqlqjLK54QKMremBGVbjIKUphBCZCCl2VhCvjJat3jxYrfOTiGkzkqUu0mq5sCBA7ANz+7duyPLOkKZUOYD+RIPHjzobFb10VMCzS6nLIqoH9XuV/Op6oOUphBCZCCl2XxCv+zd3d0VfaTLly93C6yiSSmlU6dOORtVpJBf7N133y0cv/nmm05VUqfyNovKhiLl5Ge+fv166AVsXualS5fcGuqORB2waAYR+ajt35O6HM2YMcO99w7JmKgaKU0hhMigkzq3dwQXL14M/UHPnj3rbIcOHXI2UjCWtWvXOtuuXbucjeaBp9b1qYU6/VMO7B//+Ednszm1pCrp+vSZ2XnmKXH+r33qWLVqlVtT8pTQqn+TlkA3zQ5gYGDA/ZEpvYVs//znPwvHd+7ccWsoVYkCE7/+9a+dreRGahmLf+Kq/zH+8Ic/OJt9pKYfqOhoXgoY0d9g586dhWO62ZY07FBj4lHQ47kQQmSgQFAH0NfX5xTB0NCQUw7Uos6OlaXGENQwmZL4+/v7nc02GKHrR9O0Gg0Fh4iXXnrJ2T788MPCMQXKKJWIHuPnzp3rbFQ+a5VlRhu4lvi8WxUpTSGEyEBKs0MhpUM26xc7ffq0W0MKldqXkSK1Y2upfVmwvLOMahVSqDCB0ngoJcgWIly9etWtofQlKoElvzKp1L179zqbJcOnKZ4gpSmEEBlIaXYuITVhxwRTigr5IT/99FNnO3funLPZ6DlFkImSNnahc6uFfJpUmkjllhZShpS9QAn10fG/FHkHpCozkdIUQogMpDRFJQpKhPyekyZNcr6+rVu3unXUJOTmzZuFY1JR5OujRHDyfZaM9ahKXZWUF7rrkzq0ypjax1EEnKAc2GvXrjnbr371q8IxZTSMjIyojDITKU0hhMhAFUGiHoS+RDRC1qpIGpNLre1ee+01Z6OST1K8QNXKipQavc8PPvigcEy+W1KoVomnxJU9ixYtcjZb4rlt2za3piQzQUpzFKQ0hRAiA/k0RT0IKZPe3t6KipQafVB+J0WoKXpOeZQQ8a5rBQxF1Lds2VI4pmbRpFCj7eiGh4ed7cUXXxx1n0+QqsxESlMIITKQ0hTNxKkaG1Wmmng7ijYlHilBjZW//PJLZ3v99dcLxyX5nSH1SeeS7bnnnqPXKED7p9xQqj2n/Nlly5YVjkkBUwcs6lUg/ouUphBCZCClKVoKygMltWW7L6UUa5ickvcd1lhd5FQZNYK2oydszX1KXFlF/kvK53z06JGzWb9vSS/UWlRlR453ltIUQogMpDRFqxHyG9qa+JS44oWUmlWuFLUmxUuvSZBitK9BSpOU8oQJXryRqqRKKqssacRJSZejRneQamukNIUQIgMpTdGuhPpdkvqk/p+R84I5n6huSX1ayHdLndtJBX/11VfOZiPq1FmJshVKxi93pKokpDSFECIDKU0xnnBqqKTLUQEaw0tqMQr5Cf/1r38Vjk+cOOHWkAImxUv+yyVLljibnb9E1UX0miVKU3ODniClKYQQGUhpivFORfVJvkRSi+TTpHMp2m+vR13sabbQpUuXnI2qi8j3eebMmYr7mjNnjrOJ0ZHSFEKIDKQ0RSdi1afz11FneIqUk9KkvE/rm9y8ebNbQxHwkydPOtvt27edjfpp2hlBy5cvr7ivUehI/yUhpSmEEBlIaYrxTsWoL83EiXY+onWUk2nX2ch2SjzBk65FlT1UV27r86lbfIn/VapyFHTTFOOdam8AoVJCeoynlB27jgJNNJrjs88+czYqwaQgkm3sMXXqVLcmknQviujxXAghMpDSFIIJKdSSRHDHnTt3CsqVxhlTmeOsWbOcjZLUKUH/ypUrhWNKZF+5ciXsFlFy+xOkNIUQIgMpTSGYqLKqul2chdKL7t6962w2lSglbhdnX5PGhpQ0F6GAV0eqSkJKUwghMpDSFIKJKquQ+qSUIMvq1audrb+/39l+/PFHZ7t//76zrV+/vnB89epVtyYjei6f5hOkNIUQIgMpTSHqT0X1SQpv3759zkYt34hvvvnG2WxZZnQcSIn67EhVSUhpCiFEBlKaQtRGyNfX09NTsA0ODrrzKAJOLd+o9JHa1j377LMVr7Vu3brQ9UvQCF8hhBCjI6UpRG1E1VVBlZF/kdrFDQwMONupU6ecjWrPrZ/zF7/4hVtTg6pMqUOUpUVKUwghMpDSFKI52HZ0oTZzFy5ccDaqIX/qKa9/bBScRmwsWLDA2UgFR2vsOwEpTSGEyEBKU4jmULFGnSLglKdJ1UWkUm0Pz5kzZ7o1X3/9tbP19fXR9jrSf0lIaQohRAZSmkI0h4pKbfLkyaGOSeRzpLpy23eT8jRpHDD5TOXT/C9SmkIIkYGUphAtAnVfpy7tpDRpUJvN3aTRwtRjs2Ssr/I0nyClKYQQGUhpCjE2OOU2PDzsFs2ZM8fZjh075my2o1FK3l9J84bsHKGUSucGRfqGdoTylNIUQogMpDSFGBtCHd97enrcIop4v/zyy85mfZhUn37r1i1nox6bvb298mk+QUpTCCEymPD4cSg1TAhRR6ifJqlKqhIi3+dHH33kbFZZUnT+hx9+cLZFixY5244dO5wN6AjlKaUphBAZyKcpRHMoKEtSlTQjiCLlVP1DeZq2n+a0adPcGvKP9vb2OtvIyIizZfTiHFdIaQohRAZSmkI0h4r+vqVLlzo/Jym8qVOnOtvhw4edzc4ImjJlilvzzDPPOBvNUCd/KKnlTkA3TSFaBLoJUdCHBrCtWLHC2SZPnlw4pqASQY/6JaWVlo5IS9LjuRBCZCClKUSLQI/ixIMHD5zt4cOHzmZTjqgJManWGh7Px52qJKQ0hRAiAylNIVqEaAoPpQkR1HTYcu3atdC1xH+R0hRCiAykNIVoYaJ+TlKpR44cKRxTcrtNgE+JE+p37tzpbHZwW3d3t6LnQgghikhpCtHC0EAzsr377rvOdvv27cLxzZs33RpqzkFJ8DTqF0ZlhNrdlaxrG6Q0hRAiAylNIVoHp8BmzJjhlBo1CZ4/f76zff/994XjgwcPujVUcUTt4pYtW+Zs4G91e+3q6mprVUlIaQohRAZSmkK0MBQVpzpwGsVrVeT27dvdGhp3QfmdNLjNDmAj1drV1SWfphBCdDJSmkK0DqHZMxQ9p9rzxYsXF44vXbrk1jz99NPOtnz5cmejHE+rLGlf4xEpTSGEyEBKU4g2w47mTSmle/fuORv5IS2kNClPk1QkdT7qBKQ0hRAiAylNIVqHUEXNvHnz3CKqUbe2K1euuDWUk3n58mVno2Frwa5MbR0pJ6Q0hRAiAylNIdqMknxIZ5szZ07h+MaNG25NcPYPdnMfGBgoHPf19YWu1e5IaQohRAYTHj8OpYYJIcaGaEWNW2f7YtJsobNnzzob5WkuXbrU2YIjfOXTFEKITkY+TSHGARQ9t8qSVCVx9+5dZ7Nd2omg8mx7pDSFECIDKU0hWpu6+QSff/55Z7MR8JRSmj59urNRbfvkyZOtadz5LwkpTSGEyEBKU4j2gzqku0U24n369OnQxSM16yml9N1331XcV3d3d1R9tk3fTd00hWg/QilHNgl+5syZ7qRHjx4527lz55yNHtltcKgkUT56M2zJGyShx3MhhMhASlOIcYpt7BFp6pFSSlu3bnU2emSnJh5B2uZRnJDSFEKIDFRGKUT7UdU/LSWo37lzx9moyTEFmqZOnVo4toPWys5LbaQqCSlNIYTIQD5NIdqPUPTc+itpPMXNmzedjdThhQsXnG3JkiXlOxzHSGkKIUQGUppCjA+c+uzq6qro+1y4cKGz0ViMZ555puIGurq6Qgq43ZHSFEKIDKQ0hRinWJ8m+SpJVdK4Xqr2mTVrVuH44sWLTlX29va2daSckNIUQogMpDSFGKeU+BgLdHd3O3V469Ytt27KlCnOdvLkycLx+vXr3ZqRkZFQc5ESWlKlSmkKIUQGUppCdA4hVfntt9+GLmbX1VCL3lZIaQohRAaqPReic3D/7FSPfuLECWejbkgRZZmhPlvSf0lIaQohRAbyaQrRwVA9uu1elFJKEydOdDYbPd+wYYNbQwo1EtVvZaQ0hRAiAylNIToHp/CGh4dDQQ3yfb766quFYxi0lpOT2TZIaQohRAaKngvR2YRuANTN/fr164VjO5MoJY6eDw4OOltPTw+9bEv6PqU0hRAiA/k0hRi/FFRkSSTb2ch/SR3ebZQ9Ou2yZD56S6pKQkpTCCEykNIUYvxSUG8lkWzn06Qo+OzZs53N+jnv3r3r1pBPs90j6lKaQgiRgZSmEJ1D1akyFBk/cOBA4XjdunVuDanKkug57a0l/ZxKORJCVGRoaMjdKO7cuVM4pmDRiy++6GzDw8PORiM2UoveNPV4LoQQGUhpCtHZhG4AAwMDzmZTk0gt0uN5jc2Kx1x9SmkKIUQGCgQJ0dmQcqNRvBUvRL5KCvpQwnuJnzOyt6YrTylNIYTIQEpTiM4mlOpz584dt86qQ1KQVJJpo+4plTbsaMk0JClNIYTIQEpTiM4mpNwmT57sbOfPn694HiXF19gGTj5NIYRoJ6Q0hRAVoQFsW7duLRxTpHzPnj3ORpH4kZER578sGcAmn6YQQrQTqggSQlSElKClv7/f2Wj0L/k0S2rPCSlNIYRoJ+TTFEJUJNLi7eHDh24NKU3yfZLSDI7naHoup5SmEEJkIJ+mECJCVTcKUos0DpiUZquO9ZXSFEKIDOTTFEJEqKrjUFdXF+Vf1mtPY4KUphBCZCClKYSoCuuvHB4edqqSOr6T/3LGjBnRl1XtuRBCtBNSmkKICBWj51SfPnv27IZs5v+hPE0hhGhlpDSFEBEoMl44jtSnp8Q9Ngnq+l4yN6ipSGkKIUQGqggSQtSLqmeoU4/N4IRK+TSFEKKVkU9TCFEVQ0NDBZVH/kbyc1JNOVUJXb9+3dkmTZpUuF5Jd/eGIqUphBAZyKcphGgYFy9edDcYip7TLHSagGlzQcdCaeqmKYSolkhJo7vBUCrRd99952xUWgk3SQWChBCilVEgSAhRLRHV56ByS1KalHLUCm3lpDSFECID+TSFEI0kdIMJDlFz65RyJIQQLY6UphCikbgbDKlKSjmiJHgbeafzent7FT0XQohWQUpTCNFsQuqTfJpWadLojBTMFy1ZVxEpTSGEyEBKUwjRSKI3mFCzD2h87C5UElGX0hRCiLFAFUFCiEYSrUd3NoqM23r0sRh/IaUphBAZSGkKIcYcqkcnGxD1VdZNkUppCiFEBoqeCyGaTdX16EePHi0cb9q0KfqaUppCCDEWSGkKIRpJ1OcYrRIqnEvjNOi8vr4+KU0hhBgLpDSFEK1KxflCVHve6CohKU0hhMhASlMI0QpUXaNe59eU0hRCiHqiiiAhRCswFgpSXY6EEKLR/A+u8cyhAO1dJQAAAABJRU5ErkJggg==" id="image533696ecf2" transform="scale(1 -1) translate(0 -264.96)" x="314.64" y="-24.48" width="239.76" height="264.96"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -1531,7 +1531,7 @@ iVBORw0KGgoAAAANSUhEUgAAAU0AAAFwCAYAAAA8FCm3AAAvGElEQVR4nO2d649V5fXHH36FARSO3GQc - + @@ -1546,7 +1546,7 @@ iVBORw0KGgoAAAANSUhEUgAAAU0AAAFwCAYAAAA8FCm3AAAvGElEQVR4nO2d649V5fXHH36FARSO3GQc - + @@ -1579,42 +1579,42 @@ iVBORw0KGgoAAAANSUhEUgAAAU0AAAFwCAYAAAA8FCm3AAAvGElEQVR4nO2d649V5fXHH36FARSO3GQc - + - + - + - + - + - + @@ -1807,225 +1807,225 @@ z " style="fill: #ffffff"/> +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" id="image3237e6f85c" transform="scale(1 -1) translate(0 -266.4)" x="561.6" y="-23.04" width="257.04" height="266.4"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -2040,7 +2040,7 @@ iVBORw0KGgoAAAANSUhEUgAAAWUAAAFyCAYAAAAtVsuYAAAgoElEQVR4nO3d2Y8VVbTH8c08yDwLEkBR - + @@ -2055,7 +2055,7 @@ iVBORw0KGgoAAAANSUhEUgAAAWUAAAFyCAYAAAAtVsuYAAAgoElEQVR4nO3d2Y8VVbTH8c08yDwLEkBR - + @@ -2088,42 +2088,42 @@ iVBORw0KGgoAAAANSUhEUgAAAWUAAAFyCAYAAAAtVsuYAAAgoElEQVR4nO3d2Y8VVbTH8c08yDwLEkBR - + - + - + - + - + - + @@ -2260,12 +2260,12 @@ z " style="fill: #ffffff"/> +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" id="imagec2ca48501a" transform="scale(1 -1) translate(0 -264.96)" x="894.24" y="-24.48" width="217.44" height="264.96"/> - + @@ -2280,7 +2280,7 @@ iVBORw0KGgoAAAANSUhEUgAAAS4AAAFwCAYAAAAGxJbEAABRlUlEQVR4nO29d3wVZdD+Pem9Q0jvIXQI - + @@ -2295,7 +2295,7 @@ iVBORw0KGgoAAAANSUhEUgAAAS4AAAFwCAYAAAAGxJbEAABRlUlEQVR4nO29d3wVZdD+Pem9Q0jvIXQI - + @@ -2328,42 +2328,42 @@ iVBORw0KGgoAAAANSUhEUgAAAS4AAAFwCAYAAAAGxJbEAABRlUlEQVR4nO29d3wVZdD+Pem9Q0jvIXQI - + - + - + - + - + - + @@ -2483,18 +2483,18 @@ z " style="fill: #ffffff"/> +iVBORw0KGgoAAAANSUhEUgAAABMAAAGBCAYAAACEiYowAAABWElEQVR4nO3Yiw0CMRAD0YDov+QLNRit5BE7LsDal88F8br33jOU91TReNlnUCmzXSYzj8xymcw8Mv+pbMmaycwjs1wmM4/MchmXyZ2My3TN8nAn4zK5a7ZkMi6Tu2bcybhM7ppxJ+MyXbM83Mm4TO6aLZmMy+Sumcxy2ZIbwJ1MZh6ZebiTcZncNZNZLuMyvZt5ZOaRmWfJ3Zyd7HmeuTIuc8l1cje7ZWDm5NEAM93NOGDmkt3kMn1QumXuZp4tu+mD0i0DM93NODLzyCyXgZl+guLIzCOzXAZm+gmKIzOPzHKZzDz+F1Quk5lHZrlMZh6Z5TKZeWSWy2Tm2cL0p3u3TGYemT+ULfkDU2YcMJN7N5e8m0uY3s28bAmTe2iX3E2ZeRmX6ScoL9vB5E62ZAO27CaX6RvQLZOZx0NbLpOZx0NbLpOZx0NbLpOZx0Ob53XOGWsbZX4BVQam4NEPKDEAAAAASUVORK5CYII=" id="imageca11b983f8" transform="scale(1 -1) translate(0 -277.2)" x="822.24" y="-12.24" width="13.68" height="277.2"/> - - + @@ -2509,7 +2509,7 @@ L 3.5 0 - + @@ -2524,110 +2524,110 @@ L 3.5 0 - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -2655,13 +2655,13 @@ z " style="fill: #ffffff"/> +iVBORw0KGgoAAAANSUhEUgAAABMAAAGBCAYAAACEiYowAAAB+ElEQVR4nO2cgW3EMAwDbSej/Qi//yhNZ1BwwB9AagCiF1K0kxbdn/19FjT32ofSWpzSWuveZ3NixRwPi7lYTE6MdpPTE2OuFEwwtLu7OR0YE/zJ0A0wY6a4mYHZCpqO2k0tZoabD1tBmFZD+2JYTDIa9G6SYl7MujkX02KyTctpqUObcaA8GW56QxtyCwqpIHKd1AcKKOZ1E30RMzctKGbGrJtTMTNm3ZyKme+0oJjZzYZ2KlbM8RRzPjSm9epeN38r1hex+RTzhZgXsxU0HjhnpJgXszmbi4VgekPbM2AuFhLaEExvaDN20/vMGtr5hIQ2BXNt7vLe3ZxPQzsf2k1uA7xuik/0mDst2LQxu8mJ1c350AeKtWnR0IK/qmtoXwxcQSsktMUcjhqTExNXkNdN0gAxZshugpRqTHY3MS0Y86RUUN2cinndPOA1tG7Op5jzYTHJDRA3LfnMzG6GYGpDWzd/Kya+Hogx9x8ppsWsm2Oxy4vJusk9M3HTXuJzM6KCyHW6zwoJbQamt4LIphVjoudmRtPGYHpDG9G0ZGuIvx7AmF43tQaAz0z8UoFiet0kN0B8cWFDm1GO4otLSmgjPsaxTesNbcZXKji0mJYaE5PiDxRSDPyjPfpAAcUu7X/MOqABajdJTLGbnB76zOgKQjE5MRTzH6nyjgS9xeYCAAAAAElFTkSuQmCC" id="image50f5a39be0" transform="scale(1 -1) translate(0 -277.2)" x="1122.48" y="-12.24" width="13.68" height="277.2"/> - + @@ -2677,7 +2677,7 @@ iVBORw0KGgoAAAANSUhEUgAAABMAAAGBCAYAAACEiYowAAAB+ElEQVR4nO2cgW3EMAwDbSej/Qi//yhN - + @@ -2693,7 +2693,7 @@ iVBORw0KGgoAAAANSUhEUgAAABMAAAGBCAYAAACEiYowAAAB+ElEQVR4nO2cgW3EMAwDbSej/Qi//yhN - + @@ -2709,7 +2709,7 @@ iVBORw0KGgoAAAANSUhEUgAAABMAAAGBCAYAAACEiYowAAAB+ElEQVR4nO2cgW3EMAwDbSej/Qi//yhN - + @@ -2725,7 +2725,7 @@ iVBORw0KGgoAAAANSUhEUgAAABMAAAGBCAYAAACEiYowAAAB+ElEQVR4nO2cgW3EMAwDbSej/Qi//yhN - + @@ -2740,7 +2740,7 @@ iVBORw0KGgoAAAANSUhEUgAAABMAAAGBCAYAAACEiYowAAAB+ElEQVR4nO2cgW3EMAwDbSej/Qi//yhN - + @@ -2755,7 +2755,7 @@ iVBORw0KGgoAAAANSUhEUgAAABMAAAGBCAYAAACEiYowAAAB+ElEQVR4nO2cgW3EMAwDbSej/Qi//yhN - + @@ -2770,7 +2770,7 @@ iVBORw0KGgoAAAANSUhEUgAAABMAAAGBCAYAAACEiYowAAAB+ElEQVR4nO2cgW3EMAwDbSej/Qi//yhN - + @@ -2785,7 +2785,7 @@ iVBORw0KGgoAAAANSUhEUgAAABMAAAGBCAYAAACEiYowAAAB+ElEQVR4nO2cgW3EMAwDbSej/Qi//yhN - + @@ -2813,13 +2813,13 @@ z - + - + - + diff --git a/dev/fitting/fitting_intro/index.html b/dev/fitting/fitting_intro/index.html index b19e300..9eb6fbb 100644 --- a/dev/fitting/fitting_intro/index.html +++ b/dev/fitting/fitting_intro/index.html @@ -18,7 +18,7 @@ ylim=nothing, nbins=nothing, xwidth=nothing, - ywidth=nothing)

Main function for generating template Hess diagrams from a simple stellar population of stars from an isochrone, including photometric error and completeness.

Arguments

  • m_ini::AbstractVector{<:Number} is a vector containing the initial stellar masses of the stars from the isochrone.
  • mags::AbstractVector{<:AbstractVector{<:Number}} is a vector of vectors. Each constituent vector with index i should have length(mags[i]) == length(m_ini), representing the magnitudes of the isochrone stars in each of the magnitudes considered. In most cases, mags should contain 2 (if y-axis mag is also involved in the x-axis color) or 3 vectors.
  • mag_err_funcs must be an indexable object (e.g., a Vector or Tuple) that contains callables (e.g., a Function) to compute the 1σ photometric errors for the same filters provided in mags. Each callable must take a single argument (an apparent magnitude) and return a Number. The length mag_err_funcs must be equal to the length of mags.
  • y_index gives a valid index (e.g., an Int or CartesianIndex) into mags for the filter you want to have on the y-axis of the Hess diagram. For example, if the mags argument contains the B and V band magnitudes as mags=[B, V] and you want V on the y-axis, you would set y_index as 2.
  • color_indices is a length-2 indexable object giving the indices into mags that are to be used to compute the x-axis color. For example, if the mags argument contains the B and V band magnitudes as mags=[B, V], and you want B-V to be the x-axis color, then color_indices should be [1,2] or (1,2) or similar.
  • imf is a callable that takes an initial stellar mass as its sole argument and returns the (properly normalized) probability density of your initial mass function model. All the models from InitialMassFunctions.jl are valid for imf.
  • completeness_functions must be an indexable object (e.g., a Vector or Tuple) that contains callables (e.g., a Function) to compute the single-filter completeness fractions as a function of apparent magnitude. Each callable in this argument must correspond to the matching filter provided in mags.

Keyword Arguments

  • dmod::Number=0 is the distance modulus in magnitudes to apply to the input mags. Leave at 0 if you are providing apparent magnitudes in mags.
  • normalize_value::Number=1 is the total stellar mass of the population you wish to model.
  • binary_model::AbstractBinaryModel=NoBinaries() is the model to use for including binary systems. Currently only StarFormationHistories.NoBinaries and StarFormationHistories.RandomBinaryPairs are supported.
  • mean_mass::Number is the expectation value of the initial mass for a random star drawn from your provided imf. This will be computed for you if your provided imf is a valid continuous, univariate Distributions.Distribution object.
  • edges is a tuple of ranges defining the left-side edges of the bins along the x-axis (edges[1]) and the y-axis (edges[2]). Example: (-1.0:0.1:1.5, 22:0.1:27.2). If edges is provided, it overrides the following keyword arguments that offer other ways to specify the extent of the Hess diagram.
  • xlim is a length-2 indexable object (e.g., a Vector or Tuple) giving the lower and upper bounds on the x-axis corresponding to the provided colors array. Example: (-1.0, 1.5). This is only used if edges is not provided.
  • ylim is as xlim but for the y-axis corresponding to the provided mags array. Example (25.0, 20.0). This is only used if edges is not provided.
  • nbins::NTuple{2, <:Integer} is a 2-tuple of integers providing the number of bins to use along the x- and y-axes. This is only used if edges is not provided.
  • xwidth is the bin width along the x-axis for the colors array. This is only used if edges and nbins are not provided. Example: 0.1.
  • ywidth is as xwidth but for the y-axis corresponding to the provided mags array. Example: 0.1.

Returns

This method returns the Hess diagram as a StatsBase.Histogram; you should refer to the StatsBase documentation for more information. In short, if the output of this method is result, then the Hess diagram represented as a Matrix is available as result.weights (this is what you would want for fit_templates and similar functions) and the edges of the histogram are available as result.edges.

source

The user-provided functions returning magnitude errors (mag_err_funcs) and completeness values (completness_functions) given a star's intrinsic apparent magnitude are important for deriving good templates. These are typically derived from catalogs of artificial star tests. Some helper functions for constructing these are provided here.

We note that in many cases it can also be helpful to add in a foreground/background template that models contamination of the Hess diagram from stars not in your population of interest – this is often done using observations of parallel fields though there are several other possible methods.

Photometric catalogs can be processed into Hess diagrams meeting our formatting requirements with the method bin_cmd.

StarFormationHistories.bin_cmdFunction
result::StatsBase.Histogram =
+                       ywidth=nothing)

Main function for generating template Hess diagrams from a simple stellar population of stars from an isochrone, including photometric error and completeness.

Arguments

  • m_ini::AbstractVector{<:Number} is a vector containing the initial stellar masses of the stars from the isochrone.
  • mags::AbstractVector{<:AbstractVector{<:Number}} is a vector of vectors. Each constituent vector with index i should have length(mags[i]) == length(m_ini), representing the magnitudes of the isochrone stars in each of the magnitudes considered. In most cases, mags should contain 2 (if y-axis mag is also involved in the x-axis color) or 3 vectors.
  • mag_err_funcs must be an indexable object (e.g., a Vector or Tuple) that contains callables (e.g., a Function) to compute the 1σ photometric errors for the same filters provided in mags. Each callable must take a single argument (an apparent magnitude) and return a Number. The length mag_err_funcs must be equal to the length of mags.
  • y_index gives a valid index (e.g., an Int or CartesianIndex) into mags for the filter you want to have on the y-axis of the Hess diagram. For example, if the mags argument contains the B and V band magnitudes as mags=[B, V] and you want V on the y-axis, you would set y_index as 2.
  • color_indices is a length-2 indexable object giving the indices into mags that are to be used to compute the x-axis color. For example, if the mags argument contains the B and V band magnitudes as mags=[B, V], and you want B-V to be the x-axis color, then color_indices should be [1,2] or (1,2) or similar.
  • imf is a callable that takes an initial stellar mass as its sole argument and returns the (properly normalized) probability density of your initial mass function model. All the models from InitialMassFunctions.jl are valid for imf.
  • completeness_functions must be an indexable object (e.g., a Vector or Tuple) that contains callables (e.g., a Function) to compute the single-filter completeness fractions as a function of apparent magnitude. Each callable in this argument must correspond to the matching filter provided in mags.

Keyword Arguments

  • dmod::Number=0 is the distance modulus in magnitudes to apply to the input mags. Leave at 0 if you are providing apparent magnitudes in mags.
  • normalize_value::Number=1 is the total stellar mass of the population you wish to model.
  • binary_model::AbstractBinaryModel=NoBinaries() is the model to use for including binary systems. Currently only StarFormationHistories.NoBinaries and StarFormationHistories.RandomBinaryPairs are supported.
  • mean_mass::Number is the expectation value of the initial mass for a random star drawn from your provided imf. This will be computed for you if your provided imf is a valid continuous, univariate Distributions.Distribution object.
  • edges is a tuple of ranges defining the left-side edges of the bins along the x-axis (edges[1]) and the y-axis (edges[2]). Example: (-1.0:0.1:1.5, 22:0.1:27.2). If edges is provided, it overrides the following keyword arguments that offer other ways to specify the extent of the Hess diagram.
  • xlim is a length-2 indexable object (e.g., a Vector or Tuple) giving the lower and upper bounds on the x-axis corresponding to the provided colors array. Example: (-1.0, 1.5). This is only used if edges is not provided.
  • ylim is as xlim but for the y-axis corresponding to the provided mags array. Example (25.0, 20.0). This is only used if edges is not provided.
  • nbins::NTuple{2, <:Integer} is a 2-tuple of integers providing the number of bins to use along the x- and y-axes. This is only used if edges is not provided.
  • xwidth is the bin width along the x-axis for the colors array. This is only used if edges and nbins are not provided. Example: 0.1.
  • ywidth is as xwidth but for the y-axis corresponding to the provided mags array. Example: 0.1.

Returns

This method returns the Hess diagram as a StatsBase.Histogram; you should refer to the StatsBase documentation for more information. In short, if the output of this method is result, then the Hess diagram represented as a Matrix is available as result.weights (this is what you would want for fit_templates and similar functions) and the edges of the histogram are available as result.edges.

source

The user-provided functions returning magnitude errors (mag_err_funcs) and completeness values (completness_functions) given a star's intrinsic apparent magnitude are important for deriving good templates. These are typically derived from catalogs of artificial star tests. Some helper functions for constructing these are provided here.

We note that in many cases it can also be helpful to add in a foreground/background template that models contamination of the Hess diagram from stars not in your population of interest – this is often done using observations of parallel fields though there are several other possible methods.

Photometric catalogs can be processed into Hess diagrams meeting our formatting requirements with the method bin_cmd.

StarFormationHistories.bin_cmdFunction
result::StatsBase.Histogram =
    bin_cmd(colors::AbstractVector{<:Number},
            mags::AbstractVector{<:Number};
            weights::AbstractVector{<:Number} = ones(promote_type(eltype(colors),
@@ -28,6 +28,6 @@
            ylim   = extrema(mags),
            nbins  = nothing,
            xwidth = nothing,
-           ywidth = nothing)

Returns a StatsBase.Histogram type containing the Hess diagram from the provided x-axis photometric colors and y-axis photometric magnitudes mags. These must all be vectors equal in length. You can either specify the bin edges directly via the edges keyword (e.g., edges = (range(-0.5, 1.6, length=100), range(17.0, 26.0, length=100))), or you can set the x- and y-limits via xlim and ylim and the number of bins as nbins, or you can omit nbins and instead pass the bin width in the x and y directions, xwidth and ywidth. See below for more info on the keyword arguments. To plot this with PyPlot.jl you should do PyPlot.imshow(permutedims(result.weights), origin="lower", extent=(extrema(result.edges[1])..., extrema(result.edges[2]), kws...) where kws... are any other keyword arguments you wish to pass to PyPlot.imshow.

Keyword Arguments

  • weights::AbstractVector{<:Number} is a array of length equal to colors and mags that contains the probabilistic weights associated with each point. This is passed to StatsBase.fit as StatsBase.Weights(weights). The following keyword arguments are passed to StarFormationHistories.calculate_edges to determine the bin edges of the histogram.
  • edges is a tuple of ranges defining the left-side edges of the bins along the x-axis (edges[1]) and the y-axis (edges[2]). Example: (-1.0:0.1:1.5, 22:0.1:27.2). If edges is provided, weights is the only other keyword that will be read; edges supercedes the other construction methods.
  • xlim is a length-2 indexable object (e.g., a vector or tuple) giving the lower and upper bounds on the x-axis corresponding to the provided colors array. Example: [-1.0, 1.5]. This is only used if edges is not provided.
  • ylim is like xlim but for the y-axis corresponding to the provided mags array. Example [25.0, 20.0]. This is only used if edges is not provided.
  • nbins::NTuple{2, <:Integer} is a 2-tuple of integers providing the number of bins to use along the x- and y-axes. This is only used if edges is not provided.
  • xwidth is the bin width along the x-axis for the colors array. This is only used if edges and nbins are not provided. Example: 0.1.
  • ywidth is like xwidth but for the y-axis corresponding to the provided mags array. Example: 0.1.
source

A Note on Array Formatting

It is expected that the user will typically have model templates stored as two-dimensional matrices as these are the obvious choice for representing a binned two-dimensional histogram. We fully support supplying the list of model templates as a list of matrices (e.g., a Vector{Matrix{<:Number}}) to the fitting functions discussed below. The important computational kernels composite! and ∇loglikelihood! have custom loops for these input types.

However, additional optimizations are possible by flattening the data. By flattening each matrix in the list of model templates into a column vector and concatenating them such that the list of model templates becomes a single matrix, we can compute the complex model Hess diagram as a single matrix-vector product rather than using a custom loop. The same optimization can be made when computing the gradient of the loglikelihood (discussed more below). The majority of all computation for the fitting methods below is spent in these two functions, so optimizing their performance translates directly to improved fitting runtimes. With this flattened memory arrangement we can use the highly optimized LinearAlgbera.mul! method to do the in-place matrix-vector product. This will typically be translated into a call to a BLAS function like gemv!. As such, we can benefit from Julia's ability to switch BLAS implementations at runtime to use Intel's Math Kernel Library, Apple's Accelerate, and others.

Most of the fitting methods below support both the natural and flattened data layouts. We provide the stack_models method to produce the optimized layout for the list of model templates.

StarFormationHistories.stack_modelsFunction
stack_models(models::AbstractVector{<:AbstractMatrix{<:Number}}) = reduce(hcat,map(vec,models))

Transforms a vector of matrices into a single matrix, with each matrix from models being transcribed into a single column in the output matrix. This data layout enables more efficient calculations in some of our internal functions like composite! and ∇loglikelihood!.

Examples

julia> stack_models([rand(5,5) for i in 1:10])
+           ywidth = nothing)

Returns a StatsBase.Histogram type containing the Hess diagram from the provided x-axis photometric colors and y-axis photometric magnitudes mags. These must all be vectors equal in length. You can either specify the bin edges directly via the edges keyword (e.g., edges = (range(-0.5, 1.6, length=100), range(17.0, 26.0, length=100))), or you can set the x- and y-limits via xlim and ylim and the number of bins as nbins, or you can omit nbins and instead pass the bin width in the x and y directions, xwidth and ywidth. See below for more info on the keyword arguments. To plot this with PyPlot.jl you should do PyPlot.imshow(permutedims(result.weights), origin="lower", extent=(extrema(result.edges[1])..., extrema(result.edges[2]), kws...) where kws... are any other keyword arguments you wish to pass to PyPlot.imshow.

Keyword Arguments

  • weights::AbstractVector{<:Number} is a array of length equal to colors and mags that contains the probabilistic weights associated with each point. This is passed to StatsBase.fit as StatsBase.Weights(weights). The following keyword arguments are passed to StarFormationHistories.calculate_edges to determine the bin edges of the histogram.
  • edges is a tuple of ranges defining the left-side edges of the bins along the x-axis (edges[1]) and the y-axis (edges[2]). Example: (-1.0:0.1:1.5, 22:0.1:27.2). If edges is provided, weights is the only other keyword that will be read; edges supercedes the other construction methods.
  • xlim is a length-2 indexable object (e.g., a vector or tuple) giving the lower and upper bounds on the x-axis corresponding to the provided colors array. Example: [-1.0, 1.5]. This is only used if edges is not provided.
  • ylim is like xlim but for the y-axis corresponding to the provided mags array. Example [25.0, 20.0]. This is only used if edges is not provided.
  • nbins::NTuple{2, <:Integer} is a 2-tuple of integers providing the number of bins to use along the x- and y-axes. This is only used if edges is not provided.
  • xwidth is the bin width along the x-axis for the colors array. This is only used if edges and nbins are not provided. Example: 0.1.
  • ywidth is like xwidth but for the y-axis corresponding to the provided mags array. Example: 0.1.
source

A Note on Array Formatting

It is expected that the user will typically have model templates stored as two-dimensional matrices as these are the obvious choice for representing a binned two-dimensional histogram. We fully support supplying the list of model templates as a list of matrices (e.g., a Vector{Matrix{<:Number}}) to the fitting functions discussed below. The important computational kernels composite! and ∇loglikelihood! have custom loops for these input types.

However, additional optimizations are possible by flattening the data. By flattening each matrix in the list of model templates into a column vector and concatenating them such that the list of model templates becomes a single matrix, we can compute the complex model Hess diagram as a single matrix-vector product rather than using a custom loop. The same optimization can be made when computing the gradient of the loglikelihood (discussed more below). The majority of all computation for the fitting methods below is spent in these two functions, so optimizing their performance translates directly to improved fitting runtimes. With this flattened memory arrangement we can use the highly optimized LinearAlgbera.mul! method to do the in-place matrix-vector product. This will typically be translated into a call to a BLAS function like gemv!. As such, we can benefit from Julia's ability to switch BLAS implementations at runtime to use Intel's Math Kernel Library, Apple's Accelerate, and others.

Most of the fitting methods below support both the natural and flattened data layouts. We provide the stack_models method to produce the optimized layout for the list of model templates.

StarFormationHistories.stack_modelsFunction
stack_models(models::AbstractVector{<:AbstractMatrix{<:Number}}) = reduce(hcat,map(vec,models))

Transforms a vector of matrices into a single matrix, with each matrix from models being transcribed into a single column in the output matrix. This data layout enables more efficient calculations in some of our internal functions like composite! and ∇loglikelihood!.

Examples

julia> stack_models([rand(5,5) for i in 1:10])
 25×10 Matrix{Float64}:
-...
source

A Note on Threading

While generally the methods using BLAS routines offer significant performance improvements, there is a caveat when multithreading from within Julia. By default Julia will allow BLAS to use multiple threads even if Julia itself is started with a single thread (i.e., by running julia -t 1). BLAS threads do not compose with Julia threads. That is, if you start Julia with N>1 threads (julia -t N) and write a threaded workload where each Julia thread is doing BLAS operations concurrently, you can easily oversubscribe the CPU. Specific recommendations vary depending on BLAS vendor (see this page and the linked discourse threads), but generally this package is in the regime of doing many small calculations that do not individually benefit much from BLAS threading (e.g., performance for OpenBLAS with 8 threads is only ~2x the 1 thread performance). As such it is often sufficient to set BLAS to use a single thread (via LinearAlgbera.BLAS.set_num_threads(1) or environment variables; see above link).

+...source

A Note on Threading

While generally the methods using BLAS routines offer significant performance improvements, there is a caveat when multithreading from within Julia. By default Julia will allow BLAS to use multiple threads even if Julia itself is started with a single thread (i.e., by running julia -t 1). BLAS threads do not compose with Julia threads. That is, if you start Julia with N>1 threads (julia -t N) and write a threaded workload where each Julia thread is doing BLAS operations concurrently, you can easily oversubscribe the CPU. Specific recommendations vary depending on BLAS vendor (see this page and the linked discourse threads), but generally this package is in the regime of doing many small calculations that do not individually benefit much from BLAS threading (e.g., performance for OpenBLAS with 8 threads is only ~2x the 1 thread performance). As such it is often sufficient to set BLAS to use a single thread (via LinearAlgbera.BLAS.set_num_threads(1) or environment variables; see above link).

diff --git a/dev/fitting/fixed_amr/index.html b/dev/fitting/fixed_amr/index.html index 7fa6c38..dd031ef 100644 --- a/dev/fitting/fixed_amr/index.html +++ b/dev/fitting/fixed_amr/index.html @@ -14,4 +14,4 @@ relweights::AbstractVector{<:Number}; relweightsmin::Number=0, x0=construct_x0_mdf(logAge, convert(S,13.7)), - kws...) where S <: Number

Method that fits a linear combination of the provided Hess diagrams models to the observed Hess diagram data, under an externally-imposed age-metallicity relation (AMR) and/or metallicity distribution function (MDF). As such, a number of coefficients equal to length(unique(logAge)) are returned; that is, only one coefficient is derived per unique entry in logAge.

The second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.

Arguments

  • models::AbstractVector{<:AbstractMatrix{<:Number}} is a vector of equal-sized matrices that represent the template Hess diagrams for the simple stellar populations that compose the observed Hess diagram.
  • data::AbstractMatrix{<:Number} is the Hess diagram for the observed data.
  • logAge::AbstractVector{<:Number} is the vector containing the effective ages of the stellar populations used to create the templates in models, in units of log10(age [yr]). For example, if a population has an age of 1 Myr, its entry in logAge should be log10(10^6) = 6.0.
  • metallicities::AbstractVector{<:Number} is the vector containing the effective metallicities of the stellar populations used to create the templates in models. This is most commonly a logarithmic abundance like [M/H] or [Fe/H], but you could use a linear abundance like the metal mass fraction Z if you wanted to. There are some notes on the Wikipedia that might be useful.
  • relweights::AbstractVector{<:Number} is a vector of length equal to that of models which contains the relative weights to apply to each model Hess diagram resulting from an externally-imposed age-metallicity relation and/or metallicity distribution function. Additional details on how to create these weights is provided in the notes below and in the online documentation.

Keyword Arguments

  • relweightsmin truncates the input list of models based on the provided relweights, providing a speedup at the cost of precision by removing models that contribute least to the overall composite model. By default, no truncation of the input is performed and all provided models are used in the fit. We recommend this only be increased when fitting performance begins to impact workflow (e.g., when running massive Monte Carlo experiments). See StarFormationHistories.truncate_relweights for implementation details.
  • x0 is the vector of initial guesses for the stellar mass coefficients per unique entry in logAge. You should basically always be calculating and passing this keyword argument. We provide StarFormationHistories.construct_x0_mdf to prepare x0 assuming constant star formation rate, which is typically a good initial guess.

Other kws... are passed to Optim.options to set things like convergence criteria for the optimization.

Notes

  • All metallicity-related weighting of the models is assumed to be captured in the provided relweights vector, which has the same length as the logAge, metallicities, and models vectors. Each entry in relweights is assumed to be a relative weight for the corresponding model. For example, for the model Hess diagram models[i], with log10(age [yr]) = logAge[i] and metallicity metallicities[i], the relative weight due to the model's age and metallicity w(logAge[i], metallicities[i]) is assumed to be relweights[i]. The sum of all relweights for each unique entry in logAge should be 1; i.e., the following condition should be met: all( sum(relweights[logAge .== la]) ≈ 1 for la in unique(logAge)). If this is not the case, this function will issue a warning and attempt to renormalize relweights by mutating the vector in place. More information on preparation of the relweights for input to this method is provided in our online documentation.
  • This function is designed to work best with a "grid" of stellar models, defined by the outer product of N unique entries in logAge and M unique entries in metallicities. See the examples for more information on usage.
source + kws...) where S <: Number

Method that fits a linear combination of the provided Hess diagrams models to the observed Hess diagram data, under an externally-imposed age-metallicity relation (AMR) and/or metallicity distribution function (MDF). As such, a number of coefficients equal to length(unique(logAge)) are returned; that is, only one coefficient is derived per unique entry in logAge.

The second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.

Arguments

  • models::AbstractVector{<:AbstractMatrix{<:Number}} is a vector of equal-sized matrices that represent the template Hess diagrams for the simple stellar populations that compose the observed Hess diagram.
  • data::AbstractMatrix{<:Number} is the Hess diagram for the observed data.
  • logAge::AbstractVector{<:Number} is the vector containing the effective ages of the stellar populations used to create the templates in models, in units of log10(age [yr]). For example, if a population has an age of 1 Myr, its entry in logAge should be log10(10^6) = 6.0.
  • metallicities::AbstractVector{<:Number} is the vector containing the effective metallicities of the stellar populations used to create the templates in models. This is most commonly a logarithmic abundance like [M/H] or [Fe/H], but you could use a linear abundance like the metal mass fraction Z if you wanted to. There are some notes on the Wikipedia that might be useful.
  • relweights::AbstractVector{<:Number} is a vector of length equal to that of models which contains the relative weights to apply to each model Hess diagram resulting from an externally-imposed age-metallicity relation and/or metallicity distribution function. Additional details on how to create these weights is provided in the notes below and in the online documentation.

Keyword Arguments

  • relweightsmin truncates the input list of models based on the provided relweights, providing a speedup at the cost of precision by removing models that contribute least to the overall composite model. By default, no truncation of the input is performed and all provided models are used in the fit. We recommend this only be increased when fitting performance begins to impact workflow (e.g., when running massive Monte Carlo experiments). See StarFormationHistories.truncate_relweights for implementation details.
  • x0 is the vector of initial guesses for the stellar mass coefficients per unique entry in logAge. You should basically always be calculating and passing this keyword argument. We provide StarFormationHistories.construct_x0_mdf to prepare x0 assuming constant star formation rate, which is typically a good initial guess.

Other kws... are passed to Optim.options to set things like convergence criteria for the optimization.

Notes

  • All metallicity-related weighting of the models is assumed to be captured in the provided relweights vector, which has the same length as the logAge, metallicities, and models vectors. Each entry in relweights is assumed to be a relative weight for the corresponding model. For example, for the model Hess diagram models[i], with log10(age [yr]) = logAge[i] and metallicity metallicities[i], the relative weight due to the model's age and metallicity w(logAge[i], metallicities[i]) is assumed to be relweights[i]. The sum of all relweights for each unique entry in logAge should be 1; i.e., the following condition should be met: all( sum(relweights[logAge .== la]) ≈ 1 for la in unique(logAge)). If this is not the case, this function will issue a warning and attempt to renormalize relweights by mutating the vector in place. More information on preparation of the relweights for input to this method is provided in our online documentation.
  • This function is designed to work best with a "grid" of stellar models, defined by the outer product of N unique entries in logAge and M unique entries in metallicities. See the examples for more information on usage.
source diff --git a/dev/fitting/internals/index.html b/dev/fitting/internals/index.html index 4f9a50b..8b4e4f9 100644 --- a/dev/fitting/internals/index.html +++ b/dev/fitting/internals/index.html @@ -7,7 +7,7 @@ julia> coeffs = rand(length(models)); julia> composite!(C, coeffs, models); julia> C ≈ sum( coeffs .* models) -truesource
composite!(composite::AbstractVector{<:Number},
+true
source
composite!(composite::AbstractVector{<:Number},
            coeffs::AbstractVector{<:Number},
            models::AbstractMatrix{<:Number})

Updates the composite vector with the matrix-vector product of models * coeffs. This is equation 1 in Dolphin 2002, $m_i = \sum_j \, r_j \, c_{i,j}$.

Examples

julia> hist_size = (5,10);
 julia> models = reduce(hcat,rand(prod(hist_size)) for i in 1:20);
@@ -15,11 +15,11 @@
 julia> C = zeros(length(axes(models,1)));
 julia> composite!(C, coeffs, models);
 julia> C ≈ models * coeffs
-true

Notes

While the other call signature for this function more closely mirrors the natural data structure for Hess diagrams (2D matrices for composite and each entry in models), this method operates on the same data but flattened. Thus composite becomes a vector rather than a matrix and models becomes a single matrix rather than a vector of matrices. The method StarFormationHistories.stack_models is provided to stack the models into this format. This data layout enables us to use the highly optimized LinearAlgebra.mul! function to perform the matrix-vector product which typically achieves >30% speedup relative to the more natural formulation. Additionally, as mul! will typically call to a BLAS matrix-vector product function like gemv! for our use-case, we can switch out Julia's default OpenBLAS at runtime for other BLAS libraries with Julia bindings like MKL and Apple Accelerate, enabling even greater performance improvements.

source
StarFormationHistories.loglikelihoodFunction
loglikelihood(composite::AbstractArray{<:Number}, data::AbstractArray{<:Number})

Returns the logarithm of the Poisson likelihood ratio given by equation 10 in Dolphin 2002,

\[\text{ln} \, \mathscr{L} = \sum_i -m_i + n_i \times \left( 1 - \text{ln} \, \left( \frac{n_i}{m_i} \right) \right)\]

with composite being the complex Hess model diagram $m_i$ (see StarFormationHistories.composite!) and data being the observed Hess diagram $n_i$.

Performance Notes

  • ~18.57 μs for composite=Matrix{Float64}(undef,99,99) and data=similar(composite).
  • ~20 μs for composite=Matrix{Float64}(undef,99,99) and data=Matrix{Int64}(undef,99,99).
  • ~9.3 μs for composite=Matrix{Float32}(undef,99,99) and data=similar(composite).
  • ~9.6 μs for composite=Matrix{Float32}(undef,99,99) and data=Matrix{Int64}(undef,99,99).
source
loglikelihood(coeffs::AbstractVector{<:Number}, models::AbstractVector{T}, data::AbstractMatrix{<:Number}) where T <: AbstractMatrix{<:Number}
-loglikelihood(coeffs::AbstractVector{<:Number}, models::AbstractMatrix{<:Number}, data::AbstractVector{<:Number})

Returns the logarithm of the Poisson likelihood ratio, but constructs the complex Hess diagram model as sum(coeffs .* models) rather than taking composite directly as an argument. Second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.

source
StarFormationHistories.∇loglikelihoodFunction
∇loglikelihood(model::AbstractArray{<:Number}, composite::AbstractArray{<:Number}, data::AbstractArray{<:Number})

Returns the partial derivative of the logarithm of the Poisson likelihood ratio (StarFormationHistories.loglikelihood) with respect to the coefficient $r_j$ on the provided model. If the complex Hess diagram model is $m_i = \sum_j \, r_j \, c_{i,j}$, then model is $c_{i,j}$, and this function computes the partial derivative of $\text{log} \, \mathscr{L}$ with respect to the coefficient $r_j$. This is given by equation 21 in Dolphin 2002,

\[\frac{\partial \, \text{log} \, \mathscr{L}}{\partial \, r_j} = \sum_i c_{i,j} \left( \frac{n_i}{m_i} - 1 \right)\]

where $n_i$ is bin $i$ of the observed Hess diagram data.

Performance Notes

  • ~4.1 μs for model, composite, data all being Matrix{Float64}(undef,99,99).
  • ~1.3 μs for model, composite, data all being Matrix{Float32}(undef,99,99).
source
∇loglikelihood(models::AbstractVector{T}, composite::AbstractMatrix{<:Number}, data::AbstractMatrix{<:Number}) where T <: AbstractMatrix{<:Number}
-∇loglikelihood(models::AbstractMatrix{<:Number}, composite::AbstractVector{<:Number}, data::AbstractVector{<:Number})

Computes the gradient of the logarithm of the Poisson likelihood ratio with respect to the coefficients by calling the single-model ∇loglikelihood for every model in models. Second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.

source
∇loglikelihood(coeffs::AbstractVector{<:Number}, models::AbstractVector{T}, data::AbstractMatrix{<:Number}) where T <: AbstractMatrix{<:Number}
-∇loglikelihood(coeffs::AbstractVector{<:Number}, models::AbstractMatrix{<:Number}, data::AbstractVector{<:Number})

Forms the composite matrix from coefficients coeffs and model templates models and returns the gradient of the loglikelihood with respect to the coefficients. Second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.

source
StarFormationHistories.∇loglikelihood!Function
 ∇loglikelihood!(G::AbstractVector, composite::AbstractMatrix{<:Number}, models::AbstractVector{S}, data::AbstractMatrix{<:Number}) where S <: AbstractMatrix{<:Number}

Efficiently computes the gradient of StarFormationHistories.loglikelihood with respect to all coefficients by updating G with the gradient. This will overwrite composite with the result of 1 .- (data ./ composite) so it shouldn't be reused after being passed to this function.

Arguments

  • G::AbstractVector is the vector that will be mutated in-place with the computed gradient values.
  • models::AbstractVector{<:AbstractMatrix{<:Number}} is the vector of matrices giving the model Hess diagrams.
  • composite::AbstractMatrix{<:Number} is a matrix that contains the composite model sum(coeffs .* models).
  • data::AbstractMatrix{<:Number} contains the observed Hess diagram that is being fit.
source
G = ∇loglikelihood!(G::AbstractVector, composite::AbstractVector{<:Number}, models::AbstractMatrix{<:Number}, data::AbstractVector{<:Number})

Updates and returns G with the gradient of the loglikelihood with respect to all coefficients. This call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.

source
StarFormationHistories.fg!Function
-logL = fg!(F, G, coeffs::AbstractVector{<:Number}, models::AbstractVector{T}, data::AbstractMatrix{<:Number}, composite::AbstractMatrix{<:Number}) where T <: AbstractMatrix{<:Number}
--logL = fg!(F, G, coeffs::AbstractVector{<:Number}, models::AbstractMatrix{<:Number}, data::AbstractVector{<:Number}, composite::AbstractVector{<:Number})

Computes -loglikelihood and its gradient simultaneously for use with Optim.jl and other optimization APIs. See documentation here. Second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.

source
StarFormationHistories.truncate_relweightsFunction
keep_idx::Vector{Int} = truncate_relweights(relweightsmin::Number, relweights::AbstractVector{<:Number}, logAge::AbstractVector{<:Number})

Method to truncate an isochrone grid with log10(age [yr]) values logAge and relative weights relweights due to an age-metallicity relation to only include models with relweights greater than relweightsmin times the maximum relative weight for each unique entry in logAge. The input vectors are the same as those for StarFormationHistories.fixed_amr, which includes more information. Returns a vector of the indices into relweights and logAge of the isochrone models whose relative weights are significant given the provided relweightsmin.

Examples

When using a fixed input age-metallicity relation as enabled by, for example, StarFormationHistories.fixed_amr, only the star formation rate (or total stellar mass) coefficients need to be fit, as the metallicity distribution is no longer a free parameter in the model. As such, the relative weights of each model with identical logAge but different metallicities only need to be computed once at the start of the optimization. As the metallicity distribution is not a free parameter, it is also possible to truncate the list of models to only those that contribute significantly to the final composite model to improve runtime performance. That is what this method does.

A simple isochrone grid will be two-dimensional, encompassing age and metallicity. Consider a subset of the model grid with the same age such that unique(logAge) = [10.0] but a series of different metallicities, metallicities = -2.5:0.25:0. If we model the metallicity distribution function for this age as having a mean [M/H] of -2.0 and a Gaussian spread of 0.2 dex, then the relative weights of these models can be approximated as

import Distributions: Normal, pdf
+true

Notes

While the other call signature for this function more closely mirrors the natural data structure for Hess diagrams (2D matrices for composite and each entry in models), this method operates on the same data but flattened. Thus composite becomes a vector rather than a matrix and models becomes a single matrix rather than a vector of matrices. The method StarFormationHistories.stack_models is provided to stack the models into this format. This data layout enables us to use the highly optimized LinearAlgebra.mul! function to perform the matrix-vector product which typically achieves >30% speedup relative to the more natural formulation. Additionally, as mul! will typically call to a BLAS matrix-vector product function like gemv! for our use-case, we can switch out Julia's default OpenBLAS at runtime for other BLAS libraries with Julia bindings like MKL and Apple Accelerate, enabling even greater performance improvements.

source
StarFormationHistories.loglikelihoodFunction
loglikelihood(composite::AbstractArray{<:Number}, data::AbstractArray{<:Number})

Returns the logarithm of the Poisson likelihood ratio given by equation 10 in Dolphin 2002,

\[\text{ln} \, \mathscr{L} = \sum_i -m_i + n_i \times \left( 1 - \text{ln} \, \left( \frac{n_i}{m_i} \right) \right)\]

with composite being the complex Hess model diagram $m_i$ (see StarFormationHistories.composite!) and data being the observed Hess diagram $n_i$.

Performance Notes

  • ~18.57 μs for composite=Matrix{Float64}(undef,99,99) and data=similar(composite).
  • ~20 μs for composite=Matrix{Float64}(undef,99,99) and data=Matrix{Int64}(undef,99,99).
  • ~9.3 μs for composite=Matrix{Float32}(undef,99,99) and data=similar(composite).
  • ~9.6 μs for composite=Matrix{Float32}(undef,99,99) and data=Matrix{Int64}(undef,99,99).
source
loglikelihood(coeffs::AbstractVector{<:Number}, models::AbstractVector{T}, data::AbstractMatrix{<:Number}) where T <: AbstractMatrix{<:Number}
+loglikelihood(coeffs::AbstractVector{<:Number}, models::AbstractMatrix{<:Number}, data::AbstractVector{<:Number})

Returns the logarithm of the Poisson likelihood ratio, but constructs the complex Hess diagram model as sum(coeffs .* models) rather than taking composite directly as an argument. Second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.

source
StarFormationHistories.∇loglikelihoodFunction
∇loglikelihood(model::AbstractArray{<:Number}, composite::AbstractArray{<:Number}, data::AbstractArray{<:Number})

Returns the partial derivative of the logarithm of the Poisson likelihood ratio (StarFormationHistories.loglikelihood) with respect to the coefficient $r_j$ on the provided model. If the complex Hess diagram model is $m_i = \sum_j \, r_j \, c_{i,j}$, then model is $c_{i,j}$, and this function computes the partial derivative of $\text{log} \, \mathscr{L}$ with respect to the coefficient $r_j$. This is given by equation 21 in Dolphin 2002,

\[\frac{\partial \, \text{log} \, \mathscr{L}}{\partial \, r_j} = \sum_i c_{i,j} \left( \frac{n_i}{m_i} - 1 \right)\]

where $n_i$ is bin $i$ of the observed Hess diagram data.

Performance Notes

  • ~4.1 μs for model, composite, data all being Matrix{Float64}(undef,99,99).
  • ~1.3 μs for model, composite, data all being Matrix{Float32}(undef,99,99).
source
∇loglikelihood(models::AbstractVector{T}, composite::AbstractMatrix{<:Number}, data::AbstractMatrix{<:Number}) where T <: AbstractMatrix{<:Number}
+∇loglikelihood(models::AbstractMatrix{<:Number}, composite::AbstractVector{<:Number}, data::AbstractVector{<:Number})

Computes the gradient of the logarithm of the Poisson likelihood ratio with respect to the coefficients by calling the single-model ∇loglikelihood for every model in models. Second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.

source
∇loglikelihood(coeffs::AbstractVector{<:Number}, models::AbstractVector{T}, data::AbstractMatrix{<:Number}) where T <: AbstractMatrix{<:Number}
+∇loglikelihood(coeffs::AbstractVector{<:Number}, models::AbstractMatrix{<:Number}, data::AbstractVector{<:Number})

Forms the composite matrix from coefficients coeffs and model templates models and returns the gradient of the loglikelihood with respect to the coefficients. Second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.

source
StarFormationHistories.∇loglikelihood!Function
 ∇loglikelihood!(G::AbstractVector, composite::AbstractMatrix{<:Number}, models::AbstractVector{S}, data::AbstractMatrix{<:Number}) where S <: AbstractMatrix{<:Number}

Efficiently computes the gradient of StarFormationHistories.loglikelihood with respect to all coefficients by updating G with the gradient. This will overwrite composite with the result of 1 .- (data ./ composite) so it shouldn't be reused after being passed to this function.

Arguments

  • G::AbstractVector is the vector that will be mutated in-place with the computed gradient values.
  • models::AbstractVector{<:AbstractMatrix{<:Number}} is the vector of matrices giving the model Hess diagrams.
  • composite::AbstractMatrix{<:Number} is a matrix that contains the composite model sum(coeffs .* models).
  • data::AbstractMatrix{<:Number} contains the observed Hess diagram that is being fit.
source
G = ∇loglikelihood!(G::AbstractVector, composite::AbstractVector{<:Number}, models::AbstractMatrix{<:Number}, data::AbstractVector{<:Number})

Updates and returns G with the gradient of the loglikelihood with respect to all coefficients. This call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.

source
StarFormationHistories.fg!Function
-logL = fg!(F, G, coeffs::AbstractVector{<:Number}, models::AbstractVector{T}, data::AbstractMatrix{<:Number}, composite::AbstractMatrix{<:Number}) where T <: AbstractMatrix{<:Number}
+-logL = fg!(F, G, coeffs::AbstractVector{<:Number}, models::AbstractMatrix{<:Number}, data::AbstractVector{<:Number}, composite::AbstractVector{<:Number})

Computes -loglikelihood and its gradient simultaneously for use with Optim.jl and other optimization APIs. See documentation here. Second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.

source
StarFormationHistories.truncate_relweightsFunction
keep_idx::Vector{Int} = truncate_relweights(relweightsmin::Number, relweights::AbstractVector{<:Number}, logAge::AbstractVector{<:Number})

Method to truncate an isochrone grid with log10(age [yr]) values logAge and relative weights relweights due to an age-metallicity relation to only include models with relweights greater than relweightsmin times the maximum relative weight for each unique entry in logAge. The input vectors are the same as those for StarFormationHistories.fixed_amr, which includes more information. Returns a vector of the indices into relweights and logAge of the isochrone models whose relative weights are significant given the provided relweightsmin.

Examples

When using a fixed input age-metallicity relation as enabled by, for example, StarFormationHistories.fixed_amr, only the star formation rate (or total stellar mass) coefficients need to be fit, as the metallicity distribution is no longer a free parameter in the model. As such, the relative weights of each model with identical logAge but different metallicities only need to be computed once at the start of the optimization. As the metallicity distribution is not a free parameter, it is also possible to truncate the list of models to only those that contribute significantly to the final composite model to improve runtime performance. That is what this method does.

A simple isochrone grid will be two-dimensional, encompassing age and metallicity. Consider a subset of the model grid with the same age such that unique(logAge) = [10.0] but a series of different metallicities, metallicities = -2.5:0.25:0. If we model the metallicity distribution function for this age as having a mean [M/H] of -2.0 and a Gaussian spread of 0.2 dex, then the relative weights of these models can be approximated as

import Distributions: Normal, pdf
 metallicities = -2.5:0.25:0
 relweights = pdf.(Normal(-2.0, 0.2), metallicities)
 relweights ./= sum(relweights) # Normalize the relative weights to unity sum
11-element Vector{Float64}:
@@ -36,4 +36,4 @@
  9.622444440364979e-23

Several of these models with very low relative weights are unlikely to contribute significantly to the final composite model. We can select out only the significant ones with, say, relative weights greater than 10% of the maximum as StarFormationHistories.truncate_relweights(0.1, relweights, fill(10.0,length(metallicities))) which will return indices into relweights whose values are greater than 0.1 * maximum(relweights) = 0.04988954088848224,

3-element Vector{Int64}:
  2
  3
- 4

which correspond to relweights[2,3,4] = [ 0.2284109622221623, 0.4988954088848224, 0.2284109622221623 ]. If we use only these 3 templates in the fit, instead of the original 11, we will achieve a speedup of almost 4x with a minor loss in precision which, in most cases, will be less than the numerical uncertainties on the individual star formation rate parameters. However, as fits of these sort are naturally quite fast, we recommend use of this type of truncation only in applications where many fits are required (e.g., Monte Carlo experiments). For most applications, this level of optimization is not necessary.

source
StarFormationHistories.calculate_edgesFunction
calculate_edges(edges, xlim, ylim, nbins, xwidth, ywidth)

Function to calculate the bin edges for 2D histograms. Returns (xbins, ybins) with both entries being ranges.

Keyword Arguments

  • edges is a tuple of ranges defining the left-side edges of the bins along the x-axis (edges[1]) and the y-axis (edges[2]). Example: (-1.0:0.1:1.5, 22:0.1:27.2). If edges is provided, it will simply be returned.
  • xlim is a length-2 indexable object (e.g., a Vector{Float64} or NTuple{2,Float64)) giving the lower and upper bounds on the x-axis corresponding to the provided colors array. Example: [-1.0, 1.5]. This is only used if edges==nothing.
  • ylim is like xlim but for the y-axis corresponding to the provided mags array. Example [25, 20]. This is only used if edges==nothing.
  • nbins::NTuple{2,<:Integer} is a 2-tuple of integers providing the number of bins to use along the x- and y-axes. This is only used if edges==nothing.
  • xwidth is the bin width along the x-axis for the colors array. This is only used if edges==nothing and nbins==nothing. Example: 0.1.
  • ywidth is like xwidth but for the y-axis corresponding to the provided mags array. Example: 0.1.
source
+ 4

which correspond to relweights[2,3,4] = [ 0.2284109622221623, 0.4988954088848224, 0.2284109622221623 ]. If we use only these 3 templates in the fit, instead of the original 11, we will achieve a speedup of almost 4x with a minor loss in precision which, in most cases, will be less than the numerical uncertainties on the individual star formation rate parameters. However, as fits of these sort are naturally quite fast, we recommend use of this type of truncation only in applications where many fits are required (e.g., Monte Carlo experiments). For most applications, this level of optimization is not necessary.

source
StarFormationHistories.calculate_edgesFunction
calculate_edges(edges, xlim, ylim, nbins, xwidth, ywidth)

Function to calculate the bin edges for 2D histograms. Returns (xbins, ybins) with both entries being ranges.

Keyword Arguments

  • edges is a tuple of ranges defining the left-side edges of the bins along the x-axis (edges[1]) and the y-axis (edges[2]). Example: (-1.0:0.1:1.5, 22:0.1:27.2). If edges is provided, it will simply be returned.
  • xlim is a length-2 indexable object (e.g., a Vector{Float64} or NTuple{2,Float64)) giving the lower and upper bounds on the x-axis corresponding to the provided colors array. Example: [-1.0, 1.5]. This is only used if edges==nothing.
  • ylim is like xlim but for the y-axis corresponding to the provided mags array. Example [25, 20]. This is only used if edges==nothing.
  • nbins::NTuple{2,<:Integer} is a 2-tuple of integers providing the number of bins to use along the x- and y-axes. This is only used if edges==nothing.
  • xwidth is the bin width along the x-axis for the colors array. This is only used if edges==nothing and nbins==nothing. Example: 0.1.
  • ywidth is like xwidth but for the y-axis corresponding to the provided mags array. Example: 0.1.
source
diff --git a/dev/fitting/kernels/index.html b/dev/fitting/kernels/index.html index c2f2c0c..0088a80 100644 --- a/dev/fitting/kernels/index.html +++ b/dev/fitting/kernels/index.html @@ -1,2 +1,2 @@ -Kernels · StarFormationHistories.jl

Kernels

One of the most important parts of our method is modelling the 2-D probability distribution of observing a star with particular intrinsic magnitudes in the Hess diagram space. We refer to these 2-D probability distributions as "kernels" throughout, as our process is conceptually similar to constructing a kernel density estimate. This document describes how these kernels are constructed and what assumptions are made to do so.

Our implementation is underpinned by the assumption that all kernels can be described the family of 2-D Gaussian probability distributions. To derive these 2-D distributions, we begin with 1-D distributions for each photometric filter used in the analysis.

We assume random photometric errors in the observed photometric catalog can be approximated as Gaussian. This is generally a good assumption for the space-based imaging used for resolved SFHs (e.g., HST and JWST) as long as the photometric completeness is high (greater than, perhaps, 50%). At low completeness, the error distributions become skewed and would be better modelled by something like a skew-normal distribution, which we do not presently consider.

We additionally require that the user can construct functions for each observed photomeric filter which, given a star's input intrinsic magnitude in a single filter $m_i$, return reliable measures for the average single-band photometric error; $\sigma(m_i)$. Assuming that the photometric errors in each band can be modelled as independent, we can derive, for a given $m_i$, the probability distribution of the observed magnitude $m_o$. We therefore have the 1-D probability distributions $P(m_o|m_i)$ for all filters.

In the case that imaging in three photometric filters are available and are used to construct a Hess diagram with a magnitude on the y-axis that does not appear on the x-axis (e.g., y=$R$ and x=$B-V$), then there is no covariance between the x and y axes and the 2-D Gaussian kernel is fully separable. We call this the "non-covariant" case.

In order to inject the kernel into the model Hess diagram, it must be integrated across the grid that defines the discretization. The integral of the non-covariant kernel across the Hess diagram pixel grid is entirely analytic, making evaluation easy and efficient. By integrating the kernel over the grid, the numerical precision is nearly independent of the grid resolution, which is not true of Monte Carlo (MC) based template construction algorithms.

In the case that imaging in only two photometric filters are available and are used to construct a Hess diagram with a magnitude on the y-axis that does appear on the x-axis (e.g., y=$B$ and x=$B-V$), then there is covariance between x and y axes. The initial implementation of this method neglected this covariance, and while the result was not catastrophic, it was certainly suboptimal. We now model the covariance explicitly. The covariance pattern does not permit a fully analytic integral as in the non-covariant case; instead, the inner integral (over, say, the x-axis) can be calculated analytically but the outer integral cannot. We therefore use Gauss-Legendre quadtrature to finish the integration over the second axis.

The types representing these kernels are not part of our public API, but we provide an example script examples/templates/kernels_example.jl which illustrates the performance of these kernels. The output of this script is reproduced and explained below.

Consider a star taken from an isochrone with intrinsic magnitudes $B=20$, $V=19$, and $R=18$ with expected random photometric errors $\sigma_B=0.02$, $\sigma_V=0.03$, and $\sigma_R=0.05$. Our example randomly samples a large population of possible observed magnitudes given these intrinsic properties and compares the distribution of the MC sample to our kernels.

We consider first the non-covariant case:

noncovariant

which has the expected, non-rotated morphology.

We next consider the covariant case with the covariance pattern y=$B$ and x=$B-V$:

covariantm1

which we can see correctly models the covariance pattern.

And finally, the covariant case with the covariance pattern y=$V$ and x=$B-V$: covariant1

+Kernels · StarFormationHistories.jl

Kernels

One of the most important parts of our method is modelling the 2-D probability distribution of observing a star with particular intrinsic magnitudes in the Hess diagram space. We refer to these 2-D probability distributions as "kernels" throughout, as our process is conceptually similar to constructing a kernel density estimate. This document describes how these kernels are constructed and what assumptions are made to do so.

Our implementation is underpinned by the assumption that all kernels can be described the family of 2-D Gaussian probability distributions. To derive these 2-D distributions, we begin with 1-D distributions for each photometric filter used in the analysis.

We assume random photometric errors in the observed photometric catalog can be approximated as Gaussian. This is generally a good assumption for the space-based imaging used for resolved SFHs (e.g., HST and JWST) as long as the photometric completeness is high (greater than, perhaps, 50%). At low completeness, the error distributions become skewed and would be better modelled by something like a skew-normal distribution, which we do not presently consider.

We additionally require that the user can construct functions for each observed photomeric filter which, given a star's input intrinsic magnitude in a single filter $m_i$, return reliable measures for the average single-band photometric error; $\sigma(m_i)$. Assuming that the photometric errors in each band can be modelled as independent, we can derive, for a given $m_i$, the probability distribution of the observed magnitude $m_o$. We therefore have the 1-D probability distributions $P(m_o|m_i)$ for all filters.

In the case that imaging in three photometric filters are available and are used to construct a Hess diagram with a magnitude on the y-axis that does not appear on the x-axis (e.g., y=$R$ and x=$B-V$), then there is no covariance between the x and y axes and the 2-D Gaussian kernel is fully separable. We call this the "non-covariant" case.

In order to inject the kernel into the model Hess diagram, it must be integrated across the grid that defines the discretization. The integral of the non-covariant kernel across the Hess diagram pixel grid is entirely analytic, making evaluation easy and efficient. By integrating the kernel over the grid, the numerical precision is nearly independent of the grid resolution, which is not true of Monte Carlo (MC) based template construction algorithms.

In the case that imaging in only two photometric filters are available and are used to construct a Hess diagram with a magnitude on the y-axis that does appear on the x-axis (e.g., y=$B$ and x=$B-V$), then there is covariance between x and y axes. The initial implementation of this method neglected this covariance, and while the result was not catastrophic, it was certainly suboptimal. We now model the covariance explicitly. The covariance pattern does not permit a fully analytic integral as in the non-covariant case; instead, the inner integral (over, say, the x-axis) can be calculated analytically but the outer integral cannot. We therefore use Gauss-Legendre quadtrature to finish the integration over the second axis.

The types representing these kernels are not part of our public API, but we provide an example script examples/templates/kernels_example.jl which illustrates the performance of these kernels. The output of this script is reproduced and explained below.

Consider a star taken from an isochrone with intrinsic magnitudes $B=20$, $V=19$, and $R=18$ with expected random photometric errors $\sigma_B=0.02$, $\sigma_V=0.03$, and $\sigma_R=0.05$. Our example randomly samples a large population of possible observed magnitudes given these intrinsic properties and compares the distribution of the MC sample to our kernels.

We consider first the non-covariant case:

noncovariant

which has the expected, non-rotated morphology.

We next consider the covariant case with the covariance pattern y=$B$ and x=$B-V$:

covariantm1

which we can see correctly models the covariance pattern.

And finally, the covariant case with the covariance pattern y=$V$ and x=$B-V$: covariant1

diff --git a/dev/fitting/linear_amr/index.html b/dev/fitting/linear_amr/index.html index 6222101..c4b5073 100644 --- a/dev/fitting/linear_amr/index.html +++ b/dev/fitting/linear_amr/index.html @@ -19,7 +19,7 @@ [, σ::Number]; x0 = vcat(construct_x0_mdf(logAge, convert(S,13.7)), [0.05, -2.0, 0.2]), - kws...) where {S <: Number}

Method that fits a linear combination of the provided Hess diagrams models to the observed Hess diagram data, constrained to have a linear age-metallicity relation with the mean metallicity of element i of unique(logAge) being μ[i] = α * (T_max - exp10(unique(logAge)[i]) / 1e9) + β. α is therefore a slope in the units of metallicities per Gyr, and β is the mean metallicity value of stars being born at a lookback time of T_max, which has units of Gyr. Individual weights for each isochrone template are then determined via Gaussian weighting with the above mean and the standard deviation σ, which can either be fixed or fit.

This function is designed to work best with a "grid" of stellar models, defined by the outer product of N unique entries in logAge and M unique entries in metallicities. See the examples for more information on usage.

The second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.

Arguments

  • models are the template Hess diagrams for the simple stellar populations that compose the observed Hess diagram.
  • data is the Hess diagram for the observed data.
  • logAge::AbstractVector{<:Number} is the vector containing the effective ages of the stellar populations used to create the templates in models, in units of log10(age [yr]). For example, if a population has an age of 1 Myr, its entry in logAge should be log10(10^6) = 6.0.
  • metallicities::AbstractVector{<:Number} is the vector containing the effective metallicities of the stellar populations used to create the templates in models. This is most commonly a logarithmic abundance like [M/H] or [Fe/H], but you could use a linear abundance like the metal mass fraction Z if you wanted to. There are some notes on the Wikipedia that might be useful.
  • T_max::Number is the time at which the age-metallicity relation has a value of eta in Gyr. For example, if the oldest stellar populations in your isochrone grid are 12 Gyr old, you could set T_max = 12.0.

Optional Arguments

  • If provided, σ::Number is the fixed width of the Gaussian the defines the metallicity distribution function (MDF) at fixed logAge. If this argument is omitted, σ will be a free parameter in the fit.

Keyword Arguments

  • x0 is the vector of initial guesses for the stellar mass coefficients per unique entry in logAge, plus the variables that define the metallicity evolution model. You should basically always be calculating and passing this keyword argument. We provide StarFormationHistories.construct_x0_mdf to prepare the first part of x0 assuming constant star formation rate, which is typically a good initial guess. You then have to concatenate that result with an initial guess for the metallicity evolution parameters. For example, x0=vcat(construct_x0_mdf(logAge, 13.7; normalize_value=1e4), [0.05,-2.0,0.2]), where logAge is a valid argument for this function (see above), and the initial guesses on the parameters are [α, β, σ] = [0.05, -2.0, 0.2]. If the provided metallicities are, for example, [M/H] values, then this mean metallicity evolution is μ(t) [dex] = 0.05 [dex/Gyr] * (T_max - t) [Gyr] - 2.0 [dex], and at fixed time, the metallicity distribution function is Gaussian with mean μ(t) and standard deviation σ. If you provide σ as an optional argument, then you should not include an entry for it in x0.
  • Other kws... are passed to Optim.options to set things like convergence criteria for the optimization.

Returns

  • This function returns an object (say, result) of similar structure to the object returned by fit_templates. Specifically, this method will return a NamedTuple with entries result.mle and result.map for the maximum likelihood and maximum a posteriori estimates, respectively. If you provide a fixed σ, those objects will be instances of StarFormationHistories.LogTransformMDFσResult. If you allow σ to be freely fit, those objects will be instances of StarFormationHistories.LogTransformMDFResult. Both of these types support sampling via, e.g., rand(result.map, 10).

Notes

  • α and σ are optimized under a logarithmic transformation, so they are constrained to be positive. β is not and may be negative. This method also uses the BFGS method from Optim.jl internally just like fit_templates; please see the notes section of that method.
source
StarFormationHistories.LogTransformMDFσResultType
LogTransformMDFσResult(μ::AbstractVector{<:Number},
+                  kws...) where {S <: Number}

Method that fits a linear combination of the provided Hess diagrams models to the observed Hess diagram data, constrained to have a linear age-metallicity relation with the mean metallicity of element i of unique(logAge) being μ[i] = α * (T_max - exp10(unique(logAge)[i]) / 1e9) + β. α is therefore a slope in the units of metallicities per Gyr, and β is the mean metallicity value of stars being born at a lookback time of T_max, which has units of Gyr. Individual weights for each isochrone template are then determined via Gaussian weighting with the above mean and the standard deviation σ, which can either be fixed or fit.

This function is designed to work best with a "grid" of stellar models, defined by the outer product of N unique entries in logAge and M unique entries in metallicities. See the examples for more information on usage.

The second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.

Arguments

  • models are the template Hess diagrams for the simple stellar populations that compose the observed Hess diagram.
  • data is the Hess diagram for the observed data.
  • logAge::AbstractVector{<:Number} is the vector containing the effective ages of the stellar populations used to create the templates in models, in units of log10(age [yr]). For example, if a population has an age of 1 Myr, its entry in logAge should be log10(10^6) = 6.0.
  • metallicities::AbstractVector{<:Number} is the vector containing the effective metallicities of the stellar populations used to create the templates in models. This is most commonly a logarithmic abundance like [M/H] or [Fe/H], but you could use a linear abundance like the metal mass fraction Z if you wanted to. There are some notes on the Wikipedia that might be useful.
  • T_max::Number is the time at which the age-metallicity relation has a value of eta in Gyr. For example, if the oldest stellar populations in your isochrone grid are 12 Gyr old, you could set T_max = 12.0.

Optional Arguments

  • If provided, σ::Number is the fixed width of the Gaussian the defines the metallicity distribution function (MDF) at fixed logAge. If this argument is omitted, σ will be a free parameter in the fit.

Keyword Arguments

  • x0 is the vector of initial guesses for the stellar mass coefficients per unique entry in logAge, plus the variables that define the metallicity evolution model. You should basically always be calculating and passing this keyword argument. We provide StarFormationHistories.construct_x0_mdf to prepare the first part of x0 assuming constant star formation rate, which is typically a good initial guess. You then have to concatenate that result with an initial guess for the metallicity evolution parameters. For example, x0=vcat(construct_x0_mdf(logAge, 13.7; normalize_value=1e4), [0.05,-2.0,0.2]), where logAge is a valid argument for this function (see above), and the initial guesses on the parameters are [α, β, σ] = [0.05, -2.0, 0.2]. If the provided metallicities are, for example, [M/H] values, then this mean metallicity evolution is μ(t) [dex] = 0.05 [dex/Gyr] * (T_max - t) [Gyr] - 2.0 [dex], and at fixed time, the metallicity distribution function is Gaussian with mean μ(t) and standard deviation σ. If you provide σ as an optional argument, then you should not include an entry for it in x0.
  • Other kws... are passed to Optim.options to set things like convergence criteria for the optimization.

Returns

  • This function returns an object (say, result) of similar structure to the object returned by fit_templates. Specifically, this method will return a NamedTuple with entries result.mle and result.map for the maximum likelihood and maximum a posteriori estimates, respectively. If you provide a fixed σ, those objects will be instances of StarFormationHistories.LogTransformMDFσResult. If you allow σ to be freely fit, those objects will be instances of StarFormationHistories.LogTransformMDFResult. Both of these types support sampling via, e.g., rand(result.map, 10).

Notes

  • α and σ are optimized under a logarithmic transformation, so they are constrained to be positive. β is not and may be negative. This method also uses the BFGS method from Optim.jl internally just like fit_templates; please see the notes section of that method.
source
StarFormationHistories.LogTransformMDFσResultType
LogTransformMDFσResult(μ::AbstractVector{<:Number},
                        σ::AbstractVector{<:Number},
                        invH::AbstractMatrix{<:Number},
                        result)

Type for containing the maximum likelihood estimate (MLE) and maximum a posteriori (MAP) results from fit_templates_mdf for fixed σ. The fitted coefficients are available in the μ field. Estimates of the standard errors are available in the σ field. These have both been transformed from the native logarithmic fitting space into natural units (i.e., stellar mass or star formation rate). The linear age-metallicity relation parameters α (slope [dex/Gyr]) and β (intercept at T_max [dex]) are available in the second-to-last and last elements of μ and σ, respectively.

invH contains the estimated inverse Hessian of the likelihood / posterior at the maximum point in the logarithmic fitting units. result is the full result object returned by the optimization routine.

This type is implemented as a subtype of Distributions.Sampleable{Multivariate, Continuous} to enable sampling from an estimate of the likelihood / posterior distribution. We approximate the distribution as a multivariate Gaussian in the native (logarithmically transformed) fitting variables with covariance matrix invH and means log.(μ). We find this approximation is good for the MAP result but less robust for the MLE. You can obtain N::Integer samples from the distribution by rand(R, N) where R is an instance of this type; this will return a size (length(μ)+2) x N matrix, or fail if invH is not positive definite.

Examples

julia> result = fit_templates_mdf(models, data, model_logAge, model_MH, 0.3);
@@ -28,7 +28,7 @@
 StarFormationHistories.LogTransformMDFσResult{...}
 
 julia> size(rand(result.map, 3)) == (length(models)+2,3)
-true
source
StarFormationHistories.LogTransformMDFResultType
LogTransformMDFResult(μ::AbstractVector{<:Number},
                       σ::AbstractVector{<:Number},
                       invH::AbstractMatrix{<:Number},
                       result)

Type for containing the maximum likelihood estimate (MLE) and maximum a posteriori (MAP) results from fit_templates_mdf when freely fitting σ. The fitted coefficients are available in the μ field. Estimates of the standard errors are available in the σ field. These have both been transformed from the native logarithmic fitting space into natural units (i.e., stellar mass or star formation rate). The linear age-metallicity relation parameters α (slope [dex/Gyr]) and β (intercept at T_max [dex]) are available in the third-to-last and second-to-last elements of μ and σ, respectively. The static Gaussian width of the MDF at fixed age is provided in the last element of μ and σ.

invH contains the estimated inverse Hessian of the likelihood / posterior at the maximum point in the logarithmic fitting units. result is the full result object returned by the optimization routine.

This type is implemented as a subtype of Distributions.Sampleable{Multivariate, Continuous} to enable sampling from an estimate of the likelihood / posterior distribution. We approximate the distribution as a multivariate Gaussian in the native (logarithmically transformed) fitting variables with covariance matrix invH and means log.(μ). We find this approximation is good for the MAP result but less robust for the MLE. You can obtain N::Integer samples from the distribution by rand(R, N) where R is an instance of this type; this will return a size (length(μ)+3) x N matrix, or fail if invH is not positive definite.

Examples

julia> result = fit_templates_mdf(models, data, model_logAge, model_MH);
@@ -37,22 +37,38 @@
 StarFormationHistories.LogTransformMDFσResult{...}
 
 julia> size(rand(result.map, 3)) == (length(models)+3,3)
-true
source

The method StarFormationHistories.construct_x0_mdf can be used to construct the stellar mass components $R_j$ of the initial guess vector x0

The method StarFormationHistories.construct_x0_mdf can be used to construct the stellar mass components $R_j$ of the initial guess vector x0

StarFormationHistories.construct_x0_mdfFunction
x0::Vector = construct_x0_mdf(logAge::AbstractVector{T},
+                              [ cum_sfh, ]
                               T_max::Number;
-                              normalize_value::Number = one(T)) where T <: Number

Generates a vector of initial stellar mass normalizations for input to StarFormationHistories.fit_templates_mdf or StarFormationHistories.hmc_sample_mdf with a total stellar mass of normalize_value such that the implied star formation rate is constant across the provided logAge vector that contains the log10(Age [yr]) of each isochrone that you are going to input as models. For the purposes of computing the constant star formation rate, the provided logAge are treated as left-bin edges, with the final right-bin edge being T_max, which has units of Gyr. For example, you might have logAge=[6.6, 6.7, 6.8] in which case a final logAge of 6.9 would give equal bin widths. In this case you would set T_max = exp10(6.9) / 1e9 ≈ 0.0079 so that the width of the final bin for the star formation rate calculation has the same log10(Age [yr]) step as the other bins.

The difference between this function and StarFormationHistories.construct_x0 is that this function generates an x0 vector that is of length length(unique(logage)) (that is, a single normalization factor for each unique entry in logAge) while StarFormationHistories.construct_x0 returns an x0 vector that is of length length(logAge); that is, a normalization factor for every entry in logAge. The order of the coefficients is such that the coefficient x[i] corresponds to the entry unique(logAge)[i].

Notes

Examples

julia> construct_x0_mdf([9.0,8.0,7.0], 10.0; normalize_value=5.0)
-3-element Vector{Float64}:
- 4.504504504504504
- 0.4504504504504504
- 0.04504504504504504
+                              normalize_value::Number = one(T)) where T <: Number

Generates a vector of initial stellar mass normalizations for input to StarFormationHistories.fit_templates_mdf or StarFormationHistories.hmc_sample_mdf with a total stellar mass of normalize_value. The logAge vector must contain the log10(Age [yr]) of each isochrone that you are going to input as models. If cum_sfh is not provided, a constant star formation rate is assumed. For the purposes of computing the constant star formation rate, the provided logAge are treated as left-bin edges, with the final right-bin edge being T_max, which has units of Gyr. For example, you might have logAge=[6.6, 6.7, 6.8] in which case a final logAge of 6.9 would give equal bin widths (in log-space). In this case you would set T_max = exp10(6.9) / 1e9 ≈ 0.0079 so that the width of the final bin for the star formation rate calculation has the same log10(Age [yr]) step as the other bins.

A desired cumulative SFH vector cum_sfh::AbstractVector{<:Number} can be provided as the second argument, which should correspond to a lookback time vector unique(logAge). You can also provide cum_sfh as a length-2 indexable (e.g., a length-2 Vector{Vector{<:Number}}) with the first element containing a list of log10(Age [yr]) values and the second element containing the cumulative SFH values at those values. This cumulative SFH is then interpolated onto the logAge provided in the first argument. This method should be used when you want to define the cumulative SFH on a different age grid from the logAge you provide in the first argument. The examples below demonstrate these use cases.

The difference between this function and StarFormationHistories.construct_x0 is that this function generates an x0 vector that is of length length(unique(logage)) (that is, a single normalization factor for each unique entry in logAge) while StarFormationHistories.construct_x0 returns an x0 vector that is of length length(logAge); that is, a normalization factor for every entry in logAge. The order of the coefficients is such that the coefficient x[i] corresponds to the entry unique(logAge)[i].

Notes

Examples – Constant SFR

julia> isapprox( construct_x0_mdf([9.0, 8.0, 7.0], 10.0; normalize_value=5.0),
+                 [4.504504504504504, 0.4504504504504504, 0.04504504504504504] )
+true
 
-julia> construct_x0_mdf(repeat([9.0,8.0,7.0,8.0];inner=3), 10.0; normalize_value=5.0)
-3-element Vector{Float64}:
- 4.504504504504504
- 0.4504504504504504
- 0.04504504504504504
+julia> isapprox( construct_x0_mdf(repeat([9.0, 8.0, 7.0, 8.0]; inner=3), 10.0; normalize_value=5.0),
+                 [4.504504504504504, 0.4504504504504504, 0.04504504504504504] )
+true
 
-julia> construct_x0_mdf(repeat([9.0,8.0,7.0,8.0],3), 10.0; normalize_value=5.0) ≈ construct_x0([9.0,8.0,7.0], 10.0; normalize_value=5.0)
-true
source

and StarFormationHistories.calculate_coeffs_mdf can be used to calculate per-template stellar mass coefficients (the $r_{j,k}$ above) given the results of a fit (which will be the $R_j$ in the equations above)

StarFormationHistories.calculate_coeffs_mdfFunction
calculate_coeffs_mdf(variables::AbstractVector{<:Number},
+julia> isapprox( construct_x0_mdf(repeat([9.0, 8.0, 7.0, 8.0]; outer=3), 10.0; normalize_value=5.0),
+                 construct_x0([9.0, 8.0, 7.0], 10.0; normalize_value=5.0) )
+true

Examples – Input Cumulative SFH defined on same logAge grid

julia> isapprox( construct_x0_mdf([9.0, 8.0, 7.0], [0.9009, 0.99099, 1.0], 10.0; normalize_value=5.0),
+                 [4.5045, 0.4504, 0.0450]; atol=1e-3 )
+true
+
+julia> isapprox( construct_x0_mdf([9.0, 8.0, 7.0], [0.1, 0.5, 1.0], 10.0; normalize_value=5.0),
+                 [0.5, 2.0, 2.5] )
+true
+
+julia> isapprox( construct_x0_mdf([7.0, 8.0, 9.0], [1.0, 0.5, 0.1], 10.0; normalize_value=5.0),
+                 [2.5, 2.0, 0.5] )
+true

Examples – Input Cumulative SFH with separate logAge grid

julia> isapprox( construct_x0_mdf([9.0, 8.0, 7.0],
+                                  [[9.0, 8.0, 7.0], [0.9009, 0.99099, 1.0]], 10.0; normalize_value=5.0),
+                 construct_x0_mdf([9.0, 8.0, 7.0], [0.9009, 0.99099, 1.0], 10.0; normalize_value=5.0) )
+true
+
+julia> isapprox( construct_x0_mdf([9.0, 8.0, 7.0],
+                                  [[9.0, 8.5, 8.25, 7.0], [0.9009, 0.945945, 0.9887375, 1.0]], 10.0; normalize_value=5.0),
+                 construct_x0_mdf([9.0, 8.0, 7.0], [0.9009, 0.99099, 1.0], 10.0; normalize_value=5.0) )
+true
source

and StarFormationHistories.calculate_coeffs_mdf can be used to calculate per-template stellar mass coefficients (the $r_{j,k}$ above) given the results of a fit (which will be the $R_j$ in the equations above)

StarFormationHistories.calculate_coeffs_mdfFunction
calculate_coeffs_mdf(variables::AbstractVector{<:Number},
                      logAge::AbstractVector{<:Number},
                      metallicities::AbstractVector{<:Number},
                      T_max::Number
@@ -63,7 +79,7 @@
                      logAge::AbstractVector{<:Number},
                      metallicities::AbstractVector{<:Number},
                      T_max::Number)

Calculates per-model stellar mass coefficients coeffs from the fitting parameters of StarFormationHistories.fit_templates_mdf and StarFormationHistories.hmc_sample_mdf. The variables returned by these functions is of length length(unique(logAge))+3. The first length(logAge) entries are stellar mass coefficients, one per unique entry in logAge. The final three elements are α, β, and σ defining a metallicity evolution such that the mean for element i of unique(logAge) is μ[i] = α * (T_max - exp10(unique(logAge)[i]) / 1e9) + β. The individual weights per each isochrone are then determined via Gaussian weighting with the above mean and the provided σ. The second call signature can be used on samples that include α, β, and σ.

Examples

julia> calculate_coeffs_mdf([1,1], [7,7,8,8], [-2,-1,-2,-1], 12, 0.05, -2.0, 0.2) ≈ [ 0.07673913563377144, 0.9232608643662287, 0.08509904500701986, 0.9149009549929802 ]
-true
source

Sampling Methods

We additionally offer a sampling method for this linear age-metallicity relation using HMC:

Sampling Methods

We additionally offer a sampling method for this linear age-metallicity relation using HMC:

StarFormationHistories.hmc_sample_mdfFunction
hmc_sample_mdf(models::AbstractVector{T},
                data::AbstractMatrix{<:Number},
                logAge::AbstractVector{<:Number},
                metallicities::AbstractVector{<:Number},
@@ -71,7 +87,7 @@
                nsteps::Integer;
                composite=Matrix{S}(undef,size(data)),
                rng::Random.AbstractRNG=Random.default_rng(),
-               kws...) where {S <: Number, T <: AbstractMatrix{S}}

Method to sample the posterior of the star formation history coefficients constrained to have a linear age-metallicity relation with the mean metallicity of element i of unique(logAge) being μ[i] = α * (T_max - exp10(unique(logAge)[i]) / 1e9) + β. α is therefore a slope in the units of metallicities per Gyr, and β is the mean metallicity value of stars born at lookback time T_max which has units of Gyr. Individual weights for each isochrone template are then determined via Gaussian weighting with the above mean and the standard deviation σ, which can either be fixed or fit. This method is essentially an analog of StarFormationHistories.fit_templates_mdf that samples the posterior rather than using optimization methods to find the maximum likelihood estimate. This method uses the No-U-Turn sampler as implemented in DynamicHMC.jl, which is a form of dynamic Hamiltonian Monte Carlo.

This function is designed to work best with a "grid" of stellar models, defined by the outer product of N unique entries in logAge and M unique entries in metallicities. See the examples for more information on usage.

Arguments

  • models::AbstractVector{<:AbstractMatrix{<:Number}} is a vector of equal-sized matrices that represent the template Hess diagrams for the simple stellar populations that compose the observed Hess diagram.
  • data::AbstractMatrix{<:Number} is the Hess diagram for the observed data.
  • logAge::AbstractVector{<:Number} is the vector containing the effective ages of the stellar populations used to create the templates in models, in units of log10(age [yr]). For example, if a population has an age of 1 Myr, its entry in logAge should be log10(10^6) = 6.0.
  • metallicities::AbstractVector{<:Number} is the vector containing the effective metallicities of the stellar populations used to create the templates in models. This is most commonly a logarithmic abundance like [M/H] or [Fe/H], but you could use a linear abundance like the metal mass fraction Z if you wanted to. There are some notes on the Wikipedia that might be useful.
  • T_max::Number is the time at which the age-metallicity relation has a value of eta in Gyr. For example, if the oldest stellar populations in your isochrone grid are 12 Gyr old, you could set T_max = 12.0.
  • nsteps::Integer is the number of samples to draw per chain.

Optional Arguments (NOT YET IMPLEMENTED)

  • nchains::Integer: If this argument is not provided, this method will return a single chain. If this argument is provided, it will sample nchains chains using all available threads and will return a vector of the individual chains. If nchains is set, composite must be a vector of matrices containing a working matrix for each chain.

Keyword Arguments

  • composite is the working matrix (or vector of matrices, if the argument nchains is provided) that will be used to store the composite Hess diagram model during computation; must be of the same size as the templates contained in models and the observed Hess diagram data.
  • rng::Random.AbstractRNG is the random number generator that will be passed to DynamicHMC.jl. If nchains is provided this method will attempt to sample in parallel, requiring a thread-safe rng such as that provided by Random.default_rng().

All other keyword arguments kws... will be passed to DynamicHMC.mcmc_with_warmup or DynamicHMC.mcmc_keep_warmup depending on whether nchains is provided.

Returns (NEEDS UPDATED)

  • If nchains is not provided, returns a NamedTuple as summarized in DynamicHMC.jl's documentation. In short, the matrix of samples can be extracted and transformed as exp.( result.posterior_matrix ). Statistics about the chain can be obtained with DynamicHMC.Diagnostics.summarize_tree_statistics(result.tree_statistics); you want to see a fairly high acceptance rate (>0.5) and the majority of samples having termination criteria being "turning." See DynamicHMC.jl's documentation for more information.
  • If nchains is provided, returns a vector of length nchains of the same NamedTuples described above. The samples from each chain in the returned vector can be stacked to a single (nsamples, nchains, length(models)) matrix with DynamicHMC.stack_posterior_matrices(result).
source

Implementation

While one could optimize the above model without an analytic gradient, such gradient-free methods are typically slower and less robust. One could also calculate the gradient numerically using finite differences or auto-differentiation, but these are still slower than analytic calculations. We will show that the gradient of this hierarchical model is analytic, allowing us to design an efficient optimization scheme.

Equation 21 in Dolphin 2001 gives the gradient of our objective function with respect to the underlying coefficients

\[\begin{aligned} + kws...) where {S <: Number, T <: AbstractMatrix{S}}

Method to sample the posterior of the star formation history coefficients constrained to have a linear age-metallicity relation with the mean metallicity of element i of unique(logAge) being μ[i] = α * (T_max - exp10(unique(logAge)[i]) / 1e9) + β. α is therefore a slope in the units of metallicities per Gyr, and β is the mean metallicity value of stars born at lookback time T_max which has units of Gyr. Individual weights for each isochrone template are then determined via Gaussian weighting with the above mean and the standard deviation σ, which can either be fixed or fit. This method is essentially an analog of StarFormationHistories.fit_templates_mdf that samples the posterior rather than using optimization methods to find the maximum likelihood estimate. This method uses the No-U-Turn sampler as implemented in DynamicHMC.jl, which is a form of dynamic Hamiltonian Monte Carlo.

This function is designed to work best with a "grid" of stellar models, defined by the outer product of N unique entries in logAge and M unique entries in metallicities. See the examples for more information on usage.

Arguments

  • models::AbstractVector{<:AbstractMatrix{<:Number}} is a vector of equal-sized matrices that represent the template Hess diagrams for the simple stellar populations that compose the observed Hess diagram.
  • data::AbstractMatrix{<:Number} is the Hess diagram for the observed data.
  • logAge::AbstractVector{<:Number} is the vector containing the effective ages of the stellar populations used to create the templates in models, in units of log10(age [yr]). For example, if a population has an age of 1 Myr, its entry in logAge should be log10(10^6) = 6.0.
  • metallicities::AbstractVector{<:Number} is the vector containing the effective metallicities of the stellar populations used to create the templates in models. This is most commonly a logarithmic abundance like [M/H] or [Fe/H], but you could use a linear abundance like the metal mass fraction Z if you wanted to. There are some notes on the Wikipedia that might be useful.
  • T_max::Number is the time at which the age-metallicity relation has a value of eta in Gyr. For example, if the oldest stellar populations in your isochrone grid are 12 Gyr old, you could set T_max = 12.0.
  • nsteps::Integer is the number of samples to draw per chain.

Optional Arguments (NOT YET IMPLEMENTED)

  • nchains::Integer: If this argument is not provided, this method will return a single chain. If this argument is provided, it will sample nchains chains using all available threads and will return a vector of the individual chains. If nchains is set, composite must be a vector of matrices containing a working matrix for each chain.

Keyword Arguments

  • composite is the working matrix (or vector of matrices, if the argument nchains is provided) that will be used to store the composite Hess diagram model during computation; must be of the same size as the templates contained in models and the observed Hess diagram data.
  • rng::Random.AbstractRNG is the random number generator that will be passed to DynamicHMC.jl. If nchains is provided this method will attempt to sample in parallel, requiring a thread-safe rng such as that provided by Random.default_rng().

All other keyword arguments kws... will be passed to DynamicHMC.mcmc_with_warmup or DynamicHMC.mcmc_keep_warmup depending on whether nchains is provided.

Returns (NEEDS UPDATED)

  • If nchains is not provided, returns a NamedTuple as summarized in DynamicHMC.jl's documentation. In short, the matrix of samples can be extracted and transformed as exp.( result.posterior_matrix ). Statistics about the chain can be obtained with DynamicHMC.Diagnostics.summarize_tree_statistics(result.tree_statistics); you want to see a fairly high acceptance rate (>0.5) and the majority of samples having termination criteria being "turning." See DynamicHMC.jl's documentation for more information.
  • If nchains is provided, returns a vector of length nchains of the same NamedTuples described above. The samples from each chain in the returned vector can be stacked to a single (nsamples, nchains, length(models)) matrix with DynamicHMC.stack_posterior_matrices(result).
source

Implementation

While one could optimize the above model without an analytic gradient, such gradient-free methods are typically slower and less robust. One could also calculate the gradient numerically using finite differences or auto-differentiation, but these are still slower than analytic calculations. We will show that the gradient of this hierarchical model is analytic, allowing us to design an efficient optimization scheme.

Equation 21 in Dolphin 2001 gives the gradient of our objective function with respect to the underlying coefficients

\[\begin{aligned} F \equiv - \text{ln} \, \mathscr{L} &= \sum_i m_i - n_i \times \left( 1 - \text{ln} \, \left( \frac{n_i}{m_i} \right) \right) \\ \frac{\partial \, F}{\partial \, r_{j,k}} &= \sum_i c_{i,j,k} \left( 1 - \frac{n_i}{m_i} \right) \end{aligned}\]

where $c_{i,j,k}$ is the value of template $j,k$ in bin $i$ and $n_i$ is bin $i$ of the observed Hess diagram. These partial derivatives are easy to obtain, but we need partials with respect to the per-age-bin fitting parameters $R_j$. Given the above relation between $r_{j,k}$ and $R_j$, we can calculate these derivatives as

\[\begin{aligned} @@ -93,4 +109,4 @@ \frac{\partial \, F}{\partial \, \sigma} &= \sum_{j,k} \frac{\partial \, F}{\partial \, r_{j,k}} \, \frac{\partial \, r_{j,k}}{\partial \, \sigma} \\ \frac{\partial \, r_{j,k}}{\partial \, \sigma} &= R_j \left( \frac{1}{\sum_k \, A_{j,k}} \, \frac{\partial \, A_{j,k}}{\partial \, \sigma} - \frac{A_{j,k}}{\left( \sum_k \, A_{j,k} \right)^2} \, \frac{\partial \, \sum_k \, A_{j,k}}{\partial \, \sigma} \right) \\ &= \frac{R_j}{\sum_k \, A_{j,k}} \left( \frac{\partial \, A_{j,k}}{\partial \, \sigma} - \frac{A_{j,k}}{\sum_k \, A_{j,k}} \sum_k \frac{\partial \, A_{j,k}}{\partial \, \sigma} \right) \\ -\end{aligned}\]

Then all we need is

\[\frac{\partial \, A_{j,k}}{\partial \, \sigma} = \frac{A_{j,k} \, \left( [\text{M}/\text{H}]_k - \mu_j \right)^2}{\sigma^3}\]

which we can substitute into the above expressions to find $\frac{\partial \, F}{\partial \, \sigma}$.

+\end{aligned}\]

Then all we need is

\[\frac{\partial \, A_{j,k}}{\partial \, \sigma} = \frac{A_{j,k} \, \left( [\text{M}/\text{H}]_k - \mu_j \right)^2}{\sigma^3}\]

which we can substitute into the above expressions to find $\frac{\partial \, F}{\partial \, \sigma}$.

diff --git a/dev/fitting/log_amr/index.html b/dev/fitting/log_amr/index.html index b928936..ccbe760 100644 --- a/dev/fitting/log_amr/index.html +++ b/dev/fitting/log_amr/index.html @@ -7,7 +7,7 @@ metallicities::AbstractVector{<:Number}, T_max::Number [, α::Number, β::Number, σ::Number]; - MH_func = StarFormationHistories.MH_from_Z)

Calculates per-model stellar mass coefficients coeffs from the fitting parameters of StarFormationHistories.fit_templates_logamr and StarFormationHistories.hmc_sample_logamr. The variables returned by these functions is of length length(unique(logAge))+3. The first length(logAge) entries are stellar mass coefficients, one per unique entry in logAge. The final three elements are α, β, and σ defining a metallicity evolution such that the mean metal mass fraction Z for element i of unique(logAge) is μ_Z[i] = α * (T_max - exp10(unique(logAge)[i]) / 1e9) + β. This is converted to a mean metallicity in [M/H] via the provided callable keyword argument MH_func which defaults to StarFormationHistories.MH_from_Z. The individual weights per each isochrone are then determined via Gaussian weighting with the above mean [M/H] and the provided σ in dex. The provided metallicities vector should be in [M/H].

Notes

  • Physically, the metal mass fraction Z must always be positive. Under the above model, this means α and β must be greater than or equal to 0. With σ being a Gaussian width, it must be positive.
  • If T_max is lower than the maximum of the logAge argument you provide, a warning will be raised which may be ignored if it does not result in any of the mean metal mass fractions Z being less than 0 for any of the provided logAge.
  • An error will be thrown if the provided age-metallicity relation variables (α, β) and T_max argument result in a mean metal mass fraction less than 0 for any time in the provided logAge vector.
source

Fitting Functions

The main function we provide to fit star formation histories to Hess diagrams under the logarithmic age-metallicity relation is fit_templates_logamr. This function operates similarly to the fitting function for the linear AMR model, fit_templates_mdf.

StarFormationHistories.fit_templates_logamrFunction
result = fit_templates_logamr(models::AbstractVector{<:AbstractMatrix{S}},
+                        MH_func = StarFormationHistories.MH_from_Z)

Calculates per-model stellar mass coefficients coeffs from the fitting parameters of StarFormationHistories.fit_templates_logamr and StarFormationHistories.hmc_sample_logamr. The variables returned by these functions is of length length(unique(logAge))+3. The first length(logAge) entries are stellar mass coefficients, one per unique entry in logAge. The final three elements are α, β, and σ defining a metallicity evolution such that the mean metal mass fraction Z for element i of unique(logAge) is μ_Z[i] = α * (T_max - exp10(unique(logAge)[i]) / 1e9) + β. This is converted to a mean metallicity in [M/H] via the provided callable keyword argument MH_func which defaults to StarFormationHistories.MH_from_Z. The individual weights per each isochrone are then determined via Gaussian weighting with the above mean [M/H] and the provided σ in dex. The provided metallicities vector should be in [M/H].

Notes

  • Physically, the metal mass fraction Z must always be positive. Under the above model, this means α and β must be greater than or equal to 0. With σ being a Gaussian width, it must be positive.
  • If T_max is lower than the maximum of the logAge argument you provide, a warning will be raised which may be ignored if it does not result in any of the mean metal mass fractions Z being less than 0 for any of the provided logAge.
  • An error will be thrown if the provided age-metallicity relation variables (α, β) and T_max argument result in a mean metal mass fraction less than 0 for any time in the provided logAge vector.
source

Fitting Functions

The main function we provide to fit star formation histories to Hess diagrams under the logarithmic age-metallicity relation is fit_templates_logamr. This function operates similarly to the fitting function for the linear AMR model, fit_templates_mdf.

StarFormationHistories.fit_templates_logamrFunction
result = fit_templates_logamr(models::AbstractVector{<:AbstractMatrix{S}},
                               data::AbstractMatrix{<:Number},
                               logAge::AbstractVector{<:Number},
                               metallicities::AbstractVector{<:Number},
@@ -28,7 +28,7 @@
                                         [1e-4, 5e-5, 0.2]),
                               MH_func = StarFormationHistories.MH_from_Z,
                               MH_deriv_Z = StarFormationHistories.dMH_dZ,
-                              kws...) where {S <: Number}

Method that fits a linear combination of the provided Hess diagrams models to the observed Hess diagram data, constrained to have a logarithmic age-metallicity relation with the mean metal mass fraction μ_Z of element i of unique(logAge) being μ_Z[i] = α * (T_max - exp10(unique(logAge)[i]) / 1e9) + β. This is converted to a mean metallicity in [M/H] via the provided callable keyword argument MH_func which defaults to MH_from_Z. α is therefore a slope in the units of inverse Gyr, and β is the mean metal mass fraction of stars born at the earliest valid lookback time, determined by keyword argument T_max which has units of Gyr. Individual weights for each isochrone template are then determined via Gaussian weighting with the above mean [M/H] and the standard deviation σ in dex, which can either be fixed or fit.

This function is designed to work best with a "grid" of stellar models, defined by the outer product of N unique entries in logAge and M unique entries in metallicities. See the examples for more information on usage.

The second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.

Arguments

  • models are the template Hess diagrams for the simple stellar populations that compose the observed Hess diagram.
  • data is the Hess diagram for the observed data.
  • logAge::AbstractVector{<:Number} is the vector containing the effective ages of the stellar populations used to create the templates in models, in units of log10(age [yr]). For example, if a population has an age of 1 Myr, its entry in logAge should be log10(10^6) = 6.0.
  • metallicities::AbstractVector{<:Number} is the vector containing the effective metallicities of the stellar populations used to create the templates in models. This should be a logarithmic abundance like [M/H] or [Fe/H].
  • T_max::Number is the maximum lookback time in Gyr for which the age-metallicity relation is to be valid. In most cases this should just be the maximum of the logAge vector argument maximum(logAge) or slightly larger. By definition, this is the lookback time at which μ_Z = β.

Optional Arguments

  • If provided, σ::Number is the fixed width of the Gaussian the defines the metallicity distribution function (MDF) at fixed logAge. If this argument is omitted, σ will be a free parameter in the fit.

Keyword Arguments

  • x0 is the vector of initial guesses for the stellar mass coefficients per unique entry in logAge, plus the variables that define the metallicity evolution model. You should basically always be calculating and passing this keyword argument. We provide construct_x0_mdf to prepare the first part of x0 assuming constant star formation rate, which is typically a good initial guess. You then have to concatenate that result with an initial guess for the metallicity evolution parameters. For example, x0=vcat(construct_x0_mdf(logAge, 13.7; normalize_value=1e4), [1e-4, 5e-5, 0.2]), where logAge is a valid argument for this function (see above), and the initial guesses on the parameters are [α, β, σ] = [1e-4, 5e-5, 0.2]. If you provide σ as an optional argument, then you should not include an entry for it in x0.
  • MH_func is a callable that takes a metal mass fraction Z and returns the logarithmic abundance [M/H]; by default uses MH_from_Z.
  • MH_deriv_Z is a callable that takes a metal mass fraction Zj and returns the derivative of MH_func with respect to the metal mass fraction Z evaluated at Zj. For the default value of MH_func, dMH_dZ provides the correct derivative. You only need to change this if you use an alternate MH_func.
  • Other kws... are passed to Optim.options to set things like convergence criteria for the optimization.
source

Sampling Functions

Fixed Logarithmic Age-Metallicity Relation

We support fitting only the star formation parameters by adopting fixed values for $\alpha$, $\beta$, and $\sigma$ through the fixed_log_amr method.

StarFormationHistories.fixed_log_amrFunction
fixed_log_amr(models,
+                              kws...) where {S <: Number}

Method that fits a linear combination of the provided Hess diagrams models to the observed Hess diagram data, constrained to have a logarithmic age-metallicity relation with the mean metal mass fraction μ_Z of element i of unique(logAge) being μ_Z[i] = α * (T_max - exp10(unique(logAge)[i]) / 1e9) + β. This is converted to a mean metallicity in [M/H] via the provided callable keyword argument MH_func which defaults to MH_from_Z. α is therefore a slope in the units of inverse Gyr, and β is the mean metal mass fraction of stars born at the earliest valid lookback time, determined by keyword argument T_max which has units of Gyr. Individual weights for each isochrone template are then determined via Gaussian weighting with the above mean [M/H] and the standard deviation σ in dex, which can either be fixed or fit.

This function is designed to work best with a "grid" of stellar models, defined by the outer product of N unique entries in logAge and M unique entries in metallicities. See the examples for more information on usage.

The second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.

Arguments

  • models are the template Hess diagrams for the simple stellar populations that compose the observed Hess diagram.
  • data is the Hess diagram for the observed data.
  • logAge::AbstractVector{<:Number} is the vector containing the effective ages of the stellar populations used to create the templates in models, in units of log10(age [yr]). For example, if a population has an age of 1 Myr, its entry in logAge should be log10(10^6) = 6.0.
  • metallicities::AbstractVector{<:Number} is the vector containing the effective metallicities of the stellar populations used to create the templates in models. This should be a logarithmic abundance like [M/H] or [Fe/H].
  • T_max::Number is the maximum lookback time in Gyr for which the age-metallicity relation is to be valid. In most cases this should just be the maximum of the logAge vector argument maximum(logAge) or slightly larger. By definition, this is the lookback time at which μ_Z = β.

Optional Arguments

  • If provided, σ::Number is the fixed width of the Gaussian the defines the metallicity distribution function (MDF) at fixed logAge. If this argument is omitted, σ will be a free parameter in the fit.

Keyword Arguments

  • x0 is the vector of initial guesses for the stellar mass coefficients per unique entry in logAge, plus the variables that define the metallicity evolution model. You should basically always be calculating and passing this keyword argument. We provide construct_x0_mdf to prepare the first part of x0 assuming constant star formation rate, which is typically a good initial guess. You then have to concatenate that result with an initial guess for the metallicity evolution parameters. For example, x0=vcat(construct_x0_mdf(logAge, 13.7; normalize_value=1e4), [1e-4, 5e-5, 0.2]), where logAge is a valid argument for this function (see above), and the initial guesses on the parameters are [α, β, σ] = [1e-4, 5e-5, 0.2]. If you provide σ as an optional argument, then you should not include an entry for it in x0.
  • MH_func is a callable that takes a metal mass fraction Z and returns the logarithmic abundance [M/H]; by default uses MH_from_Z.
  • MH_deriv_Z is a callable that takes a metal mass fraction Zj and returns the derivative of MH_func with respect to the metal mass fraction Z evaluated at Zj. For the default value of MH_func, dMH_dZ provides the correct derivative. You only need to change this if you use an alternate MH_func.
  • Other kws... are passed to Optim.options to set things like convergence criteria for the optimization.
source

Sampling Functions

Fixed Logarithmic Age-Metallicity Relation

We support fitting only the star formation parameters by adopting fixed values for $\alpha$, $\beta$, and $\sigma$ through the fixed_log_amr method.

StarFormationHistories.fixed_log_amrFunction
fixed_log_amr(models,
               data,
               logAge::AbstractVector{<:Number},
               metallicities::AbstractVector{<:Number},
@@ -37,7 +37,7 @@
               β::Number,
               σ::Number;
               MH_func = StarFormationHistories.MH_from_Z,
-              kws...)

Given a fully specified logarithmic age-metallicity relation with parameters (α, β, σ), fits maximum likelihood and maximum a posteriori star formation parameters. MH_func is a callable that returns a logarithmic metallicity [M/H] for a metal mass fraction argument and defaults to MH_from_Z. T_max is the lookback time in Gyr at which the mean metal mass fraction is eta. See fixed_amr for info on format of returned result.

source
fixed_log_amr(models,
+              kws...)

Given a fully specified logarithmic age-metallicity relation with parameters (α, β, σ), fits maximum likelihood and maximum a posteriori star formation parameters. MH_func is a callable that returns a logarithmic metallicity [M/H] for a metal mass fraction argument and defaults to MH_from_Z. T_max is the lookback time in Gyr at which the mean metal mass fraction is eta. See fixed_amr for info on format of returned result.

source
fixed_log_amr(models,
               data,
               logAge::AbstractVector{<:Number},
               metallicities::AbstractVector{<:Number},
@@ -46,11 +46,11 @@
               constraint2,
               σ::Number;
               Z_func = StarFormationHistories.Z_from_MH,
-              kws...)

Call signature that takes two fixed points low_constraint and high_constraint that define points that must lie on the logarithmic age-metallicity relation and calculates the slope paramters α and β for you. Format is ([M/H], age [Gyr]), i.e. constraint1 = (-2.5, 13.7) for the first point at [M/H] = -2.5 at 13.7 Gyr lookback time and constraint2 = (-0.8, 0.0) for the second point at [M/H] = -0.8 at present-day (0.0 Gyr lookback time). The AMR is normalized so that the mean metal mass fraction at a lookback time in Gyr of T_max is Z = β. Metallicities in [M/H] are converted to metal mass fractions Z via the provided callable keyword argument Z_func which defaults to Z_from_MH. See fixed_amr for info on format of returned result.

source

We provide the calculate_αβ_logamr convenience function to calculate the slope $\alpha$ and intercept $\beta$ from two points on the age-metallicity relation.

StarFormationHistories.calculate_αβ_logamrFunction
(α, β) = calculate_αβ_logamr(low_constraint,
+              kws...)

Call signature that takes two fixed points low_constraint and high_constraint that define points that must lie on the logarithmic age-metallicity relation and calculates the slope paramters α and β for you. Format is ([M/H], age [Gyr]), i.e. constraint1 = (-2.5, 13.7) for the first point at [M/H] = -2.5 at 13.7 Gyr lookback time and constraint2 = (-0.8, 0.0) for the second point at [M/H] = -0.8 at present-day (0.0 Gyr lookback time). The AMR is normalized so that the mean metal mass fraction at a lookback time in Gyr of T_max is Z = β. Metallicities in [M/H] are converted to metal mass fractions Z via the provided callable keyword argument Z_func which defaults to Z_from_MH. See fixed_amr for info on format of returned result.

source

We provide the calculate_αβ_logamr convenience function to calculate the slope $\alpha$ and intercept $\beta$ from two points on the age-metallicity relation.

StarFormationHistories.calculate_αβ_logamrFunction
(α, β) = calculate_αβ_logamr(low_constraint,
                              high_constraint,
                              T_max,
                              Z_func=Z_from_MH)

Calculates linear Z (log [M/H]) age-metallicity relation (AMR) slope α and intercept β from two points on the line with form ([M/H], age [Gyr]) given by the first two arguments. The AMR is normalized so that the mean metal mass fraction at a lookback time in Gyr of T_max is Z = β. More info given in fixed_log_amr.

Examples

julia> calculate_αβ_logamr((-2.5, 13.7), (-1.0, 0.0), 13.7) isa NTuple{2,Float64}
-true
source

Implementation

As the only part of the model that differs from the linear AMR case is the mean age-metallicity relation, most of the derivation for the linear AMR case is still valid here. In particular, only the partial derivatives of the relative weights $A_{j,k} \equiv \text{exp} \left( -\frac{1}{2 \, \sigma^2} \, \left( [\text{M}/\text{H}]_k - \mu_j \right)^2\right)$ with respect to the fitting parameters $\alpha$ and $\beta$ need to be recalculated under the new model. The partial derivative with respect to $\sigma$ is the same, as the mean metallicity in time bin $j$, denoted $\mu_j$, does not depend on $\sigma$.

\[\begin{aligned} +truesource

Implementation

As the only part of the model that differs from the linear AMR case is the mean age-metallicity relation, most of the derivation for the linear AMR case is still valid here. In particular, only the partial derivatives of the relative weights $A_{j,k} \equiv \text{exp} \left( -\frac{1}{2 \, \sigma^2} \, \left( [\text{M}/\text{H}]_k - \mu_j \right)^2\right)$ with respect to the fitting parameters $\alpha$ and $\beta$ need to be recalculated under the new model. The partial derivative with respect to $\sigma$ is the same, as the mean metallicity in time bin $j$, denoted $\mu_j$, does not depend on $\sigma$.

\[\begin{aligned} Z_j &\equiv \langle Z \left(t_j\right) \rangle = \alpha \, \left( T_\text{max} - t_j \right) + \beta \\ \\ \mu_j &\equiv \langle [\text{M}/\text{H}] \rangle \left(t_j\right) = \text{log} \left( \frac{\langle Z\left(t_j\right) \rangle}{X_j} \right) - \text{log} \left( \frac{Z_\odot}{X_\odot} \right) \\ @@ -83,4 +83,4 @@ %% \frac{\partial \, A_{j,k}}{\partial \, \beta} &= \frac{\partial \, A_{j,k}}{\partial \, \mu_j} \, \frac{\partial \mu_j}{\partial \beta} = \left( \frac{A_{j,k} \, \left( [\text{M}/\text{H}]_k - \mu_j \right)}{\sigma^2} \right) \, \left( \frac{1}{\left( t_j \, \alpha + \beta \right) \, \text{ln}(10)} \right) \\ %% &= \frac{A_{j,k} \, \left( [\text{M}/\text{H}]_k - \mu_j \right)}{\text{ln}(10) \, \sigma^2 \, \left( t_j \, \alpha + \beta \right)} \\ %% \frac{\partial \, A_{j,k}}{\partial \, \alpha} &= \frac{\partial \, A_{j,k}}{\partial \, \mu_j} \, \frac{\partial \mu_j}{\partial \alpha} = t \, \frac{\partial \, A_{j,k}}{\partial \, \beta} -\end{aligned}\]

+\end{aligned}\]

diff --git a/dev/fitting/log_amr_plot.svg b/dev/fitting/log_amr_plot.svg index 58e6867..ee3289a 100644 --- a/dev/fitting/log_amr_plot.svg +++ b/dev/fitting/log_amr_plot.svg @@ -1,81 +1,81 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - - - - - - - - + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - - - - - - - - + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - - - - - - - + + + + + + + + diff --git a/dev/fitting/unconstrained/index.html b/dev/fitting/unconstrained/index.html index 45abc24..02cf118 100644 --- a/dev/fitting/unconstrained/index.html +++ b/dev/fitting/unconstrained/index.html @@ -5,29 +5,29 @@ 9-element Vector{Float64}: ... julia> sum(x0) -4.99... # Close to `normalize_value`.source

When it comes to performing the optimization, the simplest method we offer is StarFormationHistories.fit_templates_lbfgsb. This will optimize one coefficient per template; there is no overarching metallicity evolution or other constraint, besides that the stellar masses of the populations cannot be negative. This performs a maximum likelihood optimization with the bounded quasi-Newton LBFGS method as implemented in L-BFGS-B and wrapped in LBFGS.jl with analytic gradients. It is fast and converges fairly reliably, even when the initial guess is not particularly close to the maximum likelihood estimate. It provides no uncertainty estimation. It is normal for some of the coefficients to converge to zero.

When it comes to performing the optimization, the simplest method we offer is StarFormationHistories.fit_templates_lbfgsb. This will optimize one coefficient per template; there is no overarching metallicity evolution or other constraint, besides that the stellar masses of the populations cannot be negative. This performs a maximum likelihood optimization with the bounded quasi-Newton LBFGS method as implemented in L-BFGS-B and wrapped in LBFGS.jl with analytic gradients. It is fast and converges fairly reliably, even when the initial guess is not particularly close to the maximum likelihood estimate. It provides no uncertainty estimation. It is normal for some of the coefficients to converge to zero.

StarFormationHistories.fit_templates_lbfgsbFunction
(-logL, coeffs) = 
 fit_templates_lbfgsb(models::AbstractVector{T},
                      data::AbstractMatrix{<:Number};
                      x0::AbstractVector{<:Number} = ones(S,length(models)),
                      factr::Number=1e-12,
                      pgtol::Number=1e-5,
                      iprint::Integer=0,
-                     kws...) where {S <: Number, T <: AbstractMatrix{S}}

Finds the coefficients coeffs that maximize the Poisson likelihood ratio (equations 7–10 in Dolphin 2002) for the composite Hess diagram model sum(models .* coeffs) given the provided templates models and the observed Hess diagram data using the box-constrained LBFGS method provided by LBFGSB.jl.

Arguments

  • models::AbstractVector{AbstractMatrix{<:Number}}: the list of template Hess diagrams for the simple stellar populations (SSPs) being considered; all must have the same size.
  • data::AbstractMatrix{<:Number}: the observed Hess diagram; must match the size of the templates contained in models.

Keyword Arguments

  • x0: The vector of initial guesses for the stellar mass coefficients. You should basically always be calculating and passing this keyword argument; we provide StarFormationHistories.construct_x0 to prepare x0 assuming constant star formation rate, which is typically a good initial guess.
  • factr::Number: Keyword argument passed to LBFGSB.lbfgsb; essentially a relative tolerance for convergence based on the inter-iteration change in the objective function.
  • pgtol::Number: Keyword argument passed to LBFGSB.lbfgsb; essentially a relative tolerance for convergence based on the inter-iteration change in the projected gradient of the objective.
  • iprint::Integer: Keyword argument passed to LBFGSB.lbfgsb controlling how much information is printed to the terminal. Setting to 1 can sometimes be helpful to diagnose convergence issues. Setting to -1 will disable printing.

Other kws... are passed to LBFGSB.lbfgsb.

Returns

  • -logL::Number: the minimum negative log-likelihood found by the optimizer.
  • coeffs::Vector{<:Number}: the maximum likelihood estimate for the coefficient vector.

Notes

  • It can be helpful to normalize your models to contain realistic total stellar masses to aid convergence stability; for example, if the total stellar mass of your population is 10^7 solar masses, then you might normalize your templates to contain 10^3 solar masses. If you are using partial_cmd_smooth to construct the templates, you can specify this normalization via the normalize_value keyword.
source
fit_templates_lbfgsb(models::AbstractMatrix{S},
+                     kws...) where {S <: Number, T <: AbstractMatrix{S}}

Finds the coefficients coeffs that maximize the Poisson likelihood ratio (equations 7–10 in Dolphin 2002) for the composite Hess diagram model sum(models .* coeffs) given the provided templates models and the observed Hess diagram data using the box-constrained LBFGS method provided by LBFGSB.jl.

Arguments

  • models::AbstractVector{AbstractMatrix{<:Number}}: the list of template Hess diagrams for the simple stellar populations (SSPs) being considered; all must have the same size.
  • data::AbstractMatrix{<:Number}: the observed Hess diagram; must match the size of the templates contained in models.

Keyword Arguments

  • x0: The vector of initial guesses for the stellar mass coefficients. You should basically always be calculating and passing this keyword argument; we provide StarFormationHistories.construct_x0 to prepare x0 assuming constant star formation rate, which is typically a good initial guess.
  • factr::Number: Keyword argument passed to LBFGSB.lbfgsb; essentially a relative tolerance for convergence based on the inter-iteration change in the objective function.
  • pgtol::Number: Keyword argument passed to LBFGSB.lbfgsb; essentially a relative tolerance for convergence based on the inter-iteration change in the projected gradient of the objective.
  • iprint::Integer: Keyword argument passed to LBFGSB.lbfgsb controlling how much information is printed to the terminal. Setting to 1 can sometimes be helpful to diagnose convergence issues. Setting to -1 will disable printing.

Other kws... are passed to LBFGSB.lbfgsb.

Returns

  • -logL::Number: the minimum negative log-likelihood found by the optimizer.
  • coeffs::Vector{<:Number}: the maximum likelihood estimate for the coefficient vector.

Notes

  • It can be helpful to normalize your models to contain realistic total stellar masses to aid convergence stability; for example, if the total stellar mass of your population is 10^7 solar masses, then you might normalize your templates to contain 10^3 solar masses. If you are using partial_cmd_smooth to construct the templates, you can specify this normalization via the normalize_value keyword.
source
fit_templates_lbfgsb(models::AbstractMatrix{S},
                      data::AbstractVector{<:Number};
                      x0::AbstractVector{<:Number} = ones(S,size(models,2)),
                      factr::Number=1e-12,
                      pgtol::Number=1e-5,
                      iprint::Integer=0,
-                     kws...) where S <: Number

This call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.

source

This method simply minimizes the negative logarithm of the Poisson likelihood ratio (Equation 10 in Dolphin 2002),

\[- \text{ln} \, \mathscr{L} = \sum_i m_i - n_i \times \left( 1 - \text{ln} \, \left( \frac{n_i}{m_i} \right) \right)\]

where $m_i$ is bin $i$ of the complex model and $n_i$ is bin $i$ of the observed Hess diagram; this can therefore be thought of as computing the maximum likelihood estimate.

We also provide StarFormationHistories.fit_templates_fast, which is the fastest method we offer for deriving a maximum likelihood estimate for the type of model described above.

This method simply minimizes the negative logarithm of the Poisson likelihood ratio (Equation 10 in Dolphin 2002),

\[- \text{ln} \, \mathscr{L} = \sum_i m_i - n_i \times \left( 1 - \text{ln} \, \left( \frac{n_i}{m_i} \right) \right)\]

where $m_i$ is bin $i$ of the complex model and $n_i$ is bin $i$ of the observed Hess diagram; this can therefore be thought of as computing the maximum likelihood estimate.

We also provide StarFormationHistories.fit_templates_fast, which is the fastest method we offer for deriving a maximum likelihood estimate for the type of model described above.

StarFormationHistories.fit_templates_fastFunction
(coeffs::Vector{::eltype(x0)}, result::Optim.MultivariateOptimizationResults) =
 fit_templates_fast(models::AbstractVector{T},
                    data::AbstractMatrix{<:Number};
                    x0::AbstractVector{<:Number} = ones(S,length(models)),
                    kws...)
-                   where {S <: Number, T <: AbstractMatrix{S}}

Finds only the maximum likelihood estimate (MLE) for the coefficients coeffs given the provided data such that the best-fit composite Hess diagram model is sum(models .* coeffs). This is a simplification of the main fit_templates function, which will return the MLE as well as the maximum a posteriori estimate, and further supports uncertainty quantification. For additional details on arguments to this method, see the documentation for fit_templates.

This method optimizes parameters θ such that coeffs = θ.^2 – this allows for faster convergence than both the fit_templates_lbfgsb method, which does not use a variable transformation, and the logarithmic transformation used in fit_templates. However, the inverse Hessian is not useful for uncertainty estimation under this transformation. As such this method only returns the MLE for coeffs as a vector and the result object returned by Optim.optimize. While this method offers fewer features than fit_templates, this method's runtime is typically half as long (or less). As such, this method is recommended for use in performance-sensitive applications like hierarchical models or hyperparameter estimation where the additional features of fit_templates are unnecessary. In these applications, the value of the objective function at the derived MLE is typically desired as well; this can be obtained the from result::Optim.MultivariateOptimizationResults object as Optim.minimum(result). Note that this will return the negative loglikelihood, which is what is minimized in this application.

Notes

  1. By passing additional convergence keyword arguments supported by Optim.Options (see this guide), it is possible to converge to the MLE in fewer than 30 iterations with fewer than 100 calls to the likelihood and gradient methods. For example, the main convergence criterion is typically the magnitude of the gradient vector, which by default is g_abstol=1e-8, terminating the optimization when the magnitude of the gradient is less than 1e-8. We find results are typically sufficiently accurate with g_abstol=1e-3, which often uses half as many objective evaluations as the default value.
source
fit_templates_fast(models::AbstractMatrix{S},
+                   where {S <: Number, T <: AbstractMatrix{S}}

Finds only the maximum likelihood estimate (MLE) for the coefficients coeffs given the provided data such that the best-fit composite Hess diagram model is sum(models .* coeffs). This is a simplification of the main fit_templates function, which will return the MLE as well as the maximum a posteriori estimate, and further supports uncertainty quantification. For additional details on arguments to this method, see the documentation for fit_templates.

This method optimizes parameters θ such that coeffs = θ.^2 – this allows for faster convergence than both the fit_templates_lbfgsb method, which does not use a variable transformation, and the logarithmic transformation used in fit_templates. However, the inverse Hessian is not useful for uncertainty estimation under this transformation. As such this method only returns the MLE for coeffs as a vector and the result object returned by Optim.optimize. While this method offers fewer features than fit_templates, this method's runtime is typically half as long (or less). As such, this method is recommended for use in performance-sensitive applications like hierarchical models or hyperparameter estimation where the additional features of fit_templates are unnecessary. In these applications, the value of the objective function at the derived MLE is typically desired as well; this can be obtained the from result::Optim.MultivariateOptimizationResults object as Optim.minimum(result). Note that this will return the negative loglikelihood, which is what is minimized in this application.

Notes

  1. By passing additional convergence keyword arguments supported by Optim.Options (see this guide), it is possible to converge to the MLE in fewer than 30 iterations with fewer than 100 calls to the likelihood and gradient methods. For example, the main convergence criterion is typically the magnitude of the gradient vector, which by default is g_abstol=1e-8, terminating the optimization when the magnitude of the gradient is less than 1e-8. We find results are typically sufficiently accurate with g_abstol=1e-3, which often uses half as many objective evaluations as the default value.
source
fit_templates_fast(models::AbstractMatrix{S},
                    data::AbstractVector{<:Number};
                    x0::AbstractVector{<:Number} = ones(S,size(models,2)),
                    kws...)
-                   where S <: Number

This call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.

source

Posterior Sampling: MCMC

For low-dimensional problems, Markov Chain Monte Carlo (MCMC) methods can be an efficient way to sample the posterior and obtain uncertainty estimates on the fitting coefficients $r_j$. We provide StarFormationHistories.mcmc_sample for this purpose. Internally this uses the multi-threaded affine-invariant MCMC sampler from KissMCMC.jl to perform the sampling, which is based on the same algorithm as Python's emcee (specifically, their emcee.moves.StretchMove). There are other MCMC packages like AdvancedMH.jl that offer additional features like distributed execution.

Posterior Sampling: MCMC

For low-dimensional problems, Markov Chain Monte Carlo (MCMC) methods can be an efficient way to sample the posterior and obtain uncertainty estimates on the fitting coefficients $r_j$. We provide StarFormationHistories.mcmc_sample for this purpose. Internally this uses the multi-threaded affine-invariant MCMC sampler from KissMCMC.jl to perform the sampling, which is based on the same algorithm as Python's emcee (specifically, their emcee.moves.StretchMove). There are other MCMC packages like AdvancedMH.jl that offer additional features like distributed execution.

StarFormationHistories.mcmc_sampleFunction
result::MCMCChains.Chains =
 mcmc_sample(models::AbstractVector{<:AbstractMatrix{T}},
             data::AbstractMatrix{S},
             x0::Union{AbstractVector{<:AbstractVector{<:Number}}, AbstractMatrix{<:Number}},
@@ -47,7 +47,7 @@
 nsteps = 400
 x0 = rand(nwalkers, length(coeffs)) # Initial walker positions
 result = mcmc_sample(models, data, x0, nwalkers, nsteps) # Sample
-Chains MCMC chain (400×10×1000 Array{Float64, 3}) ...
source

Posterior Sampling: Change of Variables and HMC

Dolphin 2013 examined methods for obtaining uncertainties on the fitted coefficients (the $r_j$ in Equation 1 of Dolphin 2002) and found that the Hamiltonian Monte Carlo (HMC) approach allowed for relatively efficient sampling of the posterior distribution when considering many isochrones in the modelling process. HMC requires that the variables to be fit are continuous over the real numbers and so requires a change of variables. Rather than sampling the variables $r_j$ directly, we can sample $\theta_j = \text{ln} \left( r_j \right)$ such that the sampled variables are continuous over the real numbers $-\infty < \theta_j < \infty$ while the $r_j=\text{exp} \left( \theta_j \right)$ coefficients are bounded from $0 < r_j < \infty$. Using a logarithmic transformation has the additional benefit that the gradient of the Poisson likelihood ratio is still continuous and easy to compute analytically.

While maximum likelihood estimates are invariant under variable transformations, sampling methods like HMC are not, as formally the posterior being sampled from is a distribution and therefore must be integrable over the sampling coefficients. We can write the posterior from which we wish to sample as

\[\begin{aligned} +Chains MCMC chain (400×10×1000 Array{Float64, 3}) ...source

Posterior Sampling: Change of Variables and HMC

Dolphin 2013 examined methods for obtaining uncertainties on the fitted coefficients (the $r_j$ in Equation 1 of Dolphin 2002) and found that the Hamiltonian Monte Carlo (HMC) approach allowed for relatively efficient sampling of the posterior distribution when considering many isochrones in the modelling process. HMC requires that the variables to be fit are continuous over the real numbers and so requires a change of variables. Rather than sampling the variables $r_j$ directly, we can sample $\theta_j = \text{ln} \left( r_j \right)$ such that the sampled variables are continuous over the real numbers $-\infty < \theta_j < \infty$ while the $r_j=\text{exp} \left( \theta_j \right)$ coefficients are bounded from $0 < r_j < \infty$. Using a logarithmic transformation has the additional benefit that the gradient of the Poisson likelihood ratio is still continuous and easy to compute analytically.

While maximum likelihood estimates are invariant under variable transformations, sampling methods like HMC are not, as formally the posterior being sampled from is a distribution and therefore must be integrable over the sampling coefficients. We can write the posterior from which we wish to sample as

\[\begin{aligned} p(r_j | D) &= \frac{p(D | r_j) \; p(r_j)}{Z} \\ p(\boldsymbol{r} | D) &= \frac{1}{Z} \; \prod_j p(D | r_j) \; p(r_j) \\ -\text{ln} \; p(\boldsymbol{r} | D) &= \text{ln} \, Z - \sum_j \, \text{ln} \, p(D | r_j) + \text{ln} \, p(r_j) \\ @@ -106,16 +106,16 @@ t_result = hmc_sample( models, data, 1000, Threads.nthreads(); reporter=DynamicHMC.ProgressMeterReport()) # Combine the multiple chains into a single matrix and transform # Can then use the same way as `mc_matrix` above -mc_matrix = exp.( DynamicHMC.pool_posterior_matrices(t_result) )source

See the DynamicHMC.jl documentation for more information on how to use the chains that are output by this method.

Inspection of the samples generated by hmc_sample shows that the posterior defined by the above model is typically smooth, well-behaved, and unimodal. In particular, we find that the sampled $r_j$ for coefficients that are non-zero in the MLE are approximately Gaussian distributed while the logarithms of the sampled $r_j$ are roughly Gaussian distributed for coefficients that are zero in the MLE; i.e.

\[\begin{cases} +mc_matrix = exp.( DynamicHMC.pool_posterior_matrices(t_result) )source

See the DynamicHMC.jl documentation for more information on how to use the chains that are output by this method.

Inspection of the samples generated by hmc_sample shows that the posterior defined by the above model is typically smooth, well-behaved, and unimodal. In particular, we find that the sampled $r_j$ for coefficients that are non-zero in the MLE are approximately Gaussian distributed while the logarithms of the sampled $r_j$ are roughly Gaussian distributed for coefficients that are zero in the MLE; i.e.

\[\begin{cases} X_j \sim \mathcal{N}; & \hat r_j > 0 \\ \text{ln} \left( X_j \right) \sim \mathcal{N}; & \hat r_j = 0 \\ \end{cases}\]

where $X_j$ are the samples of $r_j$ obtained from the posterior and $\hat r_j$ is the maximum likelihood estimate of $r_j$.

This indicates we may be able to approximate the posterior in the region surrounding the maximum a posteriori (MAP) value by the inverse of the Hessian matrix (see, e.g., Dovi et al. 1991), allowing us to estimate parameter uncertainties very cheaply. The inverse of the Hessian matrix is exactly equal to the variance-covariance matrix of the parameters for a Gaussian probability distribution; for other probability distributions, the inverse of the Hessian approximates the variance-covariance matrix of the parameters when the second-order expansion defined by the Hessian at the maximum is a reasonable approximation to the real objective function being optimized. A particularly simple form arises when the logarithm of the objective is quadratic in the fitting parameters, as in the Gaussian case, because the second derivatives of the objective are constant and do not depend on the fitting parameters or the MAP estimate.

Maximum a Posteriori Optimization

Direct computation of the Hessian and its inverse is expensive, so we'd like another way to obtain it. The first-order, quasi-Newton BFGS optimization algorithm provides such a method as it iteratively builds a dense approximation to the inverse Hessian using the change in the gradient of the objective, which we can compute analytically. It is, however, much less memory efficient than the LBFGS algorithm we use in StarFormationHistories.fit_templates_lbfgsb. For moderate isochrone grids up to a few hundred model templates, this is not a problem. Beyond this it may be better to use StarFormationHistories.fit_templates_lbfgsb to obtain the MLE and hmc_sample to obtain posterior samples.

We implement this optimization scheme in fit_templates, which is our recommended method for unconstrained SFH fitting (i.e., direct fitting of the $r_j$ coefficients). See the next section for notes on more complicated, hierarchical models that can incorporate features like metallicity distribution functions.

StarFormationHistories.fit_templatesFunction
result = fit_templates(models::AbstractVector{T},
                        data::AbstractMatrix{<:Number};
                        x0::AbstractVector{<:Number} = ones(S,length(models)),
-                       kws...) where {S <: Number, T <: AbstractMatrix{S}}

Finds both maximum likelihood estimate (MLE) and maximum a posteriori estimate (MAP) for the coefficients coeffs such that the composite Hess diagram model is sum(models .* coeffs) using the provided templates models and the observed Hess diagram data. Utilizes the Poisson likelihood ratio (equations 7–10 in Dolphin 2002) for the likelihood of the data given the model. See the examples in the documentation for comparisons of the results of this method and hmc_sample which samples the posterior via Hamiltonian Monte Carlo.

Arguments

  • models::AbstractVector{AbstractMatrix{<:Number}}: the list of template Hess diagrams for the simple stellar populations (SSPs) being considered; all must have the same size.
  • data::AbstractMatrix{<:Number}: the observed Hess diagram; must match the size of the templates contained in models.

Keyword Arguments

  • x0: The vector of initial guesses for the stellar mass coefficients. You should basically always be calculating and passing this keyword argument; we provide StarFormationHistories.construct_x0 to prepare x0 assuming constant star formation rate, which is typically a good initial guess.

Other kws... are passed to Optim.options to set things like convergence criteria for the optimization.

Returns

result is a NamedTuple containing two StarFormationHistories.LogTransformFTResult. The two components of result are result.map or result[1], which contains the results of the MAP optimization, and result.mle or result[2], which contains the results of the MLE optimization. The documentation for StarFormationHistories.LogTransformFTResult contains more information about these types, but briefly they contain the following fields, accessible as, e.g., result.map.μ, result.map.σ, etc.:

  • μ::Vector{<:Number} are the optimized coeffs at the maximum.
  • σ::Vector{<:Number} are the standard errors on the coeffs μ calculated from an estimate of the inverse Hessian matrix evaluated at μ. The inverse of the Hessian matrix at the maximum of the likelihood (or posterior) is a estimator for the variance-covariance matrix of the parameters, but is only accurate when the second-order expansion given by the Hessian at the maximum is a good approximation to the function being optimized (i.e., when the optimized function is approximately quadratic around the maximum; see Dovi et al. 1991 for more information). We find this is often the case for the MAP estimate, but the errors found for coefficients that are ≈0 in the MLE are typically unrealistically small. For coefficients significantly greater than 0, the σ values from the MAP and MLE are typically consistent to 5–10%.
  • invH::Matrix{<:Number} is the estimate of the inverse Hessian matrix at μ that was used to derive σ. The optimization is done under a logarithmic transform, such that θ[j] = log(coeffs[j]) are the actual parameters optimized, so the entries in the Hessian are actually

\[H^{(j,k)} ( \boldsymbol{\hat \theta} ) = \left. \frac{\partial^2 \, J(\boldsymbol{\theta})}{\partial \theta_j \, \partial \theta_k} \right\vert_{\boldsymbol{\theta}=\boldsymbol{\hat \theta}}\]

  • result is the full object returned by the optimization from Optim.jl; this is of type Optim.MultivariateOptimizationResults. Remember that the optimization is done with parameters θ[j] = log(coeffs[j]) when dealing with this raw output. This means that, for example, we calculate result.map.μ as exp.(Optim.minimizer(result.map.result)).

The special property of the StarFormationHistories.LogTransformFTResult type is that you can draw a set of N::Integer random parameter samples from the result using the inverse Hessian approximation discussed above by doing rand(result.map, N). This type implements the random sampling API of Distributions.jl so the other standard sampling methods should work as well. In our tests these samples compare very favorably against those from hmc_sample, which samples the posterior via Hamiltonian Monte Carlo and is therefore more robust but much more expensive. We compare these methods in the examples.

Notes

  • This method uses the BFGS method from Optim.jl internally because it builds and tracks the inverse Hessian matrix approximation which can be used to estimate parameter uncertainties. BFGS is much more memory-intensive than LBFGS (as used for StarFormationHistories.fit_templates_lbfgsb) for large numbers of parameters (equivalently, many models), so you should consider LBFGS to solve for the MLE along with hmc_sample to sample the posterior if you are using a large grid of models (greater than a few hundred).
  • The BFGS implementation we use from Optim.jl uses BLAS operations during its iteration. The OpenBLAS that Julia ships with will often default to running on multiple threads even if Julia itself is started with only a single thread. You can check the current number of BLAS threads with import LinearAlgebra: BLAS; BLAS.get_num_threads(). For the problem sizes typical of this function we actually see performance regression with larger numbers of BLAS threads. For this reason you may wish to use BLAS in single-threaded mode; you can set this as import LinearAlgebra: BLAS; BLAS.set_num_threads(1).
source
fit_templates(models::AbstractMatrix{S},
+                       kws...) where {S <: Number, T <: AbstractMatrix{S}}

Finds both maximum likelihood estimate (MLE) and maximum a posteriori estimate (MAP) for the coefficients coeffs such that the composite Hess diagram model is sum(models .* coeffs) using the provided templates models and the observed Hess diagram data. Utilizes the Poisson likelihood ratio (equations 7–10 in Dolphin 2002) for the likelihood of the data given the model. See the examples in the documentation for comparisons of the results of this method and hmc_sample which samples the posterior via Hamiltonian Monte Carlo.

Arguments

  • models::AbstractVector{AbstractMatrix{<:Number}}: the list of template Hess diagrams for the simple stellar populations (SSPs) being considered; all must have the same size.
  • data::AbstractMatrix{<:Number}: the observed Hess diagram; must match the size of the templates contained in models.

Keyword Arguments

  • x0: The vector of initial guesses for the stellar mass coefficients. You should basically always be calculating and passing this keyword argument; we provide StarFormationHistories.construct_x0 to prepare x0 assuming constant star formation rate, which is typically a good initial guess.

Other kws... are passed to Optim.options to set things like convergence criteria for the optimization.

Returns

result is a NamedTuple containing two StarFormationHistories.LogTransformFTResult. The two components of result are result.map or result[1], which contains the results of the MAP optimization, and result.mle or result[2], which contains the results of the MLE optimization. The documentation for StarFormationHistories.LogTransformFTResult contains more information about these types, but briefly they contain the following fields, accessible as, e.g., result.map.μ, result.map.σ, etc.:

  • μ::Vector{<:Number} are the optimized coeffs at the maximum.
  • σ::Vector{<:Number} are the standard errors on the coeffs μ calculated from an estimate of the inverse Hessian matrix evaluated at μ. The inverse of the Hessian matrix at the maximum of the likelihood (or posterior) is a estimator for the variance-covariance matrix of the parameters, but is only accurate when the second-order expansion given by the Hessian at the maximum is a good approximation to the function being optimized (i.e., when the optimized function is approximately quadratic around the maximum; see Dovi et al. 1991 for more information). We find this is often the case for the MAP estimate, but the errors found for coefficients that are ≈0 in the MLE are typically unrealistically small. For coefficients significantly greater than 0, the σ values from the MAP and MLE are typically consistent to 5–10%.
  • invH::Matrix{<:Number} is the estimate of the inverse Hessian matrix at μ that was used to derive σ. The optimization is done under a logarithmic transform, such that θ[j] = log(coeffs[j]) are the actual parameters optimized, so the entries in the Hessian are actually

\[H^{(j,k)} ( \boldsymbol{\hat \theta} ) = \left. \frac{\partial^2 \, J(\boldsymbol{\theta})}{\partial \theta_j \, \partial \theta_k} \right\vert_{\boldsymbol{\theta}=\boldsymbol{\hat \theta}}\]

  • result is the full object returned by the optimization from Optim.jl; this is of type Optim.MultivariateOptimizationResults. Remember that the optimization is done with parameters θ[j] = log(coeffs[j]) when dealing with this raw output. This means that, for example, we calculate result.map.μ as exp.(Optim.minimizer(result.map.result)).

The special property of the StarFormationHistories.LogTransformFTResult type is that you can draw a set of N::Integer random parameter samples from the result using the inverse Hessian approximation discussed above by doing rand(result.map, N). This type implements the random sampling API of Distributions.jl so the other standard sampling methods should work as well. In our tests these samples compare very favorably against those from hmc_sample, which samples the posterior via Hamiltonian Monte Carlo and is therefore more robust but much more expensive. We compare these methods in the examples.

Notes

  • This method uses the BFGS method from Optim.jl internally because it builds and tracks the inverse Hessian matrix approximation which can be used to estimate parameter uncertainties. BFGS is much more memory-intensive than LBFGS (as used for StarFormationHistories.fit_templates_lbfgsb) for large numbers of parameters (equivalently, many models), so you should consider LBFGS to solve for the MLE along with hmc_sample to sample the posterior if you are using a large grid of models (greater than a few hundred).
  • The BFGS implementation we use from Optim.jl uses BLAS operations during its iteration. The OpenBLAS that Julia ships with will often default to running on multiple threads even if Julia itself is started with only a single thread. You can check the current number of BLAS threads with import LinearAlgebra: BLAS; BLAS.get_num_threads(). For the problem sizes typical of this function we actually see performance regression with larger numbers of BLAS threads. For this reason you may wish to use BLAS in single-threaded mode; you can set this as import LinearAlgebra: BLAS; BLAS.set_num_threads(1).
source
fit_templates(models::AbstractMatrix{S},
               data::AbstractVector{<:Number};
               x0::AbstractVector{<:Number} = ones(S,length(models)),
-              kws...) where S <: Number

This call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.

source
StarFormationHistories.LogTransformFTResultType
LogTransformFTResult(μ::AbstractVector{<:Number},
                      σ::AbstractVector{<:Number},
                      invH::AbstractMatrix{<:Number},
                      result)

Type for containing the maximum likelihood estimate (MLE) and maximum a posteriori (MAP) results from fit_templates. The fitted coefficients are available in the μ field. Estimates of the standard errors are available in the σ field. These have both been transformed from the native logarithmic fitting space into natural units (i.e., stellar mass or star formation rate).

invH contains the estimated inverse Hessian of the likelihood / posterior at the maximum point in the logarithmic fitting units. result is the full result object returned by the optimization routine.

This type is implemented as a subtype of Distributions.Sampleable{Multivariate, Continuous} to enable sampling from an estimate of the likelihood / posterior distribution. We approximate the distribution as a multivariate Gaussian in the native (logarithmically transformed) fitting variables with covariance matrix invH and means log.(μ). We find this approximation is good for the MAP result but less robust for the MLE. You can obtain N::Integer samples from the distribution by rand(R, N) where R is an instance of this type; this will return a size length(μ) x N matrix, or fail if invH is not positive definite.

Examples

julia> result = fit_templates(models, data);
@@ -124,10 +124,10 @@
 StarFormationHistories.LogTransformFTResult{...}
 
 julia> size(rand(result.map, 3)) == (length(models),3)
-true
source

Once you have obtained stellar mass coefficients from the above methods, you can convert them into star formation rates and compute per-age mean metallicities with StarFormationHistories.calculate_cum_sfr.

Once you have obtained stellar mass coefficients from the above methods, you can convert them into star formation rates and compute per-age mean metallicities with StarFormationHistories.calculate_cum_sfr.

StarFormationHistories.calculate_cum_sfrFunction
(unique_logAge, cum_sfh, sfr, mean_MH) =
     calculate_cum_sfr(coeffs::AbstractVector,
                       logAge::AbstractVector,
                       MH::AbstractVector,
                       T_max::Number;
                       normalize_value=1,
-                      sorted::Bool=false)

Calculates cumulative star formation history, star formation rates, and mean metallicity evolution as functions of logAge = log10(age [yr]).

Arguments

  • coeffs::AbstractVector is a vector of stellar mass coefficients such as those returned by fit_templates, for example. Actual stellar mass in stellar population j is coeffs[j] * normalize_value.
  • logAge::AbstractVector is a vector giving the log10(age [yr]) of the stellar populations corresponding to the provided coeffs. For the purposes of calculating star formation rates, these are assumed to be left-bin edges.
  • MH::AbstractVector is a vector giving the metallicities of the stellar populations corresponding to the provided coeffs.
  • T_max::Number is the rightmost final bin edge for calculating star formation rates. For example, you might have logAge=[6.6, 6.7, 6.8] in which case a final logAge of 6.9 would give equal bin widths. In this case you would set T_max = exp10(6.9) / 1e9 ≈ 0.0079 so that the width of the final bin for the star formation rate calculation has the same log10(Age [yr]) step as the other bins.

Keyword Arguments

  • normalize_value is a multiplicative prefactor to apply to all the coeffs; same as the keyword in partial_cmd_smooth.
  • sorted::Bool is either true or false and signifies whether to assume logAge is sorted.

Returns

  • unique_logAge::Vector is essentially unique(sort(logAge)) and provides the x-values you would plot the other returned vectors against.
  • cum_sfh::Vector is the normalized cumulative SFH implied by the provided coeffs. This is ~1 at the most recent time in logAge and decreases as logAge increases.
  • sfr::Vector gives the star formation rate across each bin in unique_logAge. If coeffs .* normalize_value are in units of solar masses, then sfr is in units of solar masses per year.
  • mean_MH::Vector gives the stellar-mass-weighted mean metallicity of the stellar population as a function of unique_logAge.
source
+ sorted::Bool=false)

Calculates cumulative star formation history, star formation rates, and mean metallicity evolution as functions of logAge = log10(age [yr]).

Arguments

  • coeffs::AbstractVector is a vector of stellar mass coefficients such as those returned by fit_templates, for example. Actual stellar mass in stellar population j is coeffs[j] * normalize_value.
  • logAge::AbstractVector is a vector giving the log10(age [yr]) of the stellar populations corresponding to the provided coeffs. For the purposes of calculating star formation rates, these are assumed to be left-bin edges.
  • MH::AbstractVector is a vector giving the metallicities of the stellar populations corresponding to the provided coeffs.
  • T_max::Number is the rightmost final bin edge for calculating star formation rates. For example, you might have logAge=[6.6, 6.7, 6.8] in which case a final logAge of 6.9 would give equal bin widths. In this case you would set T_max = exp10(6.9) / 1e9 ≈ 0.0079 so that the width of the final bin for the star formation rate calculation has the same log10(Age [yr]) step as the other bins.

Keyword Arguments

  • normalize_value is a multiplicative prefactor to apply to all the coeffs; same as the keyword in partial_cmd_smooth.
  • sorted::Bool is either true or false and signifies whether to assume logAge is sorted.

Returns

  • unique_logAge::Vector is essentially unique(sort(logAge)) and provides the x-values you would plot the other returned vectors against.
  • cum_sfh::Vector is the normalized cumulative SFH implied by the provided coeffs. This is ~1 at the most recent time in logAge and decreases as logAge increases.
  • sfr::Vector gives the star formation rate across each bin in unique_logAge. If coeffs .* normalize_value are in units of solar masses, then sfr is in units of solar masses per year.
  • mean_MH::Vector gives the stellar-mass-weighted mean metallicity of the stellar population as a function of unique_logAge.
source diff --git a/dev/helpers/index.html b/dev/helpers/index.html index 6c99989..5ced95c 100644 --- a/dev/helpers/index.html +++ b/dev/helpers/index.html @@ -1,15 +1,15 @@ -Helper Functions · StarFormationHistories.jl

Helper Functions

Distances and Sizes

StarFormationHistories.arcsec_to_pcFunction
arcsec_to_pc(arcsec, dist_mod)

Converts on-sky angle in arcseconds to physical separation based on distance modulus under the small-angle approximation.

\[r ≈ 10^{μ/5 + 1} \times \text{atan}(θ/3600)\]

source

Magnitudes and Luminosities

StarFormationHistories.mag2fluxFunction
mag2flux(m, zpt=0)

Convert a magnitude m to a flux assuming a photometric zeropoint of zpt, defined as the magnitude of an object that produces one count (or data number, DN) per second.

julia> mag2flux(15.0, 25.0) ≈ exp10(4 * (25.0 - 15.0) / 10)
-true
source
StarFormationHistories.flux2magFunction
flux2mag(f, zpt=0)

Convert a flux f to a magnitude assuming a photometric zeropoint of zpt, defined as the magnitude of an object that produces one count (or data number, DN) per second.

julia> flux2mag(10000.0, 25.0) ≈ 25.0 - 5 * log10(10000.0) / 2
-true
source
StarFormationHistories.magerrFunction
magerr(f, σf)

Returns an error in magnitudes given a flux and a flux uncertainty.

julia> magerr(100.0, 1.0) ≈ 2.5 / log(10) * (1.0 / 100.0)
-true
source
StarFormationHistories.fluxerrFunction
fluxerr(f, σm)

Returns an error in flux given a flux and a magnitude uncertainty.

julia> fluxerr(100.0, 0.01) ≈ (0.01 * 100.0) / 2.5 * log(10)
-true
source
StarFormationHistories.snr_magerrFunction
snr_magerr(σm)

Returns a signal-to-noise ratio $(f/σf)$ given an uncertainty in magnitudes.

julia> snr_magerr(0.01) ≈ 2.5 / log(10) / 0.01
-true
source
StarFormationHistories.magerr_snrFunction
magerr_snr(snr)

Returns a magnitude uncertainty given a signal-to-noise ratio $(f/σf)$.

julia> magerr_snr(100.0) ≈ 2.5 / log(10) / 100.0
-true
source

Metallicities

StarFormationHistories.Y_from_ZFunction
Y_from_Z(Z, Y_p=0.2485, γ=1.78)

Calculates the helium mass fraction (Y) for a star given its metal mass fraction (Z) using the approximation Y = Y_p + γ * Z, with Y_p being the primordial helium abundance Y_p=0.2485 as assumed for PARSEC isochrones and γ=1.78 matching the input scaling for PARSEC as well.

source
StarFormationHistories.X_from_ZFunction
X_from_Z(Z[, Yp, γ])

Calculates the hydrogen mass fraction (X) for a star given its metal mass fraction (Z) via X = 1 - (Z + Y), with the helium mass fraction Y approximated via StarFormationHistories.Y_from_Z. You may also provide the primordial helium abundance Y_p and γ such that Y = Y_p + γ * Z; these are passed through to X_from_Z.

source
StarFormationHistories.MH_from_ZFunction
MH_from_Z(Z, solZ=0.01524; Y_p = 0.2485, γ = 1.78)

Calculates [M/H] = log(Z/X) - log(Z/X)⊙. Given the provided solar metal mass fraction solZ, it calculates the hydrogen mass fraction X for both the Sun and the provided Z with StarFormationHistories.X_from_Z. You may also provide the primordial helium abundance Y_p and γ such that Y = Y_p + γ * Z; these are passed through to X_from_Z.

The present-day solar Z is measured to be 0.01524 (Caffau et al. 2011), but for PARSEC isochrones an [M/H] of 0 corresponds to Z=0.01471. This is because of a difference between the Sun's initial and present helium content caused by diffusion. If you want to reproduce PARSEC's scaling, you should set solZ=0.01471.

This function is an approximation and may not be suitable for precision calculations.

source
StarFormationHistories.Z_from_MHFunction
Z_from_MH(MH, solZ=0.01524; Y_p = 0.2485, γ = 1.78)

Calculates metal mass fraction Z assuming

  • the PARSEC relation for the helium mass fraction Y = Y_p + γ * Z with primordial helium abundance Y_p = 0.2485, and γ = 1.78, and
  • the solar metal mass fraction solZ = 0.01524.
source
StarFormationHistories.mdf_amrFunction
(unique_MH, mass_mdf) =
+Helper Functions · StarFormationHistories.jl

Helper Functions

Distances and Sizes

StarFormationHistories.arcsec_to_pcFunction
arcsec_to_pc(arcsec, dist_mod)

Converts on-sky angle in arcseconds to physical separation based on distance modulus under the small-angle approximation.

\[r ≈ 10^{μ/5 + 1} \times \text{atan}(θ/3600)\]

source

Magnitudes and Luminosities

StarFormationHistories.mag2fluxFunction
mag2flux(m, zpt=0)

Convert a magnitude m to a flux assuming a photometric zeropoint of zpt, defined as the magnitude of an object that produces one count (or data number, DN) per second.

julia> mag2flux(15.0, 25.0) ≈ exp10(4 * (25.0 - 15.0) / 10)
+true
source
StarFormationHistories.flux2magFunction
flux2mag(f, zpt=0)

Convert a flux f to a magnitude assuming a photometric zeropoint of zpt, defined as the magnitude of an object that produces one count (or data number, DN) per second.

julia> flux2mag(10000.0, 25.0) ≈ 25.0 - 5 * log10(10000.0) / 2
+true
source
StarFormationHistories.magerrFunction
magerr(f, σf)

Returns an error in magnitudes given a flux and a flux uncertainty.

julia> magerr(100.0, 1.0) ≈ 2.5 / log(10) * (1.0 / 100.0)
+true
source
StarFormationHistories.fluxerrFunction
fluxerr(f, σm)

Returns an error in flux given a flux and a magnitude uncertainty.

julia> fluxerr(100.0, 0.01) ≈ (0.01 * 100.0) / 2.5 * log(10)
+true
source
StarFormationHistories.snr_magerrFunction
snr_magerr(σm)

Returns a signal-to-noise ratio $(f/σf)$ given an uncertainty in magnitudes.

julia> snr_magerr(0.01) ≈ 2.5 / log(10) / 0.01
+true
source
StarFormationHistories.magerr_snrFunction
magerr_snr(snr)

Returns a magnitude uncertainty given a signal-to-noise ratio $(f/σf)$.

julia> magerr_snr(100.0) ≈ 2.5 / log(10) / 100.0
+true
source

Metallicities

StarFormationHistories.Y_from_ZFunction
Y_from_Z(Z, Y_p=0.2485, γ=1.78)

Calculates the helium mass fraction (Y) for a star given its metal mass fraction (Z) using the approximation Y = Y_p + γ * Z, with Y_p being the primordial helium abundance Y_p=0.2485 as assumed for PARSEC isochrones and γ=1.78 matching the input scaling for PARSEC as well.

source
StarFormationHistories.X_from_ZFunction
X_from_Z(Z[, Yp, γ])

Calculates the hydrogen mass fraction (X) for a star given its metal mass fraction (Z) via X = 1 - (Z + Y), with the helium mass fraction Y approximated via StarFormationHistories.Y_from_Z. You may also provide the primordial helium abundance Y_p and γ such that Y = Y_p + γ * Z; these are passed through to X_from_Z.

source
StarFormationHistories.MH_from_ZFunction
MH_from_Z(Z, solZ=0.01524; Y_p = 0.2485, γ = 1.78)

Calculates [M/H] = log(Z/X) - log(Z/X)⊙. Given the provided solar metal mass fraction solZ, it calculates the hydrogen mass fraction X for both the Sun and the provided Z with StarFormationHistories.X_from_Z. You may also provide the primordial helium abundance Y_p and γ such that Y = Y_p + γ * Z; these are passed through to X_from_Z.

The present-day solar Z is measured to be 0.01524 (Caffau et al. 2011), but for PARSEC isochrones an [M/H] of 0 corresponds to Z=0.01471. This is because of a difference between the Sun's initial and present helium content caused by diffusion. If you want to reproduce PARSEC's scaling, you should set solZ=0.01471.

This function is an approximation and may not be suitable for precision calculations.

source
StarFormationHistories.Z_from_MHFunction
Z_from_MH(MH, solZ=0.01524; Y_p = 0.2485, γ = 1.78)

Calculates metal mass fraction Z assuming

  • the PARSEC relation for the helium mass fraction Y = Y_p + γ * Z with primordial helium abundance Y_p = 0.2485, and γ = 1.78, and
  • the solar metal mass fraction solZ = 0.01524.
source
StarFormationHistories.mdf_amrFunction
(unique_MH, mass_mdf) =
 mdf_amr(coeffs::AbstractVector{<:Number},
         logAge::AbstractVector{<:Number},
         metallicities::AbstractVector{<:Number})

Calculates the mass-weighted metallicity distribution function given a set of stellar mass coefficients coeffs for stellar populations with logarithmic ages logAge=log10(age [yr]) and metallicities given by metallicities. This is calculated as

\[P_j = \frac{ \sum_k r_{j,k} \, [\text{M} / \text{H}]_k}{\sum_{j,k} r_{j,k} \, [\text{M} / \text{H}]_k}\]

where $r_{j,k}$ are the elements of coeffs where $j$ indexes over unique entries in logAge and $k$ indexes over unique entries in metallicities. This is the same nomenclature used in the the documentation on constrained metallicity evolutions. The return values are sorted so that unique_MH is in increasing order.

Examples

julia> mdf_amr([1.0, 2.0, 1.0], [10, 10, 10], [-2, -1.5, -1])
-([-2.0, -1.5, -1.0], [0.25, 0.5, 0.25])
source

Photometric Error and Completeness Models

StarFormationHistories.Martin2016_completeFunction
η(m) = Martin2016_complete(m, A, m50, ρ)

Completeness model of Martin et al. 2016 implemented as their Equation 7:

\[\eta(m) = \frac{A}{1 + \text{exp} \left( \frac{m - m_{50}}{\rho} \right)}\]

m is the magnitude of interest, A is the maximum completeness, m50 is the magnitude at which the data are 50% complete, and ρ is an effective slope modifier.

source
StarFormationHistories.exp_photerrFunction
exp_photerr(m, a, b, c, d)

Exponential model for photometric errors of the form

\[\sigma(m) = a^{b \times \left( m-c \right)} + d\]

Reported values for some HST data were a=1.05, b=10.0, c=32.0, d=0.01.

source

Photometric Error and Completeness Models

StarFormationHistories.Martin2016_completeFunction
η(m) = Martin2016_complete(m, A, m50, ρ)

Completeness model of Martin et al. 2016 implemented as their Equation 7:

\[\eta(m) = \frac{A}{1 + \text{exp} \left( \frac{m - m_{50}}{\rho} \right)}\]

m is the magnitude of interest, A is the maximum completeness, m50 is the magnitude at which the data are 50% complete, and ρ is an effective slope modifier.

source
StarFormationHistories.exp_photerrFunction
exp_photerr(m, a, b, c, d)

Exponential model for photometric errors of the form

\[\sigma(m) = a^{b \times \left( m-c \right)} + d\]

Reported values for some HST data were a=1.05, b=10.0, c=32.0, d=0.01.

source
StarFormationHistories.process_ASTsFunction
process_ASTs(ASTs::Union{DataFrames.DataFrame,
                          TypedTables.Table},
              inmag::Symbol,
              outmag::Symbol,
@@ -18,4 +18,4 @@
              statistic=StatsBase.median)

Processes a table of artificial stars to calculate photometric completeness, bias, and error across the provided bins. This method has no default implementation and is implemented in package extensions that rely on either DataFrames.jl or TypedTables.jl being loaded into your Julia session to load the relevant method. This method therefore requires Julia 1.9 or greater to use.

Arguments

  • ASTs is the table of artificial stars to be analyzed.
  • inmag is the column name in symbol format (e.g., :F606Wi) that corresponds to the intrinsic (input) magnitudes of the artificial stars.
  • outmag is the column name in symbol format (e.g., :F606Wo) that corresponds to the measured (output) magnitude of the artificial stars.
  • bins give the bin edges to be used when computing the binned statistics.
  • selectfunc is a method that takes a single row from ASTs, corresponding to a single artificial star, and returns a boolean that is true if the star is considered successfully measured.

Keyword Arguments

  • statistic is the method that will be used to determine the bias and error, i.e., bias = statistic(out .- in) and error = statistic(abs.(out .- in)). By default we use StatsBase.median, but you could instead use a simple or sigma-clipped mean if so desired.

Returns

This method returns a result of type NTuple{4,Vector{Float64}}. Each vector is of length length(bins)-1. result contains the following elements, each of which are computed over the provided bins considering only artificial stars for which selectfunc returned true:

  • result[1] contains the mean input magnitude of the stars in each bin.
  • result[2] contains the completeness value measured for each bin, defined as the fraction of input stars in each bin for which selectfunc returned true.
  • result[3] contains the photometric bias measured for each bin, defined as statistic(out .- in), where out are the measured (output) magnitudes and in are the intrinsic (input) magnitudes.
  • result[4] contains the photometric error measured for each bin, defined as statistic(abs.(out .- in)), with out and in defined as above.

Examples

Let

  • F606Wi be a vector containing the input magnitudes of your artificial stars
  • F606Wo be a vector containing the measured magnitudes of the artificial stars, where a value of 99.999 indicates a non-detection.
  • flag be a vector of booleans that indicates whether the artificial star passed additional quality cuts (star-galaxy separation, etc.)

You could call this method as

import TypedTables: Table
 process_ASTs(Table(input=F606Wi, output=F606Wo, good=flag),
              :input, :output, minimum(F606Wi):0.1:maximum(F606Wi),
-             x -> (x.good==true) & (x.output != 99.999))

See also the tests in test/utilities/process_ASTs_test.jl.

source
+ x -> (x.good==true) & (x.output != 99.999))

See also the tests in test/utilities/process_ASTs_test.jl.

source
diff --git a/dev/index.html b/dev/index.html index bc60497..62d7134 100644 --- a/dev/index.html +++ b/dev/index.html @@ -1,2 +1,2 @@ -Overview · StarFormationHistories.jl

Overview

This package enables many different kinds of analysis related to astrophysical star formation histories (SFHs). Core among these functionalities are generation of mock color-magnitude diagrams (CMDs) from input SFHs and fitting of SFHs from observed CMDs. The methods of this package have been designed to be simple to use but easy to extend. It is recommended that you use this package in conjunction with InitialMassFunctions.jl which provides implementations of the most popular stellar initial mass functions as new types which are natively supported by this package's methods.

+Overview · StarFormationHistories.jl

Overview

This package enables many different kinds of analysis related to astrophysical star formation histories (SFHs). Core among these functionalities are generation of mock color-magnitude diagrams (CMDs) from input SFHs and fitting of SFHs from observed CMDs. The methods of this package have been designed to be simple to use but easy to extend. It is recommended that you use this package in conjunction with InitialMassFunctions.jl which provides implementations of the most popular stellar initial mass functions as new types which are natively supported by this package's methods.

diff --git a/dev/search_index.js b/dev/search_index.js index 972403f..ff98567 100644 --- a/dev/search_index.js +++ b/dev/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"fitting/fitting_intro/#fitting","page":"Background and Template Construction","title":"Background and Template Construction","text":"","category":"section"},{"location":"fitting/fitting_intro/#Background","page":"Background and Template Construction","title":"Background","text":"","category":"section"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"In the classic formulation of star formation history fitting from resolved-star photometry (Dolphin 2002), an observed color-magnitude diagram (CMD) is binned into a 2-D histogram known as a Hess diagram. Such a CMD and Hess diagram pair is shown below.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"(Image: Comparison of CMD and a Hess diagram generated from the same observational data.)","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"The representation of the observations as a Hess diagram allows one to apply Poisson statistics, specifically the Poisson likelihood ratio (Equations 7–10 in Dolphin 2002), to model the observations. As the CMD of a complex stellar population is simply the sum of the CMDs of its sub-populations, one need only prepare a number of templates for each simple stellar population (SSP) which may make up the complex population in question and model the observed Hess diagram as a linear combination of these templates. Keeping the same notation as Dolphin 2002 (Equation 1), the complex model Hess diagram is simply","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"m_i = sum_j r_j c_ij","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"where m_i is the value of the complex model in bin i, c_ij is the value of simple template j in bin i, and r_j is the multiplicative coefficient determining how significant template j is to the complex population. In Dolphin 2002, he normalizes the templates to identical star formation rates (SFRs) and so the r_j are SFRs as well. In this package, we prefer to normalize our templates to identical population stellar masses, so our r_j are stellar masses, but the principal is the same.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"Construction of the templates is, however, not a trivial exercise. Ideally, a template constructed to represent a particular SSP would accurately reflect the expectation of how such a population would be observed. Thus, these templates must be adjusted for photometric error, incompleteness, and other effects such as those caused by unresolved binary- and multi-star systems. Observational effects such as photometric error and incompleteness are best measured from artificial star tests (ASTs). It is worth mentioning that ASTs can often return \"best case\" results, as they typically neglect systematics like uncertainty in the point-spread model used for the photometry; as such it is sometimes necessary to add a systematic error floor to photometric error results from ASTs.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"Such templates can be constructed by sampling many mock stars from an initial mass function (IMF), interpolating their absolute magnitudes from an isochrone of the relevant SSP, and \"mock observing\" them by applying photometric error and completeness functions (for example, by looking up the 1sigma photometric error and completeness value from a catalog of artificial stars). Such Monte Carlo templates can be slow to construct and exhibit Poisson shot-noise, requiring a statistical data–data comparison rather than a model–data comparison. Thus this method is non-optimal from both a practical and statistical perspective.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"It is better to form what Dolphin 2002 calls a \"blurred isochrone;\" in this form of template, the SSP isochrone is first interpolated in initial stellar mass to improve the point density along the isochrone. The number of interpolated points is generally a function of the size of the bins in the Hess diagram and the observational error; more points are required as the bin size or photometric errors become smaller. These points are then weighted according to the IMF and the photometric completeness, and this weight is distributed into the Hess diagram following the photometric error distribution determined by similar artificial stars. Dolphin 2002 also mentions interpolating across stellar age/metallicity when constructing such templates; for example, for an SSP with an age of 1 Gyr and a metallicity of [M/H]=-1.0, you could interpolate the isochrones to introduce a Gaussian metallicity spread of 0.05 dex or an age spread of 100 Myr. The general effects of this form of interpolation is to broaden the model templates, particularly features that are very sharp in true SSP models. We neglect this form of interpolation in our implementation as it adds significant complexity and requires users to provide more information about the isochrones that are providing. Such widening of the individual templates is most impactful when photometric errors in the observational data are low (perhaps <0.10 mag).","category":"page"},{"location":"fitting/fitting_intro/#templates","page":"Background and Template Construction","title":"Constructing Templates","text":"","category":"section"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"While the above description summarizes the necessary components for constructing such a blurred isochrone, it can be a bit difficult to figure out how best to actually construct them. Specifically there are many ways that one could implement the observational effects of photometric error and incompleteness. We provide a method partial_cmd_smooth to construct such templates under the assumption of Gaussian photometric error distributions, which is often a good approximation in the high-completeness regime. This method makes use of user-defined functions for the mean photometric error and completeness as a function of magnitude and filter, such that these can be defined in a number of ways; for example, as direct lookups from a large table of ASTs or as simple function evaluations of analytic approximations or fits to the ASTs.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"This method begins by interpolating the provided SSP isochrone to increase point density. For every such point with i band apparent magnitude m_i, it calls a user-defined function to estimate the 1sigma photometric error as sigma_i = f_i(m_i). This is done for each provided photometric filter. These errors are used to define an asymmetric 2-D Gaussian kernel for each point in the interpolated isochrone. This kernel describes the probability distribution of where the isochrone point would be observed in the Hess diagram. The shape of the kernel is automatically adjusted to account for covariance when the magnitude on the y-axis of the Hess diagram also appears in the x-axis color, as occurs when only two filters of imaging are available (for example, an x-axis of B-V and a y-axis magnitude of V). Details on these kernels are given here. However, the kernel also must be normalized (weighted) according to the IMF and observational completeness functions.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"Assume that the vector of initial stellar masses for the points in the interpolated isochrone are m_i and that they are sorted such that m_i m_i+1. The IMF weight on point m_i can be approximated as the number fraction of stars born between m_i and m_i+1 divided by the mean mass per star born langle m rangle, such that the weight effectively represents the number of stars expected to be born with masses between m_i and m_i+1 per solar mass of star formation:","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"beginaligned\nw_itextIMF = frac int_0^m_i+1 fracdN(m)dm dm - int_0^m_i fracdN(m)dm dm int_0^infty m times fracdN(m)dm dm = frac int_m_i^m_i+1 fracdN(m)dm dm langle m rangle\nendaligned","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"The numerator can either be calculated as the difference in the cumulative distribution function across the bin or approximated efficiently via the trapezoidal rule. The denominator is a function only of the IMF and need only be calculated once. Multiplying this weight by the probability of detection in the relevant bands gives the final weight.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"mv(\"../../../examples/templates/template_compare.svg\", \"figures/template_compare.svg\") # hide\nmv(\"../../../examples/templates/sigma_distribution.svg\", \"figures/sigma_distribution.svg\") # hide\nnothing # hide","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"A worked example comparing a sampled stellar population with a smooth Hess diagram template is available in examples/templates/smooth_template.jl. The smooth Hess diagram template is constructed with partial_cmd_smooth and the Monte Carlo population is sampled with generate_stars_mass, with observational effects modelled by model_cmd. The isochrone used comes from PARSEC and has an age of 12.6 Gyr and initial metallicity [M/H] of -2.8. The output figure is shown below. A distance modulus of 25 mag is used for this example, with photometric error and completeness functions roughly based on those we observe in the JWST/NIRCAM data of WLM (see Weisz et al. 2024).","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"(Image: Comparison of CMD-sampled population with smooth Hess diagram template.)","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"At left is a population of stars sampled from an SSP with the methods described in the section of the documentation on simulating CMDs. The points from the isochrone are colored orange. The next figure shows the binned Hess diagram derived from these data. The next figure shows our smooth Hess diagram template calculated for this SSP. The final figure at right shows the residual between the data and model in units of standard deviations. These are sometimes called Pearson residuals. Below we show the distribution of these residuals, which should be Gaussian with mean 0 and standard deviation 1 if the model were perfect. Note that, because all bins with 0 observed stars are excluded from the distribution and the observed Hess diagram contains Poisson error, the mean of our distribution will always be slightly higher than 0. We do, however, achieve a standard deviation of nearly 1, indicating that our model is robust. We show a Gaussian PDF with standard deviation 1 and mean equal to the observed mean of the residuals for comparison.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"(Image: Distribution of data - model residuals, in units of standard deviations.)","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"The method used to create these smooth Hess diagram templates is partial_cmd_smooth.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"partial_cmd_smooth","category":"page"},{"location":"fitting/fitting_intro/#StarFormationHistories.partial_cmd_smooth","page":"Background and Template Construction","title":"StarFormationHistories.partial_cmd_smooth","text":"result::StatsBase.Histogram =\n partial_cmd_smooth(m_ini::AbstractVector{<:Number},\n mags::AbstractVector{<:AbstractVector{<:Number}},\n mag_err_funcs,\n y_index,\n color_indices,\n imf,\n completeness_funcs=[one for i in mags];\n dmod::Number=0,\n normalize_value::Number=1,\n binary_model::AbstractBinaryModel=NoBinaries(),\n mean_mass=mean(imf),\n edges=nothing,\n xlim=nothing,\n ylim=nothing,\n nbins=nothing,\n xwidth=nothing,\n ywidth=nothing)\n\nMain function for generating template Hess diagrams from a simple stellar population of stars from an isochrone, including photometric error and completeness.\n\nArguments\n\nm_ini::AbstractVector{<:Number} is a vector containing the initial stellar masses of the stars from the isochrone.\nmags::AbstractVector{<:AbstractVector{<:Number}} is a vector of vectors. Each constituent vector with index i should have length(mags[i]) == length(m_ini), representing the magnitudes of the isochrone stars in each of the magnitudes considered. In most cases, mags should contain 2 (if y-axis mag is also involved in the x-axis color) or 3 vectors.\nmag_err_funcs must be an indexable object (e.g., a Vector or Tuple) that contains callables (e.g., a Function) to compute the 1σ photometric errors for the same filters provided in mags. Each callable must take a single argument (an apparent magnitude) and return a Number. The length mag_err_funcs must be equal to the length of mags.\ny_index gives a valid index (e.g., an Int or CartesianIndex) into mags for the filter you want to have on the y-axis of the Hess diagram. For example, if the mags argument contains the B and V band magnitudes as mags=[B, V] and you want V on the y-axis, you would set y_index as 2. \ncolor_indices is a length-2 indexable object giving the indices into mags that are to be used to compute the x-axis color. For example, if the mags argument contains the B and V band magnitudes as mags=[B, V], and you want B-V to be the x-axis color, then color_indices should be [1,2] or (1,2) or similar.\nimf is a callable that takes an initial stellar mass as its sole argument and returns the (properly normalized) probability density of your initial mass function model. All the models from InitialMassFunctions.jl are valid for imf.\ncompleteness_functions must be an indexable object (e.g., a Vector or Tuple) that contains callables (e.g., a Function) to compute the single-filter completeness fractions as a function of apparent magnitude. Each callable in this argument must correspond to the matching filter provided in mags.\n\nKeyword Arguments\n\ndmod::Number=0 is the distance modulus in magnitudes to apply to the input mags. Leave at 0 if you are providing apparent magnitudes in mags.\nnormalize_value::Number=1 is the total stellar mass of the population you wish to model.\nbinary_model::AbstractBinaryModel=NoBinaries() is the model to use for including binary systems. Currently only StarFormationHistories.NoBinaries and StarFormationHistories.RandomBinaryPairs are supported.\nmean_mass::Number is the expectation value of the initial mass for a random star drawn from your provided imf. This will be computed for you if your provided imf is a valid continuous, univariate Distributions.Distribution object.\nedges is a tuple of ranges defining the left-side edges of the bins along the x-axis (edges[1]) and the y-axis (edges[2]). Example: (-1.0:0.1:1.5, 22:0.1:27.2). If edges is provided, it overrides the following keyword arguments that offer other ways to specify the extent of the Hess diagram.\nxlim is a length-2 indexable object (e.g., a Vector or Tuple) giving the lower and upper bounds on the x-axis corresponding to the provided colors array. Example: (-1.0, 1.5). This is only used if edges is not provided. \nylim is as xlim but for the y-axis corresponding to the provided mags array. Example (25.0, 20.0). This is only used if edges is not provided.\nnbins::NTuple{2, <:Integer} is a 2-tuple of integers providing the number of bins to use along the x- and y-axes. This is only used if edges is not provided.\nxwidth is the bin width along the x-axis for the colors array. This is only used if edges and nbins are not provided. Example: 0.1.\nywidth is as xwidth but for the y-axis corresponding to the provided mags array. Example: 0.1.\n\nReturns\n\nThis method returns the Hess diagram as a StatsBase.Histogram; you should refer to the StatsBase documentation for more information. In short, if the output of this method is result, then the Hess diagram represented as a Matrix is available as result.weights (this is what you would want for fit_templates and similar functions) and the edges of the histogram are available as result.edges.\n\n\n\n\n\n","category":"function"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"The user-provided functions returning magnitude errors (mag_err_funcs) and completeness values (completness_functions) given a star's intrinsic apparent magnitude are important for deriving good templates. These are typically derived from catalogs of artificial star tests. Some helper functions for constructing these are provided here.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"We note that in many cases it can also be helpful to add in a foreground/background template that models contamination of the Hess diagram from stars not in your population of interest – this is often done using observations of parallel fields though there are several other possible methods.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"Photometric catalogs can be processed into Hess diagrams meeting our formatting requirements with the method bin_cmd.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"StarFormationHistories.bin_cmd","category":"page"},{"location":"fitting/fitting_intro/#StarFormationHistories.bin_cmd","page":"Background and Template Construction","title":"StarFormationHistories.bin_cmd","text":"result::StatsBase.Histogram =\n bin_cmd(colors::AbstractVector{<:Number},\n mags::AbstractVector{<:Number};\n weights::AbstractVector{<:Number} = ones(promote_type(eltype(colors),\n eltype(mags)), size(colors)),\n edges = nothing,\n xlim = extrema(colors),\n ylim = extrema(mags),\n nbins = nothing,\n xwidth = nothing,\n ywidth = nothing)\n\nReturns a StatsBase.Histogram type containing the Hess diagram from the provided x-axis photometric colors and y-axis photometric magnitudes mags. These must all be vectors equal in length. You can either specify the bin edges directly via the edges keyword (e.g., edges = (range(-0.5, 1.6, length=100), range(17.0, 26.0, length=100))), or you can set the x- and y-limits via xlim and ylim and the number of bins as nbins, or you can omit nbins and instead pass the bin width in the x and y directions, xwidth and ywidth. See below for more info on the keyword arguments. To plot this with PyPlot.jl you should do PyPlot.imshow(permutedims(result.weights), origin=\"lower\", extent=(extrema(result.edges[1])..., extrema(result.edges[2]), kws...) where kws... are any other keyword arguments you wish to pass to PyPlot.imshow.\n\nKeyword Arguments\n\nweights::AbstractVector{<:Number} is a array of length equal to colors and mags that contains the probabilistic weights associated with each point. This is passed to StatsBase.fit as StatsBase.Weights(weights). The following keyword arguments are passed to StarFormationHistories.calculate_edges to determine the bin edges of the histogram.\nedges is a tuple of ranges defining the left-side edges of the bins along the x-axis (edges[1]) and the y-axis (edges[2]). Example: (-1.0:0.1:1.5, 22:0.1:27.2). If edges is provided, weights is the only other keyword that will be read; edges supercedes the other construction methods. \nxlim is a length-2 indexable object (e.g., a vector or tuple) giving the lower and upper bounds on the x-axis corresponding to the provided colors array. Example: [-1.0, 1.5]. This is only used if edges is not provided. \nylim is like xlim but for the y-axis corresponding to the provided mags array. Example [25.0, 20.0]. This is only used if edges is not provided.\nnbins::NTuple{2, <:Integer} is a 2-tuple of integers providing the number of bins to use along the x- and y-axes. This is only used if edges is not provided.\nxwidth is the bin width along the x-axis for the colors array. This is only used if edges and nbins are not provided. Example: 0.1. \nywidth is like xwidth but for the y-axis corresponding to the provided mags array. Example: 0.1.\n\n\n\n\n\n","category":"function"},{"location":"fitting/fitting_intro/#A-Note-on-Array-Formatting","page":"Background and Template Construction","title":"A Note on Array Formatting","text":"","category":"section"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"It is expected that the user will typically have model templates stored as two-dimensional matrices as these are the obvious choice for representing a binned two-dimensional histogram. We fully support supplying the list of model templates as a list of matrices (e.g., a Vector{Matrix{<:Number}}) to the fitting functions discussed below. The important computational kernels composite! and ∇loglikelihood! have custom loops for these input types.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"However, additional optimizations are possible by flattening the data. By flattening each matrix in the list of model templates into a column vector and concatenating them such that the list of model templates becomes a single matrix, we can compute the complex model Hess diagram as a single matrix-vector product rather than using a custom loop. The same optimization can be made when computing the gradient of the loglikelihood (discussed more below). The majority of all computation for the fitting methods below is spent in these two functions, so optimizing their performance translates directly to improved fitting runtimes. With this flattened memory arrangement we can use the highly optimized LinearAlgbera.mul! method to do the in-place matrix-vector product. This will typically be translated into a call to a BLAS function like gemv!. As such, we can benefit from Julia's ability to switch BLAS implementations at runtime to use Intel's Math Kernel Library, Apple's Accelerate, and others.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"Most of the fitting methods below support both the natural and flattened data layouts. We provide the stack_models method to produce the optimized layout for the list of model templates.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"StarFormationHistories.stack_models","category":"page"},{"location":"fitting/fitting_intro/#StarFormationHistories.stack_models","page":"Background and Template Construction","title":"StarFormationHistories.stack_models","text":"stack_models(models::AbstractVector{<:AbstractMatrix{<:Number}}) = reduce(hcat,map(vec,models))\n\nTransforms a vector of matrices into a single matrix, with each matrix from models being transcribed into a single column in the output matrix. This data layout enables more efficient calculations in some of our internal functions like composite! and ∇loglikelihood!.\n\nExamples\n\njulia> stack_models([rand(5,5) for i in 1:10])\n25×10 Matrix{Float64}:\n...\n\n\n\n\n\n","category":"function"},{"location":"fitting/fitting_intro/#A-Note-on-Threading","page":"Background and Template Construction","title":"A Note on Threading","text":"","category":"section"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"While generally the methods using BLAS routines offer significant performance improvements, there is a caveat when multithreading from within Julia. By default Julia will allow BLAS to use multiple threads even if Julia itself is started with a single thread (i.e., by running julia -t 1). BLAS threads do not compose with Julia threads. That is, if you start Julia with N>1 threads (julia -t N) and write a threaded workload where each Julia thread is doing BLAS operations concurrently, you can easily oversubscribe the CPU. Specific recommendations vary depending on BLAS vendor (see this page and the linked discourse threads), but generally this package is in the regime of doing many small calculations that do not individually benefit much from BLAS threading (e.g., performance for OpenBLAS with 8 threads is only ~2x the 1 thread performance). As such it is often sufficient to set BLAS to use a single thread (via LinearAlgbera.BLAS.set_num_threads(1) or environment variables; see above link).","category":"page"},{"location":"doc_index/#index","page":"Index","title":"Index","text":"","category":"section"},{"location":"doc_index/","page":"Index","title":"Index","text":"Modules = [StarFormationHistories]","category":"page"},{"location":"fitting/fixed_amr/#Fixed-Age-Metallicity-Relations","page":"Fixed Age-Metallicity Relations","title":"Fixed Age-Metallicity Relations","text":"","category":"section"},{"location":"fitting/fixed_amr/","page":"Fixed Age-Metallicity Relations","title":"Fixed Age-Metallicity Relations","text":"It is often the case that one may want to fit for star formation rates under a fixed age-metallicity relation or other metallicity evolution model with no degrees of freedom. Such functionality is provided by fixed_amr, which takes as input the relative weights (relweights in the function call, equivalently the r_jk in the above derivation) on each template due to a predetermined metallicity model and fits only the per-age-bin coefficients (R_j in the above derivation). ","category":"page"},{"location":"fitting/fixed_amr/","page":"Fixed Age-Metallicity Relations","title":"Fixed Age-Metallicity Relations","text":"StarFormationHistories.fixed_amr","category":"page"},{"location":"fitting/fixed_amr/#StarFormationHistories.fixed_amr","page":"Fixed Age-Metallicity Relations","title":"StarFormationHistories.fixed_amr","text":"fixed_amr(models::AbstractVector{T},\n data::AbstractMatrix{<:Number},\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number},\n relweights::AbstractVector{<:Number};\n relweightsmin::Number=0, \n x0=construct_x0_mdf(logAge, convert(S,13.7)),\n kws...) where {S <: Number, T <: AbstractMatrix{S}}\nfixed_amr(models::AbstractMatrix{S},\n data::AbstractVector{<:Number},\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number},\n relweights::AbstractVector{<:Number};\n relweightsmin::Number=0,\n x0=construct_x0_mdf(logAge, convert(S,13.7)),\n kws...) where S <: Number\n\nMethod that fits a linear combination of the provided Hess diagrams models to the observed Hess diagram data, under an externally-imposed age-metallicity relation (AMR) and/or metallicity distribution function (MDF). As such, a number of coefficients equal to length(unique(logAge)) are returned; that is, only one coefficient is derived per unique entry in logAge.\n\nThe second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\nArguments\n\nmodels::AbstractVector{<:AbstractMatrix{<:Number}} is a vector of equal-sized matrices that represent the template Hess diagrams for the simple stellar populations that compose the observed Hess diagram.\ndata::AbstractMatrix{<:Number} is the Hess diagram for the observed data.\nlogAge::AbstractVector{<:Number} is the vector containing the effective ages of the stellar populations used to create the templates in models, in units of log10(age [yr]). For example, if a population has an age of 1 Myr, its entry in logAge should be log10(10^6) = 6.0.\nmetallicities::AbstractVector{<:Number} is the vector containing the effective metallicities of the stellar populations used to create the templates in models. This is most commonly a logarithmic abundance like [M/H] or [Fe/H], but you could use a linear abundance like the metal mass fraction Z if you wanted to. There are some notes on the Wikipedia that might be useful.\nrelweights::AbstractVector{<:Number} is a vector of length equal to that of models which contains the relative weights to apply to each model Hess diagram resulting from an externally-imposed age-metallicity relation and/or metallicity distribution function. Additional details on how to create these weights is provided in the notes below and in the online documentation.\n\nKeyword Arguments\n\nrelweightsmin truncates the input list of models based on the provided relweights, providing a speedup at the cost of precision by removing models that contribute least to the overall composite model. By default, no truncation of the input is performed and all provided models are used in the fit. We recommend this only be increased when fitting performance begins to impact workflow (e.g., when running massive Monte Carlo experiments). See StarFormationHistories.truncate_relweights for implementation details. \nx0 is the vector of initial guesses for the stellar mass coefficients per unique entry in logAge. You should basically always be calculating and passing this keyword argument. We provide StarFormationHistories.construct_x0_mdf to prepare x0 assuming constant star formation rate, which is typically a good initial guess. \n\nOther kws... are passed to Optim.options to set things like convergence criteria for the optimization.\n\nNotes\n\nAll metallicity-related weighting of the models is assumed to be captured in the provided relweights vector, which has the same length as the logAge, metallicities, and models vectors. Each entry in relweights is assumed to be a relative weight for the corresponding model. For example, for the model Hess diagram models[i], with log10(age [yr]) = logAge[i] and metallicity metallicities[i], the relative weight due to the model's age and metallicity w(logAge[i], metallicities[i]) is assumed to be relweights[i]. The sum of all relweights for each unique entry in logAge should be 1; i.e., the following condition should be met: all( sum(relweights[logAge .== la]) ≈ 1 for la in unique(logAge)). If this is not the case, this function will issue a warning and attempt to renormalize relweights by mutating the vector in place. More information on preparation of the relweights for input to this method is provided in our online documentation. \nThis function is designed to work best with a \"grid\" of stellar models, defined by the outer product of N unique entries in logAge and M unique entries in metallicities. See the examples for more information on usage.\n\n\n\n\n\n","category":"function"},{"location":"fitting/linear_amr/#linear_amr_section","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"","category":"section"},{"location":"fitting/linear_amr/#metal_evo_intro","page":"Linear Age-Metallicity Relation","title":"Why Should Metallicity Evolutions Be Constrained?","text":"","category":"section"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"While the above methods work well for optimizing the per-template r_j as a means for fitting SFHs, these methods can produce metallicity evolutions that could be considered unphysical, with large changes in the mean metallicity over small changes in time. An example of this type of behavior is shown in the SFH fit below.","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"(Image: Example of a SFH fit with variations in the metallicity evolution.)","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"While some metallicity variation in the star-forming gas is to be expected, these variations in the SFH fit can end up being quite large depending on the data and isochrone grid adopted. A solution is to construct a more physically-motivated model.","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"We can do this using a hierarchical model with a parameterized metallicity distribution function (MDF) where the the r_j are not the parameters directly optimized. Rather, we can optimize one stellar mass (or star formation rate) parameter per age bin, and then a number of MDF parameters that determine how that stellar mass is split between models with different metallicities at fixed age. An example for one such MDF model is a linear mean metallicity relation langle textMtextH rangle (t) = alpha left( T_textmax - t right) + beta with a Gaussian distribution in metallicity at fixed age. T_textmax here is the earliest lookback time under consideration such that langle textMtextH rangle (T_textmax) = beta. If the per-age-bin stellar mass coefficients are R_j, the age of the stellar population j is t_j, and the metallicity of population k is textMtextH_k, then we can write the per-model r_jk (where we are now using separate indices for age and metallicity) as","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"beginaligned\nmu_j = alpha left( T_textmax - t_j right) + beta \nr_jk = R_j frac textexp left( - left( frac textMtextH_k - mu_jsigma right)^2 right)sum_k textexp left( - left( frac textMtextH_k - mu_jsigma right)^2 right)\nendaligned","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"where the numerator is the MDF at fixed age evaluated at metallicity textMtextH_k and the denominator is a normalizing coefficient that ensures sum_k r_jk = R_j. In this notation, bin i of the complex model Hess diagram (equation 1 of Dolphin 2002) is","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"m_i = sum_jk r_jk c_ijk","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"Below we show a fit using this hierarchical model to the same data as above. ","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"(Image: Example of a SFH fit with a linear metallicity evolution.)","category":"page"},{"location":"fitting/linear_amr/#Fitting-Functions","page":"Linear Age-Metallicity Relation","title":"Fitting Functions","text":"","category":"section"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"We provide the method StarFormationHistories.fit_templates_mdf to fit this model to an observed Hess diagram.","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"StarFormationHistories.fit_templates_mdf\nStarFormationHistories.LogTransformMDFσResult\nStarFormationHistories.LogTransformMDFResult","category":"page"},{"location":"fitting/linear_amr/#StarFormationHistories.fit_templates_mdf","page":"Linear Age-Metallicity Relation","title":"StarFormationHistories.fit_templates_mdf","text":"fit_templates_mdf(models::AbstractVector{<:AbstractMatrix{S}},\n data::AbstractMatrix{<:Number},\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number},\n T_max::Number\n [, σ::Number];\n x0 = vcat(construct_x0_mdf(logAge, convert(S,13.7)),\n [0.05, -2.0, 0.2]),\n kws...) where {S <: Number}\nfit_templates_mdf(models::AbstractMatrix{S},\n data::AbstractVector{<:Number},\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number},\n T_max::Number\n [, σ::Number];\n x0 = vcat(construct_x0_mdf(logAge, convert(S,13.7)),\n [0.05, -2.0, 0.2]),\n kws...) where {S <: Number}\n\nMethod that fits a linear combination of the provided Hess diagrams models to the observed Hess diagram data, constrained to have a linear age-metallicity relation with the mean metallicity of element i of unique(logAge) being μ[i] = α * (T_max - exp10(unique(logAge)[i]) / 1e9) + β. α is therefore a slope in the units of metallicities per Gyr, and β is the mean metallicity value of stars being born at a lookback time of T_max, which has units of Gyr. Individual weights for each isochrone template are then determined via Gaussian weighting with the above mean and the standard deviation σ, which can either be fixed or fit.\n\nThis function is designed to work best with a \"grid\" of stellar models, defined by the outer product of N unique entries in logAge and M unique entries in metallicities. See the examples for more information on usage.\n\nThe second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\nArguments\n\nmodels are the template Hess diagrams for the simple stellar populations that compose the observed Hess diagram. \ndata is the Hess diagram for the observed data. \nlogAge::AbstractVector{<:Number} is the vector containing the effective ages of the stellar populations used to create the templates in models, in units of log10(age [yr]). For example, if a population has an age of 1 Myr, its entry in logAge should be log10(10^6) = 6.0.\nmetallicities::AbstractVector{<:Number} is the vector containing the effective metallicities of the stellar populations used to create the templates in models. This is most commonly a logarithmic abundance like [M/H] or [Fe/H], but you could use a linear abundance like the metal mass fraction Z if you wanted to. There are some notes on the Wikipedia that might be useful.\nT_max::Number is the time at which the age-metallicity relation has a value of \beta in Gyr. For example, if the oldest stellar populations in your isochrone grid are 12 Gyr old, you could set T_max = 12.0. \n\nOptional Arguments\n\nIf provided, σ::Number is the fixed width of the Gaussian the defines the metallicity distribution function (MDF) at fixed logAge. If this argument is omitted, σ will be a free parameter in the fit. \n\nKeyword Arguments\n\nx0 is the vector of initial guesses for the stellar mass coefficients per unique entry in logAge, plus the variables that define the metallicity evolution model. You should basically always be calculating and passing this keyword argument. We provide StarFormationHistories.construct_x0_mdf to prepare the first part of x0 assuming constant star formation rate, which is typically a good initial guess. You then have to concatenate that result with an initial guess for the metallicity evolution parameters. For example, x0=vcat(construct_x0_mdf(logAge, 13.7; normalize_value=1e4), [0.05,-2.0,0.2]), where logAge is a valid argument for this function (see above), and the initial guesses on the parameters are [α, β, σ] = [0.05, -2.0, 0.2]. If the provided metallicities are, for example, [M/H] values, then this mean metallicity evolution is μ(t) [dex] = 0.05 [dex/Gyr] * (T_max - t) [Gyr] - 2.0 [dex], and at fixed time, the metallicity distribution function is Gaussian with mean μ(t) and standard deviation σ. If you provide σ as an optional argument, then you should not include an entry for it in x0.\nOther kws... are passed to Optim.options to set things like convergence criteria for the optimization.\n\nReturns\n\nThis function returns an object (say, result) of similar structure to the object returned by fit_templates. Specifically, this method will return a NamedTuple with entries result.mle and result.map for the maximum likelihood and maximum a posteriori estimates, respectively. If you provide a fixed σ, those objects will be instances of StarFormationHistories.LogTransformMDFσResult. If you allow σ to be freely fit, those objects will be instances of StarFormationHistories.LogTransformMDFResult. Both of these types support sampling via, e.g., rand(result.map, 10). \n\nNotes\n\nα and σ are optimized under a logarithmic transformation, so they are constrained to be positive. β is not and may be negative. This method also uses the BFGS method from Optim.jl internally just like fit_templates; please see the notes section of that method. \n\n\n\n\n\n","category":"function"},{"location":"fitting/linear_amr/#StarFormationHistories.LogTransformMDFσResult","page":"Linear Age-Metallicity Relation","title":"StarFormationHistories.LogTransformMDFσResult","text":"LogTransformMDFσResult(μ::AbstractVector{<:Number},\n σ::AbstractVector{<:Number},\n invH::AbstractMatrix{<:Number},\n result)\n\nType for containing the maximum likelihood estimate (MLE) and maximum a posteriori (MAP) results from fit_templates_mdf for fixed σ. The fitted coefficients are available in the μ field. Estimates of the standard errors are available in the σ field. These have both been transformed from the native logarithmic fitting space into natural units (i.e., stellar mass or star formation rate). The linear age-metallicity relation parameters α (slope [dex/Gyr]) and β (intercept at T_max [dex]) are available in the second-to-last and last elements of μ and σ, respectively. \n\ninvH contains the estimated inverse Hessian of the likelihood / posterior at the maximum point in the logarithmic fitting units. result is the full result object returned by the optimization routine.\n\nThis type is implemented as a subtype of Distributions.Sampleable{Multivariate, Continuous} to enable sampling from an estimate of the likelihood / posterior distribution. We approximate the distribution as a multivariate Gaussian in the native (logarithmically transformed) fitting variables with covariance matrix invH and means log.(μ). We find this approximation is good for the MAP result but less robust for the MLE. You can obtain N::Integer samples from the distribution by rand(R, N) where R is an instance of this type; this will return a size (length(μ)+2) x N matrix, or fail if invH is not positive definite.\n\nExamples\n\njulia> result = fit_templates_mdf(models, data, model_logAge, model_MH, 0.3);\n\njulia> typeof(result.map)\nStarFormationHistories.LogTransformMDFσResult{...}\n\njulia> size(rand(result.map, 3)) == (length(models)+2,3)\ntrue\n\n\n\n\n\n","category":"type"},{"location":"fitting/linear_amr/#StarFormationHistories.LogTransformMDFResult","page":"Linear Age-Metallicity Relation","title":"StarFormationHistories.LogTransformMDFResult","text":"LogTransformMDFResult(μ::AbstractVector{<:Number},\n σ::AbstractVector{<:Number},\n invH::AbstractMatrix{<:Number},\n result)\n\nType for containing the maximum likelihood estimate (MLE) and maximum a posteriori (MAP) results from fit_templates_mdf when freely fitting σ. The fitted coefficients are available in the μ field. Estimates of the standard errors are available in the σ field. These have both been transformed from the native logarithmic fitting space into natural units (i.e., stellar mass or star formation rate). The linear age-metallicity relation parameters α (slope [dex/Gyr]) and β (intercept at T_max [dex]) are available in the third-to-last and second-to-last elements of μ and σ, respectively. The static Gaussian width of the MDF at fixed age is provided in the last element of μ and σ. \n\ninvH contains the estimated inverse Hessian of the likelihood / posterior at the maximum point in the logarithmic fitting units. result is the full result object returned by the optimization routine.\n\nThis type is implemented as a subtype of Distributions.Sampleable{Multivariate, Continuous} to enable sampling from an estimate of the likelihood / posterior distribution. We approximate the distribution as a multivariate Gaussian in the native (logarithmically transformed) fitting variables with covariance matrix invH and means log.(μ). We find this approximation is good for the MAP result but less robust for the MLE. You can obtain N::Integer samples from the distribution by rand(R, N) where R is an instance of this type; this will return a size (length(μ)+3) x N matrix, or fail if invH is not positive definite.\n\nExamples\n\njulia> result = fit_templates_mdf(models, data, model_logAge, model_MH);\n\njulia> typeof(result.map)\nStarFormationHistories.LogTransformMDFσResult{...}\n\njulia> size(rand(result.map, 3)) == (length(models)+3,3)\ntrue\n\n\n\n\n\n","category":"type"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"The method StarFormationHistories.construct_x0_mdf can be used to construct the stellar mass components R_j of the initial guess vector x0","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"StarFormationHistories.construct_x0_mdf","category":"page"},{"location":"fitting/linear_amr/#StarFormationHistories.construct_x0_mdf","page":"Linear Age-Metallicity Relation","title":"StarFormationHistories.construct_x0_mdf","text":"x0::Vector = construct_x0_mdf(logAge::AbstractVector{T},\n T_max::Number;\n normalize_value::Number = one(T)) where T <: Number\n\nGenerates a vector of initial stellar mass normalizations for input to StarFormationHistories.fit_templates_mdf or StarFormationHistories.hmc_sample_mdf with a total stellar mass of normalize_value such that the implied star formation rate is constant across the provided logAge vector that contains the log10(Age [yr]) of each isochrone that you are going to input as models. For the purposes of computing the constant star formation rate, the provided logAge are treated as left-bin edges, with the final right-bin edge being T_max, which has units of Gyr. For example, you might have logAge=[6.6, 6.7, 6.8] in which case a final logAge of 6.9 would give equal bin widths. In this case you would set T_max = exp10(6.9) / 1e9 ≈ 0.0079 so that the width of the final bin for the star formation rate calculation has the same log10(Age [yr]) step as the other bins.\n\nThe difference between this function and StarFormationHistories.construct_x0 is that this function generates an x0 vector that is of length length(unique(logage)) (that is, a single normalization factor for each unique entry in logAge) while StarFormationHistories.construct_x0 returns an x0 vector that is of length length(logAge); that is, a normalization factor for every entry in logAge. The order of the coefficients is such that the coefficient x[i] corresponds to the entry unique(logAge)[i]. \n\nNotes\n\nExamples\n\njulia> construct_x0_mdf([9.0,8.0,7.0], 10.0; normalize_value=5.0)\n3-element Vector{Float64}:\n 4.504504504504504\n 0.4504504504504504\n 0.04504504504504504\n\njulia> construct_x0_mdf(repeat([9.0,8.0,7.0,8.0];inner=3), 10.0; normalize_value=5.0)\n3-element Vector{Float64}:\n 4.504504504504504\n 0.4504504504504504\n 0.04504504504504504\n\njulia> construct_x0_mdf(repeat([9.0,8.0,7.0,8.0],3), 10.0; normalize_value=5.0) ≈ construct_x0([9.0,8.0,7.0], 10.0; normalize_value=5.0)\ntrue\n\n\n\n\n\n","category":"function"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"and StarFormationHistories.calculate_coeffs_mdf can be used to calculate per-template stellar mass coefficients (the r_jk above) given the results of a fit (which will be the R_j in the equations above)","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"StarFormationHistories.calculate_coeffs_mdf","category":"page"},{"location":"fitting/linear_amr/#StarFormationHistories.calculate_coeffs_mdf","page":"Linear Age-Metallicity Relation","title":"StarFormationHistories.calculate_coeffs_mdf","text":"calculate_coeffs_mdf(variables::AbstractVector{<:Number},\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number},\n T_max::Number\n α::Number,\n β::Number,\n σ::Number,)\ncalculate_coeffs_mdf(variables::AbstractVector{<:Number},\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number},\n T_max::Number)\n\nCalculates per-model stellar mass coefficients coeffs from the fitting parameters of StarFormationHistories.fit_templates_mdf and StarFormationHistories.hmc_sample_mdf. The variables returned by these functions is of length length(unique(logAge))+3. The first length(logAge) entries are stellar mass coefficients, one per unique entry in logAge. The final three elements are α, β, and σ defining a metallicity evolution such that the mean for element i of unique(logAge) is μ[i] = α * (T_max - exp10(unique(logAge)[i]) / 1e9) + β. The individual weights per each isochrone are then determined via Gaussian weighting with the above mean and the provided σ. The second call signature can be used on samples that include α, β, and σ.\n\nExamples\n\njulia> calculate_coeffs_mdf([1,1], [7,7,8,8], [-2,-1,-2,-1], 12, 0.05, -2.0, 0.2) ≈ [ 0.07673913563377144, 0.9232608643662287, 0.08509904500701986, 0.9149009549929802 ]\ntrue\n\n\n\n\n\n","category":"function"},{"location":"fitting/linear_amr/#Sampling-Methods","page":"Linear Age-Metallicity Relation","title":"Sampling Methods","text":"","category":"section"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"We additionally offer a sampling method for this linear age-metallicity relation using HMC:","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"StarFormationHistories.hmc_sample_mdf","category":"page"},{"location":"fitting/linear_amr/#StarFormationHistories.hmc_sample_mdf","page":"Linear Age-Metallicity Relation","title":"StarFormationHistories.hmc_sample_mdf","text":"hmc_sample_mdf(models::AbstractVector{T},\n data::AbstractMatrix{<:Number},\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number},\n T_max::Number,\n nsteps::Integer;\n composite=Matrix{S}(undef,size(data)),\n rng::Random.AbstractRNG=Random.default_rng(),\n kws...) where {S <: Number, T <: AbstractMatrix{S}}\n\nMethod to sample the posterior of the star formation history coefficients constrained to have a linear age-metallicity relation with the mean metallicity of element i of unique(logAge) being μ[i] = α * (T_max - exp10(unique(logAge)[i]) / 1e9) + β. α is therefore a slope in the units of metallicities per Gyr, and β is the mean metallicity value of stars born at lookback time T_max which has units of Gyr. Individual weights for each isochrone template are then determined via Gaussian weighting with the above mean and the standard deviation σ, which can either be fixed or fit. This method is essentially an analog of StarFormationHistories.fit_templates_mdf that samples the posterior rather than using optimization methods to find the maximum likelihood estimate. This method uses the No-U-Turn sampler as implemented in DynamicHMC.jl, which is a form of dynamic Hamiltonian Monte Carlo.\n\nThis function is designed to work best with a \"grid\" of stellar models, defined by the outer product of N unique entries in logAge and M unique entries in metallicities. See the examples for more information on usage.\n\nArguments\n\nmodels::AbstractVector{<:AbstractMatrix{<:Number}} is a vector of equal-sized matrices that represent the template Hess diagrams for the simple stellar populations that compose the observed Hess diagram.\ndata::AbstractMatrix{<:Number} is the Hess diagram for the observed data.\nlogAge::AbstractVector{<:Number} is the vector containing the effective ages of the stellar populations used to create the templates in models, in units of log10(age [yr]). For example, if a population has an age of 1 Myr, its entry in logAge should be log10(10^6) = 6.0.\nmetallicities::AbstractVector{<:Number} is the vector containing the effective metallicities of the stellar populations used to create the templates in models. This is most commonly a logarithmic abundance like [M/H] or [Fe/H], but you could use a linear abundance like the metal mass fraction Z if you wanted to. There are some notes on the Wikipedia that might be useful.\nT_max::Number is the time at which the age-metallicity relation has a value of \beta in Gyr. For example, if the oldest stellar populations in your isochrone grid are 12 Gyr old, you could set T_max = 12.0. \nnsteps::Integer is the number of samples to draw per chain.\n\nOptional Arguments (NOT YET IMPLEMENTED)\n\nnchains::Integer: If this argument is not provided, this method will return a single chain. If this argument is provided, it will sample nchains chains using all available threads and will return a vector of the individual chains. If nchains is set, composite must be a vector of matrices containing a working matrix for each chain. \n\nKeyword Arguments\n\ncomposite is the working matrix (or vector of matrices, if the argument nchains is provided) that will be used to store the composite Hess diagram model during computation; must be of the same size as the templates contained in models and the observed Hess diagram data.\nrng::Random.AbstractRNG is the random number generator that will be passed to DynamicHMC.jl. If nchains is provided this method will attempt to sample in parallel, requiring a thread-safe rng such as that provided by Random.default_rng(). \n\nAll other keyword arguments kws... will be passed to DynamicHMC.mcmc_with_warmup or DynamicHMC.mcmc_keep_warmup depending on whether nchains is provided.\n\nReturns (NEEDS UPDATED)\n\nIf nchains is not provided, returns a NamedTuple as summarized in DynamicHMC.jl's documentation. In short, the matrix of samples can be extracted and transformed as exp.( result.posterior_matrix ). Statistics about the chain can be obtained with DynamicHMC.Diagnostics.summarize_tree_statistics(result.tree_statistics); you want to see a fairly high acceptance rate (>0.5) and the majority of samples having termination criteria being \"turning.\" See DynamicHMC.jl's documentation for more information.\nIf nchains is provided, returns a vector of length nchains of the same NamedTuples described above. The samples from each chain in the returned vector can be stacked to a single (nsamples, nchains, length(models)) matrix with DynamicHMC.stack_posterior_matrices(result).\n\n\n\n\n\n","category":"function"},{"location":"fitting/linear_amr/#linear_amr_implementation","page":"Linear Age-Metallicity Relation","title":"Implementation","text":"","category":"section"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"While one could optimize the above model without an analytic gradient, such gradient-free methods are typically slower and less robust. One could also calculate the gradient numerically using finite differences or auto-differentiation, but these are still slower than analytic calculations. We will show that the gradient of this hierarchical model is analytic, allowing us to design an efficient optimization scheme.","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"Equation 21 in Dolphin 2001 gives the gradient of our objective function with respect to the underlying coefficients","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"beginaligned\nF equiv - textln mathscrL = sum_i m_i - n_i times left( 1 - textln left( fracn_im_i right) right) \nfracpartial Fpartial r_jk = sum_i c_ijk left( 1 - fracn_im_i right)\nendaligned","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"where c_ijk is the value of template jk in bin i and n_i is bin i of the observed Hess diagram. These partial derivatives are easy to obtain, but we need partials with respect to the per-age-bin fitting parameters R_j. Given the above relation between r_jk and R_j, we can calculate these derivatives as","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"beginaligned\nfracpartial Fpartial R_j = sum_k fracpartial Fpartial r_jk fracpartial r_jkpartial R_j \nfracpartial r_jkpartial R_j = frac textexp left( - frac12 left( frac textMtextH_k - mu_jsigma right)^2 right)sum_k textexp left( - frac12 left( frac textMtextH_k - mu_jsigma right)^2 right) = fracr_jkR_j\nendaligned","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"Then we need only the partial derivatives of the objective function F with respect to the MDF parameters, which in this case are alpha beta sigma. For convenience we will rewrite","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"r_jk = R_j frac textexp left( - frac12 left( frac textMtextH_k - mu_jsigma right)^2 right)sum_k textexp left( - frac12 left( frac textMtextH_k - mu_jsigma right)^2 right) = R_j fracA_jksum_k A_jk","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"as many different types of models can be expressed via this simplified notation by substituting the A_jk with different distributions. This allows us to write ","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"beginaligned\nfracpartial Fpartial beta = sum_jk fracpartial Fpartial r_jk fracpartial r_jkpartial beta \nfracpartial r_jkpartial beta = R_j left( frac1sum_k A_jk fracpartial A_jkpartial beta - fracA_jkleft( sum_k A_jk right)^2 fracpartial sum_k A_jkpartial beta right) \n= fracR_jsum_k A_jk left( fracpartial A_jkpartial beta - fracA_jksum_k A_jk sum_k fracpartial A_jkpartial beta right) \nendaligned","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"Given our specific definition of A_jk being a Gaussian distribution, we have","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"beginaligned\nmu_j = alpha left( T_textmax - t_j right) + beta \nfracpartial A_jkpartial beta = fracpartialpartial beta left textexp left( - frac12 left( frac textMtextH_k - mu_jsigma right)^2 right) right \n= fracA_jksigma^2 left( textMtextH_k - mu_j right)\nendaligned","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"We can now substitute this result into the above expressions to write","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"beginaligned\nfracpartial Fpartial beta = sum_jk fracpartial Fpartial r_jk fracpartial r_jkpartial beta \n= sum_jk fracpartial Fpartial r_jk fracR_jsum_k A_jk left( fracpartial A_jkpartial beta - fracA_jksum_k A_jk sum_k fracpartial A_jkpartial beta right) \n= sum_jk fracpartial Fpartial r_jk fracR_jsigma^2 sum_k A_jk left( A_jk left( textMtextH_k - mu_j right) - fracA_jksum_k A_jk sum_k A_jk left( textMtextH_k - mu_j right) right)\nendaligned","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"It can be shown that the partial derivative of F with respect to alpha is simply","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"fracpartial Fpartial alpha = sum_jk fracpartial Fpartial r_jk fracpartial r_jkpartial alpha = sum_jk fracpartial Fpartial r_jk fracpartial r_jkpartial beta times left( T_textmax - t_j right) ","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"The partial derivative with respect to sigma is slightly more complicated, but we can start identically to how we started above when deriving fracpartial Fpartial beta with","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"beginaligned\nfracpartial Fpartial sigma = sum_jk fracpartial Fpartial r_jk fracpartial r_jkpartial sigma \nfracpartial r_jkpartial sigma = R_j left( frac1sum_k A_jk fracpartial A_jkpartial sigma - fracA_jkleft( sum_k A_jk right)^2 fracpartial sum_k A_jkpartial sigma right) \n= fracR_jsum_k A_jk left( fracpartial A_jkpartial sigma - fracA_jksum_k A_jk sum_k fracpartial A_jkpartial sigma right) \nendaligned","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"Then all we need is","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"fracpartial A_jkpartial sigma = fracA_jk left( textMtextH_k - mu_j right)^2sigma^3","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"which we can substitute into the above expressions to find fracpartial Fpartial sigma.","category":"page"},{"location":"simulate/#simulate","page":"Simulating Color-Magnitude Diagrams","title":"Simulating Color-Magnitude Diagrams","text":"","category":"section"},{"location":"simulate/","page":"Simulating Color-Magnitude Diagrams","title":"Simulating Color-Magnitude Diagrams","text":"Modelling observations of resolved stellar populations (e.g., color-magnitude or Hess diagrams) with user-defined star formation histories can be useful for comparison to actual observations, but also enables a number of other scientific activities (e.g., making predictions to motivate observational proposals). To support these uses we offer methods for sampling stellar populations from isochrones using user-defined star formation histories, initial mass functions, and stellar binary models. These methods require data from user-provided isochrones (this package does not provide any), an initial mass function model (such as those provided in InitialMassFunctions.jl), and a model specifying how (or if) to sample binary or multi-star systems. ","category":"page"},{"location":"simulate/","page":"Simulating Color-Magnitude Diagrams","title":"Simulating Color-Magnitude Diagrams","text":"The simplest methods only sample stars from a single stellar population. We provide a method that samples up to a provided stellar mass, generate_stars_mass (e.g., 10^7 textM_odot) and a method that samples up to a provided absolute magnitude generate_stars_mag (e.g., M_V=-10). These are documented under the first subsection below. These methods are single-threaded.","category":"page"},{"location":"simulate/","page":"Simulating Color-Magnitude Diagrams","title":"Simulating Color-Magnitude Diagrams","text":"We also offer methods for sampling populations with complex star formation histories; these are implicitly multi-threaded across the separate populations if you start Julia with multiple threads (e.g., with julia -t 4 or similar). We provide generate_stars_mass_composite for sampling such populations up to a provided stellar mass and generate_stars_mag_composite for sampling such populations up to a provided absolute magnitude. These are documented under the second subsection below.","category":"page"},{"location":"simulate/#Simple-Stellar-Populations","page":"Simulating Color-Magnitude Diagrams","title":"Simple Stellar Populations","text":"","category":"section"},{"location":"simulate/","page":"Simulating Color-Magnitude Diagrams","title":"Simulating Color-Magnitude Diagrams","text":"generate_stars_mass\ngenerate_stars_mag","category":"page"},{"location":"simulate/#StarFormationHistories.generate_stars_mass","page":"Simulating Color-Magnitude Diagrams","title":"StarFormationHistories.generate_stars_mass","text":"generate_stars_mass(mini_vec::AbstractVector{<:Number},\n mags, mag_names::AbstractVector{String},\n limit::Number,\n imf::Distributions.Sampleable{Distributions.Univariate, Distributions.Continuous};\n dist_mod::Number=0,\n rng::Random.AbstractRNG=Random.default_rng(),\n mag_lim::Number = Inf,\n mag_lim_name::String = \"V\",\n binary_model::StarFormationHistories.AbstractBinaryModel =\n StarFormationHistories.RandomBinaryPairs(0.3))\n\nGenerates a random sample of stars from an isochrone defined by the provided initial stellar masses mini_vec, absolute magnitudes mags, and filter names mag_names with total population birth stellar mass limit (e.g., 1e5 solar masses). Initial stellar masses are sampled from the provided imf. \n\nArguments\n\nmini_vec::AbstractVector{<:Number} contains the initial masses (in solar masses) for the stars in the isochrone; must be mutable as we call Interpolations.deduplicate_knots!(mini_vec).\nmags contains the absolute magnitudes from the isochrone in the desired filters corresponding to the same stars as provided in mini_vec. mags is internally interpreted and converted into a standard format by StarFormationHistories.ingest_mags. Valid inputs are:\nmags::AbstractVector{AbstractVector{<:Number}}, in which case the length of the outer vector length(mags) can either be equal to length(mini_vec), in which case the length of the inner vectors must all be equal to the number of filters you are providing, or the length of the outer vector can be equal to the number of filters you are providing, and the length of the inner vectors must all be equal to length(mini_vec); this is the more common use-case.\nmags::AbstractMatrix{<:Number}, in which case mags must be 2-dimensional. Valid shapes are size(mags) == (length(mini_vec), nfilters) or size(mags) == (nfilters, length(mini_vec)), with nfilters being the number of filters you are providing.\nmag_names::AbstractVector{String} contains strings describing the filters you are providing in mags; an example might be [\"B\",\"V\"]. These are used when mag_lim is finite to determine what filter you want to use to limit the faintest stars you want returned.\nlimit::Number gives the total birth stellar mass of the population you want to sample. See the \"Notes\" section on population masses for more information.\nimf::Distributions.Sampleable{Distributions.Univariate, Distributions.Continuous} is a sampleable continuous univariate distribution implementing a stellar initial mass function with a defined rand(rng::Random.AbstractRNG, imf) method to use for sampling masses. All instances of Distributions.ContinuousUnivariateDistribution are also valid. Implementations of commonly used IMFs are available in InitialMassFunctions.jl.\n\nKeyword Arguments\n\ndist_mod::Number=0 is the distance modulus (see StarFormationHistories.distance_modulus) you wish to have added to the returned magnitudes to simulate a population at a particular distance.\nrng::Random.AbstractRNG=Random.default_rng() is the rng instance that will be used to sample the stellar initial masses from imf.\nmag_lim::Number=Inf gives the faintest apparent magnitude for stars you want to be returned in the output. Stars fainter than this magnitude will still be sampled and contribute properly to the total mass of the population, but they will not be returned.\nmag_lim_name::String=\"V\" gives the filter name (as contained in mag_names) to use when considering if a star is fainter than mag_lim. This is unused if mag_lim is infinite.\nbinary_model::StarFormationHistories.AbstractBinaryModel=StarFormationHistories.RandomBinaryPairs(0.3) is an instance of a model for treating binaries; currently provided options are NoBinaries, RandomBinaryPairs, and BinaryMassRatio.\n\nReturns\n\n(sampled_masses, sampled_mags) defined as\n\nsampled_masses::Vector{SVector{N,eltype(imf)}} is a vector containing the initial stellar masses of the stars sampled by sample_system; see that method's documentation for details on format. In short, each StaticArrays.SVector represents one stellar system and each entry in each StaticArrays.SVector is one star in that system. Entries will be 0 when companions could have been sampled but were not (i.e., when using a binary_model that supports multi-star systems). \nsampled_mags::Vector{SVector{N,<:Number}} is a vector containing StaticArrays.SVectors with the multi-band magnitudes of the sampled stars. To get the magnitude of star i in band j, you index as sampled_mags[i][j]. This can be reinterpreted as a 2-dimensional Matrix with reduce(hcat,sampled_mags). \n\nNotes\n\nPopulation Masses\n\nGiven a particular isochrone with an initial mass vector mini_vec, it will never cover the full range of stellar birth masses because stars that die before present-day are not included in the isochrone. However, these stars were born, and so contribute to the total birth mass of the system. There are two ways to properly account for this lost mass when sampling:\n\nSet the upper limit for masses that can be sampled from the imf distribution to a physical value for the maximum birth mass of stars (e.g., 100 solar masses). In this case, these stars will be sampled from imf, and will contribute their masses to the system, but they will not be returned if their birth mass is greater than maximum(mini_vec). This is typically easiest for the user and only results in ∼15% loss of efficiency for 10 Gyr isochrones. This approach is preferred when sampling with binaries.\nSet the upper limit for masses that can be sampled from the imf distribution to maximum(mini_vec) and adjust limit to respect the amount of initial stellar mass lost by not sampling higher mass stars. This can be calculated as new_limit = limit * ( QuadGK.quadgk(x->x*pdf(imf,x), minimum(mini_vec), maximum(mini_vec))[1] / QuadGK.quadgk(x->x*pdf(imf,x), minimum(imf), maximum(imf))[1] ), with the multiplicative factor being the fraction of the population stellar mass contained in stars with initial masses between minimum(mini_vec) and maximum(mini_vec); this factor is the ratio\n\nfracint_a^b m times fracdN(m)dm dmint_0^ m times fracdN(m)dm dm\n\nNote that, if binaries are included, this approach only forms binary pairs between stars whose masses are less than maximum(mini_vec). This is probably not desired, so we recommend the previous approach when including binaries.\n\n\n\n\n\n","category":"function"},{"location":"simulate/#StarFormationHistories.generate_stars_mag","page":"Simulating Color-Magnitude Diagrams","title":"StarFormationHistories.generate_stars_mag","text":"(sampled_masses, sampled_mags) = generate_stars_mag(mini_vec::AbstractVector{<:Number}, mags, mag_names::AbstractVector{String}, absmag::Real, absmag_name::String, imf::Distributions.Sampleable{Distributions.Univariate,Distributions.Continuous}; dist_mod::Number=0, rng::AbstractRNG=default_rng(), mag_lim::Number=Inf, mag_lim_name::String=\"V\", binary_model::StarFormationHistories.AbstractBinaryModel=RandomBinaryPairs(0.3))\n\nGenerates a mock stellar population from an isochrone defined by the provided initial stellar masses mini_vec, absolute magnitudes mags, and filter names mag_names. The population is sampled to a total absolute magnitude absmag::Real (e.g., -7 or -12) in the filter absmag_name::String (e.g., \"V\" or \"F606W\") which is contained in the provided mag_names::AbstractVector{String}. Other arguments are shared with generate_stars_mass, which contains the main documentation.\n\nNotes\n\nPopulation Magnitudes\n\nUnlike when sampling a population to a fixed initial birth mass, as is implemented in generate_stars_mass, when generating a population up to a fixed absolute magnitude, only stars that survive to present-day contribute to the flux of the population. If you choose to limit the apparent magnitude of stars returned by passing the mag_lim and mag_lim_name keyword arguments, stars fainter than your chosen limit will still be sampled and will still contribute their luminosity to the total population, but they will not be contained in the returned output. \n\n\n\n\n\n","category":"function"},{"location":"simulate/#Complex-Stellar-Populations","page":"Simulating Color-Magnitude Diagrams","title":"Complex Stellar Populations","text":"","category":"section"},{"location":"simulate/","page":"Simulating Color-Magnitude Diagrams","title":"Simulating Color-Magnitude Diagrams","text":"generate_stars_mass_composite\ngenerate_stars_mag_composite","category":"page"},{"location":"simulate/#StarFormationHistories.generate_stars_mass_composite","page":"Simulating Color-Magnitude Diagrams","title":"StarFormationHistories.generate_stars_mass_composite","text":"(sampled_masses, sampled_mags) = generate_stars_mass_composite(mini_vec::AbstractVector{<:AbstractVector{<:Number}}, mags::AbstractVector, mag_names::AbstractVector{String}, limit::Number, massfrac::AbstractVector{<:Number}, imf::Sampleable{Univariate,Continuous}; kws...)\n\nGenerates a random sample of stars with a complex star formation history using multiple isochrones. Very similar to generate_stars_mass except the isochrone-related arguments mini_vec and mags should now be vectors of vectors containing the relevant data for the full set of isochrones to be considered. The total birth stellar mass of the sampled population is given by limit. The proportion of this mass allotted to each of the individual isochrones is given by the entries of the massfrac vector. This basically just proportions limit according to massfrac and calls generate_stars_mass for each of the individual stellar populations; as such it is set up to multi-thread across the multiple stellar populations. \n\nArguments\n\nmini_vec::AbstractVector{<:AbstractVector{<:Number}} contains the initial masses (in solar masses) for the stars in each isochrone; the internal vectors must be mutable as we will call Interpolations.deduplicate_knots! on each. The length of mini_vec should be equal to the number of isochrones. \nmags contains the absolute magnitudes from the isochrones in the desired filters corresponding to the same stars as provided in mini_vec. The length of mags should be equal to the number of isochrones. The individual elements of mags are each internally interpreted and converted into a standard format by StarFormationHistories.ingest_mags. The valid formats for the individual elements of mags are:\nAbstractVector{AbstractVector{<:Number}}, in which case the length of the vector length(mags[i]) can either be equal to length(mini_vec[i]), in which case the length of the inner vectors must all be equal to the number of filters you are providing, or the length of the outer vector can be equal to the number of filters you are providing, and the length of the inner vectors must all be equal to length(mini_vec[i]); this is the more common use-case.\nAbstractMatrix{<:Number}, in which case mags[i] must be 2-dimensional. Valid shapes are size(mags[i]) == (length(mini_vec[i]), nfilters) or size(mags[i]) == (nfilters, length(mini_vec[i])), with nfilters being the number of filters you are providing.\nmag_names::AbstractVector{String} contains strings describing the filters you are providing in mags; an example might be [\"B\",\"V\"]. These are used when mag_lim is finite to determine what filter you want to use to limit the faintest stars you want returned. These are assumed to be the same for all isochrones.\nlimit::Number gives the total birth stellar mass of the population you want to sample. \nmassfrac::AbstractVector{<:Number} is vector giving the relative fraction of mass allotted to each individual stellar population; length must be equal to the length of mini_vec and mags. \nimf::Distributions.Sampleable{Distributions.Univariate, Distributions.Continuous} is a sampleable continuous univariate distribution implementing a stellar initial mass function with a defined rand(rng::Random.AbstractRNG, imf) method to use for sampling masses. All instances of Distributions.ContinuousUnivariateDistribution are also valid. Implementations of commonly used IMFs are available in InitialMassFunctions.jl.\n\nKeyword Arguments\n\nAll keyword arguments kws... are passed to generate_stars_mass; you should refer to that method's documentation for more information. \n\nReturns\n\nsampled_masses::Vector{Vector{SVector{N,eltype(imf)}}} is a vector of vectors containing the initial stellar masses of the sampled stars. The outer vectors are separated by the isochrone the stars were generated from; i.e., all of sampled_masses[1] were sampled from mini_vec[1] and so on. These can be concatenated into a single vector with reduce(vcat,sampled_masses). The format of the contained StaticArrays.SVectors are as output from sample_system; see that method's documentation for more details. \nsampled_mags::Vector{Vector{SVector{N,<:Number}}} is a vector of vectors containing StaticArrays.SVectors with the multi-band magnitudes of the sampled stars. The outer vectors are separated by the isochrone the stars were generated from; i.e. all of sampled_mags[1] were sampled from mags[1] and so on. To get the magnitude of star i in band j sampled from isochrone k, you would do sampled_mags[k][i][j]. This can be concatenated into a Vector{SVector} with reduce(vcat,sampled_mags) and a 2-D Matrix with reduce(hcat,reduce(vcat,sampled_mags)). \n\n\n\n\n\n","category":"function"},{"location":"simulate/#StarFormationHistories.generate_stars_mag_composite","page":"Simulating Color-Magnitude Diagrams","title":"StarFormationHistories.generate_stars_mag_composite","text":"(sampled_masses, sampled_mags) = generate_stars_mag_composite(mini_vec::AbstractVector{<:AbstractVector{<:Number}}, mags::AbstractVector, mag_names::AbstractVector{String}, absmag::Number, absmag_name::String, fracs::AbstractVector{<:Number}, imf::Sampleable{Univariate,Continuous}; frac_type::String=\"lum\", kws...)\n\nGenerates a random sample of stars with a complex star formation history using multiple isochrones. Very similar to generate_stars_mag except the isochrone-related arguments mini_vec and mags should now be vectors of vectors containing the relevant data for the full set of isochrones to be considered. The total absolute magnitude of the sampled population is given by absmag. The proportion of the luminosity allotted to each of the individual isochrones is given by the entries of the frac vector. This basically just proportions the luminosity according to frac and calls generate_stars_mag for each of the individual stellar populations; as such it is set up to multi-thread across the multiple stellar populations. \n\nArguments\n\nmini_vec::AbstractVector{<:AbstractVector{<:Number}} contains the initial masses (in solar masses) for the stars in each isochrone; the internal vectors must be mutable as we will call Interpolations.deduplicate_knots! on each. The length of mini_vec should be equal to the number of isochrones. \nmags contains the absolute magnitudes from the isochrones in the desired filters corresponding to the same stars as provided in mini_vec. The length of mags should be equal to the number of isochrones. The individual elements of mags are each internally interpreted and converted into a standard format by StarFormationHistories.ingest_mags. The valid formats for the individual elements of mags are:\nAbstractVector{AbstractVector{<:Number}}, in which case the length of the vector length(mags[i]) can either be equal to length(mini_vec[i]), in which case the length of the inner vectors must all be equal to the number of filters you are providing, or the length of the outer vector can be equal to the number of filters you are providing, and the length of the inner vectors must all be equal to length(mini_vec[i]); this is the more common use-case.\nAbstractMatrix{<:Number}, in which case mags[i] must be 2-dimensional. Valid shapes are size(mags[i]) == (length(mini_vec[i]), nfilters) or size(mags[i]) == (nfilters, length(mini_vec[i])), with nfilters being the number of filters you are providing.\nmag_names::AbstractVector{String} contains strings describing the filters you are providing in mags; an example might be [\"B\",\"V\"]. These are used when mag_lim is finite to determine what filter you want to use to limit the faintest stars you want returned. These are assumed to be the same for all isochrones.\nabsmag::Number gives the total absolute magnitude of the complex population to be sampled. \nfracs::AbstractVector{<:Number} is a vector giving the relative fraction of luminosity or mass (determined by the frac_type keyword argument) allotted to each individual stellar population; length must be equal to the length of mini_vec and mags. \nimf::Distributions.Sampleable{Distributions.Univariate, Distributions.Continuous} is a sampleable continuous univariate distribution implementing a stellar initial mass function with a defined rand(rng::Random.AbstractRNG, imf) method to use for sampling masses. All instances of Distributions.ContinuousUnivariateDistribution are also valid. Implementations of commonly used IMFs are available in InitialMassFunctions.jl.\n\nKeyword Arguments\n\nfrac_type::String either \"lum\", in which case fracs is assumed to contain the relative luminosity fractions for each individual isochrone, or \"mass\", in which case it is assumed that fracs contains mass fractions (\"mass\" is not yet implemented). \n\nAll other keyword arguments kws... are passed to generate_stars_mag; you should refer to that method's documentation for more information. \n\nReturns\n\nsampled_masses::Vector{Vector{SVector{N,eltype(imf)}}} is a vector of vectors containing the initial stellar masses of the sampled stars. The outer vectors are separated by the isochrone the stars were generated from; i.e., all of sampled_masses[1] were sampled from mini_vec[1] and so on. These can be concatenated into a single vector with reduce(vcat,sampled_masses). The format of the contained StaticArrays.SVectors are as output from sample_system; see that method's documentation for more details. \nsampled_mags::Vector{Vector{SVector{N,<:Number}}} is a vector of vectors containing StaticArrays.SVectors with the multi-band magnitudes of the sampled stars. The outer vectors are separated by the isochrone the stars were generated from; i.e. all of sampled_mags[1] were sampled from mags[1] and so on. To get the magnitude of star i in band j sampled from isochrone k, you would do sampled_mags[k][i][j]. This can be concatenated into a Vector{SVector} with reduce(vcat,sampled_mags) and a 2-D Matrix with reduce(hcat,reduce(vcat,sampled_mags)). \n\n\n\n\n\n","category":"function"},{"location":"simulate/#Observational-Effects","page":"Simulating Color-Magnitude Diagrams","title":"Observational Effects","text":"","category":"section"},{"location":"simulate/","page":"Simulating Color-Magnitude Diagrams","title":"Simulating Color-Magnitude Diagrams","text":"The output produced from the above methods are clean in the sense that they do not include any observational effects like photometric error or incompleteness. These effects should be implemented in a post-processing step. We provide a simple method model_cmd that accepts user-defined photometric error and completeness functions and applies them to the initial catalog, returning a Monte Carlo realization of a possible observed catalog. This method assumes Gaussian photometric errors and that the photometric error and completeness functions are separable by filter – these assumptions are not applicable for all types of data, but the source code for the method is exceedingly simple (~20 lines) and should provide an example for how you could write a similar method that more accurately reflects your data.","category":"page"},{"location":"simulate/","page":"Simulating Color-Magnitude Diagrams","title":"Simulating Color-Magnitude Diagrams","text":"model_cmd","category":"page"},{"location":"simulate/#StarFormationHistories.model_cmd","page":"Simulating Color-Magnitude Diagrams","title":"StarFormationHistories.model_cmd","text":"new_mags [, good_idxs] = model_cmd(mags::AbstractVector{<:AbstractVector{<:Number}}, errfuncs, completefuncs; rng::Random.AbstractRNG=Random.default_rng(), ret_idxs::Bool=false)\n\nSimple method for modelling photometric error and incompleteness to \"mock observe\" a pure catalog of stellar photometry, such as those produced by generate_stars_mass and generate_stars_mag. This method assumes Gaussian photometric errors and that the photometric error and completeness functions are separable by filter. \n\nArguments\n\nmags::AbstractVector{<:AbstractVector{<:Number}}: a vector of vectors giving the magnitudes of each star to be modelled. The first index is the per-star index and the second index is the per-filter index (so mags[10][2] would give the magnitude of the tenth star in the second filter). This is the same format as the magnitudes returned by generate_stars_mass and generate_stars_mag; to use output from the composite versions, you must first reduce(vcat,mags) before passing to this function.\nerrfuncs: an iterable (typically a vector or tuple) of callables (typically functions or interpolators) with length equal to the number of filters contained in the elements of mags. This iterable must contain callables that, when called with the associated magnitudes from mags, will return the expected 1-σ photometric error at that magnitude. The organization is such that the photometric error for star i in band j is σ_ij = errfuncs[j](mags[i][j]). \ncompletefuncs: an iterable (typically a vector or tuple) of callables (typically functions or interpolators) with length equal to the number of filters contained in the elements of mags. This iterable must contain callables that, when called with the associated magnitudes from mags, will return the probability that a star with that magnitude in that band will be found in your color-magnitude diagram (this should include the original detection probability and any post-detection quality, morphology, or other cuts). The organization is such that the detection probability for star i in band j is c_ij = completefuncs[j](mags[i][j]).\n\nKeyword Arguments\n\nrng::AbstractRNG=Random.default_rng(): The object to use for random number generation.\nret_idxs::Bool: whether to return the indices of the input mags for the stars that were successfully \"observed\" and are represented in the output new_mags.\n\nReturns\n\nnew_mags: an object similar to mags (i.e., a Vector{Vector{<:Number}}, Vector{SVector{N,<:Number}}, etc.) containing the magnitudes of the mock-observed stars. This will be shorter than the provided mags vector as we are modelling photometric incompleteness, and the magnitudes will also have random photometric errors added to them. This can be reinterpreted as a 2-dimensional Matrix with reduce(hcat,new_mags).\ngood_idxs: if ret_idxs is true, the vector of indices into the input mags for the stars that were successfully \"observed\" and are represented in the output new_mags.\n\nNotes\n\nThis is a simple implementation that seeks to show a simple example of how one can post-process catalogs of \"pure\" stars from methods like generate_stars_mass and generate_stars_mag to include observational effects. This method assumes Gaussian photometric errors, which may not, in general, be accurate. It also assumes that the total detection probability can be modelled as the product of the single-filter detection probabilities as computed by completefuncs (i.e., that the completeness functions are separable across filters). This can be a reasonable assumption when you have separate photometric catalogs derived for each filter and you only collate them afterwards, but it is generally not a good assumption for detection algorithms that operate on simultaneously on multi-band photometry – the completeness functions for these types of algorithms are generally not separable.\n\n\n\n\n\n","category":"function"},{"location":"simulate/#Developer-Internals","page":"Simulating Color-Magnitude Diagrams","title":"Developer Internals","text":"","category":"section"},{"location":"simulate/","page":"Simulating Color-Magnitude Diagrams","title":"Simulating Color-Magnitude Diagrams","text":"StarFormationHistories.ingest_mags\nStarFormationHistories.sort_ingested\nStarFormationHistories.mass_limits","category":"page"},{"location":"simulate/#StarFormationHistories.ingest_mags","page":"Simulating Color-Magnitude Diagrams","title":"StarFormationHistories.ingest_mags","text":"new_mags = ingest_mags(mini_vec::AbstractVector, mags::AbstractVector{T}) where {S <: Number, T <: AbstractVector{S}}\nnew_mags = ingest_mags(mini_vec::AbstractVector, mags::AbstractMatrix{S}) where S <: Number\n\nReinterprets provided mags to be in the correct format for input to Interpolations.interpolate.\n\nReturns\n\nnew_mags::Base.ReinterpretArray{StaticArrays.SVector}: a length(mini_vec) vector of StaticArrays.SVectors containing the same data as mags but formatted for input to Interpolations.interpolate.\n\n\n\n\n\n","category":"function"},{"location":"simulate/#StarFormationHistories.sort_ingested","page":"Simulating Color-Magnitude Diagrams","title":"StarFormationHistories.sort_ingested","text":"(new_mini_vec, new_mags) = sort_ingested(mini_vec::AbstractVector, mags::AbstractVector)\n\nTakes mini_vec and mags and ensures that mini_vec is sorted (sometimes in PARSEC isochrones they are not) and calls Interpolations.deduplicate_knots! on mini_vec to ensure there are no repeat entries. Arguments must satisfy length(mini_vec) == length(mags). \n\n\n\n\n\n","category":"function"},{"location":"simulate/#StarFormationHistories.mass_limits","page":"Simulating Color-Magnitude Diagrams","title":"StarFormationHistories.mass_limits","text":"(mmin, mmax) = mass_limits(mini_vec::AbstractVector{<:Number}, mags::AbstractVector{T},\n mag_names::AbstractVector{String}, mag_lim::Number,\n mag_lim_name::String) where T <: AbstractVector{<:Number}\n\nCalculates initial mass limits that reflect a given faint-end magnitude limit.\n\nArguments\n\nmini_vec::AbstractVector{<:Number}: a length nstars vector containing initial stellar masses.\nmags::AbstractVector{<:AbstractVector{<:Number}}: a length nstars vector, with each element being a length nfilters vector giving the magnitudes of each star in the filters mag_names.\nmag_names::AbstractVector{String}: a vector giving the names of each filter as strings.\nmag_lim::Number: the faint-end magnitude limit you wish to use.\nmag_lim_name::String: the name of the filter in which mag_lim is to be applied. Must be contained in mag_names.\n\nReturns\n\nmmin::eltype(mini_vec): the initial mass corresponding to your requested mag_lim in the filter mag_lim_name. If all stars provided are brighter than your requested mag_lim, then this will be equal to minimum(mini_vec).\nmmax::eltype(mini_vec): the maximum valid mass in mini_vec; simply maximum(mini_vec).\n\nExamples\n\njulia> mass_limits([0.05,0.1,0.2,0.3], [[4.0],[3.0],[2.0],[1.0]], [\"F090W\"], 2.5, \"F090W\")\n(0.15, 0.3)\n\njulia> mass_limits([0.05,0.1,0.2,0.3], [[4.0,3.0],[3.0,2.0],[2.0,1.0],[1.0,0.0]], [\"F090W\",\"F150W\"], 2.5, \"F090W\")\n(0.15, 0.3)\n\n\n\n\n\n","category":"function"},{"location":"fitting/internals/#Low-Level-Functions","page":"Low-Level Functions","title":"Low-Level Functions","text":"","category":"section"},{"location":"fitting/internals/","page":"Low-Level Functions","title":"Low-Level Functions","text":"StarFormationHistories.composite!\nStarFormationHistories.loglikelihood\nStarFormationHistories.∇loglikelihood\nStarFormationHistories.∇loglikelihood!\nStarFormationHistories.fg!\nStarFormationHistories.truncate_relweights\nStarFormationHistories.calculate_edges","category":"page"},{"location":"fitting/internals/#StarFormationHistories.composite!","page":"Low-Level Functions","title":"StarFormationHistories.composite!","text":" composite!(composite::AbstractMatrix{<:Number},\n coeffs::AbstractVector{<:Number},\n models::AbstractVector{T})\n where T <: AbstractMatrix{<:Number}\n\nUpdates the composite matrix in place with the linear combination of sum( coeffs .* models ); this is equation 1 in Dolphin 2002, m_i = sum_j r_j c_ij.\n\nExamples\n\njulia> C = zeros(5,5);\njulia> models = [rand(size(C)...) for i in 1:5];\njulia> coeffs = rand(length(models));\njulia> composite!(C, coeffs, models);\njulia> C ≈ sum( coeffs .* models)\ntrue\n\n\n\n\n\ncomposite!(composite::AbstractVector{<:Number},\n coeffs::AbstractVector{<:Number},\n models::AbstractMatrix{<:Number})\n\nUpdates the composite vector with the matrix-vector product of models * coeffs. This is equation 1 in Dolphin 2002, m_i = sum_j r_j c_ij.\n\nExamples\n\njulia> hist_size = (5,10);\njulia> models = reduce(hcat,rand(prod(hist_size)) for i in 1:20);\njulia> coeffs = rand(length(axes(models,2)));\njulia> C = zeros(length(axes(models,1)));\njulia> composite!(C, coeffs, models);\njulia> C ≈ models * coeffs\ntrue\n\nNotes\n\nWhile the other call signature for this function more closely mirrors the natural data structure for Hess diagrams (2D matrices for composite and each entry in models), this method operates on the same data but flattened. Thus composite becomes a vector rather than a matrix and models becomes a single matrix rather than a vector of matrices. The method StarFormationHistories.stack_models is provided to stack the models into this format. This data layout enables us to use the highly optimized LinearAlgebra.mul! function to perform the matrix-vector product which typically achieves >30% speedup relative to the more natural formulation. Additionally, as mul! will typically call to a BLAS matrix-vector product function like gemv! for our use-case, we can switch out Julia's default OpenBLAS at runtime for other BLAS libraries with Julia bindings like MKL and Apple Accelerate, enabling even greater performance improvements.\n\n\n\n\n\n","category":"function"},{"location":"fitting/internals/#StarFormationHistories.loglikelihood","page":"Low-Level Functions","title":"StarFormationHistories.loglikelihood","text":"loglikelihood(composite::AbstractArray{<:Number}, data::AbstractArray{<:Number})\n\nReturns the logarithm of the Poisson likelihood ratio given by equation 10 in Dolphin 2002,\n\ntextln mathscrL = sum_i -m_i + n_i times left( 1 - textln left( fracn_im_i right) right)\n\nwith composite being the complex Hess model diagram m_i (see StarFormationHistories.composite!) and data being the observed Hess diagram n_i.\n\nPerformance Notes\n\n~18.57 μs for composite=Matrix{Float64}(undef,99,99) and data=similar(composite).\n~20 μs for composite=Matrix{Float64}(undef,99,99) and data=Matrix{Int64}(undef,99,99).\n~9.3 μs for composite=Matrix{Float32}(undef,99,99) and data=similar(composite).\n~9.6 μs for composite=Matrix{Float32}(undef,99,99) and data=Matrix{Int64}(undef,99,99).\n\n\n\n\n\nloglikelihood(coeffs::AbstractVector{<:Number}, models::AbstractVector{T}, data::AbstractMatrix{<:Number}) where T <: AbstractMatrix{<:Number}\nloglikelihood(coeffs::AbstractVector{<:Number}, models::AbstractMatrix{<:Number}, data::AbstractVector{<:Number})\n\nReturns the logarithm of the Poisson likelihood ratio, but constructs the complex Hess diagram model as sum(coeffs .* models) rather than taking composite directly as an argument. Second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\n\n\n\n\n","category":"function"},{"location":"fitting/internals/#StarFormationHistories.∇loglikelihood","page":"Low-Level Functions","title":"StarFormationHistories.∇loglikelihood","text":"∇loglikelihood(model::AbstractArray{<:Number}, composite::AbstractArray{<:Number}, data::AbstractArray{<:Number})\n\nReturns the partial derivative of the logarithm of the Poisson likelihood ratio (StarFormationHistories.loglikelihood) with respect to the coefficient r_j on the provided model. If the complex Hess diagram model is m_i = sum_j r_j c_ij, then model is c_ij, and this function computes the partial derivative of textlog mathscrL with respect to the coefficient r_j. This is given by equation 21 in Dolphin 2002,\n\nfracpartial textlog mathscrLpartial r_j = sum_i c_ij left( fracn_im_i - 1 right)\n\nwhere n_i is bin i of the observed Hess diagram data. \n\nPerformance Notes\n\n~4.1 μs for model, composite, data all being Matrix{Float64}(undef,99,99).\n~1.3 μs for model, composite, data all being Matrix{Float32}(undef,99,99). \n\n\n\n\n\n∇loglikelihood(models::AbstractVector{T}, composite::AbstractMatrix{<:Number}, data::AbstractMatrix{<:Number}) where T <: AbstractMatrix{<:Number}\n∇loglikelihood(models::AbstractMatrix{<:Number}, composite::AbstractVector{<:Number}, data::AbstractVector{<:Number})\n\nComputes the gradient of the logarithm of the Poisson likelihood ratio with respect to the coefficients by calling the single-model ∇loglikelihood for every model in models. Second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\n\n\n\n\n∇loglikelihood(coeffs::AbstractVector{<:Number}, models::AbstractVector{T}, data::AbstractMatrix{<:Number}) where T <: AbstractMatrix{<:Number}\n∇loglikelihood(coeffs::AbstractVector{<:Number}, models::AbstractMatrix{<:Number}, data::AbstractVector{<:Number})\n\nForms the composite matrix from coefficients coeffs and model templates models and returns the gradient of the loglikelihood with respect to the coefficients. Second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\n\n\n\n\n","category":"function"},{"location":"fitting/internals/#StarFormationHistories.∇loglikelihood!","page":"Low-Level Functions","title":"StarFormationHistories.∇loglikelihood!","text":" ∇loglikelihood!(G::AbstractVector, composite::AbstractMatrix{<:Number}, models::AbstractVector{S}, data::AbstractMatrix{<:Number}) where S <: AbstractMatrix{<:Number}\n\nEfficiently computes the gradient of StarFormationHistories.loglikelihood with respect to all coefficients by updating G with the gradient. This will overwrite composite with the result of 1 .- (data ./ composite) so it shouldn't be reused after being passed to this function. \n\nArguments\n\nG::AbstractVector is the vector that will be mutated in-place with the computed gradient values.\nmodels::AbstractVector{<:AbstractMatrix{<:Number}} is the vector of matrices giving the model Hess diagrams.\ncomposite::AbstractMatrix{<:Number} is a matrix that contains the composite model sum(coeffs .* models).\ndata::AbstractMatrix{<:Number} contains the observed Hess diagram that is being fit.\n\n\n\n\n\nG = ∇loglikelihood!(G::AbstractVector, composite::AbstractVector{<:Number}, models::AbstractMatrix{<:Number}, data::AbstractVector{<:Number})\n\nUpdates and returns G with the gradient of the loglikelihood with respect to all coefficients. This call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\n\n\n\n\n","category":"function"},{"location":"fitting/internals/#StarFormationHistories.fg!","page":"Low-Level Functions","title":"StarFormationHistories.fg!","text":"-logL = fg!(F, G, coeffs::AbstractVector{<:Number}, models::AbstractVector{T}, data::AbstractMatrix{<:Number}, composite::AbstractMatrix{<:Number}) where T <: AbstractMatrix{<:Number}\n-logL = fg!(F, G, coeffs::AbstractVector{<:Number}, models::AbstractMatrix{<:Number}, data::AbstractVector{<:Number}, composite::AbstractVector{<:Number})\n\nComputes -loglikelihood and its gradient simultaneously for use with Optim.jl and other optimization APIs. See documentation here. Second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\n\n\n\n\n","category":"function"},{"location":"fitting/internals/#StarFormationHistories.truncate_relweights","page":"Low-Level Functions","title":"StarFormationHistories.truncate_relweights","text":"keep_idx::Vector{Int} = truncate_relweights(relweightsmin::Number, relweights::AbstractVector{<:Number}, logAge::AbstractVector{<:Number})\n\nMethod to truncate an isochrone grid with log10(age [yr]) values logAge and relative weights relweights due to an age-metallicity relation to only include models with relweights greater than relweightsmin times the maximum relative weight for each unique entry in logAge. The input vectors are the same as those for StarFormationHistories.fixed_amr, which includes more information. Returns a vector of the indices into relweights and logAge of the isochrone models whose relative weights are significant given the provided relweightsmin.\n\nExamples\n\nWhen using a fixed input age-metallicity relation as enabled by, for example, StarFormationHistories.fixed_amr, only the star formation rate (or total stellar mass) coefficients need to be fit, as the metallicity distribution is no longer a free parameter in the model. As such, the relative weights of each model with identical logAge but different metallicities only need to be computed once at the start of the optimization. As the metallicity distribution is not a free parameter, it is also possible to truncate the list of models to only those that contribute significantly to the final composite model to improve runtime performance. That is what this method does.\n\nA simple isochrone grid will be two-dimensional, encompassing age and metallicity. Consider a subset of the model grid with the same age such that unique(logAge) = [10.0] but a series of different metallicities, metallicities = -2.5:0.25:0. If we model the metallicity distribution function for this age as having a mean [M/H] of -2.0 and a Gaussian spread of 0.2 dex, then the relative weights of these models can be approximated as \n\nimport Distributions: Normal, pdf\nmetallicities = -2.5:0.25:0\nrelweights = pdf.(Normal(-2.0, 0.2), metallicities)\nrelweights ./= sum(relweights) # Normalize the relative weights to unity sum\n\n11-element Vector{Float64}:\n 0.021919934465195145\n 0.2284109622221623\n 0.4988954088848224\n 0.2284109622221623\n 0.021919934465195145\n 0.0004409368867815243\n 1.8592101580561089e-6\n 1.6432188478108614e-9\n 3.0442281937632026e-13\n 1.1821534989089337e-17\n 9.622444440364979e-23\n\nSeveral of these models with very low relative weights are unlikely to contribute significantly to the final composite model. We can select out only the significant ones with, say, relative weights greater than 10% of the maximum as StarFormationHistories.truncate_relweights(0.1, relweights, fill(10.0,length(metallicities))) which will return indices into relweights whose values are greater than 0.1 * maximum(relweights) = 0.04988954088848224,\n\n3-element Vector{Int64}:\n 2\n 3\n 4\n\nwhich correspond to relweights[2,3,4] = [ 0.2284109622221623, 0.4988954088848224, 0.2284109622221623 ]. If we use only these 3 templates in the fit, instead of the original 11, we will achieve a speedup of almost 4x with a minor loss in precision which, in most cases, will be less than the numerical uncertainties on the individual star formation rate parameters. However, as fits of these sort are naturally quite fast, we recommend use of this type of truncation only in applications where many fits are required (e.g., Monte Carlo experiments). For most applications, this level of optimization is not necessary.\n\n\n\n\n\n","category":"function"},{"location":"fitting/internals/#StarFormationHistories.calculate_edges","page":"Low-Level Functions","title":"StarFormationHistories.calculate_edges","text":"calculate_edges(edges, xlim, ylim, nbins, xwidth, ywidth)\n\nFunction to calculate the bin edges for 2D histograms. Returns (xbins, ybins) with both entries being ranges.\n\nKeyword Arguments\n\nedges is a tuple of ranges defining the left-side edges of the bins along the x-axis (edges[1]) and the y-axis (edges[2]). Example: (-1.0:0.1:1.5, 22:0.1:27.2). If edges is provided, it will simply be returned.\nxlim is a length-2 indexable object (e.g., a Vector{Float64} or NTuple{2,Float64)) giving the lower and upper bounds on the x-axis corresponding to the provided colors array. Example: [-1.0, 1.5]. This is only used if edges==nothing.\nylim is like xlim but for the y-axis corresponding to the provided mags array. Example [25, 20]. This is only used if edges==nothing.\nnbins::NTuple{2,<:Integer} is a 2-tuple of integers providing the number of bins to use along the x- and y-axes. This is only used if edges==nothing.\nxwidth is the bin width along the x-axis for the colors array. This is only used if edges==nothing and nbins==nothing. Example: 0.1. \nywidth is like xwidth but for the y-axis corresponding to the provided mags array. Example: 0.1.\n\n\n\n\n\n","category":"function"},{"location":"fitting/log_amr/#Logarithmic-Age-Metallicity-Relation","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"","category":"section"},{"location":"fitting/log_amr/#Definition","page":"Logarithmic Age-Metallicity Relation","title":"Definition","text":"","category":"section"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"This model differs from the linear age-metallicity relation (AMR) in its definition of the mean metallicity at lookback time t. In the linear model, we defined the mean metallicity as langle textMtextH rangle (t) = alpha left( T_textmax - t_j right) + beta, whereas in this model we define the metal mass fraction Z to be linear with lookback time, such that [M/H] scales logarithmically with lookback time,","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"beginaligned\nlangle Z rangle (t) = alpha left( T_textmax - t right) + beta \nlangle textMtextHrangle (t) equiv textlog left( fraclangle Z rangle left(tright)X right) - textlog left( fracZ_odotX_odot right)\nendaligned","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"with T_textmax being the earliest lookback time under consideration, such that langle Z rangle (T_textmax) = beta. We choose this parameterization so that positive alpha and beta result in an age-metallicity relation that is monotonically increasing with decreasing lookback time t. We model the spread in metallicities at fixed t as Gaussian in [M/H], identically to how it is modelled in the linear AMR case. This implies the spread is asymmetric in Z; this can be seen in the output of examples/log_amr/log_amr_example.jl, shown below, which illustrates the relative weights due to a logarithmic AMR across a grid of ages and metallicities. The per-model coefficients implied by a such a logarithmic AMR can be calculated with calculate_coeffs_logamr.","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"ENV[\"GKSwstype\"] = \"100\" # https://discourse.julialang.org/t/generation-of-documentation-fails-qt-qpa-xcb-could-not-connect-to-display/60988 # hide\ninclude(\"../../../examples/log_amr/log_amr_example.jl\") # hide\nsavefig(\"log_amr_plot.svg\"); nothing # hide","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"(Image: Visualization of the relative weights across a grid of logAge and metallicity under a logarithmic age-metallicity relation.)","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"StarFormationHistories.calculate_coeffs_logamr","category":"page"},{"location":"fitting/log_amr/#StarFormationHistories.calculate_coeffs_logamr","page":"Logarithmic Age-Metallicity Relation","title":"StarFormationHistories.calculate_coeffs_logamr","text":"calculate_coeffs_logamr(variables::AbstractVector{<:Number},\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number},\n T_max::Number\n [, α::Number, β::Number, σ::Number];\n MH_func = StarFormationHistories.MH_from_Z)\n\nCalculates per-model stellar mass coefficients coeffs from the fitting parameters of StarFormationHistories.fit_templates_logamr and StarFormationHistories.hmc_sample_logamr. The variables returned by these functions is of length length(unique(logAge))+3. The first length(logAge) entries are stellar mass coefficients, one per unique entry in logAge. The final three elements are α, β, and σ defining a metallicity evolution such that the mean metal mass fraction Z for element i of unique(logAge) is μ_Z[i] = α * (T_max - exp10(unique(logAge)[i]) / 1e9) + β. This is converted to a mean metallicity in [M/H] via the provided callable keyword argument MH_func which defaults to StarFormationHistories.MH_from_Z. The individual weights per each isochrone are then determined via Gaussian weighting with the above mean [M/H] and the provided σ in dex. The provided metallicities vector should be in [M/H]. \n\nNotes\n\nPhysically, the metal mass fraction Z must always be positive. Under the above model, this means α and β must be greater than or equal to 0. With σ being a Gaussian width, it must be positive.\nIf T_max is lower than the maximum of the logAge argument you provide, a warning will be raised which may be ignored if it does not result in any of the mean metal mass fractions Z being less than 0 for any of the provided logAge.\nAn error will be thrown if the provided age-metallicity relation variables (α, β) and T_max argument result in a mean metal mass fraction less than 0 for any time in the provided logAge vector. \n\n\n\n\n\n","category":"function"},{"location":"fitting/log_amr/#Fitting-Functions","page":"Logarithmic Age-Metallicity Relation","title":"Fitting Functions","text":"","category":"section"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"The main function we provide to fit star formation histories to Hess diagrams under the logarithmic age-metallicity relation is fit_templates_logamr. This function operates similarly to the fitting function for the linear AMR model, fit_templates_mdf. ","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"StarFormationHistories.fit_templates_logamr","category":"page"},{"location":"fitting/log_amr/#StarFormationHistories.fit_templates_logamr","page":"Logarithmic Age-Metallicity Relation","title":"StarFormationHistories.fit_templates_logamr","text":"result = fit_templates_logamr(models::AbstractVector{<:AbstractMatrix{S}},\n data::AbstractMatrix{<:Number},\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number},\n T_max::Number\n [, σ::Number];\n x0 = vcat(construct_x0_mdf(logAge, convert(S,13.7)),\n [1e-4, 5e-5, 0.2]),\n MH_func = StarFormationHistories.MH_from_Z,\n MH_deriv_Z = StarFormationHistories.dMH_dZ,\n kws...) where {S <: Number}\nresult = fit_templates_logamr(models::AbstractMatrix{S},\n data::AbstractVector{<:Number},\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number}\n T_max::Number\n [, σ::Number];\n x0 = vcat(construct_x0_mdf(logAge, convert(S,13.7)),\n [1e-4, 5e-5, 0.2]),\n MH_func = StarFormationHistories.MH_from_Z,\n MH_deriv_Z = StarFormationHistories.dMH_dZ,\n kws...) where {S <: Number}\n\nMethod that fits a linear combination of the provided Hess diagrams models to the observed Hess diagram data, constrained to have a logarithmic age-metallicity relation with the mean metal mass fraction μ_Z of element i of unique(logAge) being μ_Z[i] = α * (T_max - exp10(unique(logAge)[i]) / 1e9) + β. This is converted to a mean metallicity in [M/H] via the provided callable keyword argument MH_func which defaults to MH_from_Z. α is therefore a slope in the units of inverse Gyr, and β is the mean metal mass fraction of stars born at the earliest valid lookback time, determined by keyword argument T_max which has units of Gyr. Individual weights for each isochrone template are then determined via Gaussian weighting with the above mean [M/H] and the standard deviation σ in dex, which can either be fixed or fit.\n\nThis function is designed to work best with a \"grid\" of stellar models, defined by the outer product of N unique entries in logAge and M unique entries in metallicities. See the examples for more information on usage.\n\nThe second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\nArguments\n\nmodels are the template Hess diagrams for the simple stellar populations that compose the observed Hess diagram. \ndata is the Hess diagram for the observed data. \nlogAge::AbstractVector{<:Number} is the vector containing the effective ages of the stellar populations used to create the templates in models, in units of log10(age [yr]). For example, if a population has an age of 1 Myr, its entry in logAge should be log10(10^6) = 6.0.\nmetallicities::AbstractVector{<:Number} is the vector containing the effective metallicities of the stellar populations used to create the templates in models. This should be a logarithmic abundance like [M/H] or [Fe/H].\nT_max::Number is the maximum lookback time in Gyr for which the age-metallicity relation is to be valid. In most cases this should just be the maximum of the logAge vector argument maximum(logAge) or slightly larger. By definition, this is the lookback time at which μ_Z = β. \n\nOptional Arguments\n\nIf provided, σ::Number is the fixed width of the Gaussian the defines the metallicity distribution function (MDF) at fixed logAge. If this argument is omitted, σ will be a free parameter in the fit. \n\nKeyword Arguments\n\nx0 is the vector of initial guesses for the stellar mass coefficients per unique entry in logAge, plus the variables that define the metallicity evolution model. You should basically always be calculating and passing this keyword argument. We provide construct_x0_mdf to prepare the first part of x0 assuming constant star formation rate, which is typically a good initial guess. You then have to concatenate that result with an initial guess for the metallicity evolution parameters. For example, x0=vcat(construct_x0_mdf(logAge, 13.7; normalize_value=1e4), [1e-4, 5e-5, 0.2]), where logAge is a valid argument for this function (see above), and the initial guesses on the parameters are [α, β, σ] = [1e-4, 5e-5, 0.2]. If you provide σ as an optional argument, then you should not include an entry for it in x0.\nMH_func is a callable that takes a metal mass fraction Z and returns the logarithmic abundance [M/H]; by default uses MH_from_Z.\nMH_deriv_Z is a callable that takes a metal mass fraction Zj and returns the derivative of MH_func with respect to the metal mass fraction Z evaluated at Zj. For the default value of MH_func, dMH_dZ provides the correct derivative. You only need to change this if you use an alternate MH_func.\nOther kws... are passed to Optim.options to set things like convergence criteria for the optimization.\n\n\n\n\n\n","category":"function"},{"location":"fitting/log_amr/#Sampling-Functions","page":"Logarithmic Age-Metallicity Relation","title":"Sampling Functions","text":"","category":"section"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"StarFormationHistories.hmc_sample_logamr","category":"page"},{"location":"fitting/log_amr/#StarFormationHistories.hmc_sample_logamr","page":"Logarithmic Age-Metallicity Relation","title":"StarFormationHistories.hmc_sample_logamr","text":"Not yet implemented \n\n\n\n\n\n","category":"function"},{"location":"fitting/log_amr/#Fixed-Logarithmic-Age-Metallicity-Relation","page":"Logarithmic Age-Metallicity Relation","title":"Fixed Logarithmic Age-Metallicity Relation","text":"","category":"section"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"We support fitting only the star formation parameters by adopting fixed values for alpha, beta, and sigma through the fixed_log_amr method.","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"StarFormationHistories.fixed_log_amr","category":"page"},{"location":"fitting/log_amr/#StarFormationHistories.fixed_log_amr","page":"Logarithmic Age-Metallicity Relation","title":"StarFormationHistories.fixed_log_amr","text":"fixed_log_amr(models,\n data,\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number},\n T_max::Number,\n α::Number,\n β::Number,\n σ::Number;\n MH_func = StarFormationHistories.MH_from_Z,\n kws...)\n\nGiven a fully specified logarithmic age-metallicity relation with parameters (α, β, σ), fits maximum likelihood and maximum a posteriori star formation parameters. MH_func is a callable that returns a logarithmic metallicity [M/H] for a metal mass fraction argument and defaults to MH_from_Z. T_max is the lookback time in Gyr at which the mean metal mass fraction is \beta. See fixed_amr for info on format of returned result.\n\n\n\n\n\nfixed_log_amr(models,\n data,\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number},\n T_max::Number,\n constraint1,\n constraint2,\n σ::Number;\n Z_func = StarFormationHistories.Z_from_MH,\n kws...)\n\nCall signature that takes two fixed points low_constraint and high_constraint that define points that must lie on the logarithmic age-metallicity relation and calculates the slope paramters α and β for you. Format is ([M/H], age [Gyr]), i.e. constraint1 = (-2.5, 13.7) for the first point at [M/H] = -2.5 at 13.7 Gyr lookback time and constraint2 = (-0.8, 0.0) for the second point at [M/H] = -0.8 at present-day (0.0 Gyr lookback time). The AMR is normalized so that the mean metal mass fraction at a lookback time in Gyr of T_max is Z = β. Metallicities in [M/H] are converted to metal mass fractions Z via the provided callable keyword argument Z_func which defaults to Z_from_MH. See fixed_amr for info on format of returned result.\n\n\n\n\n\n","category":"function"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"We provide the calculate_αβ_logamr convenience function to calculate the slope alpha and intercept beta from two points on the age-metallicity relation.","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"StarFormationHistories.calculate_αβ_logamr","category":"page"},{"location":"fitting/log_amr/#StarFormationHistories.calculate_αβ_logamr","page":"Logarithmic Age-Metallicity Relation","title":"StarFormationHistories.calculate_αβ_logamr","text":"(α, β) = calculate_αβ_logamr(low_constraint,\n high_constraint,\n T_max,\n Z_func=Z_from_MH)\n\nCalculates linear Z (log [M/H]) age-metallicity relation (AMR) slope α and intercept β from two points on the line with form ([M/H], age [Gyr]) given by the first two arguments. The AMR is normalized so that the mean metal mass fraction at a lookback time in Gyr of T_max is Z = β. More info given in fixed_log_amr.\n\nExamples\n\njulia> calculate_αβ_logamr((-2.5, 13.7), (-1.0, 0.0), 13.7) isa NTuple{2,Float64}\ntrue\n\n\n\n\n\n","category":"function"},{"location":"fitting/log_amr/#Implementation","page":"Logarithmic Age-Metallicity Relation","title":"Implementation","text":"","category":"section"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"As the only part of the model that differs from the linear AMR case is the mean age-metallicity relation, most of the derivation for the linear AMR case is still valid here. In particular, only the partial derivatives of the relative weights A_jk equiv textexp left( -frac12 sigma^2 left( textMtextH_k - mu_j right)^2right) with respect to the fitting parameters alpha and beta need to be recalculated under the new model. The partial derivative with respect to sigma is the same, as the mean metallicity in time bin j, denoted mu_j, does not depend on sigma.","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"beginaligned\nZ_j equiv langle Z left(t_jright) rangle = alpha left( T_textmax - t_j right) + beta \n \nmu_j equiv langle textMtextH rangle left(t_jright) = textlog left( fraclangle Zleft(t_jright) rangleX_j right) - textlog left( fracZ_odotX_odot right) \n= textlog left frac alpha left( T_textmax - t_j right) + betaX_j right - textlog left( fracZ_odotX_odot right)\nendaligned","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"We can use the chain rule to write","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"beginaligned\nA_jk equiv textexp left( - frac12 left( frac textMtextH_k - mu_jsigma right)^2 right) \nfracpartial A_jkpartial beta = fracpartial A_jkpartial mu_j fracpartial mu_jpartial beta \nfracpartial A_jkpartial alpha = fracpartial A_jkpartial mu_j fracpartial mu_jpartial alpha \nendaligned ","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"By definition the hydrogen, helium, and metal mass fractions, X, Y, and Z respectively, must sum to 1. For PARSEC models, Y is a function of Z (see Y_from_Z) such that X_j is a function of Z_j and therefore also alpha and beta. Under the PARSEC model, Y = Y_p + gamma Z, we have X(Z) = 1 - left( Y_p + gamma Z right) - Z such that we can rewrite the mu_j as ","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"mu_j = textlog left( fracZ_j1 - left( Y_p + gamma Z_j right) - Z_j right) - textlog left( fracZ_odotX_odot right)","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"and we can further expand the partial derivatives of mu_j as","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"beginaligned\nfracpartial mu_jpartial beta = fracpartial mu_jpartial Z_j fracpartial Z_jpartial beta \nfracpartial mu_jpartial alpha = fracpartial mu_jpartial Z_j fracpartial Z_jpartial alpha \nendaligned ","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"such that the model-dependent portion left( fracpartial Z_jpartial beta right) can be separated from what is essentially a calibration defining how [M/H] is calculated from Z left( fracpartial mu_jpartial Z_j right). Given our model Z_j = alpha left( T_textmax - t_j right) + beta and the PARSEC calibration for conversion between Z_j and mu_j (i.e., [M/H]), we have","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"beginaligned\nfracpartial mu_jpartial Z_j = fracY_p - 1textln10 Z_j left( Y_p + Z_j + gamma Z_j - 1 right) \nfracpartial Z_jpartial beta = fracpartial leftalpha left( T_textmax - t_j right) + beta rightpartial beta = 1 \nfracpartial Z_jpartial alpha = fracpartial left alpha left( T_textmax - t_j right) + beta rightpartial alpha = T_textmax - t_j \nendaligned","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"[M/H] as a function of Z for the PARSEC calibration is available as MH_from_Z and the partial derivative fracpartial mu_jpartial Z_j is available as dMH_dZ.","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"Which gives us final results","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"beginaligned\nfracpartial A_jkpartial mu_j = fracA_jk left( textMtextH_k - mu_j right)sigma^2 \nfracpartial mu_jpartial beta = fracpartial mu_jpartial Z_j fracpartial Z_jpartial beta = left( fracY_p - 1textln(10) Z_j left( Y_p + Z_j + gamma Z_j - 1 right) right) left( 1 right) \nfracpartial mu_jpartial alpha = fracpartial mu_jpartial Z_j fracpartial Z_jpartial alpha = left( fracY_p - 1textln(10) Z_j left( Y_p + Z_j + gamma Z_j - 1 right) right) left( T_textmax - t_j right) \n\n fracpartial mu_jpartial beta = frac1left( t_j alpha + beta right) textln(10) \n fracpartial mu_jpartial alpha = fractleft( t_j alpha + beta right) textln(10) = t fracpartial mu_jpartial beta \nendaligned","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"such that","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"beginaligned\nfracpartial A_jkpartial beta = fracpartial A_jkpartial mu_j fracpartial mu_jpartial beta = fracpartial A_jkpartial mu_j fracpartial mu_jpartial Z_j fracpartial Z_jpartial beta \n= left( fracA_jk left( textMtextH_k - mu_j right)sigma^2 right) left( fracY_p - 1textln10 Z_j left( Y_p + Z_j + gamma Z_j - 1 right) right) \nfracpartial A_jkpartial alpha = fracpartial A_jkpartial mu_j fracpartial mu_jpartial alpha = fracpartial A_jkpartial mu_j fracpartial mu_jpartial Z_j fracpartial Z_jpartial alpha \n= left( fracA_jk left( textMtextH_k - mu_j right)sigma^2 right) left( fracY_p - 1textln10 Z_j left( Y_p + Z_j + gamma Z_j - 1 right) right) left( T_textmax - t_j right) \n= left( T_textmax - t_j right) fracpartial A_jkpartial beta \n\n fracpartial A_jkpartial beta = fracpartial A_jkpartial mu_j fracpartial mu_jpartial beta = left( fracA_jk left( textMtextH_k - mu_j right)sigma^2 right) left( frac1left( t_j alpha + beta right) textln(10) right) \n = fracA_jk left( textMtextH_k - mu_j right)textln(10) sigma^2 left( t_j alpha + beta right) \n fracpartial A_jkpartial alpha = fracpartial A_jkpartial mu_j fracpartial mu_jpartial alpha = t fracpartial A_jkpartial beta\nendaligned","category":"page"},{"location":"binaries/#binaries","page":"Binary Systems","title":"Binary Systems","text":"","category":"section"},{"location":"binaries/","page":"Binary Systems","title":"Binary Systems","text":"Here we review the API for including binary systems in our population models. Our Monte Carlo sampling methods supports all three models, while our smooth template modelling procedure only supports NoBinaries and RandomBinaryPairs. A comparison between a Monte Carlo population and a smooth template model for a RandomBinaryPairs model with binary fraction of 70% is shown below. The redward shift of the lower main sequence typical of populations with high binary fractions is clearly evident and robustly modelled.","category":"page"},{"location":"binaries/","page":"Binary Systems","title":"Binary Systems","text":"mv(\"../../examples/templates/template_compare_binaries.svg\", \"template_compare_binaries.svg\") # hide\nmv(\"../../examples/templates/sigma_distribution_binaries.svg\", \"sigma_distribution_binaries.svg\") # hide\nnothing # hide","category":"page"},{"location":"binaries/","page":"Binary Systems","title":"Binary Systems","text":"(Image: Comparison of CMD-sampled population with smooth Hess diagram template, with binaries.)","category":"page"},{"location":"binaries/#Types","page":"Binary Systems","title":"Types","text":"","category":"section"},{"location":"binaries/","page":"Binary Systems","title":"Binary Systems","text":"StarFormationHistories.AbstractBinaryModel\nStarFormationHistories.NoBinaries\nStarFormationHistories.RandomBinaryPairs\nStarFormationHistories.BinaryMassRatio","category":"page"},{"location":"binaries/#StarFormationHistories.AbstractBinaryModel","page":"Binary Systems","title":"StarFormationHistories.AbstractBinaryModel","text":"StarFormationHistories.AbstractBinaryModel is the abstract supertype for all types that are used to model multi-star systems in the package. All concrete subtypes should implement the following methods to support all features:\n\nStarFormationHistories.sample_system\nStarFormationHistories.binary_system_fraction\nStarFormationHistories.binary_mass_fraction\nBase.length, which should return an integer indicating the number of stars per system that can be sampled by the model; this is equivalent to the length of the mass vector returned by sample_system.\n\nNote that all quantities relating to binary populations (e.g., binary_system_fraction) should be defined for the population at birth. As the stars in a binary system evolve, the more massive star may die before the system is observed at present-day. Of course, the stars in single-star systems can also die. If the rate at which binary systems become single-star systems is not equal to the rate at which single-star systems die, then there can be net transfer between these populations over time. Therefore the observed, present-day binary system fraction of an evolved population is not necessarily equal to the fraction at birth, which is the more fundamental quantity.\n\n\n\n\n\n","category":"type"},{"location":"binaries/#StarFormationHistories.NoBinaries","page":"Binary Systems","title":"StarFormationHistories.NoBinaries","text":"NoBinaries()\n\nThe NoBinaries type indicates that no binaries of any kind should be created. \n\n\n\n\n\n","category":"type"},{"location":"binaries/#StarFormationHistories.RandomBinaryPairs","page":"Binary Systems","title":"StarFormationHistories.RandomBinaryPairs","text":"RandomBinaryPairs(fraction::Real)\n\nThe RandomBinaryPairs type takes one argument 0 <= fraction::Real <= 1 that denotes the number fraction of stellar systems that are binaries (e.g., 0.3 for 30% binary fraction) and will sample binaries as random pairs of two stars drawn from the same single-star IMF. This model will ONLY generate up to one additional star – it will not generate any 3+ star systems. This model typically incurs a 10–20% speed penalty relative to NoBinaries. \n\n\n\n\n\n","category":"type"},{"location":"binaries/#StarFormationHistories.BinaryMassRatio","page":"Binary Systems","title":"StarFormationHistories.BinaryMassRatio","text":"BinaryMassRatio(fraction::Real,\n qdist::Distributions.ContinuousUnivariateDistribution =\n Distributions.Uniform(0.1, 1.0))\n\nThe BinaryMassRatio type takes two arguments; the number fraction of stellar systems that are binaries 0 <= fraction::Real <= 1 and a continuous univariate distribution qdist from which to sample binary mass ratios, defined as the ratio of the secondary mass to the primary mass: q = textM_S textM_P. The provided qdist must have the proper support of (minimum(qdist) >= 0) & (maximum(qdist) <= 1). Users may find the Distributions.truncated method useful for enforcing this support on more general distributions. The default qdist is a uniform distribution from 0.1 to 1, which appears to give reasonably good agreement to observations (see, e.g., Goodwin 2013).\n\n\n\n\n\n","category":"type"},{"location":"binaries/#Methods","page":"Binary Systems","title":"Methods","text":"","category":"section"},{"location":"binaries/","page":"Binary Systems","title":"Binary Systems","text":"StarFormationHistories.binary_system_fraction\nStarFormationHistories.binary_number_fraction\nStarFormationHistories.binary_mass_fraction\nStarFormationHistories.sample_system","category":"page"},{"location":"binaries/#StarFormationHistories.binary_system_fraction","page":"Binary Systems","title":"StarFormationHistories.binary_system_fraction","text":"binary_system_fraction(model::T) where T <: AbstractBinaryModel\n\nReturns the number fraction of stellar systems that are binaries for the given concrete subtype T <: AbstractBinaryModel. Has a default implementation of binary_system_fraction(model::AbstractBinaryModel) = model.fraction.\n\n\n\n\n\n","category":"function"},{"location":"binaries/#StarFormationHistories.binary_number_fraction","page":"Binary Systems","title":"StarFormationHistories.binary_number_fraction","text":"binary_number_fraction(model::T) where T <: AbstractBinaryModel\n\nReturns the number fraction of stars that in binary pairs for the given concrete subtype T <: AbstractBinaryModel. Has a default implementation of 2b / (1+b), where b is the result of StarFormationHistories.binary_system_fraction.\n\n\n\n\n\n","category":"function"},{"location":"binaries/#StarFormationHistories.binary_mass_fraction","page":"Binary Systems","title":"StarFormationHistories.binary_mass_fraction","text":"binary_mass_fraction(model::T, imf) where T <: AbstractBinaryModel\n\nReturns the fraction of stellar mass in binary systems for the given concrete subtype T <: AbstractBinaryModel and initial mass function imf. imf must be a properly normalized probability distribution such that the number fraction of stars/systems between mass m1 and m2 is given by the integral of dispatch_imf(imf, x) from m1 to m2. \n\n\n\n\n\nbinary_mass_fraction(m::RandomBinaryPairs, imf)\n\nThe RandomBinaryPairs model uses a single-star imf. If a system is chosen to be a binary pair, two stars are drawn from the single-star imf and the more massive star is made the primary. Given this model, it can be shown that the expectation value for the mass of a binary system is twice the expectation value for single star systems:\n\n2int_textM_textmin^textM_textmax textM fracdtextN left( textM right)dtextM dtextM = int_textM_textmin^textM_textmax int_textM_textmin^textM_textmax left( textM_P + textM_S right) fracdtextN left( textM_S right)dtextM fracdtextN left( textM_P right)dtextM dtextM_S dtextM_P\n\nfor primary mass textM_P, secondary mass textM_S, and single-star IMF dtextN dtextM. As such, the fraction of total stellar mass in binaries is equal to the number fraction of all stars in binary pairs, which is given by StarFormationHistories.binary_number_fraction.\n\n\n\n\n\nbinary_mass_fraction(m::BinaryMassRatio, imf)\n\nThis binary model requires an imf that is defined by stellar system mass. If a system with a randomly sampled mass M is is a binary, the primary and secondary mass are determined based on a binary mass ratio q sampled from a user-defined distribution. By definition, the expectation value for the total mass of a binary system is equal to the expectation value for single-star systems. In this case the binary mass fraction is equal the binary system number fraction as given by StarFormationHistories.binary_system_fraction.\n\n\n\n\n\n","category":"function"},{"location":"binaries/#StarFormationHistories.sample_system","page":"Binary Systems","title":"StarFormationHistories.sample_system","text":"masses = sample_system(imf, rng::AbstractRNG, binarymodel::StarFormationHistories.AbstractBinaryModel)\n\nSimulates the effects of non-interacting, unresolved stellar companions on stellar photometry. Implementation depends on the choice of binarymodel.\n\nArguments\n\nimf: an object implementing rand(imf) to draw a random mass for a single star or a stellar system (depends on choice of binarymodel)\nrng::AbstractRNG: the random number generator to use when sampling stars\nbinarymodel::StarFormationHistories.AbstractBinaryModel: an instance of a binary model that determines which implementation will be used; currently provided options are NoBinaries, RandomBinaryPairs, and BinaryMassRatio.\n\nReturns\n\nmasses::SVector{N,eltype(imf)}: the masses of the individual stars sampled in the system in descending order where N is the maximum number of stars that can be sampled by the provided binarymodel as given by Base.length(binarymodel). \n\n\n\n\n\n","category":"function"},{"location":"binaries/#Notes","page":"Binary Systems","title":"Notes","text":"","category":"section"},{"location":"binaries/","page":"Binary Systems","title":"Binary Systems","text":"The trickiest part of including binaries in the smooth template modelling procedure is deriving the IMF weights. Let M_p be the sorted list of initial masses for primary stars and M_s be the sorted list of initial masses for secondary stars. Conceptually, the IMF weight for a binary system with primary mass M_pi and secondary mass M_sj should compute the number fraction of binary systems born with primary masses between M_pi and M_pi+1 and secondary masses between M_sj and M_sj+1 per unit solar mass formed. ","category":"page"},{"location":"binaries/","page":"Binary Systems","title":"Binary Systems","text":"In the case of the RandomBinaryPairs model, the IMF weights are calculated as follows, with dN(M)dM being the IMF for single stars, langle M rangle being the mean mass of single stars over the full range of possible initial masses, and the integral in the denominator being over the range of initial masses in the isochrone. The integral in the denominator accounts for losses due to stellar evolution.","category":"page"},{"location":"binaries/","page":"Binary Systems","title":"Binary Systems","text":" w_textIMFij = fracint_M_pi^M_pi+1 int_M_sj^M_sj+1 fracdN(M_p)dM fracdN(M_s)dM dM_p dM_slangle M rangle int_M_textmin^M_textmax fracdN(M)dM dM","category":"page"},{"location":"helpers/#helpers","page":"Helper Functions","title":"Helper Functions","text":"","category":"section"},{"location":"helpers/#Distances-and-Sizes","page":"Helper Functions","title":"Distances and Sizes","text":"","category":"section"},{"location":"helpers/","page":"Helper Functions","title":"Helper Functions","text":"StarFormationHistories.arcsec_to_pc\nStarFormationHistories.pc_to_arcsec\nStarFormationHistories.distance_modulus\nStarFormationHistories.distance_modulus_to_distance","category":"page"},{"location":"helpers/#StarFormationHistories.arcsec_to_pc","page":"Helper Functions","title":"StarFormationHistories.arcsec_to_pc","text":"arcsec_to_pc(arcsec, dist_mod)\n\nConverts on-sky angle in arcseconds to physical separation based on distance modulus under the small-angle approximation.\n\nr 10^μ5 + 1 times textatan(θ3600)\n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.pc_to_arcsec","page":"Helper Functions","title":"StarFormationHistories.pc_to_arcsec","text":"pc_to_arcsec(pc, dist_mod)\n\nInverse of arcsec_to_pc.\n\nθ texttanleft( r 10^μ5 + 1 right) times 3600\n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.distance_modulus","page":"Helper Functions","title":"StarFormationHistories.distance_modulus","text":"distance_modulus(distance)\n\nFinds distance modulus for distance in parsecs.\n\nμ = 5 times log_10(d) - 5\n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.distance_modulus_to_distance","page":"Helper Functions","title":"StarFormationHistories.distance_modulus_to_distance","text":"distance_modulus_to_distance(dist_mod)\n\nConverts distance modulus to distance in parsecs.\n\nd = 10^μ5 + 1\n\n\n\n\n\n","category":"function"},{"location":"helpers/#Magnitudes-and-Luminosities","page":"Helper Functions","title":"Magnitudes and Luminosities","text":"","category":"section"},{"location":"helpers/","page":"Helper Functions","title":"Helper Functions","text":"StarFormationHistories.mag2flux\nStarFormationHistories.flux2mag\nStarFormationHistories.magerr\nStarFormationHistories.fluxerr\nStarFormationHistories.snr_magerr\nStarFormationHistories.magerr_snr","category":"page"},{"location":"helpers/#StarFormationHistories.mag2flux","page":"Helper Functions","title":"StarFormationHistories.mag2flux","text":"mag2flux(m, zpt=0)\n\nConvert a magnitude m to a flux assuming a photometric zeropoint of zpt, defined as the magnitude of an object that produces one count (or data number, DN) per second.\n\njulia> mag2flux(15.0, 25.0) ≈ exp10(4 * (25.0 - 15.0) / 10)\ntrue\n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.flux2mag","page":"Helper Functions","title":"StarFormationHistories.flux2mag","text":"flux2mag(f, zpt=0)\n\nConvert a flux f to a magnitude assuming a photometric zeropoint of zpt, defined as the magnitude of an object that produces one count (or data number, DN) per second.\n\njulia> flux2mag(10000.0, 25.0) ≈ 25.0 - 5 * log10(10000.0) / 2\ntrue\n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.magerr","page":"Helper Functions","title":"StarFormationHistories.magerr","text":"magerr(f, σf)\n\nReturns an error in magnitudes given a flux and a flux uncertainty.\n\njulia> magerr(100.0, 1.0) ≈ 2.5 / log(10) * (1.0 / 100.0)\ntrue\n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.fluxerr","page":"Helper Functions","title":"StarFormationHistories.fluxerr","text":"fluxerr(f, σm)\n\nReturns an error in flux given a flux and a magnitude uncertainty.\n\njulia> fluxerr(100.0, 0.01) ≈ (0.01 * 100.0) / 2.5 * log(10)\ntrue\n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.snr_magerr","page":"Helper Functions","title":"StarFormationHistories.snr_magerr","text":"snr_magerr(σm)\n\nReturns a signal-to-noise ratio (fσf) given an uncertainty in magnitudes.\n\njulia> snr_magerr(0.01) ≈ 2.5 / log(10) / 0.01\ntrue\n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.magerr_snr","page":"Helper Functions","title":"StarFormationHistories.magerr_snr","text":"magerr_snr(snr)\n\nReturns a magnitude uncertainty given a signal-to-noise ratio (fσf).\n\njulia> magerr_snr(100.0) ≈ 2.5 / log(10) / 100.0\ntrue\n\n\n\n\n\n","category":"function"},{"location":"helpers/#metallicity_helpers","page":"Helper Functions","title":"Metallicities","text":"","category":"section"},{"location":"helpers/","page":"Helper Functions","title":"Helper Functions","text":"StarFormationHistories.Y_from_Z\nStarFormationHistories.X_from_Z\nStarFormationHistories.MH_from_Z\nStarFormationHistories.dMH_dZ\nStarFormationHistories.Z_from_MH\nStarFormationHistories.mdf_amr","category":"page"},{"location":"helpers/#StarFormationHistories.Y_from_Z","page":"Helper Functions","title":"StarFormationHistories.Y_from_Z","text":"Y_from_Z(Z, Y_p=0.2485, γ=1.78)\n\nCalculates the helium mass fraction (Y) for a star given its metal mass fraction (Z) using the approximation Y = Y_p + γ * Z, with Y_p being the primordial helium abundance Y_p=0.2485 as assumed for PARSEC isochrones and γ=1.78 matching the input scaling for PARSEC as well. \n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.X_from_Z","page":"Helper Functions","title":"StarFormationHistories.X_from_Z","text":"X_from_Z(Z[, Yp, γ])\n\nCalculates the hydrogen mass fraction (X) for a star given its metal mass fraction (Z) via X = 1 - (Z + Y), with the helium mass fraction Y approximated via StarFormationHistories.Y_from_Z. You may also provide the primordial helium abundance Y_p and γ such that Y = Y_p + γ * Z; these are passed through to X_from_Z. \n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.MH_from_Z","page":"Helper Functions","title":"StarFormationHistories.MH_from_Z","text":"MH_from_Z(Z, solZ=0.01524; Y_p = 0.2485, γ = 1.78)\n\nCalculates [M/H] = log(Z/X) - log(Z/X)⊙. Given the provided solar metal mass fraction solZ, it calculates the hydrogen mass fraction X for both the Sun and the provided Z with StarFormationHistories.X_from_Z. You may also provide the primordial helium abundance Y_p and γ such that Y = Y_p + γ * Z; these are passed through to X_from_Z. \n\nThe present-day solar Z is measured to be 0.01524 (Caffau et al. 2011), but for PARSEC isochrones an [M/H] of 0 corresponds to Z=0.01471. This is because of a difference between the Sun's initial and present helium content caused by diffusion. If you want to reproduce PARSEC's scaling, you should set solZ=0.01471.\n\nThis function is an approximation and may not be suitable for precision calculations.\n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.dMH_dZ","page":"Helper Functions","title":"StarFormationHistories.dMH_dZ","text":"dMH_dZ(Z, solZ=0.01524; Y_p = 0.2485, γ = 1.78)\n\nPartial derivative of MH_from_Z with respect to the input metal mass fraction Z. Used for some optimizations. \n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.Z_from_MH","page":"Helper Functions","title":"StarFormationHistories.Z_from_MH","text":"Z_from_MH(MH, solZ=0.01524; Y_p = 0.2485, γ = 1.78)\n\nCalculates metal mass fraction Z assuming\n\nthe PARSEC relation for the helium mass fraction Y = Y_p + γ * Z with primordial helium abundance Y_p = 0.2485, and γ = 1.78, and\nthe solar metal mass fraction solZ = 0.01524.\n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.mdf_amr","page":"Helper Functions","title":"StarFormationHistories.mdf_amr","text":"(unique_MH, mass_mdf) =\nmdf_amr(coeffs::AbstractVector{<:Number},\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number})\n\nCalculates the mass-weighted metallicity distribution function given a set of stellar mass coefficients coeffs for stellar populations with logarithmic ages logAge=log10(age [yr]) and metallicities given by metallicities. This is calculated as\n\nP_j = frac sum_k r_jk textM textH_ksum_jk r_jk textM textH_k\n\nwhere r_jk are the elements of coeffs where j indexes over unique entries in logAge and k indexes over unique entries in metallicities. This is the same nomenclature used in the the documentation on constrained metallicity evolutions. The return values are sorted so that unique_MH is in increasing order.\n\nExamples\n\njulia> mdf_amr([1.0, 2.0, 1.0], [10, 10, 10], [-2, -1.5, -1])\n([-2.0, -1.5, -1.0], [0.25, 0.5, 0.25])\n\n\n\n\n\n","category":"function"},{"location":"helpers/#phot_helpers","page":"Helper Functions","title":"Photometric Error and Completeness Models","text":"","category":"section"},{"location":"helpers/","page":"Helper Functions","title":"Helper Functions","text":"StarFormationHistories.Martin2016_complete\nStarFormationHistories.exp_photerr\nStarFormationHistories.process_ASTs","category":"page"},{"location":"helpers/#StarFormationHistories.Martin2016_complete","page":"Helper Functions","title":"StarFormationHistories.Martin2016_complete","text":"η(m) = Martin2016_complete(m, A, m50, ρ)\n\nCompleteness model of Martin et al. 2016 implemented as their Equation 7:\n\neta(m) = fracA1 + textexp left( fracm - m_50rho right)\n\nm is the magnitude of interest, A is the maximum completeness, m50 is the magnitude at which the data are 50% complete, and ρ is an effective slope modifier.\n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.exp_photerr","page":"Helper Functions","title":"StarFormationHistories.exp_photerr","text":"exp_photerr(m, a, b, c, d)\n\nExponential model for photometric errors of the form\n\nsigma(m) = a^b times left( m-c right) + d\n\nReported values for some HST data were a=1.05, b=10.0, c=32.0, d=0.01. \n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.process_ASTs","page":"Helper Functions","title":"StarFormationHistories.process_ASTs","text":"process_ASTs(ASTs::Union{DataFrames.DataFrame,\n TypedTables.Table},\n inmag::Symbol,\n outmag::Symbol,\n bins::AbstractVector{<:Real},\n selectfunc;\n statistic=StatsBase.median)\n\nProcesses a table of artificial stars to calculate photometric completeness, bias, and error across the provided bins. This method has no default implementation and is implemented in package extensions that rely on either DataFrames.jl or TypedTables.jl being loaded into your Julia session to load the relevant method. This method therefore requires Julia 1.9 or greater to use.\n\nArguments\n\nASTs is the table of artificial stars to be analyzed.\ninmag is the column name in symbol format (e.g., :F606Wi) that corresponds to the intrinsic (input) magnitudes of the artificial stars.\noutmag is the column name in symbol format (e.g., :F606Wo) that corresponds to the measured (output) magnitude of the artificial stars.\nbins give the bin edges to be used when computing the binned statistics.\nselectfunc is a method that takes a single row from ASTs, corresponding to a single artificial star, and returns a boolean that is true if the star is considered successfully measured.\n\nKeyword Arguments\n\nstatistic is the method that will be used to determine the bias and error, i.e., bias = statistic(out .- in) and error = statistic(abs.(out .- in)). By default we use StatsBase.median, but you could instead use a simple or sigma-clipped mean if so desired.\n\nReturns\n\nThis method returns a result of type NTuple{4,Vector{Float64}}. Each vector is of length length(bins)-1. result contains the following elements, each of which are computed over the provided bins considering only artificial stars for which selectfunc returned true:\n\nresult[1] contains the mean input magnitude of the stars in each bin.\nresult[2] contains the completeness value measured for each bin, defined as the fraction of input stars in each bin for which selectfunc returned true.\nresult[3] contains the photometric bias measured for each bin, defined as statistic(out .- in), where out are the measured (output) magnitudes and in are the intrinsic (input) magnitudes.\nresult[4] contains the photometric error measured for each bin, defined as statistic(abs.(out .- in)), with out and in defined as above.\n\nExamples\n\nLet\n\nF606Wi be a vector containing the input magnitudes of your artificial stars\nF606Wo be a vector containing the measured magnitudes of the artificial stars, where a value of 99.999 indicates a non-detection.\nflag be a vector of booleans that indicates whether the artificial star passed additional quality cuts (star-galaxy separation, etc.)\n\nYou could call this method as\n\nimport TypedTables: Table\nprocess_ASTs(Table(input=F606Wi, output=F606Wo, good=flag),\n :input, :output, minimum(F606Wi):0.1:maximum(F606Wi),\n x -> (x.good==true) & (x.output != 99.999))\n\nSee also the tests in test/utilities/process_ASTs_test.jl.\n\n\n\n\n\n","category":"function"},{"location":"fitting/kernels/#kernels","page":"Kernels","title":"Kernels","text":"","category":"section"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"One of the most important parts of our method is modelling the 2-D probability distribution of observing a star with particular intrinsic magnitudes in the Hess diagram space. We refer to these 2-D probability distributions as \"kernels\" throughout, as our process is conceptually similar to constructing a kernel density estimate. This document describes how these kernels are constructed and what assumptions are made to do so.","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"Our implementation is underpinned by the assumption that all kernels can be described the family of 2-D Gaussian probability distributions. To derive these 2-D distributions, we begin with 1-D distributions for each photometric filter used in the analysis.","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"We assume random photometric errors in the observed photometric catalog can be approximated as Gaussian. This is generally a good assumption for the space-based imaging used for resolved SFHs (e.g., HST and JWST) as long as the photometric completeness is high (greater than, perhaps, 50%). At low completeness, the error distributions become skewed and would be better modelled by something like a skew-normal distribution, which we do not presently consider.","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"We additionally require that the user can construct functions for each observed photomeric filter which, given a star's input intrinsic magnitude in a single filter m_i, return reliable measures for the average single-band photometric error; sigma(m_i). Assuming that the photometric errors in each band can be modelled as independent, we can derive, for a given m_i, the probability distribution of the observed magnitude m_o. We therefore have the 1-D probability distributions P(m_om_i) for all filters.","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"In the case that imaging in three photometric filters are available and are used to construct a Hess diagram with a magnitude on the y-axis that does not appear on the x-axis (e.g., y=R and x=B-V), then there is no covariance between the x and y axes and the 2-D Gaussian kernel is fully separable. We call this the \"non-covariant\" case.","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"In order to inject the kernel into the model Hess diagram, it must be integrated across the grid that defines the discretization. The integral of the non-covariant kernel across the Hess diagram pixel grid is entirely analytic, making evaluation easy and efficient. By integrating the kernel over the grid, the numerical precision is nearly independent of the grid resolution, which is not true of Monte Carlo (MC) based template construction algorithms.","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"In the case that imaging in only two photometric filters are available and are used to construct a Hess diagram with a magnitude on the y-axis that does appear on the x-axis (e.g., y=B and x=B-V), then there is covariance between x and y axes. The initial implementation of this method neglected this covariance, and while the result was not catastrophic, it was certainly suboptimal. We now model the covariance explicitly. The covariance pattern does not permit a fully analytic integral as in the non-covariant case; instead, the inner integral (over, say, the x-axis) can be calculated analytically but the outer integral cannot. We therefore use Gauss-Legendre quadtrature to finish the integration over the second axis.","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"The types representing these kernels are not part of our public API, but we provide an example script examples/templates/kernels_example.jl which illustrates the performance of these kernels. The output of this script is reproduced and explained below.","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"Consider a star taken from an isochrone with intrinsic magnitudes B=20, V=19, and R=18 with expected random photometric errors sigma_B=002, sigma_V=003, and sigma_R=005. Our example randomly samples a large population of possible observed magnitudes given these intrinsic properties and compares the distribution of the MC sample to our kernels.","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"We consider first the non-covariant case:","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"mv(\"../../../examples/templates/covar_0.svg\", \"figures/covar_0.svg\") # hide\nmv(\"../../../examples/templates/covar_1.svg\", \"figures/covar_1.svg\") # hide\nmv(\"../../../examples/templates/covar_m1.svg\", \"figures/covar_m1.svg\") # hide\nnothing # hide","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"(Image: noncovariant)","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"which has the expected, non-rotated morphology.","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"We next consider the covariant case with the covariance pattern y=B and x=B-V:","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"(Image: covariantm1)","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"which we can see correctly models the covariance pattern.","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"And finally, the covariant case with the covariance pattern y=V and x=B-V: (Image: covariant1)","category":"page"},{"location":"fitting/unconstrained/#High-Level-Methods-for-Unconstrained-Fitting","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"","category":"section"},{"location":"fitting/unconstrained/#Maximum-Likelihood-Optimization","page":"High-Level Methods for Unconstrained Fitting","title":"Maximum Likelihood Optimization","text":"","category":"section"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"Template construction is by far the most complicated step in the fitting procedure. Once your templates have been constructed, fitting them to an observed Hess diagram amounts to maximization of the Poisson likelihood ratio (Dolphin 2002). It is possible to construct more complicated hierarchical models including things like metallicity distribution functions; we discuss these in the next section. In this section we discuss methods for fitting where the only constraint is that star formation rates cannot be negative. We provide the StarFormationHistories.construct_x0 method to help with setting the initial guess for this optimization. ","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"StarFormationHistories.construct_x0","category":"page"},{"location":"fitting/unconstrained/#StarFormationHistories.construct_x0","page":"High-Level Methods for Unconstrained Fitting","title":"StarFormationHistories.construct_x0","text":"x0::typeof(logage) = construct_x0(logAge::AbstractVector{T},\n T_max::Number;\n normalize_value::Number=one(T)) where T <: Number\n\nGenerates a vector of initial stellar mass normalizations for input to fit_templates or hmc_sample with a total stellar mass of normalize_value such that the implied star formation rate is constant across the provided logAge vector that contains the log10(Age [yr]) of each isochrone that you are going to input as models. For the purposes of computing the constant star formation rate, the provided logAge are treated as left-bin edges, with the final right-bin edge being T_max, which has units of Gyr. For example, you might have logAge=[6.6, 6.7, 6.8] in which case a final logAge of 6.9 would give equal bin widths. In this case you would set T_max = exp10(6.9) / 1e9 ≈ 0.0079 so that the width of the final bin for the star formation rate calculation has the same log10(Age [yr]) step as the other bins.\n\nExamples\n\njulia> x0 = construct_x0(repeat([7.0,8.0,9.0],3), 10.0; normalize_value=5.0)\n9-element Vector{Float64}: ...\n\njulia> sum(x0)\n4.99... # Close to `normalize_value`.\n\n\n\n\n\n","category":"function"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"When it comes to performing the optimization, the simplest method we offer is StarFormationHistories.fit_templates_lbfgsb. This will optimize one coefficient per template; there is no overarching metallicity evolution or other constraint, besides that the stellar masses of the populations cannot be negative. This performs a maximum likelihood optimization with the bounded quasi-Newton LBFGS method as implemented in L-BFGS-B and wrapped in LBFGS.jl with analytic gradients. It is fast and converges fairly reliably, even when the initial guess is not particularly close to the maximum likelihood estimate. It provides no uncertainty estimation. It is normal for some of the coefficients to converge to zero.","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"StarFormationHistories.fit_templates_lbfgsb","category":"page"},{"location":"fitting/unconstrained/#StarFormationHistories.fit_templates_lbfgsb","page":"High-Level Methods for Unconstrained Fitting","title":"StarFormationHistories.fit_templates_lbfgsb","text":"(-logL, coeffs) = \nfit_templates_lbfgsb(models::AbstractVector{T},\n data::AbstractMatrix{<:Number};\n x0::AbstractVector{<:Number} = ones(S,length(models)),\n factr::Number=1e-12,\n pgtol::Number=1e-5,\n iprint::Integer=0,\n kws...) where {S <: Number, T <: AbstractMatrix{S}}\n\nFinds the coefficients coeffs that maximize the Poisson likelihood ratio (equations 7–10 in Dolphin 2002) for the composite Hess diagram model sum(models .* coeffs) given the provided templates models and the observed Hess diagram data using the box-constrained LBFGS method provided by LBFGSB.jl. \n\nArguments\n\nmodels::AbstractVector{AbstractMatrix{<:Number}}: the list of template Hess diagrams for the simple stellar populations (SSPs) being considered; all must have the same size.\ndata::AbstractMatrix{<:Number}: the observed Hess diagram; must match the size of the templates contained in models.\n\nKeyword Arguments\n\nx0: The vector of initial guesses for the stellar mass coefficients. You should basically always be calculating and passing this keyword argument; we provide StarFormationHistories.construct_x0 to prepare x0 assuming constant star formation rate, which is typically a good initial guess.\nfactr::Number: Keyword argument passed to LBFGSB.lbfgsb; essentially a relative tolerance for convergence based on the inter-iteration change in the objective function.\npgtol::Number: Keyword argument passed to LBFGSB.lbfgsb; essentially a relative tolerance for convergence based on the inter-iteration change in the projected gradient of the objective.\niprint::Integer: Keyword argument passed to LBFGSB.lbfgsb controlling how much information is printed to the terminal. Setting to 1 can sometimes be helpful to diagnose convergence issues. Setting to -1 will disable printing.\n\nOther kws... are passed to LBFGSB.lbfgsb.\n\nReturns\n\n-logL::Number: the minimum negative log-likelihood found by the optimizer.\ncoeffs::Vector{<:Number}: the maximum likelihood estimate for the coefficient vector. \n\nNotes\n\nIt can be helpful to normalize your models to contain realistic total stellar masses to aid convergence stability; for example, if the total stellar mass of your population is 10^7 solar masses, then you might normalize your templates to contain 10^3 solar masses. If you are using partial_cmd_smooth to construct the templates, you can specify this normalization via the normalize_value keyword. \n\n\n\n\n\nfit_templates_lbfgsb(models::AbstractMatrix{S},\n data::AbstractVector{<:Number};\n x0::AbstractVector{<:Number} = ones(S,size(models,2)),\n factr::Number=1e-12,\n pgtol::Number=1e-5,\n iprint::Integer=0,\n kws...) where S <: Number\n\nThis call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\n\n\n\n\n","category":"function"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"This method simply minimizes the negative logarithm of the Poisson likelihood ratio (Equation 10 in Dolphin 2002),","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"- textln mathscrL = sum_i m_i - n_i times left( 1 - textln left( fracn_im_i right) right)","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"where m_i is bin i of the complex model and n_i is bin i of the observed Hess diagram; this can therefore be thought of as computing the maximum likelihood estimate.","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"We also provide StarFormationHistories.fit_templates_fast, which is the fastest method we offer for deriving a maximum likelihood estimate for the type of model described above.","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"StarFormationHistories.fit_templates_fast","category":"page"},{"location":"fitting/unconstrained/#StarFormationHistories.fit_templates_fast","page":"High-Level Methods for Unconstrained Fitting","title":"StarFormationHistories.fit_templates_fast","text":"(coeffs::Vector{::eltype(x0)}, result::Optim.MultivariateOptimizationResults) =\nfit_templates_fast(models::AbstractVector{T},\n data::AbstractMatrix{<:Number};\n x0::AbstractVector{<:Number} = ones(S,length(models)),\n kws...)\n where {S <: Number, T <: AbstractMatrix{S}}\n\nFinds only the maximum likelihood estimate (MLE) for the coefficients coeffs given the provided data such that the best-fit composite Hess diagram model is sum(models .* coeffs). This is a simplification of the main fit_templates function, which will return the MLE as well as the maximum a posteriori estimate, and further supports uncertainty quantification. For additional details on arguments to this method, see the documentation for fit_templates. \n\nThis method optimizes parameters θ such that coeffs = θ.^2 – this allows for faster convergence than both the fit_templates_lbfgsb method, which does not use a variable transformation, and the logarithmic transformation used in fit_templates. However, the inverse Hessian is not useful for uncertainty estimation under this transformation. As such this method only returns the MLE for coeffs as a vector and the result object returned by Optim.optimize. While this method offers fewer features than fit_templates, this method's runtime is typically half as long (or less). As such, this method is recommended for use in performance-sensitive applications like hierarchical models or hyperparameter estimation where the additional features of fit_templates are unnecessary. In these applications, the value of the objective function at the derived MLE is typically desired as well; this can be obtained the from result::Optim.MultivariateOptimizationResults object as Optim.minimum(result). Note that this will return the negative loglikelihood, which is what is minimized in this application.\n\nNotes\n\nBy passing additional convergence keyword arguments supported by Optim.Options (see this guide), it is possible to converge to the MLE in fewer than 30 iterations with fewer than 100 calls to the likelihood and gradient methods. For example, the main convergence criterion is typically the magnitude of the gradient vector, which by default is g_abstol=1e-8, terminating the optimization when the magnitude of the gradient is less than 1e-8. We find results are typically sufficiently accurate with g_abstol=1e-3, which often uses half as many objective evaluations as the default value.\n\n\n\n\n\nfit_templates_fast(models::AbstractMatrix{S},\n data::AbstractVector{<:Number};\n x0::AbstractVector{<:Number} = ones(S,size(models,2)),\n kws...)\n where S <: Number\n\nThis call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\n\n\n\n\n","category":"function"},{"location":"fitting/unconstrained/#Posterior-Sampling:-MCMC","page":"High-Level Methods for Unconstrained Fitting","title":"Posterior Sampling: MCMC","text":"","category":"section"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"For low-dimensional problems, Markov Chain Monte Carlo (MCMC) methods can be an efficient way to sample the posterior and obtain uncertainty estimates on the fitting coefficients r_j. We provide StarFormationHistories.mcmc_sample for this purpose. Internally this uses the multi-threaded affine-invariant MCMC sampler from KissMCMC.jl to perform the sampling, which is based on the same algorithm as Python's emcee (specifically, their emcee.moves.StretchMove). There are other MCMC packages like AdvancedMH.jl that offer additional features like distributed execution. ","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"StarFormationHistories.mcmc_sample","category":"page"},{"location":"fitting/unconstrained/#StarFormationHistories.mcmc_sample","page":"High-Level Methods for Unconstrained Fitting","title":"StarFormationHistories.mcmc_sample","text":"result::MCMCChains.Chains =\nmcmc_sample(models::AbstractVector{<:AbstractMatrix{T}},\n data::AbstractMatrix{S},\n x0::Union{AbstractVector{<:AbstractVector{<:Number}}, AbstractMatrix{<:Number}},\n nwalkers::Integer,\n nsteps::Integer;\n nburnin::Integer=0,\n nthin::Integer=1,\n a_scale::Number=2.0,\n use_progress_meter::Bool=true)\nmcmc_sample(models::AbstractMatrix{<:Number},\n data::AbstractVector{<:Number},\n args...; kws...)\n\nSamples the posterior of the coefficients coeffs such that the full model of the observational data is sum(models .* coeffs). Uses the Poisson likelihood ratio as defined by equations 7–10 of Dolphin 2002. Sampling is done using the affine-invariant MCMC sampler implemented in KissMCMC.jl, which is analogous to Python's emcee.moves.StretchMove. This method will automatically parallelize over threads. If you need distributed execution, you may want to look into AdvancedMH.jl.\n\nThe second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\nArguments\n\nmodels::AbstractVector{<:AbstractMatrix{<:Number}} is a vector of equal-sized matrices that represent the template Hess diagrams for the simple stellar populations that compose the observed Hess diagram.\ndata::AbstractMatrix{<:Number} is the Hess diagram for the observed data.\nx0::Union{AbstractVector{<:AbstractVector{<:Number}}, AbstractMatrix{<:Number}} are the initial positions for the MCMC walkers. If providing a vector of vectors, it must be a vector of length nwalkers with each internal vector having length equal to length(models). You can alternatively provide a matrix of size (nwalkers, length(models)) or (length(models), nwalkers).\nnwalkers::Integer is the number of unique walkers or chains to use.\nnsteps::Integer is the number of steps evolve the walkers for.\n\nKeyword Arguments\n\nnburnin::Integer=0 is the number of steps to discard from the start of each chain.\nnthin::Integer=1 is the factor by which to thin the chain; walker positions will only be saved every nthin steps.\na_scale::Number=2.0 is the scale parameter for the stretch move; probably shouldn't need to be changed.\nuse_progress_Meter::Bool=true indicates whether or not to show a progress bar during the MCMC procedure.\n\nReturns\n\nresult is a MCMCChains.Chains instance which enables easy calculation of diagnostic and summary statistics. This type can be indexed and used like a 3-D array of samples with shape (nsteps, length(models), nwalkers).\n\nNotes\n\nWhen displaying result to the terminal it will display summary statistics (MCMCChains.summarystats) and quantiles (MCMCChains.quantile) by calling the MCMCChains.describe method. This can take a second but is nice to have as an option.\nThe highest posterior density interval, which is the narrowest credible interval that includes the posterior mode, can be calculated with the MCMCChains.hpd method. \nIf you want to extract the array of samples from the MCMCChains.Chains object, you can index result.value – this will return an AxisArray but can be converted to a normal array with Array(result.value).\n\nExamples\n\nimport Distributions: Poisson\ncoeffs = rand(10) # SFH coefficients we want to sample\nmodels = [rand(100,100) .* 100 for i in 1:length(coeffs)] # Vector of model Hess diagrams\ndata = rand.(Poisson.( sum(models .* coeffs) ) ) # Poisson-sample the model `sum(models .* coeffs)`\nnwalkers = 1000\nnsteps = 400\nx0 = rand(nwalkers, length(coeffs)) # Initial walker positions\nresult = mcmc_sample(models, data, x0, nwalkers, nsteps) # Sample\nChains MCMC chain (400×10×1000 Array{Float64, 3}) ...\n\n\n\n\n\n","category":"function"},{"location":"fitting/unconstrained/#Posterior-Sampling:-Change-of-Variables-and-HMC","page":"High-Level Methods for Unconstrained Fitting","title":"Posterior Sampling: Change of Variables and HMC","text":"","category":"section"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"Dolphin 2013 examined methods for obtaining uncertainties on the fitted coefficients (the r_j in Equation 1 of Dolphin 2002) and found that the Hamiltonian Monte Carlo (HMC) approach allowed for relatively efficient sampling of the posterior distribution when considering many isochrones in the modelling process. HMC requires that the variables to be fit are continuous over the real numbers and so requires a change of variables. Rather than sampling the variables r_j directly, we can sample theta_j = textln left( r_j right) such that the sampled variables are continuous over the real numbers -infty theta_j infty while the r_j=textexp left( theta_j right) coefficients are bounded from 0 r_j infty. Using a logarithmic transformation has the additional benefit that the gradient of the Poisson likelihood ratio is still continuous and easy to compute analytically.","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"While maximum likelihood estimates are invariant under variable transformations, sampling methods like HMC are not, as formally the posterior being sampled from is a distribution and therefore must be integrable over the sampling coefficients. We can write the posterior from which we wish to sample as","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"beginaligned\np(r_j D) = fracp(D r_j) p(r_j)Z \np(boldsymbolr D) = frac1Z prod_j p(D r_j) p(r_j) \n-textln p(boldsymbolr D) = textln Z - sum_j textln p(D r_j) + textln p(r_j) \n= textln Z - textln mathscrL + sum_j textln p(r_j)\nendaligned","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"where Z is the Bayesian evidence (a constant that can be neglected for sampling methods), p left( r_j right) is the prior on the star formation history, and mathscrL is the Poisson likelihood ratio discussed above. An uninformative (and unnormalized) prior on the coefficients r_j could take the form of","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"p(r_j) = begincases\n1 r_j geq 0\n0 r_j 0\nendcases","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"such that, if the coefficients r_j are guaranteed to be positive, the final term becomes zero (since textln(1)=0) and","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"-textln p(boldsymbolr D) = textln Z - textln mathscrL","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"When sampling with methods like HMC, constants like textln Z can be neglected and -textln p(boldsymbolr D) propto - textln mathscrL such that the posterior is approximated by the likelihood surface.","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"Let us consider now what happens when we wish to do a variable transformation from r_j to theta_j = textln (r_j). From above we can write the posterior as","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"p(r_j D) = fracp(D r_j) p(r_j)Z ","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"Under the change of variables formula we can write","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"beginaligned\np(theta_j D) = p(r_j D) left fracd r_jd theta_j right \n= p(r_j D) left fracd theta_jd r_j right^-1\nendaligned","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"where left fracd theta_jd r_j right^-1 is often called the Jacobian correction. We choose theta_j such that","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"beginaligned\ntheta_j = textln ( r_j ) \nleft fracd theta_jd r_j right = frac1r_j \nr_j = textexp (theta_j) \nendaligned","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"which leads to a posterior of","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"p(theta_j D) = textexp (theta_j) times p(textexp (theta_j) D) = r_j times p(r_j D) ","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"We can then write the product over the theta_j as","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"beginaligned\np(boldsymboltheta D) = frac1Z prod_j r_j p(D r_j) p(r_j) \n-textln p(boldsymboltheta D) = textln Z - sum_j textln (r_j) + textln p(D r_j) + textln p(r_j) \n= textln Z - sum_j textln p(D r_j) + textln p(r_j) - sum_j theta_j \n= -textln p(boldsymbolr D) - sum_j theta_j \n= -textln p(boldsymbolr D) - sum_j textln (r_j)\nendaligned","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"The choice of a logarithmic transformation means that the negative logarithm of the posterior (which is what HMC uses for its objective function) has this very simple form which allows for simple analytic gradients as well. Once samples of theta have been obtained from this distribution via HMC or any other sampling method, they can be directly transformed back to the standard coefficients r_j = textexp(theta_j).","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"The method hmc_sample implements this approach for sampling the theta_j coefficients; these samples can then be used to estimate random uncertainties on the derived star formation history.","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"StarFormationHistories.hmc_sample","category":"page"},{"location":"fitting/unconstrained/#StarFormationHistories.hmc_sample","page":"High-Level Methods for Unconstrained Fitting","title":"StarFormationHistories.hmc_sample","text":"hmc_sample(models::AbstractVector{T},\n data::AbstractMatrix{<:Number},\n nsteps::Integer [, nchains::Integer];\n rng::Random.AbstractRNG=Random.default_rng(),\n kws...)\n where {S <: Number, T <: AbstractMatrix{S}}\nhmc_sample(models::AbstractMatrix{S},\n data::AbstractVector{<:Number},\n nsteps::Integer;\n rng::AbstractRNG=default_rng(),\n kws...)\n where S <: Number\n\nFunction to sample the posterior of the coefficients coeffs such that the full model of the observational data is sum(models .* coeffs). Uses the Poisson likelihood ratio as defined by equations 7–10 of Dolphin 2002 along with a logarithmic transformation of the coeffs so that the fitting variables are continuous and differentiable over all reals. Sampling is done using the No-U-Turn sampler as implemented in DynamicHMC.jl, which is a form of dynamic Hamiltonian Monte Carlo.\n\nThe second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\nArguments\n\nmodels::AbstractVector{<:AbstractMatrix{<:Number}} is a vector of equal-sized matrices that represent the template Hess diagrams for the simple stellar populations that compose the observed Hess diagram.\ndata::AbstractMatrix{<:Number} is the Hess diagram for the observed data.\nnsteps::Integer is the number of samples to draw per chain.\n\nOptional Arguments\n\nnchains::Integer: If this argument is not provided, this method will return a single chain. If this argument is provided, it will sample nchains chains using all available Julia threads and will return a vector of the individual chains.\n\nKeyword Arguments\n\nrng::Random.AbstractRNG is the random number generator that will be passed to DynamicHMC.jl. If nchains is provided this method will attempt to sample in parallel, requiring a thread-safe rng such as that provided by Random.default_rng(). \n\nAll other keyword arguments kws... will be passed to DynamicHMC.mcmc_with_warmup or DynamicHMC.mcmc_keep_warmup depending on whether nchains is provided.\n\nReturns\n\nIf nchains is not provided, returns a NamedTuple as summarized in DynamicHMC.jl's documentation. In short, the matrix of samples can be extracted and transformed as exp.( result.posterior_matrix ). Statistics about the chain can be obtained with DynamicHMC.Diagnostics.summarize_tree_statistics(result.tree_statistics); you want to see a fairly high acceptance rate (>0.5) and the majority of samples having termination criteria being \"turning.\" See DynamicHMC.jl's documentation for more information.\nIf nchains is provided, returns a vector of length nchains of the same NamedTuples described above. The samples from each chain in the returned vector can be stacked to a single (nsamples, nchains, length(models)) matrix with DynamicHMC.stack_posterior_matrices(result). \n\nExamples\n\nimport DynamicHMC\nimport StatFormationHistories: hmc_sample\nimport Statistics: mean\n# Run sampler using progress meter to monitor progress\n# assuming you have constructed some templates `models` and your observational Hess diagram `data`\nresult = hmc_sample( models, data, 1000; reporter=DynamicHMC.ProgressMeterReport())\n# The chain values are stored in result.posterior matrix; extract them with `result.posterior_matrix`\n# An exponential transformation is needed since the optimization internally uses a logarithmic \n# transformation and samples log(θ) rather than θ directly. \nmc_matrix = exp.( result.posterior_matrix )\n# We can look at some statistics from the chain; want to see high acceptance rate (>0.5) and large % of\n# \"turning\" for termination criteria. \nDynamicHMC.Diagnostics.summarize_tree_statistics(result.tree_statistics)\n Hamiltonian Monte Carlo sample of length 1000\n acceptance rate mean: 0.92, 5/25/50/75/95%: 0.65 0.88 0.97 1.0 1.0\n termination: divergence => 0%, max_depth => 0%, turning => 100%\n depth: 0 => 0%, 1 => 64%, 2 => 36%\n# mc_matrix has size `(length(models), nsteps)` so each column is an independent\n# sample of the SFH as defined by the coefficients and the rows contain the samples\n# for each parameter. \nmstar_tot = sum.(eachcol(mc_matrix)) # Total stellar mass of the modelled system per sample\nmc_means = mean.(eachrow(mc_matrix)) # Mean of each coefficient evaluated across all samples\n# Example with multiple chains sampled in parallel via multi-threading\nimport Threads\nt_result = hmc_sample( models, data, 1000, Threads.nthreads(); reporter=DynamicHMC.ProgressMeterReport())\n# Combine the multiple chains into a single matrix and transform\n# Can then use the same way as `mc_matrix` above\nmc_matrix = exp.( DynamicHMC.pool_posterior_matrices(t_result) )\n\n\n\n\n\n","category":"function"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"See the DynamicHMC.jl documentation for more information on how to use the chains that are output by this method.","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"Inspection of the samples generated by hmc_sample shows that the posterior defined by the above model is typically smooth, well-behaved, and unimodal. In particular, we find that the sampled r_j for coefficients that are non-zero in the MLE are approximately Gaussian distributed while the logarithms of the sampled r_j are roughly Gaussian distributed for coefficients that are zero in the MLE; i.e.","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"begincases\nX_j sim mathcalN hat r_j 0 \ntextln left( X_j right) sim mathcalN hat r_j = 0 \nendcases","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"where X_j are the samples of r_j obtained from the posterior and hat r_j is the maximum likelihood estimate of r_j. ","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"This indicates we may be able to approximate the posterior in the region surrounding the maximum a posteriori (MAP) value by the inverse of the Hessian matrix (see, e.g., Dovi et al. 1991), allowing us to estimate parameter uncertainties very cheaply. The inverse of the Hessian matrix is exactly equal to the variance-covariance matrix of the parameters for a Gaussian probability distribution; for other probability distributions, the inverse of the Hessian approximates the variance-covariance matrix of the parameters when the second-order expansion defined by the Hessian at the maximum is a reasonable approximation to the real objective function being optimized. A particularly simple form arises when the logarithm of the objective is quadratic in the fitting parameters, as in the Gaussian case, because the second derivatives of the objective are constant and do not depend on the fitting parameters or the MAP estimate.","category":"page"},{"location":"fitting/unconstrained/#Maximum-a-Posteriori-Optimization","page":"High-Level Methods for Unconstrained Fitting","title":"Maximum a Posteriori Optimization","text":"","category":"section"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"Direct computation of the Hessian and its inverse is expensive, so we'd like another way to obtain it. The first-order, quasi-Newton BFGS optimization algorithm provides such a method as it iteratively builds a dense approximation to the inverse Hessian using the change in the gradient of the objective, which we can compute analytically. It is, however, much less memory efficient than the LBFGS algorithm we use in StarFormationHistories.fit_templates_lbfgsb. For moderate isochrone grids up to a few hundred model templates, this is not a problem. Beyond this it may be better to use StarFormationHistories.fit_templates_lbfgsb to obtain the MLE and hmc_sample to obtain posterior samples.","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"We implement this optimization scheme in fit_templates, which is our recommended method for unconstrained SFH fitting (i.e., direct fitting of the r_j coefficients). See the next section for notes on more complicated, hierarchical models that can incorporate features like metallicity distribution functions.","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"StarFormationHistories.fit_templates\nStarFormationHistories.LogTransformFTResult","category":"page"},{"location":"fitting/unconstrained/#StarFormationHistories.fit_templates","page":"High-Level Methods for Unconstrained Fitting","title":"StarFormationHistories.fit_templates","text":"result = fit_templates(models::AbstractVector{T},\n data::AbstractMatrix{<:Number};\n x0::AbstractVector{<:Number} = ones(S,length(models)),\n kws...) where {S <: Number, T <: AbstractMatrix{S}}\n\nFinds both maximum likelihood estimate (MLE) and maximum a posteriori estimate (MAP) for the coefficients coeffs such that the composite Hess diagram model is sum(models .* coeffs) using the provided templates models and the observed Hess diagram data. Utilizes the Poisson likelihood ratio (equations 7–10 in Dolphin 2002) for the likelihood of the data given the model. See the examples in the documentation for comparisons of the results of this method and hmc_sample which samples the posterior via Hamiltonian Monte Carlo. \n\nArguments\n\nmodels::AbstractVector{AbstractMatrix{<:Number}}: the list of template Hess diagrams for the simple stellar populations (SSPs) being considered; all must have the same size.\ndata::AbstractMatrix{<:Number}: the observed Hess diagram; must match the size of the templates contained in models.\n\nKeyword Arguments\n\nx0: The vector of initial guesses for the stellar mass coefficients. You should basically always be calculating and passing this keyword argument; we provide StarFormationHistories.construct_x0 to prepare x0 assuming constant star formation rate, which is typically a good initial guess.\n\nOther kws... are passed to Optim.options to set things like convergence criteria for the optimization. \n\nReturns\n\nresult is a NamedTuple containing two StarFormationHistories.LogTransformFTResult. The two components of result are result.map or result[1], which contains the results of the MAP optimization, and result.mle or result[2], which contains the results of the MLE optimization. The documentation for StarFormationHistories.LogTransformFTResult contains more information about these types, but briefly they contain the following fields, accessible as, e.g., result.map.μ, result.map.σ, etc.:\n\nμ::Vector{<:Number} are the optimized coeffs at the maximum.\nσ::Vector{<:Number} are the standard errors on the coeffs μ calculated from an estimate of the inverse Hessian matrix evaluated at μ. The inverse of the Hessian matrix at the maximum of the likelihood (or posterior) is a estimator for the variance-covariance matrix of the parameters, but is only accurate when the second-order expansion given by the Hessian at the maximum is a good approximation to the function being optimized (i.e., when the optimized function is approximately quadratic around the maximum; see Dovi et al. 1991 for more information). We find this is often the case for the MAP estimate, but the errors found for coefficients that are ≈0 in the MLE are typically unrealistically small. For coefficients significantly greater than 0, the σ values from the MAP and MLE are typically consistent to 5–10%.\ninvH::Matrix{<:Number} is the estimate of the inverse Hessian matrix at μ that was used to derive σ. The optimization is done under a logarithmic transform, such that θ[j] = log(coeffs[j]) are the actual parameters optimized, so the entries in the Hessian are actually\n\nH^(jk) ( boldsymbolhat theta ) = left fracpartial^2 J(boldsymboltheta)partial theta_j partial theta_k rightvert_boldsymboltheta=boldsymbolhat theta\n\nresult is the full object returned by the optimization from Optim.jl; this is of type Optim.MultivariateOptimizationResults. Remember that the optimization is done with parameters θ[j] = log(coeffs[j]) when dealing with this raw output. This means that, for example, we calculate result.map.μ as exp.(Optim.minimizer(result.map.result)).\n\nThe special property of the StarFormationHistories.LogTransformFTResult type is that you can draw a set of N::Integer random parameter samples from the result using the inverse Hessian approximation discussed above by doing rand(result.map, N). This type implements the random sampling API of Distributions.jl so the other standard sampling methods should work as well. In our tests these samples compare very favorably against those from hmc_sample, which samples the posterior via Hamiltonian Monte Carlo and is therefore more robust but much more expensive. We compare these methods in the examples.\n\nNotes\n\nThis method uses the BFGS method from Optim.jl internally because it builds and tracks the inverse Hessian matrix approximation which can be used to estimate parameter uncertainties. BFGS is much more memory-intensive than LBFGS (as used for StarFormationHistories.fit_templates_lbfgsb) for large numbers of parameters (equivalently, many models), so you should consider LBFGS to solve for the MLE along with hmc_sample to sample the posterior if you are using a large grid of models (greater than a few hundred).\nThe BFGS implementation we use from Optim.jl uses BLAS operations during its iteration. The OpenBLAS that Julia ships with will often default to running on multiple threads even if Julia itself is started with only a single thread. You can check the current number of BLAS threads with import LinearAlgebra: BLAS; BLAS.get_num_threads(). For the problem sizes typical of this function we actually see performance regression with larger numbers of BLAS threads. For this reason you may wish to use BLAS in single-threaded mode; you can set this as import LinearAlgebra: BLAS; BLAS.set_num_threads(1).\n\n\n\n\n\nfit_templates(models::AbstractMatrix{S},\n data::AbstractVector{<:Number};\n x0::AbstractVector{<:Number} = ones(S,length(models)),\n kws...) where S <: Number\n\nThis call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\n\n\n\n\n","category":"function"},{"location":"fitting/unconstrained/#StarFormationHistories.LogTransformFTResult","page":"High-Level Methods for Unconstrained Fitting","title":"StarFormationHistories.LogTransformFTResult","text":"LogTransformFTResult(μ::AbstractVector{<:Number},\n σ::AbstractVector{<:Number},\n invH::AbstractMatrix{<:Number},\n result)\n\nType for containing the maximum likelihood estimate (MLE) and maximum a posteriori (MAP) results from fit_templates. The fitted coefficients are available in the μ field. Estimates of the standard errors are available in the σ field. These have both been transformed from the native logarithmic fitting space into natural units (i.e., stellar mass or star formation rate).\n\ninvH contains the estimated inverse Hessian of the likelihood / posterior at the maximum point in the logarithmic fitting units. result is the full result object returned by the optimization routine.\n\nThis type is implemented as a subtype of Distributions.Sampleable{Multivariate, Continuous} to enable sampling from an estimate of the likelihood / posterior distribution. We approximate the distribution as a multivariate Gaussian in the native (logarithmically transformed) fitting variables with covariance matrix invH and means log.(μ). We find this approximation is good for the MAP result but less robust for the MLE. You can obtain N::Integer samples from the distribution by rand(R, N) where R is an instance of this type; this will return a size length(μ) x N matrix, or fail if invH is not positive definite.\n\nExamples\n\njulia> result = fit_templates(models, data);\n\njulia> typeof(result.map)\nStarFormationHistories.LogTransformFTResult{...}\n\njulia> size(rand(result.map, 3)) == (length(models),3)\ntrue\n\n\n\n\n\n","category":"type"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"Once you have obtained stellar mass coefficients from the above methods, you can convert them into star formation rates and compute per-age mean metallicities with StarFormationHistories.calculate_cum_sfr.","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"StarFormationHistories.calculate_cum_sfr","category":"page"},{"location":"fitting/unconstrained/#StarFormationHistories.calculate_cum_sfr","page":"High-Level Methods for Unconstrained Fitting","title":"StarFormationHistories.calculate_cum_sfr","text":"(unique_logAge, cum_sfh, sfr, mean_MH) =\n calculate_cum_sfr(coeffs::AbstractVector,\n logAge::AbstractVector,\n MH::AbstractVector,\n T_max::Number;\n normalize_value=1,\n sorted::Bool=false)\n\nCalculates cumulative star formation history, star formation rates, and mean metallicity evolution as functions of logAge = log10(age [yr]).\n\nArguments\n\ncoeffs::AbstractVector is a vector of stellar mass coefficients such as those returned by fit_templates, for example. Actual stellar mass in stellar population j is coeffs[j] * normalize_value.\nlogAge::AbstractVector is a vector giving the log10(age [yr]) of the stellar populations corresponding to the provided coeffs. For the purposes of calculating star formation rates, these are assumed to be left-bin edges.\nMH::AbstractVector is a vector giving the metallicities of the stellar populations corresponding to the provided coeffs.\nT_max::Number is the rightmost final bin edge for calculating star formation rates. For example, you might have logAge=[6.6, 6.7, 6.8] in which case a final logAge of 6.9 would give equal bin widths. In this case you would set T_max = exp10(6.9) / 1e9 ≈ 0.0079 so that the width of the final bin for the star formation rate calculation has the same log10(Age [yr]) step as the other bins.\n\nKeyword Arguments\n\nnormalize_value is a multiplicative prefactor to apply to all the coeffs; same as the keyword in partial_cmd_smooth.\nsorted::Bool is either true or false and signifies whether to assume logAge is sorted.\n\nReturns\n\nunique_logAge::Vector is essentially unique(sort(logAge)) and provides the x-values you would plot the other returned vectors against.\ncum_sfh::Vector is the normalized cumulative SFH implied by the provided coeffs. This is ~1 at the most recent time in logAge and decreases as logAge increases.\nsfr::Vector gives the star formation rate across each bin in unique_logAge. If coeffs .* normalize_value are in units of solar masses, then sfr is in units of solar masses per year.\nmean_MH::Vector gives the stellar-mass-weighted mean metallicity of the stellar population as a function of unique_logAge. \n\n\n\n\n\n","category":"function"},{"location":"examples/#examples","page":"Examples","title":"Examples","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"We have constructed an example Jupyter notebook that highlights some common use cases supported by the package. It is recommended that you read some of the background in our main documentation before or concurrently with the example. The notebook is available in the repository as examples/fitting1.ipynb and can be viewed in rendered form at this link. It relies on a custom isochrone file which can be made available upon request to the package author.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"There are other scripts in the examples directory of the source repository that are used to generate figures for the documentation and provide more targeted examples of usage.","category":"page"},{"location":"#overview","page":"Overview","title":"Overview","text":"","category":"section"},{"location":"","page":"Overview","title":"Overview","text":"This package enables many different kinds of analysis related to astrophysical star formation histories (SFHs). Core among these functionalities are generation of mock color-magnitude diagrams (CMDs) from input SFHs and fitting of SFHs from observed CMDs. The methods of this package have been designed to be simple to use but easy to extend. It is recommended that you use this package in conjunction with InitialMassFunctions.jl which provides implementations of the most popular stellar initial mass functions as new types which are natively supported by this package's methods.","category":"page"}] +[{"location":"fitting/fitting_intro/#fitting","page":"Background and Template Construction","title":"Background and Template Construction","text":"","category":"section"},{"location":"fitting/fitting_intro/#Background","page":"Background and Template Construction","title":"Background","text":"","category":"section"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"In the classic formulation of star formation history fitting from resolved-star photometry (Dolphin 2002), an observed color-magnitude diagram (CMD) is binned into a 2-D histogram known as a Hess diagram. Such a CMD and Hess diagram pair is shown below.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"(Image: Comparison of CMD and a Hess diagram generated from the same observational data.)","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"The representation of the observations as a Hess diagram allows one to apply Poisson statistics, specifically the Poisson likelihood ratio (Equations 7–10 in Dolphin 2002), to model the observations. As the CMD of a complex stellar population is simply the sum of the CMDs of its sub-populations, one need only prepare a number of templates for each simple stellar population (SSP) which may make up the complex population in question and model the observed Hess diagram as a linear combination of these templates. Keeping the same notation as Dolphin 2002 (Equation 1), the complex model Hess diagram is simply","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"m_i = sum_j r_j c_ij","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"where m_i is the value of the complex model in bin i, c_ij is the value of simple template j in bin i, and r_j is the multiplicative coefficient determining how significant template j is to the complex population. In Dolphin 2002, he normalizes the templates to identical star formation rates (SFRs) and so the r_j are SFRs as well. In this package, we prefer to normalize our templates to identical population stellar masses, so our r_j are stellar masses, but the principal is the same.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"Construction of the templates is, however, not a trivial exercise. Ideally, a template constructed to represent a particular SSP would accurately reflect the expectation of how such a population would be observed. Thus, these templates must be adjusted for photometric error, incompleteness, and other effects such as those caused by unresolved binary- and multi-star systems. Observational effects such as photometric error and incompleteness are best measured from artificial star tests (ASTs). It is worth mentioning that ASTs can often return \"best case\" results, as they typically neglect systematics like uncertainty in the point-spread model used for the photometry; as such it is sometimes necessary to add a systematic error floor to photometric error results from ASTs.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"Such templates can be constructed by sampling many mock stars from an initial mass function (IMF), interpolating their absolute magnitudes from an isochrone of the relevant SSP, and \"mock observing\" them by applying photometric error and completeness functions (for example, by looking up the 1sigma photometric error and completeness value from a catalog of artificial stars). Such Monte Carlo templates can be slow to construct and exhibit Poisson shot-noise, requiring a statistical data–data comparison rather than a model–data comparison. Thus this method is non-optimal from both a practical and statistical perspective.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"It is better to form what Dolphin 2002 calls a \"blurred isochrone;\" in this form of template, the SSP isochrone is first interpolated in initial stellar mass to improve the point density along the isochrone. The number of interpolated points is generally a function of the size of the bins in the Hess diagram and the observational error; more points are required as the bin size or photometric errors become smaller. These points are then weighted according to the IMF and the photometric completeness, and this weight is distributed into the Hess diagram following the photometric error distribution determined by similar artificial stars. Dolphin 2002 also mentions interpolating across stellar age/metallicity when constructing such templates; for example, for an SSP with an age of 1 Gyr and a metallicity of [M/H]=-1.0, you could interpolate the isochrones to introduce a Gaussian metallicity spread of 0.05 dex or an age spread of 100 Myr. The general effects of this form of interpolation is to broaden the model templates, particularly features that are very sharp in true SSP models. We neglect this form of interpolation in our implementation as it adds significant complexity and requires users to provide more information about the isochrones that are providing. Such widening of the individual templates is most impactful when photometric errors in the observational data are low (perhaps <0.10 mag).","category":"page"},{"location":"fitting/fitting_intro/#templates","page":"Background and Template Construction","title":"Constructing Templates","text":"","category":"section"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"While the above description summarizes the necessary components for constructing such a blurred isochrone, it can be a bit difficult to figure out how best to actually construct them. Specifically there are many ways that one could implement the observational effects of photometric error and incompleteness. We provide a method partial_cmd_smooth to construct such templates under the assumption of Gaussian photometric error distributions, which is often a good approximation in the high-completeness regime. This method makes use of user-defined functions for the mean photometric error and completeness as a function of magnitude and filter, such that these can be defined in a number of ways; for example, as direct lookups from a large table of ASTs or as simple function evaluations of analytic approximations or fits to the ASTs.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"This method begins by interpolating the provided SSP isochrone to increase point density. For every such point with i band apparent magnitude m_i, it calls a user-defined function to estimate the 1sigma photometric error as sigma_i = f_i(m_i). This is done for each provided photometric filter. These errors are used to define an asymmetric 2-D Gaussian kernel for each point in the interpolated isochrone. This kernel describes the probability distribution of where the isochrone point would be observed in the Hess diagram. The shape of the kernel is automatically adjusted to account for covariance when the magnitude on the y-axis of the Hess diagram also appears in the x-axis color, as occurs when only two filters of imaging are available (for example, an x-axis of B-V and a y-axis magnitude of V). Details on these kernels are given here. However, the kernel also must be normalized (weighted) according to the IMF and observational completeness functions.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"Assume that the vector of initial stellar masses for the points in the interpolated isochrone are m_i and that they are sorted such that m_i m_i+1. The IMF weight on point m_i can be approximated as the number fraction of stars born between m_i and m_i+1 divided by the mean mass per star born langle m rangle, such that the weight effectively represents the number of stars expected to be born with masses between m_i and m_i+1 per solar mass of star formation:","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"beginaligned\nw_itextIMF = frac int_0^m_i+1 fracdN(m)dm dm - int_0^m_i fracdN(m)dm dm int_0^infty m times fracdN(m)dm dm = frac int_m_i^m_i+1 fracdN(m)dm dm langle m rangle\nendaligned","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"The numerator can either be calculated as the difference in the cumulative distribution function across the bin or approximated efficiently via the trapezoidal rule. The denominator is a function only of the IMF and need only be calculated once. Multiplying this weight by the probability of detection in the relevant bands gives the final weight.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"mv(\"../../../examples/templates/template_compare.svg\", \"figures/template_compare.svg\") # hide\nmv(\"../../../examples/templates/sigma_distribution.svg\", \"figures/sigma_distribution.svg\") # hide\nnothing # hide","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"A worked example comparing a sampled stellar population with a smooth Hess diagram template is available in examples/templates/smooth_template.jl. The smooth Hess diagram template is constructed with partial_cmd_smooth and the Monte Carlo population is sampled with generate_stars_mass, with observational effects modelled by model_cmd. The isochrone used comes from PARSEC and has an age of 12.6 Gyr and initial metallicity [M/H] of -2.8. The output figure is shown below. A distance modulus of 25 mag is used for this example, with photometric error and completeness functions roughly based on those we observe in the JWST/NIRCAM data of WLM (see Weisz et al. 2024).","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"(Image: Comparison of CMD-sampled population with smooth Hess diagram template.)","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"At left is a population of stars sampled from an SSP with the methods described in the section of the documentation on simulating CMDs. The points from the isochrone are colored orange. The next figure shows the binned Hess diagram derived from these data. The next figure shows our smooth Hess diagram template calculated for this SSP. The final figure at right shows the residual between the data and model in units of standard deviations. These are sometimes called Pearson residuals. Below we show the distribution of these residuals, which should be Gaussian with mean 0 and standard deviation 1 if the model were perfect. Note that, because all bins with 0 observed stars are excluded from the distribution and the observed Hess diagram contains Poisson error, the mean of our distribution will always be slightly higher than 0. We do, however, achieve a standard deviation of nearly 1, indicating that our model is robust. We show a Gaussian PDF with standard deviation 1 and mean equal to the observed mean of the residuals for comparison.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"(Image: Distribution of data - model residuals, in units of standard deviations.)","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"The method used to create these smooth Hess diagram templates is partial_cmd_smooth.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"partial_cmd_smooth","category":"page"},{"location":"fitting/fitting_intro/#StarFormationHistories.partial_cmd_smooth","page":"Background and Template Construction","title":"StarFormationHistories.partial_cmd_smooth","text":"result::StatsBase.Histogram =\n partial_cmd_smooth(m_ini::AbstractVector{<:Number},\n mags::AbstractVector{<:AbstractVector{<:Number}},\n mag_err_funcs,\n y_index,\n color_indices,\n imf,\n completeness_funcs=[one for i in mags];\n dmod::Number=0,\n normalize_value::Number=1,\n binary_model::AbstractBinaryModel=NoBinaries(),\n mean_mass=mean(imf),\n edges=nothing,\n xlim=nothing,\n ylim=nothing,\n nbins=nothing,\n xwidth=nothing,\n ywidth=nothing)\n\nMain function for generating template Hess diagrams from a simple stellar population of stars from an isochrone, including photometric error and completeness.\n\nArguments\n\nm_ini::AbstractVector{<:Number} is a vector containing the initial stellar masses of the stars from the isochrone.\nmags::AbstractVector{<:AbstractVector{<:Number}} is a vector of vectors. Each constituent vector with index i should have length(mags[i]) == length(m_ini), representing the magnitudes of the isochrone stars in each of the magnitudes considered. In most cases, mags should contain 2 (if y-axis mag is also involved in the x-axis color) or 3 vectors.\nmag_err_funcs must be an indexable object (e.g., a Vector or Tuple) that contains callables (e.g., a Function) to compute the 1σ photometric errors for the same filters provided in mags. Each callable must take a single argument (an apparent magnitude) and return a Number. The length mag_err_funcs must be equal to the length of mags.\ny_index gives a valid index (e.g., an Int or CartesianIndex) into mags for the filter you want to have on the y-axis of the Hess diagram. For example, if the mags argument contains the B and V band magnitudes as mags=[B, V] and you want V on the y-axis, you would set y_index as 2. \ncolor_indices is a length-2 indexable object giving the indices into mags that are to be used to compute the x-axis color. For example, if the mags argument contains the B and V band magnitudes as mags=[B, V], and you want B-V to be the x-axis color, then color_indices should be [1,2] or (1,2) or similar.\nimf is a callable that takes an initial stellar mass as its sole argument and returns the (properly normalized) probability density of your initial mass function model. All the models from InitialMassFunctions.jl are valid for imf.\ncompleteness_functions must be an indexable object (e.g., a Vector or Tuple) that contains callables (e.g., a Function) to compute the single-filter completeness fractions as a function of apparent magnitude. Each callable in this argument must correspond to the matching filter provided in mags.\n\nKeyword Arguments\n\ndmod::Number=0 is the distance modulus in magnitudes to apply to the input mags. Leave at 0 if you are providing apparent magnitudes in mags.\nnormalize_value::Number=1 is the total stellar mass of the population you wish to model.\nbinary_model::AbstractBinaryModel=NoBinaries() is the model to use for including binary systems. Currently only StarFormationHistories.NoBinaries and StarFormationHistories.RandomBinaryPairs are supported.\nmean_mass::Number is the expectation value of the initial mass for a random star drawn from your provided imf. This will be computed for you if your provided imf is a valid continuous, univariate Distributions.Distribution object.\nedges is a tuple of ranges defining the left-side edges of the bins along the x-axis (edges[1]) and the y-axis (edges[2]). Example: (-1.0:0.1:1.5, 22:0.1:27.2). If edges is provided, it overrides the following keyword arguments that offer other ways to specify the extent of the Hess diagram.\nxlim is a length-2 indexable object (e.g., a Vector or Tuple) giving the lower and upper bounds on the x-axis corresponding to the provided colors array. Example: (-1.0, 1.5). This is only used if edges is not provided. \nylim is as xlim but for the y-axis corresponding to the provided mags array. Example (25.0, 20.0). This is only used if edges is not provided.\nnbins::NTuple{2, <:Integer} is a 2-tuple of integers providing the number of bins to use along the x- and y-axes. This is only used if edges is not provided.\nxwidth is the bin width along the x-axis for the colors array. This is only used if edges and nbins are not provided. Example: 0.1.\nywidth is as xwidth but for the y-axis corresponding to the provided mags array. Example: 0.1.\n\nReturns\n\nThis method returns the Hess diagram as a StatsBase.Histogram; you should refer to the StatsBase documentation for more information. In short, if the output of this method is result, then the Hess diagram represented as a Matrix is available as result.weights (this is what you would want for fit_templates and similar functions) and the edges of the histogram are available as result.edges.\n\n\n\n\n\n","category":"function"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"The user-provided functions returning magnitude errors (mag_err_funcs) and completeness values (completness_functions) given a star's intrinsic apparent magnitude are important for deriving good templates. These are typically derived from catalogs of artificial star tests. Some helper functions for constructing these are provided here.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"We note that in many cases it can also be helpful to add in a foreground/background template that models contamination of the Hess diagram from stars not in your population of interest – this is often done using observations of parallel fields though there are several other possible methods.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"Photometric catalogs can be processed into Hess diagrams meeting our formatting requirements with the method bin_cmd.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"StarFormationHistories.bin_cmd","category":"page"},{"location":"fitting/fitting_intro/#StarFormationHistories.bin_cmd","page":"Background and Template Construction","title":"StarFormationHistories.bin_cmd","text":"result::StatsBase.Histogram =\n bin_cmd(colors::AbstractVector{<:Number},\n mags::AbstractVector{<:Number};\n weights::AbstractVector{<:Number} = ones(promote_type(eltype(colors),\n eltype(mags)), size(colors)),\n edges = nothing,\n xlim = extrema(colors),\n ylim = extrema(mags),\n nbins = nothing,\n xwidth = nothing,\n ywidth = nothing)\n\nReturns a StatsBase.Histogram type containing the Hess diagram from the provided x-axis photometric colors and y-axis photometric magnitudes mags. These must all be vectors equal in length. You can either specify the bin edges directly via the edges keyword (e.g., edges = (range(-0.5, 1.6, length=100), range(17.0, 26.0, length=100))), or you can set the x- and y-limits via xlim and ylim and the number of bins as nbins, or you can omit nbins and instead pass the bin width in the x and y directions, xwidth and ywidth. See below for more info on the keyword arguments. To plot this with PyPlot.jl you should do PyPlot.imshow(permutedims(result.weights), origin=\"lower\", extent=(extrema(result.edges[1])..., extrema(result.edges[2]), kws...) where kws... are any other keyword arguments you wish to pass to PyPlot.imshow.\n\nKeyword Arguments\n\nweights::AbstractVector{<:Number} is a array of length equal to colors and mags that contains the probabilistic weights associated with each point. This is passed to StatsBase.fit as StatsBase.Weights(weights). The following keyword arguments are passed to StarFormationHistories.calculate_edges to determine the bin edges of the histogram.\nedges is a tuple of ranges defining the left-side edges of the bins along the x-axis (edges[1]) and the y-axis (edges[2]). Example: (-1.0:0.1:1.5, 22:0.1:27.2). If edges is provided, weights is the only other keyword that will be read; edges supercedes the other construction methods. \nxlim is a length-2 indexable object (e.g., a vector or tuple) giving the lower and upper bounds on the x-axis corresponding to the provided colors array. Example: [-1.0, 1.5]. This is only used if edges is not provided. \nylim is like xlim but for the y-axis corresponding to the provided mags array. Example [25.0, 20.0]. This is only used if edges is not provided.\nnbins::NTuple{2, <:Integer} is a 2-tuple of integers providing the number of bins to use along the x- and y-axes. This is only used if edges is not provided.\nxwidth is the bin width along the x-axis for the colors array. This is only used if edges and nbins are not provided. Example: 0.1. \nywidth is like xwidth but for the y-axis corresponding to the provided mags array. Example: 0.1.\n\n\n\n\n\n","category":"function"},{"location":"fitting/fitting_intro/#A-Note-on-Array-Formatting","page":"Background and Template Construction","title":"A Note on Array Formatting","text":"","category":"section"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"It is expected that the user will typically have model templates stored as two-dimensional matrices as these are the obvious choice for representing a binned two-dimensional histogram. We fully support supplying the list of model templates as a list of matrices (e.g., a Vector{Matrix{<:Number}}) to the fitting functions discussed below. The important computational kernels composite! and ∇loglikelihood! have custom loops for these input types.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"However, additional optimizations are possible by flattening the data. By flattening each matrix in the list of model templates into a column vector and concatenating them such that the list of model templates becomes a single matrix, we can compute the complex model Hess diagram as a single matrix-vector product rather than using a custom loop. The same optimization can be made when computing the gradient of the loglikelihood (discussed more below). The majority of all computation for the fitting methods below is spent in these two functions, so optimizing their performance translates directly to improved fitting runtimes. With this flattened memory arrangement we can use the highly optimized LinearAlgbera.mul! method to do the in-place matrix-vector product. This will typically be translated into a call to a BLAS function like gemv!. As such, we can benefit from Julia's ability to switch BLAS implementations at runtime to use Intel's Math Kernel Library, Apple's Accelerate, and others.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"Most of the fitting methods below support both the natural and flattened data layouts. We provide the stack_models method to produce the optimized layout for the list of model templates.","category":"page"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"StarFormationHistories.stack_models","category":"page"},{"location":"fitting/fitting_intro/#StarFormationHistories.stack_models","page":"Background and Template Construction","title":"StarFormationHistories.stack_models","text":"stack_models(models::AbstractVector{<:AbstractMatrix{<:Number}}) = reduce(hcat,map(vec,models))\n\nTransforms a vector of matrices into a single matrix, with each matrix from models being transcribed into a single column in the output matrix. This data layout enables more efficient calculations in some of our internal functions like composite! and ∇loglikelihood!.\n\nExamples\n\njulia> stack_models([rand(5,5) for i in 1:10])\n25×10 Matrix{Float64}:\n...\n\n\n\n\n\n","category":"function"},{"location":"fitting/fitting_intro/#A-Note-on-Threading","page":"Background and Template Construction","title":"A Note on Threading","text":"","category":"section"},{"location":"fitting/fitting_intro/","page":"Background and Template Construction","title":"Background and Template Construction","text":"While generally the methods using BLAS routines offer significant performance improvements, there is a caveat when multithreading from within Julia. By default Julia will allow BLAS to use multiple threads even if Julia itself is started with a single thread (i.e., by running julia -t 1). BLAS threads do not compose with Julia threads. That is, if you start Julia with N>1 threads (julia -t N) and write a threaded workload where each Julia thread is doing BLAS operations concurrently, you can easily oversubscribe the CPU. Specific recommendations vary depending on BLAS vendor (see this page and the linked discourse threads), but generally this package is in the regime of doing many small calculations that do not individually benefit much from BLAS threading (e.g., performance for OpenBLAS with 8 threads is only ~2x the 1 thread performance). As such it is often sufficient to set BLAS to use a single thread (via LinearAlgbera.BLAS.set_num_threads(1) or environment variables; see above link).","category":"page"},{"location":"doc_index/#index","page":"Index","title":"Index","text":"","category":"section"},{"location":"doc_index/","page":"Index","title":"Index","text":"Modules = [StarFormationHistories]","category":"page"},{"location":"fitting/fixed_amr/#Fixed-Age-Metallicity-Relations","page":"Fixed Age-Metallicity Relations","title":"Fixed Age-Metallicity Relations","text":"","category":"section"},{"location":"fitting/fixed_amr/","page":"Fixed Age-Metallicity Relations","title":"Fixed Age-Metallicity Relations","text":"It is often the case that one may want to fit for star formation rates under a fixed age-metallicity relation or other metallicity evolution model with no degrees of freedom. Such functionality is provided by fixed_amr, which takes as input the relative weights (relweights in the function call, equivalently the r_jk in the above derivation) on each template due to a predetermined metallicity model and fits only the per-age-bin coefficients (R_j in the above derivation). ","category":"page"},{"location":"fitting/fixed_amr/","page":"Fixed Age-Metallicity Relations","title":"Fixed Age-Metallicity Relations","text":"StarFormationHistories.fixed_amr","category":"page"},{"location":"fitting/fixed_amr/#StarFormationHistories.fixed_amr","page":"Fixed Age-Metallicity Relations","title":"StarFormationHistories.fixed_amr","text":"fixed_amr(models::AbstractVector{T},\n data::AbstractMatrix{<:Number},\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number},\n relweights::AbstractVector{<:Number};\n relweightsmin::Number=0, \n x0=construct_x0_mdf(logAge, convert(S,13.7)),\n kws...) where {S <: Number, T <: AbstractMatrix{S}}\nfixed_amr(models::AbstractMatrix{S},\n data::AbstractVector{<:Number},\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number},\n relweights::AbstractVector{<:Number};\n relweightsmin::Number=0,\n x0=construct_x0_mdf(logAge, convert(S,13.7)),\n kws...) where S <: Number\n\nMethod that fits a linear combination of the provided Hess diagrams models to the observed Hess diagram data, under an externally-imposed age-metallicity relation (AMR) and/or metallicity distribution function (MDF). As such, a number of coefficients equal to length(unique(logAge)) are returned; that is, only one coefficient is derived per unique entry in logAge.\n\nThe second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\nArguments\n\nmodels::AbstractVector{<:AbstractMatrix{<:Number}} is a vector of equal-sized matrices that represent the template Hess diagrams for the simple stellar populations that compose the observed Hess diagram.\ndata::AbstractMatrix{<:Number} is the Hess diagram for the observed data.\nlogAge::AbstractVector{<:Number} is the vector containing the effective ages of the stellar populations used to create the templates in models, in units of log10(age [yr]). For example, if a population has an age of 1 Myr, its entry in logAge should be log10(10^6) = 6.0.\nmetallicities::AbstractVector{<:Number} is the vector containing the effective metallicities of the stellar populations used to create the templates in models. This is most commonly a logarithmic abundance like [M/H] or [Fe/H], but you could use a linear abundance like the metal mass fraction Z if you wanted to. There are some notes on the Wikipedia that might be useful.\nrelweights::AbstractVector{<:Number} is a vector of length equal to that of models which contains the relative weights to apply to each model Hess diagram resulting from an externally-imposed age-metallicity relation and/or metallicity distribution function. Additional details on how to create these weights is provided in the notes below and in the online documentation.\n\nKeyword Arguments\n\nrelweightsmin truncates the input list of models based on the provided relweights, providing a speedup at the cost of precision by removing models that contribute least to the overall composite model. By default, no truncation of the input is performed and all provided models are used in the fit. We recommend this only be increased when fitting performance begins to impact workflow (e.g., when running massive Monte Carlo experiments). See StarFormationHistories.truncate_relweights for implementation details. \nx0 is the vector of initial guesses for the stellar mass coefficients per unique entry in logAge. You should basically always be calculating and passing this keyword argument. We provide StarFormationHistories.construct_x0_mdf to prepare x0 assuming constant star formation rate, which is typically a good initial guess. \n\nOther kws... are passed to Optim.options to set things like convergence criteria for the optimization.\n\nNotes\n\nAll metallicity-related weighting of the models is assumed to be captured in the provided relweights vector, which has the same length as the logAge, metallicities, and models vectors. Each entry in relweights is assumed to be a relative weight for the corresponding model. For example, for the model Hess diagram models[i], with log10(age [yr]) = logAge[i] and metallicity metallicities[i], the relative weight due to the model's age and metallicity w(logAge[i], metallicities[i]) is assumed to be relweights[i]. The sum of all relweights for each unique entry in logAge should be 1; i.e., the following condition should be met: all( sum(relweights[logAge .== la]) ≈ 1 for la in unique(logAge)). If this is not the case, this function will issue a warning and attempt to renormalize relweights by mutating the vector in place. More information on preparation of the relweights for input to this method is provided in our online documentation. \nThis function is designed to work best with a \"grid\" of stellar models, defined by the outer product of N unique entries in logAge and M unique entries in metallicities. See the examples for more information on usage.\n\n\n\n\n\n","category":"function"},{"location":"fitting/linear_amr/#linear_amr_section","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"","category":"section"},{"location":"fitting/linear_amr/#metal_evo_intro","page":"Linear Age-Metallicity Relation","title":"Why Should Metallicity Evolutions Be Constrained?","text":"","category":"section"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"While the above methods work well for optimizing the per-template r_j as a means for fitting SFHs, these methods can produce metallicity evolutions that could be considered unphysical, with large changes in the mean metallicity over small changes in time. An example of this type of behavior is shown in the SFH fit below.","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"(Image: Example of a SFH fit with variations in the metallicity evolution.)","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"While some metallicity variation in the star-forming gas is to be expected, these variations in the SFH fit can end up being quite large depending on the data and isochrone grid adopted. A solution is to construct a more physically-motivated model.","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"We can do this using a hierarchical model with a parameterized metallicity distribution function (MDF) where the the r_j are not the parameters directly optimized. Rather, we can optimize one stellar mass (or star formation rate) parameter per age bin, and then a number of MDF parameters that determine how that stellar mass is split between models with different metallicities at fixed age. An example for one such MDF model is a linear mean metallicity relation langle textMtextH rangle (t) = alpha left( T_textmax - t right) + beta with a Gaussian distribution in metallicity at fixed age. T_textmax here is the earliest lookback time under consideration such that langle textMtextH rangle (T_textmax) = beta. If the per-age-bin stellar mass coefficients are R_j, the age of the stellar population j is t_j, and the metallicity of population k is textMtextH_k, then we can write the per-model r_jk (where we are now using separate indices for age and metallicity) as","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"beginaligned\nmu_j = alpha left( T_textmax - t_j right) + beta \nr_jk = R_j frac textexp left( - left( frac textMtextH_k - mu_jsigma right)^2 right)sum_k textexp left( - left( frac textMtextH_k - mu_jsigma right)^2 right)\nendaligned","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"where the numerator is the MDF at fixed age evaluated at metallicity textMtextH_k and the denominator is a normalizing coefficient that ensures sum_k r_jk = R_j. In this notation, bin i of the complex model Hess diagram (equation 1 of Dolphin 2002) is","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"m_i = sum_jk r_jk c_ijk","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"Below we show a fit using this hierarchical model to the same data as above. ","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"(Image: Example of a SFH fit with a linear metallicity evolution.)","category":"page"},{"location":"fitting/linear_amr/#Fitting-Functions","page":"Linear Age-Metallicity Relation","title":"Fitting Functions","text":"","category":"section"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"We provide the method StarFormationHistories.fit_templates_mdf to fit this model to an observed Hess diagram.","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"StarFormationHistories.fit_templates_mdf\nStarFormationHistories.LogTransformMDFσResult\nStarFormationHistories.LogTransformMDFResult","category":"page"},{"location":"fitting/linear_amr/#StarFormationHistories.fit_templates_mdf","page":"Linear Age-Metallicity Relation","title":"StarFormationHistories.fit_templates_mdf","text":"fit_templates_mdf(models::AbstractVector{<:AbstractMatrix{S}},\n data::AbstractMatrix{<:Number},\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number},\n T_max::Number\n [, σ::Number];\n x0 = vcat(construct_x0_mdf(logAge, convert(S,13.7)),\n [0.05, -2.0, 0.2]),\n kws...) where {S <: Number}\nfit_templates_mdf(models::AbstractMatrix{S},\n data::AbstractVector{<:Number},\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number},\n T_max::Number\n [, σ::Number];\n x0 = vcat(construct_x0_mdf(logAge, convert(S,13.7)),\n [0.05, -2.0, 0.2]),\n kws...) where {S <: Number}\n\nMethod that fits a linear combination of the provided Hess diagrams models to the observed Hess diagram data, constrained to have a linear age-metallicity relation with the mean metallicity of element i of unique(logAge) being μ[i] = α * (T_max - exp10(unique(logAge)[i]) / 1e9) + β. α is therefore a slope in the units of metallicities per Gyr, and β is the mean metallicity value of stars being born at a lookback time of T_max, which has units of Gyr. Individual weights for each isochrone template are then determined via Gaussian weighting with the above mean and the standard deviation σ, which can either be fixed or fit.\n\nThis function is designed to work best with a \"grid\" of stellar models, defined by the outer product of N unique entries in logAge and M unique entries in metallicities. See the examples for more information on usage.\n\nThe second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\nArguments\n\nmodels are the template Hess diagrams for the simple stellar populations that compose the observed Hess diagram. \ndata is the Hess diagram for the observed data. \nlogAge::AbstractVector{<:Number} is the vector containing the effective ages of the stellar populations used to create the templates in models, in units of log10(age [yr]). For example, if a population has an age of 1 Myr, its entry in logAge should be log10(10^6) = 6.0.\nmetallicities::AbstractVector{<:Number} is the vector containing the effective metallicities of the stellar populations used to create the templates in models. This is most commonly a logarithmic abundance like [M/H] or [Fe/H], but you could use a linear abundance like the metal mass fraction Z if you wanted to. There are some notes on the Wikipedia that might be useful.\nT_max::Number is the time at which the age-metallicity relation has a value of \beta in Gyr. For example, if the oldest stellar populations in your isochrone grid are 12 Gyr old, you could set T_max = 12.0. \n\nOptional Arguments\n\nIf provided, σ::Number is the fixed width of the Gaussian the defines the metallicity distribution function (MDF) at fixed logAge. If this argument is omitted, σ will be a free parameter in the fit. \n\nKeyword Arguments\n\nx0 is the vector of initial guesses for the stellar mass coefficients per unique entry in logAge, plus the variables that define the metallicity evolution model. You should basically always be calculating and passing this keyword argument. We provide StarFormationHistories.construct_x0_mdf to prepare the first part of x0 assuming constant star formation rate, which is typically a good initial guess. You then have to concatenate that result with an initial guess for the metallicity evolution parameters. For example, x0=vcat(construct_x0_mdf(logAge, 13.7; normalize_value=1e4), [0.05,-2.0,0.2]), where logAge is a valid argument for this function (see above), and the initial guesses on the parameters are [α, β, σ] = [0.05, -2.0, 0.2]. If the provided metallicities are, for example, [M/H] values, then this mean metallicity evolution is μ(t) [dex] = 0.05 [dex/Gyr] * (T_max - t) [Gyr] - 2.0 [dex], and at fixed time, the metallicity distribution function is Gaussian with mean μ(t) and standard deviation σ. If you provide σ as an optional argument, then you should not include an entry for it in x0.\nOther kws... are passed to Optim.options to set things like convergence criteria for the optimization.\n\nReturns\n\nThis function returns an object (say, result) of similar structure to the object returned by fit_templates. Specifically, this method will return a NamedTuple with entries result.mle and result.map for the maximum likelihood and maximum a posteriori estimates, respectively. If you provide a fixed σ, those objects will be instances of StarFormationHistories.LogTransformMDFσResult. If you allow σ to be freely fit, those objects will be instances of StarFormationHistories.LogTransformMDFResult. Both of these types support sampling via, e.g., rand(result.map, 10). \n\nNotes\n\nα and σ are optimized under a logarithmic transformation, so they are constrained to be positive. β is not and may be negative. This method also uses the BFGS method from Optim.jl internally just like fit_templates; please see the notes section of that method. \n\n\n\n\n\n","category":"function"},{"location":"fitting/linear_amr/#StarFormationHistories.LogTransformMDFσResult","page":"Linear Age-Metallicity Relation","title":"StarFormationHistories.LogTransformMDFσResult","text":"LogTransformMDFσResult(μ::AbstractVector{<:Number},\n σ::AbstractVector{<:Number},\n invH::AbstractMatrix{<:Number},\n result)\n\nType for containing the maximum likelihood estimate (MLE) and maximum a posteriori (MAP) results from fit_templates_mdf for fixed σ. The fitted coefficients are available in the μ field. Estimates of the standard errors are available in the σ field. These have both been transformed from the native logarithmic fitting space into natural units (i.e., stellar mass or star formation rate). The linear age-metallicity relation parameters α (slope [dex/Gyr]) and β (intercept at T_max [dex]) are available in the second-to-last and last elements of μ and σ, respectively. \n\ninvH contains the estimated inverse Hessian of the likelihood / posterior at the maximum point in the logarithmic fitting units. result is the full result object returned by the optimization routine.\n\nThis type is implemented as a subtype of Distributions.Sampleable{Multivariate, Continuous} to enable sampling from an estimate of the likelihood / posterior distribution. We approximate the distribution as a multivariate Gaussian in the native (logarithmically transformed) fitting variables with covariance matrix invH and means log.(μ). We find this approximation is good for the MAP result but less robust for the MLE. You can obtain N::Integer samples from the distribution by rand(R, N) where R is an instance of this type; this will return a size (length(μ)+2) x N matrix, or fail if invH is not positive definite.\n\nExamples\n\njulia> result = fit_templates_mdf(models, data, model_logAge, model_MH, 0.3);\n\njulia> typeof(result.map)\nStarFormationHistories.LogTransformMDFσResult{...}\n\njulia> size(rand(result.map, 3)) == (length(models)+2,3)\ntrue\n\n\n\n\n\n","category":"type"},{"location":"fitting/linear_amr/#StarFormationHistories.LogTransformMDFResult","page":"Linear Age-Metallicity Relation","title":"StarFormationHistories.LogTransformMDFResult","text":"LogTransformMDFResult(μ::AbstractVector{<:Number},\n σ::AbstractVector{<:Number},\n invH::AbstractMatrix{<:Number},\n result)\n\nType for containing the maximum likelihood estimate (MLE) and maximum a posteriori (MAP) results from fit_templates_mdf when freely fitting σ. The fitted coefficients are available in the μ field. Estimates of the standard errors are available in the σ field. These have both been transformed from the native logarithmic fitting space into natural units (i.e., stellar mass or star formation rate). The linear age-metallicity relation parameters α (slope [dex/Gyr]) and β (intercept at T_max [dex]) are available in the third-to-last and second-to-last elements of μ and σ, respectively. The static Gaussian width of the MDF at fixed age is provided in the last element of μ and σ. \n\ninvH contains the estimated inverse Hessian of the likelihood / posterior at the maximum point in the logarithmic fitting units. result is the full result object returned by the optimization routine.\n\nThis type is implemented as a subtype of Distributions.Sampleable{Multivariate, Continuous} to enable sampling from an estimate of the likelihood / posterior distribution. We approximate the distribution as a multivariate Gaussian in the native (logarithmically transformed) fitting variables with covariance matrix invH and means log.(μ). We find this approximation is good for the MAP result but less robust for the MLE. You can obtain N::Integer samples from the distribution by rand(R, N) where R is an instance of this type; this will return a size (length(μ)+3) x N matrix, or fail if invH is not positive definite.\n\nExamples\n\njulia> result = fit_templates_mdf(models, data, model_logAge, model_MH);\n\njulia> typeof(result.map)\nStarFormationHistories.LogTransformMDFσResult{...}\n\njulia> size(rand(result.map, 3)) == (length(models)+3,3)\ntrue\n\n\n\n\n\n","category":"type"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"The method StarFormationHistories.construct_x0_mdf can be used to construct the stellar mass components R_j of the initial guess vector x0","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"StarFormationHistories.construct_x0_mdf","category":"page"},{"location":"fitting/linear_amr/#StarFormationHistories.construct_x0_mdf","page":"Linear Age-Metallicity Relation","title":"StarFormationHistories.construct_x0_mdf","text":"x0::Vector = construct_x0_mdf(logAge::AbstractVector{T},\n [ cum_sfh, ]\n T_max::Number;\n normalize_value::Number = one(T)) where T <: Number\n\nGenerates a vector of initial stellar mass normalizations for input to StarFormationHistories.fit_templates_mdf or StarFormationHistories.hmc_sample_mdf with a total stellar mass of normalize_value. The logAge vector must contain the log10(Age [yr]) of each isochrone that you are going to input as models. If cum_sfh is not provided, a constant star formation rate is assumed. For the purposes of computing the constant star formation rate, the provided logAge are treated as left-bin edges, with the final right-bin edge being T_max, which has units of Gyr. For example, you might have logAge=[6.6, 6.7, 6.8] in which case a final logAge of 6.9 would give equal bin widths (in log-space). In this case you would set T_max = exp10(6.9) / 1e9 ≈ 0.0079 so that the width of the final bin for the star formation rate calculation has the same log10(Age [yr]) step as the other bins.\n\nA desired cumulative SFH vector cum_sfh::AbstractVector{<:Number} can be provided as the second argument, which should correspond to a lookback time vector unique(logAge). You can also provide cum_sfh as a length-2 indexable (e.g., a length-2 Vector{Vector{<:Number}}) with the first element containing a list of log10(Age [yr]) values and the second element containing the cumulative SFH values at those values. This cumulative SFH is then interpolated onto the logAge provided in the first argument. This method should be used when you want to define the cumulative SFH on a different age grid from the logAge you provide in the first argument. The examples below demonstrate these use cases.\n\nThe difference between this function and StarFormationHistories.construct_x0 is that this function generates an x0 vector that is of length length(unique(logage)) (that is, a single normalization factor for each unique entry in logAge) while StarFormationHistories.construct_x0 returns an x0 vector that is of length length(logAge); that is, a normalization factor for every entry in logAge. The order of the coefficients is such that the coefficient x[i] corresponds to the entry unique(logAge)[i]. \n\nNotes\n\nExamples – Constant SFR\n\njulia> isapprox( construct_x0_mdf([9.0, 8.0, 7.0], 10.0; normalize_value=5.0),\n [4.504504504504504, 0.4504504504504504, 0.04504504504504504] )\ntrue\n\njulia> isapprox( construct_x0_mdf(repeat([9.0, 8.0, 7.0, 8.0]; inner=3), 10.0; normalize_value=5.0),\n [4.504504504504504, 0.4504504504504504, 0.04504504504504504] )\ntrue\n\njulia> isapprox( construct_x0_mdf(repeat([9.0, 8.0, 7.0, 8.0]; outer=3), 10.0; normalize_value=5.0),\n construct_x0([9.0, 8.0, 7.0], 10.0; normalize_value=5.0) )\ntrue\n\nExamples – Input Cumulative SFH defined on same logAge grid\n\njulia> isapprox( construct_x0_mdf([9.0, 8.0, 7.0], [0.9009, 0.99099, 1.0], 10.0; normalize_value=5.0),\n [4.5045, 0.4504, 0.0450]; atol=1e-3 )\ntrue\n\njulia> isapprox( construct_x0_mdf([9.0, 8.0, 7.0], [0.1, 0.5, 1.0], 10.0; normalize_value=5.0),\n [0.5, 2.0, 2.5] )\ntrue\n\njulia> isapprox( construct_x0_mdf([7.0, 8.0, 9.0], [1.0, 0.5, 0.1], 10.0; normalize_value=5.0),\n [2.5, 2.0, 0.5] )\ntrue\n\nExamples – Input Cumulative SFH with separate logAge grid\n\njulia> isapprox( construct_x0_mdf([9.0, 8.0, 7.0],\n [[9.0, 8.0, 7.0], [0.9009, 0.99099, 1.0]], 10.0; normalize_value=5.0),\n construct_x0_mdf([9.0, 8.0, 7.0], [0.9009, 0.99099, 1.0], 10.0; normalize_value=5.0) )\ntrue\n\njulia> isapprox( construct_x0_mdf([9.0, 8.0, 7.0],\n [[9.0, 8.5, 8.25, 7.0], [0.9009, 0.945945, 0.9887375, 1.0]], 10.0; normalize_value=5.0),\n construct_x0_mdf([9.0, 8.0, 7.0], [0.9009, 0.99099, 1.0], 10.0; normalize_value=5.0) )\ntrue\n\n\n\n\n\n","category":"function"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"and StarFormationHistories.calculate_coeffs_mdf can be used to calculate per-template stellar mass coefficients (the r_jk above) given the results of a fit (which will be the R_j in the equations above)","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"StarFormationHistories.calculate_coeffs_mdf","category":"page"},{"location":"fitting/linear_amr/#StarFormationHistories.calculate_coeffs_mdf","page":"Linear Age-Metallicity Relation","title":"StarFormationHistories.calculate_coeffs_mdf","text":"calculate_coeffs_mdf(variables::AbstractVector{<:Number},\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number},\n T_max::Number\n α::Number,\n β::Number,\n σ::Number,)\ncalculate_coeffs_mdf(variables::AbstractVector{<:Number},\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number},\n T_max::Number)\n\nCalculates per-model stellar mass coefficients coeffs from the fitting parameters of StarFormationHistories.fit_templates_mdf and StarFormationHistories.hmc_sample_mdf. The variables returned by these functions is of length length(unique(logAge))+3. The first length(logAge) entries are stellar mass coefficients, one per unique entry in logAge. The final three elements are α, β, and σ defining a metallicity evolution such that the mean for element i of unique(logAge) is μ[i] = α * (T_max - exp10(unique(logAge)[i]) / 1e9) + β. The individual weights per each isochrone are then determined via Gaussian weighting with the above mean and the provided σ. The second call signature can be used on samples that include α, β, and σ.\n\nExamples\n\njulia> calculate_coeffs_mdf([1,1], [7,7,8,8], [-2,-1,-2,-1], 12, 0.05, -2.0, 0.2) ≈ [ 0.07673913563377144, 0.9232608643662287, 0.08509904500701986, 0.9149009549929802 ]\ntrue\n\n\n\n\n\n","category":"function"},{"location":"fitting/linear_amr/#Sampling-Methods","page":"Linear Age-Metallicity Relation","title":"Sampling Methods","text":"","category":"section"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"We additionally offer a sampling method for this linear age-metallicity relation using HMC:","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"StarFormationHistories.hmc_sample_mdf","category":"page"},{"location":"fitting/linear_amr/#StarFormationHistories.hmc_sample_mdf","page":"Linear Age-Metallicity Relation","title":"StarFormationHistories.hmc_sample_mdf","text":"hmc_sample_mdf(models::AbstractVector{T},\n data::AbstractMatrix{<:Number},\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number},\n T_max::Number,\n nsteps::Integer;\n composite=Matrix{S}(undef,size(data)),\n rng::Random.AbstractRNG=Random.default_rng(),\n kws...) where {S <: Number, T <: AbstractMatrix{S}}\n\nMethod to sample the posterior of the star formation history coefficients constrained to have a linear age-metallicity relation with the mean metallicity of element i of unique(logAge) being μ[i] = α * (T_max - exp10(unique(logAge)[i]) / 1e9) + β. α is therefore a slope in the units of metallicities per Gyr, and β is the mean metallicity value of stars born at lookback time T_max which has units of Gyr. Individual weights for each isochrone template are then determined via Gaussian weighting with the above mean and the standard deviation σ, which can either be fixed or fit. This method is essentially an analog of StarFormationHistories.fit_templates_mdf that samples the posterior rather than using optimization methods to find the maximum likelihood estimate. This method uses the No-U-Turn sampler as implemented in DynamicHMC.jl, which is a form of dynamic Hamiltonian Monte Carlo.\n\nThis function is designed to work best with a \"grid\" of stellar models, defined by the outer product of N unique entries in logAge and M unique entries in metallicities. See the examples for more information on usage.\n\nArguments\n\nmodels::AbstractVector{<:AbstractMatrix{<:Number}} is a vector of equal-sized matrices that represent the template Hess diagrams for the simple stellar populations that compose the observed Hess diagram.\ndata::AbstractMatrix{<:Number} is the Hess diagram for the observed data.\nlogAge::AbstractVector{<:Number} is the vector containing the effective ages of the stellar populations used to create the templates in models, in units of log10(age [yr]). For example, if a population has an age of 1 Myr, its entry in logAge should be log10(10^6) = 6.0.\nmetallicities::AbstractVector{<:Number} is the vector containing the effective metallicities of the stellar populations used to create the templates in models. This is most commonly a logarithmic abundance like [M/H] or [Fe/H], but you could use a linear abundance like the metal mass fraction Z if you wanted to. There are some notes on the Wikipedia that might be useful.\nT_max::Number is the time at which the age-metallicity relation has a value of \beta in Gyr. For example, if the oldest stellar populations in your isochrone grid are 12 Gyr old, you could set T_max = 12.0. \nnsteps::Integer is the number of samples to draw per chain.\n\nOptional Arguments (NOT YET IMPLEMENTED)\n\nnchains::Integer: If this argument is not provided, this method will return a single chain. If this argument is provided, it will sample nchains chains using all available threads and will return a vector of the individual chains. If nchains is set, composite must be a vector of matrices containing a working matrix for each chain. \n\nKeyword Arguments\n\ncomposite is the working matrix (or vector of matrices, if the argument nchains is provided) that will be used to store the composite Hess diagram model during computation; must be of the same size as the templates contained in models and the observed Hess diagram data.\nrng::Random.AbstractRNG is the random number generator that will be passed to DynamicHMC.jl. If nchains is provided this method will attempt to sample in parallel, requiring a thread-safe rng such as that provided by Random.default_rng(). \n\nAll other keyword arguments kws... will be passed to DynamicHMC.mcmc_with_warmup or DynamicHMC.mcmc_keep_warmup depending on whether nchains is provided.\n\nReturns (NEEDS UPDATED)\n\nIf nchains is not provided, returns a NamedTuple as summarized in DynamicHMC.jl's documentation. In short, the matrix of samples can be extracted and transformed as exp.( result.posterior_matrix ). Statistics about the chain can be obtained with DynamicHMC.Diagnostics.summarize_tree_statistics(result.tree_statistics); you want to see a fairly high acceptance rate (>0.5) and the majority of samples having termination criteria being \"turning.\" See DynamicHMC.jl's documentation for more information.\nIf nchains is provided, returns a vector of length nchains of the same NamedTuples described above. The samples from each chain in the returned vector can be stacked to a single (nsamples, nchains, length(models)) matrix with DynamicHMC.stack_posterior_matrices(result).\n\n\n\n\n\n","category":"function"},{"location":"fitting/linear_amr/#linear_amr_implementation","page":"Linear Age-Metallicity Relation","title":"Implementation","text":"","category":"section"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"While one could optimize the above model without an analytic gradient, such gradient-free methods are typically slower and less robust. One could also calculate the gradient numerically using finite differences or auto-differentiation, but these are still slower than analytic calculations. We will show that the gradient of this hierarchical model is analytic, allowing us to design an efficient optimization scheme.","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"Equation 21 in Dolphin 2001 gives the gradient of our objective function with respect to the underlying coefficients","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"beginaligned\nF equiv - textln mathscrL = sum_i m_i - n_i times left( 1 - textln left( fracn_im_i right) right) \nfracpartial Fpartial r_jk = sum_i c_ijk left( 1 - fracn_im_i right)\nendaligned","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"where c_ijk is the value of template jk in bin i and n_i is bin i of the observed Hess diagram. These partial derivatives are easy to obtain, but we need partials with respect to the per-age-bin fitting parameters R_j. Given the above relation between r_jk and R_j, we can calculate these derivatives as","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"beginaligned\nfracpartial Fpartial R_j = sum_k fracpartial Fpartial r_jk fracpartial r_jkpartial R_j \nfracpartial r_jkpartial R_j = frac textexp left( - frac12 left( frac textMtextH_k - mu_jsigma right)^2 right)sum_k textexp left( - frac12 left( frac textMtextH_k - mu_jsigma right)^2 right) = fracr_jkR_j\nendaligned","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"Then we need only the partial derivatives of the objective function F with respect to the MDF parameters, which in this case are alpha beta sigma. For convenience we will rewrite","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"r_jk = R_j frac textexp left( - frac12 left( frac textMtextH_k - mu_jsigma right)^2 right)sum_k textexp left( - frac12 left( frac textMtextH_k - mu_jsigma right)^2 right) = R_j fracA_jksum_k A_jk","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"as many different types of models can be expressed via this simplified notation by substituting the A_jk with different distributions. This allows us to write ","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"beginaligned\nfracpartial Fpartial beta = sum_jk fracpartial Fpartial r_jk fracpartial r_jkpartial beta \nfracpartial r_jkpartial beta = R_j left( frac1sum_k A_jk fracpartial A_jkpartial beta - fracA_jkleft( sum_k A_jk right)^2 fracpartial sum_k A_jkpartial beta right) \n= fracR_jsum_k A_jk left( fracpartial A_jkpartial beta - fracA_jksum_k A_jk sum_k fracpartial A_jkpartial beta right) \nendaligned","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"Given our specific definition of A_jk being a Gaussian distribution, we have","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"beginaligned\nmu_j = alpha left( T_textmax - t_j right) + beta \nfracpartial A_jkpartial beta = fracpartialpartial beta left textexp left( - frac12 left( frac textMtextH_k - mu_jsigma right)^2 right) right \n= fracA_jksigma^2 left( textMtextH_k - mu_j right)\nendaligned","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"We can now substitute this result into the above expressions to write","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"beginaligned\nfracpartial Fpartial beta = sum_jk fracpartial Fpartial r_jk fracpartial r_jkpartial beta \n= sum_jk fracpartial Fpartial r_jk fracR_jsum_k A_jk left( fracpartial A_jkpartial beta - fracA_jksum_k A_jk sum_k fracpartial A_jkpartial beta right) \n= sum_jk fracpartial Fpartial r_jk fracR_jsigma^2 sum_k A_jk left( A_jk left( textMtextH_k - mu_j right) - fracA_jksum_k A_jk sum_k A_jk left( textMtextH_k - mu_j right) right)\nendaligned","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"It can be shown that the partial derivative of F with respect to alpha is simply","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"fracpartial Fpartial alpha = sum_jk fracpartial Fpartial r_jk fracpartial r_jkpartial alpha = sum_jk fracpartial Fpartial r_jk fracpartial r_jkpartial beta times left( T_textmax - t_j right) ","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"The partial derivative with respect to sigma is slightly more complicated, but we can start identically to how we started above when deriving fracpartial Fpartial beta with","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"beginaligned\nfracpartial Fpartial sigma = sum_jk fracpartial Fpartial r_jk fracpartial r_jkpartial sigma \nfracpartial r_jkpartial sigma = R_j left( frac1sum_k A_jk fracpartial A_jkpartial sigma - fracA_jkleft( sum_k A_jk right)^2 fracpartial sum_k A_jkpartial sigma right) \n= fracR_jsum_k A_jk left( fracpartial A_jkpartial sigma - fracA_jksum_k A_jk sum_k fracpartial A_jkpartial sigma right) \nendaligned","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"Then all we need is","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"fracpartial A_jkpartial sigma = fracA_jk left( textMtextH_k - mu_j right)^2sigma^3","category":"page"},{"location":"fitting/linear_amr/","page":"Linear Age-Metallicity Relation","title":"Linear Age-Metallicity Relation","text":"which we can substitute into the above expressions to find fracpartial Fpartial sigma.","category":"page"},{"location":"simulate/#simulate","page":"Simulating Color-Magnitude Diagrams","title":"Simulating Color-Magnitude Diagrams","text":"","category":"section"},{"location":"simulate/","page":"Simulating Color-Magnitude Diagrams","title":"Simulating Color-Magnitude Diagrams","text":"Modelling observations of resolved stellar populations (e.g., color-magnitude or Hess diagrams) with user-defined star formation histories can be useful for comparison to actual observations, but also enables a number of other scientific activities (e.g., making predictions to motivate observational proposals). To support these uses we offer methods for sampling stellar populations from isochrones using user-defined star formation histories, initial mass functions, and stellar binary models. These methods require data from user-provided isochrones (this package does not provide any), an initial mass function model (such as those provided in InitialMassFunctions.jl), and a model specifying how (or if) to sample binary or multi-star systems. ","category":"page"},{"location":"simulate/","page":"Simulating Color-Magnitude Diagrams","title":"Simulating Color-Magnitude Diagrams","text":"The simplest methods only sample stars from a single stellar population. We provide a method that samples up to a provided stellar mass, generate_stars_mass (e.g., 10^7 textM_odot) and a method that samples up to a provided absolute magnitude generate_stars_mag (e.g., M_V=-10). These are documented under the first subsection below. These methods are single-threaded.","category":"page"},{"location":"simulate/","page":"Simulating Color-Magnitude Diagrams","title":"Simulating Color-Magnitude Diagrams","text":"We also offer methods for sampling populations with complex star formation histories; these are implicitly multi-threaded across the separate populations if you start Julia with multiple threads (e.g., with julia -t 4 or similar). We provide generate_stars_mass_composite for sampling such populations up to a provided stellar mass and generate_stars_mag_composite for sampling such populations up to a provided absolute magnitude. These are documented under the second subsection below.","category":"page"},{"location":"simulate/#Simple-Stellar-Populations","page":"Simulating Color-Magnitude Diagrams","title":"Simple Stellar Populations","text":"","category":"section"},{"location":"simulate/","page":"Simulating Color-Magnitude Diagrams","title":"Simulating Color-Magnitude Diagrams","text":"generate_stars_mass\ngenerate_stars_mag","category":"page"},{"location":"simulate/#StarFormationHistories.generate_stars_mass","page":"Simulating Color-Magnitude Diagrams","title":"StarFormationHistories.generate_stars_mass","text":"generate_stars_mass(mini_vec::AbstractVector{<:Number},\n mags, mag_names::AbstractVector{String},\n limit::Number,\n imf::Distributions.Sampleable{Distributions.Univariate, Distributions.Continuous};\n dist_mod::Number=0,\n rng::Random.AbstractRNG=Random.default_rng(),\n mag_lim::Number = Inf,\n mag_lim_name::String = \"V\",\n binary_model::StarFormationHistories.AbstractBinaryModel =\n StarFormationHistories.RandomBinaryPairs(0.3))\n\nGenerates a random sample of stars from an isochrone defined by the provided initial stellar masses mini_vec, absolute magnitudes mags, and filter names mag_names with total population birth stellar mass limit (e.g., 1e5 solar masses). Initial stellar masses are sampled from the provided imf. \n\nArguments\n\nmini_vec::AbstractVector{<:Number} contains the initial masses (in solar masses) for the stars in the isochrone; must be mutable as we call Interpolations.deduplicate_knots!(mini_vec).\nmags contains the absolute magnitudes from the isochrone in the desired filters corresponding to the same stars as provided in mini_vec. mags is internally interpreted and converted into a standard format by StarFormationHistories.ingest_mags. Valid inputs are:\nmags::AbstractVector{AbstractVector{<:Number}}, in which case the length of the outer vector length(mags) can either be equal to length(mini_vec), in which case the length of the inner vectors must all be equal to the number of filters you are providing, or the length of the outer vector can be equal to the number of filters you are providing, and the length of the inner vectors must all be equal to length(mini_vec); this is the more common use-case.\nmags::AbstractMatrix{<:Number}, in which case mags must be 2-dimensional. Valid shapes are size(mags) == (length(mini_vec), nfilters) or size(mags) == (nfilters, length(mini_vec)), with nfilters being the number of filters you are providing.\nmag_names::AbstractVector{String} contains strings describing the filters you are providing in mags; an example might be [\"B\",\"V\"]. These are used when mag_lim is finite to determine what filter you want to use to limit the faintest stars you want returned.\nlimit::Number gives the total birth stellar mass of the population you want to sample. See the \"Notes\" section on population masses for more information.\nimf::Distributions.Sampleable{Distributions.Univariate, Distributions.Continuous} is a sampleable continuous univariate distribution implementing a stellar initial mass function with a defined rand(rng::Random.AbstractRNG, imf) method to use for sampling masses. All instances of Distributions.ContinuousUnivariateDistribution are also valid. Implementations of commonly used IMFs are available in InitialMassFunctions.jl.\n\nKeyword Arguments\n\ndist_mod::Number=0 is the distance modulus (see StarFormationHistories.distance_modulus) you wish to have added to the returned magnitudes to simulate a population at a particular distance.\nrng::Random.AbstractRNG=Random.default_rng() is the rng instance that will be used to sample the stellar initial masses from imf.\nmag_lim::Number=Inf gives the faintest apparent magnitude for stars you want to be returned in the output. Stars fainter than this magnitude will still be sampled and contribute properly to the total mass of the population, but they will not be returned.\nmag_lim_name::String=\"V\" gives the filter name (as contained in mag_names) to use when considering if a star is fainter than mag_lim. This is unused if mag_lim is infinite.\nbinary_model::StarFormationHistories.AbstractBinaryModel=StarFormationHistories.RandomBinaryPairs(0.3) is an instance of a model for treating binaries; currently provided options are NoBinaries, RandomBinaryPairs, and BinaryMassRatio.\n\nReturns\n\n(sampled_masses, sampled_mags) defined as\n\nsampled_masses::Vector{SVector{N,eltype(imf)}} is a vector containing the initial stellar masses of the stars sampled by sample_system; see that method's documentation for details on format. In short, each StaticArrays.SVector represents one stellar system and each entry in each StaticArrays.SVector is one star in that system. Entries will be 0 when companions could have been sampled but were not (i.e., when using a binary_model that supports multi-star systems). \nsampled_mags::Vector{SVector{N,<:Number}} is a vector containing StaticArrays.SVectors with the multi-band magnitudes of the sampled stars. To get the magnitude of star i in band j, you index as sampled_mags[i][j]. This can be reinterpreted as a 2-dimensional Matrix with reduce(hcat,sampled_mags). \n\nNotes\n\nPopulation Masses\n\nGiven a particular isochrone with an initial mass vector mini_vec, it will never cover the full range of stellar birth masses because stars that die before present-day are not included in the isochrone. However, these stars were born, and so contribute to the total birth mass of the system. There are two ways to properly account for this lost mass when sampling:\n\nSet the upper limit for masses that can be sampled from the imf distribution to a physical value for the maximum birth mass of stars (e.g., 100 solar masses). In this case, these stars will be sampled from imf, and will contribute their masses to the system, but they will not be returned if their birth mass is greater than maximum(mini_vec). This is typically easiest for the user and only results in ∼15% loss of efficiency for 10 Gyr isochrones. This approach is preferred when sampling with binaries.\nSet the upper limit for masses that can be sampled from the imf distribution to maximum(mini_vec) and adjust limit to respect the amount of initial stellar mass lost by not sampling higher mass stars. This can be calculated as new_limit = limit * ( QuadGK.quadgk(x->x*pdf(imf,x), minimum(mini_vec), maximum(mini_vec))[1] / QuadGK.quadgk(x->x*pdf(imf,x), minimum(imf), maximum(imf))[1] ), with the multiplicative factor being the fraction of the population stellar mass contained in stars with initial masses between minimum(mini_vec) and maximum(mini_vec); this factor is the ratio\n\nfracint_a^b m times fracdN(m)dm dmint_0^ m times fracdN(m)dm dm\n\nNote that, if binaries are included, this approach only forms binary pairs between stars whose masses are less than maximum(mini_vec). This is probably not desired, so we recommend the previous approach when including binaries.\n\n\n\n\n\n","category":"function"},{"location":"simulate/#StarFormationHistories.generate_stars_mag","page":"Simulating Color-Magnitude Diagrams","title":"StarFormationHistories.generate_stars_mag","text":"(sampled_masses, sampled_mags) = generate_stars_mag(mini_vec::AbstractVector{<:Number}, mags, mag_names::AbstractVector{String}, absmag::Real, absmag_name::String, imf::Distributions.Sampleable{Distributions.Univariate,Distributions.Continuous}; dist_mod::Number=0, rng::AbstractRNG=default_rng(), mag_lim::Number=Inf, mag_lim_name::String=\"V\", binary_model::StarFormationHistories.AbstractBinaryModel=RandomBinaryPairs(0.3))\n\nGenerates a mock stellar population from an isochrone defined by the provided initial stellar masses mini_vec, absolute magnitudes mags, and filter names mag_names. The population is sampled to a total absolute magnitude absmag::Real (e.g., -7 or -12) in the filter absmag_name::String (e.g., \"V\" or \"F606W\") which is contained in the provided mag_names::AbstractVector{String}. Other arguments are shared with generate_stars_mass, which contains the main documentation.\n\nNotes\n\nPopulation Magnitudes\n\nUnlike when sampling a population to a fixed initial birth mass, as is implemented in generate_stars_mass, when generating a population up to a fixed absolute magnitude, only stars that survive to present-day contribute to the flux of the population. If you choose to limit the apparent magnitude of stars returned by passing the mag_lim and mag_lim_name keyword arguments, stars fainter than your chosen limit will still be sampled and will still contribute their luminosity to the total population, but they will not be contained in the returned output. \n\n\n\n\n\n","category":"function"},{"location":"simulate/#Complex-Stellar-Populations","page":"Simulating Color-Magnitude Diagrams","title":"Complex Stellar Populations","text":"","category":"section"},{"location":"simulate/","page":"Simulating Color-Magnitude Diagrams","title":"Simulating Color-Magnitude Diagrams","text":"generate_stars_mass_composite\ngenerate_stars_mag_composite","category":"page"},{"location":"simulate/#StarFormationHistories.generate_stars_mass_composite","page":"Simulating Color-Magnitude Diagrams","title":"StarFormationHistories.generate_stars_mass_composite","text":"(sampled_masses, sampled_mags) = generate_stars_mass_composite(mini_vec::AbstractVector{<:AbstractVector{<:Number}}, mags::AbstractVector, mag_names::AbstractVector{String}, limit::Number, massfrac::AbstractVector{<:Number}, imf::Sampleable{Univariate,Continuous}; kws...)\n\nGenerates a random sample of stars with a complex star formation history using multiple isochrones. Very similar to generate_stars_mass except the isochrone-related arguments mini_vec and mags should now be vectors of vectors containing the relevant data for the full set of isochrones to be considered. The total birth stellar mass of the sampled population is given by limit. The proportion of this mass allotted to each of the individual isochrones is given by the entries of the massfrac vector. This basically just proportions limit according to massfrac and calls generate_stars_mass for each of the individual stellar populations; as such it is set up to multi-thread across the multiple stellar populations. \n\nArguments\n\nmini_vec::AbstractVector{<:AbstractVector{<:Number}} contains the initial masses (in solar masses) for the stars in each isochrone; the internal vectors must be mutable as we will call Interpolations.deduplicate_knots! on each. The length of mini_vec should be equal to the number of isochrones. \nmags contains the absolute magnitudes from the isochrones in the desired filters corresponding to the same stars as provided in mini_vec. The length of mags should be equal to the number of isochrones. The individual elements of mags are each internally interpreted and converted into a standard format by StarFormationHistories.ingest_mags. The valid formats for the individual elements of mags are:\nAbstractVector{AbstractVector{<:Number}}, in which case the length of the vector length(mags[i]) can either be equal to length(mini_vec[i]), in which case the length of the inner vectors must all be equal to the number of filters you are providing, or the length of the outer vector can be equal to the number of filters you are providing, and the length of the inner vectors must all be equal to length(mini_vec[i]); this is the more common use-case.\nAbstractMatrix{<:Number}, in which case mags[i] must be 2-dimensional. Valid shapes are size(mags[i]) == (length(mini_vec[i]), nfilters) or size(mags[i]) == (nfilters, length(mini_vec[i])), with nfilters being the number of filters you are providing.\nmag_names::AbstractVector{String} contains strings describing the filters you are providing in mags; an example might be [\"B\",\"V\"]. These are used when mag_lim is finite to determine what filter you want to use to limit the faintest stars you want returned. These are assumed to be the same for all isochrones.\nlimit::Number gives the total birth stellar mass of the population you want to sample. \nmassfrac::AbstractVector{<:Number} is vector giving the relative fraction of mass allotted to each individual stellar population; length must be equal to the length of mini_vec and mags. \nimf::Distributions.Sampleable{Distributions.Univariate, Distributions.Continuous} is a sampleable continuous univariate distribution implementing a stellar initial mass function with a defined rand(rng::Random.AbstractRNG, imf) method to use for sampling masses. All instances of Distributions.ContinuousUnivariateDistribution are also valid. Implementations of commonly used IMFs are available in InitialMassFunctions.jl.\n\nKeyword Arguments\n\nAll keyword arguments kws... are passed to generate_stars_mass; you should refer to that method's documentation for more information. \n\nReturns\n\nsampled_masses::Vector{Vector{SVector{N,eltype(imf)}}} is a vector of vectors containing the initial stellar masses of the sampled stars. The outer vectors are separated by the isochrone the stars were generated from; i.e., all of sampled_masses[1] were sampled from mini_vec[1] and so on. These can be concatenated into a single vector with reduce(vcat,sampled_masses). The format of the contained StaticArrays.SVectors are as output from sample_system; see that method's documentation for more details. \nsampled_mags::Vector{Vector{SVector{N,<:Number}}} is a vector of vectors containing StaticArrays.SVectors with the multi-band magnitudes of the sampled stars. The outer vectors are separated by the isochrone the stars were generated from; i.e. all of sampled_mags[1] were sampled from mags[1] and so on. To get the magnitude of star i in band j sampled from isochrone k, you would do sampled_mags[k][i][j]. This can be concatenated into a Vector{SVector} with reduce(vcat,sampled_mags) and a 2-D Matrix with reduce(hcat,reduce(vcat,sampled_mags)). \n\n\n\n\n\n","category":"function"},{"location":"simulate/#StarFormationHistories.generate_stars_mag_composite","page":"Simulating Color-Magnitude Diagrams","title":"StarFormationHistories.generate_stars_mag_composite","text":"(sampled_masses, sampled_mags) = generate_stars_mag_composite(mini_vec::AbstractVector{<:AbstractVector{<:Number}}, mags::AbstractVector, mag_names::AbstractVector{String}, absmag::Number, absmag_name::String, fracs::AbstractVector{<:Number}, imf::Sampleable{Univariate,Continuous}; frac_type::String=\"lum\", kws...)\n\nGenerates a random sample of stars with a complex star formation history using multiple isochrones. Very similar to generate_stars_mag except the isochrone-related arguments mini_vec and mags should now be vectors of vectors containing the relevant data for the full set of isochrones to be considered. The total absolute magnitude of the sampled population is given by absmag. The proportion of the luminosity allotted to each of the individual isochrones is given by the entries of the frac vector. This basically just proportions the luminosity according to frac and calls generate_stars_mag for each of the individual stellar populations; as such it is set up to multi-thread across the multiple stellar populations. \n\nArguments\n\nmini_vec::AbstractVector{<:AbstractVector{<:Number}} contains the initial masses (in solar masses) for the stars in each isochrone; the internal vectors must be mutable as we will call Interpolations.deduplicate_knots! on each. The length of mini_vec should be equal to the number of isochrones. \nmags contains the absolute magnitudes from the isochrones in the desired filters corresponding to the same stars as provided in mini_vec. The length of mags should be equal to the number of isochrones. The individual elements of mags are each internally interpreted and converted into a standard format by StarFormationHistories.ingest_mags. The valid formats for the individual elements of mags are:\nAbstractVector{AbstractVector{<:Number}}, in which case the length of the vector length(mags[i]) can either be equal to length(mini_vec[i]), in which case the length of the inner vectors must all be equal to the number of filters you are providing, or the length of the outer vector can be equal to the number of filters you are providing, and the length of the inner vectors must all be equal to length(mini_vec[i]); this is the more common use-case.\nAbstractMatrix{<:Number}, in which case mags[i] must be 2-dimensional. Valid shapes are size(mags[i]) == (length(mini_vec[i]), nfilters) or size(mags[i]) == (nfilters, length(mini_vec[i])), with nfilters being the number of filters you are providing.\nmag_names::AbstractVector{String} contains strings describing the filters you are providing in mags; an example might be [\"B\",\"V\"]. These are used when mag_lim is finite to determine what filter you want to use to limit the faintest stars you want returned. These are assumed to be the same for all isochrones.\nabsmag::Number gives the total absolute magnitude of the complex population to be sampled. \nfracs::AbstractVector{<:Number} is a vector giving the relative fraction of luminosity or mass (determined by the frac_type keyword argument) allotted to each individual stellar population; length must be equal to the length of mini_vec and mags. \nimf::Distributions.Sampleable{Distributions.Univariate, Distributions.Continuous} is a sampleable continuous univariate distribution implementing a stellar initial mass function with a defined rand(rng::Random.AbstractRNG, imf) method to use for sampling masses. All instances of Distributions.ContinuousUnivariateDistribution are also valid. Implementations of commonly used IMFs are available in InitialMassFunctions.jl.\n\nKeyword Arguments\n\nfrac_type::String either \"lum\", in which case fracs is assumed to contain the relative luminosity fractions for each individual isochrone, or \"mass\", in which case it is assumed that fracs contains mass fractions (\"mass\" is not yet implemented). \n\nAll other keyword arguments kws... are passed to generate_stars_mag; you should refer to that method's documentation for more information. \n\nReturns\n\nsampled_masses::Vector{Vector{SVector{N,eltype(imf)}}} is a vector of vectors containing the initial stellar masses of the sampled stars. The outer vectors are separated by the isochrone the stars were generated from; i.e., all of sampled_masses[1] were sampled from mini_vec[1] and so on. These can be concatenated into a single vector with reduce(vcat,sampled_masses). The format of the contained StaticArrays.SVectors are as output from sample_system; see that method's documentation for more details. \nsampled_mags::Vector{Vector{SVector{N,<:Number}}} is a vector of vectors containing StaticArrays.SVectors with the multi-band magnitudes of the sampled stars. The outer vectors are separated by the isochrone the stars were generated from; i.e. all of sampled_mags[1] were sampled from mags[1] and so on. To get the magnitude of star i in band j sampled from isochrone k, you would do sampled_mags[k][i][j]. This can be concatenated into a Vector{SVector} with reduce(vcat,sampled_mags) and a 2-D Matrix with reduce(hcat,reduce(vcat,sampled_mags)). \n\n\n\n\n\n","category":"function"},{"location":"simulate/#Observational-Effects","page":"Simulating Color-Magnitude Diagrams","title":"Observational Effects","text":"","category":"section"},{"location":"simulate/","page":"Simulating Color-Magnitude Diagrams","title":"Simulating Color-Magnitude Diagrams","text":"The output produced from the above methods are clean in the sense that they do not include any observational effects like photometric error or incompleteness. These effects should be implemented in a post-processing step. We provide a simple method model_cmd that accepts user-defined photometric error and completeness functions and applies them to the initial catalog, returning a Monte Carlo realization of a possible observed catalog. This method assumes Gaussian photometric errors and that the photometric error and completeness functions are separable by filter – these assumptions are not applicable for all types of data, but the source code for the method is exceedingly simple (~20 lines) and should provide an example for how you could write a similar method that more accurately reflects your data.","category":"page"},{"location":"simulate/","page":"Simulating Color-Magnitude Diagrams","title":"Simulating Color-Magnitude Diagrams","text":"model_cmd","category":"page"},{"location":"simulate/#StarFormationHistories.model_cmd","page":"Simulating Color-Magnitude Diagrams","title":"StarFormationHistories.model_cmd","text":"new_mags [, good_idxs] = model_cmd(mags::AbstractVector{<:AbstractVector{<:Number}}, errfuncs, completefuncs; rng::Random.AbstractRNG=Random.default_rng(), ret_idxs::Bool=false)\n\nSimple method for modelling photometric error and incompleteness to \"mock observe\" a pure catalog of stellar photometry, such as those produced by generate_stars_mass and generate_stars_mag. This method assumes Gaussian photometric errors and that the photometric error and completeness functions are separable by filter. \n\nArguments\n\nmags::AbstractVector{<:AbstractVector{<:Number}}: a vector of vectors giving the magnitudes of each star to be modelled. The first index is the per-star index and the second index is the per-filter index (so mags[10][2] would give the magnitude of the tenth star in the second filter). This is the same format as the magnitudes returned by generate_stars_mass and generate_stars_mag; to use output from the composite versions, you must first reduce(vcat,mags) before passing to this function.\nerrfuncs: an iterable (typically a vector or tuple) of callables (typically functions or interpolators) with length equal to the number of filters contained in the elements of mags. This iterable must contain callables that, when called with the associated magnitudes from mags, will return the expected 1-σ photometric error at that magnitude. The organization is such that the photometric error for star i in band j is σ_ij = errfuncs[j](mags[i][j]). \ncompletefuncs: an iterable (typically a vector or tuple) of callables (typically functions or interpolators) with length equal to the number of filters contained in the elements of mags. This iterable must contain callables that, when called with the associated magnitudes from mags, will return the probability that a star with that magnitude in that band will be found in your color-magnitude diagram (this should include the original detection probability and any post-detection quality, morphology, or other cuts). The organization is such that the detection probability for star i in band j is c_ij = completefuncs[j](mags[i][j]).\n\nKeyword Arguments\n\nrng::AbstractRNG=Random.default_rng(): The object to use for random number generation.\nret_idxs::Bool: whether to return the indices of the input mags for the stars that were successfully \"observed\" and are represented in the output new_mags.\n\nReturns\n\nnew_mags: an object similar to mags (i.e., a Vector{Vector{<:Number}}, Vector{SVector{N,<:Number}}, etc.) containing the magnitudes of the mock-observed stars. This will be shorter than the provided mags vector as we are modelling photometric incompleteness, and the magnitudes will also have random photometric errors added to them. This can be reinterpreted as a 2-dimensional Matrix with reduce(hcat,new_mags).\ngood_idxs: if ret_idxs is true, the vector of indices into the input mags for the stars that were successfully \"observed\" and are represented in the output new_mags.\n\nNotes\n\nThis is a simple implementation that seeks to show a simple example of how one can post-process catalogs of \"pure\" stars from methods like generate_stars_mass and generate_stars_mag to include observational effects. This method assumes Gaussian photometric errors, which may not, in general, be accurate. It also assumes that the total detection probability can be modelled as the product of the single-filter detection probabilities as computed by completefuncs (i.e., that the completeness functions are separable across filters). This can be a reasonable assumption when you have separate photometric catalogs derived for each filter and you only collate them afterwards, but it is generally not a good assumption for detection algorithms that operate on simultaneously on multi-band photometry – the completeness functions for these types of algorithms are generally not separable.\n\n\n\n\n\n","category":"function"},{"location":"simulate/#Developer-Internals","page":"Simulating Color-Magnitude Diagrams","title":"Developer Internals","text":"","category":"section"},{"location":"simulate/","page":"Simulating Color-Magnitude Diagrams","title":"Simulating Color-Magnitude Diagrams","text":"StarFormationHistories.ingest_mags\nStarFormationHistories.sort_ingested\nStarFormationHistories.mass_limits","category":"page"},{"location":"simulate/#StarFormationHistories.ingest_mags","page":"Simulating Color-Magnitude Diagrams","title":"StarFormationHistories.ingest_mags","text":"new_mags = ingest_mags(mini_vec::AbstractVector, mags::AbstractVector{T}) where {S <: Number, T <: AbstractVector{S}}\nnew_mags = ingest_mags(mini_vec::AbstractVector, mags::AbstractMatrix{S}) where S <: Number\n\nReinterprets provided mags to be in the correct format for input to Interpolations.interpolate.\n\nReturns\n\nnew_mags::Base.ReinterpretArray{StaticArrays.SVector}: a length(mini_vec) vector of StaticArrays.SVectors containing the same data as mags but formatted for input to Interpolations.interpolate.\n\n\n\n\n\n","category":"function"},{"location":"simulate/#StarFormationHistories.sort_ingested","page":"Simulating Color-Magnitude Diagrams","title":"StarFormationHistories.sort_ingested","text":"(new_mini_vec, new_mags) = sort_ingested(mini_vec::AbstractVector, mags::AbstractVector)\n\nTakes mini_vec and mags and ensures that mini_vec is sorted (sometimes in PARSEC isochrones they are not) and calls Interpolations.deduplicate_knots! on mini_vec to ensure there are no repeat entries. Arguments must satisfy length(mini_vec) == length(mags). \n\n\n\n\n\n","category":"function"},{"location":"simulate/#StarFormationHistories.mass_limits","page":"Simulating Color-Magnitude Diagrams","title":"StarFormationHistories.mass_limits","text":"(mmin, mmax) = mass_limits(mini_vec::AbstractVector{<:Number}, mags::AbstractVector{T},\n mag_names::AbstractVector{String}, mag_lim::Number,\n mag_lim_name::String) where T <: AbstractVector{<:Number}\n\nCalculates initial mass limits that reflect a given faint-end magnitude limit.\n\nArguments\n\nmini_vec::AbstractVector{<:Number}: a length nstars vector containing initial stellar masses.\nmags::AbstractVector{<:AbstractVector{<:Number}}: a length nstars vector, with each element being a length nfilters vector giving the magnitudes of each star in the filters mag_names.\nmag_names::AbstractVector{String}: a vector giving the names of each filter as strings.\nmag_lim::Number: the faint-end magnitude limit you wish to use.\nmag_lim_name::String: the name of the filter in which mag_lim is to be applied. Must be contained in mag_names.\n\nReturns\n\nmmin::eltype(mini_vec): the initial mass corresponding to your requested mag_lim in the filter mag_lim_name. If all stars provided are brighter than your requested mag_lim, then this will be equal to minimum(mini_vec).\nmmax::eltype(mini_vec): the maximum valid mass in mini_vec; simply maximum(mini_vec).\n\nExamples\n\njulia> mass_limits([0.05,0.1,0.2,0.3], [[4.0],[3.0],[2.0],[1.0]], [\"F090W\"], 2.5, \"F090W\")\n(0.15, 0.3)\n\njulia> mass_limits([0.05,0.1,0.2,0.3], [[4.0,3.0],[3.0,2.0],[2.0,1.0],[1.0,0.0]], [\"F090W\",\"F150W\"], 2.5, \"F090W\")\n(0.15, 0.3)\n\n\n\n\n\n","category":"function"},{"location":"fitting/internals/#Low-Level-Functions","page":"Low-Level Functions","title":"Low-Level Functions","text":"","category":"section"},{"location":"fitting/internals/","page":"Low-Level Functions","title":"Low-Level Functions","text":"StarFormationHistories.composite!\nStarFormationHistories.loglikelihood\nStarFormationHistories.∇loglikelihood\nStarFormationHistories.∇loglikelihood!\nStarFormationHistories.fg!\nStarFormationHistories.truncate_relweights\nStarFormationHistories.calculate_edges","category":"page"},{"location":"fitting/internals/#StarFormationHistories.composite!","page":"Low-Level Functions","title":"StarFormationHistories.composite!","text":" composite!(composite::AbstractMatrix{<:Number},\n coeffs::AbstractVector{<:Number},\n models::AbstractVector{T})\n where T <: AbstractMatrix{<:Number}\n\nUpdates the composite matrix in place with the linear combination of sum( coeffs .* models ); this is equation 1 in Dolphin 2002, m_i = sum_j r_j c_ij.\n\nExamples\n\njulia> C = zeros(5,5);\njulia> models = [rand(size(C)...) for i in 1:5];\njulia> coeffs = rand(length(models));\njulia> composite!(C, coeffs, models);\njulia> C ≈ sum( coeffs .* models)\ntrue\n\n\n\n\n\ncomposite!(composite::AbstractVector{<:Number},\n coeffs::AbstractVector{<:Number},\n models::AbstractMatrix{<:Number})\n\nUpdates the composite vector with the matrix-vector product of models * coeffs. This is equation 1 in Dolphin 2002, m_i = sum_j r_j c_ij.\n\nExamples\n\njulia> hist_size = (5,10);\njulia> models = reduce(hcat,rand(prod(hist_size)) for i in 1:20);\njulia> coeffs = rand(length(axes(models,2)));\njulia> C = zeros(length(axes(models,1)));\njulia> composite!(C, coeffs, models);\njulia> C ≈ models * coeffs\ntrue\n\nNotes\n\nWhile the other call signature for this function more closely mirrors the natural data structure for Hess diagrams (2D matrices for composite and each entry in models), this method operates on the same data but flattened. Thus composite becomes a vector rather than a matrix and models becomes a single matrix rather than a vector of matrices. The method StarFormationHistories.stack_models is provided to stack the models into this format. This data layout enables us to use the highly optimized LinearAlgebra.mul! function to perform the matrix-vector product which typically achieves >30% speedup relative to the more natural formulation. Additionally, as mul! will typically call to a BLAS matrix-vector product function like gemv! for our use-case, we can switch out Julia's default OpenBLAS at runtime for other BLAS libraries with Julia bindings like MKL and Apple Accelerate, enabling even greater performance improvements.\n\n\n\n\n\n","category":"function"},{"location":"fitting/internals/#StarFormationHistories.loglikelihood","page":"Low-Level Functions","title":"StarFormationHistories.loglikelihood","text":"loglikelihood(composite::AbstractArray{<:Number}, data::AbstractArray{<:Number})\n\nReturns the logarithm of the Poisson likelihood ratio given by equation 10 in Dolphin 2002,\n\ntextln mathscrL = sum_i -m_i + n_i times left( 1 - textln left( fracn_im_i right) right)\n\nwith composite being the complex Hess model diagram m_i (see StarFormationHistories.composite!) and data being the observed Hess diagram n_i.\n\nPerformance Notes\n\n~18.57 μs for composite=Matrix{Float64}(undef,99,99) and data=similar(composite).\n~20 μs for composite=Matrix{Float64}(undef,99,99) and data=Matrix{Int64}(undef,99,99).\n~9.3 μs for composite=Matrix{Float32}(undef,99,99) and data=similar(composite).\n~9.6 μs for composite=Matrix{Float32}(undef,99,99) and data=Matrix{Int64}(undef,99,99).\n\n\n\n\n\nloglikelihood(coeffs::AbstractVector{<:Number}, models::AbstractVector{T}, data::AbstractMatrix{<:Number}) where T <: AbstractMatrix{<:Number}\nloglikelihood(coeffs::AbstractVector{<:Number}, models::AbstractMatrix{<:Number}, data::AbstractVector{<:Number})\n\nReturns the logarithm of the Poisson likelihood ratio, but constructs the complex Hess diagram model as sum(coeffs .* models) rather than taking composite directly as an argument. Second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\n\n\n\n\n","category":"function"},{"location":"fitting/internals/#StarFormationHistories.∇loglikelihood","page":"Low-Level Functions","title":"StarFormationHistories.∇loglikelihood","text":"∇loglikelihood(model::AbstractArray{<:Number}, composite::AbstractArray{<:Number}, data::AbstractArray{<:Number})\n\nReturns the partial derivative of the logarithm of the Poisson likelihood ratio (StarFormationHistories.loglikelihood) with respect to the coefficient r_j on the provided model. If the complex Hess diagram model is m_i = sum_j r_j c_ij, then model is c_ij, and this function computes the partial derivative of textlog mathscrL with respect to the coefficient r_j. This is given by equation 21 in Dolphin 2002,\n\nfracpartial textlog mathscrLpartial r_j = sum_i c_ij left( fracn_im_i - 1 right)\n\nwhere n_i is bin i of the observed Hess diagram data. \n\nPerformance Notes\n\n~4.1 μs for model, composite, data all being Matrix{Float64}(undef,99,99).\n~1.3 μs for model, composite, data all being Matrix{Float32}(undef,99,99). \n\n\n\n\n\n∇loglikelihood(models::AbstractVector{T}, composite::AbstractMatrix{<:Number}, data::AbstractMatrix{<:Number}) where T <: AbstractMatrix{<:Number}\n∇loglikelihood(models::AbstractMatrix{<:Number}, composite::AbstractVector{<:Number}, data::AbstractVector{<:Number})\n\nComputes the gradient of the logarithm of the Poisson likelihood ratio with respect to the coefficients by calling the single-model ∇loglikelihood for every model in models. Second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\n\n\n\n\n∇loglikelihood(coeffs::AbstractVector{<:Number}, models::AbstractVector{T}, data::AbstractMatrix{<:Number}) where T <: AbstractMatrix{<:Number}\n∇loglikelihood(coeffs::AbstractVector{<:Number}, models::AbstractMatrix{<:Number}, data::AbstractVector{<:Number})\n\nForms the composite matrix from coefficients coeffs and model templates models and returns the gradient of the loglikelihood with respect to the coefficients. Second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\n\n\n\n\n","category":"function"},{"location":"fitting/internals/#StarFormationHistories.∇loglikelihood!","page":"Low-Level Functions","title":"StarFormationHistories.∇loglikelihood!","text":" ∇loglikelihood!(G::AbstractVector, composite::AbstractMatrix{<:Number}, models::AbstractVector{S}, data::AbstractMatrix{<:Number}) where S <: AbstractMatrix{<:Number}\n\nEfficiently computes the gradient of StarFormationHistories.loglikelihood with respect to all coefficients by updating G with the gradient. This will overwrite composite with the result of 1 .- (data ./ composite) so it shouldn't be reused after being passed to this function. \n\nArguments\n\nG::AbstractVector is the vector that will be mutated in-place with the computed gradient values.\nmodels::AbstractVector{<:AbstractMatrix{<:Number}} is the vector of matrices giving the model Hess diagrams.\ncomposite::AbstractMatrix{<:Number} is a matrix that contains the composite model sum(coeffs .* models).\ndata::AbstractMatrix{<:Number} contains the observed Hess diagram that is being fit.\n\n\n\n\n\nG = ∇loglikelihood!(G::AbstractVector, composite::AbstractVector{<:Number}, models::AbstractMatrix{<:Number}, data::AbstractVector{<:Number})\n\nUpdates and returns G with the gradient of the loglikelihood with respect to all coefficients. This call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\n\n\n\n\n","category":"function"},{"location":"fitting/internals/#StarFormationHistories.fg!","page":"Low-Level Functions","title":"StarFormationHistories.fg!","text":"-logL = fg!(F, G, coeffs::AbstractVector{<:Number}, models::AbstractVector{T}, data::AbstractMatrix{<:Number}, composite::AbstractMatrix{<:Number}) where T <: AbstractMatrix{<:Number}\n-logL = fg!(F, G, coeffs::AbstractVector{<:Number}, models::AbstractMatrix{<:Number}, data::AbstractVector{<:Number}, composite::AbstractVector{<:Number})\n\nComputes -loglikelihood and its gradient simultaneously for use with Optim.jl and other optimization APIs. See documentation here. Second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\n\n\n\n\n","category":"function"},{"location":"fitting/internals/#StarFormationHistories.truncate_relweights","page":"Low-Level Functions","title":"StarFormationHistories.truncate_relweights","text":"keep_idx::Vector{Int} = truncate_relweights(relweightsmin::Number, relweights::AbstractVector{<:Number}, logAge::AbstractVector{<:Number})\n\nMethod to truncate an isochrone grid with log10(age [yr]) values logAge and relative weights relweights due to an age-metallicity relation to only include models with relweights greater than relweightsmin times the maximum relative weight for each unique entry in logAge. The input vectors are the same as those for StarFormationHistories.fixed_amr, which includes more information. Returns a vector of the indices into relweights and logAge of the isochrone models whose relative weights are significant given the provided relweightsmin.\n\nExamples\n\nWhen using a fixed input age-metallicity relation as enabled by, for example, StarFormationHistories.fixed_amr, only the star formation rate (or total stellar mass) coefficients need to be fit, as the metallicity distribution is no longer a free parameter in the model. As such, the relative weights of each model with identical logAge but different metallicities only need to be computed once at the start of the optimization. As the metallicity distribution is not a free parameter, it is also possible to truncate the list of models to only those that contribute significantly to the final composite model to improve runtime performance. That is what this method does.\n\nA simple isochrone grid will be two-dimensional, encompassing age and metallicity. Consider a subset of the model grid with the same age such that unique(logAge) = [10.0] but a series of different metallicities, metallicities = -2.5:0.25:0. If we model the metallicity distribution function for this age as having a mean [M/H] of -2.0 and a Gaussian spread of 0.2 dex, then the relative weights of these models can be approximated as \n\nimport Distributions: Normal, pdf\nmetallicities = -2.5:0.25:0\nrelweights = pdf.(Normal(-2.0, 0.2), metallicities)\nrelweights ./= sum(relweights) # Normalize the relative weights to unity sum\n\n11-element Vector{Float64}:\n 0.021919934465195145\n 0.2284109622221623\n 0.4988954088848224\n 0.2284109622221623\n 0.021919934465195145\n 0.0004409368867815243\n 1.8592101580561089e-6\n 1.6432188478108614e-9\n 3.0442281937632026e-13\n 1.1821534989089337e-17\n 9.622444440364979e-23\n\nSeveral of these models with very low relative weights are unlikely to contribute significantly to the final composite model. We can select out only the significant ones with, say, relative weights greater than 10% of the maximum as StarFormationHistories.truncate_relweights(0.1, relweights, fill(10.0,length(metallicities))) which will return indices into relweights whose values are greater than 0.1 * maximum(relweights) = 0.04988954088848224,\n\n3-element Vector{Int64}:\n 2\n 3\n 4\n\nwhich correspond to relweights[2,3,4] = [ 0.2284109622221623, 0.4988954088848224, 0.2284109622221623 ]. If we use only these 3 templates in the fit, instead of the original 11, we will achieve a speedup of almost 4x with a minor loss in precision which, in most cases, will be less than the numerical uncertainties on the individual star formation rate parameters. However, as fits of these sort are naturally quite fast, we recommend use of this type of truncation only in applications where many fits are required (e.g., Monte Carlo experiments). For most applications, this level of optimization is not necessary.\n\n\n\n\n\n","category":"function"},{"location":"fitting/internals/#StarFormationHistories.calculate_edges","page":"Low-Level Functions","title":"StarFormationHistories.calculate_edges","text":"calculate_edges(edges, xlim, ylim, nbins, xwidth, ywidth)\n\nFunction to calculate the bin edges for 2D histograms. Returns (xbins, ybins) with both entries being ranges.\n\nKeyword Arguments\n\nedges is a tuple of ranges defining the left-side edges of the bins along the x-axis (edges[1]) and the y-axis (edges[2]). Example: (-1.0:0.1:1.5, 22:0.1:27.2). If edges is provided, it will simply be returned.\nxlim is a length-2 indexable object (e.g., a Vector{Float64} or NTuple{2,Float64)) giving the lower and upper bounds on the x-axis corresponding to the provided colors array. Example: [-1.0, 1.5]. This is only used if edges==nothing.\nylim is like xlim but for the y-axis corresponding to the provided mags array. Example [25, 20]. This is only used if edges==nothing.\nnbins::NTuple{2,<:Integer} is a 2-tuple of integers providing the number of bins to use along the x- and y-axes. This is only used if edges==nothing.\nxwidth is the bin width along the x-axis for the colors array. This is only used if edges==nothing and nbins==nothing. Example: 0.1. \nywidth is like xwidth but for the y-axis corresponding to the provided mags array. Example: 0.1.\n\n\n\n\n\n","category":"function"},{"location":"fitting/log_amr/#Logarithmic-Age-Metallicity-Relation","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"","category":"section"},{"location":"fitting/log_amr/#Definition","page":"Logarithmic Age-Metallicity Relation","title":"Definition","text":"","category":"section"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"This model differs from the linear age-metallicity relation (AMR) in its definition of the mean metallicity at lookback time t. In the linear model, we defined the mean metallicity as langle textMtextH rangle (t) = alpha left( T_textmax - t_j right) + beta, whereas in this model we define the metal mass fraction Z to be linear with lookback time, such that [M/H] scales logarithmically with lookback time,","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"beginaligned\nlangle Z rangle (t) = alpha left( T_textmax - t right) + beta \nlangle textMtextHrangle (t) equiv textlog left( fraclangle Z rangle left(tright)X right) - textlog left( fracZ_odotX_odot right)\nendaligned","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"with T_textmax being the earliest lookback time under consideration, such that langle Z rangle (T_textmax) = beta. We choose this parameterization so that positive alpha and beta result in an age-metallicity relation that is monotonically increasing with decreasing lookback time t. We model the spread in metallicities at fixed t as Gaussian in [M/H], identically to how it is modelled in the linear AMR case. This implies the spread is asymmetric in Z; this can be seen in the output of examples/log_amr/log_amr_example.jl, shown below, which illustrates the relative weights due to a logarithmic AMR across a grid of ages and metallicities. The per-model coefficients implied by a such a logarithmic AMR can be calculated with calculate_coeffs_logamr.","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"ENV[\"GKSwstype\"] = \"100\" # https://discourse.julialang.org/t/generation-of-documentation-fails-qt-qpa-xcb-could-not-connect-to-display/60988 # hide\ninclude(\"../../../examples/log_amr/log_amr_example.jl\") # hide\nsavefig(\"log_amr_plot.svg\"); nothing # hide","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"(Image: Visualization of the relative weights across a grid of logAge and metallicity under a logarithmic age-metallicity relation.)","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"StarFormationHistories.calculate_coeffs_logamr","category":"page"},{"location":"fitting/log_amr/#StarFormationHistories.calculate_coeffs_logamr","page":"Logarithmic Age-Metallicity Relation","title":"StarFormationHistories.calculate_coeffs_logamr","text":"calculate_coeffs_logamr(variables::AbstractVector{<:Number},\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number},\n T_max::Number\n [, α::Number, β::Number, σ::Number];\n MH_func = StarFormationHistories.MH_from_Z)\n\nCalculates per-model stellar mass coefficients coeffs from the fitting parameters of StarFormationHistories.fit_templates_logamr and StarFormationHistories.hmc_sample_logamr. The variables returned by these functions is of length length(unique(logAge))+3. The first length(logAge) entries are stellar mass coefficients, one per unique entry in logAge. The final three elements are α, β, and σ defining a metallicity evolution such that the mean metal mass fraction Z for element i of unique(logAge) is μ_Z[i] = α * (T_max - exp10(unique(logAge)[i]) / 1e9) + β. This is converted to a mean metallicity in [M/H] via the provided callable keyword argument MH_func which defaults to StarFormationHistories.MH_from_Z. The individual weights per each isochrone are then determined via Gaussian weighting with the above mean [M/H] and the provided σ in dex. The provided metallicities vector should be in [M/H]. \n\nNotes\n\nPhysically, the metal mass fraction Z must always be positive. Under the above model, this means α and β must be greater than or equal to 0. With σ being a Gaussian width, it must be positive.\nIf T_max is lower than the maximum of the logAge argument you provide, a warning will be raised which may be ignored if it does not result in any of the mean metal mass fractions Z being less than 0 for any of the provided logAge.\nAn error will be thrown if the provided age-metallicity relation variables (α, β) and T_max argument result in a mean metal mass fraction less than 0 for any time in the provided logAge vector. \n\n\n\n\n\n","category":"function"},{"location":"fitting/log_amr/#Fitting-Functions","page":"Logarithmic Age-Metallicity Relation","title":"Fitting Functions","text":"","category":"section"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"The main function we provide to fit star formation histories to Hess diagrams under the logarithmic age-metallicity relation is fit_templates_logamr. This function operates similarly to the fitting function for the linear AMR model, fit_templates_mdf. ","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"StarFormationHistories.fit_templates_logamr","category":"page"},{"location":"fitting/log_amr/#StarFormationHistories.fit_templates_logamr","page":"Logarithmic Age-Metallicity Relation","title":"StarFormationHistories.fit_templates_logamr","text":"result = fit_templates_logamr(models::AbstractVector{<:AbstractMatrix{S}},\n data::AbstractMatrix{<:Number},\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number},\n T_max::Number\n [, σ::Number];\n x0 = vcat(construct_x0_mdf(logAge, convert(S,13.7)),\n [1e-4, 5e-5, 0.2]),\n MH_func = StarFormationHistories.MH_from_Z,\n MH_deriv_Z = StarFormationHistories.dMH_dZ,\n kws...) where {S <: Number}\nresult = fit_templates_logamr(models::AbstractMatrix{S},\n data::AbstractVector{<:Number},\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number}\n T_max::Number\n [, σ::Number];\n x0 = vcat(construct_x0_mdf(logAge, convert(S,13.7)),\n [1e-4, 5e-5, 0.2]),\n MH_func = StarFormationHistories.MH_from_Z,\n MH_deriv_Z = StarFormationHistories.dMH_dZ,\n kws...) where {S <: Number}\n\nMethod that fits a linear combination of the provided Hess diagrams models to the observed Hess diagram data, constrained to have a logarithmic age-metallicity relation with the mean metal mass fraction μ_Z of element i of unique(logAge) being μ_Z[i] = α * (T_max - exp10(unique(logAge)[i]) / 1e9) + β. This is converted to a mean metallicity in [M/H] via the provided callable keyword argument MH_func which defaults to MH_from_Z. α is therefore a slope in the units of inverse Gyr, and β is the mean metal mass fraction of stars born at the earliest valid lookback time, determined by keyword argument T_max which has units of Gyr. Individual weights for each isochrone template are then determined via Gaussian weighting with the above mean [M/H] and the standard deviation σ in dex, which can either be fixed or fit.\n\nThis function is designed to work best with a \"grid\" of stellar models, defined by the outer product of N unique entries in logAge and M unique entries in metallicities. See the examples for more information on usage.\n\nThe second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\nArguments\n\nmodels are the template Hess diagrams for the simple stellar populations that compose the observed Hess diagram. \ndata is the Hess diagram for the observed data. \nlogAge::AbstractVector{<:Number} is the vector containing the effective ages of the stellar populations used to create the templates in models, in units of log10(age [yr]). For example, if a population has an age of 1 Myr, its entry in logAge should be log10(10^6) = 6.0.\nmetallicities::AbstractVector{<:Number} is the vector containing the effective metallicities of the stellar populations used to create the templates in models. This should be a logarithmic abundance like [M/H] or [Fe/H].\nT_max::Number is the maximum lookback time in Gyr for which the age-metallicity relation is to be valid. In most cases this should just be the maximum of the logAge vector argument maximum(logAge) or slightly larger. By definition, this is the lookback time at which μ_Z = β. \n\nOptional Arguments\n\nIf provided, σ::Number is the fixed width of the Gaussian the defines the metallicity distribution function (MDF) at fixed logAge. If this argument is omitted, σ will be a free parameter in the fit. \n\nKeyword Arguments\n\nx0 is the vector of initial guesses for the stellar mass coefficients per unique entry in logAge, plus the variables that define the metallicity evolution model. You should basically always be calculating and passing this keyword argument. We provide construct_x0_mdf to prepare the first part of x0 assuming constant star formation rate, which is typically a good initial guess. You then have to concatenate that result with an initial guess for the metallicity evolution parameters. For example, x0=vcat(construct_x0_mdf(logAge, 13.7; normalize_value=1e4), [1e-4, 5e-5, 0.2]), where logAge is a valid argument for this function (see above), and the initial guesses on the parameters are [α, β, σ] = [1e-4, 5e-5, 0.2]. If you provide σ as an optional argument, then you should not include an entry for it in x0.\nMH_func is a callable that takes a metal mass fraction Z and returns the logarithmic abundance [M/H]; by default uses MH_from_Z.\nMH_deriv_Z is a callable that takes a metal mass fraction Zj and returns the derivative of MH_func with respect to the metal mass fraction Z evaluated at Zj. For the default value of MH_func, dMH_dZ provides the correct derivative. You only need to change this if you use an alternate MH_func.\nOther kws... are passed to Optim.options to set things like convergence criteria for the optimization.\n\n\n\n\n\n","category":"function"},{"location":"fitting/log_amr/#Sampling-Functions","page":"Logarithmic Age-Metallicity Relation","title":"Sampling Functions","text":"","category":"section"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"StarFormationHistories.hmc_sample_logamr","category":"page"},{"location":"fitting/log_amr/#StarFormationHistories.hmc_sample_logamr","page":"Logarithmic Age-Metallicity Relation","title":"StarFormationHistories.hmc_sample_logamr","text":"Not yet implemented \n\n\n\n\n\n","category":"function"},{"location":"fitting/log_amr/#Fixed-Logarithmic-Age-Metallicity-Relation","page":"Logarithmic Age-Metallicity Relation","title":"Fixed Logarithmic Age-Metallicity Relation","text":"","category":"section"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"We support fitting only the star formation parameters by adopting fixed values for alpha, beta, and sigma through the fixed_log_amr method.","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"StarFormationHistories.fixed_log_amr","category":"page"},{"location":"fitting/log_amr/#StarFormationHistories.fixed_log_amr","page":"Logarithmic Age-Metallicity Relation","title":"StarFormationHistories.fixed_log_amr","text":"fixed_log_amr(models,\n data,\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number},\n T_max::Number,\n α::Number,\n β::Number,\n σ::Number;\n MH_func = StarFormationHistories.MH_from_Z,\n kws...)\n\nGiven a fully specified logarithmic age-metallicity relation with parameters (α, β, σ), fits maximum likelihood and maximum a posteriori star formation parameters. MH_func is a callable that returns a logarithmic metallicity [M/H] for a metal mass fraction argument and defaults to MH_from_Z. T_max is the lookback time in Gyr at which the mean metal mass fraction is \beta. See fixed_amr for info on format of returned result.\n\n\n\n\n\nfixed_log_amr(models,\n data,\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number},\n T_max::Number,\n constraint1,\n constraint2,\n σ::Number;\n Z_func = StarFormationHistories.Z_from_MH,\n kws...)\n\nCall signature that takes two fixed points low_constraint and high_constraint that define points that must lie on the logarithmic age-metallicity relation and calculates the slope paramters α and β for you. Format is ([M/H], age [Gyr]), i.e. constraint1 = (-2.5, 13.7) for the first point at [M/H] = -2.5 at 13.7 Gyr lookback time and constraint2 = (-0.8, 0.0) for the second point at [M/H] = -0.8 at present-day (0.0 Gyr lookback time). The AMR is normalized so that the mean metal mass fraction at a lookback time in Gyr of T_max is Z = β. Metallicities in [M/H] are converted to metal mass fractions Z via the provided callable keyword argument Z_func which defaults to Z_from_MH. See fixed_amr for info on format of returned result.\n\n\n\n\n\n","category":"function"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"We provide the calculate_αβ_logamr convenience function to calculate the slope alpha and intercept beta from two points on the age-metallicity relation.","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"StarFormationHistories.calculate_αβ_logamr","category":"page"},{"location":"fitting/log_amr/#StarFormationHistories.calculate_αβ_logamr","page":"Logarithmic Age-Metallicity Relation","title":"StarFormationHistories.calculate_αβ_logamr","text":"(α, β) = calculate_αβ_logamr(low_constraint,\n high_constraint,\n T_max,\n Z_func=Z_from_MH)\n\nCalculates linear Z (log [M/H]) age-metallicity relation (AMR) slope α and intercept β from two points on the line with form ([M/H], age [Gyr]) given by the first two arguments. The AMR is normalized so that the mean metal mass fraction at a lookback time in Gyr of T_max is Z = β. More info given in fixed_log_amr.\n\nExamples\n\njulia> calculate_αβ_logamr((-2.5, 13.7), (-1.0, 0.0), 13.7) isa NTuple{2,Float64}\ntrue\n\n\n\n\n\n","category":"function"},{"location":"fitting/log_amr/#Implementation","page":"Logarithmic Age-Metallicity Relation","title":"Implementation","text":"","category":"section"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"As the only part of the model that differs from the linear AMR case is the mean age-metallicity relation, most of the derivation for the linear AMR case is still valid here. In particular, only the partial derivatives of the relative weights A_jk equiv textexp left( -frac12 sigma^2 left( textMtextH_k - mu_j right)^2right) with respect to the fitting parameters alpha and beta need to be recalculated under the new model. The partial derivative with respect to sigma is the same, as the mean metallicity in time bin j, denoted mu_j, does not depend on sigma.","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"beginaligned\nZ_j equiv langle Z left(t_jright) rangle = alpha left( T_textmax - t_j right) + beta \n \nmu_j equiv langle textMtextH rangle left(t_jright) = textlog left( fraclangle Zleft(t_jright) rangleX_j right) - textlog left( fracZ_odotX_odot right) \n= textlog left frac alpha left( T_textmax - t_j right) + betaX_j right - textlog left( fracZ_odotX_odot right)\nendaligned","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"We can use the chain rule to write","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"beginaligned\nA_jk equiv textexp left( - frac12 left( frac textMtextH_k - mu_jsigma right)^2 right) \nfracpartial A_jkpartial beta = fracpartial A_jkpartial mu_j fracpartial mu_jpartial beta \nfracpartial A_jkpartial alpha = fracpartial A_jkpartial mu_j fracpartial mu_jpartial alpha \nendaligned ","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"By definition the hydrogen, helium, and metal mass fractions, X, Y, and Z respectively, must sum to 1. For PARSEC models, Y is a function of Z (see Y_from_Z) such that X_j is a function of Z_j and therefore also alpha and beta. Under the PARSEC model, Y = Y_p + gamma Z, we have X(Z) = 1 - left( Y_p + gamma Z right) - Z such that we can rewrite the mu_j as ","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"mu_j = textlog left( fracZ_j1 - left( Y_p + gamma Z_j right) - Z_j right) - textlog left( fracZ_odotX_odot right)","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"and we can further expand the partial derivatives of mu_j as","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"beginaligned\nfracpartial mu_jpartial beta = fracpartial mu_jpartial Z_j fracpartial Z_jpartial beta \nfracpartial mu_jpartial alpha = fracpartial mu_jpartial Z_j fracpartial Z_jpartial alpha \nendaligned ","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"such that the model-dependent portion left( fracpartial Z_jpartial beta right) can be separated from what is essentially a calibration defining how [M/H] is calculated from Z left( fracpartial mu_jpartial Z_j right). Given our model Z_j = alpha left( T_textmax - t_j right) + beta and the PARSEC calibration for conversion between Z_j and mu_j (i.e., [M/H]), we have","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"beginaligned\nfracpartial mu_jpartial Z_j = fracY_p - 1textln10 Z_j left( Y_p + Z_j + gamma Z_j - 1 right) \nfracpartial Z_jpartial beta = fracpartial leftalpha left( T_textmax - t_j right) + beta rightpartial beta = 1 \nfracpartial Z_jpartial alpha = fracpartial left alpha left( T_textmax - t_j right) + beta rightpartial alpha = T_textmax - t_j \nendaligned","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"[M/H] as a function of Z for the PARSEC calibration is available as MH_from_Z and the partial derivative fracpartial mu_jpartial Z_j is available as dMH_dZ.","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"Which gives us final results","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"beginaligned\nfracpartial A_jkpartial mu_j = fracA_jk left( textMtextH_k - mu_j right)sigma^2 \nfracpartial mu_jpartial beta = fracpartial mu_jpartial Z_j fracpartial Z_jpartial beta = left( fracY_p - 1textln(10) Z_j left( Y_p + Z_j + gamma Z_j - 1 right) right) left( 1 right) \nfracpartial mu_jpartial alpha = fracpartial mu_jpartial Z_j fracpartial Z_jpartial alpha = left( fracY_p - 1textln(10) Z_j left( Y_p + Z_j + gamma Z_j - 1 right) right) left( T_textmax - t_j right) \n\n fracpartial mu_jpartial beta = frac1left( t_j alpha + beta right) textln(10) \n fracpartial mu_jpartial alpha = fractleft( t_j alpha + beta right) textln(10) = t fracpartial mu_jpartial beta \nendaligned","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"such that","category":"page"},{"location":"fitting/log_amr/","page":"Logarithmic Age-Metallicity Relation","title":"Logarithmic Age-Metallicity Relation","text":"beginaligned\nfracpartial A_jkpartial beta = fracpartial A_jkpartial mu_j fracpartial mu_jpartial beta = fracpartial A_jkpartial mu_j fracpartial mu_jpartial Z_j fracpartial Z_jpartial beta \n= left( fracA_jk left( textMtextH_k - mu_j right)sigma^2 right) left( fracY_p - 1textln10 Z_j left( Y_p + Z_j + gamma Z_j - 1 right) right) \nfracpartial A_jkpartial alpha = fracpartial A_jkpartial mu_j fracpartial mu_jpartial alpha = fracpartial A_jkpartial mu_j fracpartial mu_jpartial Z_j fracpartial Z_jpartial alpha \n= left( fracA_jk left( textMtextH_k - mu_j right)sigma^2 right) left( fracY_p - 1textln10 Z_j left( Y_p + Z_j + gamma Z_j - 1 right) right) left( T_textmax - t_j right) \n= left( T_textmax - t_j right) fracpartial A_jkpartial beta \n\n fracpartial A_jkpartial beta = fracpartial A_jkpartial mu_j fracpartial mu_jpartial beta = left( fracA_jk left( textMtextH_k - mu_j right)sigma^2 right) left( frac1left( t_j alpha + beta right) textln(10) right) \n = fracA_jk left( textMtextH_k - mu_j right)textln(10) sigma^2 left( t_j alpha + beta right) \n fracpartial A_jkpartial alpha = fracpartial A_jkpartial mu_j fracpartial mu_jpartial alpha = t fracpartial A_jkpartial beta\nendaligned","category":"page"},{"location":"binaries/#binaries","page":"Binary Systems","title":"Binary Systems","text":"","category":"section"},{"location":"binaries/","page":"Binary Systems","title":"Binary Systems","text":"Here we review the API for including binary systems in our population models. Our Monte Carlo sampling methods supports all three models, while our smooth template modelling procedure only supports NoBinaries and RandomBinaryPairs. A comparison between a Monte Carlo population and a smooth template model for a RandomBinaryPairs model with binary fraction of 70% is shown below. The redward shift of the lower main sequence typical of populations with high binary fractions is clearly evident and robustly modelled.","category":"page"},{"location":"binaries/","page":"Binary Systems","title":"Binary Systems","text":"mv(\"../../examples/templates/template_compare_binaries.svg\", \"template_compare_binaries.svg\") # hide\nmv(\"../../examples/templates/sigma_distribution_binaries.svg\", \"sigma_distribution_binaries.svg\") # hide\nnothing # hide","category":"page"},{"location":"binaries/","page":"Binary Systems","title":"Binary Systems","text":"(Image: Comparison of CMD-sampled population with smooth Hess diagram template, with binaries.)","category":"page"},{"location":"binaries/#Types","page":"Binary Systems","title":"Types","text":"","category":"section"},{"location":"binaries/","page":"Binary Systems","title":"Binary Systems","text":"StarFormationHistories.AbstractBinaryModel\nStarFormationHistories.NoBinaries\nStarFormationHistories.RandomBinaryPairs\nStarFormationHistories.BinaryMassRatio","category":"page"},{"location":"binaries/#StarFormationHistories.AbstractBinaryModel","page":"Binary Systems","title":"StarFormationHistories.AbstractBinaryModel","text":"StarFormationHistories.AbstractBinaryModel is the abstract supertype for all types that are used to model multi-star systems in the package. All concrete subtypes should implement the following methods to support all features:\n\nStarFormationHistories.sample_system\nStarFormationHistories.binary_system_fraction\nStarFormationHistories.binary_mass_fraction\nBase.length, which should return an integer indicating the number of stars per system that can be sampled by the model; this is equivalent to the length of the mass vector returned by sample_system.\n\nNote that all quantities relating to binary populations (e.g., binary_system_fraction) should be defined for the population at birth. As the stars in a binary system evolve, the more massive star may die before the system is observed at present-day. Of course, the stars in single-star systems can also die. If the rate at which binary systems become single-star systems is not equal to the rate at which single-star systems die, then there can be net transfer between these populations over time. Therefore the observed, present-day binary system fraction of an evolved population is not necessarily equal to the fraction at birth, which is the more fundamental quantity.\n\n\n\n\n\n","category":"type"},{"location":"binaries/#StarFormationHistories.NoBinaries","page":"Binary Systems","title":"StarFormationHistories.NoBinaries","text":"NoBinaries()\n\nThe NoBinaries type indicates that no binaries of any kind should be created. \n\n\n\n\n\n","category":"type"},{"location":"binaries/#StarFormationHistories.RandomBinaryPairs","page":"Binary Systems","title":"StarFormationHistories.RandomBinaryPairs","text":"RandomBinaryPairs(fraction::Real)\n\nThe RandomBinaryPairs type takes one argument 0 <= fraction::Real <= 1 that denotes the number fraction of stellar systems that are binaries (e.g., 0.3 for 30% binary fraction) and will sample binaries as random pairs of two stars drawn from the same single-star IMF. This model will ONLY generate up to one additional star – it will not generate any 3+ star systems. This model typically incurs a 10–20% speed penalty relative to NoBinaries. \n\n\n\n\n\n","category":"type"},{"location":"binaries/#StarFormationHistories.BinaryMassRatio","page":"Binary Systems","title":"StarFormationHistories.BinaryMassRatio","text":"BinaryMassRatio(fraction::Real,\n qdist::Distributions.ContinuousUnivariateDistribution =\n Distributions.Uniform(0.1, 1.0))\n\nThe BinaryMassRatio type takes two arguments; the number fraction of stellar systems that are binaries 0 <= fraction::Real <= 1 and a continuous univariate distribution qdist from which to sample binary mass ratios, defined as the ratio of the secondary mass to the primary mass: q = textM_S textM_P. The provided qdist must have the proper support of (minimum(qdist) >= 0) & (maximum(qdist) <= 1). Users may find the Distributions.truncated method useful for enforcing this support on more general distributions. The default qdist is a uniform distribution from 0.1 to 1, which appears to give reasonably good agreement to observations (see, e.g., Goodwin 2013).\n\n\n\n\n\n","category":"type"},{"location":"binaries/#Methods","page":"Binary Systems","title":"Methods","text":"","category":"section"},{"location":"binaries/","page":"Binary Systems","title":"Binary Systems","text":"StarFormationHistories.binary_system_fraction\nStarFormationHistories.binary_number_fraction\nStarFormationHistories.binary_mass_fraction\nStarFormationHistories.sample_system","category":"page"},{"location":"binaries/#StarFormationHistories.binary_system_fraction","page":"Binary Systems","title":"StarFormationHistories.binary_system_fraction","text":"binary_system_fraction(model::T) where T <: AbstractBinaryModel\n\nReturns the number fraction of stellar systems that are binaries for the given concrete subtype T <: AbstractBinaryModel. Has a default implementation of binary_system_fraction(model::AbstractBinaryModel) = model.fraction.\n\n\n\n\n\n","category":"function"},{"location":"binaries/#StarFormationHistories.binary_number_fraction","page":"Binary Systems","title":"StarFormationHistories.binary_number_fraction","text":"binary_number_fraction(model::T) where T <: AbstractBinaryModel\n\nReturns the number fraction of stars that in binary pairs for the given concrete subtype T <: AbstractBinaryModel. Has a default implementation of 2b / (1+b), where b is the result of StarFormationHistories.binary_system_fraction.\n\n\n\n\n\n","category":"function"},{"location":"binaries/#StarFormationHistories.binary_mass_fraction","page":"Binary Systems","title":"StarFormationHistories.binary_mass_fraction","text":"binary_mass_fraction(model::T, imf) where T <: AbstractBinaryModel\n\nReturns the fraction of stellar mass in binary systems for the given concrete subtype T <: AbstractBinaryModel and initial mass function imf. imf must be a properly normalized probability distribution such that the number fraction of stars/systems between mass m1 and m2 is given by the integral of dispatch_imf(imf, x) from m1 to m2. \n\n\n\n\n\nbinary_mass_fraction(m::RandomBinaryPairs, imf)\n\nThe RandomBinaryPairs model uses a single-star imf. If a system is chosen to be a binary pair, two stars are drawn from the single-star imf and the more massive star is made the primary. Given this model, it can be shown that the expectation value for the mass of a binary system is twice the expectation value for single star systems:\n\n2int_textM_textmin^textM_textmax textM fracdtextN left( textM right)dtextM dtextM = int_textM_textmin^textM_textmax int_textM_textmin^textM_textmax left( textM_P + textM_S right) fracdtextN left( textM_S right)dtextM fracdtextN left( textM_P right)dtextM dtextM_S dtextM_P\n\nfor primary mass textM_P, secondary mass textM_S, and single-star IMF dtextN dtextM. As such, the fraction of total stellar mass in binaries is equal to the number fraction of all stars in binary pairs, which is given by StarFormationHistories.binary_number_fraction.\n\n\n\n\n\nbinary_mass_fraction(m::BinaryMassRatio, imf)\n\nThis binary model requires an imf that is defined by stellar system mass. If a system with a randomly sampled mass M is is a binary, the primary and secondary mass are determined based on a binary mass ratio q sampled from a user-defined distribution. By definition, the expectation value for the total mass of a binary system is equal to the expectation value for single-star systems. In this case the binary mass fraction is equal the binary system number fraction as given by StarFormationHistories.binary_system_fraction.\n\n\n\n\n\n","category":"function"},{"location":"binaries/#StarFormationHistories.sample_system","page":"Binary Systems","title":"StarFormationHistories.sample_system","text":"masses = sample_system(imf, rng::AbstractRNG, binarymodel::StarFormationHistories.AbstractBinaryModel)\n\nSimulates the effects of non-interacting, unresolved stellar companions on stellar photometry. Implementation depends on the choice of binarymodel.\n\nArguments\n\nimf: an object implementing rand(imf) to draw a random mass for a single star or a stellar system (depends on choice of binarymodel)\nrng::AbstractRNG: the random number generator to use when sampling stars\nbinarymodel::StarFormationHistories.AbstractBinaryModel: an instance of a binary model that determines which implementation will be used; currently provided options are NoBinaries, RandomBinaryPairs, and BinaryMassRatio.\n\nReturns\n\nmasses::SVector{N,eltype(imf)}: the masses of the individual stars sampled in the system in descending order where N is the maximum number of stars that can be sampled by the provided binarymodel as given by Base.length(binarymodel). \n\n\n\n\n\n","category":"function"},{"location":"binaries/#Notes","page":"Binary Systems","title":"Notes","text":"","category":"section"},{"location":"binaries/","page":"Binary Systems","title":"Binary Systems","text":"The trickiest part of including binaries in the smooth template modelling procedure is deriving the IMF weights. Let M_p be the sorted list of initial masses for primary stars and M_s be the sorted list of initial masses for secondary stars. Conceptually, the IMF weight for a binary system with primary mass M_pi and secondary mass M_sj should compute the number fraction of binary systems born with primary masses between M_pi and M_pi+1 and secondary masses between M_sj and M_sj+1 per unit solar mass formed. ","category":"page"},{"location":"binaries/","page":"Binary Systems","title":"Binary Systems","text":"In the case of the RandomBinaryPairs model, the IMF weights are calculated as follows, with dN(M)dM being the IMF for single stars, langle M rangle being the mean mass of single stars over the full range of possible initial masses, and the integral in the denominator being over the range of initial masses in the isochrone. The integral in the denominator accounts for losses due to stellar evolution.","category":"page"},{"location":"binaries/","page":"Binary Systems","title":"Binary Systems","text":" w_textIMFij = fracint_M_pi^M_pi+1 int_M_sj^M_sj+1 fracdN(M_p)dM fracdN(M_s)dM dM_p dM_slangle M rangle int_M_textmin^M_textmax fracdN(M)dM dM","category":"page"},{"location":"helpers/#helpers","page":"Helper Functions","title":"Helper Functions","text":"","category":"section"},{"location":"helpers/#Distances-and-Sizes","page":"Helper Functions","title":"Distances and Sizes","text":"","category":"section"},{"location":"helpers/","page":"Helper Functions","title":"Helper Functions","text":"StarFormationHistories.arcsec_to_pc\nStarFormationHistories.pc_to_arcsec\nStarFormationHistories.distance_modulus\nStarFormationHistories.distance_modulus_to_distance","category":"page"},{"location":"helpers/#StarFormationHistories.arcsec_to_pc","page":"Helper Functions","title":"StarFormationHistories.arcsec_to_pc","text":"arcsec_to_pc(arcsec, dist_mod)\n\nConverts on-sky angle in arcseconds to physical separation based on distance modulus under the small-angle approximation.\n\nr 10^μ5 + 1 times textatan(θ3600)\n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.pc_to_arcsec","page":"Helper Functions","title":"StarFormationHistories.pc_to_arcsec","text":"pc_to_arcsec(pc, dist_mod)\n\nInverse of arcsec_to_pc.\n\nθ texttanleft( r 10^μ5 + 1 right) times 3600\n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.distance_modulus","page":"Helper Functions","title":"StarFormationHistories.distance_modulus","text":"distance_modulus(distance)\n\nFinds distance modulus for distance in parsecs.\n\nμ = 5 times log_10(d) - 5\n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.distance_modulus_to_distance","page":"Helper Functions","title":"StarFormationHistories.distance_modulus_to_distance","text":"distance_modulus_to_distance(dist_mod)\n\nConverts distance modulus to distance in parsecs.\n\nd = 10^μ5 + 1\n\n\n\n\n\n","category":"function"},{"location":"helpers/#Magnitudes-and-Luminosities","page":"Helper Functions","title":"Magnitudes and Luminosities","text":"","category":"section"},{"location":"helpers/","page":"Helper Functions","title":"Helper Functions","text":"StarFormationHistories.mag2flux\nStarFormationHistories.flux2mag\nStarFormationHistories.magerr\nStarFormationHistories.fluxerr\nStarFormationHistories.snr_magerr\nStarFormationHistories.magerr_snr","category":"page"},{"location":"helpers/#StarFormationHistories.mag2flux","page":"Helper Functions","title":"StarFormationHistories.mag2flux","text":"mag2flux(m, zpt=0)\n\nConvert a magnitude m to a flux assuming a photometric zeropoint of zpt, defined as the magnitude of an object that produces one count (or data number, DN) per second.\n\njulia> mag2flux(15.0, 25.0) ≈ exp10(4 * (25.0 - 15.0) / 10)\ntrue\n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.flux2mag","page":"Helper Functions","title":"StarFormationHistories.flux2mag","text":"flux2mag(f, zpt=0)\n\nConvert a flux f to a magnitude assuming a photometric zeropoint of zpt, defined as the magnitude of an object that produces one count (or data number, DN) per second.\n\njulia> flux2mag(10000.0, 25.0) ≈ 25.0 - 5 * log10(10000.0) / 2\ntrue\n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.magerr","page":"Helper Functions","title":"StarFormationHistories.magerr","text":"magerr(f, σf)\n\nReturns an error in magnitudes given a flux and a flux uncertainty.\n\njulia> magerr(100.0, 1.0) ≈ 2.5 / log(10) * (1.0 / 100.0)\ntrue\n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.fluxerr","page":"Helper Functions","title":"StarFormationHistories.fluxerr","text":"fluxerr(f, σm)\n\nReturns an error in flux given a flux and a magnitude uncertainty.\n\njulia> fluxerr(100.0, 0.01) ≈ (0.01 * 100.0) / 2.5 * log(10)\ntrue\n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.snr_magerr","page":"Helper Functions","title":"StarFormationHistories.snr_magerr","text":"snr_magerr(σm)\n\nReturns a signal-to-noise ratio (fσf) given an uncertainty in magnitudes.\n\njulia> snr_magerr(0.01) ≈ 2.5 / log(10) / 0.01\ntrue\n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.magerr_snr","page":"Helper Functions","title":"StarFormationHistories.magerr_snr","text":"magerr_snr(snr)\n\nReturns a magnitude uncertainty given a signal-to-noise ratio (fσf).\n\njulia> magerr_snr(100.0) ≈ 2.5 / log(10) / 100.0\ntrue\n\n\n\n\n\n","category":"function"},{"location":"helpers/#metallicity_helpers","page":"Helper Functions","title":"Metallicities","text":"","category":"section"},{"location":"helpers/","page":"Helper Functions","title":"Helper Functions","text":"StarFormationHistories.Y_from_Z\nStarFormationHistories.X_from_Z\nStarFormationHistories.MH_from_Z\nStarFormationHistories.dMH_dZ\nStarFormationHistories.Z_from_MH\nStarFormationHistories.mdf_amr","category":"page"},{"location":"helpers/#StarFormationHistories.Y_from_Z","page":"Helper Functions","title":"StarFormationHistories.Y_from_Z","text":"Y_from_Z(Z, Y_p=0.2485, γ=1.78)\n\nCalculates the helium mass fraction (Y) for a star given its metal mass fraction (Z) using the approximation Y = Y_p + γ * Z, with Y_p being the primordial helium abundance Y_p=0.2485 as assumed for PARSEC isochrones and γ=1.78 matching the input scaling for PARSEC as well. \n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.X_from_Z","page":"Helper Functions","title":"StarFormationHistories.X_from_Z","text":"X_from_Z(Z[, Yp, γ])\n\nCalculates the hydrogen mass fraction (X) for a star given its metal mass fraction (Z) via X = 1 - (Z + Y), with the helium mass fraction Y approximated via StarFormationHistories.Y_from_Z. You may also provide the primordial helium abundance Y_p and γ such that Y = Y_p + γ * Z; these are passed through to X_from_Z. \n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.MH_from_Z","page":"Helper Functions","title":"StarFormationHistories.MH_from_Z","text":"MH_from_Z(Z, solZ=0.01524; Y_p = 0.2485, γ = 1.78)\n\nCalculates [M/H] = log(Z/X) - log(Z/X)⊙. Given the provided solar metal mass fraction solZ, it calculates the hydrogen mass fraction X for both the Sun and the provided Z with StarFormationHistories.X_from_Z. You may also provide the primordial helium abundance Y_p and γ such that Y = Y_p + γ * Z; these are passed through to X_from_Z. \n\nThe present-day solar Z is measured to be 0.01524 (Caffau et al. 2011), but for PARSEC isochrones an [M/H] of 0 corresponds to Z=0.01471. This is because of a difference between the Sun's initial and present helium content caused by diffusion. If you want to reproduce PARSEC's scaling, you should set solZ=0.01471.\n\nThis function is an approximation and may not be suitable for precision calculations.\n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.dMH_dZ","page":"Helper Functions","title":"StarFormationHistories.dMH_dZ","text":"dMH_dZ(Z, solZ=0.01524; Y_p = 0.2485, γ = 1.78)\n\nPartial derivative of MH_from_Z with respect to the input metal mass fraction Z. Used for some optimizations. \n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.Z_from_MH","page":"Helper Functions","title":"StarFormationHistories.Z_from_MH","text":"Z_from_MH(MH, solZ=0.01524; Y_p = 0.2485, γ = 1.78)\n\nCalculates metal mass fraction Z assuming\n\nthe PARSEC relation for the helium mass fraction Y = Y_p + γ * Z with primordial helium abundance Y_p = 0.2485, and γ = 1.78, and\nthe solar metal mass fraction solZ = 0.01524.\n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.mdf_amr","page":"Helper Functions","title":"StarFormationHistories.mdf_amr","text":"(unique_MH, mass_mdf) =\nmdf_amr(coeffs::AbstractVector{<:Number},\n logAge::AbstractVector{<:Number},\n metallicities::AbstractVector{<:Number})\n\nCalculates the mass-weighted metallicity distribution function given a set of stellar mass coefficients coeffs for stellar populations with logarithmic ages logAge=log10(age [yr]) and metallicities given by metallicities. This is calculated as\n\nP_j = frac sum_k r_jk textM textH_ksum_jk r_jk textM textH_k\n\nwhere r_jk are the elements of coeffs where j indexes over unique entries in logAge and k indexes over unique entries in metallicities. This is the same nomenclature used in the the documentation on constrained metallicity evolutions. The return values are sorted so that unique_MH is in increasing order.\n\nExamples\n\njulia> mdf_amr([1.0, 2.0, 1.0], [10, 10, 10], [-2, -1.5, -1])\n([-2.0, -1.5, -1.0], [0.25, 0.5, 0.25])\n\n\n\n\n\n","category":"function"},{"location":"helpers/#phot_helpers","page":"Helper Functions","title":"Photometric Error and Completeness Models","text":"","category":"section"},{"location":"helpers/","page":"Helper Functions","title":"Helper Functions","text":"StarFormationHistories.Martin2016_complete\nStarFormationHistories.exp_photerr\nStarFormationHistories.process_ASTs","category":"page"},{"location":"helpers/#StarFormationHistories.Martin2016_complete","page":"Helper Functions","title":"StarFormationHistories.Martin2016_complete","text":"η(m) = Martin2016_complete(m, A, m50, ρ)\n\nCompleteness model of Martin et al. 2016 implemented as their Equation 7:\n\neta(m) = fracA1 + textexp left( fracm - m_50rho right)\n\nm is the magnitude of interest, A is the maximum completeness, m50 is the magnitude at which the data are 50% complete, and ρ is an effective slope modifier.\n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.exp_photerr","page":"Helper Functions","title":"StarFormationHistories.exp_photerr","text":"exp_photerr(m, a, b, c, d)\n\nExponential model for photometric errors of the form\n\nsigma(m) = a^b times left( m-c right) + d\n\nReported values for some HST data were a=1.05, b=10.0, c=32.0, d=0.01. \n\n\n\n\n\n","category":"function"},{"location":"helpers/#StarFormationHistories.process_ASTs","page":"Helper Functions","title":"StarFormationHistories.process_ASTs","text":"process_ASTs(ASTs::Union{DataFrames.DataFrame,\n TypedTables.Table},\n inmag::Symbol,\n outmag::Symbol,\n bins::AbstractVector{<:Real},\n selectfunc;\n statistic=StatsBase.median)\n\nProcesses a table of artificial stars to calculate photometric completeness, bias, and error across the provided bins. This method has no default implementation and is implemented in package extensions that rely on either DataFrames.jl or TypedTables.jl being loaded into your Julia session to load the relevant method. This method therefore requires Julia 1.9 or greater to use.\n\nArguments\n\nASTs is the table of artificial stars to be analyzed.\ninmag is the column name in symbol format (e.g., :F606Wi) that corresponds to the intrinsic (input) magnitudes of the artificial stars.\noutmag is the column name in symbol format (e.g., :F606Wo) that corresponds to the measured (output) magnitude of the artificial stars.\nbins give the bin edges to be used when computing the binned statistics.\nselectfunc is a method that takes a single row from ASTs, corresponding to a single artificial star, and returns a boolean that is true if the star is considered successfully measured.\n\nKeyword Arguments\n\nstatistic is the method that will be used to determine the bias and error, i.e., bias = statistic(out .- in) and error = statistic(abs.(out .- in)). By default we use StatsBase.median, but you could instead use a simple or sigma-clipped mean if so desired.\n\nReturns\n\nThis method returns a result of type NTuple{4,Vector{Float64}}. Each vector is of length length(bins)-1. result contains the following elements, each of which are computed over the provided bins considering only artificial stars for which selectfunc returned true:\n\nresult[1] contains the mean input magnitude of the stars in each bin.\nresult[2] contains the completeness value measured for each bin, defined as the fraction of input stars in each bin for which selectfunc returned true.\nresult[3] contains the photometric bias measured for each bin, defined as statistic(out .- in), where out are the measured (output) magnitudes and in are the intrinsic (input) magnitudes.\nresult[4] contains the photometric error measured for each bin, defined as statistic(abs.(out .- in)), with out and in defined as above.\n\nExamples\n\nLet\n\nF606Wi be a vector containing the input magnitudes of your artificial stars\nF606Wo be a vector containing the measured magnitudes of the artificial stars, where a value of 99.999 indicates a non-detection.\nflag be a vector of booleans that indicates whether the artificial star passed additional quality cuts (star-galaxy separation, etc.)\n\nYou could call this method as\n\nimport TypedTables: Table\nprocess_ASTs(Table(input=F606Wi, output=F606Wo, good=flag),\n :input, :output, minimum(F606Wi):0.1:maximum(F606Wi),\n x -> (x.good==true) & (x.output != 99.999))\n\nSee also the tests in test/utilities/process_ASTs_test.jl.\n\n\n\n\n\n","category":"function"},{"location":"fitting/kernels/#kernels","page":"Kernels","title":"Kernels","text":"","category":"section"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"One of the most important parts of our method is modelling the 2-D probability distribution of observing a star with particular intrinsic magnitudes in the Hess diagram space. We refer to these 2-D probability distributions as \"kernels\" throughout, as our process is conceptually similar to constructing a kernel density estimate. This document describes how these kernels are constructed and what assumptions are made to do so.","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"Our implementation is underpinned by the assumption that all kernels can be described the family of 2-D Gaussian probability distributions. To derive these 2-D distributions, we begin with 1-D distributions for each photometric filter used in the analysis.","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"We assume random photometric errors in the observed photometric catalog can be approximated as Gaussian. This is generally a good assumption for the space-based imaging used for resolved SFHs (e.g., HST and JWST) as long as the photometric completeness is high (greater than, perhaps, 50%). At low completeness, the error distributions become skewed and would be better modelled by something like a skew-normal distribution, which we do not presently consider.","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"We additionally require that the user can construct functions for each observed photomeric filter which, given a star's input intrinsic magnitude in a single filter m_i, return reliable measures for the average single-band photometric error; sigma(m_i). Assuming that the photometric errors in each band can be modelled as independent, we can derive, for a given m_i, the probability distribution of the observed magnitude m_o. We therefore have the 1-D probability distributions P(m_om_i) for all filters.","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"In the case that imaging in three photometric filters are available and are used to construct a Hess diagram with a magnitude on the y-axis that does not appear on the x-axis (e.g., y=R and x=B-V), then there is no covariance between the x and y axes and the 2-D Gaussian kernel is fully separable. We call this the \"non-covariant\" case.","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"In order to inject the kernel into the model Hess diagram, it must be integrated across the grid that defines the discretization. The integral of the non-covariant kernel across the Hess diagram pixel grid is entirely analytic, making evaluation easy and efficient. By integrating the kernel over the grid, the numerical precision is nearly independent of the grid resolution, which is not true of Monte Carlo (MC) based template construction algorithms.","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"In the case that imaging in only two photometric filters are available and are used to construct a Hess diagram with a magnitude on the y-axis that does appear on the x-axis (e.g., y=B and x=B-V), then there is covariance between x and y axes. The initial implementation of this method neglected this covariance, and while the result was not catastrophic, it was certainly suboptimal. We now model the covariance explicitly. The covariance pattern does not permit a fully analytic integral as in the non-covariant case; instead, the inner integral (over, say, the x-axis) can be calculated analytically but the outer integral cannot. We therefore use Gauss-Legendre quadtrature to finish the integration over the second axis.","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"The types representing these kernels are not part of our public API, but we provide an example script examples/templates/kernels_example.jl which illustrates the performance of these kernels. The output of this script is reproduced and explained below.","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"Consider a star taken from an isochrone with intrinsic magnitudes B=20, V=19, and R=18 with expected random photometric errors sigma_B=002, sigma_V=003, and sigma_R=005. Our example randomly samples a large population of possible observed magnitudes given these intrinsic properties and compares the distribution of the MC sample to our kernels.","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"We consider first the non-covariant case:","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"mv(\"../../../examples/templates/covar_0.svg\", \"figures/covar_0.svg\") # hide\nmv(\"../../../examples/templates/covar_1.svg\", \"figures/covar_1.svg\") # hide\nmv(\"../../../examples/templates/covar_m1.svg\", \"figures/covar_m1.svg\") # hide\nnothing # hide","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"(Image: noncovariant)","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"which has the expected, non-rotated morphology.","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"We next consider the covariant case with the covariance pattern y=B and x=B-V:","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"(Image: covariantm1)","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"which we can see correctly models the covariance pattern.","category":"page"},{"location":"fitting/kernels/","page":"Kernels","title":"Kernels","text":"And finally, the covariant case with the covariance pattern y=V and x=B-V: (Image: covariant1)","category":"page"},{"location":"fitting/unconstrained/#High-Level-Methods-for-Unconstrained-Fitting","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"","category":"section"},{"location":"fitting/unconstrained/#Maximum-Likelihood-Optimization","page":"High-Level Methods for Unconstrained Fitting","title":"Maximum Likelihood Optimization","text":"","category":"section"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"Template construction is by far the most complicated step in the fitting procedure. Once your templates have been constructed, fitting them to an observed Hess diagram amounts to maximization of the Poisson likelihood ratio (Dolphin 2002). It is possible to construct more complicated hierarchical models including things like metallicity distribution functions; we discuss these in the next section. In this section we discuss methods for fitting where the only constraint is that star formation rates cannot be negative. We provide the StarFormationHistories.construct_x0 method to help with setting the initial guess for this optimization. ","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"StarFormationHistories.construct_x0","category":"page"},{"location":"fitting/unconstrained/#StarFormationHistories.construct_x0","page":"High-Level Methods for Unconstrained Fitting","title":"StarFormationHistories.construct_x0","text":"x0::typeof(logage) = construct_x0(logAge::AbstractVector{T},\n T_max::Number;\n normalize_value::Number=one(T)) where T <: Number\n\nGenerates a vector of initial stellar mass normalizations for input to fit_templates or hmc_sample with a total stellar mass of normalize_value such that the implied star formation rate is constant across the provided logAge vector that contains the log10(Age [yr]) of each isochrone that you are going to input as models. For the purposes of computing the constant star formation rate, the provided logAge are treated as left-bin edges, with the final right-bin edge being T_max, which has units of Gyr. For example, you might have logAge=[6.6, 6.7, 6.8] in which case a final logAge of 6.9 would give equal bin widths. In this case you would set T_max = exp10(6.9) / 1e9 ≈ 0.0079 so that the width of the final bin for the star formation rate calculation has the same log10(Age [yr]) step as the other bins.\n\nExamples\n\njulia> x0 = construct_x0(repeat([7.0,8.0,9.0],3), 10.0; normalize_value=5.0)\n9-element Vector{Float64}: ...\n\njulia> sum(x0)\n4.99... # Close to `normalize_value`.\n\n\n\n\n\n","category":"function"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"When it comes to performing the optimization, the simplest method we offer is StarFormationHistories.fit_templates_lbfgsb. This will optimize one coefficient per template; there is no overarching metallicity evolution or other constraint, besides that the stellar masses of the populations cannot be negative. This performs a maximum likelihood optimization with the bounded quasi-Newton LBFGS method as implemented in L-BFGS-B and wrapped in LBFGS.jl with analytic gradients. It is fast and converges fairly reliably, even when the initial guess is not particularly close to the maximum likelihood estimate. It provides no uncertainty estimation. It is normal for some of the coefficients to converge to zero.","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"StarFormationHistories.fit_templates_lbfgsb","category":"page"},{"location":"fitting/unconstrained/#StarFormationHistories.fit_templates_lbfgsb","page":"High-Level Methods for Unconstrained Fitting","title":"StarFormationHistories.fit_templates_lbfgsb","text":"(-logL, coeffs) = \nfit_templates_lbfgsb(models::AbstractVector{T},\n data::AbstractMatrix{<:Number};\n x0::AbstractVector{<:Number} = ones(S,length(models)),\n factr::Number=1e-12,\n pgtol::Number=1e-5,\n iprint::Integer=0,\n kws...) where {S <: Number, T <: AbstractMatrix{S}}\n\nFinds the coefficients coeffs that maximize the Poisson likelihood ratio (equations 7–10 in Dolphin 2002) for the composite Hess diagram model sum(models .* coeffs) given the provided templates models and the observed Hess diagram data using the box-constrained LBFGS method provided by LBFGSB.jl. \n\nArguments\n\nmodels::AbstractVector{AbstractMatrix{<:Number}}: the list of template Hess diagrams for the simple stellar populations (SSPs) being considered; all must have the same size.\ndata::AbstractMatrix{<:Number}: the observed Hess diagram; must match the size of the templates contained in models.\n\nKeyword Arguments\n\nx0: The vector of initial guesses for the stellar mass coefficients. You should basically always be calculating and passing this keyword argument; we provide StarFormationHistories.construct_x0 to prepare x0 assuming constant star formation rate, which is typically a good initial guess.\nfactr::Number: Keyword argument passed to LBFGSB.lbfgsb; essentially a relative tolerance for convergence based on the inter-iteration change in the objective function.\npgtol::Number: Keyword argument passed to LBFGSB.lbfgsb; essentially a relative tolerance for convergence based on the inter-iteration change in the projected gradient of the objective.\niprint::Integer: Keyword argument passed to LBFGSB.lbfgsb controlling how much information is printed to the terminal. Setting to 1 can sometimes be helpful to diagnose convergence issues. Setting to -1 will disable printing.\n\nOther kws... are passed to LBFGSB.lbfgsb.\n\nReturns\n\n-logL::Number: the minimum negative log-likelihood found by the optimizer.\ncoeffs::Vector{<:Number}: the maximum likelihood estimate for the coefficient vector. \n\nNotes\n\nIt can be helpful to normalize your models to contain realistic total stellar masses to aid convergence stability; for example, if the total stellar mass of your population is 10^7 solar masses, then you might normalize your templates to contain 10^3 solar masses. If you are using partial_cmd_smooth to construct the templates, you can specify this normalization via the normalize_value keyword. \n\n\n\n\n\nfit_templates_lbfgsb(models::AbstractMatrix{S},\n data::AbstractVector{<:Number};\n x0::AbstractVector{<:Number} = ones(S,size(models,2)),\n factr::Number=1e-12,\n pgtol::Number=1e-5,\n iprint::Integer=0,\n kws...) where S <: Number\n\nThis call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\n\n\n\n\n","category":"function"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"This method simply minimizes the negative logarithm of the Poisson likelihood ratio (Equation 10 in Dolphin 2002),","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"- textln mathscrL = sum_i m_i - n_i times left( 1 - textln left( fracn_im_i right) right)","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"where m_i is bin i of the complex model and n_i is bin i of the observed Hess diagram; this can therefore be thought of as computing the maximum likelihood estimate.","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"We also provide StarFormationHistories.fit_templates_fast, which is the fastest method we offer for deriving a maximum likelihood estimate for the type of model described above.","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"StarFormationHistories.fit_templates_fast","category":"page"},{"location":"fitting/unconstrained/#StarFormationHistories.fit_templates_fast","page":"High-Level Methods for Unconstrained Fitting","title":"StarFormationHistories.fit_templates_fast","text":"(coeffs::Vector{::eltype(x0)}, result::Optim.MultivariateOptimizationResults) =\nfit_templates_fast(models::AbstractVector{T},\n data::AbstractMatrix{<:Number};\n x0::AbstractVector{<:Number} = ones(S,length(models)),\n kws...)\n where {S <: Number, T <: AbstractMatrix{S}}\n\nFinds only the maximum likelihood estimate (MLE) for the coefficients coeffs given the provided data such that the best-fit composite Hess diagram model is sum(models .* coeffs). This is a simplification of the main fit_templates function, which will return the MLE as well as the maximum a posteriori estimate, and further supports uncertainty quantification. For additional details on arguments to this method, see the documentation for fit_templates. \n\nThis method optimizes parameters θ such that coeffs = θ.^2 – this allows for faster convergence than both the fit_templates_lbfgsb method, which does not use a variable transformation, and the logarithmic transformation used in fit_templates. However, the inverse Hessian is not useful for uncertainty estimation under this transformation. As such this method only returns the MLE for coeffs as a vector and the result object returned by Optim.optimize. While this method offers fewer features than fit_templates, this method's runtime is typically half as long (or less). As such, this method is recommended for use in performance-sensitive applications like hierarchical models or hyperparameter estimation where the additional features of fit_templates are unnecessary. In these applications, the value of the objective function at the derived MLE is typically desired as well; this can be obtained the from result::Optim.MultivariateOptimizationResults object as Optim.minimum(result). Note that this will return the negative loglikelihood, which is what is minimized in this application.\n\nNotes\n\nBy passing additional convergence keyword arguments supported by Optim.Options (see this guide), it is possible to converge to the MLE in fewer than 30 iterations with fewer than 100 calls to the likelihood and gradient methods. For example, the main convergence criterion is typically the magnitude of the gradient vector, which by default is g_abstol=1e-8, terminating the optimization when the magnitude of the gradient is less than 1e-8. We find results are typically sufficiently accurate with g_abstol=1e-3, which often uses half as many objective evaluations as the default value.\n\n\n\n\n\nfit_templates_fast(models::AbstractMatrix{S},\n data::AbstractVector{<:Number};\n x0::AbstractVector{<:Number} = ones(S,size(models,2)),\n kws...)\n where S <: Number\n\nThis call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\n\n\n\n\n","category":"function"},{"location":"fitting/unconstrained/#Posterior-Sampling:-MCMC","page":"High-Level Methods for Unconstrained Fitting","title":"Posterior Sampling: MCMC","text":"","category":"section"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"For low-dimensional problems, Markov Chain Monte Carlo (MCMC) methods can be an efficient way to sample the posterior and obtain uncertainty estimates on the fitting coefficients r_j. We provide StarFormationHistories.mcmc_sample for this purpose. Internally this uses the multi-threaded affine-invariant MCMC sampler from KissMCMC.jl to perform the sampling, which is based on the same algorithm as Python's emcee (specifically, their emcee.moves.StretchMove). There are other MCMC packages like AdvancedMH.jl that offer additional features like distributed execution. ","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"StarFormationHistories.mcmc_sample","category":"page"},{"location":"fitting/unconstrained/#StarFormationHistories.mcmc_sample","page":"High-Level Methods for Unconstrained Fitting","title":"StarFormationHistories.mcmc_sample","text":"result::MCMCChains.Chains =\nmcmc_sample(models::AbstractVector{<:AbstractMatrix{T}},\n data::AbstractMatrix{S},\n x0::Union{AbstractVector{<:AbstractVector{<:Number}}, AbstractMatrix{<:Number}},\n nwalkers::Integer,\n nsteps::Integer;\n nburnin::Integer=0,\n nthin::Integer=1,\n a_scale::Number=2.0,\n use_progress_meter::Bool=true)\nmcmc_sample(models::AbstractMatrix{<:Number},\n data::AbstractVector{<:Number},\n args...; kws...)\n\nSamples the posterior of the coefficients coeffs such that the full model of the observational data is sum(models .* coeffs). Uses the Poisson likelihood ratio as defined by equations 7–10 of Dolphin 2002. Sampling is done using the affine-invariant MCMC sampler implemented in KissMCMC.jl, which is analogous to Python's emcee.moves.StretchMove. This method will automatically parallelize over threads. If you need distributed execution, you may want to look into AdvancedMH.jl.\n\nThe second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\nArguments\n\nmodels::AbstractVector{<:AbstractMatrix{<:Number}} is a vector of equal-sized matrices that represent the template Hess diagrams for the simple stellar populations that compose the observed Hess diagram.\ndata::AbstractMatrix{<:Number} is the Hess diagram for the observed data.\nx0::Union{AbstractVector{<:AbstractVector{<:Number}}, AbstractMatrix{<:Number}} are the initial positions for the MCMC walkers. If providing a vector of vectors, it must be a vector of length nwalkers with each internal vector having length equal to length(models). You can alternatively provide a matrix of size (nwalkers, length(models)) or (length(models), nwalkers).\nnwalkers::Integer is the number of unique walkers or chains to use.\nnsteps::Integer is the number of steps evolve the walkers for.\n\nKeyword Arguments\n\nnburnin::Integer=0 is the number of steps to discard from the start of each chain.\nnthin::Integer=1 is the factor by which to thin the chain; walker positions will only be saved every nthin steps.\na_scale::Number=2.0 is the scale parameter for the stretch move; probably shouldn't need to be changed.\nuse_progress_Meter::Bool=true indicates whether or not to show a progress bar during the MCMC procedure.\n\nReturns\n\nresult is a MCMCChains.Chains instance which enables easy calculation of diagnostic and summary statistics. This type can be indexed and used like a 3-D array of samples with shape (nsteps, length(models), nwalkers).\n\nNotes\n\nWhen displaying result to the terminal it will display summary statistics (MCMCChains.summarystats) and quantiles (MCMCChains.quantile) by calling the MCMCChains.describe method. This can take a second but is nice to have as an option.\nThe highest posterior density interval, which is the narrowest credible interval that includes the posterior mode, can be calculated with the MCMCChains.hpd method. \nIf you want to extract the array of samples from the MCMCChains.Chains object, you can index result.value – this will return an AxisArray but can be converted to a normal array with Array(result.value).\n\nExamples\n\nimport Distributions: Poisson\ncoeffs = rand(10) # SFH coefficients we want to sample\nmodels = [rand(100,100) .* 100 for i in 1:length(coeffs)] # Vector of model Hess diagrams\ndata = rand.(Poisson.( sum(models .* coeffs) ) ) # Poisson-sample the model `sum(models .* coeffs)`\nnwalkers = 1000\nnsteps = 400\nx0 = rand(nwalkers, length(coeffs)) # Initial walker positions\nresult = mcmc_sample(models, data, x0, nwalkers, nsteps) # Sample\nChains MCMC chain (400×10×1000 Array{Float64, 3}) ...\n\n\n\n\n\n","category":"function"},{"location":"fitting/unconstrained/#Posterior-Sampling:-Change-of-Variables-and-HMC","page":"High-Level Methods for Unconstrained Fitting","title":"Posterior Sampling: Change of Variables and HMC","text":"","category":"section"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"Dolphin 2013 examined methods for obtaining uncertainties on the fitted coefficients (the r_j in Equation 1 of Dolphin 2002) and found that the Hamiltonian Monte Carlo (HMC) approach allowed for relatively efficient sampling of the posterior distribution when considering many isochrones in the modelling process. HMC requires that the variables to be fit are continuous over the real numbers and so requires a change of variables. Rather than sampling the variables r_j directly, we can sample theta_j = textln left( r_j right) such that the sampled variables are continuous over the real numbers -infty theta_j infty while the r_j=textexp left( theta_j right) coefficients are bounded from 0 r_j infty. Using a logarithmic transformation has the additional benefit that the gradient of the Poisson likelihood ratio is still continuous and easy to compute analytically.","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"While maximum likelihood estimates are invariant under variable transformations, sampling methods like HMC are not, as formally the posterior being sampled from is a distribution and therefore must be integrable over the sampling coefficients. We can write the posterior from which we wish to sample as","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"beginaligned\np(r_j D) = fracp(D r_j) p(r_j)Z \np(boldsymbolr D) = frac1Z prod_j p(D r_j) p(r_j) \n-textln p(boldsymbolr D) = textln Z - sum_j textln p(D r_j) + textln p(r_j) \n= textln Z - textln mathscrL + sum_j textln p(r_j)\nendaligned","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"where Z is the Bayesian evidence (a constant that can be neglected for sampling methods), p left( r_j right) is the prior on the star formation history, and mathscrL is the Poisson likelihood ratio discussed above. An uninformative (and unnormalized) prior on the coefficients r_j could take the form of","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"p(r_j) = begincases\n1 r_j geq 0\n0 r_j 0\nendcases","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"such that, if the coefficients r_j are guaranteed to be positive, the final term becomes zero (since textln(1)=0) and","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"-textln p(boldsymbolr D) = textln Z - textln mathscrL","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"When sampling with methods like HMC, constants like textln Z can be neglected and -textln p(boldsymbolr D) propto - textln mathscrL such that the posterior is approximated by the likelihood surface.","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"Let us consider now what happens when we wish to do a variable transformation from r_j to theta_j = textln (r_j). From above we can write the posterior as","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"p(r_j D) = fracp(D r_j) p(r_j)Z ","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"Under the change of variables formula we can write","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"beginaligned\np(theta_j D) = p(r_j D) left fracd r_jd theta_j right \n= p(r_j D) left fracd theta_jd r_j right^-1\nendaligned","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"where left fracd theta_jd r_j right^-1 is often called the Jacobian correction. We choose theta_j such that","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"beginaligned\ntheta_j = textln ( r_j ) \nleft fracd theta_jd r_j right = frac1r_j \nr_j = textexp (theta_j) \nendaligned","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"which leads to a posterior of","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"p(theta_j D) = textexp (theta_j) times p(textexp (theta_j) D) = r_j times p(r_j D) ","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"We can then write the product over the theta_j as","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"beginaligned\np(boldsymboltheta D) = frac1Z prod_j r_j p(D r_j) p(r_j) \n-textln p(boldsymboltheta D) = textln Z - sum_j textln (r_j) + textln p(D r_j) + textln p(r_j) \n= textln Z - sum_j textln p(D r_j) + textln p(r_j) - sum_j theta_j \n= -textln p(boldsymbolr D) - sum_j theta_j \n= -textln p(boldsymbolr D) - sum_j textln (r_j)\nendaligned","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"The choice of a logarithmic transformation means that the negative logarithm of the posterior (which is what HMC uses for its objective function) has this very simple form which allows for simple analytic gradients as well. Once samples of theta have been obtained from this distribution via HMC or any other sampling method, they can be directly transformed back to the standard coefficients r_j = textexp(theta_j).","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"The method hmc_sample implements this approach for sampling the theta_j coefficients; these samples can then be used to estimate random uncertainties on the derived star formation history.","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"StarFormationHistories.hmc_sample","category":"page"},{"location":"fitting/unconstrained/#StarFormationHistories.hmc_sample","page":"High-Level Methods for Unconstrained Fitting","title":"StarFormationHistories.hmc_sample","text":"hmc_sample(models::AbstractVector{T},\n data::AbstractMatrix{<:Number},\n nsteps::Integer [, nchains::Integer];\n rng::Random.AbstractRNG=Random.default_rng(),\n kws...)\n where {S <: Number, T <: AbstractMatrix{S}}\nhmc_sample(models::AbstractMatrix{S},\n data::AbstractVector{<:Number},\n nsteps::Integer;\n rng::AbstractRNG=default_rng(),\n kws...)\n where S <: Number\n\nFunction to sample the posterior of the coefficients coeffs such that the full model of the observational data is sum(models .* coeffs). Uses the Poisson likelihood ratio as defined by equations 7–10 of Dolphin 2002 along with a logarithmic transformation of the coeffs so that the fitting variables are continuous and differentiable over all reals. Sampling is done using the No-U-Turn sampler as implemented in DynamicHMC.jl, which is a form of dynamic Hamiltonian Monte Carlo.\n\nThe second call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\nArguments\n\nmodels::AbstractVector{<:AbstractMatrix{<:Number}} is a vector of equal-sized matrices that represent the template Hess diagrams for the simple stellar populations that compose the observed Hess diagram.\ndata::AbstractMatrix{<:Number} is the Hess diagram for the observed data.\nnsteps::Integer is the number of samples to draw per chain.\n\nOptional Arguments\n\nnchains::Integer: If this argument is not provided, this method will return a single chain. If this argument is provided, it will sample nchains chains using all available Julia threads and will return a vector of the individual chains.\n\nKeyword Arguments\n\nrng::Random.AbstractRNG is the random number generator that will be passed to DynamicHMC.jl. If nchains is provided this method will attempt to sample in parallel, requiring a thread-safe rng such as that provided by Random.default_rng(). \n\nAll other keyword arguments kws... will be passed to DynamicHMC.mcmc_with_warmup or DynamicHMC.mcmc_keep_warmup depending on whether nchains is provided.\n\nReturns\n\nIf nchains is not provided, returns a NamedTuple as summarized in DynamicHMC.jl's documentation. In short, the matrix of samples can be extracted and transformed as exp.( result.posterior_matrix ). Statistics about the chain can be obtained with DynamicHMC.Diagnostics.summarize_tree_statistics(result.tree_statistics); you want to see a fairly high acceptance rate (>0.5) and the majority of samples having termination criteria being \"turning.\" See DynamicHMC.jl's documentation for more information.\nIf nchains is provided, returns a vector of length nchains of the same NamedTuples described above. The samples from each chain in the returned vector can be stacked to a single (nsamples, nchains, length(models)) matrix with DynamicHMC.stack_posterior_matrices(result). \n\nExamples\n\nimport DynamicHMC\nimport StatFormationHistories: hmc_sample\nimport Statistics: mean\n# Run sampler using progress meter to monitor progress\n# assuming you have constructed some templates `models` and your observational Hess diagram `data`\nresult = hmc_sample( models, data, 1000; reporter=DynamicHMC.ProgressMeterReport())\n# The chain values are stored in result.posterior matrix; extract them with `result.posterior_matrix`\n# An exponential transformation is needed since the optimization internally uses a logarithmic \n# transformation and samples log(θ) rather than θ directly. \nmc_matrix = exp.( result.posterior_matrix )\n# We can look at some statistics from the chain; want to see high acceptance rate (>0.5) and large % of\n# \"turning\" for termination criteria. \nDynamicHMC.Diagnostics.summarize_tree_statistics(result.tree_statistics)\n Hamiltonian Monte Carlo sample of length 1000\n acceptance rate mean: 0.92, 5/25/50/75/95%: 0.65 0.88 0.97 1.0 1.0\n termination: divergence => 0%, max_depth => 0%, turning => 100%\n depth: 0 => 0%, 1 => 64%, 2 => 36%\n# mc_matrix has size `(length(models), nsteps)` so each column is an independent\n# sample of the SFH as defined by the coefficients and the rows contain the samples\n# for each parameter. \nmstar_tot = sum.(eachcol(mc_matrix)) # Total stellar mass of the modelled system per sample\nmc_means = mean.(eachrow(mc_matrix)) # Mean of each coefficient evaluated across all samples\n# Example with multiple chains sampled in parallel via multi-threading\nimport Threads\nt_result = hmc_sample( models, data, 1000, Threads.nthreads(); reporter=DynamicHMC.ProgressMeterReport())\n# Combine the multiple chains into a single matrix and transform\n# Can then use the same way as `mc_matrix` above\nmc_matrix = exp.( DynamicHMC.pool_posterior_matrices(t_result) )\n\n\n\n\n\n","category":"function"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"See the DynamicHMC.jl documentation for more information on how to use the chains that are output by this method.","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"Inspection of the samples generated by hmc_sample shows that the posterior defined by the above model is typically smooth, well-behaved, and unimodal. In particular, we find that the sampled r_j for coefficients that are non-zero in the MLE are approximately Gaussian distributed while the logarithms of the sampled r_j are roughly Gaussian distributed for coefficients that are zero in the MLE; i.e.","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"begincases\nX_j sim mathcalN hat r_j 0 \ntextln left( X_j right) sim mathcalN hat r_j = 0 \nendcases","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"where X_j are the samples of r_j obtained from the posterior and hat r_j is the maximum likelihood estimate of r_j. ","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"This indicates we may be able to approximate the posterior in the region surrounding the maximum a posteriori (MAP) value by the inverse of the Hessian matrix (see, e.g., Dovi et al. 1991), allowing us to estimate parameter uncertainties very cheaply. The inverse of the Hessian matrix is exactly equal to the variance-covariance matrix of the parameters for a Gaussian probability distribution; for other probability distributions, the inverse of the Hessian approximates the variance-covariance matrix of the parameters when the second-order expansion defined by the Hessian at the maximum is a reasonable approximation to the real objective function being optimized. A particularly simple form arises when the logarithm of the objective is quadratic in the fitting parameters, as in the Gaussian case, because the second derivatives of the objective are constant and do not depend on the fitting parameters or the MAP estimate.","category":"page"},{"location":"fitting/unconstrained/#Maximum-a-Posteriori-Optimization","page":"High-Level Methods for Unconstrained Fitting","title":"Maximum a Posteriori Optimization","text":"","category":"section"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"Direct computation of the Hessian and its inverse is expensive, so we'd like another way to obtain it. The first-order, quasi-Newton BFGS optimization algorithm provides such a method as it iteratively builds a dense approximation to the inverse Hessian using the change in the gradient of the objective, which we can compute analytically. It is, however, much less memory efficient than the LBFGS algorithm we use in StarFormationHistories.fit_templates_lbfgsb. For moderate isochrone grids up to a few hundred model templates, this is not a problem. Beyond this it may be better to use StarFormationHistories.fit_templates_lbfgsb to obtain the MLE and hmc_sample to obtain posterior samples.","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"We implement this optimization scheme in fit_templates, which is our recommended method for unconstrained SFH fitting (i.e., direct fitting of the r_j coefficients). See the next section for notes on more complicated, hierarchical models that can incorporate features like metallicity distribution functions.","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"StarFormationHistories.fit_templates\nStarFormationHistories.LogTransformFTResult","category":"page"},{"location":"fitting/unconstrained/#StarFormationHistories.fit_templates","page":"High-Level Methods for Unconstrained Fitting","title":"StarFormationHistories.fit_templates","text":"result = fit_templates(models::AbstractVector{T},\n data::AbstractMatrix{<:Number};\n x0::AbstractVector{<:Number} = ones(S,length(models)),\n kws...) where {S <: Number, T <: AbstractMatrix{S}}\n\nFinds both maximum likelihood estimate (MLE) and maximum a posteriori estimate (MAP) for the coefficients coeffs such that the composite Hess diagram model is sum(models .* coeffs) using the provided templates models and the observed Hess diagram data. Utilizes the Poisson likelihood ratio (equations 7–10 in Dolphin 2002) for the likelihood of the data given the model. See the examples in the documentation for comparisons of the results of this method and hmc_sample which samples the posterior via Hamiltonian Monte Carlo. \n\nArguments\n\nmodels::AbstractVector{AbstractMatrix{<:Number}}: the list of template Hess diagrams for the simple stellar populations (SSPs) being considered; all must have the same size.\ndata::AbstractMatrix{<:Number}: the observed Hess diagram; must match the size of the templates contained in models.\n\nKeyword Arguments\n\nx0: The vector of initial guesses for the stellar mass coefficients. You should basically always be calculating and passing this keyword argument; we provide StarFormationHistories.construct_x0 to prepare x0 assuming constant star formation rate, which is typically a good initial guess.\n\nOther kws... are passed to Optim.options to set things like convergence criteria for the optimization. \n\nReturns\n\nresult is a NamedTuple containing two StarFormationHistories.LogTransformFTResult. The two components of result are result.map or result[1], which contains the results of the MAP optimization, and result.mle or result[2], which contains the results of the MLE optimization. The documentation for StarFormationHistories.LogTransformFTResult contains more information about these types, but briefly they contain the following fields, accessible as, e.g., result.map.μ, result.map.σ, etc.:\n\nμ::Vector{<:Number} are the optimized coeffs at the maximum.\nσ::Vector{<:Number} are the standard errors on the coeffs μ calculated from an estimate of the inverse Hessian matrix evaluated at μ. The inverse of the Hessian matrix at the maximum of the likelihood (or posterior) is a estimator for the variance-covariance matrix of the parameters, but is only accurate when the second-order expansion given by the Hessian at the maximum is a good approximation to the function being optimized (i.e., when the optimized function is approximately quadratic around the maximum; see Dovi et al. 1991 for more information). We find this is often the case for the MAP estimate, but the errors found for coefficients that are ≈0 in the MLE are typically unrealistically small. For coefficients significantly greater than 0, the σ values from the MAP and MLE are typically consistent to 5–10%.\ninvH::Matrix{<:Number} is the estimate of the inverse Hessian matrix at μ that was used to derive σ. The optimization is done under a logarithmic transform, such that θ[j] = log(coeffs[j]) are the actual parameters optimized, so the entries in the Hessian are actually\n\nH^(jk) ( boldsymbolhat theta ) = left fracpartial^2 J(boldsymboltheta)partial theta_j partial theta_k rightvert_boldsymboltheta=boldsymbolhat theta\n\nresult is the full object returned by the optimization from Optim.jl; this is of type Optim.MultivariateOptimizationResults. Remember that the optimization is done with parameters θ[j] = log(coeffs[j]) when dealing with this raw output. This means that, for example, we calculate result.map.μ as exp.(Optim.minimizer(result.map.result)).\n\nThe special property of the StarFormationHistories.LogTransformFTResult type is that you can draw a set of N::Integer random parameter samples from the result using the inverse Hessian approximation discussed above by doing rand(result.map, N). This type implements the random sampling API of Distributions.jl so the other standard sampling methods should work as well. In our tests these samples compare very favorably against those from hmc_sample, which samples the posterior via Hamiltonian Monte Carlo and is therefore more robust but much more expensive. We compare these methods in the examples.\n\nNotes\n\nThis method uses the BFGS method from Optim.jl internally because it builds and tracks the inverse Hessian matrix approximation which can be used to estimate parameter uncertainties. BFGS is much more memory-intensive than LBFGS (as used for StarFormationHistories.fit_templates_lbfgsb) for large numbers of parameters (equivalently, many models), so you should consider LBFGS to solve for the MLE along with hmc_sample to sample the posterior if you are using a large grid of models (greater than a few hundred).\nThe BFGS implementation we use from Optim.jl uses BLAS operations during its iteration. The OpenBLAS that Julia ships with will often default to running on multiple threads even if Julia itself is started with only a single thread. You can check the current number of BLAS threads with import LinearAlgebra: BLAS; BLAS.get_num_threads(). For the problem sizes typical of this function we actually see performance regression with larger numbers of BLAS threads. For this reason you may wish to use BLAS in single-threaded mode; you can set this as import LinearAlgebra: BLAS; BLAS.set_num_threads(1).\n\n\n\n\n\nfit_templates(models::AbstractMatrix{S},\n data::AbstractVector{<:Number};\n x0::AbstractVector{<:Number} = ones(S,length(models)),\n kws...) where S <: Number\n\nThis call signature supports the flattened formats for models and data. See the notes for the flattened call signature of StarFormationHistories.composite! for more details.\n\n\n\n\n\n","category":"function"},{"location":"fitting/unconstrained/#StarFormationHistories.LogTransformFTResult","page":"High-Level Methods for Unconstrained Fitting","title":"StarFormationHistories.LogTransformFTResult","text":"LogTransformFTResult(μ::AbstractVector{<:Number},\n σ::AbstractVector{<:Number},\n invH::AbstractMatrix{<:Number},\n result)\n\nType for containing the maximum likelihood estimate (MLE) and maximum a posteriori (MAP) results from fit_templates. The fitted coefficients are available in the μ field. Estimates of the standard errors are available in the σ field. These have both been transformed from the native logarithmic fitting space into natural units (i.e., stellar mass or star formation rate).\n\ninvH contains the estimated inverse Hessian of the likelihood / posterior at the maximum point in the logarithmic fitting units. result is the full result object returned by the optimization routine.\n\nThis type is implemented as a subtype of Distributions.Sampleable{Multivariate, Continuous} to enable sampling from an estimate of the likelihood / posterior distribution. We approximate the distribution as a multivariate Gaussian in the native (logarithmically transformed) fitting variables with covariance matrix invH and means log.(μ). We find this approximation is good for the MAP result but less robust for the MLE. You can obtain N::Integer samples from the distribution by rand(R, N) where R is an instance of this type; this will return a size length(μ) x N matrix, or fail if invH is not positive definite.\n\nExamples\n\njulia> result = fit_templates(models, data);\n\njulia> typeof(result.map)\nStarFormationHistories.LogTransformFTResult{...}\n\njulia> size(rand(result.map, 3)) == (length(models),3)\ntrue\n\n\n\n\n\n","category":"type"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"Once you have obtained stellar mass coefficients from the above methods, you can convert them into star formation rates and compute per-age mean metallicities with StarFormationHistories.calculate_cum_sfr.","category":"page"},{"location":"fitting/unconstrained/","page":"High-Level Methods for Unconstrained Fitting","title":"High-Level Methods for Unconstrained Fitting","text":"StarFormationHistories.calculate_cum_sfr","category":"page"},{"location":"fitting/unconstrained/#StarFormationHistories.calculate_cum_sfr","page":"High-Level Methods for Unconstrained Fitting","title":"StarFormationHistories.calculate_cum_sfr","text":"(unique_logAge, cum_sfh, sfr, mean_MH) =\n calculate_cum_sfr(coeffs::AbstractVector,\n logAge::AbstractVector,\n MH::AbstractVector,\n T_max::Number;\n normalize_value=1,\n sorted::Bool=false)\n\nCalculates cumulative star formation history, star formation rates, and mean metallicity evolution as functions of logAge = log10(age [yr]).\n\nArguments\n\ncoeffs::AbstractVector is a vector of stellar mass coefficients such as those returned by fit_templates, for example. Actual stellar mass in stellar population j is coeffs[j] * normalize_value.\nlogAge::AbstractVector is a vector giving the log10(age [yr]) of the stellar populations corresponding to the provided coeffs. For the purposes of calculating star formation rates, these are assumed to be left-bin edges.\nMH::AbstractVector is a vector giving the metallicities of the stellar populations corresponding to the provided coeffs.\nT_max::Number is the rightmost final bin edge for calculating star formation rates. For example, you might have logAge=[6.6, 6.7, 6.8] in which case a final logAge of 6.9 would give equal bin widths. In this case you would set T_max = exp10(6.9) / 1e9 ≈ 0.0079 so that the width of the final bin for the star formation rate calculation has the same log10(Age [yr]) step as the other bins.\n\nKeyword Arguments\n\nnormalize_value is a multiplicative prefactor to apply to all the coeffs; same as the keyword in partial_cmd_smooth.\nsorted::Bool is either true or false and signifies whether to assume logAge is sorted.\n\nReturns\n\nunique_logAge::Vector is essentially unique(sort(logAge)) and provides the x-values you would plot the other returned vectors against.\ncum_sfh::Vector is the normalized cumulative SFH implied by the provided coeffs. This is ~1 at the most recent time in logAge and decreases as logAge increases.\nsfr::Vector gives the star formation rate across each bin in unique_logAge. If coeffs .* normalize_value are in units of solar masses, then sfr is in units of solar masses per year.\nmean_MH::Vector gives the stellar-mass-weighted mean metallicity of the stellar population as a function of unique_logAge. \n\n\n\n\n\n","category":"function"},{"location":"examples/#examples","page":"Examples","title":"Examples","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"We have constructed an example Jupyter notebook that highlights some common use cases supported by the package. It is recommended that you read some of the background in our main documentation before or concurrently with the example. The notebook is available in the repository as examples/fitting1.ipynb and can be viewed in rendered form at this link. It relies on a custom isochrone file which can be made available upon request to the package author.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"There are other scripts in the examples directory of the source repository that are used to generate figures for the documentation and provide more targeted examples of usage.","category":"page"},{"location":"#overview","page":"Overview","title":"Overview","text":"","category":"section"},{"location":"","page":"Overview","title":"Overview","text":"This package enables many different kinds of analysis related to astrophysical star formation histories (SFHs). Core among these functionalities are generation of mock color-magnitude diagrams (CMDs) from input SFHs and fitting of SFHs from observed CMDs. The methods of this package have been designed to be simple to use but easy to extend. It is recommended that you use this package in conjunction with InitialMassFunctions.jl which provides implementations of the most popular stellar initial mass functions as new types which are natively supported by this package's methods.","category":"page"}] } diff --git a/dev/sigma_distribution_binaries.svg b/dev/sigma_distribution_binaries.svg index bc4c1a4..633becb 100644 --- a/dev/sigma_distribution_binaries.svg +++ b/dev/sigma_distribution_binaries.svg @@ -6,7 +6,7 @@ - 2024-09-28T21:34:20.896888 + 2024-10-07T06:58:04.970336 image/svg+xml @@ -40,213 +40,213 @@ z +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> +" clip-path="url(#p39d238024c)" style="fill: #1f77b4; stroke: #000000; stroke-linejoin: miter"/> - - + @@ -293,7 +293,7 @@ z - + @@ -329,7 +329,7 @@ z - + @@ -751,12 +751,12 @@ z - - + @@ -780,12 +780,12 @@ z - + - + - + - + @@ -830,12 +830,12 @@ z - + - + - + - + - + - + - + - + - @@ -1288,9 +1285,9 @@ z - + - + + + - + - - + + @@ -1430,7 +1478,7 @@ z - + diff --git a/dev/simulate/index.html b/dev/simulate/index.html index 3c150f9..fc8988f 100644 --- a/dev/simulate/index.html +++ b/dev/simulate/index.html @@ -8,11 +8,11 @@ mag_lim::Number = Inf, mag_lim_name::String = "V", binary_model::StarFormationHistories.AbstractBinaryModel = - StarFormationHistories.RandomBinaryPairs(0.3))

Generates a random sample of stars from an isochrone defined by the provided initial stellar masses mini_vec, absolute magnitudes mags, and filter names mag_names with total population birth stellar mass limit (e.g., 1e5 solar masses). Initial stellar masses are sampled from the provided imf.

Arguments

  • mini_vec::AbstractVector{<:Number} contains the initial masses (in solar masses) for the stars in the isochrone; must be mutable as we call Interpolations.deduplicate_knots!(mini_vec).
  • mags contains the absolute magnitudes from the isochrone in the desired filters corresponding to the same stars as provided in mini_vec. mags is internally interpreted and converted into a standard format by StarFormationHistories.ingest_mags. Valid inputs are:
    • mags::AbstractVector{AbstractVector{<:Number}}, in which case the length of the outer vector length(mags) can either be equal to length(mini_vec), in which case the length of the inner vectors must all be equal to the number of filters you are providing, or the length of the outer vector can be equal to the number of filters you are providing, and the length of the inner vectors must all be equal to length(mini_vec); this is the more common use-case.
    • mags::AbstractMatrix{<:Number}, in which case mags must be 2-dimensional. Valid shapes are size(mags) == (length(mini_vec), nfilters) or size(mags) == (nfilters, length(mini_vec)), with nfilters being the number of filters you are providing.
  • mag_names::AbstractVector{String} contains strings describing the filters you are providing in mags; an example might be ["B","V"]. These are used when mag_lim is finite to determine what filter you want to use to limit the faintest stars you want returned.
  • limit::Number gives the total birth stellar mass of the population you want to sample. See the "Notes" section on population masses for more information.
  • imf::Distributions.Sampleable{Distributions.Univariate, Distributions.Continuous} is a sampleable continuous univariate distribution implementing a stellar initial mass function with a defined rand(rng::Random.AbstractRNG, imf) method to use for sampling masses. All instances of Distributions.ContinuousUnivariateDistribution are also valid. Implementations of commonly used IMFs are available in InitialMassFunctions.jl.

Keyword Arguments

  • dist_mod::Number=0 is the distance modulus (see StarFormationHistories.distance_modulus) you wish to have added to the returned magnitudes to simulate a population at a particular distance.
  • rng::Random.AbstractRNG=Random.default_rng() is the rng instance that will be used to sample the stellar initial masses from imf.
  • mag_lim::Number=Inf gives the faintest apparent magnitude for stars you want to be returned in the output. Stars fainter than this magnitude will still be sampled and contribute properly to the total mass of the population, but they will not be returned.
  • mag_lim_name::String="V" gives the filter name (as contained in mag_names) to use when considering if a star is fainter than mag_lim. This is unused if mag_lim is infinite.
  • binary_model::StarFormationHistories.AbstractBinaryModel=StarFormationHistories.RandomBinaryPairs(0.3) is an instance of a model for treating binaries; currently provided options are NoBinaries, RandomBinaryPairs, and BinaryMassRatio.

Returns

(sampled_masses, sampled_mags) defined as

  • sampled_masses::Vector{SVector{N,eltype(imf)}} is a vector containing the initial stellar masses of the stars sampled by sample_system; see that method's documentation for details on format. In short, each StaticArrays.SVector represents one stellar system and each entry in each StaticArrays.SVector is one star in that system. Entries will be 0 when companions could have been sampled but were not (i.e., when using a binary_model that supports multi-star systems).
  • sampled_mags::Vector{SVector{N,<:Number}} is a vector containing StaticArrays.SVectors with the multi-band magnitudes of the sampled stars. To get the magnitude of star i in band j, you index as sampled_mags[i][j]. This can be reinterpreted as a 2-dimensional Matrix with reduce(hcat,sampled_mags).

Notes

Population Masses

Given a particular isochrone with an initial mass vector mini_vec, it will never cover the full range of stellar birth masses because stars that die before present-day are not included in the isochrone. However, these stars were born, and so contribute to the total birth mass of the system. There are two ways to properly account for this lost mass when sampling:

  1. Set the upper limit for masses that can be sampled from the imf distribution to a physical value for the maximum birth mass of stars (e.g., 100 solar masses). In this case, these stars will be sampled from imf, and will contribute their masses to the system, but they will not be returned if their birth mass is greater than maximum(mini_vec). This is typically easiest for the user and only results in ∼15% loss of efficiency for 10 Gyr isochrones. This approach is preferred when sampling with binaries.
  2. Set the upper limit for masses that can be sampled from the imf distribution to maximum(mini_vec) and adjust limit to respect the amount of initial stellar mass lost by not sampling higher mass stars. This can be calculated as new_limit = limit * ( QuadGK.quadgk(x->x*pdf(imf,x), minimum(mini_vec), maximum(mini_vec))[1] / QuadGK.quadgk(x->x*pdf(imf,x), minimum(imf), maximum(imf))[1] ), with the multiplicative factor being the fraction of the population stellar mass contained in stars with initial masses between minimum(mini_vec) and maximum(mini_vec); this factor is the ratio

\[\frac{\int_a^b \ m \times \frac{dN(m)}{dm} \ dm}{\int_0^∞ \ m \times \frac{dN(m)}{dm} \ dm}.\]

Note that, if binaries are included, this approach only forms binary pairs between stars whose masses are less than maximum(mini_vec). This is probably not desired, so we recommend the previous approach when including binaries.

source
StarFormationHistories.generate_stars_magFunction
(sampled_masses, sampled_mags) =  generate_stars_mag(mini_vec::AbstractVector{<:Number}, mags, mag_names::AbstractVector{String}, absmag::Real, absmag_name::String, imf::Distributions.Sampleable{Distributions.Univariate,Distributions.Continuous}; dist_mod::Number=0, rng::AbstractRNG=default_rng(), mag_lim::Number=Inf, mag_lim_name::String="V", binary_model::StarFormationHistories.AbstractBinaryModel=RandomBinaryPairs(0.3))

Generates a mock stellar population from an isochrone defined by the provided initial stellar masses mini_vec, absolute magnitudes mags, and filter names mag_names. The population is sampled to a total absolute magnitude absmag::Real (e.g., -7 or -12) in the filter absmag_name::String (e.g., "V" or "F606W") which is contained in the provided mag_names::AbstractVector{String}. Other arguments are shared with generate_stars_mass, which contains the main documentation.

Notes

Population Magnitudes

Unlike when sampling a population to a fixed initial birth mass, as is implemented in generate_stars_mass, when generating a population up to a fixed absolute magnitude, only stars that survive to present-day contribute to the flux of the population. If you choose to limit the apparent magnitude of stars returned by passing the mag_lim and mag_lim_name keyword arguments, stars fainter than your chosen limit will still be sampled and will still contribute their luminosity to the total population, but they will not be contained in the returned output.

source

Complex Stellar Populations

StarFormationHistories.generate_stars_mass_compositeFunction
(sampled_masses, sampled_mags) = generate_stars_mass_composite(mini_vec::AbstractVector{<:AbstractVector{<:Number}}, mags::AbstractVector, mag_names::AbstractVector{String}, limit::Number, massfrac::AbstractVector{<:Number}, imf::Sampleable{Univariate,Continuous}; kws...)

Generates a random sample of stars with a complex star formation history using multiple isochrones. Very similar to generate_stars_mass except the isochrone-related arguments mini_vec and mags should now be vectors of vectors containing the relevant data for the full set of isochrones to be considered. The total birth stellar mass of the sampled population is given by limit. The proportion of this mass allotted to each of the individual isochrones is given by the entries of the massfrac vector. This basically just proportions limit according to massfrac and calls generate_stars_mass for each of the individual stellar populations; as such it is set up to multi-thread across the multiple stellar populations.

Arguments

  • mini_vec::AbstractVector{<:AbstractVector{<:Number}} contains the initial masses (in solar masses) for the stars in each isochrone; the internal vectors must be mutable as we will call Interpolations.deduplicate_knots! on each. The length of mini_vec should be equal to the number of isochrones.
  • mags contains the absolute magnitudes from the isochrones in the desired filters corresponding to the same stars as provided in mini_vec. The length of mags should be equal to the number of isochrones. The individual elements of mags are each internally interpreted and converted into a standard format by StarFormationHistories.ingest_mags. The valid formats for the individual elements of mags are:
    • AbstractVector{AbstractVector{<:Number}}, in which case the length of the vector length(mags[i]) can either be equal to length(mini_vec[i]), in which case the length of the inner vectors must all be equal to the number of filters you are providing, or the length of the outer vector can be equal to the number of filters you are providing, and the length of the inner vectors must all be equal to length(mini_vec[i]); this is the more common use-case.
    • AbstractMatrix{<:Number}, in which case mags[i] must be 2-dimensional. Valid shapes are size(mags[i]) == (length(mini_vec[i]), nfilters) or size(mags[i]) == (nfilters, length(mini_vec[i])), with nfilters being the number of filters you are providing.
  • mag_names::AbstractVector{String} contains strings describing the filters you are providing in mags; an example might be ["B","V"]. These are used when mag_lim is finite to determine what filter you want to use to limit the faintest stars you want returned. These are assumed to be the same for all isochrones.
  • limit::Number gives the total birth stellar mass of the population you want to sample.
  • massfrac::AbstractVector{<:Number} is vector giving the relative fraction of mass allotted to each individual stellar population; length must be equal to the length of mini_vec and mags.
  • imf::Distributions.Sampleable{Distributions.Univariate, Distributions.Continuous} is a sampleable continuous univariate distribution implementing a stellar initial mass function with a defined rand(rng::Random.AbstractRNG, imf) method to use for sampling masses. All instances of Distributions.ContinuousUnivariateDistribution are also valid. Implementations of commonly used IMFs are available in InitialMassFunctions.jl.

Keyword Arguments

All keyword arguments kws... are passed to generate_stars_mass; you should refer to that method's documentation for more information.

Returns

  • sampled_masses::Vector{Vector{SVector{N,eltype(imf)}}} is a vector of vectors containing the initial stellar masses of the sampled stars. The outer vectors are separated by the isochrone the stars were generated from; i.e., all of sampled_masses[1] were sampled from mini_vec[1] and so on. These can be concatenated into a single vector with reduce(vcat,sampled_masses). The format of the contained StaticArrays.SVectors are as output from sample_system; see that method's documentation for more details.
  • sampled_mags::Vector{Vector{SVector{N,<:Number}}} is a vector of vectors containing StaticArrays.SVectors with the multi-band magnitudes of the sampled stars. The outer vectors are separated by the isochrone the stars were generated from; i.e. all of sampled_mags[1] were sampled from mags[1] and so on. To get the magnitude of star i in band j sampled from isochrone k, you would do sampled_mags[k][i][j]. This can be concatenated into a Vector{SVector} with reduce(vcat,sampled_mags) and a 2-D Matrix with reduce(hcat,reduce(vcat,sampled_mags)).
source
StarFormationHistories.generate_stars_mag_compositeFunction
(sampled_masses, sampled_mags) = generate_stars_mag_composite(mini_vec::AbstractVector{<:AbstractVector{<:Number}}, mags::AbstractVector, mag_names::AbstractVector{String}, absmag::Number, absmag_name::String, fracs::AbstractVector{<:Number}, imf::Sampleable{Univariate,Continuous}; frac_type::String="lum", kws...)

Generates a random sample of stars with a complex star formation history using multiple isochrones. Very similar to generate_stars_mag except the isochrone-related arguments mini_vec and mags should now be vectors of vectors containing the relevant data for the full set of isochrones to be considered. The total absolute magnitude of the sampled population is given by absmag. The proportion of the luminosity allotted to each of the individual isochrones is given by the entries of the frac vector. This basically just proportions the luminosity according to frac and calls generate_stars_mag for each of the individual stellar populations; as such it is set up to multi-thread across the multiple stellar populations.

Arguments

  • mini_vec::AbstractVector{<:AbstractVector{<:Number}} contains the initial masses (in solar masses) for the stars in each isochrone; the internal vectors must be mutable as we will call Interpolations.deduplicate_knots! on each. The length of mini_vec should be equal to the number of isochrones.
  • mags contains the absolute magnitudes from the isochrones in the desired filters corresponding to the same stars as provided in mini_vec. The length of mags should be equal to the number of isochrones. The individual elements of mags are each internally interpreted and converted into a standard format by StarFormationHistories.ingest_mags. The valid formats for the individual elements of mags are:
    • AbstractVector{AbstractVector{<:Number}}, in which case the length of the vector length(mags[i]) can either be equal to length(mini_vec[i]), in which case the length of the inner vectors must all be equal to the number of filters you are providing, or the length of the outer vector can be equal to the number of filters you are providing, and the length of the inner vectors must all be equal to length(mini_vec[i]); this is the more common use-case.
    • AbstractMatrix{<:Number}, in which case mags[i] must be 2-dimensional. Valid shapes are size(mags[i]) == (length(mini_vec[i]), nfilters) or size(mags[i]) == (nfilters, length(mini_vec[i])), with nfilters being the number of filters you are providing.
  • mag_names::AbstractVector{String} contains strings describing the filters you are providing in mags; an example might be ["B","V"]. These are used when mag_lim is finite to determine what filter you want to use to limit the faintest stars you want returned. These are assumed to be the same for all isochrones.
  • absmag::Number gives the total absolute magnitude of the complex population to be sampled.
  • fracs::AbstractVector{<:Number} is a vector giving the relative fraction of luminosity or mass (determined by the frac_type keyword argument) allotted to each individual stellar population; length must be equal to the length of mini_vec and mags.
  • imf::Distributions.Sampleable{Distributions.Univariate, Distributions.Continuous} is a sampleable continuous univariate distribution implementing a stellar initial mass function with a defined rand(rng::Random.AbstractRNG, imf) method to use for sampling masses. All instances of Distributions.ContinuousUnivariateDistribution are also valid. Implementations of commonly used IMFs are available in InitialMassFunctions.jl.

Keyword Arguments

  • frac_type::String either "lum", in which case fracs is assumed to contain the relative luminosity fractions for each individual isochrone, or "mass", in which case it is assumed that fracs contains mass fractions ("mass" is not yet implemented).

All other keyword arguments kws... are passed to generate_stars_mag; you should refer to that method's documentation for more information.

Returns

  • sampled_masses::Vector{Vector{SVector{N,eltype(imf)}}} is a vector of vectors containing the initial stellar masses of the sampled stars. The outer vectors are separated by the isochrone the stars were generated from; i.e., all of sampled_masses[1] were sampled from mini_vec[1] and so on. These can be concatenated into a single vector with reduce(vcat,sampled_masses). The format of the contained StaticArrays.SVectors are as output from sample_system; see that method's documentation for more details.
  • sampled_mags::Vector{Vector{SVector{N,<:Number}}} is a vector of vectors containing StaticArrays.SVectors with the multi-band magnitudes of the sampled stars. The outer vectors are separated by the isochrone the stars were generated from; i.e. all of sampled_mags[1] were sampled from mags[1] and so on. To get the magnitude of star i in band j sampled from isochrone k, you would do sampled_mags[k][i][j]. This can be concatenated into a Vector{SVector} with reduce(vcat,sampled_mags) and a 2-D Matrix with reduce(hcat,reduce(vcat,sampled_mags)).
source

Observational Effects

The output produced from the above methods are clean in the sense that they do not include any observational effects like photometric error or incompleteness. These effects should be implemented in a post-processing step. We provide a simple method model_cmd that accepts user-defined photometric error and completeness functions and applies them to the initial catalog, returning a Monte Carlo realization of a possible observed catalog. This method assumes Gaussian photometric errors and that the photometric error and completeness functions are separable by filter – these assumptions are not applicable for all types of data, but the source code for the method is exceedingly simple (~20 lines) and should provide an example for how you could write a similar method that more accurately reflects your data.

StarFormationHistories.model_cmdFunction
new_mags [, good_idxs] = model_cmd(mags::AbstractVector{<:AbstractVector{<:Number}}, errfuncs, completefuncs; rng::Random.AbstractRNG=Random.default_rng(), ret_idxs::Bool=false)

Simple method for modelling photometric error and incompleteness to "mock observe" a pure catalog of stellar photometry, such as those produced by generate_stars_mass and generate_stars_mag. This method assumes Gaussian photometric errors and that the photometric error and completeness functions are separable by filter.

Arguments

  • mags::AbstractVector{<:AbstractVector{<:Number}}: a vector of vectors giving the magnitudes of each star to be modelled. The first index is the per-star index and the second index is the per-filter index (so mags[10][2] would give the magnitude of the tenth star in the second filter). This is the same format as the magnitudes returned by generate_stars_mass and generate_stars_mag; to use output from the composite versions, you must first reduce(vcat,mags) before passing to this function.
  • errfuncs: an iterable (typically a vector or tuple) of callables (typically functions or interpolators) with length equal to the number of filters contained in the elements of mags. This iterable must contain callables that, when called with the associated magnitudes from mags, will return the expected 1-σ photometric error at that magnitude. The organization is such that the photometric error for star i in band j is σ_ij = errfuncs[j](mags[i][j]).
  • completefuncs: an iterable (typically a vector or tuple) of callables (typically functions or interpolators) with length equal to the number of filters contained in the elements of mags. This iterable must contain callables that, when called with the associated magnitudes from mags, will return the probability that a star with that magnitude in that band will be found in your color-magnitude diagram (this should include the original detection probability and any post-detection quality, morphology, or other cuts). The organization is such that the detection probability for star i in band j is c_ij = completefuncs[j](mags[i][j]).

Keyword Arguments

  • rng::AbstractRNG=Random.default_rng(): The object to use for random number generation.
  • ret_idxs::Bool: whether to return the indices of the input mags for the stars that were successfully "observed" and are represented in the output new_mags.

Returns

  • new_mags: an object similar to mags (i.e., a Vector{Vector{<:Number}}, Vector{SVector{N,<:Number}}, etc.) containing the magnitudes of the mock-observed stars. This will be shorter than the provided mags vector as we are modelling photometric incompleteness, and the magnitudes will also have random photometric errors added to them. This can be reinterpreted as a 2-dimensional Matrix with reduce(hcat,new_mags).
  • good_idxs: if ret_idxs is true, the vector of indices into the input mags for the stars that were successfully "observed" and are represented in the output new_mags.

Notes

  • This is a simple implementation that seeks to show a simple example of how one can post-process catalogs of "pure" stars from methods like generate_stars_mass and generate_stars_mag to include observational effects. This method assumes Gaussian photometric errors, which may not, in general, be accurate. It also assumes that the total detection probability can be modelled as the product of the single-filter detection probabilities as computed by completefuncs (i.e., that the completeness functions are separable across filters). This can be a reasonable assumption when you have separate photometric catalogs derived for each filter and you only collate them afterwards, but it is generally not a good assumption for detection algorithms that operate on simultaneously on multi-band photometry – the completeness functions for these types of algorithms are generally not separable.
source

Developer Internals

StarFormationHistories.ingest_magsFunction
new_mags = ingest_mags(mini_vec::AbstractVector, mags::AbstractVector{T}) where {S <: Number, T <: AbstractVector{S}}
-new_mags = ingest_mags(mini_vec::AbstractVector, mags::AbstractMatrix{S}) where S <: Number

Reinterprets provided mags to be in the correct format for input to Interpolations.interpolate.

Returns

  • new_mags::Base.ReinterpretArray{StaticArrays.SVector}: a length(mini_vec) vector of StaticArrays.SVectors containing the same data as mags but formatted for input to Interpolations.interpolate.
source
StarFormationHistories.sort_ingestedFunction
(new_mini_vec, new_mags) = sort_ingested(mini_vec::AbstractVector, mags::AbstractVector)

Takes mini_vec and mags and ensures that mini_vec is sorted (sometimes in PARSEC isochrones they are not) and calls Interpolations.deduplicate_knots! on mini_vec to ensure there are no repeat entries. Arguments must satisfy length(mini_vec) == length(mags).

source
StarFormationHistories.mass_limitsFunction
(mmin, mmax) = mass_limits(mini_vec::AbstractVector{<:Number}, mags::AbstractVector{T},
+                        StarFormationHistories.RandomBinaryPairs(0.3))

Generates a random sample of stars from an isochrone defined by the provided initial stellar masses mini_vec, absolute magnitudes mags, and filter names mag_names with total population birth stellar mass limit (e.g., 1e5 solar masses). Initial stellar masses are sampled from the provided imf.

Arguments

  • mini_vec::AbstractVector{<:Number} contains the initial masses (in solar masses) for the stars in the isochrone; must be mutable as we call Interpolations.deduplicate_knots!(mini_vec).
  • mags contains the absolute magnitudes from the isochrone in the desired filters corresponding to the same stars as provided in mini_vec. mags is internally interpreted and converted into a standard format by StarFormationHistories.ingest_mags. Valid inputs are:
    • mags::AbstractVector{AbstractVector{<:Number}}, in which case the length of the outer vector length(mags) can either be equal to length(mini_vec), in which case the length of the inner vectors must all be equal to the number of filters you are providing, or the length of the outer vector can be equal to the number of filters you are providing, and the length of the inner vectors must all be equal to length(mini_vec); this is the more common use-case.
    • mags::AbstractMatrix{<:Number}, in which case mags must be 2-dimensional. Valid shapes are size(mags) == (length(mini_vec), nfilters) or size(mags) == (nfilters, length(mini_vec)), with nfilters being the number of filters you are providing.
  • mag_names::AbstractVector{String} contains strings describing the filters you are providing in mags; an example might be ["B","V"]. These are used when mag_lim is finite to determine what filter you want to use to limit the faintest stars you want returned.
  • limit::Number gives the total birth stellar mass of the population you want to sample. See the "Notes" section on population masses for more information.
  • imf::Distributions.Sampleable{Distributions.Univariate, Distributions.Continuous} is a sampleable continuous univariate distribution implementing a stellar initial mass function with a defined rand(rng::Random.AbstractRNG, imf) method to use for sampling masses. All instances of Distributions.ContinuousUnivariateDistribution are also valid. Implementations of commonly used IMFs are available in InitialMassFunctions.jl.

Keyword Arguments

  • dist_mod::Number=0 is the distance modulus (see StarFormationHistories.distance_modulus) you wish to have added to the returned magnitudes to simulate a population at a particular distance.
  • rng::Random.AbstractRNG=Random.default_rng() is the rng instance that will be used to sample the stellar initial masses from imf.
  • mag_lim::Number=Inf gives the faintest apparent magnitude for stars you want to be returned in the output. Stars fainter than this magnitude will still be sampled and contribute properly to the total mass of the population, but they will not be returned.
  • mag_lim_name::String="V" gives the filter name (as contained in mag_names) to use when considering if a star is fainter than mag_lim. This is unused if mag_lim is infinite.
  • binary_model::StarFormationHistories.AbstractBinaryModel=StarFormationHistories.RandomBinaryPairs(0.3) is an instance of a model for treating binaries; currently provided options are NoBinaries, RandomBinaryPairs, and BinaryMassRatio.

Returns

(sampled_masses, sampled_mags) defined as

  • sampled_masses::Vector{SVector{N,eltype(imf)}} is a vector containing the initial stellar masses of the stars sampled by sample_system; see that method's documentation for details on format. In short, each StaticArrays.SVector represents one stellar system and each entry in each StaticArrays.SVector is one star in that system. Entries will be 0 when companions could have been sampled but were not (i.e., when using a binary_model that supports multi-star systems).
  • sampled_mags::Vector{SVector{N,<:Number}} is a vector containing StaticArrays.SVectors with the multi-band magnitudes of the sampled stars. To get the magnitude of star i in band j, you index as sampled_mags[i][j]. This can be reinterpreted as a 2-dimensional Matrix with reduce(hcat,sampled_mags).

Notes

Population Masses

Given a particular isochrone with an initial mass vector mini_vec, it will never cover the full range of stellar birth masses because stars that die before present-day are not included in the isochrone. However, these stars were born, and so contribute to the total birth mass of the system. There are two ways to properly account for this lost mass when sampling:

  1. Set the upper limit for masses that can be sampled from the imf distribution to a physical value for the maximum birth mass of stars (e.g., 100 solar masses). In this case, these stars will be sampled from imf, and will contribute their masses to the system, but they will not be returned if their birth mass is greater than maximum(mini_vec). This is typically easiest for the user and only results in ∼15% loss of efficiency for 10 Gyr isochrones. This approach is preferred when sampling with binaries.
  2. Set the upper limit for masses that can be sampled from the imf distribution to maximum(mini_vec) and adjust limit to respect the amount of initial stellar mass lost by not sampling higher mass stars. This can be calculated as new_limit = limit * ( QuadGK.quadgk(x->x*pdf(imf,x), minimum(mini_vec), maximum(mini_vec))[1] / QuadGK.quadgk(x->x*pdf(imf,x), minimum(imf), maximum(imf))[1] ), with the multiplicative factor being the fraction of the population stellar mass contained in stars with initial masses between minimum(mini_vec) and maximum(mini_vec); this factor is the ratio

\[\frac{\int_a^b \ m \times \frac{dN(m)}{dm} \ dm}{\int_0^∞ \ m \times \frac{dN(m)}{dm} \ dm}.\]

Note that, if binaries are included, this approach only forms binary pairs between stars whose masses are less than maximum(mini_vec). This is probably not desired, so we recommend the previous approach when including binaries.

source
StarFormationHistories.generate_stars_magFunction
(sampled_masses, sampled_mags) =  generate_stars_mag(mini_vec::AbstractVector{<:Number}, mags, mag_names::AbstractVector{String}, absmag::Real, absmag_name::String, imf::Distributions.Sampleable{Distributions.Univariate,Distributions.Continuous}; dist_mod::Number=0, rng::AbstractRNG=default_rng(), mag_lim::Number=Inf, mag_lim_name::String="V", binary_model::StarFormationHistories.AbstractBinaryModel=RandomBinaryPairs(0.3))

Generates a mock stellar population from an isochrone defined by the provided initial stellar masses mini_vec, absolute magnitudes mags, and filter names mag_names. The population is sampled to a total absolute magnitude absmag::Real (e.g., -7 or -12) in the filter absmag_name::String (e.g., "V" or "F606W") which is contained in the provided mag_names::AbstractVector{String}. Other arguments are shared with generate_stars_mass, which contains the main documentation.

Notes

Population Magnitudes

Unlike when sampling a population to a fixed initial birth mass, as is implemented in generate_stars_mass, when generating a population up to a fixed absolute magnitude, only stars that survive to present-day contribute to the flux of the population. If you choose to limit the apparent magnitude of stars returned by passing the mag_lim and mag_lim_name keyword arguments, stars fainter than your chosen limit will still be sampled and will still contribute their luminosity to the total population, but they will not be contained in the returned output.

source

Complex Stellar Populations

StarFormationHistories.generate_stars_mass_compositeFunction
(sampled_masses, sampled_mags) = generate_stars_mass_composite(mini_vec::AbstractVector{<:AbstractVector{<:Number}}, mags::AbstractVector, mag_names::AbstractVector{String}, limit::Number, massfrac::AbstractVector{<:Number}, imf::Sampleable{Univariate,Continuous}; kws...)

Generates a random sample of stars with a complex star formation history using multiple isochrones. Very similar to generate_stars_mass except the isochrone-related arguments mini_vec and mags should now be vectors of vectors containing the relevant data for the full set of isochrones to be considered. The total birth stellar mass of the sampled population is given by limit. The proportion of this mass allotted to each of the individual isochrones is given by the entries of the massfrac vector. This basically just proportions limit according to massfrac and calls generate_stars_mass for each of the individual stellar populations; as such it is set up to multi-thread across the multiple stellar populations.

Arguments

  • mini_vec::AbstractVector{<:AbstractVector{<:Number}} contains the initial masses (in solar masses) for the stars in each isochrone; the internal vectors must be mutable as we will call Interpolations.deduplicate_knots! on each. The length of mini_vec should be equal to the number of isochrones.
  • mags contains the absolute magnitudes from the isochrones in the desired filters corresponding to the same stars as provided in mini_vec. The length of mags should be equal to the number of isochrones. The individual elements of mags are each internally interpreted and converted into a standard format by StarFormationHistories.ingest_mags. The valid formats for the individual elements of mags are:
    • AbstractVector{AbstractVector{<:Number}}, in which case the length of the vector length(mags[i]) can either be equal to length(mini_vec[i]), in which case the length of the inner vectors must all be equal to the number of filters you are providing, or the length of the outer vector can be equal to the number of filters you are providing, and the length of the inner vectors must all be equal to length(mini_vec[i]); this is the more common use-case.
    • AbstractMatrix{<:Number}, in which case mags[i] must be 2-dimensional. Valid shapes are size(mags[i]) == (length(mini_vec[i]), nfilters) or size(mags[i]) == (nfilters, length(mini_vec[i])), with nfilters being the number of filters you are providing.
  • mag_names::AbstractVector{String} contains strings describing the filters you are providing in mags; an example might be ["B","V"]. These are used when mag_lim is finite to determine what filter you want to use to limit the faintest stars you want returned. These are assumed to be the same for all isochrones.
  • limit::Number gives the total birth stellar mass of the population you want to sample.
  • massfrac::AbstractVector{<:Number} is vector giving the relative fraction of mass allotted to each individual stellar population; length must be equal to the length of mini_vec and mags.
  • imf::Distributions.Sampleable{Distributions.Univariate, Distributions.Continuous} is a sampleable continuous univariate distribution implementing a stellar initial mass function with a defined rand(rng::Random.AbstractRNG, imf) method to use for sampling masses. All instances of Distributions.ContinuousUnivariateDistribution are also valid. Implementations of commonly used IMFs are available in InitialMassFunctions.jl.

Keyword Arguments

All keyword arguments kws... are passed to generate_stars_mass; you should refer to that method's documentation for more information.

Returns

  • sampled_masses::Vector{Vector{SVector{N,eltype(imf)}}} is a vector of vectors containing the initial stellar masses of the sampled stars. The outer vectors are separated by the isochrone the stars were generated from; i.e., all of sampled_masses[1] were sampled from mini_vec[1] and so on. These can be concatenated into a single vector with reduce(vcat,sampled_masses). The format of the contained StaticArrays.SVectors are as output from sample_system; see that method's documentation for more details.
  • sampled_mags::Vector{Vector{SVector{N,<:Number}}} is a vector of vectors containing StaticArrays.SVectors with the multi-band magnitudes of the sampled stars. The outer vectors are separated by the isochrone the stars were generated from; i.e. all of sampled_mags[1] were sampled from mags[1] and so on. To get the magnitude of star i in band j sampled from isochrone k, you would do sampled_mags[k][i][j]. This can be concatenated into a Vector{SVector} with reduce(vcat,sampled_mags) and a 2-D Matrix with reduce(hcat,reduce(vcat,sampled_mags)).
source
StarFormationHistories.generate_stars_mag_compositeFunction
(sampled_masses, sampled_mags) = generate_stars_mag_composite(mini_vec::AbstractVector{<:AbstractVector{<:Number}}, mags::AbstractVector, mag_names::AbstractVector{String}, absmag::Number, absmag_name::String, fracs::AbstractVector{<:Number}, imf::Sampleable{Univariate,Continuous}; frac_type::String="lum", kws...)

Generates a random sample of stars with a complex star formation history using multiple isochrones. Very similar to generate_stars_mag except the isochrone-related arguments mini_vec and mags should now be vectors of vectors containing the relevant data for the full set of isochrones to be considered. The total absolute magnitude of the sampled population is given by absmag. The proportion of the luminosity allotted to each of the individual isochrones is given by the entries of the frac vector. This basically just proportions the luminosity according to frac and calls generate_stars_mag for each of the individual stellar populations; as such it is set up to multi-thread across the multiple stellar populations.

Arguments

  • mini_vec::AbstractVector{<:AbstractVector{<:Number}} contains the initial masses (in solar masses) for the stars in each isochrone; the internal vectors must be mutable as we will call Interpolations.deduplicate_knots! on each. The length of mini_vec should be equal to the number of isochrones.
  • mags contains the absolute magnitudes from the isochrones in the desired filters corresponding to the same stars as provided in mini_vec. The length of mags should be equal to the number of isochrones. The individual elements of mags are each internally interpreted and converted into a standard format by StarFormationHistories.ingest_mags. The valid formats for the individual elements of mags are:
    • AbstractVector{AbstractVector{<:Number}}, in which case the length of the vector length(mags[i]) can either be equal to length(mini_vec[i]), in which case the length of the inner vectors must all be equal to the number of filters you are providing, or the length of the outer vector can be equal to the number of filters you are providing, and the length of the inner vectors must all be equal to length(mini_vec[i]); this is the more common use-case.
    • AbstractMatrix{<:Number}, in which case mags[i] must be 2-dimensional. Valid shapes are size(mags[i]) == (length(mini_vec[i]), nfilters) or size(mags[i]) == (nfilters, length(mini_vec[i])), with nfilters being the number of filters you are providing.
  • mag_names::AbstractVector{String} contains strings describing the filters you are providing in mags; an example might be ["B","V"]. These are used when mag_lim is finite to determine what filter you want to use to limit the faintest stars you want returned. These are assumed to be the same for all isochrones.
  • absmag::Number gives the total absolute magnitude of the complex population to be sampled.
  • fracs::AbstractVector{<:Number} is a vector giving the relative fraction of luminosity or mass (determined by the frac_type keyword argument) allotted to each individual stellar population; length must be equal to the length of mini_vec and mags.
  • imf::Distributions.Sampleable{Distributions.Univariate, Distributions.Continuous} is a sampleable continuous univariate distribution implementing a stellar initial mass function with a defined rand(rng::Random.AbstractRNG, imf) method to use for sampling masses. All instances of Distributions.ContinuousUnivariateDistribution are also valid. Implementations of commonly used IMFs are available in InitialMassFunctions.jl.

Keyword Arguments

  • frac_type::String either "lum", in which case fracs is assumed to contain the relative luminosity fractions for each individual isochrone, or "mass", in which case it is assumed that fracs contains mass fractions ("mass" is not yet implemented).

All other keyword arguments kws... are passed to generate_stars_mag; you should refer to that method's documentation for more information.

Returns

  • sampled_masses::Vector{Vector{SVector{N,eltype(imf)}}} is a vector of vectors containing the initial stellar masses of the sampled stars. The outer vectors are separated by the isochrone the stars were generated from; i.e., all of sampled_masses[1] were sampled from mini_vec[1] and so on. These can be concatenated into a single vector with reduce(vcat,sampled_masses). The format of the contained StaticArrays.SVectors are as output from sample_system; see that method's documentation for more details.
  • sampled_mags::Vector{Vector{SVector{N,<:Number}}} is a vector of vectors containing StaticArrays.SVectors with the multi-band magnitudes of the sampled stars. The outer vectors are separated by the isochrone the stars were generated from; i.e. all of sampled_mags[1] were sampled from mags[1] and so on. To get the magnitude of star i in band j sampled from isochrone k, you would do sampled_mags[k][i][j]. This can be concatenated into a Vector{SVector} with reduce(vcat,sampled_mags) and a 2-D Matrix with reduce(hcat,reduce(vcat,sampled_mags)).
source

Observational Effects

The output produced from the above methods are clean in the sense that they do not include any observational effects like photometric error or incompleteness. These effects should be implemented in a post-processing step. We provide a simple method model_cmd that accepts user-defined photometric error and completeness functions and applies them to the initial catalog, returning a Monte Carlo realization of a possible observed catalog. This method assumes Gaussian photometric errors and that the photometric error and completeness functions are separable by filter – these assumptions are not applicable for all types of data, but the source code for the method is exceedingly simple (~20 lines) and should provide an example for how you could write a similar method that more accurately reflects your data.

StarFormationHistories.model_cmdFunction
new_mags [, good_idxs] = model_cmd(mags::AbstractVector{<:AbstractVector{<:Number}}, errfuncs, completefuncs; rng::Random.AbstractRNG=Random.default_rng(), ret_idxs::Bool=false)

Simple method for modelling photometric error and incompleteness to "mock observe" a pure catalog of stellar photometry, such as those produced by generate_stars_mass and generate_stars_mag. This method assumes Gaussian photometric errors and that the photometric error and completeness functions are separable by filter.

Arguments

  • mags::AbstractVector{<:AbstractVector{<:Number}}: a vector of vectors giving the magnitudes of each star to be modelled. The first index is the per-star index and the second index is the per-filter index (so mags[10][2] would give the magnitude of the tenth star in the second filter). This is the same format as the magnitudes returned by generate_stars_mass and generate_stars_mag; to use output from the composite versions, you must first reduce(vcat,mags) before passing to this function.
  • errfuncs: an iterable (typically a vector or tuple) of callables (typically functions or interpolators) with length equal to the number of filters contained in the elements of mags. This iterable must contain callables that, when called with the associated magnitudes from mags, will return the expected 1-σ photometric error at that magnitude. The organization is such that the photometric error for star i in band j is σ_ij = errfuncs[j](mags[i][j]).
  • completefuncs: an iterable (typically a vector or tuple) of callables (typically functions or interpolators) with length equal to the number of filters contained in the elements of mags. This iterable must contain callables that, when called with the associated magnitudes from mags, will return the probability that a star with that magnitude in that band will be found in your color-magnitude diagram (this should include the original detection probability and any post-detection quality, morphology, or other cuts). The organization is such that the detection probability for star i in band j is c_ij = completefuncs[j](mags[i][j]).

Keyword Arguments

  • rng::AbstractRNG=Random.default_rng(): The object to use for random number generation.
  • ret_idxs::Bool: whether to return the indices of the input mags for the stars that were successfully "observed" and are represented in the output new_mags.

Returns

  • new_mags: an object similar to mags (i.e., a Vector{Vector{<:Number}}, Vector{SVector{N,<:Number}}, etc.) containing the magnitudes of the mock-observed stars. This will be shorter than the provided mags vector as we are modelling photometric incompleteness, and the magnitudes will also have random photometric errors added to them. This can be reinterpreted as a 2-dimensional Matrix with reduce(hcat,new_mags).
  • good_idxs: if ret_idxs is true, the vector of indices into the input mags for the stars that were successfully "observed" and are represented in the output new_mags.

Notes

  • This is a simple implementation that seeks to show a simple example of how one can post-process catalogs of "pure" stars from methods like generate_stars_mass and generate_stars_mag to include observational effects. This method assumes Gaussian photometric errors, which may not, in general, be accurate. It also assumes that the total detection probability can be modelled as the product of the single-filter detection probabilities as computed by completefuncs (i.e., that the completeness functions are separable across filters). This can be a reasonable assumption when you have separate photometric catalogs derived for each filter and you only collate them afterwards, but it is generally not a good assumption for detection algorithms that operate on simultaneously on multi-band photometry – the completeness functions for these types of algorithms are generally not separable.
source

Developer Internals

StarFormationHistories.ingest_magsFunction
new_mags = ingest_mags(mini_vec::AbstractVector, mags::AbstractVector{T}) where {S <: Number, T <: AbstractVector{S}}
+new_mags = ingest_mags(mini_vec::AbstractVector, mags::AbstractMatrix{S}) where S <: Number

Reinterprets provided mags to be in the correct format for input to Interpolations.interpolate.

Returns

  • new_mags::Base.ReinterpretArray{StaticArrays.SVector}: a length(mini_vec) vector of StaticArrays.SVectors containing the same data as mags but formatted for input to Interpolations.interpolate.
source
StarFormationHistories.sort_ingestedFunction
(new_mini_vec, new_mags) = sort_ingested(mini_vec::AbstractVector, mags::AbstractVector)

Takes mini_vec and mags and ensures that mini_vec is sorted (sometimes in PARSEC isochrones they are not) and calls Interpolations.deduplicate_knots! on mini_vec to ensure there are no repeat entries. Arguments must satisfy length(mini_vec) == length(mags).

source
StarFormationHistories.mass_limitsFunction
(mmin, mmax) = mass_limits(mini_vec::AbstractVector{<:Number}, mags::AbstractVector{T},
                  mag_names::AbstractVector{String}, mag_lim::Number,
                  mag_lim_name::String) where T <: AbstractVector{<:Number}

Calculates initial mass limits that reflect a given faint-end magnitude limit.

Arguments

  • mini_vec::AbstractVector{<:Number}: a length nstars vector containing initial stellar masses.
  • mags::AbstractVector{<:AbstractVector{<:Number}}: a length nstars vector, with each element being a length nfilters vector giving the magnitudes of each star in the filters mag_names.
  • mag_names::AbstractVector{String}: a vector giving the names of each filter as strings.
  • mag_lim::Number: the faint-end magnitude limit you wish to use.
  • mag_lim_name::String: the name of the filter in which mag_lim is to be applied. Must be contained in mag_names.

Returns

  • mmin::eltype(mini_vec): the initial mass corresponding to your requested mag_lim in the filter mag_lim_name. If all stars provided are brighter than your requested mag_lim, then this will be equal to minimum(mini_vec).
  • mmax::eltype(mini_vec): the maximum valid mass in mini_vec; simply maximum(mini_vec).

Examples

julia> mass_limits([0.05,0.1,0.2,0.3], [[4.0],[3.0],[2.0],[1.0]], ["F090W"], 2.5, "F090W")
 (0.15, 0.3)
 
 julia> mass_limits([0.05,0.1,0.2,0.3], [[4.0,3.0],[3.0,2.0],[2.0,1.0],[1.0,0.0]], ["F090W","F150W"], 2.5, "F090W")
-(0.15, 0.3)
source
+(0.15, 0.3)source diff --git a/dev/template_compare_binaries.svg b/dev/template_compare_binaries.svg index 799f6d2..6bafdc0 100644 --- a/dev/template_compare_binaries.svg +++ b/dev/template_compare_binaries.svg @@ -6,7 +6,7 @@ - 2024-09-28T21:34:16.304900 + 2024-10-07T06:58:00.308351 image/svg+xml @@ -38,17 +38,17 @@ z " style="fill: #ffffff"/>
+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" id="image55e59735a2" transform="scale(1 -1) translate(0 -273.6)" x="48.96" y="-12.24" width="256.32" height="273.6"/> - - + @@ -119,7 +119,7 @@ z - + @@ -159,7 +159,7 @@ z - + @@ -204,7 +204,7 @@ z - + @@ -385,12 +385,12 @@ z - - + @@ -404,7 +404,7 @@ L -3.5 0 - + @@ -442,7 +442,7 @@ z - + @@ -456,7 +456,7 @@ z - + @@ -470,7 +470,7 @@ z - + @@ -549,7 +549,7 @@ L 166.049838 7.30751 L 170.083381 -0.512448 L 170.317235 -1 L 170.317235 -1 -" clip-path="url(#pc6ead54d2d)" style="fill: none; stroke: #ffa500; stroke-width: 1.5; stroke-linecap: square"/> +" clip-path="url(#pfd85751a10)" style="fill: none; stroke: #ffa500; stroke-width: 1.5; stroke-linecap: square"/> - - - - - - - + + + + + + @@ -1227,12 +1227,12 @@ z " style="fill: #ffffff"/> +iVBORw0KGgoAAAANSUhEUgAAAWUAAAF5CAYAAABHkTtbAAA1LUlEQVR4nO2d3Y+V5dm+b2qpVq2KfMmXICAiItqWCk7ZKHZiJjUpO6TpholumZh0o39ME5Pu1CZN7IY72NROkHR2CAEkCmhxlG/5EPCbltaWfvy2fu/bdd3HvHOx1prhnlnHsbeuPM+znjUzLM7nvM/ruuf85z//KSL95osvvuj4w7rnnnvmZM4bHx+v/iDXr19fnZu5fjxmIi5dulTV6D2n+/rZ98z+bGVm8LWbfQMiIvK/zFEpy0RkVVlW3Xb7nqQ077vvvq7uK8tnn31W1TZs2DDpfb377rup6995552p41atWjXpMQcOHKhqd9xxR1UbHh5OvWek30q826eoQUGlLCLSECplmRayqjvr08Zzf/3rX1fnPfjgg9V5V69erWoZNVpKrYJJ7d51111Vbe/evVVt9erVVW18fLyqbd68edL7On369KTHlFLKwoULq9q1a9eqGv3cIvfee29VO3PmTFWjn0ckPoGU0l8ffqahUhYRaQiVskwJGcWbVcqZ47LXIgVGRM+a3mPfvn3V9Y8cOVKd9+c//7mq/eMf/0gd981vfrPj9d/+9rfqmPnz51e1Tz/9dNJrlVLK4sWLq9rtt9/e8fqvf/1rdQxBSnzjxo1VLSrqjJoupZShoaHU7/jYsWOpc6eabr1zv5SlKbqNfNGiHi3WEdlH8b/85S8dr+nRn6BFt48//riqxS9D4ujRo1WNvmy//vWvp2pE/JKnL3j6j4C4cOFCVXv22Wc7Xo+OjlbHjIyMVLVefp/0n2xkqhexs2hfiIg0hEpZmqLfC4IZstenx+IIqTSKrJFtsH///kmPI4uAFhKXLl36f97n/+cb3/hGVTt37lzHa7Ja1qxZU9XoOFpYjXYFLXrS/feyIBgVbybyWEpvFlu3qJRFRBoiZzKJTBNZxdGtz0xe8datW7PKp+M4UoHHjx+vapcvX65qpJT/+c9/VjXyZCPbtm2raqdOnUq9J3nU8+bN63hNC5DLly+vaocPH65qmXgdRQHJc89CC7ARWvijv5d77rmnOrfbJ7fs37ZKWUSkIVTKMisgpULe4vr161Pnkto6ePBgx2tKCGTjXdSwQqo1+rSU0HjzzTerGiUyPv/886r2rW99q6rFphg67/z581WNoDhdVN70hECfM9v4Q09D8bhsqiLbqt/PRIZKWUSkIVTKMiPJBPOzaojOvXr1anVuVMZ0rdHR0eq8LVu2xFIZGxurauT5xowzecVLliypaqTif/WrX1U1UqmZ+4qZ7VJKWbBgQVUj1RozzuTb0nvSUwilKMjDj4yMjHStirN56W6bR1TKIiINYU5ZZgW9ZEe7HVFKiol8T4LUHPmvMZdMQ5AOHTpU1cgHpo6+v//971UtKl4aeESqmLr8Nm3aVNVirvq2226rjvnqq6+qGkHpDuqgjLlnSsns2LGjqu3Zs6eq7dy5s6vscjZbr1IWEWkIPWUZeKiTjjzZDNkxoJS0IGIKITtEn1IV5B+Tov7www87XpNnvWzZsqpGmWr6nNevX69qEcpek2Kn+yelHMnkp0vhgUrZbs8NGzZ0NTRLpSwi0hAqZZkV9HtLocxK/MWLF6taZtZDKZwuIKKii3MpSsn7r9nJcevWret4/c4771THfPTRR1WNUhTkF8+dO7fjNSlnenqh63/yySdVjZIhsUPwgQceqI7JjnWl47odDUp/typlEZGGMH0hA0V2BXwCj3DS80j5UHcgrf6//fbbVS1626QgSe2uWLGiqtF70tD8L774ouM1+dO33nprVaMkBx0XfeD777+/OobUP6lu+pyZofw0bY+SLdk1AkrixImBmZnOpaiURUSaQk9ZZi29ZJfvvffeSRU1eYuXLl2qziOFTX40qbKYZyYvlFIJlHr417/+VdUobxx9a5pDQUkOmqecIbMNVinsH9M8aJrPHGd1ZFVxZgeaUjilken8o+urlEVEGkJPWWYFvexY0u183OyuFzQPgxIZmVnM1CH3xhtvVDXygUnxkrqN90HpiJjQKKWUK1euVDXaiSX62HF+cymsnikvTaqVVHD8nKSmCZrrvH379tS50UN29oWIyAxET1lmBb3sWEKqOKOCjx07Vp1H1yKvklbiSSnHzrYjR45Ux5DSJOI86FJ4vnT0mWMaoxRWxXQcEb1W8o9JKZOnTJ745s2bq1qcD0LzoGk3FfLw6XdHaZ3oF1OXKCVzVMoiIg2hpyySJKoaSlVkdrwuhRUveb7RU6ZOQOryo3TE2bNnq9rdd9/9f95nKZza+PTTT6saebl0bswuX7t2rTqG8sc0W2PRokVVjfz0mNKg1AZlqrP+cbfQGoRKWUSkIVTKIkC3GWeamUGT3UhVUpIgQl4oJShoBgd1xBHRz6VOPVLApPTJB47ccsstqeuTeqYngsyEOfpM5M3TxDlS9jSLOa5LkBft7AsRkcZRKcvAk52HkdlJIjtpjLzEzJxe8qKzMyco0UDecIQ+E12fZk6Qio/T3rJzNEjJ0nGUdomK/fHHH6+OGR4ermo0a4Sun923L0LT5VTKIiINYU5ZBp5uZzFnJ8nRcaSKaT+46CFTlpY8a1LFpDQpIxzVM2V1KZNM0+rI26bjIqS6ybPOzljOvCc9EZAqpp83zb6IHnL2KcovZWmejL3Qy5D7bluvqYU4+w+PoAWk+Gg/Pj5eHUMxOVoAo3GYtLgYF9kockdNG7RgR9GzaGmQhUKLenE4fik82CnzHyN9SdOXLf2HRFtQxY1ZSynlwIEDHa9p0ZCad7QvREQawoU+GXj6OaSIFoa2bt1a1cjSIOX96quvdrwmtUsRsNhWXAorPLIhojqk0aA0HJ8W9SiGR4o6Qko8NtJMdBzZF2vXru14TU8X1JxCG6zSufTEEdVzZpOEUlTKIiJNoacsA0+3w4xomExWFWc3XY1NJuTRks88f/78qkZK88svv5z0OIqs/fvf/07ViBgfo+t/7Wu1XiSFeurUqapGi3+08BlZtmxZVaOnBPKGKU5HC7cRUs8qZRGRhtBTFknSbQqEWq9pGyBSslGpPf3009Ux1FBy4sSJqkaJA4qexRopYFKedC1KgURPme5rzZo1VY1azMmHJ588qmzytSn+Rtei3wGNXSVlH6HNDlTKIiINoacss5Z+bxGVabMmn5m8YlLFpKzi6v/u3burYygJQdePCYRS2DONypjULl2f/G4aLBQVKeWzyeumnyPdGyU+4ojSjz76qDqGajQGlNYDKNkSf3emL0REZiAqZZm19LJFVIZsm3W2y48yzrF9l5QhqTRqqabOPFK3UbnS9UmN0sam1IX3/vvvd7xeuHBhdQz9zCj1QOeS4o05YlLA9Lsjr5g2U6WtnmjdIEKfU6UsItIQKmWRJNFDznqEBHmyNNQmdoVRAuH69etVjbraaKAPKe+YyKIcMaliGl9JnW5xCBJdn+6LhifRBq7U5Re7DWnkJ/1OqKOPapnPTqqYBt+rlEVEGkKlLANPt1s/3YAXXV2fkhaUhIgda6TmMoPkJzqOkhvRj6YkBPmqWSUePWrygGlDVMoM0yhTOi6S2TC2FP6dUB6bvO345EOqmNYRVMoiIg1hR59IH8luEUUeZGarJ1KBGWVYCitemjER1TN5vuRZ0wQ7SmnEvDEp7Dlz6ocQ8s5pxsfKlSurWvSjScHT9anLj1QxEdcDKGdN11cpi4g0hJ6yDBTd+sd0Lp1HqpjyqjTzgBRY9G7JKyZoNw/yj0m9ReX6zjvvVMfQ/VNnHs2miHMnSBXTvWZmSZRSytmzZ6tazDjTjiI0g4NSGuSn0/Ui9DOjDXRVyiIiDaFSloGil7384rk0/Y1W2Mk/JoVKRNVKu2OQqqSZE6SeMztwkMIjBZndZDTmiCkVQp7vyZMnqxp1LpJHHaEpd/SZKDtONXoaijl2eoqivyGVsohIQ6iUZaDo5+Q4UsUE+arE0aNHq1pUspRmIPVMqYr777+/qpEPHJMKNBWN/Nf33nuvqlF3XcwpU7qDzqPPTokJmudBP49I3OWlFPaPR0dHqxp9hgjtSkOolEVEGsKcssgUMzo6Wv0jo3311q9fX9XicTSZjhQwqVvajYR81Ojx0i4d5NtS6oEUdVStpDLpPJpW9/DDD6fuLfOeDz30UFUjz51qmfnYKmURkRmInrII0EueOULpheeff76qkQqOii67EwYdRxlnOjdmbskrpu5A8s7Js45P55RTpvQIzVOm65PKpnMjNNOC1DnV6Ckn/t6dEiciMgNRKcvA009VTNei3StIoZLqi55vNtNLai6TQCgll1MmKI9NiYmoSEl1036CWVVMROVN52Un65GnnP0ZRUg9+6UsA082Epchu40Rxa/oSycOG6JFN7IqshuDZtqss1+29EVHlkDcmJUW2Gg4fnYYfmazVvoSpcFOdP1NmzZVteywoQzaFyIiDaFSloGn24aSCSyOlMI+cOBAVSNFHdUntQeTwqaFJxq3SY/isW2bVD01aNBWVTRMPloC1IpN90/XzzRtlFJbMrSNFCnszZs3VzVS8dRkEiF1TluKqZRFRBpCpSwDT7eecnYgEY1nPHDgQEpRx8HxpAxpoYyUIJ1LKjv64jS8nlQlLYoRc+fO7XhNG6LSwP9sWzup/+hjk9qlhT5qCiFefPHF6ne8b9++jt9xdgiVSllEpCFUyiJAcuPU1LVIUZMCo1X9CG0oSqqYRklSkoAaPqI/SkqWGj7I76YxDjHmR9enbZ5IAdN9kLJfsmRJVYusWLGiqpF3TtDvOP5sSSnT35lKWUSkIVTKIkky6Qvyoild8MILL1S1X/7yl1WNRlhGKOFAjSKUw6XsclTUdEx2aya6j6huyZ+moUKUvqBEA50bPwM9IdCTyunTp6saQX73/v37O17TQCL6e1Epi4g0hEpZBp5+tlkTtH0QQZ150Xsm/5iUIR1H/i55prFbj9Q6JT5I8cakRSmlLF26tOM1KXFSspSzJh+YfPfY1h7voRRWxbSFFmWooyoupVbx2ScrlbKISEOolGXgyarieFy38zFK4RkZlF6I0OwLqlGigVIJdB/xuJjxnej65B/T7Is4S4Py03SvcaRoKewzZ/xumtORGVQ/0XvSZ6DscgaVsohIQ6iURfpIxlssJT+YPnqapOYyMzNK4XGe5N3GORGkgKlGOVz67Jkt6MgTp8l0y5cvr2qnTp2qajH58OGHH1bH0M8sux5Ax2XSOuaURUQaR6Us0kdohi5119HqP82hiOkFUsXZOcCkBMkvjsddv369OoY2XKWuOVLP0QPPdhoSNMGOOHny5KTXX7duXVWj30k2ox239xoaGqqOoU5AlbKISEOolGXgyaYoov9HfuClS5eqa1En1yuvvJK6t6haKfVAXjFBCQHaVSQmH+J85VI4f0x5Y8pBx5QJpSrovigHTT48KXY6NzJv3rxJjymFn4ayE+wy56mURUQaQqUsAmSyy6SwyT+mXUbIk6UZv1EpZzZXLYXzweTvZrxnUrvkM2f24yulTm5QPpvmedC9Urojs0cf3T9BHj5BqZsIdQKavhARaRyVsgw83c65iKvrpbByo5kKpIrJG467eZAKpO63hQsXVjXK/tK58T1pRxFKclCOmNIR0duma9GcZFKt5D3TPIwIqW76PVH+mKb+UZpmaGio40Nkd6pRKYuINIRKWQaebnezjkqoFFZDlC4gH5iIqoyms5GSPXToUFUjb5iSFdFvJTVNHX10rczOIJQooV1daE7Hd7/73apGyjsmQ+j69HuiXarpKaRb9JRFRBpHpSwDT7eeMqniLNlMbPSoKX1BPjZ1+ZHKpglz0W8lJUtJC/KUKX0R1TMdQ4mMbBaYPmeckUFPF+TpP/HEE1WNugHJ2+52iqBKWUSkIVTKIgCp4JhBps4uUkxHjx6taqQ0SR3GpEJ2njJ51rRDCR0XVTZ1ApJXTDuUkB8dPevsJDnyxGnaHv1e4mcg9X/u3LmqRlPoSClTDTpAq2MIv5Rl4KHHzPXr11ffOvfdd1/HcbTwRFArMH0pP/nkk1Xtd7/7XcdripidOHGiqtFCHD2y05dyXMii/2joPwJqs6Yv3NiiTV/wdH26f/qPhuyQ2HpN9hFF7ujnTZE4Oi7+vRAu9ImINI5KWQae7EJfbBbZsGFDdQy1VFPUiqyK3bt3V7UY7yJlSxGwrM1B6jOqeGq0oCYWUq0Uk4st1NTyTNenKBotLpJNED9ndogTNYWQ+s+Q/TtTKYuINIRKWQQgnzk2i+zbt686hnzJ3//+91WNWoGp9Toqalo4o/PIsybvlrzQTz75pOM1+dO0+EeLc7QAFhX7/PnzU/dFqpueEmgRL26rRcfQwKDjx49XtQcffLCqUSNRt6iURUQaQqUszZNtg+7ntagWlTGNwqTmDlKCFOUiordKTSHkyZLvef78+apGKjiqSBpon9nmqRT2o6O/S6qengjo6YJ8ZkqLPPPMMx2vKUFBPzPaoIBSN6Ojo6nNDSKmL0REGmdOZrtvkdlMt9tBkToixsfHq1rcyLMUVp+xRiqQkgTRQy2Fmy8ofRFrlPvNQuozJkgox71s2bLU9deuXVvVKLkRVTwlZ6ilmsj6x5m/K5WyiEjj6CnLQNHtkJhS6tZrSj3QqEfygan7jdRtTCGQR0vZ5aznS0o5etSksKlG/jT50fE9s1szUWaY0heZYU/ZQUNEP9c4CJWyiEhDqJRloCBFk92mJ/q5lLS4fPlyVTt79mxVo41BSSnHIUjkKZNvm906idRz9JBJTdN5pHgpqRBTFJQZpqcL6oykJwfyqDNbRNGmtyMjI31TwHb0iYjMQFTKMvCQgqQpcVevXp3Uj6ZsKo2EpPkMlGeOsx1ItdJQd8r+ZomJLOoEJD+duvDoc0YlTukO8qdptgZ9TlLPixcv7nhNOfHh4eGqln2KymbdM6iURUQaQqUsA0VmpsVERL94y5Yt1TG/+c1vqhoptzhfohROUVy4cKHjdXb2BalP8nypTyHW6FrkWZO6zXT00bUoCUF+PfnMVIsbyZLHHKcAlsJ55qlGpSwi0hAqZZm19JInpXPjdDCan0D+bmbL+1I4zRHzwNkOXDqOPFlS3jENQQqb8sfZHVbiz4OuRfdPKQ2aSx39Y7qPrDonJthxpm/ZZZWyiEhDqJRl1tJLlxWpoSNHjnS8Ju+S1ChBiY/Vq1dXtcOHD3e8XrRoUXUMZXopR0zziDN7+dEsiWwKhNRtTGTQvdJ70tMFeb7ksT/22GNVLXIDqYpJr9ULKmURkYZQKUtTTPVcgez1SclGFTw2NlYdQ6kKUoK0df0HH3xQ1WIig+4r6x+TUqZ0BOWSM8dQjTzfVatWdbwmNU1kJ8LR/JHo11NyJtvtSVCuvVtUyiIiDaFSlqboVhWToiH1ktlRZCJiZx4lC8hXzU5xI9UX0xc0H4MSFJTkICj5ENMWlEogT5l2lqYuxej5UrpjyZIlVY1U/aOPPlrVyOuP70lPHPQURT7zBOmLvqFSFhFpCJWyzApIFZMCpu49UoI0MSx2gZEKpF1ASFGT55uZsUyZZ1KolECgpAWp1JjhJSVOCpu8YdpDL6p4ulb0nUvhJw6qEXEWczaTbPpCRGTAUSlLU/QzfdHvuQXRU84mLcgLpVTCrbfeWtViooGUIaliglIapLzjrAv6THQe+eTkDcfOP/KnCcpo0+/g9OnTVS0qdnpimiBpUdXIU6Yntfi3nP079ktZmqLbL+DsNk/ZxZwDBw5UtfgFQF+stMBG/4jJNqBFwviFTtsw0chM+vLObrsUFxwp6kb/0dBjPbUzx58bbZJKi57EggULqtr27dsnPS/7dzDB32NXG+1m0b4QEWkIlbLMCrLBfxoiRNCYyLhY9P7771fHkMLLDmLPPMZTSzWN1iRooY8sjaiosxuiEvTkEBUptUDTUwgpalowHR0drWpxo9QbUMVdH9ctKmURkYZQKcuMJHqC5NtSg8DGjRurGrVLk2qN6o3ULi26kcKjLaJIUce4G8XfKLJG6pm84YwKpmPIK6brk5KN0Tk6hn622fjbyMhI6rhWUSmLiDSESlmmhG7jQJlrZaFIHG35Qw0O5ClHpZYZe1kK+6MENV9E9UyqOAv53fSeMbJGqQ1KWpDPTIo6qvgTJ05Ux3z/+99PXSvjWZdS//11+zc10bn99JlVyiIiDaFSlimhn8qBrpVROtnBMZTIoDGXUfGSAibfk/xd8kwzKQpSqJTIIO+ZFC/5wFGdUxaYniRoGyZq7ohPMNQ0Q+3qcTuuUrhhZf/+/VXtrrvu6vh76aWxyPSFiMgAoVKW5qG8MfmGGUgh7d69u6qRwouK7tSpU9UxlPuloevUppzZFDXrT5P6pPekFuroldN9kWK/++67U/cRnyZI6cfhT6WUcvDgwaq2fv36qjYyMjKpkqW/qakeNJRFpSwi0hAqZWmezLCXzDGlsN+Y3Xg0+sCkILPpiOzGo3HEJHmt5PlSKoH8Y7rf+Nkzx5TCczkoox1TGqSKYwKklN6GMUX6uX1Tv1Epi4g0hEpZmiK7rVOEBtofOXKkOu7ChQup+6CB87FDkBIOd911V1WjJAelF6izLb4n+cKknil/TPdBg/rj1DnKN2dSG3StUmqV/fjjj6fOI7Zu3VrVuv0bmur8cRaVsohIQ8zJrPiK9EovKoTOjZ15lFclaAA6TXu7cuVKVYuJDFKotLEp3Rt1v5G3HRVpZqpbKaxuyXsm4ntQKmHevHlVbeXKlVWNfOZ169Z1vKbM9rPPPlvVsusGN0Pd9hOVsohIQ+gpy7SQ7crLqpyYN961a1d1DM1KIC+XOv9I3cbVf1KB2SwwKXHylCPU9UfqmTxZOpdUcFSupOopf0ybka5du7aqxZ8bJVEoh97PeRUtq2mVsohIQ6iU5aaRVSuZjSoXL15cqShKQoyPj1c1Un00xyGqbPJyaeIcdeGRQs2oYMrq0rVIsVONiIkMUvD0s6WfR2YC3I4dO6pjXn755ar2/PPPV7VWdgvpJyplEZGGUCnLjGR0dLRDQlJn16FDh6oaJSYov0uqNe4WQsqTvGhKF5DSpHOjyiZ/lzxZUv90H+RHZzoXyT+me6NZI/Ep5Be/+EV1zM9+9rOqNsHUv+rm6LjoUbesnFXKIiINYU5ZmiK7wr5nz55Jj6H5D6+++mpVI8VL8x6in0vT38jzJSVL6pzOjTXKH1PKhKApbsuXL69qMR1B+WPyyX/4wx+mjovX//nPf16pVurQHBoaMqcsIiLTi56yTAu9ZExJFcddqV977bXqGJo0Rl4rzWwghRe79cgDpkwydfnR3AyaJxGvR77t0qVLU/dBn5O87ej50s+C5hhT0oJyypG4PlAKz7SYjaqYUCmLiDSESlmmhWxHH+02TUrwzJkzHa8vXrxYHUNeLilU8nJJVcYdOciLphQI5YhpLYeSIfF+yReOk+RK4bw0pS/IF49540ceeaQ6hjoSaQdqIipxUuuzUQFnUSmLiDSESlmmBZpxS1Cu9ZVXXqlqsbuOVHF2x2jKtWaULB2TnTNMiQmagRw/F30muhYlT2g3aLpeVNl0/zQljrogM9DvvN9TBWeS8vZLWXoms4iXHbtI0KN4hL5cyEqgVmAaaUmP9dG+oPek/xxoOBA9/tNjfPyippZnauSg6BxZN7TwGf/jomOWLFlS1ejnTdZTvN9+f2HOpC9gQvtCRKQhVMrSM5lFPIo90VbwdNz58+er94wD56kZg9QobfNE55I6j9E2WiAkVZm9PinqeC69J1kVNPqSbJrvfe97VS3G6ciWoKaTjCqWyVEpi4g0hEpZSin5xZHscbG2devW6jxa/Nu7d291b+T5Rn+UvGJa1MsOVI+Ru1LqhT1a7CJVH7c/KoU96y+//LKqxfslL/fhhx+uatH/LiUfp4s/jxUrVlTHENRMQ4t4M93znWpUyiIiDaFSllJKb8PCMymKCcYuVmQHsWc2MSWvlY6LIzlL4TblqCqpjZuULKlzqtH1oidL6jz+LCaCngiosSV6wxSJo3hd1j+OT0iUzCFmetQti0pZRKQhVMpyQ5BayWzXVJLDyGkQzdjYWFWL6o38WPJLKUtLnim1bS9btqzjNanubDaaVCvlnqN3TgOJSKHSwKAPPvigqi1atKiqvfDCCx2vqfU9O0aThknt3LlzUnU7KKqYUCmLiDSEQ+6lZzKe8v79+6va0aNHq9q2bduq2uuvv17V3n333Y7X5NGSMiRPljxTUrwxI0wKmFIP5BXTcURU5/REQOkOar2mHDR1CMb7feqpp6pjjh8/XtVoA9RuGRRVTKiURUQaQqUsPZPx/yiTPDo6Wl2LOu6IqFrffPPNSY+ZqEbpBUpfxGQIqUzaRorULXm5pGTjfVAnHV2LoIFHNCPjJz/5Scdryh8T1KE5yN5wt6iURUQawvSF9Ewmu3zgwIHqPFJ9pFrJu41bFGUmyZVSe9Gl8PQ0SjlEpZwdo0mdhfSe0T8upVbZlIMmtRs3Jy2llE2bNlU1ul/qwovQGgGhKr5xVMoiIg2hUpYJ6SGTXHbt2tXxOjuInfxXSkJE/5XOO3nyZFUjH5gULxGPI9+ZfGHKEdM0OVLPlCqJULqD/GPKVVNnXvwdU6aa/GPpDyplEZGGUCnPMvq52k3nHTt2rLr+q6++WtW2bNnS8Zo8ZeoUI1VM/OlPf+p4TakKUp4054KULPnY0VMmpUyqlRQqKWCa+xHvja5F6p9mINPPiIgT8kgpy9ShUhYRaQiV8iwjk4ToZUWcVCV1d8W5E6Tc6DzyZEk9x3w9JReimi6FkxDZFEVUspQUISVL/jF5vjSxLc6woBw0fXaC5mEQca9AExTTi0pZRKQhVMoDQEbpUMcdeYlUI1V58ODBSe+LfFvakePs2bNVLfqjtA8eZY3J8816w1Flk7Kl82KmuhTeTYWeEmKC5JlnnqmOob3xstA0vI0bN3Z9PekdlbKISEOolAcQSmhMsCtFpbD37dtXnUsK74svvuh4Tb4tKUjyTDMzIaiDjbLLNOuFOgspCRF9cjommz8mdUu1+NnHx8erY+jp4sUXX0zNoaC5FtmdQGRqUCmLiDSEU+IGgEz6glQUzTdYtWpVVaMdPv74xz92vKakAmV1aRdpmhwXEwIEKXHyscnfJcWeOS+b5CDvmTzwzByKkZGRqka/k8y1Sqk7+lTO04tKWUSkIfSUB4Buc6bZ7rq9e/dWtag0SSnT7AvyhikdEXcLoW41Oo/UKEFKOe5acvfdd1fHUHcdzTsmn5lUdvSoaRpeP1VxKSrjm41fygNAXJyjTS9pg0taeKLRl7RYdOXKlY7X1LRBUPPFnDn1d0T8QqcvQzqPvrxpwY5GZMYveVrUo2vRtlT0pUkRu3i/tKg3PDxc1ejLlhZz/QJuD+0LEZGGUCnPEHoZNBRbo6lRhKA2aFKytBAXVSTZBrRhKS3OzZ8/v6pF1UrXos1JV65cWdXo8Z8WEuPCHi3WkXom+4KUPUXz4hMBjdqkvwNS3RRn3LBhg9s1NYZKWUSkIVTKA0D0DUl1j42NVeeRmiN/lI67fPlyx2tSre+9915VI9+TfNQYM6N7ILVLg+/XrFlT1TKROFLF9CRBw4doQZMWPuMIVPKA6fdJ0FqCtIdKWUSkIVTKM4RsyzOt6sdh8jR+k1IJFy5cqGo0+IdUcGyzpial1atXVzVKclDzRbxfipjRZ3ryySer2okTJ6oabUYaFS/536SeaWwpnbtjx46qBg081Q+SPHFSxf3cAEGmDpWyiEhD2GY9g8m2RkdvNbZAl8KNC59++mlVo4aMmEkupU5DkAKmNmtSt+QXR/VMw4eouSM7ppPSC/HcFStWVMdQCmTr1q1VLXrupfATwc6dOyddD1Dtzi5UyiIiDaGnPIMhVUyq7OWXX+54TaMwCVLPb731VlWjpEL0eEkFklImKJUQfXHyyclrpa2TqHuP7i0qZTqGfv6UAskMVJLBRKUsItIQesoNks2d7tq1K3W9c+fOTXoMeZzUqUepBMoRRyirS5leUtRxEFAptYdMCpiSEDSDg2qPPPJIVaNscYTGaB44cKCqxfxxKZzRjsmZbKqC0HueGaiURUQaQk+5QTIJilI41/rKK69Utej50rVI7VIq4fz581UtoyBJ7WYH39O0uugzk39MfOc730kdR/cRc9X0syBVTND9klLOjODMKmCTGzMDlbKISEPoKU8z2QltEZpjfOrUqapGqjV211E3GdVIzVF2mWYIx+vRbGPymSlbTOmLqPbpPPKUKaVBfvRjjz1W1Q4ePNjxev369dUxBCUysvOOo5JV7c5+VMoiIg2hUr7JkHKmzUOzWzP94Q9/qGoxXxuVcymcQKAuuew2RqS8I+RtU+aZPOX4mWgmxwMPPFDVKPGR3TopKmNSwER2o1oVr5SiUhYRaQrTF30iq3yiMiZVvGrVqqpG846zm4XG40h104aclEkmlU0qNaYtyN+lz0nvmZkc9+1vf7s6ht6TuvcoGZKZYZH1hQk699ixY9XfkDOQBw+VsohIQ6iU+wSp4tHR0Ur5RHV4+vTp6lpUo9QD5XwpCRHVLSU0aNIbQZ4vqezoM9O8DXpKINV6/fr1qhY/A3UfkiqmpwR6z+3bt1e1SLZTjybOZXeRjk9g+s6zH5WyiEhDqJS7IDtrgIjeMOVhad4xQQkH8nejqrx48WJ1DHnRVKNML/mjUT3TtWhOMu02TTuDxGTIM888Ux1D+WZSxTRvI7MXHqnirALOojIePFTKIiINYU55EnpRxUScTUFqlya20SwGmldBnm9MElBqg/bZo0wypSNIacYaZZJptsbcuXNT9xF/buQfL1++vKpRF+S2bduqGnX+9VsFR8wuSykqZRGRplApTwLtGJ2daUsT26JKpVnHpEYpaUEdd+vWratqcSYv5XdJiZMnS/vSZbLR2Rw03T9lo2Oi4YknnqiOOX78eFXrdo5xKbV6nmrlLIOJC32TQI0L9EVNj7tkE8RIFkXYaLGOvviovTkz9pNajen69AVMi3NEfE/6WSxatKiqUWSN2pnHx8c7Xh85cqQ6hhZRM0N/Sillw4YNqhW5KWhfiIg0hPZFIBPWf+mll6ofGqkyUtl79+7teE3K8OzZs1WNxlxSkwkt2EXVSrEwUt3UZEKROFLeUQWT1bJmzZqqRjYK1Xbu3NnxmhQwRfWyZBW1SL9RKYuINMRAK2VanMuoK2oPJtV64cKFqhbbmSnWRqqYlCxFyqgxJA6ApzGdpNgffvjhqkaqlQbMP/jggx2vFy9eXB1D0T8aBEQjOOO5zz33XKViyfsnaHSnqlhuFiplEZGGmPFKud+B+3i9PXv2VMdQVIxUXybuRr4tKXFqoCDvmRITUQVTPI1SIDRIh5Qyqewf/OAHHa/Jd6bmjo0bN1Y12gorMjw8XNX6+XfQ6/VEsqiURUQaoumcciYJ0ctWO5njrl27Vh1Dqo+G5lCTRmyEIP+YVCu1T1N6gfLGlMiIkD9NeWnamonUfvSGSRXTtQg6LrOFU79b5EWmA5WyiEhDNK2Uu/XwsudlBtNnUwOU/SUobREhpUxq97PPPqtq1FkYa+SJk8Im9Rw3Dy2Fh/zEzj96T8pxU3qElHL83dEGtGaNZSaiUhYRaYimlXK30DZM2e3gIzRykrZmonwtHReh/DH5xzS8hzYepcRH7MKj88gTJ4V6/vz5qkaecswpZ6HfU0bd9jIcyKSFtIRKWUSkIQY6p5yZ9jY6Olqdlx2jSUo5Zn9pLCWpZ0ppkHombztOaKNJb5RJpuPIY6enhOi7//SnP62OoY1Hd+zYQfemapWBQaUsItIQM8pTzqri7MwDWunftWtXx2vanohmWlC6gLZJiokDmkNBNVLFpKhJyVKuOkKddHfeeWdVO3r0aFWjlMbTTz/d8ZqSEFnfmZIV0UPWF5bZgkpZRKQhptxTvhkKht6TZljE3StKqdUhzaGgTj2CthSKE9Uoa0xeLmWXKalAHnU8lxQwza+gORqkxCmhEjvuaPoeJSYyqlhkNqNSFhFpiBmVvsiqKPKUyT8m4kSyuJV9KaW89957VY1mOxCkeCM0c4J2/Pjoo4+q2kMPPVTVKEccofun7kOabUy5Z/KQI9knJv1iGSRUyiIiDdGMUs6oYDqGIPVM++pRjjh201H+mDxfyupSYiKqYFLFlEkmaM4F5ZSjN0z3SjOWiZ07d6Z84KiU+61sMxMERWYiKmURkYZoJqecWWGnpAJNGhsbG0vtNr179+6qFlMIpHYpSRC75iYiesOUlnj00Ucnva9SSpk/f35X90E++fbt26sa/bzJ383sJE0dg714xSpjma2olEVEGqIZT7lbaCIcqUpKEpCnHFUk7YNHOWVKVZDSjF1+5E/T9UkVU2aYPlNMX2zatKk6hrLGxNDQUMpTjpg1FsmhUhYRaYhmPGVSWzEzTCv/p0+frq5FWVrq3iPiPAlKOFBigqBEQ1TBtM8e7RhNqpggvzjOpiBVTJ+ToAw4qWcR6Q6VsohIQ9wUTzm76h7Vc2aVv5RSfvvb31Y18nxpr704m+LKlSvVMTQVjfapoy65OLGN9rzLQp16pKjjjGL6mWU68ErpvgvPtIRIjqYX+jILSDSEPvv4TwODItTIQYt/K1eurGq06BYbOegYalsmaJGQxmFGa8JBQCLton0hItIQaaU81Y+jtIAUxz/S+E2C4m9vv/12VVu7dm1Ve+uttzpe06D6JUuWVDWyR6gx5JZbbqlqmfuiRUMawUnEhT1a1MuqYrKeCO0Kke5QKYuINEQ6EjfVyicTySIFTL4qLeARNM4zKmNq5FiwYEFVO3nyZOre4iIhDZLfsmVLVRsbG6tqS5curWrUdh6fOIjMUKGJUBWL9A+VsohIQ0x58wh5kJnUQyn1FvS9NIVkt3CKx5EHTOkOagKhMZoR8qxpCyoaqJQlKlkbQETaRaUsItIQU66UyW+89957K6VG6jAq6th4UQonHCj7m+Wpp57qeH348OHUeaSeaZh89JnJFz5+/HhVoyFL1HaeSUdkVbHbMIlMPyplEZGGmHKlTKM1t27dWh1HK/1x2BAN2yGlTGMu6VzKFsfB9+RF00AiSlFcuHChqsWOO0pGUCpkeHi4qmWy3VlUxSJtoFIWEWmIrmdfZJVVNn1B6vDatWuTnhdHbZbCWziRv0tKedGiRR2vKWuchQYGxUQGzeSg0ZqkgFWyIrMPlbKISENMuVLO8tJLL1XXy2wCSqkHUt2knuOYzlJKue222zpeb968uTrm0KFDVY1UMW27FMluw5SdV5FJX6iwRdpFpSwi0hB9nadMKo0GqtOGokRUvDRInjxZUrKUjjh37tyk16PUBs1YpkQJ1eLPIzu4v5cpbipjkZmDSllEpCH6mlMmhUedejTFjRRvTF9Qpx55udnJceQp79y5s+M17WxC2zzR9bt9Ssh6xSpgkdmHSllEpCFQKXerykgFUrogduqVUsrRo0erWlTP5OVS+oI6+khR07lR2dMxNK8iMw86e1wvGXARmdmolEVEGqKve/SRctu/f391LZp4RruKxOQDqV2afUGzKebOnVvVYia5FFbZEcpPk89M8yoy6Qu9YpHBRaUsItIQU56+IE+ZaqSUo6dMvjPNr6D88bp166oadevF91i9enV1DOWlabYxTchbtWpVx2tVsYj8NyplEZGG6OvsC4Imu9GOy+QzR0Uad4IuhWcWk1f81VdfpY5bv359x2tKWtDEtj179lS1m+Epm2cWmdmolEVEGiLtKUcFRv5x3H26lHqnjVLYU6Z5FXfeeWfHa1LFCxYsqGqffPJJVVu2bFlVoxRFVMZHjhypjiGlT6p4AoU6aYqlF1TFIjObrhf66LGbvoAJ+qKLX8Cl1JYG2Q0URSMrZOHChal7i9YEfQET9J/UpUuXKishtlCPj49PeoyIDA7aFyIiDTHn888/72qljx6TSfWRpUEcP368qkXl/fnnn1fHkCqOi3UTQTG8yGOPPVbVaKGPFjQzxw0NDamKReR/UCmLiDREOhIXVTANHyK1SxuWErToFlVwpgX6RqAB9j/+8Y+7upZbM4lIP1Api4g0BCrlTAPCvn37qmNoLCV5ytQG/dRTT1W1119/veM1xdooJpeNv82bN6+qRR+bfGFKWlAahcgoYxMZIoOLSllEpCEwfUFKMJJVhjS684033qhqNMD+Rz/6Ucdr8qxJdVPmmYbVb9q0qarFzU71fEVkOlEpi4g0RF8HElFWl3zm1157rarRiMy4cWpWFT/xxBNVjboIKYOc3dYpoucrIv1ApSwi0hBdK2VKCGShRMaOHTuq2q5duzpex6H3pZRy+fLlqkadf9kuvziEXgUsItOJSllEpCH6qpRJVdKWSATNsDh8+HDH65jGKIUTGVu2bKlqZ86cydxGpZSJbPeeyQ0RuVFUyiIiDYHzlDOqj9Ri1memDVAzMydIFd9xxx1VbXR0tKpluvdKqfPXmcy2iEi/UCmLiDRE2lPu58QzUtTvvvtudVzMKZNXPDY2VtVuv/32qkbJDdqWKkK55exmp/rMInKjqJRFRBoivUdfVHgT+MddZ5fjhqWl1FPiCFLFBOWZSSlfvHix4zVNicuqXVWxiNwoKmURkYbo627WvcwZpmTFtm3bOl6Tst29e3dV27hxY1WjGRnkF2dmX2T89VJUyiJy46iURUQaAucp91PhkfecVc9RedO+gNEDLoX9aVLAdL14nLMvRGQ6USmLiDTE16fa9yRVTHOXKeWQ4eOPP04dR9eneRhRKbtfnohMJyplEZGG6LqjL6uws0kF2ssv7pfXS7qD1DlBHnVkeHg4dS3TFyJyo3Q9urMX9u3bV70p2QvdLvRRJI6GFI2MjFS1+B6Z+ypFS0NE+oP2hYhIQ3TdPNILQ0NDWVXZoaizw+VJydJmqmR9xMFI2QXCeK+lqJ5F5MZRKYuINMRN8ZSzdDsulM6jhcTM6M5uo3qluNAnIjeOSllEpCFmlFLObs1EA/N37tyZ2r4q+sy7du2qrvXcc8+l1HkG1bSI/DcqZRGRhrgp6YssUUVeunSp6+FG2S2o4vVoY1bKWRM3kDIRESmlqJRFRJqiaU85QzannCXT0Ud0+55uwioi/41KWUSkIZpWyt0OQcpcayIyg4t62Uw1ettZpSwig4FKWUSkIZpWyhmyXmu3OeJeyIwjJUitZ5Mces8iMxuVsohIQ8x4pUz0ohZHR0c7ziVlS0o2uzHr1atXO16vWrWqOoamy2U3oM08JaicRdpFpSwi0hCzUilPNVnVStnlqJ6jci6llJGREXPKIgOKSllEpCFUylNIdi/CyFQrYFW3SLuolEVEGqLpKXE3g26TCqSKaaftmNKghEaB/f6yXYSqYJGZjUpZRKQh9JT7RD87Cym10ctO3pmZ09n3FJGpRaUsItIQKuWbTDahkZlelz2X5mjoRYu0gUpZRKQhVMp9op9Ks5fZz9QhGOdrtOwVq9hl0FEpi4g0hEp5Csko3n6rwIzSpNkdRMuKWmS2YvPIFNLPL9xs/K3bL9JMbG6i+9BeEOkf2hciIg2hUr7JZJXnBLWu3vPMmTNVjZSyqlhk+lEpi4g0hAt9M4RsTK7b1u6sAqZFwoyPreoWyaFSFhFpCD3lm0wvCrKfozuzSlzvWWRqUSmLiDSESvkmk1WUlEnuNn2RvQ+bTESmH5WyiEhDmL5okF48WhoFGregInpRuzejnVxktqJSFhFpCJXyABC9YUpQZJVsP9MdqmeRGpWyiEhDqJRnCL0ozWwGOdLtJqyl5LagEpEalbKISEOolKeQqfZRu1XAdA+U2siqW9MXIv1DpSwi0hAq5QEgpi+ymeRedjuJ53Y7vW6ic0VmKyplEZGGUCnPEPqZvpgO1Rqv18t+gmajZZBQKYuINIRKWSbkZqhzlawMOiplEZGGcJ6yTEgv6hY85NR5veSlRWYDKmURkYbQU5ZSSvfdgRMRVXC/d+PWe5bZikpZRKQhVMoyLdyMOSCqaZmJ+KUsPUMbrMbGEFuqRXJoX4iINIRKWSak34tz3V5LZJBQKYuINITNIzIh/VTA2esTLuLJIKFSFhFpCD1lmRYooZHdhFVVLIOESllEpCH0lGVCptrL7efge9W0zBZUyiIiDaFSllJKfwfaZ8/tZTsokdmKSllEpCFMX8gNkVXF/dwOypyyDBIqZRGRhvh/H/KeHIjfJh4AAAAASUVORK5CYII=" id="image276b4737f4" transform="scale(1 -1) translate(0 -271.44)" x="304.56" y="-12.24" width="257.04" height="271.44"/> - + @@ -1247,7 +1247,7 @@ iVBORw0KGgoAAAANSUhEUgAAAWUAAAF7CAYAAAAKWZpQAAA1t0lEQVR4nO2dXa9dZfW+HxQQBQURi6Xl - + @@ -1262,7 +1262,7 @@ iVBORw0KGgoAAAANSUhEUgAAAWUAAAF7CAYAAAAKWZpQAAA1t0lEQVR4nO2dXa9dZfW+HxQQBQURi6Xl - + @@ -1277,7 +1277,7 @@ iVBORw0KGgoAAAANSUhEUgAAAWUAAAF7CAYAAAAKWZpQAAA1t0lEQVR4nO2dXa9dZfW+HxQQBQURi6Xl - + @@ -1310,35 +1310,35 @@ iVBORw0KGgoAAAANSUhEUgAAAWUAAAF7CAYAAAAKWZpQAAA1t0lEQVR4nO2dXa9dZfW+HxQQBQURi6Xl - + - + - + - + - + @@ -1413,7 +1413,7 @@ L 206.751953 3.047384 L 221.052696 -0.629164 L 222.524099 -1 L 222.524099 -1 -" clip-path="url(#pcfc27376b8)" style="fill: none; stroke: #ffa500; stroke-width: 1.5; stroke-linecap: square"/> +" clip-path="url(#p2316fc1cd6)" style="fill: none; stroke: #ffa500; stroke-width: 1.5; stroke-linecap: square"/> - - - - - - + + + + + + @@ -1617,12 +1617,12 @@ z " style="fill: #ffffff"/> +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" id="image2005dda625" transform="scale(1 -1) translate(0 -277.2)" x="561.6" y="-12.24" width="257.04" height="277.2"/> - + @@ -1637,7 +1637,7 @@ iVBORw0KGgoAAAANSUhEUgAAAWUAAAGBCAYAAAC+UKAvAAAj/0lEQVR4nO2d2ZbctrIF4VGD5ePx/7/P - + @@ -1652,7 +1652,7 @@ iVBORw0KGgoAAAANSUhEUgAAAWUAAAGBCAYAAAC+UKAvAAAj/0lEQVR4nO2d2ZbctrIF4VGD5ePx/7/P - + @@ -1667,7 +1667,7 @@ iVBORw0KGgoAAAANSUhEUgAAAWUAAAGBCAYAAAC+UKAvAAAj/0lEQVR4nO2d2ZbctrIF4VGD5ePx/7/P - + @@ -1700,35 +1700,35 @@ iVBORw0KGgoAAAANSUhEUgAAAWUAAAGBCAYAAAC+UKAvAAAj/0lEQVR4nO2d2ZbctrIF4VGD5ePx/7/P - + - + - + - + - + @@ -1803,7 +1803,7 @@ L 463.431953 3.047384 L 477.732696 -0.629164 L 479.204099 -1 L 479.204099 -1 -" clip-path="url(#p1a40674423)" style="fill: none; stroke: #ffa500; stroke-width: 1.5; stroke-linecap: square"/> +" clip-path="url(#p922327e41c)" style="fill: none; stroke: #ffa500; stroke-width: 1.5; stroke-linecap: square"/> - - - - - - + + + + + + @@ -1946,12 +1946,12 @@ z " style="fill: #ffffff"/> +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" id="image626ac58f41" transform="scale(1 -1) translate(0 -271.44)" x="884.88" y="-12.24" width="233.28" height="271.44"/> - + @@ -1966,7 +1966,7 @@ iVBORw0KGgoAAAANSUhEUgAAAUQAAAF7CAYAAACqxvK+AABguklEQVR4nO2dZ3hU5fb2V3rvIQ3SSaOF - + @@ -1981,7 +1981,7 @@ iVBORw0KGgoAAAANSUhEUgAAAUQAAAF7CAYAAACqxvK+AABguklEQVR4nO2dZ3hU5fb2V3rvIQ3SSaOF - + @@ -1996,7 +1996,7 @@ iVBORw0KGgoAAAANSUhEUgAAAUQAAAF7CAYAAACqxvK+AABguklEQVR4nO2dZ3hU5fb2V3rvIQ3SSaOF - + @@ -2029,35 +2029,35 @@ iVBORw0KGgoAAAANSUhEUgAAAUQAAAF7CAYAAACqxvK+AABguklEQVR4nO2dZ3hU5fb2V3rvIQ3SSaOF - + - + - + - + - + @@ -2131,7 +2131,7 @@ L 786.133317 6.140352 L 796.117531 3.047384 L 810.432469 -1 L 810.432469 -1 -" clip-path="url(#pf81a1cdde8)" style="fill: none; stroke: #ffa500; stroke-width: 1.5; stroke-linecap: square"/> +" clip-path="url(#p470a6a59cb)" style="fill: none; stroke: #ffa500; stroke-width: 1.5; stroke-linecap: square"/> - - - - - - + + + + + + @@ -2257,18 +2257,18 @@ z " style="fill: #ffffff"/> +iVBORw0KGgoAAAANSUhEUgAAABMAAAGBCAYAAACEiYowAAABWElEQVR4nO3Yiw0CMRAD0YDov+QLNRit5BE7LsDal88F8br33jOU91TReNlnUCmzXSYzj8xymcw8Mv+pbMmaycwjs1wmM4/MchmXyZ2My3TN8nAn4zK5a7ZkMi6Tu2bcybhM7ppxJ+MyXbM83Mm4TO6aLZmMy+Sumcxy2ZIbwJ1MZh6ZebiTcZncNZNZLuMyvZt5ZOaRmWfJ3Zyd7HmeuTIuc8l1cje7ZWDm5NEAM93NOGDmkt3kMn1QumXuZp4tu+mD0i0DM93NODLzyCyXgZl+guLIzCOzXAZm+gmKIzOPzHKZzDz+F1Quk5lHZrlMZh6Z5TKZeWSWy2Tm2cL0p3u3TGYemT+ULfkDU2YcMJN7N5e8m0uY3s28bAmTe2iX3E2ZeRmX6ScoL9vB5E62ZAO27CaX6RvQLZOZx0NbLpOZx0NbLpOZx0NbLpOZx0Ob53XOGWsbZX4BVQam4NEPKDEAAAAASUVORK5CYII=" id="image1f307128df" transform="scale(1 -1) translate(0 -277.2)" x="822.24" y="-12.24" width="13.68" height="277.2"/> - - + @@ -2283,7 +2283,7 @@ L 3.5 0 - + @@ -2298,7 +2298,7 @@ L 3.5 0 - + @@ -2313,152 +2313,152 @@ L 3.5 0 - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -2486,13 +2486,13 @@ z " style="fill: #ffffff"/> +iVBORw0KGgoAAAANSUhEUgAAABMAAAGBCAYAAACEiYowAAAB+ElEQVR4nO2cgW3EMAwDbSej/Qi//yhNZ1BwwB9AagCiF1K0kxbdn/19FjT32ofSWpzSWuveZ3NixRwPi7lYTE6MdpPTE2OuFEwwtLu7OR0YE/zJ0A0wY6a4mYHZCpqO2k0tZoabD1tBmFZD+2JYTDIa9G6SYl7MujkX02KyTctpqUObcaA8GW56QxtyCwqpIHKd1AcKKOZ1E30RMzctKGbGrJtTMTNm3ZyKme+0oJjZzYZ2KlbM8RRzPjSm9epeN38r1hex+RTzhZgXsxU0HjhnpJgXszmbi4VgekPbM2AuFhLaEExvaDN20/vMGtr5hIQ2BXNt7vLe3ZxPQzsf2k1uA7xuik/0mDst2LQxu8mJ1c350AeKtWnR0IK/qmtoXwxcQSsktMUcjhqTExNXkNdN0gAxZshugpRqTHY3MS0Y86RUUN2cinndPOA1tG7Op5jzYTHJDRA3LfnMzG6GYGpDWzd/Kya+Hogx9x8ppsWsm2Oxy4vJusk9M3HTXuJzM6KCyHW6zwoJbQamt4LIphVjoudmRtPGYHpDG9G0ZGuIvx7AmF43tQaAz0z8UoFiet0kN0B8cWFDm1GO4otLSmgjPsaxTesNbcZXKji0mJYaE5PiDxRSDPyjPfpAAcUu7X/MOqABajdJTLGbnB76zOgKQjE5MRTzH6nyjgS9xeYCAAAAAElFTkSuQmCC" id="image6f64d0df6a" transform="scale(1 -1) translate(0 -277.2)" x="1122.48" y="-12.24" width="13.68" height="277.2"/> - + @@ -2508,7 +2508,7 @@ iVBORw0KGgoAAAANSUhEUgAAABMAAAGBCAYAAACEiYowAAAB+ElEQVR4nO2cgW3EMAwDbSej/Qi//yhN - + @@ -2524,7 +2524,7 @@ iVBORw0KGgoAAAANSUhEUgAAABMAAAGBCAYAAACEiYowAAAB+ElEQVR4nO2cgW3EMAwDbSej/Qi//yhN - + @@ -2540,7 +2540,7 @@ iVBORw0KGgoAAAANSUhEUgAAABMAAAGBCAYAAACEiYowAAAB+ElEQVR4nO2cgW3EMAwDbSej/Qi//yhN - + @@ -2556,7 +2556,7 @@ iVBORw0KGgoAAAANSUhEUgAAABMAAAGBCAYAAACEiYowAAAB+ElEQVR4nO2cgW3EMAwDbSej/Qi//yhN - + @@ -2571,7 +2571,7 @@ iVBORw0KGgoAAAANSUhEUgAAABMAAAGBCAYAAACEiYowAAAB+ElEQVR4nO2cgW3EMAwDbSej/Qi//yhN - + @@ -2586,7 +2586,7 @@ iVBORw0KGgoAAAANSUhEUgAAABMAAAGBCAYAAACEiYowAAAB+ElEQVR4nO2cgW3EMAwDbSej/Qi//yhN - + @@ -2601,7 +2601,7 @@ iVBORw0KGgoAAAANSUhEUgAAABMAAAGBCAYAAACEiYowAAAB+ElEQVR4nO2cgW3EMAwDbSej/Qi//yhN - + @@ -2616,7 +2616,7 @@ iVBORw0KGgoAAAANSUhEUgAAABMAAAGBCAYAAACEiYowAAAB+ElEQVR4nO2cgW3EMAwDbSej/Qi//yhN - + @@ -2644,16 +2644,16 @@ z - + - + - + - +