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
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import json
|
|
5
6
|
import logging
|
|
6
|
-
import pathlib
|
|
7
7
|
from abc import ABC, abstractmethod
|
|
8
8
|
from enum import Enum
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import TypeVar
|
|
9
11
|
|
|
10
12
|
import astropy_healpix.healpy as hp
|
|
11
13
|
import numpy as np
|
|
@@ -23,6 +25,9 @@ from imap_processing.spice.time import ttj2000ns_to_et
|
|
|
23
25
|
|
|
24
26
|
logger = logging.getLogger(__name__)
|
|
25
27
|
|
|
28
|
+
# Set the maximum recursion depth for the conversion from Healpix to rectangular SkyMap.
|
|
29
|
+
MAX_SUBDIV_RECURSION_DEPTH = 8
|
|
30
|
+
|
|
26
31
|
|
|
27
32
|
class SkyTilingType(Enum):
|
|
28
33
|
"""Enumeration of the types of tiling used in the ENA maps."""
|
|
@@ -133,9 +138,9 @@ def match_coords_to_indices(
|
|
|
133
138
|
# which must be converted to ephemeris time (ET) for SPICE.
|
|
134
139
|
if event_et is None:
|
|
135
140
|
if isinstance(input_object, PointingSet):
|
|
136
|
-
event_et = ttj2000ns_to_et(input_object.
|
|
141
|
+
event_et = ttj2000ns_to_et(input_object.epoch)
|
|
137
142
|
elif isinstance(output_object, PointingSet):
|
|
138
|
-
event_et = ttj2000ns_to_et(output_object.
|
|
143
|
+
event_et = ttj2000ns_to_et(output_object.epoch)
|
|
139
144
|
else:
|
|
140
145
|
raise ValueError(
|
|
141
146
|
"Event time must be specified if both objects are SkyMaps."
|
|
@@ -199,6 +204,11 @@ def match_coords_to_indices(
|
|
|
199
204
|
return flat_indices_input_grid_output_frame
|
|
200
205
|
|
|
201
206
|
|
|
207
|
+
# Define a TypeVar type to dynamically hint the return type of the base PointingSet
|
|
208
|
+
# class classmethod
|
|
209
|
+
T = TypeVar("T", bound="PointingSet")
|
|
210
|
+
|
|
211
|
+
|
|
202
212
|
# Define the pointing set classes
|
|
203
213
|
class PointingSet(ABC):
|
|
204
214
|
"""
|
|
@@ -209,20 +219,69 @@ class PointingSet(ABC):
|
|
|
209
219
|
|
|
210
220
|
Parameters
|
|
211
221
|
----------
|
|
212
|
-
dataset : xr.Dataset
|
|
213
|
-
Dataset containing the pointing set data.
|
|
222
|
+
dataset : xr.Dataset | str | Path
|
|
223
|
+
Dataset or path to CDF file containing the pointing set data.
|
|
214
224
|
spice_reference_frame : geometry.SpiceFrame
|
|
215
225
|
The reference Spice frame of the pointing set.
|
|
216
226
|
"""
|
|
217
227
|
|
|
228
|
+
# The minimum set of class attributes for any PointingSet to function with
|
|
229
|
+
# a SkyMap using only the PUSH method of projecting are defined here.
|
|
230
|
+
|
|
231
|
+
# ======== Attributes that are set in the ABC __init__ method ========
|
|
232
|
+
# The xarray.Dataset containing the data from the PSET CDF
|
|
233
|
+
data: xr.Dataset
|
|
234
|
+
# The spice frame that the az_el_points are expressed in
|
|
235
|
+
spice_reference_frame: geometry.SpiceFrame
|
|
236
|
+
|
|
237
|
+
# ======== Attributes required to be set in a subclass ========
|
|
238
|
+
# Azimuth and elevation coordinates of each spatial pixel. The ndarray should
|
|
239
|
+
# have the shape (n, 2) where n is the number of spatial pixels
|
|
240
|
+
az_el_points: np.ndarray
|
|
241
|
+
# Tuple containing the names of each spatial coordinate of the xarray.Dataset
|
|
242
|
+
# stored in the data attribute
|
|
243
|
+
spatial_coords: tuple[str, ...]
|
|
244
|
+
|
|
218
245
|
@abstractmethod
|
|
219
|
-
def __init__(
|
|
246
|
+
def __init__(
|
|
247
|
+
self,
|
|
248
|
+
dataset: xr.Dataset | str | Path,
|
|
249
|
+
spice_reference_frame: geometry.SpiceFrame = geometry.SpiceFrame.IMAP_DPS,
|
|
250
|
+
):
|
|
220
251
|
"""Abstract method to initialize the pointing set object."""
|
|
221
252
|
self.spice_reference_frame = spice_reference_frame
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
self.
|
|
253
|
+
|
|
254
|
+
if isinstance(dataset, (str, Path)):
|
|
255
|
+
dataset = load_cdf(dataset)
|
|
256
|
+
self.data = dataset
|
|
257
|
+
|
|
258
|
+
# A PSET must have a single epoch
|
|
259
|
+
if len(np.unique(self.data["epoch"].values)) > 1:
|
|
260
|
+
raise ValueError("Multiple epochs found in the dataset.")
|
|
261
|
+
|
|
262
|
+
@property
|
|
263
|
+
def num_points(self) -> int:
|
|
264
|
+
"""
|
|
265
|
+
The number of spatial pixels in the pointing set.
|
|
266
|
+
|
|
267
|
+
Returns
|
|
268
|
+
-------
|
|
269
|
+
num_points: int
|
|
270
|
+
The number of spatial pixels in the pointing set.
|
|
271
|
+
"""
|
|
272
|
+
return self.az_el_points.shape[0]
|
|
273
|
+
|
|
274
|
+
@property
|
|
275
|
+
def epoch(self) -> int:
|
|
276
|
+
"""
|
|
277
|
+
The singular epoch value from the xarray.Dataset.
|
|
278
|
+
|
|
279
|
+
Returns
|
|
280
|
+
-------
|
|
281
|
+
epoch: int
|
|
282
|
+
The epoch value [J2000 TT ns] of the pointing set.
|
|
283
|
+
"""
|
|
284
|
+
return self.data["epoch"].values[0]
|
|
226
285
|
|
|
227
286
|
@property
|
|
228
287
|
def unwrapped_dims_dict(self) -> dict[str, tuple[str, ...]]:
|
|
@@ -234,7 +293,7 @@ class PointingSet(ABC):
|
|
|
234
293
|
unwrapped_dims_dict : dict[str, tuple[str, ...]]
|
|
235
294
|
Dictionary of variable names and their dimensions, with only 1 spatial dim.
|
|
236
295
|
The generic pixel dimension is always included.
|
|
237
|
-
E.g.: {"counts": ("epoch", "
|
|
296
|
+
E.g.: {"counts": ("epoch", "energy", "pixel")} .
|
|
238
297
|
"""
|
|
239
298
|
variable_dims = {}
|
|
240
299
|
for var_name in self.data.data_vars:
|
|
@@ -287,8 +346,8 @@ class RectangularPointingSet(PointingSet):
|
|
|
287
346
|
|
|
288
347
|
Parameters
|
|
289
348
|
----------
|
|
290
|
-
|
|
291
|
-
|
|
349
|
+
dataset : xr.Dataset | str | Path
|
|
350
|
+
Dataset or path to CDF file containing the pointing set data.
|
|
292
351
|
Currently, the dataset is expected to be tiled in a rectangular grid,
|
|
293
352
|
with data_vars indexed along the coordinates:
|
|
294
353
|
- 'epoch' : time value (1 value per PSET)
|
|
@@ -306,26 +365,19 @@ class RectangularPointingSet(PointingSet):
|
|
|
306
365
|
If multiple epochs are found in the dataset.
|
|
307
366
|
"""
|
|
308
367
|
|
|
368
|
+
# In addition to the required attributes defined in the base PointingSet
|
|
369
|
+
# class, the following attributes are required for a RectangularPointingSet
|
|
370
|
+
# to be projected using the PULL method.
|
|
371
|
+
tiling_type: SkyTilingType = SkyTilingType.RECTANGULAR
|
|
372
|
+
sky_grid: spatial_utils.AzElSkyGrid
|
|
373
|
+
|
|
309
374
|
def __init__(
|
|
310
375
|
self,
|
|
311
|
-
|
|
376
|
+
dataset: xr.Dataset | str | Path,
|
|
312
377
|
spice_reference_frame: geometry.SpiceFrame = geometry.SpiceFrame.IMAP_DPS,
|
|
313
378
|
):
|
|
314
|
-
|
|
315
|
-
self.spice_reference_frame = spice_reference_frame
|
|
316
|
-
|
|
317
|
-
# Read in the data and store the xarray dataset as data attr
|
|
318
|
-
if isinstance(l1c_dataset, (str, pathlib.Path)):
|
|
319
|
-
self.data = load_cdf(pathlib.Path(l1c_dataset))
|
|
320
|
-
elif isinstance(l1c_dataset, xr.Dataset):
|
|
321
|
-
self.data = l1c_dataset
|
|
322
|
-
|
|
323
|
-
# A PSET must have a single epoch
|
|
324
|
-
self.epoch = self.data["epoch"].values
|
|
325
|
-
if len(np.unique(self.epoch)) > 1:
|
|
326
|
-
raise ValueError("Multiple epochs found in the dataset.")
|
|
379
|
+
super().__init__(dataset, spice_reference_frame)
|
|
327
380
|
|
|
328
|
-
self.tiling_type = SkyTilingType.RECTANGULAR
|
|
329
381
|
self.spatial_coords = (
|
|
330
382
|
CoordNames.AZIMUTH_L1C.value,
|
|
331
383
|
CoordNames.ELEVATION_L1C.value,
|
|
@@ -344,12 +396,12 @@ class RectangularPointingSet(PointingSet):
|
|
|
344
396
|
"Azimuth and elevation bin spacing do not match: "
|
|
345
397
|
f"az {az_bin_delta[0]} != el {el_bin_delta[0]}."
|
|
346
398
|
)
|
|
347
|
-
|
|
399
|
+
spacing_deg = az_bin_delta[0]
|
|
348
400
|
|
|
349
401
|
# Build the az/azimuth and el/elevation grids with an AzElSkyGrid object
|
|
350
402
|
# and check that the 1D axes match the dataset's az and el.
|
|
351
403
|
self.sky_grid = spatial_utils.AzElSkyGrid(
|
|
352
|
-
spacing_deg=
|
|
404
|
+
spacing_deg=spacing_deg,
|
|
353
405
|
)
|
|
354
406
|
|
|
355
407
|
for dim, constructed_bins in zip(
|
|
@@ -378,23 +430,47 @@ class RectangularPointingSet(PointingSet):
|
|
|
378
430
|
self.sky_grid.el_grid.ravel(),
|
|
379
431
|
)
|
|
380
432
|
)
|
|
381
|
-
self.num_points = self.az_el_points.shape[0]
|
|
382
433
|
|
|
383
|
-
# Also store the bin edges for the pointing set to allow for "pull" method
|
|
384
|
-
# of index matching (not yet implemented).
|
|
385
|
-
# These are 1D arrays of different lengths and cannot be stacked.
|
|
386
|
-
self.az_bin_edges = self.sky_grid.az_bin_edges
|
|
387
|
-
self.el_bin_edges = self.sky_grid.el_bin_edges
|
|
388
434
|
|
|
435
|
+
class HealpixPointingSet(PointingSet, ABC):
|
|
436
|
+
"""
|
|
437
|
+
Abstract base class for Healpix pointing sets.
|
|
438
|
+
|
|
439
|
+
Defines additional properties and absract properties that are required
|
|
440
|
+
for a PointingSet instance to be used with the match_coords_to_indices
|
|
441
|
+
function.
|
|
442
|
+
"""
|
|
443
|
+
|
|
444
|
+
tiling_type: SkyTilingType = SkyTilingType.HEALPIX
|
|
445
|
+
|
|
446
|
+
@property
|
|
447
|
+
def nside(self) -> int:
|
|
448
|
+
"""
|
|
449
|
+
Number of pixels on the side of one of the 12 top-level healpix tiles.
|
|
450
|
+
|
|
451
|
+
Returns
|
|
452
|
+
-------
|
|
453
|
+
npix: int
|
|
454
|
+
The number of pixels on the side of one of the 12 ‘top-level’ healpix
|
|
455
|
+
tiles.
|
|
456
|
+
"""
|
|
457
|
+
return hp.npix_to_nside(self.num_points)
|
|
389
458
|
|
|
390
|
-
|
|
459
|
+
@property
|
|
460
|
+
@abstractmethod
|
|
461
|
+
def nested(self) -> bool:
|
|
462
|
+
"""Abstract property for getting nested boolean."""
|
|
463
|
+
raise NotImplementedError
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
class UltraPointingSet(HealpixPointingSet):
|
|
391
467
|
"""
|
|
392
468
|
Pointing set object specifically for Healpix-tiled ULTRA data, nominally at Level1C.
|
|
393
469
|
|
|
394
470
|
Parameters
|
|
395
471
|
----------
|
|
396
|
-
|
|
397
|
-
|
|
472
|
+
dataset : xr.Dataset | str | Path
|
|
473
|
+
Dataset or path to CDF file containing the pointing set data.
|
|
398
474
|
Currently, the dataset is expected to be tiled in a HEALPix tessellation,
|
|
399
475
|
with data_vars indexed along the coordinates:
|
|
400
476
|
- 'epoch' : time value (1 value per PSET, from the mean of the PSET)
|
|
@@ -415,36 +491,19 @@ class UltraPointingSet(PointingSet):
|
|
|
415
491
|
|
|
416
492
|
def __init__(
|
|
417
493
|
self,
|
|
418
|
-
|
|
494
|
+
dataset: xr.Dataset | str | Path,
|
|
419
495
|
spice_reference_frame: geometry.SpiceFrame = geometry.SpiceFrame.IMAP_DPS,
|
|
420
496
|
):
|
|
421
|
-
|
|
422
|
-
self.spice_reference_frame = spice_reference_frame
|
|
423
|
-
|
|
424
|
-
# Read in the data and store the xarray dataset as data attr
|
|
425
|
-
if isinstance(l1c_dataset, (str, pathlib.Path)):
|
|
426
|
-
self.data = load_cdf(pathlib.Path(l1c_dataset))
|
|
427
|
-
elif isinstance(l1c_dataset, xr.Dataset):
|
|
428
|
-
self.data = l1c_dataset
|
|
497
|
+
super().__init__(dataset, spice_reference_frame)
|
|
429
498
|
|
|
430
|
-
#
|
|
431
|
-
self.epoch = self.data["epoch"].values
|
|
432
|
-
if len(np.unique(self.epoch)) > 1:
|
|
433
|
-
raise ValueError("Multiple epochs found in the dataset.")
|
|
434
|
-
|
|
435
|
-
# Set the tiling type and number of points
|
|
436
|
-
self.tiling_type = SkyTilingType.HEALPIX
|
|
499
|
+
# Set the spatial coordinates and number of points
|
|
437
500
|
self.spatial_coords = (CoordNames.HEALPIX_INDEX.value,)
|
|
438
|
-
self.num_points = self.data[CoordNames.HEALPIX_INDEX.value].size
|
|
439
|
-
self.nside = hp.npix_to_nside(self.num_points)
|
|
440
501
|
|
|
441
|
-
#
|
|
442
|
-
self.
|
|
443
|
-
self.data[CoordNames.HEALPIX_INDEX.value].attrs.get("nested", False)
|
|
444
|
-
)
|
|
502
|
+
# Tracks Per-Pixel Solid Angle in steradians.
|
|
503
|
+
self.solid_angle = hp.nside2pixarea(self.nside, degrees=False)
|
|
445
504
|
|
|
446
505
|
# Get the azimuth and elevation coordinates of the healpix pixel centers (deg)
|
|
447
|
-
|
|
506
|
+
azimuth_pixel_center, elevation_pixel_center = hp.pix2ang(
|
|
448
507
|
nside=self.nside,
|
|
449
508
|
ipix=np.arange(self.num_points),
|
|
450
509
|
nest=self.nested,
|
|
@@ -457,7 +516,7 @@ class UltraPointingSet(PointingSet):
|
|
|
457
516
|
# (e.g. "longitude"/"latitude" vs "azimuth"/"elevation").
|
|
458
517
|
for dim, constructed_bins in zip(
|
|
459
518
|
[CoordNames.AZIMUTH_L1C.value, CoordNames.ELEVATION_L1C.value],
|
|
460
|
-
[
|
|
519
|
+
[azimuth_pixel_center, elevation_pixel_center],
|
|
461
520
|
):
|
|
462
521
|
if not np.allclose(
|
|
463
522
|
self.data[dim],
|
|
@@ -475,7 +534,33 @@ class UltraPointingSet(PointingSet):
|
|
|
475
534
|
# of shape (num_points, 2) where column 0 is the lon/az
|
|
476
535
|
# and column 1 is the lat/el.
|
|
477
536
|
self.az_el_points = np.column_stack(
|
|
478
|
-
(
|
|
537
|
+
(azimuth_pixel_center, elevation_pixel_center)
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
@property
|
|
541
|
+
def num_points(self) -> int:
|
|
542
|
+
"""
|
|
543
|
+
Override the base class property to get the number from the dataset.
|
|
544
|
+
|
|
545
|
+
Returns
|
|
546
|
+
-------
|
|
547
|
+
num_points: int
|
|
548
|
+
The number of healpix pixels in the pointing set.
|
|
549
|
+
"""
|
|
550
|
+
return self.data[CoordNames.HEALPIX_INDEX.value].size
|
|
551
|
+
|
|
552
|
+
@property
|
|
553
|
+
def nested(self) -> bool:
|
|
554
|
+
"""
|
|
555
|
+
Whether the healpix tessellation is nested.
|
|
556
|
+
|
|
557
|
+
Returns
|
|
558
|
+
-------
|
|
559
|
+
nested: bool
|
|
560
|
+
Whether the healpix tessellation is nested.
|
|
561
|
+
"""
|
|
562
|
+
return bool(
|
|
563
|
+
self.data[CoordNames.HEALPIX_INDEX.value].attrs.get("nested", False)
|
|
479
564
|
)
|
|
480
565
|
|
|
481
566
|
def __repr__(self) -> str:
|
|
@@ -494,6 +579,27 @@ class UltraPointingSet(PointingSet):
|
|
|
494
579
|
)
|
|
495
580
|
|
|
496
581
|
|
|
582
|
+
class HiPointingSet(PointingSet):
|
|
583
|
+
"""
|
|
584
|
+
PointingSet object specific to Hi L1C PSet data.
|
|
585
|
+
|
|
586
|
+
Parameters
|
|
587
|
+
----------
|
|
588
|
+
dataset : xarray.Dataset
|
|
589
|
+
Hi L1C pointing set data loaded in an xarray.DataArray.
|
|
590
|
+
"""
|
|
591
|
+
|
|
592
|
+
def __init__(self, dataset: xr.Dataset):
|
|
593
|
+
super().__init__(dataset, spice_reference_frame=geometry.SpiceFrame.ECLIPJ2000)
|
|
594
|
+
self.az_el_points = np.column_stack(
|
|
595
|
+
(
|
|
596
|
+
np.squeeze(self.data["hae_longitude"]),
|
|
597
|
+
np.squeeze(self.data["hae_latitude"]),
|
|
598
|
+
)
|
|
599
|
+
)
|
|
600
|
+
self.spatial_coords = ("spin_angle_bin",)
|
|
601
|
+
|
|
602
|
+
|
|
497
603
|
# Define the Map classes
|
|
498
604
|
class AbstractSkyMap(ABC):
|
|
499
605
|
"""
|
|
@@ -519,6 +625,10 @@ class AbstractSkyMap(ABC):
|
|
|
519
625
|
self.binning_grid_shape: tuple[int, ...]
|
|
520
626
|
self.data_1d: xr.Dataset
|
|
521
627
|
|
|
628
|
+
# Initialize values to be used by the instrument code to push/pull
|
|
629
|
+
self.values_to_push_project: list[str] = []
|
|
630
|
+
self.values_to_pull_project: list[str] = []
|
|
631
|
+
|
|
522
632
|
def to_dataset(self) -> xr.Dataset:
|
|
523
633
|
"""
|
|
524
634
|
Get the SkyMap data as a formatted xarray Dataset.
|
|
@@ -685,8 +795,164 @@ class AbstractSkyMap(ABC):
|
|
|
685
795
|
"Only PUSH and PULL index matching methods are supported."
|
|
686
796
|
)
|
|
687
797
|
|
|
798
|
+
# TODO: we may need to allow for unweighted/weighted means here by
|
|
799
|
+
# dividing pointing_projected_values by some binned weights.
|
|
800
|
+
# For unweighted means, we could use the number of pointing set pixels
|
|
801
|
+
# that correspond to each map pixel as the weights.
|
|
688
802
|
self.data_1d[value_key] += pointing_projected_values
|
|
689
803
|
|
|
804
|
+
@classmethod
|
|
805
|
+
def from_json(cls, json_path: str | Path) -> RectangularSkyMap | HealpixSkyMap:
|
|
806
|
+
"""
|
|
807
|
+
Create a SkyMap object from a JSON configuration file.
|
|
808
|
+
|
|
809
|
+
Parameters
|
|
810
|
+
----------
|
|
811
|
+
json_path : str | Path
|
|
812
|
+
Path to the JSON configuration file.
|
|
813
|
+
|
|
814
|
+
Returns
|
|
815
|
+
-------
|
|
816
|
+
RectangularSkyMap | HealpixSkyMap
|
|
817
|
+
An instance of a SkyMap object with the specified properties.
|
|
818
|
+
"""
|
|
819
|
+
with open(json_path) as f:
|
|
820
|
+
properties = json.load(f)
|
|
821
|
+
return cls.from_dict(properties)
|
|
822
|
+
|
|
823
|
+
@classmethod
|
|
824
|
+
def from_dict(cls, properties: dict) -> RectangularSkyMap | HealpixSkyMap:
|
|
825
|
+
"""
|
|
826
|
+
Create a SkyMap object from a dictionary of properties.
|
|
827
|
+
|
|
828
|
+
Parameters
|
|
829
|
+
----------
|
|
830
|
+
properties : dict
|
|
831
|
+
Dictionary containing the map properties. The required keys are:
|
|
832
|
+
- "spice_reference_frame" : str
|
|
833
|
+
The reference Spice frame of the map as a string. The available
|
|
834
|
+
options are defined in the spice geometry module:
|
|
835
|
+
`imap_processing.geometry.spice.SpiceFrame`. Example: "ECLIPJ2000".
|
|
836
|
+
- "sky_tiling_type" : str
|
|
837
|
+
The type of sky tiling, either "HEALPIX" or "RECTANGULAR".
|
|
838
|
+
- if "HEALPIX":
|
|
839
|
+
- "nside" : int
|
|
840
|
+
The nside parameter for the Healpix tessellation.
|
|
841
|
+
- "nested" : bool
|
|
842
|
+
Whether the Healpix tessellation is nested or not.
|
|
843
|
+
- if "RECTANGULAR":
|
|
844
|
+
- "spacing_deg" : float
|
|
845
|
+
The spacing of the rectangular grid in degrees.
|
|
846
|
+
- "values_to_push_project" : list[str], optional
|
|
847
|
+
The names of the variables to project to the map with the PUSH method.
|
|
848
|
+
NOTE: The projection is done by the instrument code, so this value can
|
|
849
|
+
only be used to inform that code. No values are projected automatically.
|
|
850
|
+
- "values_to_pull_project" : list[str], optional
|
|
851
|
+
The names of the variables to project to the map with the PULL method.
|
|
852
|
+
See the above note for more details.
|
|
853
|
+
|
|
854
|
+
See example dictionary in notes section.
|
|
855
|
+
|
|
856
|
+
Returns
|
|
857
|
+
-------
|
|
858
|
+
RectangularSkyMap | HealpixSkyMap
|
|
859
|
+
An instance of a SkyMap object with the specified properties.
|
|
860
|
+
|
|
861
|
+
Raises
|
|
862
|
+
------
|
|
863
|
+
ValueError
|
|
864
|
+
If the sky tiling type is not recognized.
|
|
865
|
+
|
|
866
|
+
Notes
|
|
867
|
+
-----
|
|
868
|
+
Example dictionary:
|
|
869
|
+
|
|
870
|
+
```python
|
|
871
|
+
properties = {
|
|
872
|
+
"spice_reference_frame": "ECLIPJ2000",
|
|
873
|
+
"sky_tiling_type": "HEALPIX",
|
|
874
|
+
"nside": 32,
|
|
875
|
+
"nested": False,
|
|
876
|
+
"values_to_push_project": ['counts', 'flux'],
|
|
877
|
+
"values_to_pull_project": []
|
|
878
|
+
}
|
|
879
|
+
```
|
|
880
|
+
"""
|
|
881
|
+
sky_tiling_type = SkyTilingType[properties["sky_tiling_type"].upper()]
|
|
882
|
+
spice_reference_frame = geometry.SpiceFrame[properties["spice_reference_frame"]]
|
|
883
|
+
|
|
884
|
+
skymap: RectangularSkyMap | HealpixSkyMap # Mypy gets confused by if/elif types
|
|
885
|
+
if sky_tiling_type is SkyTilingType.HEALPIX:
|
|
886
|
+
skymap = HealpixSkyMap(
|
|
887
|
+
nside=properties["nside"],
|
|
888
|
+
nested=properties["nested"],
|
|
889
|
+
spice_frame=spice_reference_frame,
|
|
890
|
+
)
|
|
891
|
+
elif sky_tiling_type is SkyTilingType.RECTANGULAR:
|
|
892
|
+
skymap = RectangularSkyMap(
|
|
893
|
+
spacing_deg=properties["spacing_deg"],
|
|
894
|
+
spice_frame=spice_reference_frame,
|
|
895
|
+
)
|
|
896
|
+
else:
|
|
897
|
+
raise ValueError(
|
|
898
|
+
f"Unknown sky tiling type: {sky_tiling_type}. "
|
|
899
|
+
f"Must be one of: {SkyTilingType.__members__.keys()}"
|
|
900
|
+
)
|
|
901
|
+
|
|
902
|
+
# Store requested variables to push/pull, which will be done by the instrument
|
|
903
|
+
# code which creates and uses the SkyMap object.
|
|
904
|
+
skymap.values_to_push_project = properties.get("values_to_push_project", [])
|
|
905
|
+
skymap.values_to_pull_project = properties.get("values_to_pull_project", [])
|
|
906
|
+
return skymap
|
|
907
|
+
|
|
908
|
+
def to_dict(self) -> dict:
|
|
909
|
+
"""
|
|
910
|
+
Convert the SkyMap object to a dictionary of properties.
|
|
911
|
+
|
|
912
|
+
Returns
|
|
913
|
+
-------
|
|
914
|
+
dict
|
|
915
|
+
Dictionary containing the map properties.
|
|
916
|
+
"""
|
|
917
|
+
if isinstance(self, HealpixSkyMap):
|
|
918
|
+
map_properties_dict = {
|
|
919
|
+
"sky_tiling_type": "HEALPIX",
|
|
920
|
+
"spice_reference_frame": self.spice_reference_frame.name,
|
|
921
|
+
"nside": self.nside,
|
|
922
|
+
"nested": self.nested,
|
|
923
|
+
}
|
|
924
|
+
elif isinstance(self, RectangularSkyMap):
|
|
925
|
+
map_properties_dict = {
|
|
926
|
+
"sky_tiling_type": "RECTANGULAR",
|
|
927
|
+
"spice_reference_frame": self.spice_reference_frame.name,
|
|
928
|
+
"spacing_deg": self.spacing_deg,
|
|
929
|
+
}
|
|
930
|
+
else:
|
|
931
|
+
raise ValueError(
|
|
932
|
+
f"Unknown SkyMap type: {self.__class__.__name__}. "
|
|
933
|
+
f"Must be one of: {AbstractSkyMap.__subclasses__()}"
|
|
934
|
+
)
|
|
935
|
+
|
|
936
|
+
map_properties_dict["values_to_push_project"] = (
|
|
937
|
+
self.values_to_push_project if self.values_to_push_project else []
|
|
938
|
+
)
|
|
939
|
+
map_properties_dict["values_to_pull_project"] = (
|
|
940
|
+
self.values_to_pull_project if self.values_to_pull_project else []
|
|
941
|
+
)
|
|
942
|
+
return map_properties_dict
|
|
943
|
+
|
|
944
|
+
def to_json(self, json_path: str | Path) -> None:
|
|
945
|
+
"""
|
|
946
|
+
Save the SkyMap object to a JSON configuration file.
|
|
947
|
+
|
|
948
|
+
Parameters
|
|
949
|
+
----------
|
|
950
|
+
json_path : str | Path
|
|
951
|
+
Path to the JSON file where the properties will be saved.
|
|
952
|
+
"""
|
|
953
|
+
with open(json_path, "w") as f:
|
|
954
|
+
json.dump(self.to_dict(), f, indent=4)
|
|
955
|
+
|
|
690
956
|
|
|
691
957
|
class RectangularSkyMap(AbstractSkyMap):
|
|
692
958
|
"""
|
|
@@ -720,7 +986,7 @@ class RectangularSkyMap(AbstractSkyMap):
|
|
|
720
986
|
in degrees, with a spacing of 30 degrees. There will be 12 azimuth bins and 6
|
|
721
987
|
elevation bins in this example, resulting in 72 pixels in the map.
|
|
722
988
|
|
|
723
|
-
A
|
|
989
|
+
A multidimensional value (e.g. counts, with energy levels at each pixel)
|
|
724
990
|
will be stored as a 2D array with the first axis as the energy dimension and the
|
|
725
991
|
second axis as the pixel index.
|
|
726
992
|
|
|
@@ -749,6 +1015,10 @@ class RectangularSkyMap(AbstractSkyMap):
|
|
|
749
1015
|
# The reference Spice frame of the map, in which angles are defined
|
|
750
1016
|
self.spice_reference_frame = spice_frame
|
|
751
1017
|
|
|
1018
|
+
# Initialize values to be used by the instrument code to push/pull
|
|
1019
|
+
self.values_to_push_project: list[str] = []
|
|
1020
|
+
self.values_to_pull_project: list[str] = []
|
|
1021
|
+
|
|
752
1022
|
# Angular spacing of the map grid (degrees) defines the number, size of pixels.
|
|
753
1023
|
self.spacing_deg = spacing_deg
|
|
754
1024
|
self.sky_grid = spatial_utils.AzElSkyGrid(
|
|
@@ -829,6 +1099,10 @@ class HealpixSkyMap(AbstractSkyMap):
|
|
|
829
1099
|
self.tiling_type = SkyTilingType.HEALPIX
|
|
830
1100
|
self.spice_reference_frame = spice_frame
|
|
831
1101
|
|
|
1102
|
+
# Initialize values to be used by the instrument code to push/pull
|
|
1103
|
+
self.values_to_push_project: list[str] = []
|
|
1104
|
+
self.values_to_pull_project: list[str] = []
|
|
1105
|
+
|
|
832
1106
|
# Tile the sky with a Healpix tessellation. Defined by nside, nested parameters.
|
|
833
1107
|
self.nside = nside
|
|
834
1108
|
self.nested = nested
|
|
@@ -867,6 +1141,325 @@ class HealpixSkyMap(AbstractSkyMap):
|
|
|
867
1141
|
}
|
|
868
1142
|
)
|
|
869
1143
|
|
|
1144
|
+
# Define several methods for converting a Healpix map to a Rectangular map:
|
|
1145
|
+
def calculate_rect_pixel_value_from_healpix_map_n_subdivisions(
|
|
1146
|
+
self,
|
|
1147
|
+
rect_pix_center_lon_lat: np.typing.NDArray | tuple[float, float],
|
|
1148
|
+
rect_pix_spacing_deg: float,
|
|
1149
|
+
value_array: xr.DataArray,
|
|
1150
|
+
num_subdivisions: int,
|
|
1151
|
+
) -> np.typing.NDArray:
|
|
1152
|
+
"""
|
|
1153
|
+
Interpolate the value of a rectangular pixel from a healpix map w/ subdivisions.
|
|
1154
|
+
|
|
1155
|
+
This function splits a single rectangular pixel into smaller subpixels
|
|
1156
|
+
and calculates the solid angle weighted mean value of
|
|
1157
|
+
the healpix map at all of the subpixel centers.
|
|
1158
|
+
|
|
1159
|
+
Parameters
|
|
1160
|
+
----------
|
|
1161
|
+
rect_pix_center_lon_lat : np.typing.NDArray | tuple[float, float]
|
|
1162
|
+
The center longitude and latitude of the rectangular pixel.
|
|
1163
|
+
rect_pix_spacing_deg : float
|
|
1164
|
+
The spacing of the rectangular pixel in degrees.
|
|
1165
|
+
value_array : xr.DataArray
|
|
1166
|
+
The data array containing the healpix map values.
|
|
1167
|
+
num_subdivisions : int
|
|
1168
|
+
The number of subdivisions to create for the rectangular pixel.
|
|
1169
|
+
The more subdivisions, the more accurate the interpolation, but also
|
|
1170
|
+
the more computationally expensive it is.
|
|
1171
|
+
|
|
1172
|
+
Returns
|
|
1173
|
+
-------
|
|
1174
|
+
np.typing.NDArray
|
|
1175
|
+
The mean value of the healpix map at the subpixel centers.
|
|
1176
|
+
|
|
1177
|
+
If value_array has a single value at each pixel, the output
|
|
1178
|
+
will be a single value, but if there are other dimensions,
|
|
1179
|
+
(e.g., if self.data_1d['flux'].sizes =
|
|
1180
|
+
{"epoch": 1, "energy": 24, "pixel": 16200}),
|
|
1181
|
+
the output will be an array with the same dims except the pixel dimension
|
|
1182
|
+
(e.g., (1, 24)).
|
|
1183
|
+
"""
|
|
1184
|
+
# Assumes that you already checked the pixel doesn't fall entirely in an HP pix
|
|
1185
|
+
# TODO: Ask Nick if we need to add this here to mimic his code.
|
|
1186
|
+
# It shouldn't really be necessary, as the next function
|
|
1187
|
+
# get_pixel_value_recursive_subdivs will finish at 1 subdivision
|
|
1188
|
+
|
|
1189
|
+
# Ensure input contains lon in the first column and lat in the second column
|
|
1190
|
+
rect_pix_center_lon_lat = np.array(rect_pix_center_lon_lat).reshape(-1, 2)
|
|
1191
|
+
|
|
1192
|
+
# Calculate the number of subdivisions and the spacing of the subpixels
|
|
1193
|
+
# Then calculate the subpixel centers
|
|
1194
|
+
n_subpix_side = 2**num_subdivisions
|
|
1195
|
+
subpix_spacing = rect_pix_spacing_deg / n_subpix_side
|
|
1196
|
+
left_edge_lon = rect_pix_center_lon_lat[:, 0] - rect_pix_spacing_deg / 2
|
|
1197
|
+
bottom_edge_lat = rect_pix_center_lon_lat[:, 1] - rect_pix_spacing_deg / 2
|
|
1198
|
+
|
|
1199
|
+
rect_subpix_lon_ctrs = (
|
|
1200
|
+
left_edge_lon
|
|
1201
|
+
+ subpix_spacing * np.arange(n_subpix_side)
|
|
1202
|
+
+ subpix_spacing / 2
|
|
1203
|
+
)
|
|
1204
|
+
rect_subpix_lat_ctrs = (
|
|
1205
|
+
bottom_edge_lat
|
|
1206
|
+
+ subpix_spacing * np.arange(n_subpix_side)
|
|
1207
|
+
+ subpix_spacing / 2
|
|
1208
|
+
)
|
|
1209
|
+
|
|
1210
|
+
# We must weight by solid angle, which is not exactly equal for all subpixels
|
|
1211
|
+
# Calculate the solid angle of the full rectangular pixel (sterad)
|
|
1212
|
+
full_rect_pixel_solid_angle = np.deg2rad(rect_pix_spacing_deg) * (
|
|
1213
|
+
np.sin(np.deg2rad(bottom_edge_lat + rect_pix_spacing_deg))
|
|
1214
|
+
- np.sin(np.deg2rad(bottom_edge_lat))
|
|
1215
|
+
)
|
|
1216
|
+
|
|
1217
|
+
# Calculate solid angle of each subpix from the rect_subpix_lat_ctrs (sterad)
|
|
1218
|
+
all_edges_lat = bottom_edge_lat + np.arange(n_subpix_side + 1) * subpix_spacing
|
|
1219
|
+
sine_all_edges_lat = np.sin(np.deg2rad(all_edges_lat))
|
|
1220
|
+
rect_subpix_solid_angle_by_lat = np.diff(sine_all_edges_lat) * np.deg2rad(
|
|
1221
|
+
subpix_spacing
|
|
1222
|
+
)
|
|
1223
|
+
rect_subpix_solid_angle_by_lat = np.repeat(
|
|
1224
|
+
rect_subpix_solid_angle_by_lat[np.newaxis, :], n_subpix_side, axis=0
|
|
1225
|
+
).reshape(-1)
|
|
1226
|
+
|
|
1227
|
+
rect_subpix_ctrs = (
|
|
1228
|
+
np.array(
|
|
1229
|
+
np.meshgrid(rect_subpix_lon_ctrs, rect_subpix_lat_ctrs, indexing="ij")
|
|
1230
|
+
)
|
|
1231
|
+
.reshape(2, -1)
|
|
1232
|
+
.T
|
|
1233
|
+
)
|
|
1234
|
+
|
|
1235
|
+
# Get the healpix pixel indices at the rectangular subpixel centers
|
|
1236
|
+
hp_pix_at_rect_subpix_ctrs = hp.ang2pix(
|
|
1237
|
+
nside=self.nside,
|
|
1238
|
+
nest=self.nested,
|
|
1239
|
+
theta=rect_subpix_ctrs[:, 0],
|
|
1240
|
+
phi=rect_subpix_ctrs[:, 1],
|
|
1241
|
+
lonlat=True,
|
|
1242
|
+
)
|
|
1243
|
+
# Get the healpix values at the rectangular subpixel centers
|
|
1244
|
+
hp_vals_at_rect_pix_ctrs = value_array.values[..., hp_pix_at_rect_subpix_ctrs]
|
|
1245
|
+
|
|
1246
|
+
# Weighted mean (weighted by solid angle) of these values over the pixel axis,
|
|
1247
|
+
# which is the last axis of this array
|
|
1248
|
+
weighted_hp_vals_at_rect_pix_ctrs = (
|
|
1249
|
+
hp_vals_at_rect_pix_ctrs * rect_subpix_solid_angle_by_lat
|
|
1250
|
+
)
|
|
1251
|
+
mean_pixel_value = (
|
|
1252
|
+
weighted_hp_vals_at_rect_pix_ctrs.sum(axis=-1) / full_rect_pixel_solid_angle
|
|
1253
|
+
)
|
|
1254
|
+
# Log the mean pixel value and the number of subdivisions for debugging
|
|
1255
|
+
logger.debug(
|
|
1256
|
+
f" Mean pixel value at Number of subdivisions: {num_subdivisions}: "
|
|
1257
|
+
f"array of shape {mean_pixel_value.shape}: {mean_pixel_value}"
|
|
1258
|
+
)
|
|
1259
|
+
return mean_pixel_value
|
|
1260
|
+
|
|
1261
|
+
def get_rect_pixel_value_recursive_subdivs(
|
|
1262
|
+
self,
|
|
1263
|
+
rect_pix_center_lon_lat: np.typing.NDArray | tuple[float, float],
|
|
1264
|
+
rect_pix_spacing_deg: float,
|
|
1265
|
+
value_array: xr.DataArray,
|
|
1266
|
+
*,
|
|
1267
|
+
rtol: float = 1e-3,
|
|
1268
|
+
atol: float = 1e-12,
|
|
1269
|
+
max_subdivision_depth: int = MAX_SUBDIV_RECURSION_DEPTH,
|
|
1270
|
+
) -> tuple[np.typing.NDArray, int]:
|
|
1271
|
+
"""
|
|
1272
|
+
Recursively subdivide a rectangular pixel to get a mean value within tolerances.
|
|
1273
|
+
|
|
1274
|
+
Takes a rectangular pixel, and recursively breaks it up into
|
|
1275
|
+
smaller and smaller subpixels, then calculates the solid-angle weighted mean
|
|
1276
|
+
of the healpix map's value at this pixel, until the difference
|
|
1277
|
+
between the mean values of two consecutive subdivisions is within the
|
|
1278
|
+
specified tolerances. The function returns the mean value at the final level
|
|
1279
|
+
of subdivision and the depth of recursion.
|
|
1280
|
+
|
|
1281
|
+
Parameters
|
|
1282
|
+
----------
|
|
1283
|
+
rect_pix_center_lon_lat : np.typing.NDArray | tuple[float, float]
|
|
1284
|
+
The center longitude and latitude of the rectangular pixel.
|
|
1285
|
+
rect_pix_spacing_deg : float
|
|
1286
|
+
The spacing of the rectangular pixel in degrees.
|
|
1287
|
+
value_array : xr.DataArray
|
|
1288
|
+
The data array containing the healpix map values to interpolate from.
|
|
1289
|
+
rtol : float, optional
|
|
1290
|
+
The relative tolerance for convergence, by default 1e-3.
|
|
1291
|
+
atol : float, optional
|
|
1292
|
+
The absolute tolerance for convergence, by default 1e-12.
|
|
1293
|
+
max_subdivision_depth : int, optional
|
|
1294
|
+
The maximum depth of recursion for subdivision,
|
|
1295
|
+
by default MAX_SUBDIV_RECURSION_DEPTH.
|
|
1296
|
+
Computation grows exponentially with depth, but only where the value
|
|
1297
|
+
has a significant gradient between adjacent healpix pixels.
|
|
1298
|
+
If the value is smooth, the recursion depth will be low.
|
|
1299
|
+
|
|
1300
|
+
Returns
|
|
1301
|
+
-------
|
|
1302
|
+
tuple[list[float], int]
|
|
1303
|
+
The mean value at the final level of subdivision and the depth of recursion.
|
|
1304
|
+
"""
|
|
1305
|
+
# Recursively subdivide a pixel and calculate its mean value until either the
|
|
1306
|
+
# difference between consecutive levels is within the specified tolerances
|
|
1307
|
+
# or the maximum recursion depth is reached
|
|
1308
|
+
depth = 0
|
|
1309
|
+
previous_mean_pixel_value: NDArray = np.full((1,), np.nan)
|
|
1310
|
+
while depth < max_subdivision_depth:
|
|
1311
|
+
mean_pixel_value = (
|
|
1312
|
+
self.calculate_rect_pixel_value_from_healpix_map_n_subdivisions(
|
|
1313
|
+
rect_pix_center_lon_lat=rect_pix_center_lon_lat,
|
|
1314
|
+
rect_pix_spacing_deg=rect_pix_spacing_deg,
|
|
1315
|
+
value_array=value_array,
|
|
1316
|
+
num_subdivisions=depth,
|
|
1317
|
+
)
|
|
1318
|
+
)
|
|
1319
|
+
|
|
1320
|
+
# Determine if tolerance is met
|
|
1321
|
+
# (skip on the 0th iteration, as there's no delta)
|
|
1322
|
+
if depth > 0:
|
|
1323
|
+
# TODO: Ask Nick/Ultra Instrument team if we need to compare each value
|
|
1324
|
+
# in the pixel's array, or just the mean value.
|
|
1325
|
+
if np.isclose(
|
|
1326
|
+
mean_pixel_value.mean(),
|
|
1327
|
+
previous_mean_pixel_value.mean(),
|
|
1328
|
+
rtol=rtol,
|
|
1329
|
+
atol=atol,
|
|
1330
|
+
):
|
|
1331
|
+
break
|
|
1332
|
+
depth += 1
|
|
1333
|
+
previous_mean_pixel_value = mean_pixel_value
|
|
1334
|
+
|
|
1335
|
+
logger.debug(
|
|
1336
|
+
f"Pixel at ({rect_pix_center_lon_lat} deg size={rect_pix_spacing_deg} deg,)"
|
|
1337
|
+
f" converged to {mean_pixel_value.mean()} in {depth} subdivisions."
|
|
1338
|
+
f" Previous mean was {previous_mean_pixel_value.mean()}."
|
|
1339
|
+
)
|
|
1340
|
+
# Only keep the last (best) mean pixel value
|
|
1341
|
+
return mean_pixel_value, depth
|
|
1342
|
+
|
|
1343
|
+
def to_rectangular_skymap(
|
|
1344
|
+
self,
|
|
1345
|
+
rect_spacing_deg: float,
|
|
1346
|
+
value_keys: list[str],
|
|
1347
|
+
max_subdivision_depth: int = MAX_SUBDIV_RECURSION_DEPTH,
|
|
1348
|
+
) -> tuple[RectangularSkyMap, dict[str, np.typing.NDArray]]:
|
|
1349
|
+
"""
|
|
1350
|
+
Interpolate a healpix map to a rectangular map using recursive subdivision.
|
|
1351
|
+
|
|
1352
|
+
Parameters
|
|
1353
|
+
----------
|
|
1354
|
+
rect_spacing_deg : float
|
|
1355
|
+
The spacing of the rectangular map in degrees.
|
|
1356
|
+
value_keys : list[str]
|
|
1357
|
+
The names of the values to interpolate from the healpix map.
|
|
1358
|
+
Each must be independently interpolated because the subdivision depth
|
|
1359
|
+
depends on the gradient of the value between adjacent healpix pixels.
|
|
1360
|
+
max_subdivision_depth : int, optional
|
|
1361
|
+
The maximum depth of recursion for subdivision,
|
|
1362
|
+
by default MAX_SUBDIV_RECURSION_DEPTH.
|
|
1363
|
+
|
|
1364
|
+
Returns
|
|
1365
|
+
-------
|
|
1366
|
+
tuple[RectangularSkyMap, dict[str, np.typing.NDArray]]
|
|
1367
|
+
A RectangularSkyMap containing the interpolated values, and a dictionary of
|
|
1368
|
+
each value and its corresponding subdivision depth by pixel.
|
|
1369
|
+
"""
|
|
1370
|
+
# Begin by defining the rectangular map we want to create, which must be
|
|
1371
|
+
# in the same spice reference frame as the healpix map
|
|
1372
|
+
rect_map = RectangularSkyMap(
|
|
1373
|
+
spacing_deg=rect_spacing_deg,
|
|
1374
|
+
spice_frame=self.spice_reference_frame,
|
|
1375
|
+
)
|
|
1376
|
+
|
|
1377
|
+
# Depending on the maximum recursion depth, the number of pixels in the
|
|
1378
|
+
# RectangularSkyMap, and the number of value keys, and especially on the
|
|
1379
|
+
# gradients of the values, the number of operations can be very large, so
|
|
1380
|
+
# log key information about the expected number of operations.
|
|
1381
|
+
approx_max_operations = (
|
|
1382
|
+
(4**max_subdivision_depth) * self.num_points * len(value_keys)
|
|
1383
|
+
)
|
|
1384
|
+
logger.info(
|
|
1385
|
+
f"Converting from a HealpixSkyMap(nside={self.nside}) to a "
|
|
1386
|
+
f"RectangularSkyMap(spacing_deg={rect_spacing_deg}) with recursive "
|
|
1387
|
+
"subdivision.\n The maximum recursion depth is "
|
|
1388
|
+
f"{max_subdivision_depth}, yielding a maximum number of healpix calls"
|
|
1389
|
+
f" of {approx_max_operations:.3e}."
|
|
1390
|
+
)
|
|
1391
|
+
|
|
1392
|
+
# Dict to hold the subdivision depth by pixel for each value key
|
|
1393
|
+
subdiv_depth_dict = {}
|
|
1394
|
+
for value_key in value_keys:
|
|
1395
|
+
# For each of the values, calculate each pixel's value with
|
|
1396
|
+
# recursive subdivision. Unfortunately, this must be done independently
|
|
1397
|
+
# for each value key.
|
|
1398
|
+
|
|
1399
|
+
# Yields a list of tuple (mean_value, depth) for each pixel in the map
|
|
1400
|
+
healpix_values_array = self.data_1d[value_key]
|
|
1401
|
+
best_value_and_recursion_depth_by_pixel = [
|
|
1402
|
+
self.get_rect_pixel_value_recursive_subdivs(
|
|
1403
|
+
rect_pix_center_lon_lat=lon_lat,
|
|
1404
|
+
rect_pix_spacing_deg=rect_map.spacing_deg,
|
|
1405
|
+
value_array=healpix_values_array,
|
|
1406
|
+
max_subdivision_depth=max_subdivision_depth,
|
|
1407
|
+
)
|
|
1408
|
+
for lon_lat in rect_map.az_el_points
|
|
1409
|
+
]
|
|
1410
|
+
|
|
1411
|
+
# Separate the best value and the recursion depth for each pixel
|
|
1412
|
+
# into two lists, then convert both to numpy arrays
|
|
1413
|
+
# and move the pixel dim to the last dim of values
|
|
1414
|
+
interpolated_data_by_rect_pixel, subdiv_depth_of_value_by_pixel = zip(
|
|
1415
|
+
*best_value_and_recursion_depth_by_pixel
|
|
1416
|
+
)
|
|
1417
|
+
interpolated_data_by_rect_pixel = np.moveaxis(
|
|
1418
|
+
np.array(interpolated_data_by_rect_pixel), 0, -1
|
|
1419
|
+
)
|
|
1420
|
+
subdiv_depth_of_value_by_pixel = np.array(subdiv_depth_of_value_by_pixel)
|
|
1421
|
+
|
|
1422
|
+
# This can introduce an extra dim as the last dim of the array
|
|
1423
|
+
# to values with only one dimension
|
|
1424
|
+
if len(healpix_values_array.dims) == 1:
|
|
1425
|
+
interpolated_data_by_rect_pixel = np.squeeze(
|
|
1426
|
+
interpolated_data_by_rect_pixel,
|
|
1427
|
+
)
|
|
1428
|
+
|
|
1429
|
+
# Store the best value(s) of each pixel in the rectangular map with the
|
|
1430
|
+
# leading coordinates of the healpix map, and the pixel coordinate last
|
|
1431
|
+
rect_map.data_1d[value_key] = xr.DataArray(
|
|
1432
|
+
data=interpolated_data_by_rect_pixel,
|
|
1433
|
+
dims=(*healpix_values_array.dims[:-1], CoordNames.GENERIC_PIXEL.value),
|
|
1434
|
+
)
|
|
1435
|
+
|
|
1436
|
+
# Update the coordinates of the rectangular map with any new coordinates
|
|
1437
|
+
# from the healpix map except the pixel coord,
|
|
1438
|
+
# which will be different in the rectangular map.
|
|
1439
|
+
for coord in healpix_values_array.coords:
|
|
1440
|
+
if coord not in (
|
|
1441
|
+
CoordNames.GENERIC_PIXEL.value,
|
|
1442
|
+
CoordNames.HEALPIX_INDEX.value,
|
|
1443
|
+
):
|
|
1444
|
+
rect_map.data_1d.coords[coord] = healpix_values_array.coords[coord]
|
|
1445
|
+
|
|
1446
|
+
# Add the subdivision depth by pixel of this value_key to the dictionary
|
|
1447
|
+
# This may be necessary for uncertainty estimation
|
|
1448
|
+
subdiv_depth_dict[value_key] = subdiv_depth_of_value_by_pixel
|
|
1449
|
+
logger.info(
|
|
1450
|
+
f"Summary of subdivision depth for {value_key}:\n"
|
|
1451
|
+
"Mean +/- std number of subdivisions for the "
|
|
1452
|
+
f"{rect_map.num_points} pixels of {value_key} is:\n"
|
|
1453
|
+
f" {np.mean(subdiv_depth_of_value_by_pixel):.6f}."
|
|
1454
|
+
f" +/- {np.std(subdiv_depth_of_value_by_pixel):.6f}.\n"
|
|
1455
|
+
"Min / Max number of subdivisions: \n"
|
|
1456
|
+
f" {np.min(subdiv_depth_of_value_by_pixel):.6f} / "
|
|
1457
|
+
f"{np.max(subdiv_depth_of_value_by_pixel):.6f}.\n"
|
|
1458
|
+
f"The maximum allowed depth is {max_subdivision_depth}."
|
|
1459
|
+
)
|
|
1460
|
+
|
|
1461
|
+
return rect_map, subdiv_depth_dict
|
|
1462
|
+
|
|
870
1463
|
def __repr__(self) -> str:
|
|
871
1464
|
"""
|
|
872
1465
|
Return a string representation of the HealpixSkyMap.
|