solarc-eclipse 0.6.2__tar.gz → 0.7.0__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.2/solarc_eclipse.egg-info → solarc_eclipse-0.7.0}/PKG-INFO +65 -34
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/README.md +56 -30
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/euvst_response/__init__.py +5 -4
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/euvst_response/analysis.py +66 -27
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/euvst_response/data_processing.py +17 -10
- solarc_eclipse-0.7.0/euvst_response/extern/__init__.py +0 -0
- solarc_eclipse-0.7.0/euvst_response/extern/mpfit.py +2388 -0
- solarc_eclipse-0.7.0/euvst_response/fitting.py +639 -0
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/euvst_response/main.py +234 -83
- solarc_eclipse-0.7.0/euvst_response/monte_carlo.py +299 -0
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/euvst_response/radiometric.py +53 -22
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/euvst_response/synthesis.py +182 -56
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/euvst_response/utils.py +113 -14
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/pyproject.toml +9 -3
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/setup.py +2 -27
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0/solarc_eclipse.egg-info}/PKG-INFO +65 -34
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/solarc_eclipse.egg-info/SOURCES.txt +2 -0
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/solarc_eclipse.egg-info/requires.txt +8 -0
- solarc_eclipse-0.6.2/euvst_response/fitting.py +0 -144
- solarc_eclipse-0.6.2/euvst_response/monte_carlo.py +0 -207
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/LICENSE +0 -0
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/MANIFEST.in +0 -0
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/euvst_response/cli.py +0 -0
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/euvst_response/config.py +0 -0
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/euvst_response/data/throughput/grating_reflection_efficiency.dat +0 -0
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/euvst_response/data/throughput/primary_mirror_coating_reflectance.dat +0 -0
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/euvst_response/data/throughput/source.txt +0 -0
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/euvst_response/data/throughput/throughput_aluminium_1000_angstrom.dat +0 -0
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/euvst_response/data/throughput/throughput_aluminium_oxide_1000_angstrom.dat +0 -0
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/euvst_response/data/throughput/throughput_carbon_1000_angstrom.dat +0 -0
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/euvst_response/pinhole_diffraction.py +0 -0
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/euvst_response/psf.py +0 -0
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/euvst_response/synthesis_cli.py +0 -0
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/setup.cfg +0 -0
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/solarc_eclipse.egg-info/dependency_links.txt +0 -0
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/solarc_eclipse.egg-info/entry_points.txt +0 -0
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/solarc_eclipse.egg-info/not-zip-safe +0 -0
- {solarc_eclipse-0.6.2 → solarc_eclipse-0.7.0}/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.
|
|
3
|
+
Version: 0.7.0
|
|
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
|
|
@@ -31,6 +29,13 @@ Requires-Dist: dill
|
|
|
31
29
|
Requires-Dist: pyyaml
|
|
32
30
|
Requires-Dist: reproject
|
|
33
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"
|
|
34
39
|
Provides-Extra: dev
|
|
35
40
|
Requires-Dist: pytest; extra == "dev"
|
|
36
41
|
Requires-Dist: black; extra == "dev"
|
|
@@ -73,7 +78,7 @@ After installation, you can run ECLIPSE from the command line:
|
|
|
73
78
|
|
|
74
79
|
```bash
|
|
75
80
|
# Run synthesis script (convert 3D MHD data to synthetic spectra)
|
|
76
|
-
synthesise-spectra --data-dir ./data/atmosphere --
|
|
81
|
+
synthesise-spectra --data-dir ./data/atmosphere --lines Fe12_195.1190 --output-dir ./run/input
|
|
77
82
|
|
|
78
83
|
# Run instrument response simulation
|
|
79
84
|
eclipse --config ./run/input/config.yaml
|
|
@@ -98,7 +103,7 @@ detector = Detector_SWC()
|
|
|
98
103
|
print(f"Telescope collecting area: {telescope.collecting_area:.4f}")
|
|
99
104
|
print(f"Detector QE (EUV): {detector.qe_euv:.2f}")
|
|
100
105
|
|
|
101
|
-
# Calculate effective area at Fe XII 195.119
|
|
106
|
+
# Calculate effective area at Fe XII 195.119 Angstrom
|
|
102
107
|
fe12_wl = 195.119 * u.AA
|
|
103
108
|
effective_area = telescope.collecting_area * telescope.throughput(fe12_wl) * detector.qe_euv
|
|
104
109
|
|
|
@@ -141,18 +146,9 @@ combo = get_results_for_combination(results, **{"simulation.expos": 40*u.s, "sim
|
|
|
141
146
|
|
|
142
147
|
## Detailed instructions
|
|
143
148
|
|
|
144
|
-
### 1.
|
|
149
|
+
### 1. Run the line synthesis
|
|
145
150
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
Run the following command:
|
|
149
|
-
```bash
|
|
150
|
-
idl -e "make_goft"
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
### 2. Run the line synthesis
|
|
154
|
-
|
|
155
|
-
The synthesis script converts 3D MHD simulation data into synthetic solar spectra. It can be run directly from the command line with extensive configuration options.
|
|
151
|
+
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).
|
|
156
152
|
|
|
157
153
|
#### Basic Usage
|
|
158
154
|
|
|
@@ -160,7 +156,9 @@ The synthesis script converts 3D MHD simulation data into synthetic solar spectr
|
|
|
160
156
|
# Example using all available command line options
|
|
161
157
|
synthesise-spectra \
|
|
162
158
|
--data-dir ./data/atmosphere \
|
|
163
|
-
--
|
|
159
|
+
--lines Fe12_195.1190 Fe12_195.1790 \
|
|
160
|
+
--abundance sun_coronal_2021_chianti \
|
|
161
|
+
--n-workers 4 \
|
|
164
162
|
--output-dir ./run/input \
|
|
165
163
|
--output-name synthesised_spectra.pkl \
|
|
166
164
|
--temp-file temp/eosT.0270000 \
|
|
@@ -180,8 +178,7 @@ synthesise-spectra \
|
|
|
180
178
|
--crop-z "0 Mm" "20 Mm" \
|
|
181
179
|
--downsample 1 \
|
|
182
180
|
--precision float64 \
|
|
183
|
-
--mean-mol-wt 1.29
|
|
184
|
-
--limit-lines Fe12_195.1190
|
|
181
|
+
--mean-mol-wt 1.29
|
|
185
182
|
|
|
186
183
|
# Show all available options
|
|
187
184
|
synthesise-spectra --help
|
|
@@ -191,10 +188,14 @@ synthesise-spectra --help
|
|
|
191
188
|
|
|
192
189
|
**Input/Output Paths:**
|
|
193
190
|
- `--data-dir`: Directory containing simulation data (default: `data/atmosphere`)
|
|
194
|
-
- `--goft-file`: Path to CHIANTI G(T,N) save file (default: `./data/gofnt.sav`)
|
|
195
191
|
- `--output-dir`: Output directory for results (default: `./run/input`)
|
|
196
192
|
- `--output-name`: Output filename (default: `synthesised_spectra.pkl`)
|
|
197
193
|
|
|
194
|
+
**Line and Abundance Selection:**
|
|
195
|
+
- `--lines`: Emission lines to synthesise, e.g., `--lines Fe12_195.1190 Fe12_195.1790` (required)
|
|
196
|
+
- `--abundance`: CHIANTI abundance dataset name (default: `sun_coronal_2021_chianti`)
|
|
197
|
+
- `--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.
|
|
198
|
+
|
|
198
199
|
**Simulation Files:**
|
|
199
200
|
- `--temp-file`: Temperature file relative to data-dir (default: `temp/eosT.0270000`)
|
|
200
201
|
- `--rho-file`: Density file relative to data-dir (default: `rho/result_prim_0.0270000`)
|
|
@@ -227,9 +228,6 @@ synthesise-spectra --help
|
|
|
227
228
|
- `--precision`: Numerical precision `float32` or `float64` (default: `float64`)
|
|
228
229
|
- `--mean-mol-wt`: Mean molecular weight (default: `1.29`)
|
|
229
230
|
|
|
230
|
-
**Line Selection:**
|
|
231
|
-
- `--limit-lines`: Limit to specific lines, e.g., `--limit-lines Fe12_195.1190 Fe12_195.1790`
|
|
232
|
-
|
|
233
231
|
#### Dynamic Mode (Time-varying Atmospheres)
|
|
234
232
|
|
|
235
233
|
For simulating raster scans over evolving atmospheres, use dynamic mode which combines MHD timesteps based on instrument scanning:
|
|
@@ -237,7 +235,8 @@ For simulating raster scans over evolving atmospheres, use dynamic mode which co
|
|
|
237
235
|
```bash
|
|
238
236
|
synthesise-spectra \
|
|
239
237
|
--data-dir ./data/atmosphere \
|
|
240
|
-
--
|
|
238
|
+
--lines Fe12_195.1190 \
|
|
239
|
+
--abundance sun_coronal_2021_chianti \
|
|
241
240
|
--output-dir ./run/input \
|
|
242
241
|
--slit-rest-time "40 s" \
|
|
243
242
|
--slit-width "0.2 arcsec" \
|
|
@@ -275,7 +274,6 @@ The synthesis produces a pickle file containing:
|
|
|
275
274
|
|
|
276
275
|
- Use `--downsample 2` or `--downsample 4` for initial testing
|
|
277
276
|
- Use `--precision float32` to reduce memory usage (may affect accuracy)
|
|
278
|
-
- Use `--limit-lines` to synthesise only specific lines for development
|
|
279
277
|
- Use spatial cropping to focus on regions of interest and reduce computation time
|
|
280
278
|
- Monitor memory usage - full resolution synthesis can require 50+ GB RAM
|
|
281
279
|
- Side views (`--integration-axis x` or `y`) may require different velocity files
|
|
@@ -288,7 +286,7 @@ The synthesis results can be loaded and analyzed using the package API:
|
|
|
288
286
|
import euvst_response
|
|
289
287
|
|
|
290
288
|
# Load synthesis results - this sums all line cubes into a single cube
|
|
291
|
-
# By default uses Fe XII 195.119
|
|
289
|
+
# By default uses Fe XII 195.119 Angstrom as reference for wavelength grid
|
|
292
290
|
cube = euvst_response.load_atmosphere("./run/input/synthesised_spectra.pkl")
|
|
293
291
|
print(f"Combined cube shape: {cube.data.shape}")
|
|
294
292
|
|
|
@@ -306,13 +304,7 @@ print(f"Rest wavelength: {fe12_195.meta['rest_wav']}")
|
|
|
306
304
|
print(f"Available spectral lines: {list(data['line_cubes'].keys())}")
|
|
307
305
|
```
|
|
308
306
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
This step can require a lot of memory at full resolution. A fully synthesised atmosphere using the Cheung et al. (2018) atmosphere (doi:10.1038/s41550-018-0629-3) for the Fe XII 195.119 and 195.179 lines, including 5 background lines from each side, can be downloaded here: https://liveuclac-my.sharepoint.com/:f:/g/personal/ucasjem_ucl_ac_uk/Es-ts6rwXIlInAweGI7hmdMB5BoGqv9uSpIXOvMkzhS3cw?e=54si7R
|
|
312
|
-
|
|
313
|
-
**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`.
|
|
314
|
-
|
|
315
|
-
### 3. Simulate the instrument response
|
|
307
|
+
### 2. Simulate the instrument response
|
|
316
308
|
|
|
317
309
|
#### Configuration File
|
|
318
310
|
|
|
@@ -328,6 +320,7 @@ simulation runs every combination (cartesian product).
|
|
|
328
320
|
- `reference_line`: spectral line used as the wavelength-grid reference (default `Fe12_195.1190`)
|
|
329
321
|
- `n_iter`: number of Monte Carlo iterations
|
|
330
322
|
- `ncpu`: CPU cores to use (`-1` = all available)
|
|
323
|
+
- `offchip_bin_slit`: off-chip slit binning factor (default `1`)
|
|
331
324
|
- `pinhole_sizes`, `pinhole_positions`: fixed paired lists for pinhole diffraction tests (SWC only)
|
|
332
325
|
- `uniform_intensity`, `rest_wavelength`, `thermal_width`: uniform-intensity mode (alternative to synthesis file)
|
|
333
326
|
|
|
@@ -342,6 +335,7 @@ reference_line: Fe12_195.1190
|
|
|
342
335
|
# Global settings (apply to all combinations)
|
|
343
336
|
n_iter: 500
|
|
344
337
|
ncpu: -1
|
|
338
|
+
offchip_bin_slit: [1, 2] # sweep no-binning and 2-pixel off-chip binning
|
|
345
339
|
|
|
346
340
|
# Simulation parameters
|
|
347
341
|
# Any field listed as a list is swept over; all combinations are run.
|
|
@@ -380,6 +374,37 @@ detector:
|
|
|
380
374
|
|
|
381
375
|
For guidance on recommended values, see McKevitt et al. (2025) (in prep.).
|
|
382
376
|
|
|
377
|
+
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:
|
|
378
|
+
|
|
379
|
+
```yaml
|
|
380
|
+
fit_signals: dn # Fit only the DN signal
|
|
381
|
+
fit_signals: photon # Fit only the photon signal
|
|
382
|
+
fit_signals: both # Fit both (default)
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
To fit blended spectral lines with multiple Gaussian components, add a `fitting` block:
|
|
386
|
+
|
|
387
|
+
```yaml
|
|
388
|
+
fitting:
|
|
389
|
+
primary_component: 0 # index of the component whose velocity is reported
|
|
390
|
+
constrain_positive_intensity: true # reject fits with negative amplitudes
|
|
391
|
+
backend: scipy # optimiser: "scipy" (default) or "mpfit"
|
|
392
|
+
components:
|
|
393
|
+
- wavelength: 195.119 angstrom # component 0: free centre, width, amplitude
|
|
394
|
+
- wavelength: 195.179 angstrom # component 1: centre & width tied to component 0
|
|
395
|
+
tie_center: 0
|
|
396
|
+
tie_width: 0
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
Each component requires a `wavelength` field giving its rest wavelength.
|
|
400
|
+
|
|
401
|
+
Each entry in `components` corresponds to one Gaussian. Optional per-component keys:
|
|
402
|
+
- `tie_center: <i>`: constrain this component's centre to match component *i*
|
|
403
|
+
- `tie_width: <i>`: constrain this component's line width to match component *i*
|
|
404
|
+
- `amplitude_greater_than: <i>`: constrain amplitude to exceed that of component *i*
|
|
405
|
+
|
|
406
|
+
Omitting the `fitting` block fits a single Gaussian (default behaviour).
|
|
407
|
+
|
|
383
408
|
If you synthesised data in dynamic mode, your configuration must specify:
|
|
384
409
|
- Exactly one slit width matching the synthesis slit width
|
|
385
410
|
- Exactly one exposure time matching the synthesis exposure time
|
|
@@ -395,6 +420,12 @@ eclipse --config ./run/input/config.yaml
|
|
|
395
420
|
- `--config`: Path to YAML configuration file (required)
|
|
396
421
|
- `--debug`: Enable debug mode with IPython breakpoints on errors (optional)
|
|
397
422
|
|
|
423
|
+
#### Multi-node MPI parallelisation
|
|
424
|
+
|
|
425
|
+
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.
|
|
426
|
+
|
|
427
|
+
Requirements: `mpi4py` and `intel-mpi` (load with `module load intel-mpi` before launching).
|
|
428
|
+
|
|
398
429
|
#### Output
|
|
399
430
|
|
|
400
431
|
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
|
|
@@ -55,7 +55,7 @@ detector = Detector_SWC()
|
|
|
55
55
|
print(f"Telescope collecting area: {telescope.collecting_area:.4f}")
|
|
56
56
|
print(f"Detector QE (EUV): {detector.qe_euv:.2f}")
|
|
57
57
|
|
|
58
|
-
# Calculate effective area at Fe XII 195.119
|
|
58
|
+
# Calculate effective area at Fe XII 195.119 Angstrom
|
|
59
59
|
fe12_wl = 195.119 * u.AA
|
|
60
60
|
effective_area = telescope.collecting_area * telescope.throughput(fe12_wl) * detector.qe_euv
|
|
61
61
|
|
|
@@ -98,18 +98,9 @@ combo = get_results_for_combination(results, **{"simulation.expos": 40*u.s, "sim
|
|
|
98
98
|
|
|
99
99
|
## Detailed instructions
|
|
100
100
|
|
|
101
|
-
### 1.
|
|
101
|
+
### 1. Run the line synthesis
|
|
102
102
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
Run the following command:
|
|
106
|
-
```bash
|
|
107
|
-
idl -e "make_goft"
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
### 2. Run the line synthesis
|
|
111
|
-
|
|
112
|
-
The synthesis script converts 3D MHD simulation data into synthetic solar spectra. It can be run directly from the command line with extensive configuration options.
|
|
103
|
+
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).
|
|
113
104
|
|
|
114
105
|
#### Basic Usage
|
|
115
106
|
|
|
@@ -117,7 +108,9 @@ The synthesis script converts 3D MHD simulation data into synthetic solar spectr
|
|
|
117
108
|
# Example using all available command line options
|
|
118
109
|
synthesise-spectra \
|
|
119
110
|
--data-dir ./data/atmosphere \
|
|
120
|
-
--
|
|
111
|
+
--lines Fe12_195.1190 Fe12_195.1790 \
|
|
112
|
+
--abundance sun_coronal_2021_chianti \
|
|
113
|
+
--n-workers 4 \
|
|
121
114
|
--output-dir ./run/input \
|
|
122
115
|
--output-name synthesised_spectra.pkl \
|
|
123
116
|
--temp-file temp/eosT.0270000 \
|
|
@@ -137,8 +130,7 @@ synthesise-spectra \
|
|
|
137
130
|
--crop-z "0 Mm" "20 Mm" \
|
|
138
131
|
--downsample 1 \
|
|
139
132
|
--precision float64 \
|
|
140
|
-
--mean-mol-wt 1.29
|
|
141
|
-
--limit-lines Fe12_195.1190
|
|
133
|
+
--mean-mol-wt 1.29
|
|
142
134
|
|
|
143
135
|
# Show all available options
|
|
144
136
|
synthesise-spectra --help
|
|
@@ -148,10 +140,14 @@ synthesise-spectra --help
|
|
|
148
140
|
|
|
149
141
|
**Input/Output Paths:**
|
|
150
142
|
- `--data-dir`: Directory containing simulation data (default: `data/atmosphere`)
|
|
151
|
-
- `--goft-file`: Path to CHIANTI G(T,N) save file (default: `./data/gofnt.sav`)
|
|
152
143
|
- `--output-dir`: Output directory for results (default: `./run/input`)
|
|
153
144
|
- `--output-name`: Output filename (default: `synthesised_spectra.pkl`)
|
|
154
145
|
|
|
146
|
+
**Line and Abundance Selection:**
|
|
147
|
+
- `--lines`: Emission lines to synthesise, e.g., `--lines Fe12_195.1190 Fe12_195.1790` (required)
|
|
148
|
+
- `--abundance`: CHIANTI abundance dataset name (default: `sun_coronal_2021_chianti`)
|
|
149
|
+
- `--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.
|
|
150
|
+
|
|
155
151
|
**Simulation Files:**
|
|
156
152
|
- `--temp-file`: Temperature file relative to data-dir (default: `temp/eosT.0270000`)
|
|
157
153
|
- `--rho-file`: Density file relative to data-dir (default: `rho/result_prim_0.0270000`)
|
|
@@ -184,9 +180,6 @@ synthesise-spectra --help
|
|
|
184
180
|
- `--precision`: Numerical precision `float32` or `float64` (default: `float64`)
|
|
185
181
|
- `--mean-mol-wt`: Mean molecular weight (default: `1.29`)
|
|
186
182
|
|
|
187
|
-
**Line Selection:**
|
|
188
|
-
- `--limit-lines`: Limit to specific lines, e.g., `--limit-lines Fe12_195.1190 Fe12_195.1790`
|
|
189
|
-
|
|
190
183
|
#### Dynamic Mode (Time-varying Atmospheres)
|
|
191
184
|
|
|
192
185
|
For simulating raster scans over evolving atmospheres, use dynamic mode which combines MHD timesteps based on instrument scanning:
|
|
@@ -194,7 +187,8 @@ For simulating raster scans over evolving atmospheres, use dynamic mode which co
|
|
|
194
187
|
```bash
|
|
195
188
|
synthesise-spectra \
|
|
196
189
|
--data-dir ./data/atmosphere \
|
|
197
|
-
--
|
|
190
|
+
--lines Fe12_195.1190 \
|
|
191
|
+
--abundance sun_coronal_2021_chianti \
|
|
198
192
|
--output-dir ./run/input \
|
|
199
193
|
--slit-rest-time "40 s" \
|
|
200
194
|
--slit-width "0.2 arcsec" \
|
|
@@ -232,7 +226,6 @@ The synthesis produces a pickle file containing:
|
|
|
232
226
|
|
|
233
227
|
- Use `--downsample 2` or `--downsample 4` for initial testing
|
|
234
228
|
- Use `--precision float32` to reduce memory usage (may affect accuracy)
|
|
235
|
-
- Use `--limit-lines` to synthesise only specific lines for development
|
|
236
229
|
- Use spatial cropping to focus on regions of interest and reduce computation time
|
|
237
230
|
- Monitor memory usage - full resolution synthesis can require 50+ GB RAM
|
|
238
231
|
- Side views (`--integration-axis x` or `y`) may require different velocity files
|
|
@@ -245,7 +238,7 @@ The synthesis results can be loaded and analyzed using the package API:
|
|
|
245
238
|
import euvst_response
|
|
246
239
|
|
|
247
240
|
# Load synthesis results - this sums all line cubes into a single cube
|
|
248
|
-
# By default uses Fe XII 195.119
|
|
241
|
+
# By default uses Fe XII 195.119 Angstrom as reference for wavelength grid
|
|
249
242
|
cube = euvst_response.load_atmosphere("./run/input/synthesised_spectra.pkl")
|
|
250
243
|
print(f"Combined cube shape: {cube.data.shape}")
|
|
251
244
|
|
|
@@ -263,13 +256,7 @@ print(f"Rest wavelength: {fe12_195.meta['rest_wav']}")
|
|
|
263
256
|
print(f"Available spectral lines: {list(data['line_cubes'].keys())}")
|
|
264
257
|
```
|
|
265
258
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
This step can require a lot of memory at full resolution. A fully synthesised atmosphere using the Cheung et al. (2018) atmosphere (doi:10.1038/s41550-018-0629-3) for the Fe XII 195.119 and 195.179 lines, including 5 background lines from each side, can be downloaded here: https://liveuclac-my.sharepoint.com/:f:/g/personal/ucasjem_ucl_ac_uk/Es-ts6rwXIlInAweGI7hmdMB5BoGqv9uSpIXOvMkzhS3cw?e=54si7R
|
|
269
|
-
|
|
270
|
-
**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`.
|
|
271
|
-
|
|
272
|
-
### 3. Simulate the instrument response
|
|
259
|
+
### 2. Simulate the instrument response
|
|
273
260
|
|
|
274
261
|
#### Configuration File
|
|
275
262
|
|
|
@@ -285,6 +272,7 @@ simulation runs every combination (cartesian product).
|
|
|
285
272
|
- `reference_line`: spectral line used as the wavelength-grid reference (default `Fe12_195.1190`)
|
|
286
273
|
- `n_iter`: number of Monte Carlo iterations
|
|
287
274
|
- `ncpu`: CPU cores to use (`-1` = all available)
|
|
275
|
+
- `offchip_bin_slit`: off-chip slit binning factor (default `1`)
|
|
288
276
|
- `pinhole_sizes`, `pinhole_positions`: fixed paired lists for pinhole diffraction tests (SWC only)
|
|
289
277
|
- `uniform_intensity`, `rest_wavelength`, `thermal_width`: uniform-intensity mode (alternative to synthesis file)
|
|
290
278
|
|
|
@@ -299,6 +287,7 @@ reference_line: Fe12_195.1190
|
|
|
299
287
|
# Global settings (apply to all combinations)
|
|
300
288
|
n_iter: 500
|
|
301
289
|
ncpu: -1
|
|
290
|
+
offchip_bin_slit: [1, 2] # sweep no-binning and 2-pixel off-chip binning
|
|
302
291
|
|
|
303
292
|
# Simulation parameters
|
|
304
293
|
# Any field listed as a list is swept over; all combinations are run.
|
|
@@ -337,6 +326,37 @@ detector:
|
|
|
337
326
|
|
|
338
327
|
For guidance on recommended values, see McKevitt et al. (2025) (in prep.).
|
|
339
328
|
|
|
329
|
+
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:
|
|
330
|
+
|
|
331
|
+
```yaml
|
|
332
|
+
fit_signals: dn # Fit only the DN signal
|
|
333
|
+
fit_signals: photon # Fit only the photon signal
|
|
334
|
+
fit_signals: both # Fit both (default)
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
To fit blended spectral lines with multiple Gaussian components, add a `fitting` block:
|
|
338
|
+
|
|
339
|
+
```yaml
|
|
340
|
+
fitting:
|
|
341
|
+
primary_component: 0 # index of the component whose velocity is reported
|
|
342
|
+
constrain_positive_intensity: true # reject fits with negative amplitudes
|
|
343
|
+
backend: scipy # optimiser: "scipy" (default) or "mpfit"
|
|
344
|
+
components:
|
|
345
|
+
- wavelength: 195.119 angstrom # component 0: free centre, width, amplitude
|
|
346
|
+
- wavelength: 195.179 angstrom # component 1: centre & width tied to component 0
|
|
347
|
+
tie_center: 0
|
|
348
|
+
tie_width: 0
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Each component requires a `wavelength` field giving its rest wavelength.
|
|
352
|
+
|
|
353
|
+
Each entry in `components` corresponds to one Gaussian. Optional per-component keys:
|
|
354
|
+
- `tie_center: <i>`: constrain this component's centre to match component *i*
|
|
355
|
+
- `tie_width: <i>`: constrain this component's line width to match component *i*
|
|
356
|
+
- `amplitude_greater_than: <i>`: constrain amplitude to exceed that of component *i*
|
|
357
|
+
|
|
358
|
+
Omitting the `fitting` block fits a single Gaussian (default behaviour).
|
|
359
|
+
|
|
340
360
|
If you synthesised data in dynamic mode, your configuration must specify:
|
|
341
361
|
- Exactly one slit width matching the synthesis slit width
|
|
342
362
|
- Exactly one exposure time matching the synthesis exposure time
|
|
@@ -352,6 +372,12 @@ eclipse --config ./run/input/config.yaml
|
|
|
352
372
|
- `--config`: Path to YAML configuration file (required)
|
|
353
373
|
- `--debug`: Enable debug mode with IPython breakpoints on errors (optional)
|
|
354
374
|
|
|
375
|
+
#### Multi-node MPI parallelisation
|
|
376
|
+
|
|
377
|
+
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.
|
|
378
|
+
|
|
379
|
+
Requirements: `mpi4py` and `intel-mpi` (load with `module load intel-mpi` before launching).
|
|
380
|
+
|
|
355
381
|
#### Output
|
|
356
382
|
|
|
357
383
|
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.
|
|
7
|
+
__version__ = "0.7.0"
|
|
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, create_uniform_intensity_cube
|
|
@@ -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",
|
|
@@ -119,7 +119,8 @@ def get_parameter_combinations(results: Dict[str, Any]) -> List[Dict]:
|
|
|
119
119
|
def analyse_fit_statistics(
|
|
120
120
|
combination_results: Dict[str, Any],
|
|
121
121
|
rest_wavelength: u.Quantity,
|
|
122
|
-
data_type: str = "dn"
|
|
122
|
+
data_type: str = "dn",
|
|
123
|
+
fit_config=None,
|
|
123
124
|
) -> Dict[str, Any]:
|
|
124
125
|
"""
|
|
125
126
|
Analyze fit statistics to compute velocity and line width statistics.
|
|
@@ -132,35 +133,52 @@ def analyse_fit_statistics(
|
|
|
132
133
|
Rest wavelength for velocity conversion.
|
|
133
134
|
data_type : str, optional
|
|
134
135
|
Either "dn" or "photon" to specify which fit statistics to analyze.
|
|
136
|
+
fit_config : FitConfig, optional
|
|
137
|
+
Multi-component fitting configuration. When provided the primary-
|
|
138
|
+
component indices are used; otherwise indices 1 (centre) and
|
|
139
|
+
2 (sigma) are assumed (single-component default).
|
|
135
140
|
|
|
136
141
|
Returns
|
|
137
142
|
-------
|
|
138
143
|
dict
|
|
139
144
|
Dictionary containing velocity and width statistics.
|
|
140
145
|
"""
|
|
146
|
+
# Determine parameter indices for the primary component
|
|
147
|
+
if fit_config is not None and not fit_config.is_single:
|
|
148
|
+
idx_center = fit_config.idx_center
|
|
149
|
+
idx_sigma = fit_config.idx_sigma
|
|
150
|
+
else:
|
|
151
|
+
idx_center = 1
|
|
152
|
+
idx_sigma = 2
|
|
153
|
+
|
|
141
154
|
# Get fit statistics
|
|
142
155
|
fit_stats_key = f"{data_type}_fit_stats"
|
|
143
156
|
if fit_stats_key not in combination_results:
|
|
144
157
|
raise ValueError(f"No {fit_stats_key} found in combination results")
|
|
145
158
|
|
|
146
159
|
fit_stats = combination_results[fit_stats_key]
|
|
160
|
+
if fit_stats is None:
|
|
161
|
+
raise ValueError(
|
|
162
|
+
f"'{data_type}' signal was not fitted for this combination. "
|
|
163
|
+
f"Check the 'fit_signals' setting in your YAML config."
|
|
164
|
+
)
|
|
147
165
|
fit_truth_data = combination_results["ground_truth"]["fit_truth_data"]
|
|
148
166
|
fit_truth_units = combination_results["ground_truth"]["fit_truth_units"]
|
|
149
167
|
|
|
150
168
|
# Extract data and units
|
|
151
|
-
mean_data = fit_stats["mean_data"] # Shape: (nx, ny,
|
|
152
|
-
std_data = fit_stats["std_data"] # Shape: (nx, ny,
|
|
153
|
-
units = fit_stats["units"] # List of
|
|
169
|
+
mean_data = fit_stats["mean_data"] # Shape: (nx, ny, n_params)
|
|
170
|
+
std_data = fit_stats["std_data"] # Shape: (nx, ny, n_params)
|
|
171
|
+
units = fit_stats["units"] # List of n_params astropy units
|
|
154
172
|
|
|
155
|
-
# Get center statistics
|
|
156
|
-
center_mean_data = mean_data[...,
|
|
157
|
-
center_std_data = std_data[...,
|
|
158
|
-
center_unit = units[
|
|
173
|
+
# Get center statistics for the primary component
|
|
174
|
+
center_mean_data = mean_data[..., idx_center]
|
|
175
|
+
center_std_data = std_data[..., idx_center]
|
|
176
|
+
center_unit = units[idx_center]
|
|
159
177
|
|
|
160
|
-
# Get width statistics
|
|
161
|
-
width_mean_data = mean_data[...,
|
|
162
|
-
width_std_data = std_data[...,
|
|
163
|
-
width_unit = units[
|
|
178
|
+
# Get width statistics for the primary component
|
|
179
|
+
width_mean_data = mean_data[..., idx_sigma]
|
|
180
|
+
width_std_data = std_data[..., idx_sigma]
|
|
181
|
+
width_unit = units[idx_sigma]
|
|
164
182
|
|
|
165
183
|
# Create quantities
|
|
166
184
|
center_mean_q = center_mean_data * center_unit
|
|
@@ -177,7 +195,7 @@ def analyse_fit_statistics(
|
|
|
177
195
|
|
|
178
196
|
# Convert to velocities
|
|
179
197
|
v_mean = centers_to_velocity(center_mean_q, rest_wavelength)
|
|
180
|
-
v_true = centers_to_velocity(fit_truth_data[...,
|
|
198
|
+
v_true = centers_to_velocity(fit_truth_data[..., idx_center] * fit_truth_units[idx_center], rest_wavelength)
|
|
181
199
|
v_err = v_true - v_mean
|
|
182
200
|
|
|
183
201
|
# Convert center std to velocity std using differential: dv/dlambda = c/lambda
|
|
@@ -377,11 +395,12 @@ def summary_table(results: Dict[str, Any]) -> None:
|
|
|
377
395
|
|
|
378
396
|
def create_sunpy_maps_from_combo(
|
|
379
397
|
combination_results: Dict[str, Any],
|
|
380
|
-
cube_reb,
|
|
398
|
+
cube_reb=None,
|
|
381
399
|
rest_wavelength: u.Quantity = 195.119 * u.AA,
|
|
382
400
|
data_type: str = "dn",
|
|
383
401
|
precision_requirement: u.Quantity = 2.0 * u.km / u.s,
|
|
384
|
-
exposure_time_results: List[Dict[str, Any]] | None = None
|
|
402
|
+
exposure_time_results: List[Dict[str, Any]] | None = None,
|
|
403
|
+
fit_config=None,
|
|
385
404
|
) -> Dict[str, Any]:
|
|
386
405
|
"""
|
|
387
406
|
Create SunPy maps from combination results using the new fit statistics structure.
|
|
@@ -390,8 +409,9 @@ def create_sunpy_maps_from_combo(
|
|
|
390
409
|
----------
|
|
391
410
|
combination_results : dict
|
|
392
411
|
Results for a specific parameter combination from get_results_for_combination().
|
|
393
|
-
cube_reb : NDCube
|
|
412
|
+
cube_reb : NDCube, optional
|
|
394
413
|
NDCube with helioprojective WCS to use for all maps.
|
|
414
|
+
If not provided, the WCS stored in the combination results is used.
|
|
395
415
|
rest_wavelength : u.Quantity, optional
|
|
396
416
|
Rest wavelength for velocity conversion (default: 195.119 A for Fe XII).
|
|
397
417
|
data_type : str, optional
|
|
@@ -401,6 +421,9 @@ def create_sunpy_maps_from_combo(
|
|
|
401
421
|
exposure_time_results : list of dict, optional
|
|
402
422
|
List of results from get_results_for_combination() for different exposure times.
|
|
403
423
|
If provided, will create an exposure time map showing minimum exposure needed.
|
|
424
|
+
fit_config : FitConfig, optional
|
|
425
|
+
Multi-component fitting configuration. When provided, the primary-
|
|
426
|
+
component indices are used to extract centre and width parameters.
|
|
404
427
|
|
|
405
428
|
Returns
|
|
406
429
|
-------
|
|
@@ -426,19 +449,27 @@ def create_sunpy_maps_from_combo(
|
|
|
426
449
|
# Extract exposure time from parameters
|
|
427
450
|
exposure_time = result["parameters"]["simulation.expos"].to_value(u.s)
|
|
428
451
|
# Create analysis for this exposure
|
|
429
|
-
analysis = analyse_fit_statistics(result, rest_wavelength, data_type)
|
|
452
|
+
analysis = analyse_fit_statistics(result, rest_wavelength, data_type, fit_config=fit_config)
|
|
430
453
|
analysis_per_exp[exposure_time] = analysis
|
|
431
454
|
else:
|
|
432
455
|
analysis_per_exp = None
|
|
433
456
|
|
|
434
|
-
# Extract 2D helioprojective WCS from the cube
|
|
435
|
-
|
|
457
|
+
# Extract 2D helioprojective WCS from the cube or stored signal WCS
|
|
458
|
+
if cube_reb is not None:
|
|
459
|
+
wcs_2d = cube_reb.wcs.celestial.swapaxes(0, 1)
|
|
460
|
+
else:
|
|
461
|
+
wcs_2d = combination_results["first_signal_wcs"].celestial.swapaxes(0, 1)
|
|
436
462
|
|
|
437
463
|
# Get the data arrays - now only first iteration is saved
|
|
438
464
|
first_photon_signal = combination_results["first_photon_signal"] # Shape: (nx, ny, nwave)
|
|
439
465
|
first_dn_signal = combination_results["first_dn_signal"] # Shape: (nx, ny, nwave)
|
|
440
466
|
fit_stats_key = f"{data_type}_fit_stats"
|
|
441
467
|
fit_stats = combination_results[fit_stats_key] # Contains first_fit_data, mean_data, std_data, units
|
|
468
|
+
if fit_stats is None:
|
|
469
|
+
raise ValueError(
|
|
470
|
+
f"'{data_type}' signal was not fitted for this combination. "
|
|
471
|
+
f"Check the 'fit_signals' setting in your YAML config."
|
|
472
|
+
)
|
|
442
473
|
|
|
443
474
|
maps = {}
|
|
444
475
|
|
|
@@ -457,14 +488,22 @@ def create_sunpy_maps_from_combo(
|
|
|
457
488
|
maps['total_dn'] = sunpy.map.Map(total_dn_data.T, wcs_2d)
|
|
458
489
|
maps['total_dn'].meta['bunit'] = str(total_dn_unit)
|
|
459
490
|
|
|
491
|
+
# Determine parameter indices for the primary component
|
|
492
|
+
if fit_config is not None and not fit_config.is_single:
|
|
493
|
+
idx_center = fit_config.idx_center
|
|
494
|
+
idx_sigma = fit_config.idx_sigma
|
|
495
|
+
else:
|
|
496
|
+
idx_center = 1
|
|
497
|
+
idx_sigma = 2
|
|
498
|
+
|
|
460
499
|
# --- Get velocity and width analysis for this combination ---
|
|
461
|
-
analysis = analyse_fit_statistics(combination_results, rest_wavelength, data_type)
|
|
500
|
+
analysis = analyse_fit_statistics(combination_results, rest_wavelength, data_type, fit_config=fit_config)
|
|
462
501
|
|
|
463
502
|
# --- Velocity maps ---
|
|
464
|
-
# Velocity from first fit (
|
|
465
|
-
first_fit_data = fit_stats["first_fit_data"] # Shape: (nx, ny,
|
|
466
|
-
center_first_data = first_fit_data[...,
|
|
467
|
-
center_first_unit = fit_stats["units"][
|
|
503
|
+
# Velocity from first fit (primary component center)
|
|
504
|
+
first_fit_data = fit_stats["first_fit_data"] # Shape: (nx, ny, n_params)
|
|
505
|
+
center_first_data = first_fit_data[..., idx_center]
|
|
506
|
+
center_first_unit = fit_stats["units"][idx_center]
|
|
468
507
|
|
|
469
508
|
def centers_to_velocity(centers_data, centers_unit, lambda0):
|
|
470
509
|
"""Convert wavelength centers to velocities"""
|
|
@@ -492,9 +531,9 @@ def create_sunpy_maps_from_combo(
|
|
|
492
531
|
maps['velocity_err'].meta['bunit'] = str(analysis["v_err"].unit)
|
|
493
532
|
|
|
494
533
|
# --- Line width maps ---
|
|
495
|
-
# Line width from first fit (
|
|
496
|
-
width_first_data = first_fit_data[...,
|
|
497
|
-
width_first_unit = fit_stats["units"][
|
|
534
|
+
# Line width from first fit (primary component sigma)
|
|
535
|
+
width_first_data = first_fit_data[..., idx_sigma]
|
|
536
|
+
width_first_unit = fit_stats["units"][idx_sigma]
|
|
498
537
|
|
|
499
538
|
# Create quantity with proper units
|
|
500
539
|
width_quantity = width_first_data * width_first_unit
|