Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Weighted Packet Sampler #2718

Merged
merged 19 commits into from
Jul 27, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
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
37 changes: 30 additions & 7 deletions tardis/transport/montecarlo/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,39 @@
from copy import deepcopy

import pytest
import numpy as np
import pytest
from numba import njit
from tardis.opacities.opacity_state import opacity_state_initialize
from tardis.transport.montecarlo.packet_collections import (
VPacketCollection,
)

from tardis.opacities.opacity_state import opacity_state_initialize
from tardis.simulation import Simulation
from tardis.transport.montecarlo import RPacket
from tardis.transport.montecarlo.estimators.radfield_mc_estimators import (
RadiationFieldMCEstimators,
)


from tardis.transport.montecarlo.numba_interface import (
opacity_state_initialize,
)
from tardis.transport.montecarlo.packet_collections import (
VPacketCollection,
)
from tardis.transport.montecarlo.weighted_packet_source import (
BlackBodyWeightedSource,
)


@pytest.fixture(scope="function")
def montecarlo_main_loop_config(
config_montecarlo_1e5_verysimple,
):
# Setup model config from verysimple

config_montecarlo_1e5_verysimple.montecarlo.last_no_of_packets = 1e5
config_montecarlo_1e5_verysimple.montecarlo.no_of_virtual_packets = 0
config_montecarlo_1e5_verysimple.montecarlo.iterations = 1
config_montecarlo_1e5_verysimple.plasma.line_interaction_type = "macroatom"

del config_montecarlo_1e5_verysimple["config_dirname"]
return config_montecarlo_1e5_verysimple


@pytest.fixture(scope="package")
Expand All @@ -28,6 +44,13 @@ def nb_simulation_verysimple(config_verysimple, atomic_dataset):
return sim


@pytest.fixture(scope="package")
def simple_weighted_packet_source():
packet_source = BlackBodyWeightedSource(base_seed=1963)

return packet_source


@pytest.fixture(scope="package")
def verysimple_opacity_state(nb_simulation_verysimple):
return opacity_state_initialize(
Expand Down
15 changes: 0 additions & 15 deletions tardis/transport/montecarlo/tests/test_montecarlo_main_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,6 @@ def test_montecarlo_radial1d():
assert False


@pytest.fixture(scope="function")
def montecarlo_main_loop_config(
config_montecarlo_1e5_verysimple,
):
# Setup model config from verysimple

config_montecarlo_1e5_verysimple.montecarlo.last_no_of_packets = 1e5
config_montecarlo_1e5_verysimple.montecarlo.no_of_virtual_packets = 0
config_montecarlo_1e5_verysimple.montecarlo.iterations = 1
config_montecarlo_1e5_verysimple.plasma.line_interaction_type = "macroatom"

del config_montecarlo_1e5_verysimple["config_dirname"]
return config_montecarlo_1e5_verysimple


def test_montecarlo_main_loop(
montecarlo_main_loop_config,
regression_data,
Expand Down
61 changes: 61 additions & 0 deletions tardis/transport/montecarlo/tests/test_weighted_packet_source.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import os

from astropy import units as u
import numpy as np
import pandas as pd
import pytest
from numpy.testing import assert_allclose

from tardis.transport.montecarlo.weighted_packet_source import (
BlackBodyWeightedSource,
)
from tardis.tests.fixtures.regression_data import RegressionData


class TestBlackBodyWeightedSource:
@pytest.fixture(scope="class")
def blackbodyweightedource(self, request):
"""
Create blackbodyweightedsource instance.

Yields
-------
tardis.transport.montecarlo.packet_source.blackbodyweightedsource
"""
cls = type(self)
bb = BlackBodyWeightedSource(

Check warning on line 26 in tardis/transport/montecarlo/tests/test_weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/tests/test_weighted_packet_source.py#L25-L26

Added lines #L25 - L26 were not covered by tests
radius=123,
temperature=10000 * u.K,
base_seed=1963,
legacy_second_seed=2508,
legacy_mode_enabled=True,
)
yield bb

Check warning on line 33 in tardis/transport/montecarlo/tests/test_weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/tests/test_weighted_packet_source.py#L33

Added line #L33 was not covered by tests

def test_bb_nus(self, regression_data, blackbodyweightedsource):
actual_nus = blackbodyweightedsource.create_packet_nus(100).value
expected_nus = regression_data.sync_ndarray(actual_nus)
assert_allclose(actual_nus, expected_nus)

Check warning on line 38 in tardis/transport/montecarlo/tests/test_weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/tests/test_weighted_packet_source.py#L36-L38

Added lines #L36 - L38 were not covered by tests

def test_bb_mus(self, regression_data, blackbodyweightedsource):
actual_mus = blackbodyweightedsource.create_packet_mus(100)
expected_mus = regression_data.sync_ndarray(actual_mus)
assert_allclose(actual_mus, expected_mus)

Check warning on line 43 in tardis/transport/montecarlo/tests/test_weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/tests/test_weighted_packet_source.py#L41-L43

Added lines #L41 - L43 were not covered by tests

def test_bb_energies(self, regression_data, blackbodyweightedsource):
actual_unif_energies = blackbodyweightedsource.create_packet_energies(

Check warning on line 46 in tardis/transport/montecarlo/tests/test_weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/tests/test_weighted_packet_source.py#L46

Added line #L46 was not covered by tests
100
).value
expected_unif_energies = regression_data.sync_ndarray(

Check warning on line 49 in tardis/transport/montecarlo/tests/test_weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/tests/test_weighted_packet_source.py#L49

Added line #L49 was not covered by tests
actual_unif_energies
)
assert_allclose(actual_unif_energies, expected_unif_energies)

Check warning on line 52 in tardis/transport/montecarlo/tests/test_weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/tests/test_weighted_packet_source.py#L52

Added line #L52 was not covered by tests

def test_bb_attributes(self, regression_data, blackbodyweightedsource):
actual_bb = blackbodyweightedsource
expected_bb = regression_data.sync_hdf_store(actual_bb)[

Check warning on line 56 in tardis/transport/montecarlo/tests/test_weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/tests/test_weighted_packet_source.py#L55-L56

Added lines #L55 - L56 were not covered by tests
"/black_body_weighted_source/scalars"
]
assert_allclose(expected_bb.base_seed, actual_bb.base_seed)
assert_allclose(expected_bb.temperature, actual_bb.temperature.value)
assert_allclose(expected_bb.radius, actual_bb.radius)

Check warning on line 61 in tardis/transport/montecarlo/tests/test_weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/tests/test_weighted_packet_source.py#L59-L61

Added lines #L59 - L61 were not covered by tests
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from copy import deepcopy

import numpy.testing as npt
import pandas as pd

from tardis.simulation import Simulation


def test_montecarlo_main_loop_weighted(
montecarlo_main_loop_config,
regression_data,
atomic_dataset,
simple_weighted_packet_source,
):
atomic_dataset = deepcopy(atomic_dataset)
montecarlo_main_loop_simulation_weighted = Simulation.from_config(

Check warning on line 16 in tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py#L15-L16

Added lines #L15 - L16 were not covered by tests
montecarlo_main_loop_config,
atom_data=atomic_dataset,
virtual_packet_logging=False,
legacy_mode_enabled=True,
)
montecarlo_main_loop_simulation_weighted.packet_source = (

Check warning on line 22 in tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py#L22

Added line #L22 was not covered by tests
simple_weighted_packet_source
)
montecarlo_main_loop_simulation_weighted.run_convergence()
montecarlo_main_loop_simulation_weighted.run_final()

Check warning on line 26 in tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py#L25-L26

Added lines #L25 - L26 were not covered by tests

# Get the montecarlo simple regression data
regression_data_dir = (

Check warning on line 29 in tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py#L29

Added line #L29 was not covered by tests
regression_data.absolute_regression_data_dir.absolute().parents[0]
/ "test_montecarlo_main_loop/test_montecarlo_main_loop.h5"
)
expected_hdf_store = pd.HDFStore(regression_data_dir, mode="r")

Check warning on line 33 in tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py#L33

Added line #L33 was not covered by tests

# Load compare data from refdata

expected_nu = expected_hdf_store[

Check warning on line 37 in tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py#L37

Added line #L37 was not covered by tests
"/simulation/transport/transport_state/output_nu"
]
expected_energy = expected_hdf_store[

Check warning on line 40 in tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py#L40

Added line #L40 was not covered by tests
"/simulation/transport/transport_state/output_energy"
]
expected_nu_bar_estimator = expected_hdf_store[

Check warning on line 43 in tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py#L43

Added line #L43 was not covered by tests
"/simulation/transport/transport_state/nu_bar_estimator"
]
expected_j_estimator = expected_hdf_store[

Check warning on line 46 in tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py#L46

Added line #L46 was not covered by tests
"/simulation/transport/transport_state/j_estimator"
]
expected_hdf_store.close()
transport_state = (

Check warning on line 50 in tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py#L49-L50

Added lines #L49 - L50 were not covered by tests
montecarlo_main_loop_simulation_weighted.transport.transport_state
)
actual_energy = transport_state.packet_collection.output_energies
actual_nu = transport_state.packet_collection.output_nus
actual_nu_bar_estimator = (

Check warning on line 55 in tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py#L53-L55

Added lines #L53 - L55 were not covered by tests
transport_state.radfield_mc_estimators.nu_bar_estimator
)
actual_j_estimator = transport_state.radfield_mc_estimators.j_estimator

Check warning on line 58 in tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py#L58

Added line #L58 was not covered by tests

# Compare
npt.assert_allclose(

Check warning on line 61 in tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py#L61

Added line #L61 was not covered by tests
actual_nu_bar_estimator, expected_nu_bar_estimator, rtol=1e-2
)
npt.assert_allclose(actual_j_estimator, expected_j_estimator, rtol=1e-2)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you using rtol upto two decimal places?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it is checking consistency with the default sampler rather than with itself. It is to make sure we're getting approximately the same solution

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am happy to approve if the tests passes.

npt.assert_allclose(actual_energy, expected_energy, rtol=1e-2)
npt.assert_allclose(actual_nu, expected_nu, rtol=1e-2)

Check warning on line 66 in tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/tests/test_weighted_packet_source_integration.py#L64-L66

Added lines #L64 - L66 were not covered by tests
177 changes: 177 additions & 0 deletions tardis/transport/montecarlo/weighted_packet_source.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import numexpr as ne
import numpy as np
from astropy import constants as const
from astropy import units as u

from tardis.transport.montecarlo.packet_source import (
BasePacketSource,
HDFWriterMixin,
)
from tardis.util.base import intensity_black_body


class BlackBodyWeightedSource(BasePacketSource, HDFWriterMixin):
"""
Simple packet source that generates Blackbody packets for the Montecarlo
part.

Parameters
----------
radius : astropy.units.Quantity
Initial packet radius
temperature : astropy.units.Quantity
Absolute Temperature.
base_seed : int
Base Seed for random number generator
legacy_secondary_seed : int
Secondary seed for global numpy rng (Deprecated: Legacy reasons only)
"""

hdf_properties = ["radius", "temperature", "base_seed"]
hdf_name = "black_body_weighted_source"

@classmethod
def from_simulation_state(cls, simulation_state, *args, **kwargs):
return cls(

Check warning on line 35 in tardis/transport/montecarlo/weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/weighted_packet_source.py#L35

Added line #L35 was not covered by tests
simulation_state.r_inner[0],
simulation_state.t_inner,
*args,
**kwargs,
)

def __init__(self, radius=None, temperature=None, **kwargs):
self.radius = radius
self.temperature = temperature
super().__init__(**kwargs)

Check warning on line 45 in tardis/transport/montecarlo/weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/weighted_packet_source.py#L43-L45

Added lines #L43 - L45 were not covered by tests

def create_packets(self, no_of_packets, *args, **kwargs):
if self.radius is None or self.temperature is None:
raise ValueError("Black body Radius or Temperature isn't set")
return super().create_packets(no_of_packets, *args, **kwargs)

Check warning on line 50 in tardis/transport/montecarlo/weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/weighted_packet_source.py#L48-L50

Added lines #L48 - L50 were not covered by tests

def create_packet_radii(self, no_of_packets):
"""
Create packet radii

Parameters
----------
no_of_packets : int
number of packets to be created

Returns
-------
Radii for packets
numpy.ndarray
"""
return np.ones(no_of_packets) * self.radius.cgs

Check warning on line 66 in tardis/transport/montecarlo/weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/weighted_packet_source.py#L66

Added line #L66 was not covered by tests

def create_packet_nus(self, no_of_packets, l_samples=1000):
Rodot- marked this conversation as resolved.
Show resolved Hide resolved
"""
Create packet :math:`\\nu` distributed using the algorithm described in
Bjorkman & Wood 2001 (page 4) which references
Carter & Cashwell 1975:
First, generate a uniform random number, :math:`\\xi_0 \\in [0, 1]` and
determine the minimum value of
:math:`l, l_{\\rm min}`, that satisfies the condition
.. math::
\\sum_{i=1}^{l} i^{-4} \\ge {{\\pi^4}\\over{90}} m_0 \\;.
Next obtain four additional uniform random numbers (in the range 0
to 1) :math:`\\xi_1, \\xi_2, \\xi_3, {\\rm and } \\xi_4`.
Finally, the packet frequency is given by
.. math::
x = -\\ln{(\\xi_1\\xi_2\\xi_3\\xi_4)}/l_{\\rm min}\\;.
where :math:`x=h\\nu/kT`

Parameters
----------
no_of_packets : int
l_samples : int
number of l_samples needed in the algorithm

Returns
-------
array of frequencies
numpy.ndarray
"""
l_array = np.cumsum(np.arange(1, l_samples, dtype=np.float64) ** -4)
l_coef = np.pi**4 / 90.0

Check warning on line 97 in tardis/transport/montecarlo/weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/weighted_packet_source.py#L96-L97

Added lines #L96 - L97 were not covered by tests

# For testing purposes
if self.legacy_mode_enabled:
xis = np.random.random((5, no_of_packets))

Check warning on line 101 in tardis/transport/montecarlo/weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/weighted_packet_source.py#L100-L101

Added lines #L100 - L101 were not covered by tests
else:
xis = self.rng.random((5, no_of_packets))

Check warning on line 103 in tardis/transport/montecarlo/weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/weighted_packet_source.py#L103

Added line #L103 was not covered by tests

l = l_array.searchsorted(xis[0] * l_coef) + 1.0
xis_prod = np.prod(xis[1:], 0)
x = ne.evaluate("-log(xis_prod)/l")

Check warning on line 107 in tardis/transport/montecarlo/weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/weighted_packet_source.py#L105-L107

Added lines #L105 - L107 were not covered by tests

nus = (x * (const.k_B * self.temperature) / const.h).cgs

Check warning on line 109 in tardis/transport/montecarlo/weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/weighted_packet_source.py#L109

Added line #L109 was not covered by tests

nu_min = nus.min()
nu_max = nus.max()

Check warning on line 112 in tardis/transport/montecarlo/weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/weighted_packet_source.py#L111-L112

Added lines #L111 - L112 were not covered by tests

self.nus = (

Check warning on line 114 in tardis/transport/montecarlo/weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/weighted_packet_source.py#L114

Added line #L114 was not covered by tests
np.random.uniform(nu_min.cgs.value, nu_max.cgs.value, no_of_packets)
* nus.unit
)

return self.nus

Check warning on line 119 in tardis/transport/montecarlo/weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/weighted_packet_source.py#L119

Added line #L119 was not covered by tests

def create_packet_mus(self, no_of_packets):
"""
Create zero-limb-darkening packet :math:`\\mu` distributed
according to :math:`\\mu=\\sqrt{z}, z \\isin [0, 1]`

Parameters
----------
no_of_packets : int
number of packets to be created

Returns
-------
Directions for packets
numpy.ndarray
"""
# For testing purposes
if self.legacy_mode_enabled:
return np.sqrt(np.random.random(no_of_packets))

Check warning on line 138 in tardis/transport/montecarlo/weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/weighted_packet_source.py#L137-L138

Added lines #L137 - L138 were not covered by tests
else:
return np.sqrt(self.rng.random(no_of_packets))

Check warning on line 140 in tardis/transport/montecarlo/weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/weighted_packet_source.py#L140

Added line #L140 was not covered by tests

def create_packet_energies(self, no_of_packets):
"""
Rodot- marked this conversation as resolved.
Show resolved Hide resolved
Uniformly distribute energy in arbitrary units where the ensemble of
packets has energy of 1.

Parameters
----------
no_of_packets : int
number of packets

Returns
-------
energies for packets
numpy.ndarray
"""
try:
self.nus
except AttributeError:
self.nus = self.create_packet_nus(no_of_packets)

Check warning on line 160 in tardis/transport/montecarlo/weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/weighted_packet_source.py#L157-L160

Added lines #L157 - L160 were not covered by tests

intensity = intensity_black_body(self.nus.cgs.value, self.temperature)
return intensity / intensity.sum() * u.erg

Check warning on line 163 in tardis/transport/montecarlo/weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/weighted_packet_source.py#L162-L163

Added lines #L162 - L163 were not covered by tests

def set_temperature_from_luminosity(self, luminosity: u.Quantity):
"""
Rodot- marked this conversation as resolved.
Show resolved Hide resolved
Set blackbody packet source temperature from luminosity

Parameters
----------
luminosity : u.Quantity

"""
self.temperature = (

Check warning on line 174 in tardis/transport/montecarlo/weighted_packet_source.py

View check run for this annotation

Codecov / codecov/patch

tardis/transport/montecarlo/weighted_packet_source.py#L174

Added line #L174 was not covered by tests
(luminosity / (4 * np.pi * self.radius**2 * const.sigma_sb))
** 0.25
).to("K")
Loading