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

Add individual intensities #212

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
172f99f
refactor distances to work with new rays
jvshields Jul 24, 2024
f7a7c14
add spherical model parsing
jvshields Jul 25, 2024
a10f42b
hook up spherical model reader
jvshields Jul 25, 2024
578fbd7
add explanatory comments
jvshields Jul 25, 2024
a5e4120
checkpoint
jvshields Jul 25, 2024
9372ea0
blackify code
jvshields Jul 25, 2024
b975ff8
apply appropriate depth masks for rays
jvshields Jul 25, 2024
61250ec
checkpoint
jvshields Jul 30, 2024
0af69a6
checkpoint for some working spherical geometry
jvshields Jul 30, 2024
d3f6182
checkpoint for photosphere correction
jvshields Aug 13, 2024
5609849
add single threaded implementation
jvshields Aug 14, 2024
2558f0a
readability cleanup
jvshields Aug 14, 2024
73f629e
add structure for intensities. Also move thetas to sampled from gauss…
jvshields Sep 6, 2024
59de710
move thetas to radiation field
jvshields Sep 25, 2024
7efc0fc
remove single threaded case
jvshields Sep 25, 2024
76e57a9
fix tests
jvshields Sep 25, 2024
e05b51e
apply black
jvshields Sep 25, 2024
1e4813b
simplify distances
jvshields Sep 26, 2024
dd77b2e
improve documentation
jvshields Sep 26, 2024
e824584
more documentation cleanup
jvshields Sep 26, 2024
d90a62f
bugfixing
jvshields Sep 26, 2024
bf6fd0e
move back to old weights and theta sampling
jvshields Sep 26, 2024
cee4e75
fix tests for old thetas
jvshields Sep 26, 2024
aa872dc
fix inward rays
jvshields Oct 1, 2024
ec52aa0
apply black
jvshields Oct 1, 2024
4902a88
more informative error message when file fails to read
jvshields Oct 3, 2024
e1aa678
make marcs reader read whether the model is plane parallel or spherical
jvshields Oct 4, 2024
e894d63
fix model reading error message in line with spherical geometry being…
jvshields Oct 4, 2024
b13a366
cleanup unused code
jvshields Oct 10, 2024
1cd79ae
fix test
jvshields Oct 10, 2024
2d22a75
Merge remote-tracking branch 'upstream/main' into add_individual_inte…
jvshields Oct 14, 2024
6e9572f
Merge remote-tracking branch 'upstream/main' into add_individual_inte…
jvshields Oct 14, 2024
6e772c7
resolve merge conflicts
jvshields Oct 23, 2024
71a7c27
small config_schema changes
jvshields Nov 4, 2024
eb49c03
remove individual intensity preservation w/ no radiation_field return
jvshields Nov 4, 2024
0c542dc
black
jvshields Nov 4, 2024
28d0881
move marcs regex patterns to new file
jvshields Nov 4, 2024
5fbf7c3
Merge branch 'main' into add_individual_intensities
jvshields Dec 2, 2024
8d8984c
Merge remote-tracking branch 'upstream/main' into add_individual_inte…
jvshields Dec 18, 2024
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
2 changes: 0 additions & 2 deletions benchmarks/run_stardis.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ def time_raytrace(self):
raytrace(
self.stellar_model,
self.stellar_radiation_field,
no_of_thetas=self.config.no_of_thetas,
)

def time_calc_alpha_line_at_nu(self):
Expand Down Expand Up @@ -209,7 +208,6 @@ def time_raytrace(self):
raytrace(
self.stellar_model,
self.stellar_radiation_field,
no_of_thetas=self.config.no_of_thetas,
)

def time_calc_alpha_line_at_nu(self):
Expand Down
18 changes: 12 additions & 6 deletions stardis/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,10 @@ def example_stellar_radiation_field(
example_stellar_model, example_config, example_tracing_nus, example_stellar_plasma
):
stellar_radiation_field = RadiationField(
example_tracing_nus, blackbody_flux_at_nu, example_stellar_model
example_tracing_nus,
blackbody_flux_at_nu,
example_stellar_model,
example_config.no_of_thetas,
)

calc_alphas(
Expand All @@ -203,7 +206,6 @@ def example_stellar_radiation_field(
raytrace(
example_stellar_model,
stellar_radiation_field,
no_of_thetas=example_config.no_of_thetas,
)
return stellar_radiation_field

Expand All @@ -216,7 +218,10 @@ def example_stellar_radiation_field_broadening(
example_stellar_plasma_broadening,
):
stellar_radiation_field = RadiationField(
example_tracing_nus, blackbody_flux_at_nu, example_stellar_model
example_tracing_nus,
blackbody_flux_at_nu,
example_stellar_model,
example_config_broadening.no_of_thetas,
)

calc_alphas(
Expand All @@ -229,7 +234,6 @@ def example_stellar_radiation_field_broadening(
raytrace(
example_stellar_model,
stellar_radiation_field,
no_of_thetas=example_config_broadening.no_of_thetas,
)
return stellar_radiation_field

Expand All @@ -242,7 +246,10 @@ def example_stellar_radiation_field_parallel(
example_stellar_plasma_broadening,
):
stellar_radiation_field = RadiationField(
example_tracing_nus, blackbody_flux_at_nu, example_stellar_model
example_tracing_nus,
blackbody_flux_at_nu,
example_stellar_model,
example_config_parallel.no_of_thetas,
)

calc_alphas(
Expand All @@ -256,7 +263,6 @@ def example_stellar_radiation_field_parallel(
raytrace(
example_stellar_model,
stellar_radiation_field,
no_of_thetas=example_config_parallel.no_of_thetas,
n_threads=example_config_parallel.n_threads,
)
return stellar_radiation_field
Expand Down
3 changes: 2 additions & 1 deletion stardis/io/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ def parse_config_to_model(config_fname, add_config_dict):
logger.info("Reading model")
if config.model.type == "marcs":
raw_marcs_model = read_marcs_model(
Path(config.model.fname), gzipped=config.model.gzipped
Path(config.model.fname),
gzipped=config.model.gzipped,
)
stellar_model = raw_marcs_model.to_stellar_model(
adata, final_atomic_number=config.model.final_atomic_number
Expand Down
112 changes: 99 additions & 13 deletions stardis/io/model/marcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from stardis.model.base import StellarModel
from tardis.model.matter.composition import Composition

logger = logging.getLogger(__name__)


@dataclass
class MARCSModel(object):
Expand All @@ -22,6 +24,7 @@ class MARCSModel(object):

metadata: dict
data: pd.DataFrame
spherical: bool

def to_geometry(self):
"""
Expand All @@ -31,10 +34,15 @@ def to_geometry(self):
-------
stardis.model.geometry.radial1d.Radial1DGeometry
"""

reference_r = None
r = (
-self.data.depth.values[::-1] * u.cm
) # Flip data to move from innermost stellar point to surface
return Radial1DGeometry(r)
if self.spherical:
r += self.metadata["radius"]
reference_r = self.metadata["radius"]
return Radial1DGeometry(r, reference_r)

def to_composition(self, atom_data, final_atomic_number):
"""
Expand Down Expand Up @@ -147,6 +155,7 @@ def to_stellar_model(self, atom_data, final_atomic_number=118):
temperatures,
marcs_geometry,
marcs_composition,
spherical=self.spherical,
microturbulence=self.metadata["microturbulence"],
)

Expand All @@ -163,14 +172,16 @@ def read_marcs_metadata(fpath, gzipped=True):
Path to model file
gzipped : Bool
Whether or not the file is gzipped
spherical : Bool
Whether or not the model is spherical

Returns
-------
dict : dictionary
metadata parameters of file
"""

METADATA_RE_STR = [
METADATA_PLANE_PARALLEL_RE_STR = [
(r"(.+)\n", "fname"),
(
r" (\d+\.)\s+Teff \[(.+)\]\.\s+Last iteration; yyyymmdd=\d+",
Expand Down Expand Up @@ -214,12 +225,61 @@ def read_marcs_metadata(fpath, gzipped=True):
"12C/13C",
),
]
BYTES_THROUGH_METADATA = 550

# Compile each of the regex pattern strings then open the file and match each of the patterns by line.
# Then add each of the matched patterns as a key:value pair to the metadata dict.
metadata_re = [re.compile(re_str[0]) for re_str in METADATA_RE_STR]
metadata = {}
METADATA_SPHERICAL_RE_STR = [
Copy link
Contributor

Choose a reason for hiding this comment

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

This is so long it would probably be best in its own file and imported

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have done this.

(r"(.+)\n", "fname"),
(
r" (\d+\.)\s+Teff \[(.+)\]\.\s+Last iteration; yyyymmdd=\d+",
"teff",
"teff_units",
),
(r" (\d+\.\d+E\+\d+) Flux \[(.+)\]", "flux", "flux_units"),
(
r" (\d+.\d+E\+\d+) Surface gravity \[(.+)\]",
"surface_grav",
"surface_grav_units",
),
(
r" (\d+\.\d+)\W+Microturbulence parameter \[(.+)\]",
"microturbulence",
"microturbulence_units",
),
(
r"\s+(\d+\.\d+)\s+Mass \[(.+)\]",
"mass",
"mass_units",
),
(
r" (\+?\-?\d+.\d+) (\+?\-?\d+.\d+) Metallicity \[Fe\/H] and \[alpha\/Fe\]",
"feh",
"afe",
),
(
r" (\d+.\d+E\+\d\d) Radius \[(.+)\] at Tau",
"radius",
"radius_units",
),
(
r"\s+(\d+\.\d+(?:E[+-]?\d+)?) Luminosity \[(.+)\]",
"luminosity",
"luminosity_units",
),
(
r" (\d+.\d+) (\d+.\d+) (\d+.\d+) (\d+.\d+) are the convection parameters: alpha, nu, y and beta",
"conv_alpha",
"conv_nu",
"conv_y",
"conv_beta",
),
(
r" (0.\d+) (0.\d+) (\d.\d+E-\d+) are X, Y and Z, 12C\/13C=(\d+.?\d+)",
"x",
"y",
"z",
"12C/13C",
),
]
BYTES_THROUGH_METADATA = 550

if gzipped:
with gzip.open(fpath, "rt") as file:
Expand All @@ -231,10 +291,29 @@ def read_marcs_metadata(fpath, gzipped=True):

lines = list(contents)

for i, line in enumerate(lines):
metadata_re_match = metadata_re[i].match(line)
# Compile each of the regex pattern strings then open the file and match each of the patterns by line.
# Then add each of the matched patterns as a key:value pair to the metadata dict.
# Files are formatted a little differently depending on if the MARCS model is spherical or plane-parallel
if "plane-parallel" in lines[5]:
logger.info("Plane-parallel model detected.")
spherical = False
metadata_re = [
re.compile(re_str[0]) for re_str in METADATA_PLANE_PARALLEL_RE_STR
]
metadata_re_str = METADATA_PLANE_PARALLEL_RE_STR
else:
logger.info("Spherical model detected.")
spherical = True
metadata_re = [re.compile(re_str[0]) for re_str in METADATA_SPHERICAL_RE_STR]
metadata_re_str = METADATA_SPHERICAL_RE_STR

for j, metadata_name in enumerate(METADATA_RE_STR[i][1:]):
metadata = {}

# Check each line against the regex patterns and add the matched values to the metadata dictionary
for i in range(len(metadata_re_str)):
line = lines[i]
metadata_re_match = metadata_re[i].match(line)
for j, metadata_name in enumerate(metadata_re_str[i][1:]):
metadata[metadata_name] = metadata_re_match.group(j + 1)

# clean up metadata dictionary by changing strings of numbers to floats and attaching parsed units where appropriate
Expand All @@ -248,7 +327,7 @@ def read_marcs_metadata(fpath, gzipped=True):
metadata[key] = float(metadata[key])
metadata = {key: metadata[key] for key in metadata if key not in keys_to_remove}

return metadata
return metadata, spherical


def read_marcs_data(fpath, gzipped=True):
Expand Down Expand Up @@ -328,13 +407,20 @@ def read_marcs_model(fpath, gzipped=True):
Path to model file
gzipped : Bool
Whether or not the file is gzipped
spherical : Bool
Whether or not the model is spherical

Returns
-------
model : MARCSModel
Assembled metadata and data pair of a MARCS model
"""
metadata = read_marcs_metadata(fpath, gzipped=gzipped)
try:
metadata, spherical = read_marcs_metadata(fpath, gzipped=gzipped)
except:
raise ValueError(
"Failed to read metadata from MARCS model file. Make sure that you are specifying if the file is gzipped appropriately."
)
data = read_marcs_data(fpath, gzipped=gzipped)

return MARCSModel(metadata, data)
return MARCSModel(metadata, data, spherical=spherical)
7 changes: 6 additions & 1 deletion stardis/model/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,21 @@ class StellarModel(HDFWriterMixin):
Composition of the model. Includes density and atomic mass fractions.
no_of_depth_points : int
Class attribute to be easily accessible for initializing arrays that need to match the shape of the model.
spherical : bool
Flag for spherical geometry.
microturbulence : float
Microturbulence in km/s.
"""

hdf_properties = ["temperatures", "geometry", "composition"]

def __init__(self, temperatures, geometry, composition, microturbulence=0.0):
def __init__(
self, temperatures, geometry, composition, spherical=False, microturbulence=0.0
):
self.temperatures = temperatures
self.geometry = geometry
self.composition = composition
self.spherical = spherical
self.microturbulence = microturbulence

@property
Expand Down
3 changes: 2 additions & 1 deletion stardis/model/geometry/radial1d.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ class Radial1DGeometry:
distance to the next depth point
"""

def __init__(self, r):
def __init__(self, r, reference_r=None):
self.r = r
self.reference_r = reference_r

@property
def dist_to_next_depth_point(self):
Expand Down
32 changes: 28 additions & 4 deletions stardis/radiation_field/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ class RadiationField(HDFWriterMixin):
----------
frequencies : astronopy.units.Quantity
source_function : stardis.radiation_field.source_function
opacities : stardis.radiation_field.opacities
stellar_model : stardis.stellar_model.StellarModel
num_of_thetas : int

Attributes
----------
Expand All @@ -31,16 +32,40 @@ class RadiationField(HDFWriterMixin):
calculate the total opacity at each frequency at each depth point.
F_nu : numpy.ndarray
Radiation field fluxes at each frequency at each depth point. Initialized as zeros and calculated by a solver.
thetas : numpy.ndarray
Theta angles for raytracing.
I_nus_weights : numpy.ndarray
Weights for the theta angles.
I_nus : numpy.ndarray
Radiation field intensity at each frequency at each depth point at each theta angle. Initialized as zeros and calculated by a solver.
"""

hdf_properties = ["frequencies", "opacities", "F_nu"]

def __init__(self, frequencies, source_function, stellar_model):
def __init__(self, frequencies, source_function, stellar_model, num_of_thetas):
self.frequencies = frequencies
self.source_function = source_function
self.opacities = Opacities(frequencies, stellar_model)
self.F_nu = np.zeros((stellar_model.no_of_depth_points, len(frequencies)))

# This uses gauss-legendre quadrature to sample thetas
thetas, weights = np.polynomial.legendre.leggauss(num_of_thetas)
self.thetas = (thetas / 2) + 0.5 * np.pi / 2
self.I_nus_weights = weights * np.pi / 2

# This was our original theta sampling method
# dtheta = (np.pi / 2) / num_of_thetas # Korg uses Gauss-Legendre quadrature here
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this need to be kept? If so I would put it in a test for comparison purposes.

# start_theta = dtheta / 2
# end_theta = (np.pi / 2) - (dtheta / 2)
# self.thetas = np.linspace(start_theta, end_theta, num_of_thetas)
# self.I_nus_weights = (
# 2 * np.pi * dtheta * np.sin(self.thetas) * np.cos(self.thetas)
# )

self.I_nus = np.zeros(
(stellar_model.no_of_depth_points, len(frequencies), len(self.thetas))
)


def create_stellar_radiation_field(tracing_nus, stellar_model, stellar_plasma, config):
"""
Expand Down Expand Up @@ -69,7 +94,7 @@ def create_stellar_radiation_field(tracing_nus, stellar_model, stellar_plasma, c
"""

stellar_radiation_field = RadiationField(
tracing_nus, blackbody_flux_at_nu, stellar_model
tracing_nus, blackbody_flux_at_nu, stellar_model, config.no_of_thetas
)
logger.info("Calculating alphas")
calc_alphas(
Expand All @@ -83,7 +108,6 @@ def create_stellar_radiation_field(tracing_nus, stellar_model, stellar_plasma, c
raytrace(
stellar_model,
stellar_radiation_field,
no_of_thetas=config.no_of_thetas,
n_threads=config.n_threads,
)

Expand Down
Loading
Loading