Skip to content

Commit eea7c90

Browse files
committed
bump msfc-ccd to 0.6.0
1 parent 16fce36 commit eea7c90

16 files changed

Lines changed: 276 additions & 202 deletions

File tree

esis/_caching.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"memory",
66
]
77

8-
_path_cache = pathlib.Path(__file__).parent.parent / ".cache"
8+
_path_cache = pathlib.Path.home() / ".esis/cache"
99

1010
memory = joblib.Memory(location=_path_cache, mmap_mode="r", verbose=0)
1111
"""A representation of the cache which stores intermediate results."""

esis/data/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@
2626
* Cosmic ray removal
2727
"""
2828

29+
from . import abc
2930
from ._level_0 import Level_0
3031
from ._level_1 import Level_1
3132

3233
__all__ = [
34+
"abc",
3335
"Level_0",
3436
"Level_1",
3537
]

esis/data/_level_0/_level_0.py

Lines changed: 2 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import named_arrays as na
1313
import msfc_ccd
1414
import esis
15+
from .. import abc
1516

1617
__all__ = [
1718
"Level_0",
@@ -21,6 +22,7 @@
2122
@dataclasses.dataclass(eq=False, repr=False)
2223
class Level_0(
2324
msfc_ccd.SensorData,
25+
abc.AbstractChannelData,
2426
):
2527
"""
2628
Representation of ESIS Level-0 images, the raw data gathered by the instrument.
@@ -33,12 +35,6 @@ class Level_0(
3335
timeline: None | esis.nsroc.Timeline = None
3436
"""The sequence of NSROC events associated with these images."""
3537

36-
axis_time: str = dataclasses.field(default="time", kw_only=True)
37-
"""The name of the logical axis corresponding to changing time."""
38-
39-
axis_channel: str = dataclasses.field(default="channel", kw_only=True)
40-
"""The name of the logical axis corresponding to the different channels."""
41-
4238
@classmethod
4339
def from_fits(
4440
cls,
@@ -193,164 +189,3 @@ def dark(self) -> Self:
193189
def dark_subtracted(self):
194190
"""Subtract the master :attr:`dark` from each image in the sequence."""
195191
return self - self.dark.outputs
196-
197-
def animate(
198-
self,
199-
ax: None | matplotlib.axes.Axes | na.AbstractArray = None,
200-
cmap: None | str | matplotlib.colors.Colormap = None,
201-
norm: None | str | matplotlib.colors.Normalize = None,
202-
vmin: None | na.ArrayLike = None,
203-
vmax: None | na.ArrayLike = None,
204-
cbar_fraction: float = 0.1,
205-
) -> matplotlib.animation.FuncAnimation:
206-
"""
207-
Create an animation using the frames in this dataset.
208-
209-
Parameters
210-
----------
211-
ax
212-
The :class:`~matplotlib.axes.Axes` instance(s) to use.
213-
If :obj:`None`, a new set of axes will be created.
214-
cmap
215-
The colormap used to map scalar data to colors.
216-
norm
217-
The normalization method used to scale data into the range [0, 1] before
218-
mapping to colors.
219-
vmin
220-
The minimum value of the data range.
221-
If `norm` is :obj:`None`, this parameter will be ignored.
222-
vmax
223-
The maximum value of the data range.
224-
If `norm` is :obj:`None`, this parameter will be ignored.
225-
cbar_fraction
226-
The fraction of the space to use for the colorbar axes.
227-
"""
228-
axis_time = self.axis_time
229-
axis_channel = self.axis_channel
230-
231-
if ax is None:
232-
figwidth = plt.rcParams["figure.figsize"][0]
233-
figwidth_eff = figwidth * (1 - 2 * cbar_fraction)
234-
shape = self.shape
235-
num_channel = shape[axis_channel]
236-
num_x = shape[self.axis_x]
237-
num_y = shape[self.axis_y]
238-
figheight = num_channel * figwidth_eff * num_y / num_x
239-
fig, ax = na.plt.subplots(
240-
axis_rows=axis_channel,
241-
nrows=num_channel,
242-
sharex=True,
243-
sharey=True,
244-
constrained_layout=True,
245-
figsize=(figwidth, figheight),
246-
origin="upper",
247-
)
248-
na.plt.set_xlabel("detector $x$ (pix)", ax=ax[{axis_channel: ~0}])
249-
na.plt.set_ylabel("detector $y$ (pix)", ax=ax)
250-
251-
data = self.outputs
252-
unit = na.unit(data)
253-
254-
if norm is None:
255-
if vmin is None:
256-
vmin = data.percentile(1).ndarray
257-
if vmax is None:
258-
vmax = data.percentile(99).ndarray
259-
260-
if unit is not None:
261-
vmin = vmin.to(unit).value
262-
vmax = vmax.to(unit).value
263-
264-
norm = plt.Normalize(
265-
vmin=vmin,
266-
vmax=vmax,
267-
)
268-
269-
colorizer = plt.Colorizer(
270-
cmap=cmap,
271-
norm=norm,
272-
)
273-
274-
pixel = self.inputs.pixel
275-
276-
ani = na.plt.pcolormovie(
277-
self.inputs.time.mean(axis_channel),
278-
pixel.x,
279-
pixel.y,
280-
C=data,
281-
axis_time=axis_time,
282-
ax=ax,
283-
kwargs_pcolormesh=dict(
284-
colorizer=colorizer,
285-
),
286-
)
287-
na.plt.text(
288-
x=0.5,
289-
y=1.01,
290-
s=self.channel,
291-
transform=na.plt.transAxes(ax),
292-
ax=ax,
293-
ha="center",
294-
va="bottom",
295-
)
296-
na.plt.set_aspect("equal", ax=ax)
297-
298-
plt.colorbar(
299-
mappable=plt.cm.ScalarMappable(colorizer=colorizer),
300-
ax=ax.ndarray,
301-
label=f"signal ({unit:latex_inline})",
302-
fraction=cbar_fraction,
303-
)
304-
305-
return ani
306-
307-
def to_jshtml(
308-
self,
309-
ax: None | matplotlib.axes.Axes | na.AbstractArray = None,
310-
cmap: None | str | matplotlib.colors.Colormap = None,
311-
norm: None | str | matplotlib.colors.Normalize = None,
312-
vmin: None | na.ArrayLike = None,
313-
vmax: None | na.ArrayLike = None,
314-
cbar_fraction: float = 0.1,
315-
) -> IPython.display.HTML:
316-
"""
317-
Create a Javascript animation ready to be displayed in a Jupyter notebook.
318-
319-
Converts the output of :meth:`animate` to Javascript using
320-
:meth:`matplotlib.animation.Animation.to_jshtml`,
321-
and then wraps the html string in :class:`IPython.display.HTML`.
322-
323-
Parameters
324-
----------
325-
ax
326-
The :class:`~matplotlib.axes.Axes` instance(s) to use.
327-
If :obj:`None`, a new set of axes will be created.
328-
cmap
329-
The colormap used to map scalar data to colors.
330-
norm
331-
The normalization method used to scale data into the range [0, 1] before
332-
mapping to colors.
333-
vmin
334-
The minimum value of the data range.
335-
If `norm` is :obj:`None`, this parameter will be ignored.
336-
vmax
337-
The maximum value of the data range.
338-
If `norm` is :obj:`None`, this parameter will be ignored.
339-
cbar_fraction
340-
The fraction of the space to use for the colorbar axes.
341-
"""
342-
ani = self.animate(
343-
ax=ax,
344-
cmap=cmap,
345-
norm=norm,
346-
vmin=vmin,
347-
vmax=vmax,
348-
cbar_fraction=cbar_fraction,
349-
)
350-
351-
result = ani.to_jshtml()
352-
result = IPython.display.HTML(result)
353-
354-
plt.close(ani._fig)
355-
356-
return result

esis/data/_level_0/_level_0_test.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import named_arrays as na
77
from msfc_ccd._images._tests.test_sensor_images import AbstractTestAbstractSensorData
88
import esis
9+
from ..abc._channel_data_test import AbstractTestAbstractChannelData
910

1011

1112
@pytest.mark.parametrize(
@@ -16,6 +17,7 @@
1617
)
1718
class TestLevel_0(
1819
AbstractTestAbstractSensorData,
20+
AbstractTestAbstractChannelData,
1921
):
2022
def test_timeline(self, a: esis.data.Level_0):
2123
result = a.timeline
@@ -63,13 +65,3 @@ def test_dark_subtracted(self, a: esis.data.Level_0):
6365
result = a[index].dark_subtracted
6466
assert isinstance(result, type(a))
6567
assert np.all(result.darks.outputs.mean() < 1 * u.DN)
66-
67-
def test_to_jshtml(self, a: esis.data.Level_0):
68-
index = {
69-
a.axis_time: slice(0, 1),
70-
a.axis_x: slice(0, 100),
71-
a.axis_y: slice(0, 100),
72-
}
73-
a = a[index]
74-
result = a.to_jshtml()
75-
assert isinstance(result, IPython.display.HTML)

esis/data/_level_1/_level_1.py

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from typing import Self
22
import dataclasses
3-
import named_arrays as na
3+
import esis
4+
from .. import abc
45
from .. import Level_0
56

67
__all__ = [
@@ -10,32 +11,23 @@
1011

1112
@dataclasses.dataclass
1213
class Level_1(
13-
na.FunctionArray[
14-
na.TemporalSpectralPositionalVectorArray,
15-
na.ScalarArray,
16-
],
14+
abc.AbstractChannelData,
1715
):
1816
"""
1917
ESIS images represented in terms of photoelectrons collected by the sensor.
2018
2119
This class is intended to be created from an instance of :class:`Level_0`
2220
using the :meth:from_level_0` method.
2321
"""
24-
25-
axis_time: str = dataclasses.field(default="time", kw_only=True)
26-
"""The name of the logical axis corresponding to changing time."""
27-
28-
axis_channel: str = dataclasses.field(default="channel", kw_only=True)
29-
"""The name of the logical axis corresponding to the different channels."""
30-
31-
axis_x: str = dataclasses.field(default="detector_x", kw_only=True)
32-
"""The name of the horizontal axis of the sensor."""
33-
34-
axis_y: str = dataclasses.field(default="detector_y", kw_only=True)
35-
"""The name of the vertical axis of the sensor."""
22+
instrument: None | esis.optics.Instrument = None
23+
"""A model of the optical system associated with these images."""
3624

3725
@classmethod
38-
def from_level_0(cls, a: Level_0) -> Self:
26+
def from_level_0(
27+
cls,
28+
a: Level_0,
29+
instrument: None | esis.optics.Instrument = None,
30+
) -> Self:
3931
"""
4032
Create a new instance of this class from an instance of :class:`Level_0`.
4133
@@ -55,6 +47,8 @@ def from_level_0(cls, a: Level_0) -> Self:
5547
----------
5648
a
5749
An instance of :class:`Level_0` to convert.
50+
instrument
51+
A model of the ESIS instrument to associate with these observations.
5852
"""
5953
taps = a.taps
6054
taps = taps.unbiased
@@ -68,6 +62,7 @@ def from_level_0(cls, a: Level_0) -> Self:
6862
return cls(
6963
inputs=a.inputs,
7064
outputs=a.outputs,
65+
instrument=instrument,
7166
axis_time=a.axis_time,
7267
axis_channel=a.axis_channel,
7368
axis_x=a.axis_x,

esis/data/_level_1/_level_1_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import pytest
2-
from msfc_ccd._images._tests.test_sensor_images import AbstractTestAbstractSensorData
2+
from msfc_ccd._images._tests.test_images import AbstractTestAbstractImageData
33
import esis
44

55

@@ -10,6 +10,6 @@
1010
],
1111
)
1212
class TestLevel_1(
13-
AbstractTestAbstractSensorData,
13+
AbstractTestAbstractImageData,
1414
):
1515
pass

esis/data/abc/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from ._channel_data import AbstractChannelData
2+
3+
__all__ = [
4+
"AbstractChannelData",
5+
]

0 commit comments

Comments
 (0)