solarc-eclipse 0.6.1.2__tar.gz → 0.6.1.4__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.2/solarc_eclipse.egg-info → solarc_eclipse-0.6.1.4}/PKG-INFO +76 -26
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/README.md +68 -25
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/euvst_response/__init__.py +1 -1
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/euvst_response/analysis.py +74 -19
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/euvst_response/data_processing.py +17 -10
- solarc_eclipse-0.6.1.4/euvst_response/extern/__init__.py +0 -0
- solarc_eclipse-0.6.1.4/euvst_response/extern/mpfit.py +2388 -0
- solarc_eclipse-0.6.1.4/euvst_response/fitting.py +639 -0
- solarc_eclipse-0.6.1.4/euvst_response/main.py +566 -0
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/euvst_response/monte_carlo.py +82 -34
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/euvst_response/synthesis.py +183 -57
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/euvst_response/utils.py +110 -0
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/pyproject.toml +8 -0
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/setup.py +1 -24
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4/solarc_eclipse.egg-info}/PKG-INFO +76 -26
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/solarc_eclipse.egg-info/SOURCES.txt +2 -0
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/solarc_eclipse.egg-info/requires.txt +8 -0
- solarc_eclipse-0.6.1.2/euvst_response/fitting.py +0 -399
- solarc_eclipse-0.6.1.2/euvst_response/main.py +0 -500
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/LICENSE +0 -0
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/MANIFEST.in +0 -0
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/euvst_response/cli.py +0 -0
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/euvst_response/config.py +0 -0
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/euvst_response/data/throughput/grating_reflection_efficiency.dat +0 -0
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/euvst_response/data/throughput/primary_mirror_coating_reflectance.dat +0 -0
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/euvst_response/data/throughput/source.txt +0 -0
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/euvst_response/data/throughput/throughput_aluminium_1000_angstrom.dat +0 -0
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/euvst_response/data/throughput/throughput_aluminium_oxide_1000_angstrom.dat +0 -0
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/euvst_response/data/throughput/throughput_carbon_1000_angstrom.dat +0 -0
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/euvst_response/pinhole_diffraction.py +0 -0
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/euvst_response/psf.py +0 -0
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/euvst_response/radiometric.py +0 -0
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/euvst_response/synthesis_cli.py +0 -0
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/setup.cfg +0 -0
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/solarc_eclipse.egg-info/dependency_links.txt +0 -0
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/solarc_eclipse.egg-info/entry_points.txt +0 -0
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/solarc_eclipse.egg-info/not-zip-safe +0 -0
- {solarc_eclipse-0.6.1.2 → solarc_eclipse-0.6.1.4}/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.4
|
|
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
|
|
@@ -29,6 +29,13 @@ Requires-Dist: dill
|
|
|
29
29
|
Requires-Dist: pyyaml
|
|
30
30
|
Requires-Dist: reproject
|
|
31
31
|
Requires-Dist: sunpy[all]
|
|
32
|
+
Requires-Dist: dask
|
|
33
|
+
Requires-Dist: psutil
|
|
34
|
+
Requires-Dist: mendeleev
|
|
35
|
+
Requires-Dist: h5py
|
|
36
|
+
Requires-Dist: fiasco
|
|
37
|
+
Provides-Extra: mpi
|
|
38
|
+
Requires-Dist: mpi4py; extra == "mpi"
|
|
32
39
|
Provides-Extra: dev
|
|
33
40
|
Requires-Dist: pytest; extra == "dev"
|
|
34
41
|
Requires-Dist: black; extra == "dev"
|
|
@@ -71,7 +78,7 @@ After installation, you can run ECLIPSE from the command line:
|
|
|
71
78
|
|
|
72
79
|
```bash
|
|
73
80
|
# Run synthesis script (convert 3D MHD data to synthetic spectra)
|
|
74
|
-
synthesise-spectra --data-dir ./data/atmosphere --
|
|
81
|
+
synthesise-spectra --data-dir ./data/atmosphere --lines Fe12_195.1190 --output-dir ./run/input
|
|
75
82
|
|
|
76
83
|
# Run instrument response simulation
|
|
77
84
|
eclipse --config ./run/input/config.yaml
|
|
@@ -95,7 +102,7 @@ detector = Detector_SWC()
|
|
|
95
102
|
print(f"Telescope collecting area: {telescope.collecting_area:.4f}")
|
|
96
103
|
print(f"Detector QE (EUV): {detector.qe_euv:.2f}")
|
|
97
104
|
|
|
98
|
-
# Calculate effective area at Fe XII 195.119
|
|
105
|
+
# Calculate effective area at Fe XII 195.119 Angstrom
|
|
99
106
|
fe12_wl = 195.119 * u.AA
|
|
100
107
|
effective_area = telescope.collecting_area * telescope.throughput(fe12_wl) * detector.qe_euv
|
|
101
108
|
|
|
@@ -125,22 +132,24 @@ from euvst_response import (
|
|
|
125
132
|
create_sunpy_maps_from_combo,
|
|
126
133
|
summary_table
|
|
127
134
|
)
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
## Detailed instructions
|
|
131
135
|
|
|
132
|
-
|
|
136
|
+
# Load results
|
|
137
|
+
results = load_instrument_response_results("run/result/my_results.pkl")
|
|
133
138
|
|
|
134
|
-
|
|
139
|
+
# Get results for a specific parameter combination:
|
|
140
|
+
combo = get_results_for_combination(
|
|
141
|
+
results, exposure=40*u.s, slit_width=0.4*u.arcsec, offchip_bin_slit=2
|
|
142
|
+
)
|
|
135
143
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
idl -e "make_goft"
|
|
144
|
+
# Create SunPy maps
|
|
145
|
+
maps = create_sunpy_maps_from_combo(combo, rest_wavelength=195.119*u.AA, data_type='dn')
|
|
139
146
|
```
|
|
140
147
|
|
|
141
|
-
|
|
148
|
+
## Detailed instructions
|
|
149
|
+
|
|
150
|
+
### 1. Run the line synthesis
|
|
142
151
|
|
|
143
|
-
The synthesis script converts 3D MHD simulation data into synthetic solar spectra.
|
|
152
|
+
The synthesis script converts 3D MHD simulation data into synthetic solar spectra. Contribution functions G(T, n_e) are computed on-the-fly using [fiasco](https://fiasco.readthedocs.io/) (a Python interface to the CHIANTI atomic database).
|
|
144
153
|
|
|
145
154
|
#### Basic Usage
|
|
146
155
|
|
|
@@ -148,7 +157,9 @@ The synthesis script converts 3D MHD simulation data into synthetic solar spectr
|
|
|
148
157
|
# Example using all available command line options
|
|
149
158
|
synthesise-spectra \
|
|
150
159
|
--data-dir ./data/atmosphere \
|
|
151
|
-
--
|
|
160
|
+
--lines Fe12_195.1190 Fe12_195.1790 \
|
|
161
|
+
--abundance sun_coronal_2021_chianti \
|
|
162
|
+
--n-workers 4 \
|
|
152
163
|
--output-dir ./run/input \
|
|
153
164
|
--output-name synthesised_spectra.pkl \
|
|
154
165
|
--temp-file temp/eosT.0270000 \
|
|
@@ -168,8 +179,7 @@ synthesise-spectra \
|
|
|
168
179
|
--crop-z "0 Mm" "20 Mm" \
|
|
169
180
|
--downsample 1 \
|
|
170
181
|
--precision float64 \
|
|
171
|
-
--mean-mol-wt 1.29
|
|
172
|
-
--limit-lines Fe12_195.1190
|
|
182
|
+
--mean-mol-wt 1.29
|
|
173
183
|
|
|
174
184
|
# Show all available options
|
|
175
185
|
synthesise-spectra --help
|
|
@@ -179,10 +189,14 @@ synthesise-spectra --help
|
|
|
179
189
|
|
|
180
190
|
**Input/Output Paths:**
|
|
181
191
|
- `--data-dir`: Directory containing simulation data (default: `data/atmosphere`)
|
|
182
|
-
- `--goft-file`: Path to CHIANTI G(T,N) save file (default: `./data/gofnt.sav`)
|
|
183
192
|
- `--output-dir`: Output directory for results (default: `./run/input`)
|
|
184
193
|
- `--output-name`: Output filename (default: `synthesised_spectra.pkl`)
|
|
185
194
|
|
|
195
|
+
**Line and Abundance Selection:**
|
|
196
|
+
- `--lines`: Emission lines to synthesise, e.g., `--lines Fe12_195.1190 Fe12_195.1790` (required)
|
|
197
|
+
- `--abundance`: CHIANTI abundance dataset name (default: `sun_coronal_2021_chianti`)
|
|
198
|
+
- `--n-workers`: Number of parallel workers for the fiasco G(T, n_e) computation. Each distinct ion is computed in a separate process. `0` uses all available CPUs (default: `0`). Set to `1` for serial execution.
|
|
199
|
+
|
|
186
200
|
**Simulation Files:**
|
|
187
201
|
- `--temp-file`: Temperature file relative to data-dir (default: `temp/eosT.0270000`)
|
|
188
202
|
- `--rho-file`: Density file relative to data-dir (default: `rho/result_prim_0.0270000`)
|
|
@@ -215,9 +229,6 @@ synthesise-spectra --help
|
|
|
215
229
|
- `--precision`: Numerical precision `float32` or `float64` (default: `float64`)
|
|
216
230
|
- `--mean-mol-wt`: Mean molecular weight (default: `1.29`)
|
|
217
231
|
|
|
218
|
-
**Line Selection:**
|
|
219
|
-
- `--limit-lines`: Limit to specific lines, e.g., `--limit-lines Fe12_195.1190 Fe12_195.1790`
|
|
220
|
-
|
|
221
232
|
#### Dynamic Mode (Time-varying Atmospheres)
|
|
222
233
|
|
|
223
234
|
For simulating raster scans over evolving atmospheres, use dynamic mode which combines MHD timesteps based on instrument scanning:
|
|
@@ -225,7 +236,8 @@ For simulating raster scans over evolving atmospheres, use dynamic mode which co
|
|
|
225
236
|
```bash
|
|
226
237
|
synthesise-spectra \
|
|
227
238
|
--data-dir ./data/atmosphere \
|
|
228
|
-
--
|
|
239
|
+
--lines Fe12_195.1190 \
|
|
240
|
+
--abundance sun_coronal_2021_chianti \
|
|
229
241
|
--output-dir ./run/input \
|
|
230
242
|
--slit-rest-time "40 s" \
|
|
231
243
|
--slit-width "0.2 arcsec" \
|
|
@@ -263,7 +275,6 @@ The synthesis produces a pickle file containing:
|
|
|
263
275
|
|
|
264
276
|
- Use `--downsample 2` or `--downsample 4` for initial testing
|
|
265
277
|
- Use `--precision float32` to reduce memory usage (may affect accuracy)
|
|
266
|
-
- Use `--limit-lines` to synthesise only specific lines for development
|
|
267
278
|
- Use spatial cropping to focus on regions of interest and reduce computation time
|
|
268
279
|
- Monitor memory usage - full resolution synthesis can require 50+ GB RAM
|
|
269
280
|
- Side views (`--integration-axis x` or `y`) may require different velocity files
|
|
@@ -276,7 +287,7 @@ The synthesis results can be loaded and analyzed using the package API:
|
|
|
276
287
|
import euvst_response
|
|
277
288
|
|
|
278
289
|
# Load synthesis results - this sums all line cubes into a single cube
|
|
279
|
-
# By default uses Fe XII 195.119
|
|
290
|
+
# By default uses Fe XII 195.119 Angstrom as reference for wavelength grid
|
|
280
291
|
cube = euvst_response.load_atmosphere("./run/input/synthesised_spectra.pkl")
|
|
281
292
|
print(f"Combined cube shape: {cube.data.shape}")
|
|
282
293
|
|
|
@@ -300,7 +311,7 @@ This step can require a lot of memory at full resolution. A fully synthesised at
|
|
|
300
311
|
|
|
301
312
|
**Important:** You can place the synthesised atmosphere file anywhere and specify its location using the `synthesis_file` parameter in your YAML configuration file. The default location is `./run/input/synthesised_spectra.pkl`.
|
|
302
313
|
|
|
303
|
-
###
|
|
314
|
+
### 2. Simulate the instrument response
|
|
304
315
|
|
|
305
316
|
#### Configuration File
|
|
306
317
|
|
|
@@ -324,7 +335,7 @@ instrument: SWC # Options: SWC (EUVST Short Wavelength) or EIS (Hinode/EIS)
|
|
|
324
335
|
synthesis_file: ./run/input/synthesised_spectra.pkl # Default location
|
|
325
336
|
|
|
326
337
|
# Reference line for wavelength grid and metadata when combining all spectral lines
|
|
327
|
-
reference_line: Fe12_195.1190 # Default reference line (Fe XII 195.119
|
|
338
|
+
reference_line: Fe12_195.1190 # Default reference line (Fe XII 195.119 Angstrom)
|
|
328
339
|
|
|
329
340
|
# Point Spread Function
|
|
330
341
|
psf: False # Enable PSF convolution
|
|
@@ -358,20 +369,53 @@ aluminium_thickness: 1485 angstrom # Default (expected value)
|
|
|
358
369
|
ccd_temperature: -60 Celsius # Default (expected operating temperature)
|
|
359
370
|
|
|
360
371
|
# Visible stray light level
|
|
361
|
-
vis_sl: 0 photon / (s *
|
|
372
|
+
vis_sl: 0 photon / (s * cm**2) # Default, (ideal case, no stray light)
|
|
362
373
|
|
|
363
374
|
# Multi-component Gaussian fitting (optional, omit for single-Gaussian)
|
|
364
375
|
fitting:
|
|
365
376
|
primary_component: 0
|
|
377
|
+
constrain_positive_intensity: true
|
|
366
378
|
components:
|
|
367
379
|
- wavelength: 195.119 angstrom
|
|
380
|
+
amplitude_greater_than: 1 # must be brighter than component 1
|
|
368
381
|
- wavelength: 195.179 angstrom
|
|
369
382
|
tie_center: 0 # Centroid offset tied to component 0
|
|
370
383
|
tie_width: 0 # Same width as component 0
|
|
371
384
|
```
|
|
372
385
|
|
|
386
|
+
Off-chip binning in the slit direction can also be performed:
|
|
387
|
+
|
|
388
|
+
```yaml
|
|
389
|
+
offchip_bin_slit: 2 # Bin every 2 slit pixels
|
|
390
|
+
offchip_bin_slit: [1, 2, 4] # Sweep over multiple binning factors
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
Alternatively, you can specify paired slit width and off-chip binning values to only simulate specific combinations:
|
|
394
|
+
|
|
395
|
+
```yaml
|
|
396
|
+
# Paired slit width / off-chip binning
|
|
397
|
+
# Only the listed (slit_width, offchip_bin_slit) pairs are simulated
|
|
398
|
+
slit_bin_pairs:
|
|
399
|
+
- slit_width: 0.2 arcsec
|
|
400
|
+
offchip_bin_slit: 1
|
|
401
|
+
- slit_width: 0.4 arcsec
|
|
402
|
+
offchip_bin_slit: 2
|
|
403
|
+
- slit_width: 0.8 arcsec
|
|
404
|
+
offchip_bin_slit: 5
|
|
405
|
+
- slit_width: 1.6 arcsec
|
|
406
|
+
offchip_bin_slit: 10
|
|
407
|
+
```
|
|
408
|
+
|
|
373
409
|
For guidance on recommended values, see McKevitt et al. (2025) (in prep.).
|
|
374
410
|
|
|
411
|
+
By default, both the DN and photon signals are fitted at every Monte Carlo iteration. To speed up the simulation when only one is needed, use the `fit_signals` option:
|
|
412
|
+
|
|
413
|
+
```yaml
|
|
414
|
+
fit_signals: dn # Fit only the DN signal
|
|
415
|
+
fit_signals: photon # Fit only the photon signal
|
|
416
|
+
fit_signals: both # Fit both (default)
|
|
417
|
+
```
|
|
418
|
+
|
|
375
419
|
If you synthesised data in dynamic mode, your configuration must specify:
|
|
376
420
|
- Exactly one slit width matching the synthesis slit width
|
|
377
421
|
- Exactly one exposure time matching the synthesis exposure time
|
|
@@ -387,6 +431,12 @@ eclipse --config ./run/input/config.yaml
|
|
|
387
431
|
- `--config`: Path to YAML configuration file (required)
|
|
388
432
|
- `--debug`: Enable debug mode with IPython breakpoints on errors (optional)
|
|
389
433
|
|
|
434
|
+
#### Multi-node MPI parallelisation
|
|
435
|
+
|
|
436
|
+
When launched with multiple MPI ranks on a SLURM cluster (via `srun` or `mpirun`, and setting `--ntasks-per-node`), ECLIPSE automatically distributes Monte Carlo iterations across ranks and gathers results on rank 0. No code or configuration changes are needed - MPI is auto-detected at runtime. If `mpi4py` is not installed or only one rank is present, the code falls back to single-process mode.
|
|
437
|
+
|
|
438
|
+
Requirements: `mpi4py` and `intel-mpi` (load with `module load intel-mpi` before launching).
|
|
439
|
+
|
|
390
440
|
#### Output
|
|
391
441
|
|
|
392
442
|
Results are saved as pickle files in the `run/result/` directory with the same base name as the configuration file. The output includes:
|
|
@@ -30,7 +30,7 @@ After installation, you can run ECLIPSE from the command line:
|
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
32
|
# Run synthesis script (convert 3D MHD data to synthetic spectra)
|
|
33
|
-
synthesise-spectra --data-dir ./data/atmosphere --
|
|
33
|
+
synthesise-spectra --data-dir ./data/atmosphere --lines Fe12_195.1190 --output-dir ./run/input
|
|
34
34
|
|
|
35
35
|
# Run instrument response simulation
|
|
36
36
|
eclipse --config ./run/input/config.yaml
|
|
@@ -54,7 +54,7 @@ detector = Detector_SWC()
|
|
|
54
54
|
print(f"Telescope collecting area: {telescope.collecting_area:.4f}")
|
|
55
55
|
print(f"Detector QE (EUV): {detector.qe_euv:.2f}")
|
|
56
56
|
|
|
57
|
-
# Calculate effective area at Fe XII 195.119
|
|
57
|
+
# Calculate effective area at Fe XII 195.119 Angstrom
|
|
58
58
|
fe12_wl = 195.119 * u.AA
|
|
59
59
|
effective_area = telescope.collecting_area * telescope.throughput(fe12_wl) * detector.qe_euv
|
|
60
60
|
|
|
@@ -84,22 +84,24 @@ from euvst_response import (
|
|
|
84
84
|
create_sunpy_maps_from_combo,
|
|
85
85
|
summary_table
|
|
86
86
|
)
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
## Detailed instructions
|
|
90
87
|
|
|
91
|
-
|
|
88
|
+
# Load results
|
|
89
|
+
results = load_instrument_response_results("run/result/my_results.pkl")
|
|
92
90
|
|
|
93
|
-
|
|
91
|
+
# Get results for a specific parameter combination:
|
|
92
|
+
combo = get_results_for_combination(
|
|
93
|
+
results, exposure=40*u.s, slit_width=0.4*u.arcsec, offchip_bin_slit=2
|
|
94
|
+
)
|
|
94
95
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
idl -e "make_goft"
|
|
96
|
+
# Create SunPy maps
|
|
97
|
+
maps = create_sunpy_maps_from_combo(combo, rest_wavelength=195.119*u.AA, data_type='dn')
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
## Detailed instructions
|
|
101
|
+
|
|
102
|
+
### 1. Run the line synthesis
|
|
101
103
|
|
|
102
|
-
The synthesis script converts 3D MHD simulation data into synthetic solar spectra.
|
|
104
|
+
The synthesis script converts 3D MHD simulation data into synthetic solar spectra. Contribution functions G(T, n_e) are computed on-the-fly using [fiasco](https://fiasco.readthedocs.io/) (a Python interface to the CHIANTI atomic database).
|
|
103
105
|
|
|
104
106
|
#### Basic Usage
|
|
105
107
|
|
|
@@ -107,7 +109,9 @@ The synthesis script converts 3D MHD simulation data into synthetic solar spectr
|
|
|
107
109
|
# Example using all available command line options
|
|
108
110
|
synthesise-spectra \
|
|
109
111
|
--data-dir ./data/atmosphere \
|
|
110
|
-
--
|
|
112
|
+
--lines Fe12_195.1190 Fe12_195.1790 \
|
|
113
|
+
--abundance sun_coronal_2021_chianti \
|
|
114
|
+
--n-workers 4 \
|
|
111
115
|
--output-dir ./run/input \
|
|
112
116
|
--output-name synthesised_spectra.pkl \
|
|
113
117
|
--temp-file temp/eosT.0270000 \
|
|
@@ -127,8 +131,7 @@ synthesise-spectra \
|
|
|
127
131
|
--crop-z "0 Mm" "20 Mm" \
|
|
128
132
|
--downsample 1 \
|
|
129
133
|
--precision float64 \
|
|
130
|
-
--mean-mol-wt 1.29
|
|
131
|
-
--limit-lines Fe12_195.1190
|
|
134
|
+
--mean-mol-wt 1.29
|
|
132
135
|
|
|
133
136
|
# Show all available options
|
|
134
137
|
synthesise-spectra --help
|
|
@@ -138,10 +141,14 @@ synthesise-spectra --help
|
|
|
138
141
|
|
|
139
142
|
**Input/Output Paths:**
|
|
140
143
|
- `--data-dir`: Directory containing simulation data (default: `data/atmosphere`)
|
|
141
|
-
- `--goft-file`: Path to CHIANTI G(T,N) save file (default: `./data/gofnt.sav`)
|
|
142
144
|
- `--output-dir`: Output directory for results (default: `./run/input`)
|
|
143
145
|
- `--output-name`: Output filename (default: `synthesised_spectra.pkl`)
|
|
144
146
|
|
|
147
|
+
**Line and Abundance Selection:**
|
|
148
|
+
- `--lines`: Emission lines to synthesise, e.g., `--lines Fe12_195.1190 Fe12_195.1790` (required)
|
|
149
|
+
- `--abundance`: CHIANTI abundance dataset name (default: `sun_coronal_2021_chianti`)
|
|
150
|
+
- `--n-workers`: Number of parallel workers for the fiasco G(T, n_e) computation. Each distinct ion is computed in a separate process. `0` uses all available CPUs (default: `0`). Set to `1` for serial execution.
|
|
151
|
+
|
|
145
152
|
**Simulation Files:**
|
|
146
153
|
- `--temp-file`: Temperature file relative to data-dir (default: `temp/eosT.0270000`)
|
|
147
154
|
- `--rho-file`: Density file relative to data-dir (default: `rho/result_prim_0.0270000`)
|
|
@@ -174,9 +181,6 @@ synthesise-spectra --help
|
|
|
174
181
|
- `--precision`: Numerical precision `float32` or `float64` (default: `float64`)
|
|
175
182
|
- `--mean-mol-wt`: Mean molecular weight (default: `1.29`)
|
|
176
183
|
|
|
177
|
-
**Line Selection:**
|
|
178
|
-
- `--limit-lines`: Limit to specific lines, e.g., `--limit-lines Fe12_195.1190 Fe12_195.1790`
|
|
179
|
-
|
|
180
184
|
#### Dynamic Mode (Time-varying Atmospheres)
|
|
181
185
|
|
|
182
186
|
For simulating raster scans over evolving atmospheres, use dynamic mode which combines MHD timesteps based on instrument scanning:
|
|
@@ -184,7 +188,8 @@ For simulating raster scans over evolving atmospheres, use dynamic mode which co
|
|
|
184
188
|
```bash
|
|
185
189
|
synthesise-spectra \
|
|
186
190
|
--data-dir ./data/atmosphere \
|
|
187
|
-
--
|
|
191
|
+
--lines Fe12_195.1190 \
|
|
192
|
+
--abundance sun_coronal_2021_chianti \
|
|
188
193
|
--output-dir ./run/input \
|
|
189
194
|
--slit-rest-time "40 s" \
|
|
190
195
|
--slit-width "0.2 arcsec" \
|
|
@@ -222,7 +227,6 @@ The synthesis produces a pickle file containing:
|
|
|
222
227
|
|
|
223
228
|
- Use `--downsample 2` or `--downsample 4` for initial testing
|
|
224
229
|
- Use `--precision float32` to reduce memory usage (may affect accuracy)
|
|
225
|
-
- Use `--limit-lines` to synthesise only specific lines for development
|
|
226
230
|
- Use spatial cropping to focus on regions of interest and reduce computation time
|
|
227
231
|
- Monitor memory usage - full resolution synthesis can require 50+ GB RAM
|
|
228
232
|
- Side views (`--integration-axis x` or `y`) may require different velocity files
|
|
@@ -235,7 +239,7 @@ The synthesis results can be loaded and analyzed using the package API:
|
|
|
235
239
|
import euvst_response
|
|
236
240
|
|
|
237
241
|
# Load synthesis results - this sums all line cubes into a single cube
|
|
238
|
-
# By default uses Fe XII 195.119
|
|
242
|
+
# By default uses Fe XII 195.119 Angstrom as reference for wavelength grid
|
|
239
243
|
cube = euvst_response.load_atmosphere("./run/input/synthesised_spectra.pkl")
|
|
240
244
|
print(f"Combined cube shape: {cube.data.shape}")
|
|
241
245
|
|
|
@@ -259,7 +263,7 @@ This step can require a lot of memory at full resolution. A fully synthesised at
|
|
|
259
263
|
|
|
260
264
|
**Important:** You can place the synthesised atmosphere file anywhere and specify its location using the `synthesis_file` parameter in your YAML configuration file. The default location is `./run/input/synthesised_spectra.pkl`.
|
|
261
265
|
|
|
262
|
-
###
|
|
266
|
+
### 2. Simulate the instrument response
|
|
263
267
|
|
|
264
268
|
#### Configuration File
|
|
265
269
|
|
|
@@ -283,7 +287,7 @@ instrument: SWC # Options: SWC (EUVST Short Wavelength) or EIS (Hinode/EIS)
|
|
|
283
287
|
synthesis_file: ./run/input/synthesised_spectra.pkl # Default location
|
|
284
288
|
|
|
285
289
|
# Reference line for wavelength grid and metadata when combining all spectral lines
|
|
286
|
-
reference_line: Fe12_195.1190 # Default reference line (Fe XII 195.119
|
|
290
|
+
reference_line: Fe12_195.1190 # Default reference line (Fe XII 195.119 Angstrom)
|
|
287
291
|
|
|
288
292
|
# Point Spread Function
|
|
289
293
|
psf: False # Enable PSF convolution
|
|
@@ -317,20 +321,53 @@ aluminium_thickness: 1485 angstrom # Default (expected value)
|
|
|
317
321
|
ccd_temperature: -60 Celsius # Default (expected operating temperature)
|
|
318
322
|
|
|
319
323
|
# Visible stray light level
|
|
320
|
-
vis_sl: 0 photon / (s *
|
|
324
|
+
vis_sl: 0 photon / (s * cm**2) # Default, (ideal case, no stray light)
|
|
321
325
|
|
|
322
326
|
# Multi-component Gaussian fitting (optional, omit for single-Gaussian)
|
|
323
327
|
fitting:
|
|
324
328
|
primary_component: 0
|
|
329
|
+
constrain_positive_intensity: true
|
|
325
330
|
components:
|
|
326
331
|
- wavelength: 195.119 angstrom
|
|
332
|
+
amplitude_greater_than: 1 # must be brighter than component 1
|
|
327
333
|
- wavelength: 195.179 angstrom
|
|
328
334
|
tie_center: 0 # Centroid offset tied to component 0
|
|
329
335
|
tie_width: 0 # Same width as component 0
|
|
330
336
|
```
|
|
331
337
|
|
|
338
|
+
Off-chip binning in the slit direction can also be performed:
|
|
339
|
+
|
|
340
|
+
```yaml
|
|
341
|
+
offchip_bin_slit: 2 # Bin every 2 slit pixels
|
|
342
|
+
offchip_bin_slit: [1, 2, 4] # Sweep over multiple binning factors
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Alternatively, you can specify paired slit width and off-chip binning values to only simulate specific combinations:
|
|
346
|
+
|
|
347
|
+
```yaml
|
|
348
|
+
# Paired slit width / off-chip binning
|
|
349
|
+
# Only the listed (slit_width, offchip_bin_slit) pairs are simulated
|
|
350
|
+
slit_bin_pairs:
|
|
351
|
+
- slit_width: 0.2 arcsec
|
|
352
|
+
offchip_bin_slit: 1
|
|
353
|
+
- slit_width: 0.4 arcsec
|
|
354
|
+
offchip_bin_slit: 2
|
|
355
|
+
- slit_width: 0.8 arcsec
|
|
356
|
+
offchip_bin_slit: 5
|
|
357
|
+
- slit_width: 1.6 arcsec
|
|
358
|
+
offchip_bin_slit: 10
|
|
359
|
+
```
|
|
360
|
+
|
|
332
361
|
For guidance on recommended values, see McKevitt et al. (2025) (in prep.).
|
|
333
362
|
|
|
363
|
+
By default, both the DN and photon signals are fitted at every Monte Carlo iteration. To speed up the simulation when only one is needed, use the `fit_signals` option:
|
|
364
|
+
|
|
365
|
+
```yaml
|
|
366
|
+
fit_signals: dn # Fit only the DN signal
|
|
367
|
+
fit_signals: photon # Fit only the photon signal
|
|
368
|
+
fit_signals: both # Fit both (default)
|
|
369
|
+
```
|
|
370
|
+
|
|
334
371
|
If you synthesised data in dynamic mode, your configuration must specify:
|
|
335
372
|
- Exactly one slit width matching the synthesis slit width
|
|
336
373
|
- Exactly one exposure time matching the synthesis exposure time
|
|
@@ -346,6 +383,12 @@ eclipse --config ./run/input/config.yaml
|
|
|
346
383
|
- `--config`: Path to YAML configuration file (required)
|
|
347
384
|
- `--debug`: Enable debug mode with IPython breakpoints on errors (optional)
|
|
348
385
|
|
|
386
|
+
#### Multi-node MPI parallelisation
|
|
387
|
+
|
|
388
|
+
When launched with multiple MPI ranks on a SLURM cluster (via `srun` or `mpirun`, and setting `--ntasks-per-node`), ECLIPSE automatically distributes Monte Carlo iterations across ranks and gathers results on rank 0. No code or configuration changes are needed - MPI is auto-detected at runtime. If `mpi4py` is not installed or only one rank is present, the code falls back to single-process mode.
|
|
389
|
+
|
|
390
|
+
Requirements: `mpi4py` and `intel-mpi` (load with `module load intel-mpi` before launching).
|
|
391
|
+
|
|
349
392
|
#### Output
|
|
350
393
|
|
|
351
394
|
Results are saved as pickle files in the `run/result/` directory with the same base name as the configuration file. The output includes:
|
|
@@ -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.4"
|
|
8
8
|
__author__ = "James McKevitt"
|
|
9
9
|
__email__ = "jm2@mssl.ucl.ac.uk"
|
|
10
10
|
|
|
@@ -150,6 +150,11 @@ def analyse_fit_statistics(
|
|
|
150
150
|
raise ValueError(f"No {fit_stats_key} found in combination results")
|
|
151
151
|
|
|
152
152
|
fit_stats = combination_results[fit_stats_key]
|
|
153
|
+
if fit_stats is None:
|
|
154
|
+
raise ValueError(
|
|
155
|
+
f"'{data_type}' signal was not fitted for this combination. "
|
|
156
|
+
f"Check the 'fit_signals' setting in your YAML config."
|
|
157
|
+
)
|
|
153
158
|
fit_truth_data = combination_results["ground_truth"]["fit_truth_data"]
|
|
154
159
|
fit_truth_units = combination_results["ground_truth"]["fit_truth_units"]
|
|
155
160
|
|
|
@@ -214,6 +219,7 @@ def get_results_for_combination(
|
|
|
214
219
|
exposure: u.Quantity = None,
|
|
215
220
|
psf: bool = None,
|
|
216
221
|
enable_pinholes: bool = None,
|
|
222
|
+
offchip_bin_slit: int = None,
|
|
217
223
|
debug: bool = False
|
|
218
224
|
) -> Dict[str, Any]:
|
|
219
225
|
"""
|
|
@@ -241,6 +247,8 @@ def get_results_for_combination(
|
|
|
241
247
|
PSF setting (True or False). If None, uses first available.
|
|
242
248
|
enable_pinholes : bool, optional
|
|
243
249
|
Pinhole effects setting (True or False). If None, uses first available.
|
|
250
|
+
offchip_bin_slit : int, optional
|
|
251
|
+
Off-chip slit binning factor. If None, uses first available.
|
|
244
252
|
debug : bool, optional
|
|
245
253
|
If True, print debugging information about available keys.
|
|
246
254
|
|
|
@@ -268,7 +276,7 @@ def get_results_for_combination(
|
|
|
268
276
|
|
|
269
277
|
# Check if no parameters were specified at all
|
|
270
278
|
no_params_specified = all(param is None for param in [slit_width, oxide_thickness, c_thickness,
|
|
271
|
-
aluminium_thickness, ccd_temperature, vis_sl, exposure, psf, enable_pinholes])
|
|
279
|
+
aluminium_thickness, ccd_temperature, vis_sl, exposure, psf, enable_pinholes, offchip_bin_slit])
|
|
272
280
|
|
|
273
281
|
if no_params_specified and len(all_combinations) > 1:
|
|
274
282
|
print(f"Error: No parameters specified, but {len(all_combinations)} combinations are available!")
|
|
@@ -286,15 +294,24 @@ def get_results_for_combination(
|
|
|
286
294
|
'vis_sl': vis_sl,
|
|
287
295
|
'exposure': exposure,
|
|
288
296
|
'psf': psf,
|
|
289
|
-
'enable_pinholes': enable_pinholes
|
|
297
|
+
'enable_pinholes': enable_pinholes,
|
|
298
|
+
'offchip_bin_slit': offchip_bin_slit,
|
|
290
299
|
}
|
|
291
300
|
|
|
301
|
+
# Detect key format: 10-element (new, includes offchip_bin_slit) or 9-element (legacy)
|
|
302
|
+
sample_key = next(iter(all_combinations.keys()))
|
|
303
|
+
has_offchip_key = len(sample_key) == 10
|
|
304
|
+
|
|
292
305
|
# FIRST: Check if the specified parameters match multiple combinations
|
|
293
306
|
# before filling in any defaults
|
|
294
307
|
matching_combinations = []
|
|
295
308
|
|
|
296
309
|
for key in all_combinations.keys():
|
|
297
|
-
|
|
310
|
+
if has_offchip_key:
|
|
311
|
+
key_slit, key_oxide, key_carbon, key_aluminium, key_ccd, key_vis_sl, key_exposure, key_psf, key_enable_pinholes, key_offchip = key
|
|
312
|
+
else:
|
|
313
|
+
key_slit, key_oxide, key_carbon, key_aluminium, key_ccd, key_vis_sl, key_exposure, key_psf, key_enable_pinholes = key
|
|
314
|
+
key_offchip = 1 # legacy default
|
|
298
315
|
|
|
299
316
|
# Check if this combination matches all specified (non-None) parameters
|
|
300
317
|
matches = True
|
|
@@ -318,6 +335,8 @@ def get_results_for_combination(
|
|
|
318
335
|
matches = False
|
|
319
336
|
if enable_pinholes is not None and key_enable_pinholes != enable_pinholes:
|
|
320
337
|
matches = False
|
|
338
|
+
if offchip_bin_slit is not None and key_offchip != offchip_bin_slit:
|
|
339
|
+
matches = False
|
|
321
340
|
|
|
322
341
|
if matches:
|
|
323
342
|
matching_combinations.append(key)
|
|
@@ -328,9 +347,14 @@ def get_results_for_combination(
|
|
|
328
347
|
print(f"Use summary_table(results) to see all available parameter combinations.")
|
|
329
348
|
print(f"Matching combinations found:")
|
|
330
349
|
for i, combo in enumerate(matching_combinations[:5]): # Show first 5
|
|
331
|
-
|
|
350
|
+
if has_offchip_key:
|
|
351
|
+
slit, oxide, carbon, aluminium, ccd, vis_sl, exp, psf_val, enable_pinholes_val, offchip = combo
|
|
352
|
+
else:
|
|
353
|
+
slit, oxide, carbon, aluminium, ccd, vis_sl, exp, psf_val, enable_pinholes_val = combo
|
|
354
|
+
offchip = 1
|
|
332
355
|
print(f" {i+1}: slit={slit:.2f}arcsec, oxide={oxide:.1f}nm, carbon={carbon:.1f}nm, "
|
|
333
|
-
f"Al={aluminium:.0f}A, CCD={ccd:.1f}C, stray={vis_sl:.2g}, exp={exp:.1f}s,
|
|
356
|
+
f"Al={aluminium:.0f}A, CCD={ccd:.1f}C, stray={vis_sl:.2g}, exp={exp:.1f}s, "
|
|
357
|
+
f"psf={psf_val}, pinholes={enable_pinholes_val}, offchip_bin={offchip}")
|
|
334
358
|
if len(matching_combinations) > 5:
|
|
335
359
|
print(f" ... and {len(matching_combinations) - 5} more")
|
|
336
360
|
raise ValueError(f"Multiple combinations match your parameters. Please specify more parameters to select a unique combination.")
|
|
@@ -357,6 +381,9 @@ def get_results_for_combination(
|
|
|
357
381
|
psf = param_ranges["psf_settings"][0]
|
|
358
382
|
if enable_pinholes is None:
|
|
359
383
|
enable_pinholes = param_ranges["enable_pinholes_vals"][0]
|
|
384
|
+
if offchip_bin_slit is None:
|
|
385
|
+
offchip_bin_slits = param_ranges.get("offchip_bin_slits", [1])
|
|
386
|
+
offchip_bin_slit = offchip_bin_slits[0]
|
|
360
387
|
|
|
361
388
|
# Convert units to the same format as stored in keys (without units)
|
|
362
389
|
slit_width_val = slit_width.to_value(u.arcsec)
|
|
@@ -367,9 +394,15 @@ def get_results_for_combination(
|
|
|
367
394
|
vis_sl_val = vis_sl.to_value() if hasattr(vis_sl, 'to_value') else vis_sl
|
|
368
395
|
exposure_val = exposure.to_value(u.s)
|
|
369
396
|
|
|
370
|
-
#
|
|
371
|
-
|
|
372
|
-
|
|
397
|
+
# Build target key in the appropriate format
|
|
398
|
+
if has_offchip_key:
|
|
399
|
+
target_key = (slit_width_val, oxide_thickness_val, c_thickness_val,
|
|
400
|
+
aluminium_thickness_val, ccd_temperature_val, vis_sl_val, exposure_val,
|
|
401
|
+
psf, enable_pinholes, offchip_bin_slit)
|
|
402
|
+
else:
|
|
403
|
+
target_key = (slit_width_val, oxide_thickness_val, c_thickness_val,
|
|
404
|
+
aluminium_thickness_val, ccd_temperature_val, vis_sl_val, exposure_val,
|
|
405
|
+
psf, enable_pinholes)
|
|
373
406
|
|
|
374
407
|
if debug:
|
|
375
408
|
print(f"Target key: {target_key}")
|
|
@@ -450,25 +483,38 @@ def summary_table(results: Dict[str, Any]) -> None:
|
|
|
450
483
|
all_combinations = results["results"]["all_combinations"]
|
|
451
484
|
param_ranges = results["results"]["parameter_ranges"]
|
|
452
485
|
|
|
486
|
+
# Detect key format
|
|
487
|
+
sample_key = next(iter(all_combinations.keys()))
|
|
488
|
+
has_offchip = len(sample_key) == 10
|
|
489
|
+
|
|
453
490
|
print("Parameter Combination Summary")
|
|
454
|
-
print("=" *
|
|
455
|
-
|
|
456
|
-
|
|
491
|
+
print("=" * 165)
|
|
492
|
+
header = f"{'Slit (arcsec)':<12} {'Oxide (nm)':<12} {'Carbon (nm)':<12} {'Al (A)':<10} {'CCD (C)':<10} {'Stray Light':<12} {'Exp (s)':<10} {'PSF':<5} {'Pinholes':<8}"
|
|
493
|
+
if has_offchip:
|
|
494
|
+
header += f" {'Slit Bin':<8}"
|
|
495
|
+
print(header)
|
|
496
|
+
print("-" * 165)
|
|
457
497
|
|
|
458
498
|
for key, combo_results in all_combinations.items():
|
|
459
|
-
|
|
460
|
-
|
|
499
|
+
if has_offchip:
|
|
500
|
+
slit, oxide, carbon, aluminium, ccd_temp, vis_sl, exposure, psf, enable_pinholes, offchip = key
|
|
501
|
+
else:
|
|
502
|
+
slit, oxide, carbon, aluminium, ccd_temp, vis_sl, exposure, psf, enable_pinholes = key
|
|
503
|
+
offchip = 1
|
|
461
504
|
|
|
462
|
-
|
|
505
|
+
row = f"{slit:<12.2f} {oxide:<12.1f} {carbon:<12.1f} {aluminium:<10.0f} {ccd_temp:<10.1f} {vis_sl:<12.2g} {exposure:<10.1f} {str(psf):<5} {str(enable_pinholes):<8}"
|
|
506
|
+
if has_offchip:
|
|
507
|
+
row += f" {offchip:<8}"
|
|
508
|
+
print(row)
|
|
463
509
|
|
|
464
|
-
print("-" *
|
|
510
|
+
print("-" * 165)
|
|
465
511
|
print(f"Total combinations: {len(all_combinations)}")
|
|
466
512
|
print(f"Exposure times: {[exp.to_value(u.s) for exp in param_ranges['exposures']]}")
|
|
467
513
|
|
|
468
514
|
|
|
469
515
|
def create_sunpy_maps_from_combo(
|
|
470
516
|
combination_results: Dict[str, Any],
|
|
471
|
-
cube_reb,
|
|
517
|
+
cube_reb=None,
|
|
472
518
|
rest_wavelength: u.Quantity = 195.119 * u.AA,
|
|
473
519
|
data_type: str = "dn",
|
|
474
520
|
precision_requirement: u.Quantity = 2.0 * u.km / u.s,
|
|
@@ -482,8 +528,9 @@ def create_sunpy_maps_from_combo(
|
|
|
482
528
|
----------
|
|
483
529
|
combination_results : dict
|
|
484
530
|
Results for a specific parameter combination from get_results_for_combination().
|
|
485
|
-
cube_reb : NDCube
|
|
531
|
+
cube_reb : NDCube, optional
|
|
486
532
|
NDCube with helioprojective WCS to use for all maps.
|
|
533
|
+
If not provided, the WCS stored in the combination results is used.
|
|
487
534
|
rest_wavelength : u.Quantity, optional
|
|
488
535
|
Rest wavelength for velocity conversion (default: 195.119 A for Fe XII).
|
|
489
536
|
data_type : str, optional
|
|
@@ -526,14 +573,22 @@ def create_sunpy_maps_from_combo(
|
|
|
526
573
|
else:
|
|
527
574
|
analysis_per_exp = None
|
|
528
575
|
|
|
529
|
-
# Extract 2D helioprojective WCS from the cube
|
|
530
|
-
|
|
576
|
+
# Extract 2D helioprojective WCS from the cube or stored signal WCS
|
|
577
|
+
if cube_reb is not None:
|
|
578
|
+
wcs_2d = cube_reb.wcs.celestial.swapaxes(0, 1)
|
|
579
|
+
else:
|
|
580
|
+
wcs_2d = combination_results["first_signal_wcs"].celestial.swapaxes(0, 1)
|
|
531
581
|
|
|
532
582
|
# Get the data arrays - now only first iteration is saved
|
|
533
583
|
first_photon_signal = combination_results["first_photon_signal"] # Shape: (nx, ny, nwave)
|
|
534
584
|
first_dn_signal = combination_results["first_dn_signal"] # Shape: (nx, ny, nwave)
|
|
535
585
|
fit_stats_key = f"{data_type}_fit_stats"
|
|
536
586
|
fit_stats = combination_results[fit_stats_key] # Contains first_fit_data, mean_data, std_data, units
|
|
587
|
+
if fit_stats is None:
|
|
588
|
+
raise ValueError(
|
|
589
|
+
f"'{data_type}' signal was not fitted for this combination. "
|
|
590
|
+
f"Check the 'fit_signals' setting in your YAML config."
|
|
591
|
+
)
|
|
537
592
|
|
|
538
593
|
maps = {}
|
|
539
594
|
|