eclipse 0.13.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. eclipse/__init__.py +95 -0
  2. eclipse/analysis.py +853 -0
  3. eclipse/atmosphere.py +859 -0
  4. eclipse/band.py +477 -0
  5. eclipse/cli.py +138 -0
  6. eclipse/config.py +1710 -0
  7. eclipse/continuum.py +413 -0
  8. eclipse/data/eis_response/EIS_EffArea_A.004 +61 -0
  9. eclipse/data/eis_response/EIS_EffArea_B.004 +60 -0
  10. eclipse/data/eis_response/EIS_EffArea_B.005 +64 -0
  11. eclipse/data/eis_response/README_dz2025 +50 -0
  12. eclipse/data/eis_response/fit_eis_ea_2023-05-04_smooth.sav +0 -0
  13. eclipse/data/eis_response/source.txt +33 -0
  14. eclipse/data/science_cases.yaml +365 -0
  15. eclipse/data/throughput/grating_reflection_efficiency.dat +25 -0
  16. eclipse/data/throughput/primary_mirror_coating_reflectance.dat +25 -0
  17. eclipse/data/throughput/source.txt +6 -0
  18. eclipse/data/throughput/throughput_aluminium_1000_angstrom.dat +503 -0
  19. eclipse/data/throughput/throughput_aluminium_oxide_1000_angstrom.dat +503 -0
  20. eclipse/data/throughput/throughput_carbon_1000_angstrom.dat +503 -0
  21. eclipse/data_processing.py +904 -0
  22. eclipse/deprecated.py +161 -0
  23. eclipse/eis_calibration.py +573 -0
  24. eclipse/extern/__init__.py +0 -0
  25. eclipse/extern/mpfit.py +2388 -0
  26. eclipse/fitting.py +1734 -0
  27. eclipse/frame.py +594 -0
  28. eclipse/full_frame.py +944 -0
  29. eclipse/main.py +1646 -0
  30. eclipse/monte_carlo.py +660 -0
  31. eclipse/pinhole_diffraction.py +409 -0
  32. eclipse/psf.py +6 -0
  33. eclipse/radiometric.py +1328 -0
  34. eclipse/raster.py +869 -0
  35. eclipse/readout.py +1002 -0
  36. eclipse/resolution_budget.py +108 -0
  37. eclipse/results_file.py +1226 -0
  38. eclipse/sampling.py +351 -0
  39. eclipse/science_cases.py +364 -0
  40. eclipse/synthesis.py +2492 -0
  41. eclipse/synthesis_cli.py +12 -0
  42. eclipse/synthesis_file.py +882 -0
  43. eclipse/utils.py +1145 -0
  44. eclipse-0.13.0.dist-info/METADATA +70 -0
  45. eclipse-0.13.0.dist-info/RECORD +50 -0
  46. eclipse-0.13.0.dist-info/WHEEL +5 -0
  47. eclipse-0.13.0.dist-info/entry_points.txt +7 -0
  48. eclipse-0.13.0.dist-info/licenses/LICENSE +3 -0
  49. eclipse-0.13.0.dist-info/top_level.txt +2 -0
  50. euvst_response/__init__.py +129 -0
eclipse/deprecated.py ADDED
@@ -0,0 +1,161 @@
1
+ """
2
+ Deprecated functions, which warn when called and will be removed in a future release.
3
+
4
+ Each can still be imported from where it was before, such as
5
+ `eclipse.frame.apply_spectral_psf`, so existing scripts still run.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from pathlib import Path
11
+ from typing import Any, Dict
12
+
13
+ import astropy.units as u
14
+ import numpy as np
15
+ from scipy.ndimage import zoom
16
+ from scipy.signal import convolve2d
17
+
18
+ from .radiometric import _spectral_kernel
19
+ from .utils import _warn_outside
20
+
21
+
22
+ def _warn(name: str, reason: str) -> None:
23
+ """Warn that `name` is deprecated, attributed to the first caller outside ECLIPSE."""
24
+ _warn_outside(f"{name} is deprecated and will be removed in a future release: {reason}")
25
+
26
+
27
+
28
+
29
+ def apply_spectral_psf(rows: u.Quantity, telescope, det, slit_width: u.Quantity,
30
+ spectral_psf: str = "quadrature", margin: int = 0) -> u.Quantity:
31
+ """
32
+ Blur a frame's rows with the instrument's spectral response.
33
+
34
+ Along the dispersion a pixel is a row. The response is that of
35
+ :func:`~eclipse.radiometric.apply_focusing_optics_psf` for a
36
+ synthesis through the same slit, on the same rows: for `spectral_psf`
37
+ `"quadrature"` a Gaussian of
38
+ :func:`~eclipse.radiometric.spectral_psf_fwhm`, and for
39
+ `"convolution"` :func:`~eclipse.radiometric.spectral_line_spread`.
40
+ The slit's image is part of the line profile, so a wider slit gives a
41
+ wider response: a FWHM of 2.54 rows for the 0.2 arcsec slit and 3.35
42
+ for the 0.4 arcsec slit.
43
+
44
+ Nothing is assumed beyond the rows given: light blurred past either end
45
+ is lost, as off the edge of a detector, and none comes in. The edge rows
46
+ of a CCD do receive light from just off the chip, and at the butted edge
47
+ the spectrum continues across the gap. To include this, lay the spectrum
48
+ with a `margin` of at least
49
+ :func:`~eclipse.radiometric.spectral_psf_reach` rows past each
50
+ end (:func:`~eclipse.frame.photons_from_lines` and
51
+ :func:`~eclipse.frame.photons_from_spectrum` take one), and pass
52
+ the same margin here. The result is then the chip's own rows. The
53
+ kernel is normalised, so flux that stays within the rows is conserved.
54
+
55
+ Deprecated since 0.12.0: blurring the rows after the light is on them
56
+ moves a line narrower than a row toward the middle of its row, by up to
57
+ a few km/s.
58
+ Give `spectral_psf` and `det` to
59
+ :func:`~eclipse.frame.photons_from_lines` or
60
+ :func:`~eclipse.frame.photons_from_spectrum` instead, which blur
61
+ the light as it is laid onto the rows.
62
+ """
63
+ _warn("apply_spectral_psf",
64
+ "blurring the rows once the light is on them moves a line narrower than a row "
65
+ "toward the middle of its row. Give spectral_psf and det to photons_from_lines or "
66
+ "photons_from_spectrum instead.")
67
+ margin = int(margin)
68
+ kernel = _spectral_kernel(telescope, det, slit_width, spectral_psf)
69
+ kernel = kernel / kernel.sum()
70
+ half = kernel.size // 2
71
+ if margin and margin < half:
72
+ raise ValueError(
73
+ f"A margin of {margin} rows is less than the {half} the spectral "
74
+ f"response reaches, so the edge rows would miss light."
75
+ )
76
+ unit = rows.unit if hasattr(rows, "unit") else 1
77
+ values = np.asarray(rows.value if hasattr(rows, "value") else rows, dtype=float)
78
+ if values.size <= 2 * margin:
79
+ raise ValueError(
80
+ f"{values.size} rows leave nothing inside a margin of {margin} at each end."
81
+ )
82
+ blurred = np.convolve(np.pad(values, half), kernel, mode="valid")
83
+ return blurred[margin:values.size - margin] * unit
84
+
85
+
86
+ def get_dem_data_from_results(results: Dict[str, Any]) -> Dict[str, Any]:
87
+ """
88
+ DEM data held in the results, which no version of ECLIPSE has written there.
89
+
90
+ Deprecated since 0.12.0. The DEM and emission measure are in the
91
+ synthesis file. Read them with
92
+ `read_synthesis_products(path, keys=["dem_map", "em_tv"])`.
93
+
94
+ Parameters
95
+ ----------
96
+ results : dict
97
+ Results from `load_instrument_response_results`.
98
+
99
+ Returns
100
+ -------
101
+ dict
102
+ `results["dem_data"]`.
103
+
104
+ Raises
105
+ ------
106
+ KeyError
107
+ If the results hold no DEM; results from `eclipse` never do.
108
+ """
109
+ _warn("get_dem_data_from_results",
110
+ "no released version wrote DEM data into the results. The DEM is in the synthesis "
111
+ "file; read it with "
112
+ "eclipse.read_synthesis_products(path, keys=['dem_map', 'em_tv']).")
113
+ if "dem_data" not in results:
114
+ raise KeyError(
115
+ "DEM data is not in the results, as no released version wrote it there. It is "
116
+ "in the synthesis file: read it with "
117
+ "eclipse.read_synthesis_products(path, keys=['dem_map', 'em_tv'])."
118
+ )
119
+
120
+ return results["dem_data"]
121
+
122
+
123
+ def _load_psf_ascii(fname: Path, skip: int) -> np.ndarray:
124
+ """Load PSF data from ASCII file."""
125
+ _warn("_load_psf_ascii", "ECLIPSE does not use it. "
126
+ "numpy.loadtxt(fname, skiprows=skip, encoding='utf-16 LE') does the same.")
127
+ return np.loadtxt(fname, skiprows=skip, encoding="utf-16 LE")
128
+
129
+
130
+ def _resample_psf(psf: np.ndarray, res_in: u.Quantity, res_out: u.Quantity) -> np.ndarray:
131
+ """Resample PSF to different pixel scale."""
132
+ _warn("_resample_psf", "ECLIPSE does not use it. scipy.ndimage.zoom does the same.")
133
+ factor = (res_in / res_out).decompose().value
134
+ return zoom(psf, factor, order=1)
135
+
136
+
137
+ def _combine_psfs(psf_focus: np.ndarray, psf_mesh: np.ndarray, crop: float = 0.99, size: int | None = None) -> np.ndarray:
138
+ """Convolve the focus and mesh PSFs and crop to an energy fraction or size."""
139
+ _warn("_combine_psfs", "ECLIPSE does not use it. scipy.signal.convolve2d does the "
140
+ "convolution.")
141
+ psf = convolve2d(psf_focus, psf_mesh, mode="same")
142
+ if size is not None:
143
+ if size % 2 == 0:
144
+ size += 1
145
+ r0, c0 = np.array(psf.shape) // 2
146
+ half = size // 2
147
+ psf = psf[r0 - half : r0 + half + 1, c0 - half : c0 + half + 1]
148
+ else:
149
+ flat = psf.ravel()
150
+ idx = flat.argsort()[::-1]
151
+ csum = flat[idx].cumsum()
152
+ thr = flat[idx[np.searchsorted(csum, flat.sum() * crop)]]
153
+ flat[flat < thr] = 0
154
+ rows, cols = np.where(flat.reshape(psf.shape))
155
+ r0, r1 = rows.min(), rows.max()
156
+ c0, c1 = cols.min(), cols.max()
157
+ side = max(r1 - r0, c1 - c0) + 1
158
+ r0 = (r0 + r1) // 2 - side // 2
159
+ c0 = (c0 + c1) // 2 - side // 2
160
+ psf = psf[r0 : r0 + side, c0 : c0 + side]
161
+ return psf / psf.sum()