imap-processing 0.12.0__py3-none-any.whl → 0.13.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of imap-processing might be problematic. Click here for more details.
- imap_processing/__init__.py +1 -0
- imap_processing/_version.py +2 -2
- imap_processing/ccsds/ccsds_data.py +1 -2
- imap_processing/ccsds/excel_to_xtce.py +1 -2
- imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +18 -12
- imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +569 -0
- imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +1846 -128
- imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +5 -5
- imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +20 -1
- imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +6 -4
- imap_processing/cdf/config/imap_idex_l1b_variable_attrs.yaml +3 -3
- imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +15 -0
- imap_processing/cdf/config/imap_swapi_variable_attrs.yaml +22 -0
- imap_processing/cdf/config/imap_swe_l1b_variable_attrs.yaml +16 -0
- imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +178 -5
- imap_processing/cdf/config/imap_ultra_l1a_variable_attrs.yaml +5045 -41
- imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +33 -19
- imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +8 -48
- imap_processing/cdf/utils.py +41 -33
- imap_processing/cli.py +463 -234
- imap_processing/codice/codice_l1a.py +260 -47
- imap_processing/codice/codice_l1b.py +51 -152
- imap_processing/codice/constants.py +38 -1
- imap_processing/ena_maps/ena_maps.py +658 -65
- imap_processing/ena_maps/utils/coordinates.py +1 -1
- imap_processing/ena_maps/utils/spatial_utils.py +10 -5
- imap_processing/glows/l1a/glows_l1a.py +28 -99
- imap_processing/glows/l1a/glows_l1a_data.py +2 -2
- imap_processing/glows/l1b/glows_l1b.py +1 -4
- imap_processing/glows/l1b/glows_l1b_data.py +1 -3
- imap_processing/glows/l2/glows_l2.py +2 -5
- imap_processing/hi/l1a/hi_l1a.py +31 -12
- imap_processing/hi/l1b/hi_l1b.py +80 -43
- imap_processing/hi/l1c/hi_l1c.py +12 -16
- imap_processing/hit/ancillary/imap_hit_l1b-to-l2-sector-dt0-factors_20250219_v002.csv +81 -0
- imap_processing/hit/hit_utils.py +93 -35
- imap_processing/hit/l0/decom_hit.py +3 -1
- imap_processing/hit/l1a/hit_l1a.py +30 -25
- imap_processing/hit/l1b/constants.py +6 -2
- imap_processing/hit/l1b/hit_l1b.py +279 -318
- imap_processing/hit/l2/constants.py +37 -0
- imap_processing/hit/l2/hit_l2.py +373 -264
- imap_processing/ialirt/l0/parse_mag.py +138 -10
- imap_processing/ialirt/l0/process_swapi.py +69 -0
- imap_processing/ialirt/l0/process_swe.py +318 -22
- imap_processing/ialirt/packet_definitions/ialirt.xml +216 -212
- imap_processing/ialirt/packet_definitions/ialirt_codicehi.xml +1 -1
- imap_processing/ialirt/packet_definitions/ialirt_codicelo.xml +1 -1
- imap_processing/ialirt/packet_definitions/ialirt_swapi.xml +14 -14
- imap_processing/ialirt/utils/grouping.py +1 -1
- imap_processing/idex/idex_constants.py +9 -1
- imap_processing/idex/idex_l0.py +22 -8
- imap_processing/idex/idex_l1a.py +75 -44
- imap_processing/idex/idex_l1b.py +9 -8
- imap_processing/idex/idex_l2a.py +79 -45
- imap_processing/idex/idex_l2b.py +120 -0
- imap_processing/idex/idex_variable_unpacking_and_eu_conversion.csv +33 -39
- imap_processing/idex/packet_definitions/idex_housekeeping_packet_definition.xml +9130 -0
- imap_processing/lo/l0/lo_science.py +1 -2
- imap_processing/lo/l1a/lo_l1a.py +1 -4
- imap_processing/lo/l1b/lo_l1b.py +527 -6
- imap_processing/lo/l1b/tof_conversions.py +11 -0
- imap_processing/lo/l1c/lo_l1c.py +1 -4
- imap_processing/mag/constants.py +43 -0
- imap_processing/mag/imap_mag_sdc_configuration_v001.py +8 -0
- imap_processing/mag/l1a/mag_l1a.py +2 -9
- imap_processing/mag/l1a/mag_l1a_data.py +10 -10
- imap_processing/mag/l1b/mag_l1b.py +84 -17
- imap_processing/mag/l1c/interpolation_methods.py +180 -3
- imap_processing/mag/l1c/mag_l1c.py +236 -70
- imap_processing/mag/l2/mag_l2.py +140 -0
- imap_processing/mag/l2/mag_l2_data.py +288 -0
- imap_processing/spacecraft/quaternions.py +1 -3
- imap_processing/spice/geometry.py +3 -3
- imap_processing/spice/kernels.py +0 -276
- imap_processing/spice/pointing_frame.py +257 -0
- imap_processing/spice/repoint.py +48 -19
- imap_processing/spice/spin.py +38 -33
- imap_processing/spice/time.py +24 -0
- imap_processing/swapi/l1/swapi_l1.py +16 -12
- imap_processing/swapi/l2/swapi_l2.py +116 -4
- imap_processing/swapi/swapi_utils.py +32 -0
- imap_processing/swe/l1a/swe_l1a.py +2 -9
- imap_processing/swe/l1a/swe_science.py +8 -11
- imap_processing/swe/l1b/swe_l1b.py +898 -23
- imap_processing/swe/l2/swe_l2.py +21 -77
- imap_processing/swe/utils/swe_constants.py +1 -0
- imap_processing/tests/ccsds/test_excel_to_xtce.py +1 -1
- imap_processing/tests/cdf/test_utils.py +14 -16
- imap_processing/tests/codice/conftest.py +44 -33
- imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-pha_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-pha_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/test_codice_l1a.py +20 -11
- imap_processing/tests/codice/test_codice_l1b.py +6 -7
- imap_processing/tests/conftest.py +78 -22
- imap_processing/tests/ena_maps/test_ena_maps.py +462 -33
- imap_processing/tests/ena_maps/test_spatial_utils.py +1 -1
- imap_processing/tests/glows/conftest.py +10 -14
- imap_processing/tests/glows/test_glows_decom.py +4 -4
- imap_processing/tests/glows/test_glows_l1a_cdf.py +6 -27
- imap_processing/tests/glows/test_glows_l1a_data.py +6 -8
- imap_processing/tests/glows/test_glows_l1b.py +11 -11
- imap_processing/tests/glows/test_glows_l1b_data.py +5 -5
- imap_processing/tests/glows/test_glows_l2.py +2 -8
- imap_processing/tests/hi/conftest.py +1 -1
- imap_processing/tests/hi/test_hi_l1b.py +10 -12
- imap_processing/tests/hi/test_hi_l1c.py +27 -24
- imap_processing/tests/hi/test_l1a.py +7 -9
- imap_processing/tests/hi/test_science_direct_event.py +2 -2
- imap_processing/tests/hit/helpers/l1_validation.py +44 -43
- imap_processing/tests/hit/test_decom_hit.py +1 -1
- imap_processing/tests/hit/test_hit_l1a.py +9 -9
- imap_processing/tests/hit/test_hit_l1b.py +172 -217
- imap_processing/tests/hit/test_hit_l2.py +380 -118
- imap_processing/tests/hit/test_hit_utils.py +122 -55
- imap_processing/tests/hit/validation_data/hit_l1b_standard_sample2_nsrl_v4_3decimals.csv +62 -62
- imap_processing/tests/hit/validation_data/sci_sample_raw.csv +1 -1
- imap_processing/tests/ialirt/unit/test_decom_ialirt.py +16 -81
- imap_processing/tests/ialirt/unit/test_grouping.py +2 -2
- imap_processing/tests/ialirt/unit/test_parse_mag.py +71 -16
- imap_processing/tests/ialirt/unit/test_process_codicehi.py +3 -3
- imap_processing/tests/ialirt/unit/test_process_codicelo.py +3 -10
- imap_processing/tests/ialirt/unit/test_process_ephemeris.py +4 -4
- imap_processing/tests/ialirt/unit/test_process_hit.py +3 -3
- imap_processing/tests/ialirt/unit/test_process_swapi.py +24 -16
- imap_processing/tests/ialirt/unit/test_process_swe.py +115 -7
- imap_processing/tests/idex/conftest.py +72 -7
- imap_processing/tests/idex/test_data/imap_idex_l0_raw_20241206_v001.pkts +0 -0
- imap_processing/tests/idex/test_data/imap_idex_l0_raw_20250108_v001.pkts +0 -0
- imap_processing/tests/idex/test_idex_l0.py +33 -11
- imap_processing/tests/idex/test_idex_l1a.py +50 -23
- imap_processing/tests/idex/test_idex_l1b.py +104 -25
- imap_processing/tests/idex/test_idex_l2a.py +48 -32
- imap_processing/tests/idex/test_idex_l2b.py +93 -0
- imap_processing/tests/lo/test_lo_l1a.py +3 -3
- imap_processing/tests/lo/test_lo_l1b.py +371 -6
- imap_processing/tests/lo/test_lo_l1c.py +1 -1
- imap_processing/tests/lo/test_lo_science.py +6 -7
- imap_processing/tests/lo/test_star_sensor.py +1 -1
- imap_processing/tests/mag/conftest.py +58 -9
- imap_processing/tests/mag/test_mag_decom.py +4 -3
- imap_processing/tests/mag/test_mag_l1a.py +13 -7
- imap_processing/tests/mag/test_mag_l1b.py +9 -9
- imap_processing/tests/mag/test_mag_l1c.py +151 -47
- imap_processing/tests/mag/test_mag_l2.py +130 -0
- imap_processing/tests/mag/test_mag_validation.py +144 -7
- imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-magi-normal-in.csv +1217 -0
- imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-magi-normal-out.csv +1857 -0
- imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-mago-normal-in.csv +1217 -0
- imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-mago-normal-out.csv +1857 -0
- imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-magi-normal-in.csv +1217 -0
- imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-magi-normal-out.csv +1793 -0
- imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-mago-normal-in.csv +1217 -0
- imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-mago-normal-out.csv +1793 -0
- imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-magi-burst-in.csv +2561 -0
- imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-magi-normal-in.csv +961 -0
- imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-magi-normal-out.csv +1539 -0
- imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-mago-normal-in.csv +1921 -0
- imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-mago-normal-out.csv +2499 -0
- imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-magi-normal-in.csv +865 -0
- imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-magi-normal-out.csv +1196 -0
- imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-mago-normal-in.csv +1729 -0
- imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-mago-normal-out.csv +3053 -0
- imap_processing/tests/mag/validation/L2/imap_mag_l1b_norm-mago_20251017_v002.cdf +0 -0
- imap_processing/tests/mag/validation/calibration/imap_mag_l2-calibration-matrices_20251017_v004.cdf +0 -0
- imap_processing/tests/mag/validation/calibration/imap_mag_l2-offsets-norm_20251017_20251017_v001.cdf +0 -0
- imap_processing/tests/spacecraft/test_quaternions.py +1 -1
- imap_processing/tests/spice/test_data/fake_repoint_data.csv +4 -4
- imap_processing/tests/spice/test_data/fake_spin_data.csv +11 -11
- imap_processing/tests/spice/test_geometry.py +3 -3
- imap_processing/tests/spice/test_kernels.py +1 -200
- imap_processing/tests/spice/test_pointing_frame.py +185 -0
- imap_processing/tests/spice/test_repoint.py +20 -10
- imap_processing/tests/spice/test_spin.py +50 -9
- imap_processing/tests/spice/test_time.py +14 -0
- imap_processing/tests/swapi/lut/imap_swapi_esa-unit-conversion_20250211_v000.csv +73 -0
- imap_processing/tests/swapi/lut/imap_swapi_lut-notes_20250211_v000.csv +1025 -0
- imap_processing/tests/swapi/test_swapi_l1.py +7 -9
- imap_processing/tests/swapi/test_swapi_l2.py +180 -8
- imap_processing/tests/swe/lut/checker-board-indices.csv +24 -0
- imap_processing/tests/swe/lut/imap_swe_esa-lut_20250301_v000.csv +385 -0
- imap_processing/tests/swe/lut/imap_swe_l1b-in-flight-cal_20240510_20260716_v000.csv +3 -0
- imap_processing/tests/swe/test_swe_l1a.py +6 -6
- imap_processing/tests/swe/test_swe_l1a_science.py +3 -3
- imap_processing/tests/swe/test_swe_l1b.py +162 -24
- imap_processing/tests/swe/test_swe_l2.py +82 -102
- imap_processing/tests/test_cli.py +171 -88
- imap_processing/tests/test_utils.py +2 -1
- imap_processing/tests/ultra/data/mock_data.py +49 -21
- imap_processing/tests/ultra/unit/conftest.py +53 -70
- imap_processing/tests/ultra/unit/test_badtimes.py +2 -4
- imap_processing/tests/ultra/unit/test_cullingmask.py +4 -6
- imap_processing/tests/ultra/unit/test_de.py +3 -10
- imap_processing/tests/ultra/unit/test_decom_apid_880.py +27 -76
- imap_processing/tests/ultra/unit/test_decom_apid_881.py +15 -16
- imap_processing/tests/ultra/unit/test_decom_apid_883.py +12 -10
- imap_processing/tests/ultra/unit/test_decom_apid_896.py +202 -55
- imap_processing/tests/ultra/unit/test_lookup_utils.py +23 -1
- imap_processing/tests/ultra/unit/test_spacecraft_pset.py +3 -4
- imap_processing/tests/ultra/unit/test_ultra_l1a.py +84 -307
- imap_processing/tests/ultra/unit/test_ultra_l1b.py +30 -12
- imap_processing/tests/ultra/unit/test_ultra_l1b_annotated.py +2 -2
- imap_processing/tests/ultra/unit/test_ultra_l1b_culling.py +4 -1
- imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +163 -29
- imap_processing/tests/ultra/unit/test_ultra_l1c.py +5 -5
- imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +32 -43
- imap_processing/tests/ultra/unit/test_ultra_l2.py +230 -0
- imap_processing/ultra/constants.py +1 -1
- imap_processing/ultra/l0/decom_tools.py +21 -34
- imap_processing/ultra/l0/decom_ultra.py +168 -204
- imap_processing/ultra/l0/ultra_utils.py +152 -136
- imap_processing/ultra/l1a/ultra_l1a.py +55 -243
- imap_processing/ultra/l1b/badtimes.py +1 -4
- imap_processing/ultra/l1b/cullingmask.py +2 -6
- imap_processing/ultra/l1b/de.py +62 -47
- imap_processing/ultra/l1b/extendedspin.py +8 -4
- imap_processing/ultra/l1b/lookup_utils.py +72 -9
- imap_processing/ultra/l1b/ultra_l1b.py +3 -8
- imap_processing/ultra/l1b/ultra_l1b_culling.py +4 -4
- imap_processing/ultra/l1b/ultra_l1b_extended.py +236 -78
- imap_processing/ultra/l1c/histogram.py +2 -6
- imap_processing/ultra/l1c/spacecraft_pset.py +2 -4
- imap_processing/ultra/l1c/ultra_l1c.py +1 -5
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +107 -60
- imap_processing/ultra/l2/ultra_l2.py +299 -0
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_LeftSlit.csv +526 -0
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_RightSlit.csv +526 -0
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_LeftSlit.csv +526 -0
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_RightSlit.csv +526 -0
- imap_processing/ultra/lookup_tables/FM45_Startup1_ULTRA_IMGPARAMS_20240719.csv +2 -2
- imap_processing/ultra/lookup_tables/FM90_Startup1_ULTRA_IMGPARAMS_20240719.csv +2 -0
- imap_processing/ultra/packet_definitions/README.md +38 -0
- imap_processing/ultra/packet_definitions/ULTRA_SCI_COMBINED.xml +15302 -482
- imap_processing/ultra/utils/ultra_l1_utils.py +13 -12
- imap_processing/utils.py +1 -1
- {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/METADATA +3 -2
- {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/RECORD +264 -225
- imap_processing/hi/l1b/hi_eng_unit_convert_table.csv +0 -154
- imap_processing/mag/imap_mag_sdc-configuration_v001.yaml +0 -6
- imap_processing/mag/l1b/__init__.py +0 -0
- imap_processing/swe/l1b/swe_esa_lookup_table.csv +0 -1441
- imap_processing/swe/l1b/swe_l1b_science.py +0 -699
- imap_processing/tests/swe/test_swe_l1b_science.py +0 -103
- imap_processing/ultra/lookup_tables/dps_sensitivity45.cdf +0 -0
- imap_processing/ultra/lookup_tables/ultra_90_dps_exposure_compressed.cdf +0 -0
- /imap_processing/idex/packet_definitions/{idex_packet_definition.xml → idex_science_packet_definition.xml} +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/20240827095047_SWE_IALIRT_packet.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971383-404.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971384-405.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971385-406.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971386-407.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971387-408.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971388-409.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971389-410.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971390-411.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971391-412.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/BinLog CCSDS_FRAG_TLM_20240826_152323Z_IALIRT_data_for_SDC.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/IALiRT Raw Packet Telemetry.txt +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/apid01152.tlm +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/eu_SWP_IAL_20240826_152033.csv +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/hi_fsw_view_1_ccsds.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/hit_ialirt_sample.ccsds +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/hit_ialirt_sample.csv +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/idle_export_eu.SWE_IALIRT_20240827_093852.csv +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/imap_codice_l1a_hi-ialirt_20240523200000_v0.0.0.cdf +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/imap_codice_l1a_lo-ialirt_20241110193700_v0.0.0.cdf +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/sample_decoded_i-alirt_data.csv +0 -0
- /imap_processing/tests/mag/validation/{imap_calibration_mag_20240229_v01.cdf → calibration/imap_mag_l1b-calibration_20240229_v001.cdf} +0 -0
- /imap_processing/{swe/l1b/engineering_unit_convert_table.csv → tests/swe/lut/imap_swe_eu-conversion_20240510_v000.csv} +0 -0
- {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/LICENSE +0 -0
- {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/WHEEL +0 -0
- {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/entry_points.txt +0 -0
|
@@ -6,7 +6,7 @@ from imap_processing.ultra.l1c.histogram import calculate_histogram
|
|
|
6
6
|
from imap_processing.ultra.l1c.spacecraft_pset import calculate_spacecraft_pset
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
def ultra_l1c(data_dict: dict
|
|
9
|
+
def ultra_l1c(data_dict: dict) -> list[xr.Dataset]:
|
|
10
10
|
"""
|
|
11
11
|
Will process ULTRA L1A and L1B data into L1C CDF files at output_filepath.
|
|
12
12
|
|
|
@@ -14,8 +14,6 @@ def ultra_l1c(data_dict: dict, data_version: str) -> list[xr.Dataset]:
|
|
|
14
14
|
----------
|
|
15
15
|
data_dict : dict
|
|
16
16
|
The data itself and its dependent data.
|
|
17
|
-
data_version : str
|
|
18
|
-
Version of the data product being created.
|
|
19
17
|
|
|
20
18
|
Returns
|
|
21
19
|
-------
|
|
@@ -31,7 +29,6 @@ def ultra_l1c(data_dict: dict, data_version: str) -> list[xr.Dataset]:
|
|
|
31
29
|
histogram_dataset = calculate_histogram(
|
|
32
30
|
data_dict[f"imap_ultra_l1a_{instrument_id}sensor-histogram"],
|
|
33
31
|
f"imap_ultra_l1c_{instrument_id}sensor-histogram",
|
|
34
|
-
data_version,
|
|
35
32
|
)
|
|
36
33
|
output_datasets = [histogram_dataset]
|
|
37
34
|
elif (
|
|
@@ -44,7 +41,6 @@ def ultra_l1c(data_dict: dict, data_version: str) -> list[xr.Dataset]:
|
|
|
44
41
|
data_dict[f"imap_ultra_l1b_{instrument_id}sensor-extendedspin"],
|
|
45
42
|
data_dict[f"imap_ultra_l1b_{instrument_id}sensor-cullingmask"],
|
|
46
43
|
f"imap_ultra_l1c_{instrument_id}sensor-spacecraftpset",
|
|
47
|
-
data_version,
|
|
48
44
|
)
|
|
49
45
|
# TODO: add calculate_helio_pset here
|
|
50
46
|
output_datasets = [spacecraft_pset]
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
import astropy_healpix.healpy as hp
|
|
4
4
|
import numpy as np
|
|
5
5
|
import pandas
|
|
6
|
+
import pandas as pd
|
|
6
7
|
from numpy.typing import NDArray
|
|
8
|
+
from scipy.interpolate import interp1d
|
|
7
9
|
|
|
8
|
-
from imap_processing.ena_maps.utils.spatial_utils import build_spatial_bins
|
|
9
10
|
from imap_processing.spice.geometry import (
|
|
10
11
|
SpiceFrame,
|
|
11
12
|
cartesian_to_spherical,
|
|
12
13
|
imap_state,
|
|
13
|
-
spherical_to_cartesian,
|
|
14
14
|
)
|
|
15
15
|
from imap_processing.ultra.constants import UltraConstants
|
|
16
16
|
|
|
@@ -70,7 +70,7 @@ def get_spacecraft_histogram(
|
|
|
70
70
|
Array of energy bin edges.
|
|
71
71
|
nside : int, optional
|
|
72
72
|
The nside parameter of the Healpix tessellation.
|
|
73
|
-
Default is
|
|
73
|
+
Default is 128.
|
|
74
74
|
nested : bool, optional
|
|
75
75
|
Whether the Healpix tessellation is nested. Default is False.
|
|
76
76
|
|
|
@@ -174,54 +174,57 @@ def get_spacecraft_exposure_times(constant_exposure: pandas.DataFrame) -> NDArra
|
|
|
174
174
|
|
|
175
175
|
def get_helio_exposure_times(
|
|
176
176
|
time: np.ndarray,
|
|
177
|
-
|
|
177
|
+
df_exposure: pd.DataFrame,
|
|
178
|
+
nside: int = 128,
|
|
179
|
+
nested: bool = False,
|
|
178
180
|
) -> NDArray:
|
|
179
181
|
"""
|
|
180
|
-
Compute a
|
|
182
|
+
Compute a 2D (Healpix index, energy) array of exposure in the helio frame.
|
|
181
183
|
|
|
182
184
|
Parameters
|
|
183
185
|
----------
|
|
184
186
|
time : np.ndarray
|
|
185
|
-
Median time of pointing in
|
|
186
|
-
|
|
187
|
-
Spacecraft exposure.
|
|
187
|
+
Median time of pointing in et.
|
|
188
|
+
df_exposure : pd.DataFrame
|
|
189
|
+
Spacecraft exposure in healpix coordinates.
|
|
190
|
+
nside : int, optional
|
|
191
|
+
The nside parameter of the Healpix tessellation (default is 128).
|
|
192
|
+
nested : bool, optional
|
|
193
|
+
Whether the Healpix tessellation is nested (default is False).
|
|
188
194
|
|
|
189
195
|
Returns
|
|
190
196
|
-------
|
|
191
|
-
|
|
192
|
-
A
|
|
197
|
+
helio_exposure : np.ndarray
|
|
198
|
+
A 2D array of shape (npix, n_energy_bins).
|
|
193
199
|
|
|
194
200
|
Notes
|
|
195
201
|
-----
|
|
196
202
|
These calculations are performed once per pointing.
|
|
197
203
|
"""
|
|
198
|
-
# Get
|
|
204
|
+
# Get energy midpoints.
|
|
199
205
|
_, energy_midpoints, _ = build_energy_bins()
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
)
|
|
203
|
-
|
|
204
|
-
# Initialize the exposure grid.
|
|
205
|
-
exposure_3d = np.zeros(
|
|
206
|
-
(len(el_bin_midpoints), len(az_bin_midpoints), len(energy_midpoints))
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
# Create a 3D Cartesian grid from spherical coordinates
|
|
210
|
-
# using azimuth and elevation midpoints.
|
|
211
|
-
az_grid, el_grid = np.meshgrid(az_bin_midpoints, el_bin_midpoints[::-1])
|
|
212
|
-
|
|
213
|
-
# Radial distance.
|
|
214
|
-
r = np.ones(el_grid.shape)
|
|
215
|
-
spherical_coords = np.stack((r, az_grid, el_grid), axis=-1)
|
|
216
|
-
cartesian_coords = spherical_to_cartesian(spherical_coords)
|
|
217
|
-
cartesian = cartesian_coords.reshape(-1, 3, order="F").T
|
|
206
|
+
# Extract (RA/Dec) and exposure from the spacecraft frame.
|
|
207
|
+
ra = df_exposure["Right Ascension (deg)"].values
|
|
208
|
+
dec = df_exposure["Declination (deg)"].values
|
|
209
|
+
exposure_flat = df_exposure["Exposure Time"].values
|
|
218
210
|
|
|
219
|
-
#
|
|
211
|
+
# The Cartesian state vector representing the position and velocity of the
|
|
212
|
+
# IMAP spacecraft.
|
|
220
213
|
state = imap_state(time, ref_frame=SpiceFrame.IMAP_DPS)
|
|
221
214
|
|
|
222
215
|
# Extract the velocity part of the state vector
|
|
223
216
|
spacecraft_velocity = state[3:6]
|
|
217
|
+
# Convert (RA, Dec) angles into 3D unit vectors.
|
|
218
|
+
# Each unit vector represents a direction in the sky where the spacecraft observed
|
|
219
|
+
# and accumulated exposure time.
|
|
220
|
+
unit_dirs = hp.ang2vec(ra, dec, lonlat=True).T # Shape (N, 3)
|
|
224
221
|
|
|
222
|
+
# Initialize output array.
|
|
223
|
+
# Each row corresponds to a HEALPix pixel, and each column to an energy bin.
|
|
224
|
+
npix = hp.nside2npix(nside)
|
|
225
|
+
helio_exposure = np.zeros((npix, len(energy_midpoints)))
|
|
226
|
+
|
|
227
|
+
# Loop through energy bins and compute transformed exposure.
|
|
225
228
|
for i, energy_midpoint in enumerate(energy_midpoints):
|
|
226
229
|
# Convert the midpoint energy to a velocity (km/s).
|
|
227
230
|
# Based on kinetic energy equation: E = 1/2 * m * v^2.
|
|
@@ -234,41 +237,35 @@ def get_helio_exposure_times(
|
|
|
234
237
|
# to the velocity wrt heliosphere.
|
|
235
238
|
# energy_velocity * cartesian -> apply the magnitude of the velocity
|
|
236
239
|
# to every position on the grid in the despun grid.
|
|
237
|
-
helio_velocity = spacecraft_velocity.reshape(
|
|
240
|
+
helio_velocity = spacecraft_velocity.reshape(1, 3) + energy_velocity * unit_dirs
|
|
238
241
|
|
|
239
242
|
# Normalized vectors representing the direction of the heliocentric velocity.
|
|
240
|
-
helio_normalized = helio_velocity
|
|
241
|
-
helio_velocity
|
|
243
|
+
helio_normalized = helio_velocity / np.linalg.norm(
|
|
244
|
+
helio_velocity, axis=1, keepdims=True
|
|
242
245
|
)
|
|
243
|
-
# Converts vectors from Cartesian coordinates (x, y, z)
|
|
244
|
-
# into spherical coordinates.
|
|
245
|
-
spherical_coords = cartesian_to_spherical(helio_normalized)
|
|
246
|
-
az, el = spherical_coords[..., 1], spherical_coords[..., 2]
|
|
247
246
|
|
|
248
|
-
#
|
|
249
|
-
|
|
250
|
-
|
|
247
|
+
# Convert Cartesian heliocentric vectors into spherical coordinates.
|
|
248
|
+
# Result: azimuth (longitude) and elevation (latitude) in degrees.
|
|
249
|
+
helio_spherical = cartesian_to_spherical(helio_normalized)
|
|
250
|
+
az, el = helio_spherical[:, 1], helio_spherical[:, 2]
|
|
251
251
|
|
|
252
|
-
#
|
|
253
|
-
|
|
254
|
-
el_idx = np.clip(el_idx, 0, len(el_bin_edges) - 2)
|
|
252
|
+
# Convert azimuth/elevation directions to HEALPix pixel indices.
|
|
253
|
+
hpix_idx = hp.ang2pix(nside, az, el, nest=nested, lonlat=True)
|
|
255
254
|
|
|
256
|
-
#
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
# Reshape the binned exposure.
|
|
261
|
-
exposure_3d[:, :, i] = binned_exposure.reshape(az_grid.shape, order="F")
|
|
255
|
+
# Accumulate exposure values into HEALPix pixels for this energy bin.
|
|
256
|
+
helio_exposure[:, i] = np.bincount(
|
|
257
|
+
hpix_idx, weights=exposure_flat, minlength=npix
|
|
258
|
+
)
|
|
262
259
|
|
|
263
|
-
return
|
|
260
|
+
return helio_exposure
|
|
264
261
|
|
|
265
262
|
|
|
266
263
|
def get_spacecraft_sensitivity(
|
|
267
264
|
efficiencies: pandas.DataFrame,
|
|
268
265
|
geometric_function: pandas.DataFrame,
|
|
269
|
-
) -> pandas.DataFrame:
|
|
266
|
+
) -> tuple[pandas.DataFrame, NDArray, NDArray, NDArray]:
|
|
270
267
|
"""
|
|
271
|
-
Compute sensitivity.
|
|
268
|
+
Compute sensitivity as efficiency * geometric factor.
|
|
272
269
|
|
|
273
270
|
Parameters
|
|
274
271
|
----------
|
|
@@ -281,19 +278,69 @@ def get_spacecraft_sensitivity(
|
|
|
281
278
|
-------
|
|
282
279
|
pointing_sensitivity : pandas.DataFrame
|
|
283
280
|
Sensitivity with dimensions (HEALPIX pixel_number, energy).
|
|
281
|
+
energy_vals : NDArray
|
|
282
|
+
Energy values of dataframe.
|
|
283
|
+
right_ascension : NDArray
|
|
284
|
+
Right ascension (longitude/azimuth) values of dataframe (0 - 360 degrees).
|
|
285
|
+
declination : NDArray
|
|
286
|
+
Declination (latitude/elevation) values of dataframe (-90 to 90 degrees).
|
|
284
287
|
"""
|
|
285
288
|
# Exclude "Right Ascension (deg)" and "Declination (deg)" from the multiplication
|
|
286
|
-
energy_columns =
|
|
287
|
-
|
|
288
|
-
|
|
289
|
+
energy_columns = [
|
|
290
|
+
col
|
|
291
|
+
for col in efficiencies.columns
|
|
292
|
+
if col not in ["Right Ascension (deg)", "Declination (deg)"]
|
|
293
|
+
]
|
|
289
294
|
sensitivity = efficiencies[energy_columns].mul(
|
|
290
295
|
geometric_function["Response (cm2-sr)"].values, axis=0
|
|
291
296
|
)
|
|
292
297
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
298
|
+
right_ascension = efficiencies["Right Ascension (deg)"]
|
|
299
|
+
declination = efficiencies["Declination (deg)"]
|
|
300
|
+
|
|
301
|
+
energy_vals = np.array([float(col.replace("keV", "")) for col in energy_columns])
|
|
302
|
+
|
|
303
|
+
return sensitivity, energy_vals, right_ascension, declination
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def grid_sensitivity(
|
|
307
|
+
efficiencies: pandas.DataFrame,
|
|
308
|
+
geometric_function: pandas.DataFrame,
|
|
309
|
+
energy: float,
|
|
310
|
+
) -> NDArray:
|
|
311
|
+
"""
|
|
312
|
+
Grid the sensitivity.
|
|
313
|
+
|
|
314
|
+
Parameters
|
|
315
|
+
----------
|
|
316
|
+
efficiencies : pandas.DataFrame
|
|
317
|
+
Efficiencies at different energy levels.
|
|
318
|
+
geometric_function : pandas.DataFrame
|
|
319
|
+
Geometric function.
|
|
320
|
+
energy : np.ndarray
|
|
321
|
+
The particle energy.
|
|
322
|
+
energy : float
|
|
323
|
+
Energy to which we are interpolating.
|
|
324
|
+
|
|
325
|
+
Returns
|
|
326
|
+
-------
|
|
327
|
+
interpolated_sensitivity : np.ndarray
|
|
328
|
+
Sensitivity with dimensions (HEALPIX pixel_number, 1).
|
|
329
|
+
"""
|
|
330
|
+
sensitivity, energy_vals, right_ascension, declination = get_spacecraft_sensitivity(
|
|
331
|
+
efficiencies, geometric_function
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
# Create interpolator over energy dimension for each pixel (axis=1)
|
|
335
|
+
interp_func = interp1d(
|
|
336
|
+
energy_vals,
|
|
337
|
+
sensitivity.values,
|
|
338
|
+
axis=1,
|
|
339
|
+
bounds_error=False,
|
|
340
|
+
fill_value=np.nan,
|
|
296
341
|
)
|
|
297
|
-
sensitivity.insert(1, "Declination (deg)", efficiencies["Declination (deg)"])
|
|
298
342
|
|
|
299
|
-
|
|
343
|
+
# Interpolate to energy
|
|
344
|
+
interpolated = interp_func(energy)
|
|
345
|
+
|
|
346
|
+
return interpolated
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"""Calculate ULTRA Level 2 (L2) ENA Map Product."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
import xarray as xr
|
|
9
|
+
|
|
10
|
+
from imap_processing.ena_maps import ena_maps
|
|
11
|
+
from imap_processing.ena_maps.utils.coordinates import CoordNames
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
logger.info("Importing ultra_l2 module")
|
|
15
|
+
|
|
16
|
+
# Default properties for the Ultra L2 map
|
|
17
|
+
DEFAULT_ULTRA_L2_MAP_STRUCTURE: ena_maps.RectangularSkyMap | ena_maps.HealpixSkyMap = (
|
|
18
|
+
ena_maps.AbstractSkyMap.from_dict(
|
|
19
|
+
{
|
|
20
|
+
"sky_tiling_type": "HEALPIX",
|
|
21
|
+
"spice_reference_frame": "ECLIPJ2000",
|
|
22
|
+
"values_to_push_project": [
|
|
23
|
+
"counts",
|
|
24
|
+
"exposure_factor",
|
|
25
|
+
"sensitivity",
|
|
26
|
+
"background_rates",
|
|
27
|
+
],
|
|
28
|
+
"nside": 32,
|
|
29
|
+
"nested": False,
|
|
30
|
+
}
|
|
31
|
+
)
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# Set some default Healpix parameters - these must be defined, even if also
|
|
35
|
+
# present in the DEFAULT_ULTRA_L2_MAP_STRUCTURE, because we always make a Healpix map
|
|
36
|
+
# regardless of the output map type
|
|
37
|
+
DEFAULT_L2_HEALPIX_NSIDE = 32
|
|
38
|
+
DEFAULT_L2_HEALPIX_NESTED = False
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# These variables must always be present in each L1C dataset
|
|
42
|
+
REQUIRED_L1C_VARIABLES = [
|
|
43
|
+
"counts",
|
|
44
|
+
"exposure_factor",
|
|
45
|
+
"sensitivity",
|
|
46
|
+
"background_rates",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
# These variables are projected to the map as the mean of pointing set pixels value,
|
|
50
|
+
# weighted by that pointing set pixel's exposure and solid angle
|
|
51
|
+
VARIABLES_TO_WEIGHT_BY_POINTING_SET_EXPOSURE_TIMES_SOLID_ANGLE = [
|
|
52
|
+
"sensitivity",
|
|
53
|
+
"background_rates",
|
|
54
|
+
"observation_time",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
# These variables are dropped after they are used to calculate flux and flux uncertainty
|
|
58
|
+
# They will not be present in the final map
|
|
59
|
+
VARIABLES_TO_DROP_AFTER_FLUX_CALCULATION = [
|
|
60
|
+
"counts",
|
|
61
|
+
"background_rates",
|
|
62
|
+
"pointing_set_exposure_times_solid_angle",
|
|
63
|
+
"num_pointing_set_pixel_members",
|
|
64
|
+
"corrected_count_rate",
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def generate_ultra_healpix_skymap(
|
|
69
|
+
ultra_l1c_psets: list[str | xr.Dataset],
|
|
70
|
+
output_map_structure: (
|
|
71
|
+
ena_maps.RectangularSkyMap | ena_maps.HealpixSkyMap
|
|
72
|
+
) = DEFAULT_ULTRA_L2_MAP_STRUCTURE,
|
|
73
|
+
) -> ena_maps.HealpixSkyMap:
|
|
74
|
+
"""
|
|
75
|
+
Generate a Healpix skymap from ULTRA L1C pointing sets.
|
|
76
|
+
|
|
77
|
+
This function combines IMAP Ultra L1C pointing sets into a single L2 HealpixSkyMap.
|
|
78
|
+
It handles the projection of values from pointing sets to the map, applies necessary
|
|
79
|
+
weighting and background subtraction, and calculates flux and flux uncertainty.
|
|
80
|
+
|
|
81
|
+
Parameters
|
|
82
|
+
----------
|
|
83
|
+
ultra_l1c_psets : list[str | xr.Dataset]
|
|
84
|
+
List of paths to ULTRA L1C pointing set files or xarray Datasets containing
|
|
85
|
+
pointing set data.
|
|
86
|
+
output_map_structure : ena_maps.RectangularSkyMap | ena_maps.HealpixSkyMap, optional
|
|
87
|
+
Empty SkyMap structure providing the properties of the map to be generated.
|
|
88
|
+
Defaults to DEFAULT_ULTRA_L2_MAP_STRUCTURE defined in this module.
|
|
89
|
+
|
|
90
|
+
Returns
|
|
91
|
+
-------
|
|
92
|
+
ena_maps.HealpixSkyMap
|
|
93
|
+
HealpixSkyMap object containing the combined data from all pointing sets,
|
|
94
|
+
with calculated flux and flux uncertainty values.
|
|
95
|
+
|
|
96
|
+
Notes
|
|
97
|
+
-----
|
|
98
|
+
The structure of this function goes as follows:
|
|
99
|
+
1. Initialize the HealpixSkyMap object with the specified properties.
|
|
100
|
+
2. Iterate over the input pointing sets and read them into UltraPointingSet objects.
|
|
101
|
+
3. For each pointing set, weight certain variables by exposure and solid angle of
|
|
102
|
+
the pointing set pixels.
|
|
103
|
+
4. Project the pointing set values to the map using the push method.
|
|
104
|
+
5. Perform subsequent processing for weighted quantities at the SkyMap level
|
|
105
|
+
(e.g., divide weighted quantities by their summed weights to
|
|
106
|
+
get their weighted mean)
|
|
107
|
+
6. Calculate corrected count rate with background subtraction applied.
|
|
108
|
+
7. Calculate flux and flux uncertainty.
|
|
109
|
+
8. Drop unnecessary variables from the map.
|
|
110
|
+
"""
|
|
111
|
+
if output_map_structure.tiling_type is ena_maps.SkyTilingType.HEALPIX:
|
|
112
|
+
map_nside, map_nested = (
|
|
113
|
+
output_map_structure.nside,
|
|
114
|
+
output_map_structure.nested,
|
|
115
|
+
)
|
|
116
|
+
else:
|
|
117
|
+
map_nside, map_nested = (DEFAULT_L2_HEALPIX_NSIDE, DEFAULT_L2_HEALPIX_NESTED)
|
|
118
|
+
|
|
119
|
+
# Initialize the HealpixSkyMap object
|
|
120
|
+
skymap = ena_maps.HealpixSkyMap(
|
|
121
|
+
nside=map_nside,
|
|
122
|
+
nested=map_nested,
|
|
123
|
+
spice_frame=output_map_structure.spice_reference_frame,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# Add additional data variables to the map
|
|
127
|
+
output_map_structure.values_to_push_project.extend(
|
|
128
|
+
[
|
|
129
|
+
"observation_time",
|
|
130
|
+
"pointing_set_exposure_times_solid_angle",
|
|
131
|
+
"num_pointing_set_pixel_members",
|
|
132
|
+
]
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Get full list of variables to push to the map: all requested variables plus
|
|
136
|
+
# any which are required for L2 processing
|
|
137
|
+
value_keys_to_push_project = list(
|
|
138
|
+
set(output_map_structure.values_to_push_project + REQUIRED_L1C_VARIABLES)
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
for ultra_l1c_pset in ultra_l1c_psets:
|
|
142
|
+
pointing_set = ena_maps.UltraPointingSet(ultra_l1c_pset)
|
|
143
|
+
logger.info(
|
|
144
|
+
f"Projecting a PointingSet with {pointing_set.num_points} pixels "
|
|
145
|
+
f"at epoch:{pointing_set.epoch}\n"
|
|
146
|
+
f"These values will be projected: {value_keys_to_push_project}"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
pointing_set.data["num_pointing_set_pixel_members"] = xr.DataArray(
|
|
150
|
+
np.ones(pointing_set.num_points, dtype=int),
|
|
151
|
+
dims=(CoordNames.HEALPIX_INDEX.value),
|
|
152
|
+
)
|
|
153
|
+
pointing_set.data["observation_time"] = xr.DataArray(
|
|
154
|
+
np.full(pointing_set.num_points, pointing_set.epoch),
|
|
155
|
+
dims=(CoordNames.HEALPIX_INDEX.value),
|
|
156
|
+
)
|
|
157
|
+
# Add solid_angle * exposure of pointing set as data_var
|
|
158
|
+
# so this quantity is projected to map pixels for use in weighted averaging
|
|
159
|
+
pointing_set.data["pointing_set_exposure_times_solid_angle"] = (
|
|
160
|
+
pointing_set.data["exposure_factor"] * pointing_set.solid_angle
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# Initial processing for weighted quantities at PSET level
|
|
164
|
+
# Weight the values by exposure and solid angle
|
|
165
|
+
pointing_set.data[
|
|
166
|
+
VARIABLES_TO_WEIGHT_BY_POINTING_SET_EXPOSURE_TIMES_SOLID_ANGLE
|
|
167
|
+
] *= pointing_set.data["pointing_set_exposure_times_solid_angle"]
|
|
168
|
+
|
|
169
|
+
skymap.project_pset_values_to_map(
|
|
170
|
+
pointing_set=pointing_set,
|
|
171
|
+
value_keys=value_keys_to_push_project,
|
|
172
|
+
index_match_method=ena_maps.IndexMatchMethod.PUSH,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Subsequent processing for weighted quantities at SkyMap level
|
|
176
|
+
skymap.data_1d[VARIABLES_TO_WEIGHT_BY_POINTING_SET_EXPOSURE_TIMES_SOLID_ANGLE] /= (
|
|
177
|
+
skymap.data_1d["pointing_set_exposure_times_solid_angle"]
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# TODO: Ask Ultra team about this - I think this is a decent
|
|
181
|
+
# but imperfect approximation for meaning the exposure:
|
|
182
|
+
# (dividing by the 1/(number of PSETs)) to fix the exposure being mean-ed over
|
|
183
|
+
# all pixels in all PSETs which feed into a map superpixel,
|
|
184
|
+
# rather than being mean-ed over pixels in a PSET and summed over PSETs
|
|
185
|
+
skymap.data_1d["exposure_factor"] /= skymap.data_1d[
|
|
186
|
+
"num_pointing_set_pixel_members"
|
|
187
|
+
] / len(ultra_l1c_psets)
|
|
188
|
+
|
|
189
|
+
# TODO: Ask Ultra team about background rates - I think they should increase when
|
|
190
|
+
# binned to larger pixels, as I've done here, but that was never explicitly stated
|
|
191
|
+
skymap.data_1d["background_rates"] *= skymap.solid_angle / pointing_set.solid_angle
|
|
192
|
+
|
|
193
|
+
# Get the energy bin widths from a PointingSet (they will all be the same)
|
|
194
|
+
delta_energy = pointing_set.data["energy_bin_delta"]
|
|
195
|
+
|
|
196
|
+
# Core calculations of flux and flux uncertainty for L2
|
|
197
|
+
# Exposure time may contain 0s, producing NaNs in the corrected count rate and flux.
|
|
198
|
+
# These NaNs are not incorrect, so we temporarily ignore numpy div by 0 warnings.
|
|
199
|
+
with np.errstate(divide="ignore"):
|
|
200
|
+
# Get corrected count rate with background subtraction applied
|
|
201
|
+
skymap.data_1d["corrected_count_rate"] = (
|
|
202
|
+
skymap.data_1d["counts"].astype(float) / skymap.data_1d["exposure_factor"]
|
|
203
|
+
) - skymap.data_1d["background_rates"]
|
|
204
|
+
|
|
205
|
+
# Calculate flux = corrected_counts / (sensitivity * solid_angle * delta_energy)
|
|
206
|
+
skymap.data_1d["flux"] = skymap.data_1d["corrected_count_rate"] / (
|
|
207
|
+
skymap.data_1d["sensitivity"] * skymap.solid_angle * delta_energy
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
skymap.data_1d["flux_uncertainty"] = (
|
|
211
|
+
skymap.data_1d["counts"].astype(float) ** 0.5
|
|
212
|
+
) / (
|
|
213
|
+
skymap.data_1d["exposure_factor"]
|
|
214
|
+
* skymap.data_1d["sensitivity"]
|
|
215
|
+
* skymap.solid_angle
|
|
216
|
+
* delta_energy
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# Drop the variables that are no longer needed
|
|
220
|
+
skymap.data_1d = skymap.data_1d.drop_vars(
|
|
221
|
+
VARIABLES_TO_DROP_AFTER_FLUX_CALCULATION,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
return skymap
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def ultra_l2(
|
|
228
|
+
data_dict: dict[str, xr.Dataset | str],
|
|
229
|
+
data_version: str,
|
|
230
|
+
output_map_structure: (
|
|
231
|
+
ena_maps.RectangularSkyMap | ena_maps.HealpixSkyMap
|
|
232
|
+
) = DEFAULT_ULTRA_L2_MAP_STRUCTURE,
|
|
233
|
+
) -> list[xr.Dataset]:
|
|
234
|
+
"""
|
|
235
|
+
Generate and format Ultra L2 ENA Map Product from L1C Products.
|
|
236
|
+
|
|
237
|
+
Parameters
|
|
238
|
+
----------
|
|
239
|
+
data_dict : dict[str, xr.Dataset]
|
|
240
|
+
Dict mapping l1c product identifiers to paths/Datasets containing l1c psets.
|
|
241
|
+
data_version : str
|
|
242
|
+
Version of the data product being created.
|
|
243
|
+
output_map_structure : ena_maps.RectangularSkyMap | ena_maps.HealpixSkyMap, optional
|
|
244
|
+
Empty SkyMap structure providing the properties of the map to be generated.
|
|
245
|
+
Defaults to DEFAULT_ULTRA_L2_MAP_STRUCTURE defined in this module.
|
|
246
|
+
|
|
247
|
+
Returns
|
|
248
|
+
-------
|
|
249
|
+
list[xarray.Dataset,]
|
|
250
|
+
L2 output dataset containing map of the counts on the sky.
|
|
251
|
+
Wrapped in a list for consistency with other product levels.
|
|
252
|
+
|
|
253
|
+
Raises
|
|
254
|
+
------
|
|
255
|
+
NotImplementedError
|
|
256
|
+
If asked to project to a rectangular map.
|
|
257
|
+
# TODO: This is coming shortly
|
|
258
|
+
"""
|
|
259
|
+
l1c_products = data_dict.values()
|
|
260
|
+
num_l1c_products = len(l1c_products)
|
|
261
|
+
logger.info(f"Running ultra_l2 processing on {num_l1c_products} L1C products")
|
|
262
|
+
|
|
263
|
+
# Regardless of the output sky tiling type, we will directly
|
|
264
|
+
# project the PSET values into a healpix map. However, if we are outputting
|
|
265
|
+
# a Healpix map, we can go directly to map with desired nside, nested params
|
|
266
|
+
healpix_skymap = generate_ultra_healpix_skymap(
|
|
267
|
+
ultra_l1c_psets=list(l1c_products),
|
|
268
|
+
output_map_structure=output_map_structure,
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
# Output formatting for HEALPIX tiling
|
|
272
|
+
if output_map_structure.tiling_type is ena_maps.SkyTilingType.HEALPIX:
|
|
273
|
+
map_dataset = healpix_skymap.to_dataset()
|
|
274
|
+
# Add attributes related to the map
|
|
275
|
+
map_attrs = {
|
|
276
|
+
"HEALPix_nside": output_map_structure.nside,
|
|
277
|
+
"HEALPix_nest": output_map_structure.nested,
|
|
278
|
+
"Data_version": data_version,
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
# TODO: Implement conversion to Rectangular map
|
|
282
|
+
elif output_map_structure.tiling_type is ena_maps.SkyTilingType.RECTANGULAR:
|
|
283
|
+
map_attrs = {
|
|
284
|
+
"Spacing_degrees": output_map_structure.spacing_deg,
|
|
285
|
+
"Data_version": data_version,
|
|
286
|
+
}
|
|
287
|
+
raise NotImplementedError
|
|
288
|
+
|
|
289
|
+
# Always add the following attributes to the map
|
|
290
|
+
map_attrs.update(
|
|
291
|
+
{
|
|
292
|
+
"Sky_tiling_type": output_map_structure.tiling_type.value,
|
|
293
|
+
"Spice_reference_frame": output_map_structure.spice_reference_frame,
|
|
294
|
+
}
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
# Add the defined attributes to the map's global attrs
|
|
298
|
+
map_dataset.attrs.update(map_attrs)
|
|
299
|
+
return [map_dataset]
|