solarc-eclipse 0.6.1__tar.gz → 0.6.1.2__tar.gz
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.
- {solarc_eclipse-0.6.1/solarc_eclipse.egg-info → solarc_eclipse-0.6.1.2}/PKG-INFO +11 -4
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/README.md +9 -0
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/euvst_response/__init__.py +5 -4
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/euvst_response/analysis.py +48 -23
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/euvst_response/config.py +10 -6
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/euvst_response/data_processing.py +53 -15
- solarc_eclipse-0.6.1.2/euvst_response/fitting.py +399 -0
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/euvst_response/main.py +37 -5
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/euvst_response/monte_carlo.py +19 -13
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/euvst_response/radiometric.py +103 -70
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/euvst_response/utils.py +19 -0
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/pyproject.toml +1 -3
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/setup.py +1 -3
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2/solarc_eclipse.egg-info}/PKG-INFO +11 -4
- solarc_eclipse-0.6.1/euvst_response/fitting.py +0 -144
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/LICENSE +0 -0
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/MANIFEST.in +0 -0
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/euvst_response/cli.py +0 -0
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/euvst_response/data/throughput/grating_reflection_efficiency.dat +0 -0
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/euvst_response/data/throughput/primary_mirror_coating_reflectance.dat +0 -0
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/euvst_response/data/throughput/source.txt +0 -0
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/euvst_response/data/throughput/throughput_aluminium_1000_angstrom.dat +0 -0
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/euvst_response/data/throughput/throughput_aluminium_oxide_1000_angstrom.dat +0 -0
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/euvst_response/data/throughput/throughput_carbon_1000_angstrom.dat +0 -0
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/euvst_response/pinhole_diffraction.py +0 -0
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/euvst_response/psf.py +0 -0
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/euvst_response/synthesis.py +0 -0
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/euvst_response/synthesis_cli.py +0 -0
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/setup.cfg +0 -0
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/solarc_eclipse.egg-info/SOURCES.txt +0 -0
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/solarc_eclipse.egg-info/dependency_links.txt +0 -0
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/solarc_eclipse.egg-info/entry_points.txt +0 -0
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/solarc_eclipse.egg-info/not-zip-safe +0 -0
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/solarc_eclipse.egg-info/requires.txt +0 -0
- {solarc_eclipse-0.6.1 → solarc_eclipse-0.6.1.2}/solarc_eclipse.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: solarc-eclipse
|
|
3
|
-
Version: 0.6.1
|
|
3
|
+
Version: 0.6.1.2
|
|
4
4
|
Summary: ECLIPSE: Emission Calculation and Line Prediction for SOLAR-C EUVST
|
|
5
5
|
Home-page: https://github.com/jamesmckevitt/eclipse
|
|
6
6
|
Author: James McKevitt
|
|
@@ -12,11 +12,9 @@ Classifier: Development Status :: 4 - Beta
|
|
|
12
12
|
Classifier: Intended Audience :: Science/Research
|
|
13
13
|
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
17
15
|
Classifier: Programming Language :: Python :: 3.10
|
|
18
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
-
Requires-Python: >=3.
|
|
17
|
+
Requires-Python: >=3.10
|
|
20
18
|
Description-Content-Type: text/markdown
|
|
21
19
|
License-File: LICENSE
|
|
22
20
|
Requires-Dist: numpy
|
|
@@ -361,6 +359,15 @@ ccd_temperature: -60 Celsius # Default (expected operating temperature)
|
|
|
361
359
|
|
|
362
360
|
# Visible stray light level
|
|
363
361
|
vis_sl: 0 photon / (s * pixel) # Default, (ideal case, no stray light)
|
|
362
|
+
|
|
363
|
+
# Multi-component Gaussian fitting (optional, omit for single-Gaussian)
|
|
364
|
+
fitting:
|
|
365
|
+
primary_component: 0
|
|
366
|
+
components:
|
|
367
|
+
- wavelength: 195.119 angstrom
|
|
368
|
+
- wavelength: 195.179 angstrom
|
|
369
|
+
tie_center: 0 # Centroid offset tied to component 0
|
|
370
|
+
tie_width: 0 # Same width as component 0
|
|
364
371
|
```
|
|
365
372
|
|
|
366
373
|
For guidance on recommended values, see McKevitt et al. (2025) (in prep.).
|
|
@@ -318,6 +318,15 @@ ccd_temperature: -60 Celsius # Default (expected operating temperature)
|
|
|
318
318
|
|
|
319
319
|
# Visible stray light level
|
|
320
320
|
vis_sl: 0 photon / (s * pixel) # Default, (ideal case, no stray light)
|
|
321
|
+
|
|
322
|
+
# Multi-component Gaussian fitting (optional, omit for single-Gaussian)
|
|
323
|
+
fitting:
|
|
324
|
+
primary_component: 0
|
|
325
|
+
components:
|
|
326
|
+
- wavelength: 195.119 angstrom
|
|
327
|
+
- wavelength: 195.179 angstrom
|
|
328
|
+
tie_center: 0 # Centroid offset tied to component 0
|
|
329
|
+
tie_width: 0 # Same width as component 0
|
|
321
330
|
```
|
|
322
331
|
|
|
323
332
|
For guidance on recommended values, see McKevitt et al. (2025) (in prep.).
|
|
@@ -4,7 +4,7 @@ ECLIPSE: Emission Calculation and Line Prediction for SOLAR-C EUVST
|
|
|
4
4
|
This package provides tools for modeling the performance of the EUV spectrograph EUVST, on SOLAR-C.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
__version__ = "0.6.1"
|
|
7
|
+
__version__ = "0.6.1.2"
|
|
8
8
|
__author__ = "James McKevitt"
|
|
9
9
|
__email__ = "jm2@mssl.ucl.ac.uk"
|
|
10
10
|
|
|
@@ -13,11 +13,11 @@ from .config import Detector_SWC, Detector_EIS, Telescope_EUVST, Telescope_EIS,
|
|
|
13
13
|
from .utils import wl_to_vel, vel_to_wl, angle_to_distance, distance_to_angle
|
|
14
14
|
from .radiometric import (
|
|
15
15
|
intensity_to_photons, add_telescope_throughput, photons_to_pixel_counts,
|
|
16
|
-
|
|
16
|
+
apply_exposure, sample_photon_arrivals, add_poisson, apply_focusing_optics_psf, to_electrons,
|
|
17
17
|
to_dn, add_visible_stray_light, add_pinhole_visible_light
|
|
18
18
|
)
|
|
19
19
|
from .pinhole_diffraction import apply_euv_pinhole_diffraction, airy_disk_pattern
|
|
20
|
-
from .fitting import fit_cube_gauss, velocity_from_fit, width_from_fit, analyse
|
|
20
|
+
from .fitting import fit_cube_gauss, velocity_from_fit, width_from_fit, analyse, FitConfig, FitComponent
|
|
21
21
|
from .monte_carlo import simulate_once, monte_carlo
|
|
22
22
|
from .main import main
|
|
23
23
|
from .data_processing import load_atmosphere
|
|
@@ -36,9 +36,10 @@ __all__ = [
|
|
|
36
36
|
"Simulation", "AluminiumFilter",
|
|
37
37
|
"wl_to_vel", "vel_to_wl", "angle_to_distance", "distance_to_angle",
|
|
38
38
|
"intensity_to_photons", "add_telescope_throughput", "photons_to_pixel_counts",
|
|
39
|
-
"
|
|
39
|
+
"apply_exposure", "sample_photon_arrivals", "add_poisson", "apply_focusing_optics_psf", "to_electrons",
|
|
40
40
|
"to_dn", "add_visible_stray_light", "add_pinhole_visible_light",
|
|
41
41
|
"fit_cube_gauss", "velocity_from_fit", "width_from_fit", "analyse",
|
|
42
|
+
"FitConfig", "FitComponent",
|
|
42
43
|
"simulate_once", "monte_carlo",
|
|
43
44
|
"main",
|
|
44
45
|
"load_atmosphere",
|
|
@@ -112,7 +112,8 @@ def get_parameter_combinations(results: Dict[str, Any]) -> List[Tuple]:
|
|
|
112
112
|
def analyse_fit_statistics(
|
|
113
113
|
combination_results: Dict[str, Any],
|
|
114
114
|
rest_wavelength: u.Quantity,
|
|
115
|
-
data_type: str = "dn"
|
|
115
|
+
data_type: str = "dn",
|
|
116
|
+
fit_config=None,
|
|
116
117
|
) -> Dict[str, Any]:
|
|
117
118
|
"""
|
|
118
119
|
Analyze fit statistics to compute velocity and line width statistics.
|
|
@@ -125,12 +126,24 @@ def analyse_fit_statistics(
|
|
|
125
126
|
Rest wavelength for velocity conversion.
|
|
126
127
|
data_type : str, optional
|
|
127
128
|
Either "dn" or "photon" to specify which fit statistics to analyze.
|
|
129
|
+
fit_config : FitConfig, optional
|
|
130
|
+
Multi-component fitting configuration. When provided the primary-
|
|
131
|
+
component indices are used; otherwise indices 1 (centre) and
|
|
132
|
+
2 (sigma) are assumed (single-component default).
|
|
128
133
|
|
|
129
134
|
Returns
|
|
130
135
|
-------
|
|
131
136
|
dict
|
|
132
137
|
Dictionary containing velocity and width statistics.
|
|
133
138
|
"""
|
|
139
|
+
# Determine parameter indices for the primary component
|
|
140
|
+
if fit_config is not None and not fit_config.is_single:
|
|
141
|
+
idx_center = fit_config.idx_center
|
|
142
|
+
idx_sigma = fit_config.idx_sigma
|
|
143
|
+
else:
|
|
144
|
+
idx_center = 1
|
|
145
|
+
idx_sigma = 2
|
|
146
|
+
|
|
134
147
|
# Get fit statistics
|
|
135
148
|
fit_stats_key = f"{data_type}_fit_stats"
|
|
136
149
|
if fit_stats_key not in combination_results:
|
|
@@ -141,19 +154,19 @@ def analyse_fit_statistics(
|
|
|
141
154
|
fit_truth_units = combination_results["ground_truth"]["fit_truth_units"]
|
|
142
155
|
|
|
143
156
|
# Extract data and units
|
|
144
|
-
mean_data = fit_stats["mean_data"] # Shape: (nx, ny,
|
|
145
|
-
std_data = fit_stats["std_data"] # Shape: (nx, ny,
|
|
146
|
-
units = fit_stats["units"] # List of
|
|
157
|
+
mean_data = fit_stats["mean_data"] # Shape: (nx, ny, n_params)
|
|
158
|
+
std_data = fit_stats["std_data"] # Shape: (nx, ny, n_params)
|
|
159
|
+
units = fit_stats["units"] # List of n_params astropy units
|
|
147
160
|
|
|
148
|
-
# Get center statistics
|
|
149
|
-
center_mean_data = mean_data[...,
|
|
150
|
-
center_std_data = std_data[...,
|
|
151
|
-
center_unit = units[
|
|
161
|
+
# Get center statistics for the primary component
|
|
162
|
+
center_mean_data = mean_data[..., idx_center]
|
|
163
|
+
center_std_data = std_data[..., idx_center]
|
|
164
|
+
center_unit = units[idx_center]
|
|
152
165
|
|
|
153
|
-
# Get width statistics
|
|
154
|
-
width_mean_data = mean_data[...,
|
|
155
|
-
width_std_data = std_data[...,
|
|
156
|
-
width_unit = units[
|
|
166
|
+
# Get width statistics for the primary component
|
|
167
|
+
width_mean_data = mean_data[..., idx_sigma]
|
|
168
|
+
width_std_data = std_data[..., idx_sigma]
|
|
169
|
+
width_unit = units[idx_sigma]
|
|
157
170
|
|
|
158
171
|
# Create quantities
|
|
159
172
|
center_mean_q = center_mean_data * center_unit
|
|
@@ -170,7 +183,7 @@ def analyse_fit_statistics(
|
|
|
170
183
|
|
|
171
184
|
# Convert to velocities
|
|
172
185
|
v_mean = centers_to_velocity(center_mean_q, rest_wavelength)
|
|
173
|
-
v_true = centers_to_velocity(fit_truth_data[...,
|
|
186
|
+
v_true = centers_to_velocity(fit_truth_data[..., idx_center] * fit_truth_units[idx_center], rest_wavelength)
|
|
174
187
|
v_err = v_true - v_mean
|
|
175
188
|
|
|
176
189
|
# Convert center std to velocity std using differential: dv/dlambda = c/lambda
|
|
@@ -459,7 +472,8 @@ def create_sunpy_maps_from_combo(
|
|
|
459
472
|
rest_wavelength: u.Quantity = 195.119 * u.AA,
|
|
460
473
|
data_type: str = "dn",
|
|
461
474
|
precision_requirement: u.Quantity = 2.0 * u.km / u.s,
|
|
462
|
-
exposure_time_results: List[Dict[str, Any]] | None = None
|
|
475
|
+
exposure_time_results: List[Dict[str, Any]] | None = None,
|
|
476
|
+
fit_config=None,
|
|
463
477
|
) -> Dict[str, Any]:
|
|
464
478
|
"""
|
|
465
479
|
Create SunPy maps from combination results using the new fit statistics structure.
|
|
@@ -479,6 +493,9 @@ def create_sunpy_maps_from_combo(
|
|
|
479
493
|
exposure_time_results : list of dict, optional
|
|
480
494
|
List of results from get_results_for_combination() for different exposure times.
|
|
481
495
|
If provided, will create an exposure time map showing minimum exposure needed.
|
|
496
|
+
fit_config : FitConfig, optional
|
|
497
|
+
Multi-component fitting configuration. When provided, the primary-
|
|
498
|
+
component indices are used to extract centre and width parameters.
|
|
482
499
|
|
|
483
500
|
Returns
|
|
484
501
|
-------
|
|
@@ -504,7 +521,7 @@ def create_sunpy_maps_from_combo(
|
|
|
504
521
|
# Extract exposure time from parameters
|
|
505
522
|
exposure_time = result["parameters"]["exposure"].to_value(u.s)
|
|
506
523
|
# Create analysis for this exposure
|
|
507
|
-
analysis = analyse_fit_statistics(result, rest_wavelength, data_type)
|
|
524
|
+
analysis = analyse_fit_statistics(result, rest_wavelength, data_type, fit_config=fit_config)
|
|
508
525
|
analysis_per_exp[exposure_time] = analysis
|
|
509
526
|
else:
|
|
510
527
|
analysis_per_exp = None
|
|
@@ -535,14 +552,22 @@ def create_sunpy_maps_from_combo(
|
|
|
535
552
|
maps['total_dn'] = sunpy.map.Map(total_dn_data.T, wcs_2d)
|
|
536
553
|
maps['total_dn'].meta['bunit'] = str(total_dn_unit)
|
|
537
554
|
|
|
555
|
+
# Determine parameter indices for the primary component
|
|
556
|
+
if fit_config is not None and not fit_config.is_single:
|
|
557
|
+
idx_center = fit_config.idx_center
|
|
558
|
+
idx_sigma = fit_config.idx_sigma
|
|
559
|
+
else:
|
|
560
|
+
idx_center = 1
|
|
561
|
+
idx_sigma = 2
|
|
562
|
+
|
|
538
563
|
# --- Get velocity and width analysis for this combination ---
|
|
539
|
-
analysis = analyse_fit_statistics(combination_results, rest_wavelength, data_type)
|
|
564
|
+
analysis = analyse_fit_statistics(combination_results, rest_wavelength, data_type, fit_config=fit_config)
|
|
540
565
|
|
|
541
566
|
# --- Velocity maps ---
|
|
542
|
-
# Velocity from first fit (
|
|
543
|
-
first_fit_data = fit_stats["first_fit_data"] # Shape: (nx, ny,
|
|
544
|
-
center_first_data = first_fit_data[...,
|
|
545
|
-
center_first_unit = fit_stats["units"][
|
|
567
|
+
# Velocity from first fit (primary component center)
|
|
568
|
+
first_fit_data = fit_stats["first_fit_data"] # Shape: (nx, ny, n_params)
|
|
569
|
+
center_first_data = first_fit_data[..., idx_center]
|
|
570
|
+
center_first_unit = fit_stats["units"][idx_center]
|
|
546
571
|
|
|
547
572
|
def centers_to_velocity(centers_data, centers_unit, lambda0):
|
|
548
573
|
"""Convert wavelength centers to velocities"""
|
|
@@ -570,9 +595,9 @@ def create_sunpy_maps_from_combo(
|
|
|
570
595
|
maps['velocity_err'].meta['bunit'] = str(analysis["v_err"].unit)
|
|
571
596
|
|
|
572
597
|
# --- Line width maps ---
|
|
573
|
-
# Line width from first fit (
|
|
574
|
-
width_first_data = first_fit_data[...,
|
|
575
|
-
width_first_unit = fit_stats["units"][
|
|
598
|
+
# Line width from first fit (primary component sigma)
|
|
599
|
+
width_first_data = first_fit_data[..., idx_sigma]
|
|
600
|
+
width_first_unit = fit_stats["units"][idx_sigma]
|
|
576
601
|
|
|
577
602
|
# Create quantity with proper units
|
|
578
603
|
width_quantity = width_first_data * width_first_unit
|
|
@@ -243,7 +243,8 @@ class Telescope_EUVST:
|
|
|
243
243
|
microroughness_sigma: u.Quantity = 0.3 * u.nm # RMS microroughness for primary mirror
|
|
244
244
|
filter: AluminiumFilter = field(default_factory=AluminiumFilter)
|
|
245
245
|
psf_type: str = "gaussian"
|
|
246
|
-
psf_params: list = field(default_factory=lambda: [
|
|
246
|
+
# psf_params: list = field(default_factory=lambda: [1.26 * u.pixel, 1.95 * u.pixel]) # [spatial_fwhm, spectral_fwhm] in pixels. From 0.200 arcsec (w/ slit-scan; FOV2) and 33.00 mA in RSC-2022021 (Oct 2023) and RSC-2022021B (Feb 2024).
|
|
247
|
+
psf_params: list = field(default_factory=lambda: [2.66 * u.pixel, 2.54 * u.pixel]) # [spatial_fwhm, spectral_fwhm] in pixels. From 0.423 arcsec (w/ slit-scan; FOV2) and 43.00 mA in RSC-2022021C (Mar 2025).
|
|
247
248
|
|
|
248
249
|
# Wavelength-dependent efficiency tables
|
|
249
250
|
pm_table: Path = field(default_factory=lambda: files('euvst_response') / 'data' / 'throughput' / 'primary_mirror_coating_reflectance.dat')
|
|
@@ -251,7 +252,7 @@ class Telescope_EUVST:
|
|
|
251
252
|
|
|
252
253
|
@property
|
|
253
254
|
def collecting_area(self) -> u.Quantity:
|
|
254
|
-
return 0.5 * np.pi * (self.D_ap / 2) ** 2
|
|
255
|
+
return 0.5 * np.pi * (self.D_ap / 2) ** 2 # Accounting for 50% loss due to beam division between SW and LW channels.
|
|
255
256
|
|
|
256
257
|
def primary_mirror_efficiency(self, wl0: u.Quantity) -> float:
|
|
257
258
|
"""
|
|
@@ -293,7 +294,10 @@ class Telescope_EUVST:
|
|
|
293
294
|
"""
|
|
294
295
|
Calculate the efficiency reduction due to primary mirror microroughness.
|
|
295
296
|
|
|
296
|
-
|
|
297
|
+
Uses the Debye-Waller factor for specular reflectance:
|
|
298
|
+
|
|
299
|
+
efficiency = exp(-(4*pi*sigma/lambda)^2)
|
|
300
|
+
|
|
297
301
|
where sigma is the RMS microroughness and lambda is the wavelength.
|
|
298
302
|
|
|
299
303
|
Parameters
|
|
@@ -313,8 +317,8 @@ class Telescope_EUVST:
|
|
|
313
317
|
# Calculate (4*pi*sigma/lambda)^2
|
|
314
318
|
roughness_term = (4 * np.pi * sigma_nm / wl_nm) ** 2
|
|
315
319
|
|
|
316
|
-
# Return
|
|
317
|
-
return
|
|
320
|
+
# Return exp(-(4*pi*sigma/lambda)^2) [Debye-Waller specular efficiency]
|
|
321
|
+
return np.exp(-roughness_term.value)
|
|
318
322
|
|
|
319
323
|
def throughput(self, wl0: u.Quantity) -> float:
|
|
320
324
|
"""
|
|
@@ -348,7 +352,7 @@ class Telescope_EUVST:
|
|
|
348
352
|
class Telescope_EIS:
|
|
349
353
|
"""Hinode/EIS telescope configuration for comparison."""
|
|
350
354
|
psf_type: str = "gaussian"
|
|
351
|
-
psf_params: list = field(default_factory=lambda: [
|
|
355
|
+
psf_params: list = field(default_factory=lambda: [3.0 * u.pixel, 3.0 * u.pixel]) # [spatial_fwhm, spectral_fwhm] in pixels
|
|
352
356
|
|
|
353
357
|
def ea_and_throughput(self, wl0: u.Quantity) -> u.Quantity:
|
|
354
358
|
# Effective area including detector QE is 0.23 cm2
|
|
@@ -152,18 +152,37 @@ def resample_ndcube_spectral_axis(ndcube, spectral_axis, output_resolution, ncpu
|
|
|
152
152
|
data = np.moveaxis(ndcube.data, spectral_axis, -1)
|
|
153
153
|
shape = data.shape
|
|
154
154
|
flat_data = data.reshape(-1, shape[-1])
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
155
|
+
n_pixels = flat_data.shape[0]
|
|
156
|
+
|
|
157
|
+
# Determine number of workers
|
|
158
|
+
import os
|
|
159
|
+
if ncpu == -1:
|
|
160
|
+
n_workers = os.cpu_count() or 1
|
|
161
|
+
else:
|
|
162
|
+
n_workers = ncpu
|
|
163
|
+
|
|
164
|
+
# Calculate batch size: aim for ~4 batches per worker to balance load
|
|
165
|
+
# but ensure each batch has enough work to justify overhead
|
|
166
|
+
min_pixels_per_batch = 100
|
|
167
|
+
n_batches = max(1, min(n_pixels // min_pixels_per_batch, n_workers * 4))
|
|
168
|
+
batch_size = (n_pixels + n_batches - 1) // n_batches # ceiling division
|
|
169
|
+
|
|
170
|
+
# Create batch indices
|
|
171
|
+
batch_indices = [(i, min(i + batch_size, n_pixels)) for i in range(0, n_pixels, batch_size)]
|
|
172
|
+
|
|
173
|
+
def _resample_batch(start_idx, end_idx):
|
|
174
|
+
"""Resample a batch of pixels."""
|
|
175
|
+
resampler = FluxConservingResampler(extrapolation_treatment="zero_fill")
|
|
176
|
+
batch_results = np.empty((end_idx - start_idx, n_spec))
|
|
177
|
+
for i, pixel_idx in enumerate(range(start_idx, end_idx)):
|
|
178
|
+
spec = Spectrum(flux=flat_data[pixel_idx] * ndcube.unit, spectral_axis=spectral_world)
|
|
179
|
+
res = resampler(spec, new_spec_grid)
|
|
180
|
+
batch_results[i] = res.flux.value
|
|
181
|
+
return batch_results
|
|
182
|
+
|
|
183
|
+
with tqdm_joblib(tqdm(total=len(batch_indices), desc="Resampling spectral axis", unit="batch", leave=False)):
|
|
165
184
|
results = Parallel(n_jobs=ncpu)(
|
|
166
|
-
delayed(
|
|
185
|
+
delayed(_resample_batch)(start, end) for start, end in batch_indices
|
|
167
186
|
)
|
|
168
187
|
resampled = np.vstack(results)
|
|
169
188
|
|
|
@@ -186,8 +205,21 @@ def resample_ndcube_spectral_axis(ndcube, spectral_axis, output_resolution, ncpu
|
|
|
186
205
|
return NDCube(resampled, wcs=new_wcs, unit=ndcube.unit, meta=ndcube.meta)
|
|
187
206
|
|
|
188
207
|
|
|
189
|
-
def reproject_ndcube_heliocentric_to_helioprojective(new_cube_spec, sim, det):
|
|
190
|
-
""" Reproject an NDCube from heliocentric to helioprojective coordinates.
|
|
208
|
+
def reproject_ndcube_heliocentric_to_helioprojective(new_cube_spec, sim, det, ncpu=-1):
|
|
209
|
+
""" Reproject an NDCube from heliocentric to helioprojective coordinates.
|
|
210
|
+
|
|
211
|
+
Parameters
|
|
212
|
+
----------
|
|
213
|
+
new_cube_spec : NDCube
|
|
214
|
+
Input NDCube in heliocentric coordinates
|
|
215
|
+
sim : Simulation
|
|
216
|
+
Simulation configuration object
|
|
217
|
+
det : Detector
|
|
218
|
+
Detector configuration object
|
|
219
|
+
ncpu : int, optional
|
|
220
|
+
Number of CPU cores for parallel reprojection. -1 uses all cores,
|
|
221
|
+
positive integers specify exact count. Default is -1.
|
|
222
|
+
"""
|
|
191
223
|
|
|
192
224
|
nx, ny, _ = new_cube_spec.shape
|
|
193
225
|
wcs_hc = new_cube_spec.wcs
|
|
@@ -234,11 +266,16 @@ def reproject_ndcube_heliocentric_to_helioprojective(new_cube_spec, sim, det):
|
|
|
234
266
|
(det.plate_scale_angle * u.pix).to_value(u.arcsec),
|
|
235
267
|
(sim.slit_width).to_value(u.arcsec)]
|
|
236
268
|
|
|
269
|
+
# Determine parallelization setting:
|
|
270
|
+
# - If ncpu=-1, use True (all available cores)
|
|
271
|
+
# - If ncpu is a positive integer, pass it directly to control thread count
|
|
272
|
+
parallel_setting = True if ncpu == -1 else ncpu
|
|
273
|
+
|
|
237
274
|
new_cube_spec_hp_spat = new_cube_spec_hp.reproject_to(
|
|
238
275
|
wcs_tgt,
|
|
239
276
|
shape_out=shape_out,
|
|
240
277
|
algorithm='interpolation',
|
|
241
|
-
parallel=
|
|
278
|
+
parallel=parallel_setting,
|
|
242
279
|
order='bilinear',
|
|
243
280
|
) * new_cube_spec_hp.unit
|
|
244
281
|
|
|
@@ -273,7 +310,8 @@ def rebin_atmosphere(cube_sim, det, sim, use_dask=False):
|
|
|
273
310
|
cube_det = reproject_ndcube_heliocentric_to_helioprojective(
|
|
274
311
|
cube_spec,
|
|
275
312
|
sim,
|
|
276
|
-
det
|
|
313
|
+
det,
|
|
314
|
+
ncpu=sim.ncpu
|
|
277
315
|
)
|
|
278
316
|
|
|
279
317
|
return cube_det
|