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

HYDRAD Interface fixes #101

Merged
merged 5 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion examples/arcade-rtv.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import synthesizAR

from synthesizAR.instruments import InstrumentSDOAIA
from synthesizAR.instruments.sdo import InstrumentSDOAIA
from synthesizAR.interfaces import RTVInterface
from synthesizAR.models import semi_circular_arcade

Expand Down
2 changes: 1 addition & 1 deletion examples/ebtel-nanoflares.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

import synthesizAR

from synthesizAR.instruments import InstrumentSDOAIA
from synthesizAR.instruments.sdo import InstrumentSDOAIA
from synthesizAR.interfaces.ebtel import EbtelInterface
from synthesizAR.interfaces.ebtel.heating_models import PowerLawNanoflareTrain
from synthesizAR.models import semi_circular_arcade
Expand Down
2 changes: 1 addition & 1 deletion examples/loop-bundle-rtv.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import synthesizAR

from synthesizAR.instruments import InstrumentSDOAIA
from synthesizAR.instruments.sdo import InstrumentSDOAIA
from synthesizAR.interfaces import RTVInterface
from synthesizAR.models import semi_circular_bundle

Expand Down
3 changes: 2 additions & 1 deletion examples/multi-instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@

import synthesizAR

from synthesizAR.instruments import InstrumentHinodeXRT, InstrumentSDOAIA
from synthesizAR.instruments.hinode import InstrumentHinodeXRT
from synthesizAR.instruments.sdo import InstrumentSDOAIA
from synthesizAR.interfaces import MartensInterface
from synthesizAR.models import semi_circular_arcade

Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ dependencies = [
"zarr",
"asdf",
"ndcube>=2.0",
"xarray",
]

[project.urls]
Expand Down Expand Up @@ -86,6 +87,7 @@ filterwarnings = [
"error",
"ignore:The unit 'G' has been deprecated in the VOUnit standard.*:astropy.units.core.UnitsWarning",
"ignore:'cgi' is deprecated and slated for removal in Python 3.13:DeprecationWarning",
"ignore:.*pkg_resources.*:DeprecationWarning"
]

[tool.coverage.run]
Expand Down
2 changes: 0 additions & 2 deletions synthesizAR/instruments/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,3 @@
"""
from .base import *
from .physical import *
from .sdo import *
from .hinode import *
78 changes: 76 additions & 2 deletions synthesizAR/instruments/util.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
"""
Utility functions for building instrument classes
"""
import astropy.units as u
import astropy.wcs
import copy
import ndcube
import numpy as np
import xarray

import astropy.units as u
from ndcube.extra_coords.table_coord import QuantityTableCoordinate
from ndcube.wcs.wrappers import CompoundLowLevelWCS

__all__ = ['get_wave_keys', 'add_wave_keys_to_header', 'extend_celestial_wcs']
__all__ = [
'get_wave_keys',
'add_wave_keys_to_header',
'extend_celestial_wcs',
'read_cube_from_dataset',
'write_cube_to_netcdf',
]


@u.quantity_input
Expand Down Expand Up @@ -45,3 +55,67 @@ def extend_celestial_wcs(celestial_wcs, array, name, physical_type):
[celestial_wcs.pixel_n_dim] * temp_table.wcs.pixel_n_dim
)
return CompoundLowLevelWCS(celestial_wcs, temp_table.wcs, mapping=mapping)


def read_cube_from_dataset(filename, axis_name, physical_type):
"""
Read an `~ndcube.NDCube` from an `xarray` dataset.

This function reads a data cube from a netCDF file and rebuilds it
as an NDCube. The assumption is that the attributes on the stored
data array have the keys necessary to reconstitute a celestial FITS
WCS and that the axis denoted by `axis_name` is the additional axis
along which to extend that celestial WCS. This works only for 3D cubes
where two of the axes correspond to spatial, celestial axes.

Parameters
----------
filename: `str`, path-like
File to read from, usually a netCDF file
axis_name: `str`
The addeded coordinate along which to extend the celestial WCS.
physical_type: `str`
The physical type of `axis_name` as denoted by the IVOA designation.
"""
ds = xarray.load_dataset(filename)
meta = ds.attrs
data = u.Quantity(ds['data'].data, meta.pop('unit'))
mask = ds['mask'].data
celestial_wcs = astropy.wcs.WCS(header=meta)
axis_array = u.Quantity(ds[axis_name].data, ds[axis_name].attrs.get('unit'))
combined_wcs = extend_celestial_wcs(celestial_wcs, axis_array, axis_name, physical_type)
return ndcube.NDCube(data, wcs=combined_wcs, meta=meta, mask=mask)


def write_cube_to_netcdf(filename, axis_name, cube):
"""
Write a `~ndcube.NDCube` to a netCDF file.

This function writes an NDCube to a netCDF file by first expressing
it as an `xarray.DataArray`. This works only for 3D cubes where two of
the axes correspond to spatial, celestial axes.

Parameters
----------
cube: `ndcube.NDCube`
axis_name: `str`
filename: `str` or path-like
"""
# FIXME: This is not a general solution and is probably really brittle
celestial_wcs = cube.wcs.low_level_wcs._wcs[0]
wcs_keys = dict(celestial_wcs.to_header())
axis_array = cube.axis_world_coords(axis_name)[0]
axis_coord = xarray.Variable(axis_name,
axis_array.value,
attrs={'unit': axis_array.unit.to_string()})
if (mask:=cube.mask) is None:
mask = np.full(cube.data.shape, False)
cube_xa = xarray.Dataset(
{'data': ([axis_name, 'lat', 'lon'], cube.data),
'mask': ([axis_name, 'lat', 'lon'], mask)},
coords={
axis_name: axis_coord,
},
attrs={**wcs_keys, 'unit': cube.unit.to_string()}
)
cube_xa.to_netcdf(filename)
36 changes: 28 additions & 8 deletions synthesizAR/interfaces/hydrad/hydrad.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
Model interface for the HYDrodynamics and RADiation (HYDRAD) code
"""
import astropy.units as u
import copy
import numpy as np
import os
import pathlib
import pydrad.parse

Expand Down Expand Up @@ -69,18 +69,31 @@ def __init__(self,
interpolate_to_norm=False):
self.output_dir = pathlib.Path(output_dir)
self.base_config = base_config
self.hydrad_dir = hydrad_dir if hydrad_dir is None else pathlib.Path(hydrad_dir)
self.hydrad_dir = hydrad_dir
self.heating_model = heating_model
self.use_gravity = use_gravity
self.use_magnetic_field = use_magnetic_field
self.use_initial_conditions = use_initial_conditions
self.maximum_chromosphere_ratio = maximum_chromosphere_ratio
self.interpolate_to_norm = interpolate_to_norm

def configure_input(self, loop):
config = self.base_config.copy()
@property
def hydrad_dir(self):
return self._hydrad_dir

@hydrad_dir.setter
def hydrad_dir(self, value):
if value is not None:
value = pathlib.Path(value)
self._hydrad_dir = value

def _map_strand_to_config_dict(self, loop):
# NOTE: This is a separate function for ease of debugging
config = copy.deepcopy(self.base_config)
# NOTE: Avoids a bug in HYDRAD where seg faults can arise due to inconsistencies between max cell
# widths and minimum number of cells
config['general']['loop_length'] = loop.length
config['initial_conditions']['heating_location'] = loop.length / 2.
config['initial_conditions']['heating_location'] = loop.length / 2
if self.maximum_chromosphere_ratio:
config['general']['footpoint_height'] = min(
config['general']['footpoint_height'], self.maximum_chromosphere_ratio * loop.length / 2)
Expand All @@ -89,10 +102,17 @@ def configure_input(self, loop):
if self.use_magnetic_field:
config['general']['poly_fit_magnetic_field'] = self.configure_magnetic_field_fit(loop)
config = self.heating_model.calculate_event_properties(config, loop)
c = Configure(config)
c.setup_simulation(os.path.join(self.output_dir, loop.name),
return config

def configure_input(self, loop, **kwargs):
# Import here to avoid circular imports
from synthesizAR import log
log.debug(f'Configuring HYDRAD for {loop.name}')
config_dict = self._map_strand_to_config_dict(loop)
c = Configure(config_dict)
c.setup_simulation(self.output_dir / loop.name,
base_path=self.hydrad_dir,
verbose=False)
**kwargs)

def load_results(self, loop):
strand = pydrad.parse.Strand(self.output_dir / loop.name)
Expand Down
16 changes: 8 additions & 8 deletions synthesizAR/visualize/aia.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
"""
Plotting functions for easily and quickily visualizing synthesized AIA results
"""
import os

import numpy as np
import astropy.units as u
import h5py
import matplotlib.pyplot as plt
import matplotlib.colors
import matplotlib.animation
import astropy.units as u
import matplotlib.colors
import matplotlib.pyplot as plt
import numpy as np
import os

from astropy.coordinates import SkyCoord
from sunpy.map import Map

Expand All @@ -22,7 +22,7 @@ def plot_aia_channels(aia, time: u.s, root_dir, corners=None, figsize=None, norm

Parameters
----------
aia : `synthesizAR.instruments.InstrumentSDOAIA`
aia : `synthesizAR.instruments.sdo.InstrumentSDOAIA`
time : `astropy.Quantity`
root_dir : `str`
figsize : `tuple`, optional
Expand Down Expand Up @@ -62,7 +62,7 @@ def plot_aia_channels(aia, time: u.s, root_dir, corners=None, figsize=None, norm
ax.text(0.1*tmp.dimensions.x.value, 0.9*tmp.dimensions.y.value,
r'${}$ $\mathrm{{\mathring{{A}}}}$'.format(channel['name']),
color='w', fontsize=fontsize)
fig.suptitle(r'$t={:.0f}$ {}'.format(time.value, time.unit.to_string()), fontsize=fontsize)
fig.suptitle(rf'$t={time.value:.0f}$ {time.unit.to_string()}', fontsize=fontsize)
if kwargs.get('use_with_animation', False):
return fig, ims

Expand Down
Loading