From 02685c8323849033f6a73925adfb164e432ea4ad Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Thu, 23 Apr 2026 11:19:49 -0600 Subject: [PATCH 01/34] Added `esis.data.synthetic.scene_aia()`, a function to download AIA imagery and convert it into a synthetic scene. --- esis/data/synthetic/__init__.py | 0 esis/data/synthetic/_scene_aia/__init__.py | 0 esis/data/synthetic/_scene_aia/_scene_aia.py | 87 ++++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 esis/data/synthetic/__init__.py create mode 100644 esis/data/synthetic/_scene_aia/__init__.py create mode 100644 esis/data/synthetic/_scene_aia/_scene_aia.py diff --git a/esis/data/synthetic/__init__.py b/esis/data/synthetic/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/esis/data/synthetic/_scene_aia/__init__.py b/esis/data/synthetic/_scene_aia/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/esis/data/synthetic/_scene_aia/_scene_aia.py b/esis/data/synthetic/_scene_aia/_scene_aia.py new file mode 100644 index 0000000..88af870 --- /dev/null +++ b/esis/data/synthetic/_scene_aia/_scene_aia.py @@ -0,0 +1,87 @@ +from typing import Literal +import astropy.units as u +import astropy.time +import named_arrays as na +import sdo + +__all__ = [ + "scene_aia", +] + +def scene_aia( + time_start: astropy.time.Time, + time_stop: astropy.time.Time, + wavelength: u.Quantity | na.AbstractScalarArray, + wavelength_new: u.Quantity | na.AbstractScalarArray, + radiance: u.Quantity | na.AbstractScalarArray, + width: u.Quantity | na.AbstractScalarArray, + axis_time: str = "time", + axis_detector_x: str = "detector_x", + axis_detector_y: str = "detector_y", + axis_velocity: str = "velocity", + num_velocity: int = 1, + num_std: float = 3, + user_email: None | str = None, +): + """ + Create a synthetic solar scene using AIA images from the specified + time range shifted to a different wavelength. + + Parameters + --------- + time_start + The start time of the AIA observations. + time_stop + The stop time of the AIA observations. + wavelength + The wavelength of the AIA observations. + wavelength_new + The rest wavelength of each spectral line in the synthetic scene. + radiance + The average radiance of each spectral line in the synthetic scene. + width + The average standard deviation of each spectral line in the synthetic scene. + axis_time + The logical axis corresponding to changes in time. + axis_detector_x + The logical axis corresponding to changes in detector :math:`x`-coordinate. + axis_detector_y + The logical axis corresponding to changes in detector :math:`y`-coordinate. + num_velocity + The number of velocity bins in the synthetic scene. + num_std + The size of the domain for each spectral line in standard deviation units. + user_email + An email address used to notify the user that their JSOC request + is complete. + This email must be registered with JSOC before using this function. + If :obj:`None`, the value is taken from the ``JSOC_EMAIL`` + environment variable. + """ + + obs = sdo.aia.Filtergram.from_time_range( + time_start=time_start, + time_stop=time_stop, + wavelength=wavelength, + user_email=user_email, + axis_time=axis_time, + axis_detector_x=axis_detector_x, + axis_detector_y=axis_detector_y, + ) + + axis_detector_xy = axis_detector_x, axis_detector_y + + outputs = radiance * obs.outputs / obs.outputs.mean(axis_detector_xy) + + velocity_max = width * num_std + + velocity = na.linspace( + start=-velocity_max, + stop=velocity_max, + axis=axis_velocity, + num=num_velocity + 1, + ) + + v = velocity.cell_centers() + + na.TemporalSpectralPositionalVectorArray From 533bda791995cfb68bc878d1a66ecb1e1bb54e90 Mon Sep 17 00:00:00 2001 From: jacobdparker Date: Thu, 23 Apr 2026 17:18:11 -0600 Subject: [PATCH 02/34] add imports and testing for scene_aia --- esis/data/__init__.py | 2 + esis/data/synthetic/_scene_aia/__init__.py | 7 +++ esis/data/synthetic/_scene_aia/_scene_aia.py | 51 ++++++++++++------- .../synthetic/_scene_aia/_scene_aia_test.py | 27 ++++++++++ 4 files changed, 69 insertions(+), 18 deletions(-) create mode 100644 esis/data/synthetic/_scene_aia/_scene_aia_test.py diff --git a/esis/data/__init__.py b/esis/data/__init__.py index 0472ab5..9b69bfa 100644 --- a/esis/data/__init__.py +++ b/esis/data/__init__.py @@ -30,9 +30,11 @@ from . import abc from ._level_0 import Level_0 from ._level_1 import Level_1 +from .synthetic._scene_aia import scene_aia __all__ = [ "abc", "Level_0", "Level_1", + "scene_aia", ] diff --git a/esis/data/synthetic/_scene_aia/__init__.py b/esis/data/synthetic/_scene_aia/__init__.py index e69de29..07c8392 100644 --- a/esis/data/synthetic/_scene_aia/__init__.py +++ b/esis/data/synthetic/_scene_aia/__init__.py @@ -0,0 +1,7 @@ +from ._scene_aia import scene_aia + +__all__ = [ + "scene_aia", +] + + diff --git a/esis/data/synthetic/_scene_aia/_scene_aia.py b/esis/data/synthetic/_scene_aia/_scene_aia.py index 88af870..6b5c936 100644 --- a/esis/data/synthetic/_scene_aia/_scene_aia.py +++ b/esis/data/synthetic/_scene_aia/_scene_aia.py @@ -3,6 +3,8 @@ import astropy.time import named_arrays as na import sdo +from astropy import constants as const +import numpy as np __all__ = [ "scene_aia", @@ -11,10 +13,10 @@ def scene_aia( time_start: astropy.time.Time, time_stop: astropy.time.Time, - wavelength: u.Quantity | na.AbstractScalarArray, + wavelength_aia: u.Quantity | na.AbstractScalarArray, wavelength_new: u.Quantity | na.AbstractScalarArray, radiance: u.Quantity | na.AbstractScalarArray, - width: u.Quantity | na.AbstractScalarArray, + width_doppler: u.Quantity | na.AbstractScalarArray, axis_time: str = "time", axis_detector_x: str = "detector_x", axis_detector_y: str = "detector_y", @@ -33,13 +35,13 @@ def scene_aia( The start time of the AIA observations. time_stop The stop time of the AIA observations. - wavelength - The wavelength of the AIA observations. + wavelength_aia + The wavelength label of the AIA channel. wavelength_new - The rest wavelength of each spectral line in the synthetic scene. + The rest wavelength of each spectral line in the synthetic scene replacing wavelength AIA. radiance - The average radiance of each spectral line in the synthetic scene. - width + The average radiance of each spectral line in the synthetic scene, units of energy/cm^2/sr/s. + width_doppler The average standard deviation of each spectral line in the synthetic scene. axis_time The logical axis corresponding to changes in time. @@ -59,29 +61,42 @@ def scene_aia( environment variable. """ + velocity_max = width_doppler * num_std + sigma = width_doppler/const.c * wavelength_new + + velocity = na.linspace( + start=-velocity_max, + stop=velocity_max, + axis=axis_velocity, + num=num_velocity + 1, + ) + + wavelength = (1 + velocity/const.c) * wavelength_new + obs = sdo.aia.Filtergram.from_time_range( time_start=time_start, time_stop=time_stop, - wavelength=wavelength, + wavelength=wavelength_aia, user_email=user_email, axis_time=axis_time, axis_detector_x=axis_detector_x, axis_detector_y=axis_detector_y, ) - axis_detector_xy = axis_detector_x, axis_detector_y outputs = radiance * obs.outputs / obs.outputs.mean(axis_detector_xy) - velocity_max = width * num_std + wv_center = wavelength.cell_centers(axis=axis_velocity) - velocity = na.linspace( - start=-velocity_max, - stop=velocity_max, - axis=axis_velocity, - num=num_velocity + 1, - ) + gaussian = 1/(sigma.to(wavelength_new.unit)*np.sqrt(2*np.pi)) * np.exp(-((wv_center - wavelength_new) ** 2 / (2 * sigma ** 2)).to('')) - v = velocity.cell_centers() + outputs = outputs*gaussian - na.TemporalSpectralPositionalVectorArray + return na.FunctionArray( + inputs = na.TemporalSpectralPositionalVectorArray( + time=obs.inputs.time, + wavelength = wavelength, + position = obs.inputs.position + ), + outputs = outputs, + ) diff --git a/esis/data/synthetic/_scene_aia/_scene_aia_test.py b/esis/data/synthetic/_scene_aia/_scene_aia_test.py new file mode 100644 index 0000000..9eb4e38 --- /dev/null +++ b/esis/data/synthetic/_scene_aia/_scene_aia_test.py @@ -0,0 +1,27 @@ +import pytest +import esis +import astropy.units as u +import named_arrays as na + + +@pytest.mark.parametrize("num_velocity", [3]) +def test_scene_aia(num_velocity: int): + l1 = esis.flights.f1.data.level_1() + vernazza = 200 * u.erg / u.cm ** 2 / u.sr / u.s / (0.2 * u.AA) + wavelength = na.ScalarArray([580, 630] * u.AA, axes='spectral_line') + scene_aia = esis.data.scene_aia( + time_start = l1.inputs[dict(time=0,channel=0)].time_start.ndarray, + time_stop = l1.inputs[dict(time=2,channel=0)].time_start.ndarray, + wavelength_aia = na.ScalarArray([304, 193] * u.AA, axes='spectral_line'), + wavelength_new =wavelength, + radiance = na.ScalarArray([1, .25] * vernazza, axes='spectral_line'), + width_doppler = na.ScalarArray([10 , 100] * u.km/u.s, axes='spectral_line'), + user_email = 'jacobdparker@gmail.com', + num_velocity=num_velocity, + ) + + assert scene_aia.shape['velocity'] == num_velocity + assert scene_aia.outputs.unit == vernazza.unit / wavelength.unit + + + From 3967be1778bd1dcb39ef877990a7647e51d88498 Mon Sep 17 00:00:00 2001 From: jacobdparker Date: Fri, 24 Apr 2026 15:24:29 -0600 Subject: [PATCH 03/34] Proper gaussian distribution along velocity axis --- esis/data/synthetic/_scene_aia/_scene_aia.py | 22 ++++++++++--------- .../synthetic/_scene_aia/_scene_aia_test.py | 11 +++++++--- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/esis/data/synthetic/_scene_aia/_scene_aia.py b/esis/data/synthetic/_scene_aia/_scene_aia.py index 6b5c936..fb68265 100644 --- a/esis/data/synthetic/_scene_aia/_scene_aia.py +++ b/esis/data/synthetic/_scene_aia/_scene_aia.py @@ -5,6 +5,7 @@ import sdo from astropy import constants as const import numpy as np +from scipy.special import erf __all__ = [ "scene_aia", @@ -26,8 +27,9 @@ def scene_aia( user_email: None | str = None, ): """ - Create a synthetic solar scene using AIA images from the specified - time range shifted to a different wavelength. + Create a synthetic solar scene (radiance as a function of time, space, and wavelength) using AIA images from channel + wavelength_aia. Each radiance is distributed using a gaussian distribution along axis_velocity with width_doppler and + into num_velocity bins. Parameters --------- @@ -38,7 +40,8 @@ def scene_aia( wavelength_aia The wavelength label of the AIA channel. wavelength_new - The rest wavelength of each spectral line in the synthetic scene replacing wavelength AIA. + The rest wavelength of each spectral line in the synthetic scene replacing wavelength AIA. Axes of wavelength_new + should be aligned to the axes of wavelength_aia. radiance The average radiance of each spectral line in the synthetic scene, units of energy/cm^2/sr/s. width_doppler @@ -62,7 +65,6 @@ def scene_aia( """ velocity_max = width_doppler * num_std - sigma = width_doppler/const.c * wavelength_new velocity = na.linspace( start=-velocity_max, @@ -71,6 +73,10 @@ def scene_aia( num=num_velocity + 1, ) + z_a = velocity[{axis_velocity:slice(0,-1)}] / (width_doppler * np.sqrt(2)) + z_b = velocity[{axis_velocity:slice(1,None)}] / (width_doppler * np.sqrt(2)) + gaussian = 0.5 * (erf(z_b) - erf(z_a)) + wavelength = (1 + velocity/const.c) * wavelength_new obs = sdo.aia.Filtergram.from_time_range( @@ -85,12 +91,8 @@ def scene_aia( axis_detector_xy = axis_detector_x, axis_detector_y outputs = radiance * obs.outputs / obs.outputs.mean(axis_detector_xy) - - wv_center = wavelength.cell_centers(axis=axis_velocity) - - gaussian = 1/(sigma.to(wavelength_new.unit)*np.sqrt(2*np.pi)) * np.exp(-((wv_center - wavelength_new) ** 2 / (2 * sigma ** 2)).to('')) - - outputs = outputs*gaussian + delta_lambda= np.diff(wavelength,axis=axis_velocity) + outputs = outputs*gaussian/delta_lambda return na.FunctionArray( inputs = na.TemporalSpectralPositionalVectorArray( diff --git a/esis/data/synthetic/_scene_aia/_scene_aia_test.py b/esis/data/synthetic/_scene_aia/_scene_aia_test.py index 9eb4e38..2c8e24b 100644 --- a/esis/data/synthetic/_scene_aia/_scene_aia_test.py +++ b/esis/data/synthetic/_scene_aia/_scene_aia_test.py @@ -2,26 +2,31 @@ import esis import astropy.units as u import named_arrays as na - +import numpy as np @pytest.mark.parametrize("num_velocity", [3]) def test_scene_aia(num_velocity: int): l1 = esis.flights.f1.data.level_1() - vernazza = 200 * u.erg / u.cm ** 2 / u.sr / u.s / (0.2 * u.AA) + vernazza = 200 * u.erg / u.cm ** 2 / u.sr / u.s + radiance = na.ScalarArray([1, .25] * vernazza, axes='spectral_line') wavelength = na.ScalarArray([580, 630] * u.AA, axes='spectral_line') scene_aia = esis.data.scene_aia( time_start = l1.inputs[dict(time=0,channel=0)].time_start.ndarray, time_stop = l1.inputs[dict(time=2,channel=0)].time_start.ndarray, wavelength_aia = na.ScalarArray([304, 193] * u.AA, axes='spectral_line'), wavelength_new =wavelength, - radiance = na.ScalarArray([1, .25] * vernazza, axes='spectral_line'), + radiance =radiance, width_doppler = na.ScalarArray([10 , 100] * u.km/u.s, axes='spectral_line'), user_email = 'jacobdparker@gmail.com', num_velocity=num_velocity, + num_std=5 ) assert scene_aia.shape['velocity'] == num_velocity assert scene_aia.outputs.unit == vernazza.unit / wavelength.unit + integrated_radiance = scene_aia.outputs.mean(axis=('detector_x', 'detector_y')) * np.diff( + scene_aia.inputs.wavelength, axis='velocity') + assert np.allclose(integrated_radiance.sum('velocity'), radiance) From 559803b8dbc75e9c34d7c42efb289b39189de3df Mon Sep 17 00:00:00 2001 From: jacobdparker Date: Fri, 24 Apr 2026 15:28:05 -0600 Subject: [PATCH 04/34] formatting --- esis/data/synthetic/_scene_aia/_scene_aia.py | 23 ++++++------ .../synthetic/_scene_aia/_scene_aia_test.py | 35 +++++++++---------- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/esis/data/synthetic/_scene_aia/_scene_aia.py b/esis/data/synthetic/_scene_aia/_scene_aia.py index fb68265..f0ef46b 100644 --- a/esis/data/synthetic/_scene_aia/_scene_aia.py +++ b/esis/data/synthetic/_scene_aia/_scene_aia.py @@ -1,4 +1,3 @@ -from typing import Literal import astropy.units as u import astropy.time import named_arrays as na @@ -11,6 +10,7 @@ "scene_aia", ] + def scene_aia( time_start: astropy.time.Time, time_stop: astropy.time.Time, @@ -32,7 +32,7 @@ def scene_aia( into num_velocity bins. Parameters - --------- + ---------- time_start The start time of the AIA observations. time_stop @@ -63,7 +63,6 @@ def scene_aia( If :obj:`None`, the value is taken from the ``JSOC_EMAIL`` environment variable. """ - velocity_max = width_doppler * num_std velocity = na.linspace( @@ -73,11 +72,11 @@ def scene_aia( num=num_velocity + 1, ) - z_a = velocity[{axis_velocity:slice(0,-1)}] / (width_doppler * np.sqrt(2)) - z_b = velocity[{axis_velocity:slice(1,None)}] / (width_doppler * np.sqrt(2)) + z_a = velocity[{axis_velocity: slice(0, -1)}] / (width_doppler * np.sqrt(2)) + z_b = velocity[{axis_velocity: slice(1, None)}] / (width_doppler * np.sqrt(2)) gaussian = 0.5 * (erf(z_b) - erf(z_a)) - wavelength = (1 + velocity/const.c) * wavelength_new + wavelength = (1 + velocity / const.c) * wavelength_new obs = sdo.aia.Filtergram.from_time_range( time_start=time_start, @@ -91,14 +90,14 @@ def scene_aia( axis_detector_xy = axis_detector_x, axis_detector_y outputs = radiance * obs.outputs / obs.outputs.mean(axis_detector_xy) - delta_lambda= np.diff(wavelength,axis=axis_velocity) - outputs = outputs*gaussian/delta_lambda + delta_lambda = np.diff(wavelength, axis=axis_velocity) + outputs = outputs * gaussian / delta_lambda return na.FunctionArray( - inputs = na.TemporalSpectralPositionalVectorArray( + inputs=na.TemporalSpectralPositionalVectorArray( time=obs.inputs.time, - wavelength = wavelength, - position = obs.inputs.position + wavelength=wavelength, + position=obs.inputs.position ), - outputs = outputs, + outputs=outputs, ) diff --git a/esis/data/synthetic/_scene_aia/_scene_aia_test.py b/esis/data/synthetic/_scene_aia/_scene_aia_test.py index 2c8e24b..7a8974c 100644 --- a/esis/data/synthetic/_scene_aia/_scene_aia_test.py +++ b/esis/data/synthetic/_scene_aia/_scene_aia_test.py @@ -4,29 +4,28 @@ import named_arrays as na import numpy as np + @pytest.mark.parametrize("num_velocity", [3]) def test_scene_aia(num_velocity: int): l1 = esis.flights.f1.data.level_1() - vernazza = 200 * u.erg / u.cm ** 2 / u.sr / u.s - radiance = na.ScalarArray([1, .25] * vernazza, axes='spectral_line') - wavelength = na.ScalarArray([580, 630] * u.AA, axes='spectral_line') + vernazza = 200 * u.erg / u.cm**2 / u.sr / u.s + radiance = na.ScalarArray([1, 0.25] * vernazza, axes="spectral_line") + wavelength = na.ScalarArray([580, 630] * u.AA, axes="spectral_line") scene_aia = esis.data.scene_aia( - time_start = l1.inputs[dict(time=0,channel=0)].time_start.ndarray, - time_stop = l1.inputs[dict(time=2,channel=0)].time_start.ndarray, - wavelength_aia = na.ScalarArray([304, 193] * u.AA, axes='spectral_line'), - wavelength_new =wavelength, - radiance =radiance, - width_doppler = na.ScalarArray([10 , 100] * u.km/u.s, axes='spectral_line'), - user_email = 'jacobdparker@gmail.com', + time_start=l1.inputs[dict(time=0, channel=0)].time_start.ndarray, + time_stop=l1.inputs[dict(time=2, channel=0)].time_start.ndarray, + wavelength_aia=na.ScalarArray([304, 193] * u.AA, axes="spectral_line"), + wavelength_new=wavelength, + radiance=radiance, + width_doppler=na.ScalarArray([10, 100] * u.km / u.s, axes="spectral_line"), + user_email="jacobdparker@gmail.com", num_velocity=num_velocity, - num_std=5 + num_std=5, ) - assert scene_aia.shape['velocity'] == num_velocity + assert scene_aia.shape["velocity"] == num_velocity assert scene_aia.outputs.unit == vernazza.unit / wavelength.unit - integrated_radiance = scene_aia.outputs.mean(axis=('detector_x', 'detector_y')) * np.diff( - scene_aia.inputs.wavelength, axis='velocity') - assert np.allclose(integrated_radiance.sum('velocity'), radiance) - - - + integrated_radiance = scene_aia.outputs.mean( + axis=("detector_x", "detector_y") + ) * np.diff(scene_aia.inputs.wavelength, axis="velocity") + assert np.allclose(integrated_radiance.sum("velocity"), radiance) From 044df217c5d99ac6ba665750f771b6a0ae5542d9 Mon Sep 17 00:00:00 2001 From: jacobdparker Date: Fri, 24 Apr 2026 15:32:50 -0600 Subject: [PATCH 05/34] formatting --- esis/data/synthetic/_scene_aia/_scene_aia.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/esis/data/synthetic/_scene_aia/_scene_aia.py b/esis/data/synthetic/_scene_aia/_scene_aia.py index f0ef46b..24933cd 100644 --- a/esis/data/synthetic/_scene_aia/_scene_aia.py +++ b/esis/data/synthetic/_scene_aia/_scene_aia.py @@ -28,8 +28,8 @@ def scene_aia( ): """ Create a synthetic solar scene (radiance as a function of time, space, and wavelength) using AIA images from channel - wavelength_aia. Each radiance is distributed using a gaussian distribution along axis_velocity with width_doppler and - into num_velocity bins. + wavelength_aia. Each radiance is distributed using a gaussian distribution along axis_velocity with width_doppler + and into num_velocity bins. Parameters ---------- @@ -40,8 +40,8 @@ def scene_aia( wavelength_aia The wavelength label of the AIA channel. wavelength_new - The rest wavelength of each spectral line in the synthetic scene replacing wavelength AIA. Axes of wavelength_new - should be aligned to the axes of wavelength_aia. + The rest wavelength of each spectral line in the synthetic scene replacing wavelength AIA. Axes of + wavelength_new should be aligned to the axes of wavelength_aia. radiance The average radiance of each spectral line in the synthetic scene, units of energy/cm^2/sr/s. width_doppler @@ -62,6 +62,7 @@ def scene_aia( This email must be registered with JSOC before using this function. If :obj:`None`, the value is taken from the ``JSOC_EMAIL`` environment variable. + """ velocity_max = width_doppler * num_std From e59b081c334e761c978b91cc1c2ab6649b3106f1 Mon Sep 17 00:00:00 2001 From: jacobdparker Date: Fri, 24 Apr 2026 15:38:56 -0600 Subject: [PATCH 06/34] ruff --- esis/data/synthetic/__init__.py | 3 +++ pyproject.toml | 1 + 2 files changed, 4 insertions(+) diff --git a/esis/data/synthetic/__init__.py b/esis/data/synthetic/__init__.py index e69de29..251b175 100644 --- a/esis/data/synthetic/__init__.py +++ b/esis/data/synthetic/__init__.py @@ -0,0 +1,3 @@ +"""" +Tools for creating synthetic solar scenes for ESIS analysis. +""" \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 2b6433b..9078ff6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ dependencies = [ "named-arrays~=1.0", "optika~=1.1", "msfc-ccd~=1.0", + "solar-dynamics-observatory", ] dynamic = ["version"] From cd5ac320f4af961375cac07213b859de8a05c52f Mon Sep 17 00:00:00 2001 From: jacobdparker Date: Fri, 24 Apr 2026 16:46:37 -0600 Subject: [PATCH 07/34] formatting --- esis/data/synthetic/__init__.py | 10 +++++--- esis/data/synthetic/_scene_aia/_scene_aia.py | 25 +++++++++++++------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/esis/data/synthetic/__init__.py b/esis/data/synthetic/__init__.py index 251b175..276f66b 100644 --- a/esis/data/synthetic/__init__.py +++ b/esis/data/synthetic/__init__.py @@ -1,3 +1,7 @@ -"""" -Tools for creating synthetic solar scenes for ESIS analysis. -""" \ No newline at end of file +"""Tools for creating synthetic solar scenes for ESIS analysis.""" + +from ._scene_aia import scene_aia + +__all__ = [ + "scene_aia", +] diff --git a/esis/data/synthetic/_scene_aia/_scene_aia.py b/esis/data/synthetic/_scene_aia/_scene_aia.py index 24933cd..ee3dbb7 100644 --- a/esis/data/synthetic/_scene_aia/_scene_aia.py +++ b/esis/data/synthetic/_scene_aia/_scene_aia.py @@ -27,9 +27,14 @@ def scene_aia( user_email: None | str = None, ): """ - Create a synthetic solar scene (radiance as a function of time, space, and wavelength) using AIA images from channel - wavelength_aia. Each radiance is distributed using a gaussian distribution along axis_velocity with width_doppler - and into num_velocity bins. + Create a synthetic solar scene using AIA images. + + AIA images from channels wavelength_aia over a supplied time range are used to + represent estimates of images at wavelength_new. A supplied mean radiance is + assigned to + each image at wavelength_new and distributed using a gaussian distribution along + axis_velocity with width_doppler and into num_velocity + bins. Parameters ---------- @@ -40,10 +45,14 @@ def scene_aia( wavelength_aia The wavelength label of the AIA channel. wavelength_new - The rest wavelength of each spectral line in the synthetic scene replacing wavelength AIA. Axes of + The rest wavelength of each spectral line in the synthetic scene replacing + wavelength AIA. + Axes of wavelength_new should be aligned to the axes of wavelength_aia. radiance - The average radiance of each spectral line in the synthetic scene, units of energy/cm^2/sr/s. + The average radiance of each spectral line in the synthetic scene, + units of + energy/cm^2/sr/s. width_doppler The average standard deviation of each spectral line in the synthetic scene. axis_time @@ -56,7 +65,7 @@ def scene_aia( The number of velocity bins in the synthetic scene. num_std The size of the domain for each spectral line in standard deviation units. - user_email + use # noqa: E501 An email address used to notify the user that their JSOC request is complete. This email must be registered with JSOC before using this function. @@ -96,9 +105,7 @@ def scene_aia( return na.FunctionArray( inputs=na.TemporalSpectralPositionalVectorArray( - time=obs.inputs.time, - wavelength=wavelength, - position=obs.inputs.position + time=obs.inputs.time, wavelength=wavelength, position=obs.inputs.position ), outputs=outputs, ) From 45a88da3325004151526879ac71818aab55afd12 Mon Sep 17 00:00:00 2001 From: jacobdparker Date: Fri, 24 Apr 2026 16:51:04 -0600 Subject: [PATCH 08/34] sdo version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9078ff6..8b9a52a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ dependencies = [ "named-arrays~=1.0", "optika~=1.1", "msfc-ccd~=1.0", - "solar-dynamics-observatory", + "solar-dynamics-obseravtory~=0.1", ] dynamic = ["version"] From 830c9f72dd22ad84bfd03d883505df842c8c9b81 Mon Sep 17 00:00:00 2001 From: jacobdparker Date: Fri, 24 Apr 2026 16:52:58 -0600 Subject: [PATCH 09/34] remove whitespace --- esis/data/synthetic/_scene_aia/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/esis/data/synthetic/_scene_aia/__init__.py b/esis/data/synthetic/_scene_aia/__init__.py index 07c8392..a15754d 100644 --- a/esis/data/synthetic/_scene_aia/__init__.py +++ b/esis/data/synthetic/_scene_aia/__init__.py @@ -3,5 +3,3 @@ __all__ = [ "scene_aia", ] - - From 8a4feee2cc98d1a4f888b67fa7dd346f7f446bef Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Sun, 26 Apr 2026 09:31:10 -0600 Subject: [PATCH 10/34] changed `sythesis` to `synth`, tweaks to docs and formatting --- esis/data/{synthetic => synth}/__init__.py | 0 .../_scene_aia/__init__.py | 0 .../_scene_aia/_scene_aia.py | 29 ++++++++++--------- .../_scene_aia/_scene_aia_test.py | 10 +++---- 4 files changed, 20 insertions(+), 19 deletions(-) rename esis/data/{synthetic => synth}/__init__.py (100%) rename esis/data/{synthetic => synth}/_scene_aia/__init__.py (100%) rename esis/data/{synthetic => synth}/_scene_aia/_scene_aia.py (84%) rename esis/data/{synthetic => synth}/_scene_aia/_scene_aia_test.py (81%) diff --git a/esis/data/synthetic/__init__.py b/esis/data/synth/__init__.py similarity index 100% rename from esis/data/synthetic/__init__.py rename to esis/data/synth/__init__.py diff --git a/esis/data/synthetic/_scene_aia/__init__.py b/esis/data/synth/_scene_aia/__init__.py similarity index 100% rename from esis/data/synthetic/_scene_aia/__init__.py rename to esis/data/synth/_scene_aia/__init__.py diff --git a/esis/data/synthetic/_scene_aia/_scene_aia.py b/esis/data/synth/_scene_aia/_scene_aia.py similarity index 84% rename from esis/data/synthetic/_scene_aia/_scene_aia.py rename to esis/data/synth/_scene_aia/_scene_aia.py index ee3dbb7..e86b695 100644 --- a/esis/data/synthetic/_scene_aia/_scene_aia.py +++ b/esis/data/synth/_scene_aia/_scene_aia.py @@ -1,9 +1,10 @@ +import numpy as np +import scipy.special as sp import astropy.units as u import astropy.time +from astropy import constants as const import named_arrays as na import sdo -from astropy import constants as const -import numpy as np from scipy.special import erf __all__ = [ @@ -26,15 +27,14 @@ def scene_aia( num_std: float = 3, user_email: None | str = None, ): - """ + r""" Create a synthetic solar scene using AIA images. - AIA images from channels wavelength_aia over a supplied time range are used to - represent estimates of images at wavelength_new. A supplied mean radiance is - assigned to - each image at wavelength_new and distributed using a gaussian distribution along - axis_velocity with width_doppler and into num_velocity - bins. + AIA images from channels `wavelength_aia` over a supplied time range are used to + represent estimates of images at `wavelength_new`. + A supplied mean radiance is assigned to each image at `wavelength_new` + and distributed along `axis_velocity` into `num_velocity` bins + using a Gaussian with standard deviation `width_doppler`. Parameters ---------- @@ -50,9 +50,8 @@ def scene_aia( Axes of wavelength_new should be aligned to the axes of wavelength_aia. radiance - The average radiance of each spectral line in the synthetic scene, - units of - energy/cm^2/sr/s. + The average radiance of each spectral line in the synthetic scene in + units of :math:`\text{erg} \text{cm}^{-2} \text{sr}^{-1} \text{s}^{-1}.` width_doppler The average standard deviation of each spectral line in the synthetic scene. axis_time @@ -65,7 +64,7 @@ def scene_aia( The number of velocity bins in the synthetic scene. num_std The size of the domain for each spectral line in standard deviation units. - use # noqa: E501 + user_email # noqa: E501 An email address used to notify the user that their JSOC request is complete. This email must be registered with JSOC before using this function. @@ -105,7 +104,9 @@ def scene_aia( return na.FunctionArray( inputs=na.TemporalSpectralPositionalVectorArray( - time=obs.inputs.time, wavelength=wavelength, position=obs.inputs.position + time=obs.inputs.time, + wavelength=wavelength, + position=obs.inputs.position, ), outputs=outputs, ) diff --git a/esis/data/synthetic/_scene_aia/_scene_aia_test.py b/esis/data/synth/_scene_aia/_scene_aia_test.py similarity index 81% rename from esis/data/synthetic/_scene_aia/_scene_aia_test.py rename to esis/data/synth/_scene_aia/_scene_aia_test.py index 7a8974c..0493e72 100644 --- a/esis/data/synthetic/_scene_aia/_scene_aia_test.py +++ b/esis/data/synth/_scene_aia/_scene_aia_test.py @@ -11,21 +11,21 @@ def test_scene_aia(num_velocity: int): vernazza = 200 * u.erg / u.cm**2 / u.sr / u.s radiance = na.ScalarArray([1, 0.25] * vernazza, axes="spectral_line") wavelength = na.ScalarArray([580, 630] * u.AA, axes="spectral_line") - scene_aia = esis.data.scene_aia( + scene_aia = esis.data.synth.scene_aia( time_start=l1.inputs[dict(time=0, channel=0)].time_start.ndarray, time_stop=l1.inputs[dict(time=2, channel=0)].time_start.ndarray, wavelength_aia=na.ScalarArray([304, 193] * u.AA, axes="spectral_line"), wavelength_new=wavelength, radiance=radiance, width_doppler=na.ScalarArray([10, 100] * u.km / u.s, axes="spectral_line"), - user_email="jacobdparker@gmail.com", num_velocity=num_velocity, num_std=5, ) assert scene_aia.shape["velocity"] == num_velocity assert scene_aia.outputs.unit == vernazza.unit / wavelength.unit - integrated_radiance = scene_aia.outputs.mean( - axis=("detector_x", "detector_y") - ) * np.diff(scene_aia.inputs.wavelength, axis="velocity") + + axis_xy = ("detector_x", "detector_y") + delta_lambda = np.diff(scene_aia.inputs.wavelength, axis="velocity") + integrated_radiance = scene_aia.outputs.mean(axis_xy) * delta_lambda assert np.allclose(integrated_radiance.sum("velocity"), radiance) From e88fa8adab9116b5cdc2b01b74cc4c8801a840b8 Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Sun, 26 Apr 2026 09:39:54 -0600 Subject: [PATCH 11/34] fix --- esis/data/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esis/data/__init__.py b/esis/data/__init__.py index 9b69bfa..3c66a0e 100644 --- a/esis/data/__init__.py +++ b/esis/data/__init__.py @@ -28,13 +28,13 @@ """ from . import abc +from . import synth from ._level_0 import Level_0 from ._level_1 import Level_1 -from .synthetic._scene_aia import scene_aia __all__ = [ "abc", + "synth", "Level_0", "Level_1", - "scene_aia", ] From 177b6f8c60c57ca41a43909de31cae36fd65aba0 Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Sun, 26 Apr 2026 09:40:38 -0600 Subject: [PATCH 12/34] update python to 3.12 --- .github/workflows/docs.yml | 2 +- .github/workflows/tests.yml | 3 ++- pyproject.toml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index c4f6fd1..f7ace25 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -32,7 +32,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: '3.12' - name: Install package run: | diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e50f63b..8f8b820 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,7 +14,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macOS-latest] - python-version: ["3.11", "3.12"] + python-version: ["3.12", "3.13"] name: ${{ matrix.os }}, Python ${{ matrix.python-version }} tests steps: - name: Set Swap Space @@ -40,6 +40,7 @@ jobs: - name: Test with pytest env: MPLBACKEND: "agg" + JSOC_EMAIL: "roytsmart@gmail.com" run: | pip install pytest pytest-cov pytest --cov=. --cov-report=xml --cov-report=html diff --git a/pyproject.toml b/pyproject.toml index 8b9a52a..4155bc9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ authors = [ ] description = "A Python library for modeling and interpreting data from the EUV Snapshot Imaging Spectrograph (ESIS)." readme = "README.md" -requires-python = ">=3.11" +requires-python = ">=3.12" classifiers = [ "Programming Language :: Python :: 3", ] From 85d7a61e3ed82b6b0b8b34cdf5c62dca6f3e4bb3 Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Sun, 26 Apr 2026 11:15:05 -0600 Subject: [PATCH 13/34] debugging --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4155bc9..9bbbaf6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ dependencies = [ "named-arrays~=1.0", "optika~=1.1", "msfc-ccd~=1.0", - "solar-dynamics-obseravtory~=0.1", + "solar-dynamics-obseravtory", ] dynamic = ["version"] From 203b104c325a8a03d1ca12b47edbe0c0c8d790ee Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Sun, 26 Apr 2026 11:18:06 -0600 Subject: [PATCH 14/34] ok, figured it out, jake cant spell --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9bbbaf6..76a036a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ dependencies = [ "named-arrays~=1.0", "optika~=1.1", "msfc-ccd~=1.0", - "solar-dynamics-obseravtory", + "solar-dynamics-observatory~=0.1", ] dynamic = ["version"] From 1f62c9f5d82eae26c99a739a329ef5204bcbb6c6 Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Sun, 26 Apr 2026 12:14:59 -0600 Subject: [PATCH 15/34] imports --- esis/data/synth/_scene_aia/_scene_aia.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/esis/data/synth/_scene_aia/_scene_aia.py b/esis/data/synth/_scene_aia/_scene_aia.py index e86b695..721c9ab 100644 --- a/esis/data/synth/_scene_aia/_scene_aia.py +++ b/esis/data/synth/_scene_aia/_scene_aia.py @@ -5,7 +5,6 @@ from astropy import constants as const import named_arrays as na import sdo -from scipy.special import erf __all__ = [ "scene_aia", @@ -83,7 +82,7 @@ def scene_aia( z_a = velocity[{axis_velocity: slice(0, -1)}] / (width_doppler * np.sqrt(2)) z_b = velocity[{axis_velocity: slice(1, None)}] / (width_doppler * np.sqrt(2)) - gaussian = 0.5 * (erf(z_b) - erf(z_a)) + gaussian = 0.5 * (sp.erf(z_b) - sp.erf(z_a)) wavelength = (1 + velocity / const.c) * wavelength_new From 42c7646508f67340431158c33be66da403bfdeab Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Sun, 26 Apr 2026 16:15:16 -0600 Subject: [PATCH 16/34] Added properties of spectrum --- esis/flights/f1/__init__.py | 1 + esis/flights/f1/spectrum/He_I.py | 18 ++++++++++++++++++ esis/flights/f1/spectrum/Mg_X.py | 23 +++++++++++++++++++++++ esis/flights/f1/spectrum/O_V.py | 23 +++++++++++++++++++++++ esis/flights/f1/spectrum/__init__.py | 9 +++++++++ 5 files changed, 74 insertions(+) create mode 100644 esis/flights/f1/spectrum/He_I.py create mode 100644 esis/flights/f1/spectrum/Mg_X.py create mode 100644 esis/flights/f1/spectrum/O_V.py create mode 100644 esis/flights/f1/spectrum/__init__.py diff --git a/esis/flights/f1/__init__.py b/esis/flights/f1/__init__.py index 74df5e5..7f7c125 100644 --- a/esis/flights/f1/__init__.py +++ b/esis/flights/f1/__init__.py @@ -5,6 +5,7 @@ from . import data __all__ = [ + "spectrum", "optics", "nsroc", "data", diff --git a/esis/flights/f1/spectrum/He_I.py b/esis/flights/f1/spectrum/He_I.py new file mode 100644 index 0000000..075b8dc --- /dev/null +++ b/esis/flights/f1/spectrum/He_I.py @@ -0,0 +1,18 @@ +r"""Properties of the :math:`\text{He\,\textsc{i}} 584\,\AA` spectral line.""" + +import astropy.units as u + +__all__ = [ + "wavelength", + "radiance", + "width_doppler", +] + +#: Rest wavelength calculated by the Chianti Atomic Database :cite:p:`Dere1997`. +wavelength = 584.334 * u.AA + +#: Average quiet-sun radiance measured by :cite:t:`Vernazza1977`. +radiance = 544.98 * u.erg / u.cm**2 / u.sr / u.s + +#: Average quiet-sun Doppler width measured by :cite:t:`Peter1999`. +width_doppler = 20 * u.km / u.s diff --git a/esis/flights/f1/spectrum/Mg_X.py b/esis/flights/f1/spectrum/Mg_X.py new file mode 100644 index 0000000..836bd65 --- /dev/null +++ b/esis/flights/f1/spectrum/Mg_X.py @@ -0,0 +1,23 @@ +r"""Properties of the :math:`\text{Mg,\textsc{x}} 609\,\AA` spectral line.""" + +import numpy as np +import astropy.units as u + +__all__ = [ + "wavelength", + "radiance", + "fwhm", +] + +#: Rest wavelength calculated by the Chianti Atomic Database :cite:p:`Dere1997`. +wavelength = 609.793 * u.AA + +#: Average quiet-sun radiance measured by :cite:t:`Vernazza1978`. +radiance = 125.05 * u.erg / u.cm**2 / u.sr / u.s + +_fwhm = 0.138 * u.mAA + +_width = _fwhm / (2 * np.sqrt(2 * np.log(2))) + +#: Average quiet-sun Doppler width measured by :cite:t:`Doschek2004`. +width_doppler = _width.to(u.km / u.s, equivalencies=u.doppler_optical(wavelength)) \ No newline at end of file diff --git a/esis/flights/f1/spectrum/O_V.py b/esis/flights/f1/spectrum/O_V.py new file mode 100644 index 0000000..9573b94 --- /dev/null +++ b/esis/flights/f1/spectrum/O_V.py @@ -0,0 +1,23 @@ +r"""Properties of the :math:`\text{O\,\textsc{v}} 630\,\AA` spectral line.""" + +import numpy as np +import astropy.units as u + +__all__ = [ + "wavelength", + "radiance", + "width_doppler", +] + +#: Rest wavelength calculated by the Chianti Atomic Database :cite:p:`Dere1997`. +wavelength = 629.732 * u.AA + +#: Average quiet-sun radiance measured by :cite:t:`Vernazza1978`. +radiance = 334.97 * u.erg / u.cm**2 / u.sr / u.s + +_fwhm = 0.129 * u.mAA + +_width = _fwhm / (2 * np.sqrt(2 * np.log(2))) + +#: Average quiet-sun Doppler width measured by :cite:t:`Doschek2004`. +width_doppler = _width.to(u.km / u.s, equivalencies=u.doppler_optical(wavelength)) diff --git a/esis/flights/f1/spectrum/__init__.py b/esis/flights/f1/spectrum/__init__.py new file mode 100644 index 0000000..19fc79d --- /dev/null +++ b/esis/flights/f1/spectrum/__init__.py @@ -0,0 +1,9 @@ +"""Properties of the spectral lines observed during this flight.""" + +from . import O_V +from . import Mg_X + +__all__ = [ + "O_V", + "Mg_X", +] From 11cbf6f618e48f0a64da392319a6406fc72566cb Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Sun, 26 Apr 2026 16:15:52 -0600 Subject: [PATCH 17/34] add refs --- docs/refs.bib | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/docs/refs.bib b/docs/refs.bib index e23b276..a910b0b 100644 --- a/docs/refs.bib +++ b/docs/refs.bib @@ -85,4 +85,56 @@ @article{Poletto2004 doi = {10.1364/AO.43.002029}, abstract = {Performances are presented of three classes of imaging slit spectrometers for extended sources with aberration-corrected gratings. A general analytical expression for minimizing off-axis grating aberrations is obtained, and it is demonstrated that these aberrations are minimized when the spectrometer is operated at a magnification higher than unity. Classical designs with toroidal uniform-line-spaced (TULS) or spherical varied-line-space (SVLS) gratings are compared with a new class of designs that utilize toroidal varied-line-space (TVLS) gratings. Although TULS and SVLS designs with two stigmatic points can be designed to operate at near-unity magnification with excellent on-axis spectral and spatial resolutions, they cannot be made to satisfy the general off-axis condition, and so their off-axis performances are not optimum. On the contrary TVLS designs with two stigmatic points can be operated at almost any magnification, thus satisfying the off-axis condition perfectly. Such designs are suitable for imaging spectrometer observations that require an extended field of view.}, } +@ARTICLE{Vernazza1978, + author = {{Vernazza}, J.~E. and {Reeves}, E.~M.}, + title = "{Extreme ultraviolet composite spectra of representative solar features.}", + journal = {\apjs}, + keywords = {Far Ultraviolet Radiation, Line Spectra, Solar Spectra, Spectrum Analysis, Tables (Data), Ultraviolet Spectra, Astronomical Photometry, Solar Corona, Solar Cycles, Solar Physics, Extreme UV:Sun, Solar Spectrum: Line Identifications}, + year = 1978, + month = aug, + volume = {37}, + pages = {485-513}, + doi = {10.1086/190539}, + adsurl = {https://ui.adsabs.harvard.edu/abs/1978ApJS...37..485V}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} +@dataset{Dere1997, + author = {{Dere}, K.~P. and {Landi}, E. and {Mason}, H.~E. and {Monsignori Fossi}, B.~C. and {Young}, P.~R.}, + title = "{VizieR Online Data Catalog: CHIANTI - An Atomic Database For Emission Lines I. (Dere+ 1997)}", + howpublished = {VizieR On-line Data Catalog: J/A+AS/125/149. Originally published in: 1997A\&AS..125..149D}, + year = 1997, + month = apr, + eid = {J/A+AS/125/149}, + adsurl = {https://ui.adsabs.harvard.edu/abs/1997yCat..41250149D}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} +@article{Doschek2004, + doi = {10.1086/379877}, + url = {https://doi.org/10.1086/379877}, + year = {2004}, + month = {jan}, + publisher = {}, + volume = {600}, + number = {2}, + pages = {1061}, + author = {Doschek, G. A. and Feldman, U.}, + title = {Properties of the Lower Transition Region: The Widths of Optically Allowed and Intersystem Spectral Lines}, + journal = {The Astrophysical Journal}, + abstract = {The widths of spectral lines in the ultraviolet (UV) and extreme ultraviolet (EUV) spectral regions that are formed in the solar transition region and corona are usually greater than the optically thin widths due to thermal Doppler broadening calculated under the assumption of ionization equilibrium. Although opacity can explain the widths of some lines, there are a host of optically thin lines for which the excess widths are attributed to nonthermal motions. Interest in these motions for coronal heating theories has led to the measurement and comparison of spectral line profiles/widths throughout the solar UV and EUV spectrum. We find that for the quiet Sun the widths of some optically allowed lower transition region lines, deduced from spectra obtained by the Solar Ultraviolet Measurements of Ultraviolet Radiation (SUMER) spectrometer on the Solar and Heliospheric Observatory (SOHO) spacecraft, are considerably larger than predicted from simply scaling previously measured wavelengths of other lines from the same ion. For example, the O III lines of the multiplet near 834 Å are considerably wider than predicted from the previously measured (from Skylab) width of the optically thin O III 1666.15 Å intersystem line. The excess widths are not due to nonthermal motions, as these are already included in the width of the 1666.15 Å line. In this paper, we analyze the widths of some prominent optically allowed lines and discuss possible causes for discrepancies with previous measurements of intersystem lines.} +} +@ARTICLE{Peter1999, + author = {{Peter}, H.}, + title = "{The Chromosphere in Coronal Holes and the Quiet-Sun Network: an HE I (584 {\r{A}}) Full-Disk Scan by SUMER/SOHO}", + journal = {\apjl}, + keywords = {SUN: SOLAR WIND, SUN: CHROMOSPHERE, SUN: CORONA, Sun: Solar Wind, Sun: Chromosphere, Sun: Corona}, + year = 1999, + month = sep, + volume = {522}, + number = {1}, + pages = {L77-L80}, + doi = {10.1086/312214}, + adsurl = {https://ui.adsabs.harvard.edu/abs/1999ApJ...522L..77P}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + From 2a74ef7c947224ac6061e98dabfcf5452bbbffc3 Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Sun, 26 Apr 2026 16:17:50 -0600 Subject: [PATCH 18/34] black --- esis/flights/f1/spectrum/Mg_X.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esis/flights/f1/spectrum/Mg_X.py b/esis/flights/f1/spectrum/Mg_X.py index 836bd65..9e6716f 100644 --- a/esis/flights/f1/spectrum/Mg_X.py +++ b/esis/flights/f1/spectrum/Mg_X.py @@ -20,4 +20,4 @@ _width = _fwhm / (2 * np.sqrt(2 * np.log(2))) #: Average quiet-sun Doppler width measured by :cite:t:`Doschek2004`. -width_doppler = _width.to(u.km / u.s, equivalencies=u.doppler_optical(wavelength)) \ No newline at end of file +width_doppler = _width.to(u.km / u.s, equivalencies=u.doppler_optical(wavelength)) From c3c9010b1612f9acf48dc9c578a2428ff0ae1a9d Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Sun, 26 Apr 2026 16:19:36 -0600 Subject: [PATCH 19/34] ruff --- esis/flights/f1/__init__.py | 1 + esis/flights/f1/spectrum/Mg_X.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/esis/flights/f1/__init__.py b/esis/flights/f1/__init__.py index 7f7c125..d051720 100644 --- a/esis/flights/f1/__init__.py +++ b/esis/flights/f1/__init__.py @@ -1,5 +1,6 @@ """Models and data associated with the first flight in 2019.""" +from . import spectrum from . import optics from . import nsroc from . import data diff --git a/esis/flights/f1/spectrum/Mg_X.py b/esis/flights/f1/spectrum/Mg_X.py index 9e6716f..78ee8ad 100644 --- a/esis/flights/f1/spectrum/Mg_X.py +++ b/esis/flights/f1/spectrum/Mg_X.py @@ -6,7 +6,7 @@ __all__ = [ "wavelength", "radiance", - "fwhm", + "width_doppler", ] #: Rest wavelength calculated by the Chianti Atomic Database :cite:p:`Dere1997`. From d12dfe0bd11236693494c5471e3b8b6ceebadc51 Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Sun, 26 Apr 2026 18:18:50 -0600 Subject: [PATCH 20/34] fixes --- esis/flights/f1/spectrum/Mg_X.py | 4 +++- esis/flights/f1/spectrum/O_V.py | 4 +++- esis/flights/f1/spectrum/__init__.py | 2 ++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/esis/flights/f1/spectrum/Mg_X.py b/esis/flights/f1/spectrum/Mg_X.py index 78ee8ad..54fb30f 100644 --- a/esis/flights/f1/spectrum/Mg_X.py +++ b/esis/flights/f1/spectrum/Mg_X.py @@ -19,5 +19,7 @@ _width = _fwhm / (2 * np.sqrt(2 * np.log(2))) +_eq = u.doppler_optical(wavelength) + #: Average quiet-sun Doppler width measured by :cite:t:`Doschek2004`. -width_doppler = _width.to(u.km / u.s, equivalencies=u.doppler_optical(wavelength)) +width_doppler = (wavelength + _width).to(u.km / u.s, equivalencies=_eq) diff --git a/esis/flights/f1/spectrum/O_V.py b/esis/flights/f1/spectrum/O_V.py index 9573b94..cbd7288 100644 --- a/esis/flights/f1/spectrum/O_V.py +++ b/esis/flights/f1/spectrum/O_V.py @@ -19,5 +19,7 @@ _width = _fwhm / (2 * np.sqrt(2 * np.log(2))) +_eq = u.doppler_optical(wavelength) + #: Average quiet-sun Doppler width measured by :cite:t:`Doschek2004`. -width_doppler = _width.to(u.km / u.s, equivalencies=u.doppler_optical(wavelength)) +width_doppler = (wavelength + _width).to(u.km / u.s, equivalencies=_eq) diff --git a/esis/flights/f1/spectrum/__init__.py b/esis/flights/f1/spectrum/__init__.py index 19fc79d..8ba9b89 100644 --- a/esis/flights/f1/spectrum/__init__.py +++ b/esis/flights/f1/spectrum/__init__.py @@ -2,8 +2,10 @@ from . import O_V from . import Mg_X +from . import He_I __all__ = [ "O_V", "Mg_X", + "He_I", ] From 1a26ddc6ea2e742a86ddb90b39d52c7805009243 Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Sun, 26 Apr 2026 23:54:06 -0600 Subject: [PATCH 21/34] Added `esis.flights.f1.data.synth.scene_aia()` function --- esis/data/synth/_scene_aia/_scene_aia.py | 27 ++-- esis/flights/f1/data/__init__.py | 2 + esis/flights/f1/data/synth/__init__.py | 8 ++ .../f1/data/synth/_scene_aia/__init__.py | 5 + .../f1/data/synth/_scene_aia/_scene_aia.py | 133 ++++++++++++++++++ .../data/synth/_scene_aia/_scene_aia_test.py | 38 +++++ esis/flights/f1/spectrum/He_I.py | 2 +- esis/flights/f1/spectrum/Mg_X.py | 2 +- esis/flights/f1/spectrum/O_V.py | 2 +- 9 files changed, 207 insertions(+), 12 deletions(-) create mode 100644 esis/flights/f1/data/synth/__init__.py create mode 100644 esis/flights/f1/data/synth/_scene_aia/__init__.py create mode 100644 esis/flights/f1/data/synth/_scene_aia/_scene_aia.py create mode 100644 esis/flights/f1/data/synth/_scene_aia/_scene_aia_test.py diff --git a/esis/data/synth/_scene_aia/_scene_aia.py b/esis/data/synth/_scene_aia/_scene_aia.py index 721c9ab..0348427 100644 --- a/esis/data/synth/_scene_aia/_scene_aia.py +++ b/esis/data/synth/_scene_aia/_scene_aia.py @@ -25,9 +25,10 @@ def scene_aia( num_velocity: int = 1, num_std: float = 3, user_email: None | str = None, + limit: None | int = None, ): r""" - Create a synthetic solar scene using AIA images. + Create a synthetic solar scene composed of AIA images. AIA images from channels `wavelength_aia` over a supplied time range are used to represent estimates of images at `wavelength_new`. @@ -45,12 +46,11 @@ def scene_aia( The wavelength label of the AIA channel. wavelength_new The rest wavelength of each spectral line in the synthetic scene replacing - wavelength AIA. - Axes of - wavelength_new should be aligned to the axes of wavelength_aia. + `wavelength_aia`. + Elements of `wavelength_new` should correspond to the elements of . radiance The average radiance of each spectral line in the synthetic scene in - units of :math:`\text{erg} \text{cm}^{-2} \text{sr}^{-1} \text{s}^{-1}.` + units of :math:`\text{erg}\,\text{cm}^{-2}\,\text{sr}^{-1}\,\text{s}^{-1}.` width_doppler The average standard deviation of each spectral line in the synthetic scene. axis_time @@ -59,17 +59,20 @@ def scene_aia( The logical axis corresponding to changes in detector :math:`x`-coordinate. axis_detector_y The logical axis corresponding to changes in detector :math:`y`-coordinate. + axis_velocity + The logical axis corresponding to changes in line-of-sight velocity. num_velocity The number of velocity bins in the synthetic scene. num_std The size of the domain for each spectral line in standard deviation units. - user_email # noqa: E501 + user_email An email address used to notify the user that their JSOC request is complete. This email must be registered with JSOC before using this function. If :obj:`None`, the value is taken from the ``JSOC_EMAIL`` environment variable. - + limit + The maximum number of files to download per wavelength. """ velocity_max = width_doppler * num_std @@ -86,7 +89,7 @@ def scene_aia( wavelength = (1 + velocity / const.c) * wavelength_new - obs = sdo.aia.Filtergram.from_time_range( + obs = sdo.aia.open( time_start=time_start, time_stop=time_stop, wavelength=wavelength_aia, @@ -94,10 +97,16 @@ def scene_aia( axis_time=axis_time, axis_detector_x=axis_detector_x, axis_detector_y=axis_detector_y, + limit=limit, ) axis_detector_xy = axis_detector_x, axis_detector_y - outputs = radiance * obs.outputs / obs.outputs.mean(axis_detector_xy) + crop = { + axis_detector_x: slice(1024, 1024 + 2048), + axis_detector_y: slice(1024, 1024 + 2048), + } + + outputs = radiance * obs.outputs / obs.outputs[crop].mean(axis_detector_xy) delta_lambda = np.diff(wavelength, axis=axis_velocity) outputs = outputs * gaussian / delta_lambda diff --git a/esis/flights/f1/data/__init__.py b/esis/flights/f1/data/__init__.py index 94eb471..481e255 100644 --- a/esis/flights/f1/data/__init__.py +++ b/esis/flights/f1/data/__init__.py @@ -3,9 +3,11 @@ from ._fits import path_fits from ._level_0 import level_0 from ._level_1 import level_1 +from . import synth __all__ = [ "path_fits", "level_0", "level_1", + "synth", ] diff --git a/esis/flights/f1/data/synth/__init__.py b/esis/flights/f1/data/synth/__init__.py new file mode 100644 index 0000000..ef594ae --- /dev/null +++ b/esis/flights/f1/data/synth/__init__.py @@ -0,0 +1,8 @@ +""" +Create synthetic solar scenes that can be observed with an instrument model. +""" +from ._scene_aia import scene_aia + +__all__ = [ + "scene_aia", +] diff --git a/esis/flights/f1/data/synth/_scene_aia/__init__.py b/esis/flights/f1/data/synth/_scene_aia/__init__.py new file mode 100644 index 0000000..a15754d --- /dev/null +++ b/esis/flights/f1/data/synth/_scene_aia/__init__.py @@ -0,0 +1,5 @@ +from ._scene_aia import scene_aia + +__all__ = [ + "scene_aia", +] diff --git a/esis/flights/f1/data/synth/_scene_aia/_scene_aia.py b/esis/flights/f1/data/synth/_scene_aia/_scene_aia.py new file mode 100644 index 0000000..b90017a --- /dev/null +++ b/esis/flights/f1/data/synth/_scene_aia/_scene_aia.py @@ -0,0 +1,133 @@ +import astropy.units as u +import astropy.time +import numpy as np +import named_arrays as na +import esis +from ... import level_1 +from ....spectrum import O_V, Mg_X, He_I + +__all__ = [ + "scene_aia", +] + +def scene_aia( + time_start: None | astropy.time.Time = None, + time_stop: None | astropy.time.Time = None, + axis_time: str = "time", + axis_detector_x: str = "detector_x", + axis_detector_y: str = "detector_y", + axis_wavelength: str = "wavelength", + axis_velocity: str = "velocity", + num_velocity: int = 1, + num_std: float = 3, + user_email: None | str = None, + limit: None | int = None, +): + r""" + A synthetic solar scene composed of AIA images captured during the flight. + + This function plugs the spectral line properties in + :mod:`esis.flights.f1.spectrum` into :func:`esis.data.synth.scene_aia` + to produce the synthic scene. + + Only the brightest three lines in the ESIS passband are recreated: + :math:`\text{O\,V} 630\,\AA`, :math:`\text{Mg\,X} 609\,\AA`, and + :math:`\text{He\,I} 584\,\AA`. + + Parameters + ---------- + time_start + The start time of the AIA observations. + If :obj:`None`, the start time of the ESIS Level-1 observations is used. + time_stop + The stop time of the AIA observations. + If :obj:`None`, the stop time of the ESIS Level-1 observations is used. + axis_time + The logical axis corresponding to changes in time. + axis_detector_x + The logical axis corresponding to changes in detector :math:`x`-coordinate. + axis_detector_y + The logical axis corresponding to changes in detector :math:`y`-coordinate. + axis_velocity + The logical axis corresponding to changes in line-of-sight velocity. + num_velocity + The number of velocity bins in the synthetic scene. + num_std + The size of the domain for each spectral line in standard deviation units. + user_email + An email address used to notify the user that their JSOC request + is complete. + This email must be registered with JSOC before using this function. + If :obj:`None`, the value is taken from the ``JSOC_EMAIL`` + environment variable. + limit + The maximum number of files to download per wavelength. + """ + + l1 = None + + if time_start is None: + if l1 is None: + l1 = level_1() + time_start = l1.inputs.time_start[{l1.axis_time: 0}].ndarray.mean() + + if time_stop is None: + if l1 is None: + l1 = level_1() + time_stop = l1.inputs.time_end[{l1.axis_time: ~0}].ndarray.mean() + + wavelength_aia = [304, 193, 304] * u.AA + + wavelength_esis = [ + O_V.wavelength, + Mg_X.wavelength, + He_I.wavelength, + ] + + radiance_esis = [ + O_V.radiance, + Mg_X.radiance, + He_I.radiance, + ] + + width_esis = [ + O_V.width_doppler, + Mg_X.width_doppler, + He_I.width_doppler, + ] + + wavelength_aia = na.ScalarArray(wavelength_aia, axis_wavelength) + wavelength_esis = na.stack(wavelength_esis, axis_wavelength) + radiance_esis = na.stack(radiance_esis, axis_wavelength) + width_esis = na.stack(width_esis, axis_wavelength) + + result = esis.data.synth.scene_aia( + time_start=time_start, + time_stop=time_stop, + wavelength_aia=wavelength_aia, + wavelength_new=wavelength_esis, + radiance=radiance_esis, + width_doppler=width_esis, + axis_time=axis_time, + axis_detector_x=axis_detector_x, + axis_detector_y=axis_detector_y, + axis_velocity=axis_velocity, + num_velocity=num_velocity, + num_std=num_std, + user_email=user_email, + limit=limit, + ) + + shape = result.outputs.shape + + num_x = shape[axis_detector_x] + num_y = shape[axis_detector_y] + + crop = { + axis_detector_x: slice(num_x // 3, 2 * num_x // 3), + axis_detector_y: slice(num_y // 3, 2 * num_y // 3), + } + + result = result[crop] + + return result diff --git a/esis/flights/f1/data/synth/_scene_aia/_scene_aia_test.py b/esis/flights/f1/data/synth/_scene_aia/_scene_aia_test.py new file mode 100644 index 0000000..6bccd9b --- /dev/null +++ b/esis/flights/f1/data/synth/_scene_aia/_scene_aia_test.py @@ -0,0 +1,38 @@ +import pytest +import astropy.units as u +import named_arrays as na +import numpy as np +import esis +from esis.flights.f1.spectrum import O_V, Mg_X, He_I + + +@pytest.mark.parametrize("num_velocity", [3]) +def test_scene_aia( + num_velocity: int, +): + + axis_x = "detector_x" + axis_y = "detector_y" + axis_xy = (axis_x, axis_y) + axis_wavelength = "wavelength" + axis_velocity = "velocity" + + limit = 1 + + result = esis.flights.f1.data.synth.scene_aia( + axis_detector_x=axis_x, + axis_detector_y=axis_y, + axis_wavelength=axis_wavelength, + axis_velocity=axis_velocity, + num_velocity=num_velocity, + limit=limit, + ) + + assert result.shape[axis_velocity] == num_velocity + assert result.outputs.unit.is_equivalent(u.erg / u.cm**2 / u.sr / u.AA / u.s) + + delta_lambda = np.diff(result.inputs.wavelength, axis=axis_velocity) + radiance = (result.outputs * delta_lambda).sum(axis_velocity).mean(axis_xy) + assert np.allclose(radiance[{axis_wavelength: 0}], O_V.radiance, rtol=1e-1) + assert np.allclose(radiance[{axis_wavelength: 1}], Mg_X.radiance, rtol=1e-1) + assert np.allclose(radiance[{axis_wavelength: 2}], He_I.radiance, rtol=1e-1) diff --git a/esis/flights/f1/spectrum/He_I.py b/esis/flights/f1/spectrum/He_I.py index 075b8dc..bd60b52 100644 --- a/esis/flights/f1/spectrum/He_I.py +++ b/esis/flights/f1/spectrum/He_I.py @@ -1,4 +1,4 @@ -r"""Properties of the :math:`\text{He\,\textsc{i}} 584\,\AA` spectral line.""" +r"""Properties of the :math:`\text{He\,I} 584\,\AA` spectral line.""" import astropy.units as u diff --git a/esis/flights/f1/spectrum/Mg_X.py b/esis/flights/f1/spectrum/Mg_X.py index 54fb30f..01d8d00 100644 --- a/esis/flights/f1/spectrum/Mg_X.py +++ b/esis/flights/f1/spectrum/Mg_X.py @@ -1,4 +1,4 @@ -r"""Properties of the :math:`\text{Mg,\textsc{x}} 609\,\AA` spectral line.""" +r"""Properties of the :math:`\text{Mg\,X} 609\,\AA` spectral line.""" import numpy as np import astropy.units as u diff --git a/esis/flights/f1/spectrum/O_V.py b/esis/flights/f1/spectrum/O_V.py index cbd7288..00202d5 100644 --- a/esis/flights/f1/spectrum/O_V.py +++ b/esis/flights/f1/spectrum/O_V.py @@ -1,4 +1,4 @@ -r"""Properties of the :math:`\text{O\,\textsc{v}} 630\,\AA` spectral line.""" +r"""Properties of the :math:`\text{O\,V} 630\,\AA` spectral line.""" import numpy as np import astropy.units as u From b7b6b10cb95e9de5f01cc14fbc7fdef6b2759d64 Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Sun, 26 Apr 2026 23:56:33 -0600 Subject: [PATCH 22/34] Added AIA image simulation example --- docs/index.rst | 1 + docs/reports/aia-image-simulation.ipynb | 192 ++++++++++++++++++++++++ pyproject.toml | 2 +- 3 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 docs/reports/aia-image-simulation.ipynb diff --git a/docs/index.rst b/docs/index.rst index 58b9f48..d558d74 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -98,6 +98,7 @@ Flight 1 (2019) reports/point-spread-function reports/throughput + reports/aia-image-simulation reports/level-0 reports/level-1 diff --git a/docs/reports/aia-image-simulation.ipynb b/docs/reports/aia-image-simulation.ipynb new file mode 100644 index 0000000..52c054f --- /dev/null +++ b/docs/reports/aia-image-simulation.ipynb @@ -0,0 +1,192 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "0", + "metadata": {}, + "outputs": [], + "source": [ + "%reload_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import named_arrays as na\n", + "import esis" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "axis_time = \"time\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3", + "metadata": {}, + "outputs": [], + "source": [ + "axis_velocity = \"velocity\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "axis_x = \"detector_x\"\n", + "axis_y = \"detector_y\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5", + "metadata": {}, + "outputs": [], + "source": [ + "scene = esis.flights.f1.data.synth.scene_aia(\n", + " axis_time=axis_time,\n", + " axis_detector_x=axis_x,\n", + " axis_detector_y=axis_y,\n", + " axis_velocity=axis_velocity,\n", + " limit=1,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "scene.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "scene.inputs.time" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], + "source": [ + "instrument = esis.flights.f1.optics.design_single(num_distribution=0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "pupil = na.Cartesian2dVectorLinearSpace(\n", + " start=-1,\n", + " stop=1,\n", + " axis=na.Cartesian2dVectorArray(\"pupil_x\", \"pupil_y\"),\n", + " num=2,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", + "images = instrument.system.image(\n", + " scene=scene[{axis_time: 0}],\n", + " pupil=pupil,\n", + " axis_wavelength=axis_velocity,\n", + " axis_field=(axis_x, axis_y),\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11", + "metadata": {}, + "outputs": [], + "source": [ + "images.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(\n", + " figsize=(11, 5),\n", + " constrained_layout=True,\n", + ")\n", + "vmax = images.outputs.percentile(99.9).value\n", + "img = na.plt.pcolormesh(\n", + " images.inputs.position.x,\n", + " images.inputs.position.y,\n", + " C=images.outputs.sum((\"wavelength\", axis_velocity)).value,\n", + " cmap=\"gray\",\n", + " vmin=0,\n", + " vmax=vmax,\n", + ")\n", + "ax.set_xticks([])\n", + "ax.set_yticks([])\n", + "ax.set_aspect(\"equal\")\n", + "plt.colorbar(img.ndarray.item(), label=f\"signal ({images.outputs.unit})\");" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/pyproject.toml b/pyproject.toml index 76a036a..83f42c2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ dependencies = [ "named-arrays~=1.0", "optika~=1.1", "msfc-ccd~=1.0", - "solar-dynamics-observatory~=0.1", + "solar-dynamics-observatory~=0.2", ] dynamic = ["version"] From 4b2b70f5eddfd3176171f3bc09476774ff491d61 Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Sun, 26 Apr 2026 23:58:49 -0600 Subject: [PATCH 23/34] black --- esis/flights/f1/data/synth/__init__.py | 1 + esis/flights/f1/data/synth/_scene_aia/_scene_aia.py | 1 + 2 files changed, 2 insertions(+) diff --git a/esis/flights/f1/data/synth/__init__.py b/esis/flights/f1/data/synth/__init__.py index ef594ae..8737184 100644 --- a/esis/flights/f1/data/synth/__init__.py +++ b/esis/flights/f1/data/synth/__init__.py @@ -1,6 +1,7 @@ """ Create synthetic solar scenes that can be observed with an instrument model. """ + from ._scene_aia import scene_aia __all__ = [ diff --git a/esis/flights/f1/data/synth/_scene_aia/_scene_aia.py b/esis/flights/f1/data/synth/_scene_aia/_scene_aia.py index b90017a..6827a0b 100644 --- a/esis/flights/f1/data/synth/_scene_aia/_scene_aia.py +++ b/esis/flights/f1/data/synth/_scene_aia/_scene_aia.py @@ -10,6 +10,7 @@ "scene_aia", ] + def scene_aia( time_start: None | astropy.time.Time = None, time_stop: None | astropy.time.Time = None, From 9645a1811f14489a38a820638cee588646889e3a Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Mon, 27 Apr 2026 00:01:24 -0600 Subject: [PATCH 24/34] ruff --- esis/flights/f1/data/synth/__init__.py | 4 +--- esis/flights/f1/data/synth/_scene_aia/_scene_aia.py | 4 +--- esis/flights/f1/data/synth/_scene_aia/_scene_aia_test.py | 1 - 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/esis/flights/f1/data/synth/__init__.py b/esis/flights/f1/data/synth/__init__.py index 8737184..61296f2 100644 --- a/esis/flights/f1/data/synth/__init__.py +++ b/esis/flights/f1/data/synth/__init__.py @@ -1,6 +1,4 @@ -""" -Create synthetic solar scenes that can be observed with an instrument model. -""" +"""Create synthetic solar scenes that can be observed with an instrument model.""" from ._scene_aia import scene_aia diff --git a/esis/flights/f1/data/synth/_scene_aia/_scene_aia.py b/esis/flights/f1/data/synth/_scene_aia/_scene_aia.py index 6827a0b..5eaa680 100644 --- a/esis/flights/f1/data/synth/_scene_aia/_scene_aia.py +++ b/esis/flights/f1/data/synth/_scene_aia/_scene_aia.py @@ -1,6 +1,5 @@ import astropy.units as u import astropy.time -import numpy as np import named_arrays as na import esis from ... import level_1 @@ -25,7 +24,7 @@ def scene_aia( limit: None | int = None, ): r""" - A synthetic solar scene composed of AIA images captured during the flight. + Load a synthetic solar scene composed of AIA images captured during the flight. This function plugs the spectral line properties in :mod:`esis.flights.f1.spectrum` into :func:`esis.data.synth.scene_aia` @@ -64,7 +63,6 @@ def scene_aia( limit The maximum number of files to download per wavelength. """ - l1 = None if time_start is None: diff --git a/esis/flights/f1/data/synth/_scene_aia/_scene_aia_test.py b/esis/flights/f1/data/synth/_scene_aia/_scene_aia_test.py index 6bccd9b..2463f64 100644 --- a/esis/flights/f1/data/synth/_scene_aia/_scene_aia_test.py +++ b/esis/flights/f1/data/synth/_scene_aia/_scene_aia_test.py @@ -1,6 +1,5 @@ import pytest import astropy.units as u -import named_arrays as na import numpy as np import esis from esis.flights.f1.spectrum import O_V, Mg_X, He_I From 24fac51fbfc26c4e52b90a5881674b4b5da5fb42 Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Mon, 27 Apr 2026 12:59:52 -0600 Subject: [PATCH 25/34] odc environment variable --- .github/workflows/docs.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index f7ace25..7a69da7 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -42,6 +42,8 @@ jobs: pip install -e .[doc] - name: Build with Sphinx + env: + JSOC_EMAIL: "roytsmart@gmail.com" run: | cd ./docs make html From b3923e92a8c382524f4a34623d5e76277e9e595a Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Mon, 27 Apr 2026 13:00:40 -0600 Subject: [PATCH 26/34] remove old test --- esis/data/synth/_scene_aia/_scene_aia_test.py | 31 ------------------- 1 file changed, 31 deletions(-) delete mode 100644 esis/data/synth/_scene_aia/_scene_aia_test.py diff --git a/esis/data/synth/_scene_aia/_scene_aia_test.py b/esis/data/synth/_scene_aia/_scene_aia_test.py deleted file mode 100644 index 0493e72..0000000 --- a/esis/data/synth/_scene_aia/_scene_aia_test.py +++ /dev/null @@ -1,31 +0,0 @@ -import pytest -import esis -import astropy.units as u -import named_arrays as na -import numpy as np - - -@pytest.mark.parametrize("num_velocity", [3]) -def test_scene_aia(num_velocity: int): - l1 = esis.flights.f1.data.level_1() - vernazza = 200 * u.erg / u.cm**2 / u.sr / u.s - radiance = na.ScalarArray([1, 0.25] * vernazza, axes="spectral_line") - wavelength = na.ScalarArray([580, 630] * u.AA, axes="spectral_line") - scene_aia = esis.data.synth.scene_aia( - time_start=l1.inputs[dict(time=0, channel=0)].time_start.ndarray, - time_stop=l1.inputs[dict(time=2, channel=0)].time_start.ndarray, - wavelength_aia=na.ScalarArray([304, 193] * u.AA, axes="spectral_line"), - wavelength_new=wavelength, - radiance=radiance, - width_doppler=na.ScalarArray([10, 100] * u.km / u.s, axes="spectral_line"), - num_velocity=num_velocity, - num_std=5, - ) - - assert scene_aia.shape["velocity"] == num_velocity - assert scene_aia.outputs.unit == vernazza.unit / wavelength.unit - - axis_xy = ("detector_x", "detector_y") - delta_lambda = np.diff(scene_aia.inputs.wavelength, axis="velocity") - integrated_radiance = scene_aia.outputs.mean(axis_xy) * delta_lambda - assert np.allclose(integrated_radiance.sum("velocity"), radiance) From a5565427e31cebfc50948c06e8906adea52f5baf Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Mon, 27 Apr 2026 14:00:31 -0600 Subject: [PATCH 27/34] Lots of fixes to docs --- docs/refs.bib | 30 +++ docs/reports/aia-image-simulation.ipynb | 252 ++++++++++++++---- esis/data/synth/_scene_aia/_scene_aia.py | 4 + .../f1/data/synth/_scene_aia/_scene_aia.py | 6 +- esis/flights/f1/spectrum/He_I.py | 4 +- esis/flights/f1/spectrum/Mg_X.py | 4 +- esis/flights/f1/spectrum/O_V.py | 4 +- 7 files changed, 248 insertions(+), 56 deletions(-) diff --git a/docs/refs.bib b/docs/refs.bib index a910b0b..9d51869 100644 --- a/docs/refs.bib +++ b/docs/refs.bib @@ -136,5 +136,35 @@ @ARTICLE{Peter1999 adsurl = {https://ui.adsabs.harvard.edu/abs/1999ApJ...522L..77P}, adsnote = {Provided by the SAO/NASA Astrophysics Data System} } +@ARTICLE{Lemen2012, + author = {{Lemen}, James R. and {Title}, Alan M. and {Akin}, David J. and {Boerner}, Paul F. and {Chou}, Catherine and {Drake}, Jerry F. and {Duncan}, Dexter W. and {Edwards}, Christopher G. and {Friedlaender}, Frank M. and {Heyman}, Gary F. and {Hurlburt}, Neal E. and {Katz}, Noah L. and {Kushner}, Gary D. and {Levay}, Michael and {Lindgren}, Russell W. and {Mathur}, Dnyanesh P. and {McFeaters}, Edward L. and {Mitchell}, Sarah and {Rehse}, Roger A. and {Schrijver}, Carolus J. and {Springer}, Larry A. and {Stern}, Robert A. and {Tarbell}, Theodore D. and {Wuelser}, Jean-Pierre and {Wolfson}, C. Jacob and {Yanari}, Carl and {Bookbinder}, Jay A. and {Cheimets}, Peter N. and {Caldwell}, David and {Deluca}, Edward E. and {Gates}, Richard and {Golub}, Leon and {Park}, Sang and {Podgorski}, William A. and {Bush}, Rock I. and {Scherrer}, Philip H. and {Gummin}, Mark A. and {Smith}, Peter and {Auker}, Gary and {Jerram}, Paul and {Pool}, Peter and {Soufli}, Regina and {Windt}, David L. and {Beardsley}, Sarah and {Clapp}, Matthew and {Lang}, James and {Waltham}, Nicholas}, + title = "{The Atmospheric Imaging Assembly (AIA) on the Solar Dynamics Observatory (SDO)}", + journal = {\solphys}, + keywords = {Solar corona, Solar instrumentation, Solar imaging, Extreme ultraviolet}, + year = 2012, + month = jan, + volume = {275}, + number = {1-2}, + pages = {17-40}, + doi = {10.1007/s11207-011-9776-8}, + adsurl = {https://ui.adsabs.harvard.edu/abs/2012SoPh..275...17L}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} +@ARTICLE{Pesnell2012, + author = {{Pesnell}, W. Dean and {Thompson}, B.~J. and {Chamberlin}, P.~C.}, + title = "{The Solar Dynamics Observatory (SDO)}", + journal = {\solphys}, + keywords = {SDO, Solar cycle, Helioseismology, Coronal, Space weather}, + year = 2012, + month = jan, + volume = {275}, + number = {1-2}, + pages = {3-15}, + doi = {10.1007/s11207-011-9841-3}, + adsurl = {https://ui.adsabs.harvard.edu/abs/2012SoPh..275....3P}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + + diff --git a/docs/reports/aia-image-simulation.ipynb b/docs/reports/aia-image-simulation.ipynb index 52c054f..1284c05 100644 --- a/docs/reports/aia-image-simulation.ipynb +++ b/docs/reports/aia-image-simulation.ipynb @@ -1,21 +1,34 @@ { "cells": [ { - "cell_type": "code", - "execution_count": null, + "cell_type": "raw", "id": "0", - "metadata": {}, - "outputs": [], + "metadata": { + "editable": true, + "raw_mimetype": "text/x-rst", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, "source": [ - "%reload_ext autoreload\n", - "%autoreload 2" + "Image Simulation Using AIA Observations\n", + "---------------------------------------\n", + "The Atmospheric Imaging Assembly (AIA) :cite:p:`Lemen2012` on the Solar Dynamics Observatory :cite:p:`Pesnell2012` is a NASA satellite that continuously observes the Sun in extreme ultraviolet (EUV). Since some of the EUV channels that AIA observes are close in temperature to the lines that ESIS observes, we can use AIA observations to simulate ESIS images.\n", + "In this notebook, we select the AIA images from during the ESIS-I 2019 flight and simulate sythetic images using the ideal ESIS design." ] }, { "cell_type": "code", "execution_count": null, "id": "1", - "metadata": {}, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", @@ -23,42 +36,127 @@ "import esis" ] }, + { + "cell_type": "raw", + "id": "2", + "metadata": { + "editable": true, + "raw_mimetype": "text/x-rst", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "Define the logical axis corresponding to changes in time." + ] + }, { "cell_type": "code", "execution_count": null, - "id": "2", - "metadata": {}, + "id": "3", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, "outputs": [], "source": [ "axis_time = \"time\"" ] }, + { + "cell_type": "raw", + "id": "4", + "metadata": { + "editable": true, + "raw_mimetype": "text/x-rst", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "Define the logical axis corresponding to changes in line-of-sight velocity." + ] + }, { "cell_type": "code", "execution_count": null, - "id": "3", - "metadata": {}, + "id": "5", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, "outputs": [], "source": [ "axis_velocity = \"velocity\"" ] }, + { + "cell_type": "raw", + "id": "6", + "metadata": { + "editable": true, + "raw_mimetype": "text/x-rst", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "Define the logical axes corresponding to changes in position on the disk of the Sun." + ] + }, { "cell_type": "code", "execution_count": null, - "id": "4", - "metadata": {}, + "id": "7", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, "outputs": [], "source": [ "axis_x = \"detector_x\"\n", "axis_y = \"detector_y\"" ] }, + { + "cell_type": "raw", + "id": "8", + "metadata": { + "editable": true, + "raw_mimetype": "text/x-rst", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "Load the sythetic scene composed of AIA images.\n", + "Here, we use the :func:`esis.flights.f1.data.synth.scene_aia` function, which recreates the brightest three lines in the ESIS passband." + ] + }, { "cell_type": "code", "execution_count": null, - "id": "5", - "metadata": {}, + "id": "9", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, "outputs": [], "source": [ "scene = esis.flights.f1.data.synth.scene_aia(\n", @@ -67,44 +165,69 @@ " axis_detector_y=axis_y,\n", " axis_velocity=axis_velocity,\n", " limit=1,\n", - ")" + ")\n", + "scene.shape" ] }, { - "cell_type": "code", - "execution_count": null, - "id": "6", - "metadata": {}, - "outputs": [], + "cell_type": "raw", + "id": "10", + "metadata": { + "editable": true, + "raw_mimetype": "text/x-rst", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, "source": [ - "scene.shape" + "Load a single channel of the ideal ESIS optical model." ] }, { "cell_type": "code", "execution_count": null, - "id": "7", - "metadata": {}, + "id": "11", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, "outputs": [], "source": [ - "scene.inputs.time" + "instrument = esis.flights.f1.optics.design_single(num_distribution=0)" ] }, { - "cell_type": "code", - "execution_count": null, - "id": "8", - "metadata": {}, - "outputs": [], + "cell_type": "raw", + "id": "12", + "metadata": { + "editable": true, + "raw_mimetype": "text/x-rst", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, "source": [ - "instrument = esis.flights.f1.optics.design_single(num_distribution=0)" + "Define a grid of pupil positions with only one cell per field angle.\n", + "This is done so that tutorial has a reasonable runtime.\n", + "for production-ready images, it is ideal to trace more pupil positions." ] }, { "cell_type": "code", "execution_count": null, - "id": "9", - "metadata": {}, + "id": "13", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, "outputs": [], "source": [ "pupil = na.Cartesian2dVectorLinearSpace(\n", @@ -115,11 +238,32 @@ ")" ] }, + { + "cell_type": "raw", + "id": "14", + "metadata": { + "editable": true, + "raw_mimetype": "text/x-rst", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "Image the sythetic scene using our single ESIS channel." + ] + }, { "cell_type": "code", "execution_count": null, - "id": "10", - "metadata": {}, + "id": "15", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, "outputs": [], "source": [ "%%time\n", @@ -128,24 +272,36 @@ " pupil=pupil,\n", " axis_wavelength=axis_velocity,\n", " axis_field=(axis_x, axis_y),\n", - ")" + ")\n", + "images.shape" ] }, { - "cell_type": "code", - "execution_count": null, - "id": "11", - "metadata": {}, - "outputs": [], + "cell_type": "raw", + "id": "16", + "metadata": { + "editable": true, + "raw_mimetype": "text/x-rst", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, "source": [ - "images.shape" + "Display the simulated image." ] }, { "cell_type": "code", "execution_count": null, - "id": "12", - "metadata": {}, + "id": "17", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, "outputs": [], "source": [ "fig, ax = plt.subplots(\n", @@ -170,21 +326,21 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", - "version": 2 + "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.6" + "pygments_lexer": "ipython3", + "version": "3.13.3" } }, "nbformat": 4, diff --git a/esis/data/synth/_scene_aia/_scene_aia.py b/esis/data/synth/_scene_aia/_scene_aia.py index 0348427..2c48514 100644 --- a/esis/data/synth/_scene_aia/_scene_aia.py +++ b/esis/data/synth/_scene_aia/_scene_aia.py @@ -73,6 +73,10 @@ def scene_aia( environment variable. limit The maximum number of files to download per wavelength. + + See Also + -------- + :func:`esis.flights.f1.data.synth.scene_aia`: A wrapper around this function for ESIS-I. """ velocity_max = width_doppler * num_std diff --git a/esis/flights/f1/data/synth/_scene_aia/_scene_aia.py b/esis/flights/f1/data/synth/_scene_aia/_scene_aia.py index 5eaa680..786d230 100644 --- a/esis/flights/f1/data/synth/_scene_aia/_scene_aia.py +++ b/esis/flights/f1/data/synth/_scene_aia/_scene_aia.py @@ -31,8 +31,8 @@ def scene_aia( to produce the synthic scene. Only the brightest three lines in the ESIS passband are recreated: - :math:`\text{O\,V} 630\,\AA`, :math:`\text{Mg\,X} 609\,\AA`, and - :math:`\text{He\,I} 584\,\AA`. + :math:`\text{O\,V}\;630\,\AA`, :math:`\text{Mg\,X}\;609\,\AA`, and + :math:`\text{He\,I}\;584\,\AA`. Parameters ---------- @@ -48,6 +48,8 @@ def scene_aia( The logical axis corresponding to changes in detector :math:`x`-coordinate. axis_detector_y The logical axis corresponding to changes in detector :math:`y`-coordinate. + axis_wavelength + The logical axis corresponding to changing spectral line. axis_velocity The logical axis corresponding to changes in line-of-sight velocity. num_velocity diff --git a/esis/flights/f1/spectrum/He_I.py b/esis/flights/f1/spectrum/He_I.py index bd60b52..5783a58 100644 --- a/esis/flights/f1/spectrum/He_I.py +++ b/esis/flights/f1/spectrum/He_I.py @@ -1,4 +1,4 @@ -r"""Properties of the :math:`\text{He\,I} 584\,\AA` spectral line.""" +r"""Properties of the :math:`\text{He\,I}\;584\,\AA` spectral line.""" import astropy.units as u @@ -11,7 +11,7 @@ #: Rest wavelength calculated by the Chianti Atomic Database :cite:p:`Dere1997`. wavelength = 584.334 * u.AA -#: Average quiet-sun radiance measured by :cite:t:`Vernazza1977`. +#: Average quiet-sun radiance measured by :cite:t:`Vernazza1978`. radiance = 544.98 * u.erg / u.cm**2 / u.sr / u.s #: Average quiet-sun Doppler width measured by :cite:t:`Peter1999`. diff --git a/esis/flights/f1/spectrum/Mg_X.py b/esis/flights/f1/spectrum/Mg_X.py index 01d8d00..6aff841 100644 --- a/esis/flights/f1/spectrum/Mg_X.py +++ b/esis/flights/f1/spectrum/Mg_X.py @@ -1,4 +1,4 @@ -r"""Properties of the :math:`\text{Mg\,X} 609\,\AA` spectral line.""" +r"""Properties of the :math:`\text{Mg\,X}\;609\,\AA` spectral line.""" import numpy as np import astropy.units as u @@ -15,7 +15,7 @@ #: Average quiet-sun radiance measured by :cite:t:`Vernazza1978`. radiance = 125.05 * u.erg / u.cm**2 / u.sr / u.s -_fwhm = 0.138 * u.mAA +_fwhm = 0.138 * u.AA _width = _fwhm / (2 * np.sqrt(2 * np.log(2))) diff --git a/esis/flights/f1/spectrum/O_V.py b/esis/flights/f1/spectrum/O_V.py index 00202d5..47d637e 100644 --- a/esis/flights/f1/spectrum/O_V.py +++ b/esis/flights/f1/spectrum/O_V.py @@ -1,4 +1,4 @@ -r"""Properties of the :math:`\text{O\,V} 630\,\AA` spectral line.""" +r"""Properties of the :math:`\text{O\,V}\;630\,\AA` spectral line.""" import numpy as np import astropy.units as u @@ -15,7 +15,7 @@ #: Average quiet-sun radiance measured by :cite:t:`Vernazza1978`. radiance = 334.97 * u.erg / u.cm**2 / u.sr / u.s -_fwhm = 0.129 * u.mAA +_fwhm = 0.129 * u.AA _width = _fwhm / (2 * np.sqrt(2 * np.log(2))) From 15abb0b76a63928d14aedeb534a7ad6f57e83fe9 Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Mon, 27 Apr 2026 14:03:04 -0600 Subject: [PATCH 28/34] ruff --- esis/data/synth/_scene_aia/_scene_aia.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esis/data/synth/_scene_aia/_scene_aia.py b/esis/data/synth/_scene_aia/_scene_aia.py index 2c48514..e6703a2 100644 --- a/esis/data/synth/_scene_aia/_scene_aia.py +++ b/esis/data/synth/_scene_aia/_scene_aia.py @@ -76,7 +76,8 @@ def scene_aia( See Also -------- - :func:`esis.flights.f1.data.synth.scene_aia`: A wrapper around this function for ESIS-I. + :func:`esis.flights.f1.data.synth.scene_aia`: + A wrapper around this function for ESIS-I. """ velocity_max = width_doppler * num_std From 16b205b8e2dc85edde784cf0b1c7eaefdd5236ec Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Mon, 27 Apr 2026 16:21:42 -0600 Subject: [PATCH 29/34] supress nbsphinx warnings --- docs/conf.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 94fbf47..6be3366 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -113,6 +113,10 @@ nbsphinx_execute = 'always' +suppress_warnings = [ + 'nbsphinx', +] + codeautolink_custom_blocks = {"jupyter-execute": None} intersphinx_mapping = { From 7508485d349cc62687260d49b6c213883a0ca8bd Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Mon, 27 Apr 2026 16:24:16 -0600 Subject: [PATCH 30/34] coverage --- esis/flights/f1/data/synth/_scene_aia/_scene_aia.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/esis/flights/f1/data/synth/_scene_aia/_scene_aia.py b/esis/flights/f1/data/synth/_scene_aia/_scene_aia.py index 786d230..f5134eb 100644 --- a/esis/flights/f1/data/synth/_scene_aia/_scene_aia.py +++ b/esis/flights/f1/data/synth/_scene_aia/_scene_aia.py @@ -65,16 +65,12 @@ def scene_aia( limit The maximum number of files to download per wavelength. """ - l1 = None + l1 = level_1() if time_start is None: - if l1 is None: - l1 = level_1() time_start = l1.inputs.time_start[{l1.axis_time: 0}].ndarray.mean() if time_stop is None: - if l1 is None: - l1 = level_1() time_stop = l1.inputs.time_end[{l1.axis_time: ~0}].ndarray.mean() wavelength_aia = [304, 193, 304] * u.AA From b2728a345020cd782783f5ac00e65920fd9ac863 Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Mon, 27 Apr 2026 16:58:52 -0600 Subject: [PATCH 31/34] tutorial improvements --- docs/reports/aia-image-simulation.ipynb | 49 ++++++++++++++++++------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/docs/reports/aia-image-simulation.ipynb b/docs/reports/aia-image-simulation.ipynb index 1284c05..300b4ba 100644 --- a/docs/reports/aia-image-simulation.ipynb +++ b/docs/reports/aia-image-simulation.ipynb @@ -212,15 +212,39 @@ "tags": [] }, "source": [ - "Define a grid of pupil positions with only one cell per field angle.\n", - "This is done so that tutorial has a reasonable runtime.\n", - "for production-ready images, it is ideal to trace more pupil positions." + "Reduce the number of rays traced per field angle so that this tutorial has a reasonable runtime.\n", + "This number should be increased to simulate research-quality images." ] }, { "cell_type": "code", "execution_count": null, "id": "13", + "metadata": {}, + "outputs": [], + "source": [ + "instrument.pupil.num = 2" + ] + }, + { + "cell_type": "raw", + "id": "14", + "metadata": { + "editable": true, + "raw_mimetype": "text/x-rst", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "Also halve the number of pixels to increase the signal-to-noise ratio." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15", "metadata": { "editable": true, "slideshow": { @@ -230,17 +254,15 @@ }, "outputs": [], "source": [ - "pupil = na.Cartesian2dVectorLinearSpace(\n", - " start=-1,\n", - " stop=1,\n", - " axis=na.Cartesian2dVectorArray(\"pupil_x\", \"pupil_y\"),\n", - " num=2,\n", - ")" + "sensor = instrument.camera.sensor\n", + "sensor.width_pixel = 2 * sensor.width_pixel\n", + "sensor.num_pixel_x = sensor.num_pixel_x // 2\n", + "sensor.num_pixel_y = sensor.num_pixel_y // 2" ] }, { "cell_type": "raw", - "id": "14", + "id": "16", "metadata": { "editable": true, "raw_mimetype": "text/x-rst", @@ -256,7 +278,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "17", "metadata": { "editable": true, "slideshow": { @@ -269,7 +291,6 @@ "%%time\n", "images = instrument.system.image(\n", " scene=scene[{axis_time: 0}],\n", - " pupil=pupil,\n", " axis_wavelength=axis_velocity,\n", " axis_field=(axis_x, axis_y),\n", ")\n", @@ -278,7 +299,7 @@ }, { "cell_type": "raw", - "id": "16", + "id": "18", "metadata": { "editable": true, "raw_mimetype": "text/x-rst", @@ -294,7 +315,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "19", "metadata": { "editable": true, "slideshow": { From d80c92d1b63288b3239d9db75acd90ddec5754f9 Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Mon, 27 Apr 2026 19:32:55 -0600 Subject: [PATCH 32/34] filter warnings --- docs/reports/aia-image-simulation.ipynb | 53 +++++++++++++++---------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/docs/reports/aia-image-simulation.ipynb b/docs/reports/aia-image-simulation.ipynb index 300b4ba..3cb87ae 100644 --- a/docs/reports/aia-image-simulation.ipynb +++ b/docs/reports/aia-image-simulation.ipynb @@ -31,14 +31,25 @@ }, "outputs": [], "source": [ + "import warnings\n", "import matplotlib.pyplot as plt\n", "import named_arrays as na\n", "import esis" ] }, { - "cell_type": "raw", + "cell_type": "code", + "execution_count": null, "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "warnings.filterwarnings(\"ignore\")" + ] + }, + { + "cell_type": "raw", + "id": "3", "metadata": { "editable": true, "raw_mimetype": "text/x-rst", @@ -54,7 +65,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3", + "id": "4", "metadata": { "editable": true, "slideshow": { @@ -69,7 +80,7 @@ }, { "cell_type": "raw", - "id": "4", + "id": "5", "metadata": { "editable": true, "raw_mimetype": "text/x-rst", @@ -85,7 +96,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5", + "id": "6", "metadata": { "editable": true, "slideshow": { @@ -100,7 +111,7 @@ }, { "cell_type": "raw", - "id": "6", + "id": "7", "metadata": { "editable": true, "raw_mimetype": "text/x-rst", @@ -116,7 +127,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "8", "metadata": { "editable": true, "slideshow": { @@ -132,7 +143,7 @@ }, { "cell_type": "raw", - "id": "8", + "id": "9", "metadata": { "editable": true, "raw_mimetype": "text/x-rst", @@ -149,7 +160,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "10", "metadata": { "editable": true, "slideshow": { @@ -165,13 +176,12 @@ " axis_detector_y=axis_y,\n", " axis_velocity=axis_velocity,\n", " limit=1,\n", - ")\n", - "scene.shape" + ")" ] }, { "cell_type": "raw", - "id": "10", + "id": "11", "metadata": { "editable": true, "raw_mimetype": "text/x-rst", @@ -187,7 +197,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "12", "metadata": { "editable": true, "slideshow": { @@ -202,7 +212,7 @@ }, { "cell_type": "raw", - "id": "12", + "id": "13", "metadata": { "editable": true, "raw_mimetype": "text/x-rst", @@ -219,7 +229,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -228,7 +238,7 @@ }, { "cell_type": "raw", - "id": "14", + "id": "15", "metadata": { "editable": true, "raw_mimetype": "text/x-rst", @@ -244,7 +254,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "16", "metadata": { "editable": true, "slideshow": { @@ -262,7 +272,7 @@ }, { "cell_type": "raw", - "id": "16", + "id": "17", "metadata": { "editable": true, "raw_mimetype": "text/x-rst", @@ -278,7 +288,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "18", "metadata": { "editable": true, "slideshow": { @@ -293,13 +303,12 @@ " scene=scene[{axis_time: 0}],\n", " axis_wavelength=axis_velocity,\n", " axis_field=(axis_x, axis_y),\n", - ")\n", - "images.shape" + ")" ] }, { "cell_type": "raw", - "id": "18", + "id": "19", "metadata": { "editable": true, "raw_mimetype": "text/x-rst", @@ -315,7 +324,7 @@ { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "20", "metadata": { "editable": true, "slideshow": { From 41c8e60cd74dded1f4813a3b62f9934f338383c4 Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Mon, 27 Apr 2026 19:37:16 -0600 Subject: [PATCH 33/34] more tutorial tweaks --- docs/reports/aia-image-simulation.ipynb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/reports/aia-image-simulation.ipynb b/docs/reports/aia-image-simulation.ipynb index 3cb87ae..b9cc529 100644 --- a/docs/reports/aia-image-simulation.ipynb +++ b/docs/reports/aia-image-simulation.ipynb @@ -154,7 +154,7 @@ }, "source": [ "Load the sythetic scene composed of AIA images.\n", - "Here, we use the :func:`esis.flights.f1.data.synth.scene_aia` function, which recreates the brightest three lines in the ESIS passband." + "Here, we use the :func:`~esis.flights.f1.data.synth.scene_aia` function, which recreates the brightest three lines in the ESIS passband." ] }, { @@ -248,7 +248,7 @@ "tags": [] }, "source": [ - "Also halve the number of pixels to increase the signal-to-noise ratio." + "Also halve the number of pixels in each axis to increase the signal-to-noise ratio of the final image." ] }, { @@ -282,7 +282,7 @@ "tags": [] }, "source": [ - "Image the sythetic scene using our single ESIS channel." + "Image the sythetic scene using our model of the ESIS optical system." ] }, { From c54f5d60e7d01590a306a36111be1bf97b3f4147 Mon Sep 17 00:00:00 2001 From: Roy Smart Date: Mon, 27 Apr 2026 19:39:21 -0600 Subject: [PATCH 34/34] tutorial title --- docs/reports/aia-image-simulation.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reports/aia-image-simulation.ipynb b/docs/reports/aia-image-simulation.ipynb index b9cc529..7634ac4 100644 --- a/docs/reports/aia-image-simulation.ipynb +++ b/docs/reports/aia-image-simulation.ipynb @@ -12,8 +12,8 @@ "tags": [] }, "source": [ - "Image Simulation Using AIA Observations\n", - "---------------------------------------\n", + "Image Simulation\n", + "----------------\n", "The Atmospheric Imaging Assembly (AIA) :cite:p:`Lemen2012` on the Solar Dynamics Observatory :cite:p:`Pesnell2012` is a NASA satellite that continuously observes the Sun in extreme ultraviolet (EUV). Since some of the EUV channels that AIA observes are close in temperature to the lines that ESIS observes, we can use AIA observations to simulate ESIS images.\n", "In this notebook, we select the AIA images from during the ESIS-I 2019 flight and simulate sythetic images using the ideal ESIS design." ]