Skip to content

MultipoleDisk: radial profiles by quadrature

Provisional

May change in any 0.4.x release without a deprecation cycle.

A disk, ring or halo described by its radial brightness profile \(I(r)\) in the plane of the disk, as one component of a System. It is placed and weighted like any other component, and its visibility is the Hankel transform of the profile. The maths, and a worked fit, are in the tutorial A planet in a gap between rings. The ideas follow frank (Jennings et al. 2020) and vis-r (Kennedy 2025); virgil reimplements them, with no shared code, for V² and closure-phase data and for composition in a scene.

Each profile owns its quadrature rule, so the transform is a sum over n_r nodes that converges spectrally: a narrow ring needs tens of nodes, not the hundreds of a midpoint stack.

Profile Brightness Rule
ThinRing(radius) \(\delta(r - r_0)\) exact: \(J_0(2\pi q r_0)\)
GaussianRing(radius, sigma) Gaussian in \(r\) Gauss–Legendre over \(r_0 \pm 6\sigma\)
RadialPowerLaw(r_in, r_out, index, edge_sigma) \(r^{\rm index}\) between the radii, sharp or soft edges Gauss–Legendre
ExpTail(r_in, scale_length, *, cutoff) \(\exp[-(r - r_{\rm in})/L]\) for \(r \ge r_{\rm in}\) Gauss–Laguerre, to infinity
Gap(base, radius, sigma, depth) a Gaussian gap cut in base that of base
RadialSum(a=..., b=(profile, weight)) named profiles, each normalized and mixed by its weight each part's
RadialGP(r_in, r_out, sigma, ell, nu) \(\exp g(r)\), \(g\) a Gaussian process its own: Gauss–Legendre on \([r_{\rm in}, r_{\rm out}]\)

A profile has no weight of its own: only the parts of a RadialSum do (disk.profile.outer.weight).

Names and units

One name for one concept across the family. Lengths are in milliarcseconds in the plane of the disk, angles in degrees, and a name carries a unit suffix only where the quantity would otherwise be ambiguous (pixel_scale_mas, rotation_deg), as in the older components.

Concept Name Where
radius of a feature's centre radius ThinRing, GaussianRing, Gap (as GaussianArc, FlaredDisk)
edges of the support r_in, r_out RadialPowerLaw, ExpTail (r_in), RadialGP, RadialGPFunction, MultipoleKernel
reference radius of a function r_ref RadialLinear, RadialPower, PowerLawHeight, MultipoleKernel
Gaussian standard deviation sigma (FWHM = 2.355 sigma) GaussianRing, Gap, as GaussianDisk; edge_sigma for a soft edge
FWHM of a blur blur_fwhm MultipoleDisk
e-folding length scale_length ExpTail
inclination, position angle of the major axis inc, pa (degrees; default 0, face-on, North) MultipoleDisk, MultipoleField
height h_ref, psi; alpha, tip PowerLawHeight, Cone (as TruncatedCone)

The older components keep their names: ModulatedGaussianRim takes a diameter diam where a profile takes radius (radius = diam / 2), and EllipticalGaussian and TruncatedCone take an axis ratio ratio where the disk takes inc (ratio = cos(inc)).

The options each have their own entry below: azimuthal structure (Harmonics, VonMises, and the priors of MultipoleKernel, with conversions between the forms in virgil.harmonics), height (Cone, PowerLawHeight), Gaussian-process priors (gp_priors), and a residual aligned with the disk (MultipoleField).

virgil.models.MultipoleDisk

Bases: Component

Axisymmetric disk with a radial profile, evaluated by quadrature.

Provisional

May change in any 0.4.x release without a deprecation cycle.

A radial profile \(I(r)\) in the disk plane, inclined by inc and rotated by pa; the visibility is its Hankel transform summed over n_r nodes of the profile's quadrature rule. Profiles: ThinRing, GaussianRing, RadialPowerLaw, ExpTail, Gap, RadialSum, RadialGP; parameters are reached by path ("disk.profile.radius"). Choosing n_r, symmetry and precision: Model notes, guide.

Source: Jennings et al. (2020), references, and Kennedy (2025), references; ideas only.

Parameters:

Name Type Description Default
profile profile

The radial profile; its quadrature rule is set to n_r nodes.

required
harmonics optional

A Harmonics, VonMises or None azimuthal factor of mean one (odd orders make closure phases non-zero); a Cone, PowerLawHeight or None height of each ring above the disk plane (see the notes).

None
height optional

A Harmonics, VonMises or None azimuthal factor of mean one (odd orders make closure phases non-zero); a Cone, PowerLawHeight or None height of each ring above the disk plane (see the notes).

None
inc float or array - like

Inclination (0 is face-on) and position angle of the projected major axis (North to East), in degrees; FWHM in mas, in the disk plane, of a smoothing Gaussian (exact envelope).

0.0
pa float or array - like

Inclination (0 is face-on) and position angle of the projected major axis (North to East), in degrees; FWHM in mas, in the disk plane, of a smoothing Gaussian (exact envelope).

0.0
blur_fwhm float or array - like

Inclination (0 is face-on) and position angle of the projected major axis (North to East), in degrees; FWHM in mas, in the disk plane, of a smoothing Gaussian (exact envelope).

0.0
flux optional

As for any Component.

1.0
dra optional

As for any Component.

1.0
ddec optional

As for any Component.

1.0
n_r int

Quadrature nodes per profile (default 64; static); suggest_n_r gives a start.

64
spectral_index optional

A float, radial function or RadialGPFunction makes the profile chromatic, \(I_0(r)(\lambda/\lambda_0)^{\alpha(r)}\); flux is then the flux at wavel0 (metres, required) and must be a number.

None
wavel0 optional

A float, radial function or RadialGPFunction makes the profile chromatic, \(I_0(r)(\lambda/\lambda_0)^{\alpha(r)}\); flux is then the flux at wavel0 (metres, required) and must be a number.

None

Examples:

>>> ring = GaussianRing(radius=10.0, sigma=3.0)
>>> profile = Gap(ring, radius=10.0, sigma=0.8)
>>> disk = MultipoleDisk(profile, inc=50.0, pa=30.0, blur_fwhm=1.0, n_r=48)

suggest_n_r(data)

A starting number of nodes for data's longest baselines.

Enough to sample the Bessel oscillations across the profile and the phase of a height's lift (at the current parameter values, which must be concrete), rounded up to a multiple of 8 (but at most the 144 nodes buildable for a Gauss-Laguerre tail). A starting point only: check convergence by doubling n_r.

Parameters:

Name Type Description Default
data OIData

Anything with u, v (metres) and wavel (metres).

required

freeze_geometry(data)

A disk with its Bessel matrices cached for data.

With inc, pa, height and the nodes fixed, the transform matrices depend only on the baselines and nodes, so a refit of the profile, harmonics or spectral index is one matrix-vector product per call (and per order) instead of the Bessel functions of every baseline and node. Never automatic: call it once the geometry is settled, and fit the returned model. The cache checks its baselines, nodes and geometry at every call and silently falls back to the full computation (correct, not faster) for any other, so changing a frozen quantity loses the speed-up. Build it in the precision you evaluate in (inside jax.enable_x64(True) for a float64 fit; a float32 cache still matches a float64 fit, to float32 rounding). It holds (2M + 1) complex matrices of shape (n_samples, n_r) (M the harmonics' degree).

Parameters:

Name Type Description Default
data OIData

Anything with u, v (metres) and wavel (metres), converted to wavelength units inside: the samples the model will be evaluated at.

required

Returns:

Type Description
MultipoleDisk

This disk with the cache (frozen); model equals the unfrozen disk's to rounding.

min_intensity(n_theta=720)

The least brightness relative to the azimuthal mean.

The minimum over the quadrature nodes and n_theta azimuths of I(r, theta) / I0(r): 1 without harmonics, and negative if the image goes negative somewhere (possible for the linear and von Mises forms, never for the positive one). is_physical is false then.

gp_sites()

The Gaussian-process parts of the disk, by path.

Returns a dict from the path (relative to the disk) of every RadialGP profile or RadialGPFunction (a harmonics coefficient or the spectral index) and of the harmonics when they carry a MultipoleKernel, to that object. Its whitened latents are at <path>.z (harmonics.z for the kernel), its hyperparameters at <path>.sigma, <path>.ell (harmonics.prior.sigma, .ell_r, .ell_theta).

Profiles

virgil.models.ThinRing

Bases: _RadialProfile

Infinitely thin ring of radius radius, \(I \propto \delta(r - r_0)\) with \(r_0\) = radius.

Provisional

May change in any 0.4.x release without a deprecation cycle.

Its Hankel transform is \(J_0(2\pi q r_0)\), so it needs no quadrature (n_r is irrelevant). Rendered, it is a Gaussian a pixel wide.

Parameters:

Name Type Description Default
radius float or array - like

Radius in milliarcseconds, in the plane of the disk.

required

virgil.models.GaussianRing

Bases: _RadialProfile

Gaussian ring, \(I \propto \exp[-(r - r_0)^2 / 2\sigma^2]\), with \(r \ge 0\) and \(r_0\) = radius.

Provisional

May change in any 0.4.x release without a deprecation cycle.

The quadrature runs over \(r_0 \pm 6\sigma\) (clipped at the centre) with a Gauss-Legendre rule, which converges spectrally.

Parameters:

Name Type Description Default
radius float or array - like

Radius of the peak in milliarcseconds.

required
sigma float or array - like

Standard deviation of the radial profile in milliarcseconds (the FWHM is \(2\sqrt{2\ln 2}\,\sigma \approx 2.355\,\sigma\)).

required
References

Gaussian rings are the basic building block of the parametric radial models of vis-r (Kennedy 2025, RASTI 4, rzaf004; https://github.com/drgmk/vis-r).

virgil.models.RadialPowerLaw

Bases: _RadialProfile

Power law \(I \propto r^{\rm index}\) between r_in and r_out.

Provisional

May change in any 0.4.x release without a deprecation cycle.

With edge_sigma (a length in mas) both edges are soft error functions of that standard deviation; the quadrature then runs over 6 edge_sigma beyond them, and n_r must resolve edge_sigma. With edge_sigma=None the edges are sharp and the Gauss-Legendre rule spans exactly [r_in, r_out], spectrally accurate for a smooth power law (index=0, r_in=0 is a uniform disk).

Source: Kennedy (2025), vis-r, references.

Parameters:

Name Type Description Default
r_in float or array - like

Inner and outer radius in mas.

required
r_out float or array - like

Inner and outer radius in mas.

required
index float or array - like

Power-law index (default 0).

0.0
edge_sigma (float, array - like or None)

Standard deviation of the soft edges in mas, or None for sharp edges (default).

None

virgil.models.ExpTail

Bases: _RadialProfile

Exponential tail, \(\exp[-(r - r_{\rm in})/L]\) beyond r_in.

Provisional

May change in any 0.4.x release without a deprecation cycle.

By default a Gauss-Laguerre rule integrates the whole tail to infinity, so no outer radius is needed; r_in = 0 is an exponential disk. The rule of thumb \(16 \cdot 2\pi q L\) nodes at the longest baseline \(q\) is optimistic for a well-resolved tail (error in \(V\) about \(2 \times 10^{-4}\) at 128 nodes, \(4 \times 10^{-5}\) at the 150-node cap). For a tail resolved much more finely, prefer a RadialPowerLaw with a soft edge, or give a cutoff: the light beyond \(r_{\rm in} + \mathrm{cutoff}\, L\) is dropped (0.7 % of a thin disk's flux at 5) and a Gauss-Legendre rule is used. See MultipoleDisk.suggest_n_r and Model notes.

Parameters:

Name Type Description Default
r_in float or array - like

Inner radius in mas, where the tail starts, and the e-folding length \(L\) in mas.

required
scale_length float or array - like

Inner radius in mas, where the tail starts, and the e-folding length \(L\) in mas.

required
cutoff float or None

Cut the tail at r_in + cutoff * scale_length (static; None, the default, is the whole tail).

None

virgil.models.Gap

Bases: _RadialProfile

A Gaussian gap cut in another profile.

Provisional

May change in any 0.4.x release without a deprecation cycle.

The brightness is that of base times \(1 - d \exp[-(r - r_0)^2 / 2 \sigma^2]\) (\(r_0\) = radius), so depth=1 empties the gap and depth=0 leaves base. The quadrature is the base profile's: its n_r must resolve sigma.

Source: Kennedy (2025), vis-r, references.

Parameters:

Name Type Description Default
base profile

The profile the gap is cut in; it must have a brightness, so a ThinRing cannot be gapped.

required
radius float or array - like

Radius of the gap's centre and its standard deviation in mas.

required
sigma float or array - like

Radius of the gap's centre and its standard deviation in mas.

required
depth float or array - like

Fractional depth between 0 and 1 (default 1).

1.0

virgil.models.RadialSum

Bases: _RadialProfile

Sum of named radial profiles, mixed by their weights.

Provisional

May change in any 0.4.x release without a deprecation cycle.

A part is a profile, or a (profile, weight) pair (a bare profile has weight 1). Each part is normalized to unit total light and then weighted by its weight (relative to the others), so the weights are the parts' shares of the disk's flux, as the flux of a System's components are. Keep the weights as parameters and fit their ratios: parts are reached by name, disk.profile.inner.radius and disk.profile.outer.weight. Each part keeps its own quadrature rule, so the sum has n_r nodes per part.

Examples:

>>> profile = RadialSum(
...     inner=GaussianRing(radius=3.0, sigma=0.5),
...     outer=(GaussianRing(radius=8.0, sigma=1.0), 0.5),
... )
>>> disk = MultipoleDisk(profile, inc=40.0, pa=20.0, n_r=32)

virgil.models.Harmonics

Bases: _Azimuthal

Azimuthal harmonics of a MultipoleDisk, of exact degree M.

Provisional

May change in any 0.4.x release without a deprecation cycle.

The brightness of a MultipoleDisk is the profile \(I_0(r)\) times an azimuthal factor of mean 1 in the disk plane (\(\theta\) from the projected major axis towards pa + 90, as for ModulatedGaussianRim), with harmonics 0..M. form="positive" (the default without c/s) is \(|h(e^{i\theta})|^2\) normalized, positive for all parameters; "linear" is \(1 + \sum_m c_m(r)\cos m\theta + s_m(r)\sin m\theta\), positive if sum(sqrt(c_m**2 + s_m**2)) <= 1 (MultipoleDisk.min_intensity reports violations). Arguments after c and s are keyword-only; see Model notes and virgil.harmonics.

Parameters:

Name Type Description Default
c float, radial function or sequence of them

Linear form: coefficients of \(\cos m\theta\), \(\sin m\theta\) from first order up; a scalar is first order only, missing orders are zero.

()
s float, radial function or sequence of them

Linear form: coefficients of \(\cos m\theta\), \(\sin m\theta\) from first order up; a scalar is first order only, missing orders are zero.

()
re float, radial function or sequence

Positive form: real and imaginary parts of the latents \(w_j\), or (constants only) w as complex numbers.

()
im float, radial function or sequence

Positive form: real and imaginary parts of the latents \(w_j\), or (constants only) w as complex numbers.

()
w float, radial function or sequence

Positive form: real and imaginary parts of the latents \(w_j\), or (constants only) w as complex numbers.

()
degree optional

Highest order M (static; changing it recompiles; default the number of coefficients); "positive" or "linear" (default "linear" if c or s is given).

None
form optional

Highest order M (static; changing it recompiles; default the number of coefficients); "positive" or "linear" (default "linear" if c or s is given).

None
prior MultipoleKernel

Gaussian-process prior over every order; needs degree and none of c, s, re, im, w. The parameter is the whitened latent z of shape (2, M, n).

None

Examples:

>>> w = (0.4 + 0.1j, 0.2j)
>>> harmonics = Harmonics(w=w)
>>> disk = MultipoleDisk(GaussianRing(6.0, 1.0), harmonics=harmonics)

image_harmonics(r=None)

Image harmonics I_0 .. I_M (complex, I_0 = 1) at the radii r (not needed for constants), shape (M + 1,) + shape(r).

Linear form: \(I_m = (c_m - i s_m)/2\). Positive form: \(I_m = \sum_j h_{j+m} \bar h_j / \sum_j |h_j|^2\).

coefficients(r)

The linear (c, s), each of shape (M,) + shape(r), at the radii r.

polynomial(r)

The minimum-phase polynomial h_0 .. h_M (h_0 = 1) of the positive form at the radii r, complex, shape (M + 1,) + shape(r).

virgil.models.VonMises

Bases: _Azimuthal

A von Mises azimuthal profile, truncated at degree.

Provisional

May change in any 0.4.x release without a deprecation cycle.

The profile \(\exp[\kappa\cos(\theta - \theta_0)]\) is an arc of width \(\sim 1/\sqrt\kappa\) radians at azimuth \(\theta_0\) in the disk plane of a MultipoleDisk (as for Harmonics); \(\kappa = 0\) is a uniform ring. The harmonics come from a small FFT, differentiable in \(\kappa\) and \(\theta_0\). The truncated series goes slightly negative if degree is too low (about \(5.7\sqrt\kappa\) for harmonics below \(10^{-6}\)); truncation_power reports the power dropped (Model notes).

Parameters:

Name Type Description Default
kappa float or radial function

Concentration (non-negative).

required
theta0 float or radial function

Azimuth of the brightest point in degrees (default 0, the major axis).

0.0
degree int

Highest order kept (default the smallest keeping the harmonics above \(10^{-6}\) for constant kappa, at most 256, else 32), and FFT points (default max(512, 8 (degree + 1)), accurate for \(\kappa\) below about n_fft**2 / 30). Both static.

None
n_fft int

Highest order kept (default the smallest keeping the harmonics above \(10^{-6}\) for constant kappa, at most 256, else 32), and FFT points (default max(512, 8 (degree + 1)), accurate for \(\kappa\) below about n_fft**2 / 30). Both static.

None

Examples:

>>> arc = VonMises(kappa=5.0, theta0=30.0, degree=20)
>>> ring = MultipoleDisk(GaussianRing(6.0, 1.0), harmonics=arc, inc=40.0)

image_harmonics(r=None)

Image harmonics I_0 .. I_M (I_0 = 1) of the truncated profile at the radii r (not needed for constants).

truncation_power(r=None)

The fraction of the power of the structure that the truncation at degree drops, \(\sum_{m > M} |I_m|^2 / \sum_{m \ne 0} |I_m|^2\) (0 for a degree that holds everything; shape of r).

virgil.models.RadialLinear

Bases: _RadialFunction

value + slope (r - r_ref): a coefficient that varies linearly.

Provisional

May change in any 0.4.x release without a deprecation cycle.

Parameters:

Name Type Description Default
value float or array - like

The function at r = r_ref.

required
slope float or array - like

Gradient per milliarcsecond (default 0).

0.0
r_ref float or array - like

Reference radius in milliarcseconds (default 0).

0.0

virgil.models.RadialPower

Bases: _RadialFunction

value (r / r_ref) ** index: a coefficient that follows a power law.

Provisional

May change in any 0.4.x release without a deprecation cycle.

Parameters:

Name Type Description Default
value float or array - like

The function at r = r_ref.

required
index float or array - like

Power-law index.

required
r_ref float or array - like

Reference radius in milliarcseconds (positive).

required

virgil.models.Cone

Bases: _Height

A conical surface: the ring at radius \(r\) lies at the height \(h = r \cot\alpha - \mathrm{tip}\) along the disk's normal.

Provisional

May change in any 0.4.x release without a deprecation cycle.

This is the geometry of TruncatedCone: the normal is the cone's axis, so the disk's inclination is $90^\circ - $ the axis's tilt out of the sky, and pa is the position angle of the major axis, 90 degrees from the projection of the axis. Positive heights shift towards position angle pa + 90. In slant distance \(s\) from the apex, \(r = s \sin\alpha\).

Parameters:

Name Type Description Default
alpha float or array - like

Half-opening angle in degrees (0 < alpha < 90).

required
tip float or array - like

Distance in milliarcseconds from the disk's centre back to the apex along the axis (the ring at \(r = 0\) lies at \(-\mathrm{tip}\)).

0.0

virgil.models.PowerLawHeight

Bases: _Height

A flared surface: \(h = h_{\rm ref} (r / r_{\rm ref})^\psi\).

Provisional

May change in any 0.4.x release without a deprecation cycle.

Positive heights shift towards position angle pa + 90 on the sky (see Cone); a negative h_ref puts the surface on the other side. A top surface lifted towards the observer shifts the apparent ring towards the far side, so when pa + 90 is the near side of the disk it has h_ref < 0.

Parameters:

Name Type Description Default
h_ref float or array - like

Height at r_ref in milliarcseconds.

required
r_ref float or array - like

Reference radius in milliarcseconds (positive).

required
psi float or array - like

Flaring index (default 1, a cone through the centre).

1.0

Gaussian-process priors

virgil.models.RadialGP

Bases: _RadialGPBase, _RadialProfile

Non-parametric radial profile: the exponential of a Gaussian process.

Provisional

May change in any 0.4.x release without a deprecation cycle.

\(I_0(r) = \exp g(r)\). One whitened latent z per Gauss-Legendre node of \([r_{\rm in}, r_{\rm out}]\) (n_r of them), \(g = \mu + \sigma L z\), interpolated linearly in \(r\). The visibility is normalized, so leave mu at 0. Without the exponential it is RadialGPFunction. Priors (z standard normal, sigma and ell log-uniform via gp_priors; MAP biases the scales), draws and the meaning of ell: Model notes.

Parameters:

Name Type Description Default
r_in float

Radii in mas of the first and last node.

required
r_out float

Radii in mas of the first and last node.

required
sigma float

Standard deviation of \(g\), correlation length in mas, and mean of \(g\) (defaults 1, 1, 0).

1.0
ell float

Standard deviation of \(g\), correlation length in mas, and mean of \(g\) (defaults 1, 1, 0).

1.0
mu float

Standard deviation of \(g\), correlation length in mas, and mean of \(g\) (defaults 1, 1, 0).

1.0
z (array - like, shape(n_r))

Whitened latents (default 0); set when the profile is built into a disk.

None
nu optional

Matérn smoothness in {0.5, 1.5, 2.5, inf} (default 1.5; inf squared exponential) and relative jitter on the correlation diagonal (default 1e-6). Both static.

1.5
jitter optional

Matérn smoothness in {0.5, 1.5, 2.5, inf} (default 1.5; inf squared exponential) and relative jitter on the correlation diagonal (default 1e-6). Both static.

1.5

Examples:

>>> profile = RadialGP(r_in=0.5, r_out=12.0, sigma=1.5, ell=2.0)
>>> disk = MultipoleDisk(profile, inc=30.0, n_r=24)
>>> disk.profile.z.shape
(24,)

draws(key, n=1, r=None, positive=False)

n draws from the prior at the radii r (the nodes by default).

Draws \(g = \mu + \sigma L z\) with \(z \sim N(0, I)\) at the current mu, sigma, ell; with positive, \(\exp g\) (the profile). Shape (n,) + shape(r). Use them to show the prior beside the posterior.

nodes()

The nodes in milliarcseconds.

virgil.models.RadialGPFunction

Bases: _RadialGPBase, _RadialFunction

A Gaussian process in the radius, as a function (no exponential).

Provisional

May change in any 0.4.x release without a deprecation cycle.

For the spectral_index of a MultipoleDisk or a coefficient of Harmonics; for a non-negative profile use RadialGP. Values at n equally spaced nodes are \(g = \mu + \sigma L z\) with whitened latents z (a standard-normal prior is the GP prior), interpolated linearly (constant beyond). Priors and ell are as for RadialGP.

Parameters:

Name Type Description Default
r_in float or None

Radii in mas of the first and last node; both or neither (default the disk's profile range).

None
r_out float or None

Radii in mas of the first and last node; both or neither (default the disk's profile range).

None
sigma float

Standard deviation of \(g\), correlation length in mas, and mean of \(g\) (defaults 1, 1, 0).

1.0
ell float

Standard deviation of \(g\), correlation length in mas, and mean of \(g\) (defaults 1, 1, 0).

1.0
mu float

Standard deviation of \(g\), correlation length in mas, and mean of \(g\) (defaults 1, 1, 0).

1.0
z optional

Whitened latents, shape (n,) (default 0; its size is the number of nodes), and the number of nodes when z is not given (default 16).

None
n optional

Whitened latents, shape (n,) (default 0; its size is the number of nodes), and the number of nodes when z is not given (default 16).

None
nu optional

Matérn smoothness in {0.5, 1.5, 2.5, inf} (default 1.5) and relative jitter on the correlation diagonal (default 1e-6). Both static.

1.5
jitter optional

Matérn smoothness in {0.5, 1.5, 2.5, inf} (default 1.5) and relative jitter on the correlation diagonal (default 1e-6). Both static.

1.5

Examples:

>>> alpha = RadialGPFunction(sigma=0.5, ell=4.0, n=12)
>>> alpha.z.shape
(12,)

draws(key, n=1, r=None, positive=False)

n draws from the prior at the radii r (the nodes by default).

Draws \(g = \mu + \sigma L z\) with \(z \sim N(0, I)\) at the current mu, sigma, ell; with positive, \(\exp g\) (the profile). Shape (n,) + shape(r). Use them to show the prior beside the posterior.

nodes()

The nodes in milliarcseconds.

virgil.models.MultipoleKernel

Bases: Base

Matérn-kernel prior over the harmonics of a MultipoleDisk.

Provisional

May change in any 0.4.x release without a deprecation cycle.

A Matérn kernel in radius and azimuth over the azimuthal-harmonic coefficients of a MultipoleDisk. Pass it to Harmonics as prior=. Each order \(m = 1 \dots M\) is an independent RadialGPFunction with radial length ell_r; the variance falls with order as a periodic Matérn spectral density of azimuthal length ell_theta (Model notes). The mean is a uniform ring. Whitened latents z have shape (2, M, n) (disk.harmonics.z, standard-normal prior); log-uniform priors on the scales: gp_priors.

Parameters:

Name Type Description Default
ell_r float

Correlation length in the radius (mas), and in the azimuth (mas along the ring at r_ref).

required
ell_theta float

Correlation length in the radius (mas), and in the azimuth (mas along the ring at r_ref).

required
sigma float

Standard deviation of the first-order coefficients (default 0.3).

0.3
r_in float or None

Range of the radial processes (default the disk's profile extent) and the radius converting ell_theta to an angle (default mid-range).

None
r_out float or None

Range of the radial processes (default the disk's profile extent) and the radius converting ell_theta to an angle (default mid-range).

None
r_ref float or None

Range of the radial processes (default the disk's profile extent) and the radius converting ell_theta to an angle (default mid-range).

None
n optional

Number of radial nodes (default 16), and Matérn smoothness in radius and azimuth in {0.5, 1.5, 2.5, inf} (default 1.5). All static.

_GP_NODES
nu_r optional

Number of radial nodes (default 16), and Matérn smoothness in radius and azimuth in {0.5, 1.5, 2.5, inf} (default 1.5). All static.

_GP_NODES
nu_theta optional

Number of radial nodes (default 16), and Matérn smoothness in radius and azimuth in {0.5, 1.5, 2.5, inf} (default 1.5). All static.

_GP_NODES

Examples:

>>> prior = MultipoleKernel(ell_r=3.0, ell_theta=4.0, sigma=0.4)
>>> harmonics = Harmonics(degree=2, prior=prior)
>>> ring = GaussianRing(6.0, 1.5)
>>> disk = MultipoleDisk(ring, harmonics=harmonics, n_r=32)
>>> disk.harmonics.z.shape
(2, 2, 16)

spectrum(degree)

S(m) / S(1) for m = 1 .. degree: the variance of the order-m coefficients relative to the first order's.

draws(key, degree, n=1, r=None)

n draws of the coefficients from the prior at the radii r (the nodes by default): shape (n, 2, degree) + shape(r).

virgil.models.gp_priors(model, sigma=None, ell=None, ell_theta=None)

Priors for a scene's Gaussian-process parameters, for fit.

Provisional

May change in any 0.4.x release without a deprecation cycle.

For the Gaussian-process parts of a scene's MultipoleDisks, for fit and numpyro_model. Returns a dict from parameter path to prior for every RadialGP profile, RadialGPFunction (a harmonics coefficient or the spectral index) and MultipoleKernel in model (a disk, a System or a rotated source). The whitened latents z get independent standard-normal priors (the GP prior itself). sigma (process standard deviations, kernel amplitude), ell (radial correlation lengths) and ell_theta (the kernel's azimuthal length), given as bounds (low, high), get log-uniform priors, so the prior and the Laplace evidence are proper; left None the hyperparameters are fixed at the model's values and not in the dict. Add priors for the other free parameters yourself.

Examples:

>>> import numpyro.distributions as dist
>>> disk = MultipoleDisk(RadialGP(0.5, 12.0), n_r=24)
>>> scene = System(star=PointSource(), disk=disk)
>>> priors = gp_priors(scene, sigma=(0.1, 3.0), ell=(0.5, 8.0))
>>> sorted(priors)
['disk.profile.ell', 'disk.profile.sigma', 'disk.profile.z']