Superposition#

Module pianistpy.superposition

Linear superposition of models that share one truncated density.

superpose combines models built on one truncated potential-density pair into one model, moments returns the velocity moments of the combined model, and mixture combines any per-component quantity by the weights.

pianistpy.superposition.mixture(weights, values, xp=np)#

Return the weighted sum of per-component values, with the weights normalized.

A superposed model combines a density, a velocity moment or any other quantity computed per component by the weighted sum mixture returns. The sum is linear in the values and, through the normalization, a smooth function of the weights. With xp=jax.numpy the sum is differentiable in the weights by JAX.

Parameters:
  • weights (array-like, shape (n_components,)) – The weights \(w_k\), positive. The call divides the weights by the weights’ sum and does not check the weights.

  • values (array-like, shape (n_components, ...)) – The value \(v_k\) of each component, along the first axis.

  • xp (module, optional) – The array module, numpy or jax.numpy. Default: np, which means NumPy arrays in and out.

Returns:

value – The sum \(\sum_k w_k v_k / \sum_k w_k\), with the shape of one component’s value, as an array of xp.

Return type:

array, shape (…)

See also

superpose

The model that combines components by the weights.

moments

The velocity moments of a superposed model.

Examples

>>> from pianistpy.superposition import mixture
>>> mixture([1.0, 3.0], [[2.0, 4.0], [6.0, 8.0]])
array([5., 7.])
pianistpy.superposition.moments(model, r, n_nodes=None)#

Return the second velocity moments of a superposed model at 3D radii.

Parameters:
  • model (Model) – A superposed model (superpose) whose components share one spatially finite density.

  • r (array-like, shape (n,)) – User-specified 3D radii, in internal units whatever the model’s unit system.

  • n_nodes (int, optional) – The number of Gauss-Legendre nodes of each component’s Jeans integral. Default: None, which means the value of the model’s setting observables.n_jeans_nodes.

Returns:

moments – Arrays in internal units, whatever the model’s unit system: sigma_r2 and sigma_t2, the squared radial and tangential dispersions of the superposed model, and beta_sigma, the superposed model’s velocity anisotropy, each with the shape of r; sigma_r2_components and beta_components, the squared radial dispersion and the anisotropy of each component, one row per component along the first axis, each row with the shape of r. beta_sigma is NaN at a radius where sigma_r2 is not positive.

Return type:

dict

Raises:
  • InputError – If model is not a Model.

  • InputError – If the density of model is not spatially finite.

See also

superpose

The superposed model.

mixture

The weighted sum the moments are formed by.

Notes

The squared radial dispersion \(\sigma_{r,k}^2\) of component \(k\) is the Jeans integral of the component’s augmented density on the shared profile, evaluated by a Gauss-Legendre rule in the variable \(\ln(1 - \mathcal{K}/P_0)\), with \(\mathcal{K}\) the binding energy and \(P_0\) the central potential. Because the components share one density, the moments of the superposed model are the weighted sums

\[\sigma_r^2 = \sum_k w_k \sigma_{r,k}^2, \qquad \sigma_t^2 = 2 \sum_k w_k (1 - \beta_k) \sigma_{r,k}^2, \qquad \beta_\sigma = 1 - \frac{\sigma_t^2}{2 \sigma_r^2},\]

with \(w_k\) the normalized weight and \(\beta_k\) the velocity anisotropy of component \(k\).

References

Chiang+(2026d), Eq. (B43), Chiang+(2026d), Eq. (B44) and Chiang+(2026d), Eq. (B74).

Examples

>>> import pianistpy as pp
>>> settings = pp.Settings(quick=True)
>>> profile = pp.DoublePowerLaw.nfw()
>>> parent = pp.solve(pp.Component(profile), pp.Truncation(k_t=0.05), settings)
>>> children = [
...     pp.solve(
...         pp.Component(parent.profile, pp.Anisotropy(beta_c=b)), None, settings
...     )
...     for b in (0.0, 0.2)
... ]
>>> model = pp.superpose({children[0]: 0.5, children[1]: 0.5})
>>> result = pp.superposition.moments(model, [0.5, 1.0])
>>> result["sigma_r2"].shape, result["sigma_r2_components"].shape
((2,), (2, 2))
pianistpy.superposition.superpose(members)#

Return the superposition of models built on one truncated profile.

The returned model is an equilibrium of the shared pair, because every member’s distribution function was inverted from the shared density.

Parameters:

members (mapping of Model to float) – Two or more models, each of one component and each built on the same truncated profile (for example, Method II children of one truncated parent), mapped to the models’ weights. Each weight is positive and finite. The weights are normalized to sum to 1, with a logged warning where the weights’ sum differs from 1 by more than \(10^{-10}\).

Returns:

model – A model with one component per member, in the order of members. Each component is the member’s component, with a mass equal to the member’s weight divided by the first member’s weight, and with the member’s DF table on the member’s own energy grid. The model’s profile is the shared truncated profile with the density split among the components by weight. Each check of the model’s ValidationReport holds the largest error of the check over the members.

Return type:

Model

Raises:
  • InputError – If members is not a mapping.

  • InputError – If fewer than two members are given.

  • InputError – If a member is not a Model, or has more than one component.

  • InputError – If a member’s profile is not a truncated profile with \(\mathcal{K}_{\rm t} > 0\).

  • InputError – If a weight is not positive and finite.

  • InputError – If the members are built on different truncated profiles.

  • InputError – If the members were computed with different settings, or are in different units.

  • InputError – If a member’s DF is signed.

  • InputError – If the members’ central values of the potential differ.

  • InputError – If some members’ distribution functions come from closed forms (directly or through a closed-form parent) and the other members’ distribution functions were computed by inversion. Build every member the same way, from closed forms or by inversion, to superpose the members.

  • InputError – If the members’ validation reports hold different sets of checks.

See also

moments

The velocity moments of the superposed model.

mixture

The weighted sum of per-component quantities.

References

Chiang+(2026d), Appendix B5; Notes Section T:4.

Examples

>>> import pianistpy as pp
>>> settings = pp.Settings(quick=True)
>>> profile = pp.DoublePowerLaw.nfw()
>>> parent = pp.solve(pp.Component(profile), pp.Truncation(k_t=0.05), settings)
>>> children = [
...     pp.solve(
...         pp.Component(parent.profile, pp.Anisotropy(beta_c=b)), None, settings
...     )
...     for b in (0.0, 0.2)
... ]
>>> model = pp.superpose({children[0]: 0.5, children[1]: 0.5})
>>> len(model.components), [c.mass for c in model.components]
(2, [1.0, 1.0])