Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
cbc8ad4
Add non-isotropic dust scattering, including:
psheehan Apr 15, 2026
e7ae911
Update dust-demo notebook to get the relevant info for non-isotropic …
psheehan Apr 15, 2026
e672d07
Merge branch 'add_abundance_variation' into add_non_isotropic_dust
psheehan May 8, 2026
9c8d4bc
Add tests for learning all of the different types of Dust, with the v…
psheehan May 12, 2026
d3e7af4
Merge branch 'add_abundance_variation' into add_non_isotropic_dust
psheehan May 16, 2026
aa105a1
Merge branch 'add_abundance_variation' into add_non_isotropic_dust
psheehan May 27, 2026
3e1417f
Merge branch 'add_abundance_variation' into add_non_isotropic_dust
psheehan May 27, 2026
7c67128
Update docs for non-isotropic dust.
psheehan May 27, 2026
5f1007a
Merge branch 'add_abundance_variation' into add_non_isotropic_dust
psheehan May 29, 2026
afe9513
Merge branch 'add_abundance_variation' into add_non_isotropic_dust
psheehan May 29, 2026
49759ec
Merge branch 'add_abundance_variation' into add_non_isotropic_dust
psheehan May 29, 2026
93f802a
With GeneralDust sampling from the scattering phase function, this ad…
psheehan May 29, 2026
2cd3dac
Merge branch 'add_abundance_variation' into add_non_isotropic_dust
psheehan May 29, 2026
936b20a
Merge branch 'main' into add_non_isotropic_dust
psheehan Jun 7, 2026
eddcff0
Enable fiducial_values for HenyeyGreenstein and GeneralDust; Enable t…
psheehan Jun 12, 2026
8039fb7
Expand dust-demo-diana notebook to show how to turn iso into hg and g…
psheehan Jun 12, 2026
389c4da
Update the E2E tests to include HenyeyGreenstein dust.
psheehan Jun 12, 2026
f6d9416
Expose accumulate_grad_batches kwarg to simulate larger batches even …
psheehan Jun 26, 2026
03aecc4
Merge branch 'add_non_isotropic_dust' of https://github.com/psheehan/…
psheehan Jun 26, 2026
3b35dbc
Add DIANA GeneralDust and some fixes so that it is loaded properly.
psheehan Jun 27, 2026
018f199
Add a plot_random_direction_model function to GeneralDust. Also, to e…
psheehan Jul 10, 2026
a73d9d7
Merge branch 'main' into add_non_isotropic_dust
psheehan Jul 11, 2026
ac89d1f
Use unified ml samples gathering functionality for HenyeyGreensteinDu…
psheehan Jul 12, 2026
d523486
Fix merge error.
psheehan Jul 12, 2026
09891a8
Import Dust into test_dust_creation for the cache tests.
psheehan Jul 12, 2026
b4bb1ce
Turns out .nonzero() returns an (nphotons, 1) shaped array, so flatte…
psheehan Jul 13, 2026
03894b9
Merge ml_scattering_phase_function theta calculation into _get_ml_opa…
psheehan Jul 13, 2026
7026de0
Update a test to use GeneralDust to expand test coverage.
psheehan Jul 13, 2026
107e6a2
Rename GeneralDust to GeneralScatteringDust.
psheehan Jul 13, 2026
3ef2719
Add coverage of plot_random_direction_model to the tests.
psheehan Jul 13, 2026
f9b8306
Update docs to reflect name change from GeneralDust -> GeneralScatter…
psheehan Jul 13, 2026
e37499e
With scattering phase functions needing a reference point, scattering…
psheehan Jul 14, 2026
61d8790
Those orientation values should have units...
psheehan Jul 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/dust.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ dust

.. autofunction:: suggest_opacity_sampling

.. autoclass:: IsotropicDust
:show-inheritance:

.. autoclass:: HenyeyGreensteinDust
:show-inheritance:

.. autoclass:: GeneralScatteringDust
:show-inheritance:

.. autoclass:: Dust
:show-inheritance:

Expand Down
109 changes: 79 additions & 30 deletions docs/dustcreation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,28 @@ Creating a dust model
=====================

Before running a radiative transfer simulation, you need to create a dust model that defines the optical properties of
the dust grains in your simulation. Pinball-rt provides a :class:`~pinballrt.dust.Dust` class that allows you to create and manipulate dust
models. To set up a dust model, the absorption and scattering opacities as a function of wavelength and grain size
distribution parameters (maximum dust grain size, size distribution power-law index, and sub-species relative abundances) are needed. At present, these
must be obtained from external sources and provided to pinball-rt. Here we'll use simple power-law prescription, but in
practice you would typically use opacities derived from laboratory measurements or Mie theory calculations. Note that
the opacities should include astropy units to ensure that there is no ambiguity.
the dust grains in your simulation. Pinball-rt provides a :class:`~pinballrt.dust.Dust` class that allows you to create
and manipulate dust models. In practice, there are three more specific dust models available:
:class:`~pinballrt.dust.IsotropicDust`, :class:`~pinballrt.dust.HenyeyGreensteinDust`, and
:class:`~pinballrt.dust.GeneralScatteringDust` that inherit from :class:`~pinballrt.dust.Dust` and enable more specific control
over dust scattering properties. To set up a dust model, the absorption and scattering opacities as a function of wavelength
and grain size distribution parameters (maximum dust grain size, size distribution power-law index, and sub-species
relative abundances) are needed. At present, these must be obtained from external sources and provided to pinball-rt.
Here we'll use simple power-law prescription, but in practice you would typically use opacities derived from laboratory
measurements or Mie theory calculations. Note that the opacities should include astropy units to ensure that there is no
ambiguity.

To enable maximum flexibility, opacities do not need to be provided on a regular grid. Instead, the opacities should be
provided at some number (nsamples) of points in the N-dimensional parameter space defined by relevant inputs for determining
the opacity (maximum grain size, size distribution power-law index, sub-species abundances), and for each sample they
should be provided at a specified set of wavelengths. The Dust class will then use machine learning to learn the opacities
at any point in the parameter space during the simulation. A helper-function, :func:`~pinballrt.dust.suggest_opacity_sampling` is provided to
help provide efficient sampling of the parameter space, but the user is free to provide any set of samples they choose.
should be provided at a specified set of wavelengths. The :class:`~pinballrt.dust.Dust` class will then use machine learning
to learn the opacities at any point in the parameter space during the simulation. A helper-function,
:func:`~pinballrt.dust.suggest_opacity_sampling` is provided to help provide efficient sampling of the parameter space,
but the user is free to provide any set of samples they choose.

.. code-block:: python

from pinballrt.dust import Dust, suggest_opacity_sampling
from pinballrt.dust import IsotropicDust, suggest_opacity_sampling
import numpy as np
import astropy.units as u

Expand All @@ -42,27 +47,25 @@ help provide efficient sampling of the parameter space, but the user is free to
kappa_abs = 1.0 * (wavelengths.to(u.micron)/100.0)**power_law_index * u.cm**2 / u.g
kappa_scat = 0.5 * (wavelengths.to(u.micron)/100.0)**power_law_index * u.cm**2 / u.g

# Create the Dust object.
dust = Dust(lam=wavelengths[0,:],
amax=amax[:,0],
p=p[:,0],
kabs=kappa_abs,
ksca=kappa_scat)
# Create the IsotropicDust object.
dust = IsotropicDust(lam=wavelengths[0,:],
amax=amax[:,0],
p=p[:,0],
kabs=kappa_abs,
ksca=kappa_scat)

This creates a Dust object with the specified opacities, however a few additional steps are needed before the dust model can be used in a
radiative transfer simulation. The Dust object uses a machine learning model to produce opacity values during the simulation, as well as to
This creates an :class:`~pinballrt.dust.IsotropicDust` object with the specified opacities, however a few additional steps are needed before the dust model can be used in a
radiative transfer simulation. The :class:`~pinballrt.dust.IsotropicDust` object uses a machine learning model to produce opacity values during the simulation, as well as to
randomnly sample photon frequencies emitted by dust grains during the simulation, but these models need to be trained first. To set up the
training, we use the :meth:`~pinballrt.dust.Dust.learn` method. For example, to set up the model to learn the absorption opacity:

.. code-block:: python

# Set up the training parameters.
dust.learn(
model="kabs",
test_fraction=0.1,
val_fraction=0.1,
hidden_units=(48, 48, 48),
)
dust.learn(model="kabs",
test_fraction=0.1,
val_fraction=0.1,
hidden_units=(48, 48, 48))

This example sets up a simple neural network model with three hidden layers of 48 units each to learn the dust absorption opacity. The training
will use the kabs samples provided above, which had 100 samples across dust properties at 100 wavelengths for 10,000 total samples, with 10% of
Expand All @@ -86,13 +89,13 @@ a simulation. In short:
.. code-block:: python

for model in ["ksca", "pmo", "random_nu"]:
if model in ["kabs", "ksca"]:
d.learn(model=model, hidden_units=(16,)*6, overwrite=True)
else:
d.learn(model=model, hidden_units=(48,)*3, overwrite=True)
if model in ["kabs", "ksca"]:
d.learn(model=model, hidden_units=(16,)*6, overwrite=True)
else:
d.learn(model=model, hidden_units=(48,)*3, overwrite=True)

d.fit(epochs=300, batch_size=1000)
d.test_model(plot=True)
d.fit(epochs=300, batch_size=1000)
d.test_model(plot=True)

This will further create models to produce the scattering opacity, planck mean opacity, and random frequencies sampled from the dust emission spectrum.
Having to train the dust model before every simulation would be inefficient, so once the model is trained it can be saved to a file using the :meth:`~pinballrt.dust.Dust.save` method,
Expand All @@ -117,6 +120,52 @@ pinball-rt provides a pre-trained dust model that can be used directly without n
# Load the pre-trained dust model.
dust = load("yso.dst")

Creating a Henyey-Greenstein dust model follows the same process as above, but with the addition of needing to train a model to produce the scattering asymmetry parameter (g) as a function of wavelength and dust properties:

.. code-block:: python

from pinballrt.dust import HenyeyGreensteinDust

g = np.tanh(p - np.log10(wavelengths.to(u.micron).value))

# Create the Henyey-Greenstein dust model.
dust = HenyeyGreensteinDust(lam=wavelengths[0,:],
amax=amax[:,0],
p=p[:,0],
kabs=kappa_abs,
ksca=kappa_scat,
g=g)

d.learn(model="g", hidden_units=(16,)*6, overwrite=True)

d.fit(epochs=300, batch_size=1000)
d.test_model(plot=True)

Similarly, the most general dust model, :class:`~pinballrt.dust.GeneralScatteringDust`, follows the same process but with the addition of needing to train a model to produce the scattering phase function as a function of wavelength, scattering angle and dust properties, and additionally to randomly sample scattering angles during the simulation:

.. code-block:: python

from pinballrt.dust import GeneralScatteringDust

g = np.repeat(np.expand_dims(np.tanh(p - np.log10(wavelengths.to(u.micron).value)), axis=-1), 5, axis=-1)
theta = np.tile(np.expand_dims(np.linspace(0, 180., 5), axis=(0,1)), (10 if len(dims) > 0 else 1, 10, 1)) * u.deg
scattering_phase_function = (1 - g**2) / (4 * np.pi * (1 + g**2 - 2*g*np.cos(theta.to(u.rad).value))**(3/2))

# Create the General dust model.
dust = GeneralScatteringDust(lam=wavelengths[0,:],
amax=amax[:,0],
p=p[:,0],
kabs=kappa_abs,
ksca=kappa_scat,
scattering_phase_function=scattering_phase_function,
theta=theta[0,0,:])

for model in ["scattering_phase_function", "random_direction"]:
d.learn(model=model, hidden_units=(16,)*6, overwrite=True)

d.fit(epochs=300, batch_size=1000)
d.test_model(plot=True)

Learning to step through high optical depth regions
---------------------------------------------------

Expand Down
1,639 changes: 230 additions & 1,409 deletions examples/dust-demo-diana.ipynb

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions examples/dust-demo.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"execution_count": null,
"id": "461d2258",
"metadata": {},
"outputs": [
Expand All @@ -16,7 +16,7 @@
}
],
"source": [
"from pinballrt.dust import Dust, load, suggest_opacity_sampling\n",
"from pinballrt.dust import IsotropicDust, load, suggest_opacity_sampling\n",
"import astropy.units as u\n",
"import numpy as np"
]
Expand Down Expand Up @@ -61,12 +61,12 @@
"kappa_scat = 0.5 * (wavelengths.to(u.micron).value/100.0)**power_law_index * u.cm**2 / u.g + silicate_feature + water_feature\n",
"\n",
"# Create the Dust object.\n",
"d = Dust(lam=wavelengths[0,:], \n",
" amax=amax[:,0], \n",
" p=p[:,0], \n",
" abundances=(silicate_fraction[:,0], water_fraction[:,0]),\n",
" kabs=kappa_abs, \n",
" ksca=kappa_scat)"
"d = IsotropicDust(lam=wavelengths[0,:], \n",
" amax=amax[:,0], \n",
" p=p[:,0], \n",
" abundances=(silicate_fraction[:,0], water_fraction[:,0]),\n",
" kabs=kappa_abs, \n",
" ksca=kappa_scat)"
]
},
{
Expand Down
3 changes: 3 additions & 0 deletions pinballrt/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ def set_orientation(self, incl, pa, dpc):
self.i = np.array([self.r*np.sin(self.incl)*np.cos(phi), \
self.r*np.sin(self.incl)*np.sin(phi), \
self.r*np.cos(self.incl)])

with wp.ScopedDevice(self.grid.device):
self.i_wp = wp.vec3(self.i)

self.ex = np.array([-np.sin(phi), np.cos(phi), 0.0])
self.ey = np.array([-np.cos(self.incl)*np.cos(phi), \
Expand Down
Loading
Loading