imap-processing 0.11.0__py3-none-any.whl → 0.12.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 +10 -11
- imap_processing/_version.py +2 -2
- imap_processing/ccsds/excel_to_xtce.py +65 -16
- imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +6 -28
- imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +365 -42
- imap_processing/cdf/config/imap_glows_global_cdf_attrs.yaml +0 -5
- imap_processing/cdf/config/imap_hi_global_cdf_attrs.yaml +10 -11
- imap_processing/cdf/config/imap_hi_variable_attrs.yaml +17 -19
- imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +26 -13
- imap_processing/cdf/config/imap_hit_l1a_variable_attrs.yaml +106 -116
- imap_processing/cdf/config/imap_hit_l1b_variable_attrs.yaml +120 -145
- imap_processing/cdf/config/imap_hit_l2_variable_attrs.yaml +14 -0
- imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +6 -9
- imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +1 -1
- imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +0 -12
- imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +1 -1
- imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +9 -21
- imap_processing/cdf/config/imap_mag_l1a_variable_attrs.yaml +361 -0
- imap_processing/cdf/config/imap_mag_l1b_variable_attrs.yaml +160 -0
- imap_processing/cdf/config/imap_mag_l1c_variable_attrs.yaml +160 -0
- imap_processing/cdf/config/imap_spacecraft_global_cdf_attrs.yaml +18 -0
- imap_processing/cdf/config/imap_spacecraft_variable_attrs.yaml +40 -0
- imap_processing/cdf/config/imap_swapi_global_cdf_attrs.yaml +1 -5
- imap_processing/cdf/config/imap_swe_global_cdf_attrs.yaml +12 -4
- imap_processing/cdf/config/imap_swe_l1a_variable_attrs.yaml +16 -2
- imap_processing/cdf/config/imap_swe_l1b_variable_attrs.yaml +48 -52
- imap_processing/cdf/config/imap_swe_l2_variable_attrs.yaml +71 -47
- imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +2 -14
- imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +51 -2
- imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +29 -14
- imap_processing/cdf/utils.py +13 -7
- imap_processing/cli.py +23 -8
- imap_processing/codice/codice_l1a.py +207 -85
- imap_processing/codice/constants.py +1322 -568
- imap_processing/codice/decompress.py +2 -6
- imap_processing/ena_maps/ena_maps.py +480 -116
- imap_processing/ena_maps/utils/coordinates.py +19 -0
- imap_processing/ena_maps/utils/map_utils.py +14 -17
- imap_processing/ena_maps/utils/spatial_utils.py +45 -47
- imap_processing/hi/l1a/hi_l1a.py +24 -18
- imap_processing/hi/l1a/histogram.py +0 -1
- imap_processing/hi/l1a/science_direct_event.py +6 -8
- imap_processing/hi/l1b/hi_l1b.py +31 -39
- imap_processing/hi/l1c/hi_l1c.py +405 -17
- imap_processing/hi/utils.py +58 -12
- imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt0-factors_20250219_v002.csv +205 -0
- imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt1-factors_20250219_v002.csv +205 -0
- imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt2-factors_20250219_v002.csv +205 -0
- imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt3-factors_20250219_v002.csv +205 -0
- imap_processing/hit/ancillary/imap_hit_l1b-to-l2-summed-dt0-factors_20250219_v002.csv +68 -0
- imap_processing/hit/hit_utils.py +173 -1
- imap_processing/hit/l0/constants.py +20 -11
- imap_processing/hit/l0/decom_hit.py +18 -4
- imap_processing/hit/l1a/hit_l1a.py +45 -54
- imap_processing/hit/l1b/constants.py +317 -0
- imap_processing/hit/l1b/hit_l1b.py +367 -18
- imap_processing/hit/l2/constants.py +281 -0
- imap_processing/hit/l2/hit_l2.py +614 -0
- imap_processing/hit/packet_definitions/hit_packet_definitions.xml +1323 -71
- imap_processing/ialirt/l0/mag_l0_ialirt_data.py +155 -0
- imap_processing/ialirt/l0/parse_mag.py +246 -0
- imap_processing/ialirt/l0/process_swe.py +252 -0
- imap_processing/ialirt/packet_definitions/ialirt.xml +7 -3
- imap_processing/ialirt/packet_definitions/ialirt_mag.xml +115 -0
- imap_processing/ialirt/utils/grouping.py +114 -0
- imap_processing/ialirt/utils/time.py +29 -0
- imap_processing/idex/atomic_masses.csv +22 -0
- imap_processing/idex/decode.py +2 -2
- imap_processing/idex/idex_constants.py +25 -0
- imap_processing/idex/idex_l1a.py +6 -7
- imap_processing/idex/idex_l1b.py +4 -31
- imap_processing/idex/idex_l2a.py +789 -0
- imap_processing/idex/idex_variable_unpacking_and_eu_conversion.csv +39 -33
- imap_processing/lo/l0/lo_science.py +6 -0
- imap_processing/lo/l1a/lo_l1a.py +0 -1
- imap_processing/lo/l1b/lo_l1b.py +177 -25
- imap_processing/mag/constants.py +8 -0
- imap_processing/mag/imap_mag_sdc-configuration_v001.yaml +6 -0
- imap_processing/mag/l0/decom_mag.py +10 -3
- imap_processing/mag/l1a/mag_l1a.py +22 -11
- imap_processing/mag/l1a/mag_l1a_data.py +28 -3
- imap_processing/mag/l1b/mag_l1b.py +190 -48
- imap_processing/mag/l1c/interpolation_methods.py +211 -0
- imap_processing/mag/l1c/mag_l1c.py +447 -9
- imap_processing/quality_flags.py +1 -0
- imap_processing/spacecraft/packet_definitions/scid_x252.xml +538 -0
- imap_processing/spacecraft/quaternions.py +123 -0
- imap_processing/spice/geometry.py +16 -19
- imap_processing/spice/repoint.py +120 -0
- imap_processing/swapi/l1/swapi_l1.py +4 -0
- imap_processing/swapi/l2/swapi_l2.py +0 -1
- imap_processing/swe/l1a/swe_l1a.py +47 -8
- imap_processing/swe/l1a/swe_science.py +5 -2
- imap_processing/swe/l1b/swe_l1b_science.py +103 -56
- imap_processing/swe/l2/swe_l2.py +60 -65
- imap_processing/swe/packet_definitions/swe_packet_definition.xml +1121 -1
- imap_processing/swe/utils/swe_constants.py +63 -0
- imap_processing/swe/utils/swe_utils.py +85 -28
- imap_processing/tests/ccsds/test_data/expected_output.xml +40 -1
- imap_processing/tests/ccsds/test_excel_to_xtce.py +23 -20
- imap_processing/tests/cdf/test_data/imap_instrument2_global_cdf_attrs.yaml +0 -2
- imap_processing/tests/codice/conftest.py +1 -1
- imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-counters-aggregated_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-counters-singles_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-ialirt_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-omni_20241110193700_v0.0.0.cdf +0 -0
- 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_hi-priorities_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-sectored_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-counters-aggregated_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-counters-singles_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-ialirt_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-angular_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-priority_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-species_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/data/validation/imap_codice_l1a_lo-sw-angular_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-priority_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-species_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/test_codice_l1a.py +110 -46
- imap_processing/tests/codice/test_decompress.py +4 -4
- imap_processing/tests/conftest.py +166 -10
- imap_processing/tests/ena_maps/conftest.py +51 -0
- imap_processing/tests/ena_maps/test_ena_maps.py +638 -109
- imap_processing/tests/ena_maps/test_map_utils.py +66 -43
- imap_processing/tests/ena_maps/test_spatial_utils.py +16 -20
- imap_processing/tests/hi/data/l0/H45_diag_fee_20250208.bin +0 -0
- imap_processing/tests/hi/data/l0/H45_diag_fee_20250208_verify.csv +205 -0
- imap_processing/tests/hi/test_hi_l1b.py +12 -15
- imap_processing/tests/hi/test_hi_l1c.py +234 -6
- imap_processing/tests/hi/test_l1a.py +30 -0
- imap_processing/tests/hi/test_science_direct_event.py +1 -1
- imap_processing/tests/hi/test_utils.py +24 -2
- imap_processing/tests/hit/helpers/l1_validation.py +39 -39
- imap_processing/tests/hit/test_data/hskp_sample.ccsds +0 -0
- imap_processing/tests/hit/test_data/imap_hit_l0_raw_20100105_v001.pkts +0 -0
- imap_processing/tests/hit/test_decom_hit.py +4 -0
- imap_processing/tests/hit/test_hit_l1a.py +24 -28
- imap_processing/tests/hit/test_hit_l1b.py +304 -40
- imap_processing/tests/hit/test_hit_l2.py +454 -0
- imap_processing/tests/hit/test_hit_utils.py +112 -2
- imap_processing/tests/hit/validation_data/hskp_sample_eu_3_6_2025.csv +89 -0
- imap_processing/tests/hit/validation_data/hskp_sample_raw.csv +89 -88
- imap_processing/tests/ialirt/test_data/l0/461971383-404.bin +0 -0
- imap_processing/tests/ialirt/test_data/l0/461971384-405.bin +0 -0
- imap_processing/tests/ialirt/test_data/l0/461971385-406.bin +0 -0
- imap_processing/tests/ialirt/test_data/l0/461971386-407.bin +0 -0
- imap_processing/tests/ialirt/test_data/l0/461971387-408.bin +0 -0
- imap_processing/tests/ialirt/test_data/l0/461971388-409.bin +0 -0
- imap_processing/tests/ialirt/test_data/l0/461971389-410.bin +0 -0
- imap_processing/tests/ialirt/test_data/l0/461971390-411.bin +0 -0
- imap_processing/tests/ialirt/test_data/l0/461971391-412.bin +0 -0
- imap_processing/tests/ialirt/test_data/l0/sample_decoded_i-alirt_data.csv +383 -0
- imap_processing/tests/ialirt/unit/test_grouping.py +81 -0
- imap_processing/tests/ialirt/unit/test_parse_mag.py +168 -0
- imap_processing/tests/ialirt/unit/test_process_swe.py +208 -3
- imap_processing/tests/ialirt/unit/test_time.py +16 -0
- imap_processing/tests/idex/conftest.py +62 -6
- imap_processing/tests/idex/test_data/imap_idex_l0_raw_20231218_v001.pkts +0 -0
- imap_processing/tests/idex/test_data/impact_14_tof_high_data.txt +4508 -4508
- imap_processing/tests/idex/test_idex_l1a.py +48 -4
- imap_processing/tests/idex/test_idex_l1b.py +3 -3
- imap_processing/tests/idex/test_idex_l2a.py +383 -0
- imap_processing/tests/lo/test_cdfs/imap_lo_l1a_de_20241022_v002.cdf +0 -0
- imap_processing/tests/lo/test_cdfs/imap_lo_l1a_spin_20241022_v002.cdf +0 -0
- imap_processing/tests/lo/test_lo_l1b.py +148 -4
- imap_processing/tests/lo/test_lo_science.py +1 -0
- imap_processing/tests/mag/conftest.py +69 -0
- imap_processing/tests/mag/test_mag_decom.py +1 -1
- imap_processing/tests/mag/test_mag_l1a.py +38 -0
- imap_processing/tests/mag/test_mag_l1b.py +34 -53
- imap_processing/tests/mag/test_mag_l1c.py +251 -20
- imap_processing/tests/mag/test_mag_validation.py +109 -25
- imap_processing/tests/mag/validation/L1b/T009/MAGScience-normal-(2,2)-8s-20250204-16h39.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T009/mag-l1a-l1b-t009-magi-out.csv +16 -16
- imap_processing/tests/mag/validation/L1b/T009/mag-l1a-l1b-t009-mago-out.csv +16 -16
- imap_processing/tests/mag/validation/L1b/T010/MAGScience-normal-(2,2)-8s-20250206-12h05.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T011/MAGScience-normal-(2,2)-8s-20250204-16h08.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T011/mag-l1a-l1b-t011-magi-out.csv +16 -16
- imap_processing/tests/mag/validation/L1b/T011/mag-l1a-l1b-t011-mago-out.csv +16 -16
- imap_processing/tests/mag/validation/L1b/T012/MAGScience-normal-(2,2)-8s-20250204-16h08.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T012/data.bin +0 -0
- imap_processing/tests/mag/validation/L1b/T012/field_like_all_ranges.txt +19200 -0
- imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-cal.cdf +0 -0
- imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-in.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-magi-out.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-mago-out.csv +17 -0
- imap_processing/tests/mag/validation/imap_calibration_mag_20240229_v01.cdf +0 -0
- imap_processing/tests/spacecraft/__init__.py +0 -0
- imap_processing/tests/spacecraft/data/SSR_2024_190_20_08_12_0483851794_2_DA_apid0594_1packet.pkts +0 -0
- imap_processing/tests/spacecraft/test_quaternions.py +71 -0
- imap_processing/tests/spice/test_data/fake_repoint_data.csv +5 -0
- imap_processing/tests/spice/test_geometry.py +6 -9
- imap_processing/tests/spice/test_repoint.py +111 -0
- imap_processing/tests/swapi/test_swapi_l1.py +7 -3
- imap_processing/tests/swe/l0_data/2024051010_SWE_HK_packet.bin +0 -0
- imap_processing/tests/swe/l0_data/2024051011_SWE_CEM_RAW_packet.bin +0 -0
- imap_processing/tests/swe/l0_validation_data/idle_export_eu.SWE_APP_HK_20240510_092742.csv +49 -0
- imap_processing/tests/swe/l0_validation_data/idle_export_eu.SWE_CEM_RAW_20240510_092742.csv +593 -0
- imap_processing/tests/swe/test_swe_l1a.py +18 -0
- imap_processing/tests/swe/test_swe_l1a_cem_raw.py +52 -0
- imap_processing/tests/swe/test_swe_l1a_hk.py +68 -0
- imap_processing/tests/swe/test_swe_l1b_science.py +23 -4
- imap_processing/tests/swe/test_swe_l2.py +112 -30
- imap_processing/tests/test_cli.py +2 -2
- imap_processing/tests/test_utils.py +138 -16
- imap_processing/tests/ultra/data/l0/FM45_UltraFM45_Functional_2024-01-22T0105_20240122T010548.CCSDS +0 -0
- imap_processing/tests/ultra/data/l0/ultra45_raw_sc_ultraimgrates_20220530_00.csv +164 -0
- imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_ultrarawimg_withFSWcalcs_FM45_40P_Phi28p5_BeamCal_LinearScan_phi2850_theta-000_20240207T102740.csv +3243 -3243
- imap_processing/tests/ultra/data/mock_data.py +341 -0
- imap_processing/tests/ultra/unit/conftest.py +69 -26
- imap_processing/tests/ultra/unit/test_badtimes.py +2 -0
- imap_processing/tests/ultra/unit/test_cullingmask.py +4 -0
- imap_processing/tests/ultra/unit/test_de.py +12 -4
- imap_processing/tests/ultra/unit/test_decom_apid_881.py +44 -0
- imap_processing/tests/ultra/unit/test_spacecraft_pset.py +78 -0
- imap_processing/tests/ultra/unit/test_ultra_l1a.py +28 -12
- imap_processing/tests/ultra/unit/test_ultra_l1b.py +34 -6
- imap_processing/tests/ultra/unit/test_ultra_l1b_culling.py +22 -26
- imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +86 -51
- imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +94 -52
- imap_processing/ultra/l0/decom_tools.py +6 -5
- imap_processing/ultra/l1a/ultra_l1a.py +28 -56
- imap_processing/ultra/l1b/de.py +72 -28
- imap_processing/ultra/l1b/extendedspin.py +12 -14
- imap_processing/ultra/l1b/ultra_l1b.py +34 -9
- imap_processing/ultra/l1b/ultra_l1b_culling.py +65 -29
- imap_processing/ultra/l1b/ultra_l1b_extended.py +64 -19
- imap_processing/ultra/l1c/spacecraft_pset.py +86 -0
- imap_processing/ultra/l1c/ultra_l1c.py +7 -4
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +112 -61
- imap_processing/ultra/lookup_tables/ultra_90_dps_exposure_compressed.cdf +0 -0
- imap_processing/ultra/utils/ultra_l1_utils.py +20 -2
- imap_processing/utils.py +68 -28
- {imap_processing-0.11.0.dist-info → imap_processing-0.12.0.dist-info}/METADATA +8 -5
- {imap_processing-0.11.0.dist-info → imap_processing-0.12.0.dist-info}/RECORD +250 -199
- imap_processing/cdf/config/imap_mag_l1_variable_attrs.yaml +0 -237
- imap_processing/hi/l1a/housekeeping.py +0 -27
- imap_processing/tests/codice/data/imap_codice_l1a_hi-counters-aggregated_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_hi-counters-singles_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_hi-omni_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_hi-sectored_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_hskp_20100101_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-counters-aggregated_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-counters-singles_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-angular_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-priority_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-species_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-angular_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-priority_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-species_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_hi-counters-aggregated_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_hi-counters-singles_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_hi-omni_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_hi-sectored_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_hskp_20100101_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-counters-aggregated_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-counters-singles_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-angular_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-priority_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-species_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-angular_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-priority_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-species_20240429_v001.cdf +0 -0
- imap_processing/tests/hi/data/l1/imap_hi_l1b_45sensor-de_20250415_v999.cdf +0 -0
- imap_processing/tests/hit/PREFLIGHT_raw_record_2023_256_15_59_04_apid1251.pkts +0 -0
- imap_processing/tests/hit/PREFLIGHT_raw_record_2023_256_15_59_04_apid1252.pkts +0 -0
- imap_processing/tests/hit/validation_data/hskp_sample_eu.csv +0 -89
- imap_processing/tests/hit/validation_data/sci_sample_raw1.csv +0 -29
- imap_processing/tests/idex/test_data/imap_idex_l0_raw_20231214_v001.pkts +0 -0
- imap_processing/tests/lo/test_cdfs/imap_lo_l1a_de_20100101_v001.cdf +0 -0
- imap_processing/tests/lo/test_cdfs/imap_lo_l1a_spin_20100101_v001.cdf +0 -0
- imap_processing/tests/ultra/test_data/mock_data.py +0 -161
- imap_processing/ultra/l1c/pset.py +0 -40
- /imap_processing/tests/ultra/{test_data → data}/l0/FM45_40P_Phi28p5_BeamCal_LinearScan_phi28.50_theta-0.00_20240207T102740.CCSDS +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l0/FM45_7P_Phi0.0_BeamCal_LinearScan_phi0.04_theta-0.01_20230821T121304.CCSDS +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l0/FM45_TV_Cycle6_Hot_Ops_Front212_20240124T063837.CCSDS +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l0/Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.CCSDS +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_auxdata_Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.csv +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_enaphxtofhangimg_FM45_TV_Cycle6_Hot_Ops_Front212_20240124T063837.csv +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_ultraimgrates_Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.csv +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_ultrarawimgevent_FM45_7P_Phi00_BeamCal_LinearScan_phi004_theta-001_20230821T121304.csv +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l1/dps_exposure_helio_45_E1.cdf +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l1/dps_exposure_helio_45_E12.cdf +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l1/dps_exposure_helio_45_E24.cdf +0 -0
- {imap_processing-0.11.0.dist-info → imap_processing-0.12.0.dist-info}/LICENSE +0 -0
- {imap_processing-0.11.0.dist-info → imap_processing-0.12.0.dist-info}/WHEEL +0 -0
- {imap_processing-0.11.0.dist-info → imap_processing-0.12.0.dist-info}/entry_points.txt +0 -0
|
@@ -2,47 +2,50 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from copy import deepcopy
|
|
5
6
|
from unittest import mock
|
|
6
7
|
|
|
8
|
+
import astropy_healpix.healpy as hp
|
|
7
9
|
import numpy as np
|
|
8
10
|
import pytest
|
|
9
11
|
import xarray as xr
|
|
10
12
|
|
|
11
13
|
from imap_processing.ena_maps import ena_maps
|
|
14
|
+
from imap_processing.ena_maps.utils import spatial_utils
|
|
15
|
+
from imap_processing.ena_maps.utils.coordinates import CoordNames
|
|
12
16
|
from imap_processing.spice import geometry
|
|
13
|
-
from imap_processing.tests.ultra.test_data.mock_data import mock_l1c_pset_product
|
|
14
17
|
|
|
15
18
|
|
|
16
|
-
@pytest.fixture()
|
|
17
|
-
def
|
|
18
|
-
"""
|
|
19
|
-
|
|
19
|
+
@pytest.fixture(autouse=True, scope="module")
|
|
20
|
+
def setup_all_pset_products(ultra_l1c_pset_datasets, rectangular_l1c_pset_datasets):
|
|
21
|
+
"""
|
|
22
|
+
Setup fixture data once for all tests.
|
|
23
|
+
|
|
24
|
+
This is relatively computationally intensive for the high resolution PSETs,
|
|
25
|
+
so we use a module-level fixture to avoid repeating the setup code. However,
|
|
26
|
+
some tests need to modify the PSETs, so we use a function-level fixture to
|
|
27
|
+
make a deepcopy of the PSETs for each test function.
|
|
28
|
+
"""
|
|
29
|
+
hp_ultra_nside = ultra_l1c_pset_datasets["nside"]
|
|
30
|
+
hp_ultra_l1c_pset_products = ultra_l1c_pset_datasets["products"]
|
|
31
|
+
rect_spacing = rectangular_l1c_pset_datasets["spacing"]
|
|
32
|
+
rect_rectangular_l1c_pset_products = rectangular_l1c_pset_datasets["products"]
|
|
20
33
|
return {
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
stripe_center_lon=mid_longitude,
|
|
26
|
-
timestr=f"2025-09-{i + 1:02d}T12:00:00",
|
|
27
|
-
head=("45" if (i % 2 == 0) else "90"),
|
|
28
|
-
)
|
|
29
|
-
for i, mid_longitude in enumerate(
|
|
30
|
-
np.arange(
|
|
31
|
-
0,
|
|
32
|
-
360,
|
|
33
|
-
45,
|
|
34
|
-
)
|
|
35
|
-
)
|
|
36
|
-
],
|
|
34
|
+
"hp_ultra_nside": hp_ultra_nside,
|
|
35
|
+
"hp_ultra_l1c_pset_products": hp_ultra_l1c_pset_products,
|
|
36
|
+
"rect_spacing": rect_spacing,
|
|
37
|
+
"rect_rectangular_l1c_pset_products": rect_rectangular_l1c_pset_products,
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
|
|
40
41
|
class TestUltraPointingSet:
|
|
41
42
|
@pytest.fixture(autouse=True)
|
|
42
|
-
def _setup_ultra_l1c_pset_products(self,
|
|
43
|
+
def _setup_ultra_l1c_pset_products(self, setup_all_pset_products):
|
|
43
44
|
"""Setup fixture data as class attributes"""
|
|
44
|
-
self.
|
|
45
|
-
self.l1c_pset_products =
|
|
45
|
+
self.nside = setup_all_pset_products["hp_ultra_nside"]
|
|
46
|
+
self.l1c_pset_products = deepcopy(
|
|
47
|
+
setup_all_pset_products["hp_ultra_l1c_pset_products"]
|
|
48
|
+
)
|
|
46
49
|
|
|
47
50
|
@pytest.mark.usefixtures("_setup_ultra_l1c_pset_products")
|
|
48
51
|
def test_instantiate(self):
|
|
@@ -56,71 +59,81 @@ class TestUltraPointingSet:
|
|
|
56
59
|
]
|
|
57
60
|
|
|
58
61
|
for ultra_pset in ultra_psets:
|
|
59
|
-
# Check tiling is
|
|
60
|
-
assert ultra_pset.tiling_type
|
|
62
|
+
# Check tiling is HEALPix
|
|
63
|
+
assert ultra_pset.tiling_type is ena_maps.SkyTilingType.HEALPIX
|
|
61
64
|
|
|
62
65
|
# Check that the reference frame is correctly set
|
|
63
|
-
assert ultra_pset.spice_reference_frame
|
|
66
|
+
assert ultra_pset.spice_reference_frame is geometry.SpiceFrame.IMAP_DPS
|
|
64
67
|
|
|
65
68
|
# Check the number of points is (360/0.5) * (180/0.5)
|
|
66
69
|
np.testing.assert_equal(
|
|
67
70
|
ultra_pset.num_points,
|
|
68
|
-
|
|
71
|
+
hp.nside2npix(self.nside),
|
|
69
72
|
)
|
|
70
73
|
|
|
71
74
|
# Check the repr exists
|
|
72
75
|
assert "UltraPointingSet" in repr(ultra_pset)
|
|
73
76
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
uneven_az_dataset["epoch"] = 1
|
|
81
|
-
uneven_az_dataset["azimuth_bin_center"] = np.array([0, 5, 15, 20, 30])
|
|
82
|
-
uneven_az_dataset["elevation_bin_center"] = np.arange(5)
|
|
83
|
-
|
|
84
|
-
with pytest.raises(ValueError, match="Azimuth bin spacing is not uniform"):
|
|
85
|
-
ena_maps.UltraPointingSet(
|
|
86
|
-
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
87
|
-
l1c_dataset=uneven_az_dataset,
|
|
77
|
+
# Checks for the property methods:
|
|
78
|
+
# Check that the unwrapped_dims_dict is as expected
|
|
79
|
+
assert ultra_pset.unwrapped_dims_dict["counts"] == (
|
|
80
|
+
"epoch",
|
|
81
|
+
"energy",
|
|
82
|
+
"pixel",
|
|
88
83
|
)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
with pytest.raises(ValueError, match="Elevation bin spacing is not uniform"):
|
|
94
|
-
ena_maps.UltraPointingSet(
|
|
95
|
-
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
96
|
-
l1c_dataset=uneven_az_dataset,
|
|
84
|
+
# Check the non_spatial_coords are as expected
|
|
85
|
+
assert tuple(ultra_pset.non_spatial_coords.keys()) == (
|
|
86
|
+
"epoch",
|
|
87
|
+
"energy",
|
|
97
88
|
)
|
|
98
89
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
90
|
+
@pytest.mark.usefixtures("_setup_ultra_l1c_pset_products")
|
|
91
|
+
@pytest.mark.usefixtures("_setup_ultra_l1c_pset_products")
|
|
92
|
+
def test_different_spacing_raises_error(self):
|
|
93
|
+
"""Test that different spaced az/el from the L1C dataset raises ValueError"""
|
|
102
94
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
95
|
+
ultra_pset_ds = self.l1c_pset_products[0]
|
|
96
|
+
# Modify the dataset to have different spacing
|
|
97
|
+
ultra_pset_ds[CoordNames.ELEVATION_L1C.value].values = np.arange(
|
|
98
|
+
ultra_pset_ds[CoordNames.ELEVATION_L1C.value].size
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
with pytest.raises(ValueError, match="do not match"):
|
|
106
102
|
ena_maps.UltraPointingSet(
|
|
107
103
|
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
108
|
-
l1c_dataset=
|
|
104
|
+
l1c_dataset=ultra_pset_ds,
|
|
109
105
|
)
|
|
110
106
|
|
|
111
107
|
|
|
112
108
|
class TestRectangularSkyMap:
|
|
113
109
|
@pytest.fixture(autouse=True)
|
|
114
|
-
def _setup_ultra_l1c_pset_products(self,
|
|
110
|
+
def _setup_ultra_l1c_pset_products(self, setup_all_pset_products):
|
|
115
111
|
"""Setup fixture data as class attributes"""
|
|
116
|
-
self.
|
|
117
|
-
self.
|
|
112
|
+
self.ultra_l1c_nside = setup_all_pset_products["hp_ultra_nside"]
|
|
113
|
+
self.ultra_l1c_pset_products = deepcopy(
|
|
114
|
+
setup_all_pset_products["hp_ultra_l1c_pset_products"]
|
|
115
|
+
)
|
|
118
116
|
self.ultra_psets = [
|
|
119
117
|
ena_maps.UltraPointingSet(
|
|
120
118
|
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
121
119
|
l1c_dataset=l1c_product,
|
|
122
120
|
)
|
|
123
|
-
for l1c_product in self.
|
|
121
|
+
for l1c_product in self.ultra_l1c_pset_products
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
@pytest.fixture(autouse=True)
|
|
125
|
+
def _setup_rectangular_l1c_pset_products(self, setup_all_pset_products):
|
|
126
|
+
"""Setup fixture data as class attributes"""
|
|
127
|
+
self.rectangular_l1c_spacing_deg = setup_all_pset_products["rect_spacing"]
|
|
128
|
+
self.rectangular_l1c_pset_products = deepcopy(
|
|
129
|
+
setup_all_pset_products["rect_rectangular_l1c_pset_products"]
|
|
130
|
+
)
|
|
131
|
+
self.rectangular_psets = [
|
|
132
|
+
ena_maps.RectangularPointingSet(
|
|
133
|
+
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
134
|
+
l1c_dataset=l1c_product,
|
|
135
|
+
)
|
|
136
|
+
for l1c_product in self.rectangular_l1c_pset_products
|
|
124
137
|
]
|
|
125
138
|
|
|
126
139
|
def test_instantiate(self):
|
|
@@ -130,8 +143,9 @@ class TestRectangularSkyMap:
|
|
|
130
143
|
spice_frame=geometry.SpiceFrame.ECLIPJ2000,
|
|
131
144
|
)
|
|
132
145
|
|
|
133
|
-
# Check that the map is empty
|
|
134
|
-
assert rm.
|
|
146
|
+
# Check that the map data is an empty xarray Dataset
|
|
147
|
+
assert isinstance(rm.data_1d, xr.Dataset)
|
|
148
|
+
assert rm.data_1d.data_vars == {}
|
|
135
149
|
|
|
136
150
|
# Check that the reference frame is correctly set
|
|
137
151
|
assert rm.spice_reference_frame == geometry.SpiceFrame.ECLIPJ2000
|
|
@@ -142,11 +156,78 @@ class TestRectangularSkyMap:
|
|
|
142
156
|
# Check the repr exists
|
|
143
157
|
assert "RectangularSkyMap" in repr(rm)
|
|
144
158
|
|
|
159
|
+
np.testing.assert_array_equal(
|
|
160
|
+
rm.binning_grid_shape, (360 / rm.spacing_deg, 180 / rm.spacing_deg)
|
|
161
|
+
)
|
|
162
|
+
|
|
145
163
|
@pytest.mark.usefixtures("_setup_ultra_l1c_pset_products")
|
|
146
164
|
@mock.patch("imap_processing.spice.geometry.frame_transform_az_el")
|
|
147
|
-
def
|
|
165
|
+
def test_project_healpix_pset_values_to_map_push_method(
|
|
166
|
+
self, mock_frame_transform_az_el
|
|
167
|
+
):
|
|
168
|
+
"""
|
|
169
|
+
Test projection of Healpix tiled PSET values to RectMap w "push" index matching.
|
|
170
|
+
|
|
171
|
+
If frame_transform_az_el is mocked to return the az and el unchanged,
|
|
172
|
+
then the map should have the same total counts in each energy bin
|
|
173
|
+
as the PSETs, summed.
|
|
174
|
+
"""
|
|
175
|
+
index_matching_method = ena_maps.IndexMatchMethod.PUSH
|
|
176
|
+
|
|
177
|
+
# Mock frame_transform to return the az and el unchanged
|
|
178
|
+
mock_frame_transform_az_el.side_effect = (
|
|
179
|
+
lambda et, az_el, from_frame, to_frame, degrees: az_el
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
rectangular_map = ena_maps.RectangularSkyMap(
|
|
183
|
+
spacing_deg=2,
|
|
184
|
+
spice_frame=geometry.SpiceFrame.ECLIPJ2000,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# Project each PSET's values to the map (push method)
|
|
188
|
+
for ultra_pset in self.ultra_psets:
|
|
189
|
+
rectangular_map.project_pset_values_to_map(
|
|
190
|
+
ultra_pset,
|
|
191
|
+
value_keys=["counts", "exposure_time"],
|
|
192
|
+
index_match_method=index_matching_method,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# Check that the map has been updated
|
|
196
|
+
assert "counts" in rectangular_map.data_1d.data_vars
|
|
197
|
+
|
|
198
|
+
# Check that the map has the same values as the PSETs, summed
|
|
199
|
+
simple_summed_pset_counts_by_energy = np.zeros(
|
|
200
|
+
shape=(
|
|
201
|
+
self.ultra_l1c_pset_products[0]["counts"].sizes[
|
|
202
|
+
CoordNames.ENERGY.value
|
|
203
|
+
],
|
|
204
|
+
)
|
|
205
|
+
)
|
|
206
|
+
for pset in self.ultra_l1c_pset_products:
|
|
207
|
+
simple_summed_pset_counts_by_energy += pset["counts"].sum(
|
|
208
|
+
dim=[d for d in pset["counts"].dims if d != CoordNames.ENERGY.value]
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
rmap_counts_per_energy_bin = rectangular_map.data_1d["counts"].sum(
|
|
212
|
+
dim=[
|
|
213
|
+
d
|
|
214
|
+
for d in rectangular_map.data_1d["counts"].dims
|
|
215
|
+
if d != CoordNames.ENERGY.value
|
|
216
|
+
]
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
np.testing.assert_array_equal(
|
|
220
|
+
rmap_counts_per_energy_bin,
|
|
221
|
+
simple_summed_pset_counts_by_energy,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
@pytest.mark.usefixtures("_setup_rectangular_l1c_pset_products")
|
|
225
|
+
@mock.patch("imap_processing.spice.geometry.frame_transform_az_el")
|
|
226
|
+
def test_project_rect_pset_values_to_map_push_method(
|
|
227
|
+
self, mock_frame_transform_az_el
|
|
228
|
+
):
|
|
148
229
|
"""
|
|
149
|
-
Test projection of PSET values to Rect
|
|
230
|
+
Test projection of Rect PSET values to Rect Map w "push" index matching method.
|
|
150
231
|
|
|
151
232
|
If frame_transform_az_el is mocked to return the az and el unchanged, and the
|
|
152
233
|
map has the same spacing as the PSETs, then the map should have
|
|
@@ -154,7 +235,7 @@ class TestRectangularSkyMap:
|
|
|
154
235
|
"""
|
|
155
236
|
index_matching_method = ena_maps.IndexMatchMethod.PUSH
|
|
156
237
|
|
|
157
|
-
pset_spacing_deg = self.
|
|
238
|
+
pset_spacing_deg = self.rectangular_psets[0].spacing_deg
|
|
158
239
|
|
|
159
240
|
# Mock frame_transform to return the az and el unchanged
|
|
160
241
|
mock_frame_transform_az_el.side_effect = (
|
|
@@ -166,59 +247,420 @@ class TestRectangularSkyMap:
|
|
|
166
247
|
spice_frame=geometry.SpiceFrame.ECLIPJ2000,
|
|
167
248
|
)
|
|
168
249
|
|
|
169
|
-
# Project each PSET's values to the map
|
|
170
|
-
for
|
|
250
|
+
# Project each PSET's values to the map (push method)
|
|
251
|
+
for rectangular_pset in self.rectangular_psets:
|
|
171
252
|
rectangular_map.project_pset_values_to_map(
|
|
172
|
-
|
|
253
|
+
rectangular_pset,
|
|
173
254
|
value_keys=["counts", "exposure_time"],
|
|
174
255
|
index_match_method=index_matching_method,
|
|
175
256
|
)
|
|
176
257
|
|
|
177
258
|
# Check that the map has been updated
|
|
178
|
-
assert rectangular_map.
|
|
259
|
+
assert "counts" in rectangular_map.data_1d.data_vars
|
|
179
260
|
|
|
180
261
|
# Check that the map has the same values as the PSETs, summed
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
262
|
+
simple_summed_pset_counts_by_energy = np.zeros(
|
|
263
|
+
shape=(
|
|
264
|
+
self.rectangular_l1c_pset_products[0]["counts"].sizes[
|
|
265
|
+
CoordNames.ENERGY.value
|
|
266
|
+
],
|
|
267
|
+
)
|
|
268
|
+
)
|
|
269
|
+
for pset in self.rectangular_l1c_pset_products:
|
|
270
|
+
simple_summed_pset_counts_by_energy += pset["counts"].sum(
|
|
271
|
+
dim=[d for d in pset["counts"].dims if d != CoordNames.ENERGY.value]
|
|
272
|
+
)
|
|
184
273
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
274
|
+
rmap_counts_per_energy_bin = rectangular_map.data_1d["counts"].sum(
|
|
275
|
+
dim=[
|
|
276
|
+
d
|
|
277
|
+
for d in rectangular_map.data_1d["counts"].dims
|
|
278
|
+
if d != CoordNames.ENERGY.value
|
|
279
|
+
]
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
np.testing.assert_array_equal(
|
|
283
|
+
rmap_counts_per_energy_bin,
|
|
284
|
+
simple_summed_pset_counts_by_energy,
|
|
188
285
|
)
|
|
189
286
|
|
|
190
287
|
@pytest.mark.usefixtures("_setup_ultra_l1c_pset_products")
|
|
288
|
+
def test_project_pset_values_to_map_errors(self):
|
|
289
|
+
index_matching_method = ena_maps.IndexMatchMethod.PUSH
|
|
290
|
+
rectangular_map = ena_maps.RectangularSkyMap(
|
|
291
|
+
spacing_deg=1,
|
|
292
|
+
spice_frame=geometry.SpiceFrame.ECLIPJ2000,
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
# An error should be raised if a key is not found in the PSET
|
|
296
|
+
with pytest.raises(ValueError, match="Value key invalid not found"):
|
|
297
|
+
rectangular_map.project_pset_values_to_map(
|
|
298
|
+
self.ultra_psets[0],
|
|
299
|
+
value_keys=["invalid"],
|
|
300
|
+
index_match_method=index_matching_method,
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
@pytest.mark.usefixtures("_setup_rectangular_l1c_pset_products")
|
|
191
304
|
@mock.patch("imap_processing.spice.geometry.frame_transform_az_el")
|
|
192
|
-
def
|
|
193
|
-
|
|
305
|
+
def test_project_rect_pset_values_to_map_pull_method(
|
|
306
|
+
self, mock_frame_transform_az_el
|
|
307
|
+
):
|
|
308
|
+
"""
|
|
309
|
+
Test projection Rect PSET to Rect. Map with "pull" index matching method.
|
|
310
|
+
|
|
311
|
+
NOTE: Pull index matching is only expected to be done with Rectangularly tiled
|
|
312
|
+
PointingSet objects.
|
|
313
|
+
"""
|
|
194
314
|
|
|
195
315
|
index_matching_method = ena_maps.IndexMatchMethod.PULL
|
|
316
|
+
skymap_spacing = 10
|
|
317
|
+
|
|
318
|
+
# Mock frame_transform to return the az and el unchanged
|
|
319
|
+
mock_frame_transform_az_el.side_effect = (
|
|
320
|
+
lambda et, az_el, from_frame, to_frame, degrees: az_el
|
|
321
|
+
)
|
|
196
322
|
rectangular_map = ena_maps.RectangularSkyMap(
|
|
197
|
-
spacing_deg=
|
|
323
|
+
spacing_deg=skymap_spacing,
|
|
198
324
|
spice_frame=geometry.SpiceFrame.ECLIPJ2000,
|
|
199
325
|
)
|
|
200
326
|
|
|
201
|
-
|
|
327
|
+
# Each map pixel will add the value of a single PSET pixel, so we'll start at 0
|
|
328
|
+
# and add 0, 1, 2, 3, ... to the map
|
|
329
|
+
expected_value_every_pixel = 0
|
|
330
|
+
|
|
331
|
+
# Another way to test this is that (if the PSET pixels are
|
|
332
|
+
# smaller than the SkyMap pixels) the sum of the counts in all PSETs should
|
|
333
|
+
# be (PSET_spacing / SkyMap_spacing)^2 times the sum of the counts in the SkyMap
|
|
334
|
+
total_pset_counts = np.zeros_like(
|
|
335
|
+
self.rectangular_l1c_pset_products[0]["counts"].values
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
# Project each PSET's values to the map (pull method)
|
|
339
|
+
for pset_num, rectangular_pset in enumerate(self.rectangular_psets):
|
|
340
|
+
# Set the counts to be 0 in the first PSET, 1 in the second, etc.
|
|
341
|
+
rectangular_pset.data["counts"].values = np.full_like(
|
|
342
|
+
rectangular_pset.data["counts"].values, pset_num
|
|
343
|
+
)
|
|
344
|
+
|
|
202
345
|
rectangular_map.project_pset_values_to_map(
|
|
203
|
-
|
|
346
|
+
rectangular_pset,
|
|
204
347
|
value_keys=["counts", "exposure_time"],
|
|
205
348
|
index_match_method=index_matching_method,
|
|
206
349
|
)
|
|
350
|
+
expected_value_every_pixel += pset_num
|
|
351
|
+
|
|
352
|
+
total_pset_counts += rectangular_pset.data["counts"].values
|
|
353
|
+
|
|
354
|
+
# Check that the map has been updated
|
|
355
|
+
assert "counts" in rectangular_map.data_1d
|
|
356
|
+
|
|
357
|
+
np.testing.assert_allclose(
|
|
358
|
+
rectangular_map.data_1d["counts"],
|
|
359
|
+
expected_value_every_pixel,
|
|
360
|
+
)
|
|
361
|
+
downsample_ratio = skymap_spacing / self.rectangular_l1c_spacing_deg
|
|
362
|
+
np.testing.assert_allclose(
|
|
363
|
+
rectangular_map.data_1d["counts"].sum(),
|
|
364
|
+
total_pset_counts.sum() / (downsample_ratio**2),
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
# Convert to xarray Dataset and check the data is as expected
|
|
368
|
+
# This is a method, which could be tested separately, but that would be
|
|
369
|
+
# innefficient, as it would require all the same, computationally intensive
|
|
370
|
+
# operations to be repeated as this test
|
|
371
|
+
rect_map_ds = rectangular_map.to_dataset()
|
|
372
|
+
assert "counts" in rect_map_ds.data_vars
|
|
373
|
+
assert rect_map_ds["counts"].shape == (
|
|
374
|
+
1,
|
|
375
|
+
rectangular_pset.data["counts"].sizes[CoordNames.ENERGY.value],
|
|
376
|
+
360 / skymap_spacing,
|
|
377
|
+
180 / skymap_spacing,
|
|
378
|
+
)
|
|
379
|
+
assert rect_map_ds["counts"].dims == (
|
|
380
|
+
CoordNames.TIME.value,
|
|
381
|
+
CoordNames.ENERGY.value,
|
|
382
|
+
CoordNames.AZIMUTH_L2.value,
|
|
383
|
+
CoordNames.ELEVATION_L2.value,
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
# Check that the data is as expected
|
|
387
|
+
np.testing.assert_array_equal(
|
|
388
|
+
rect_map_ds["counts"].values,
|
|
389
|
+
spatial_utils.rewrap_even_spaced_az_el_grid(
|
|
390
|
+
rectangular_map.data_1d["counts"].values,
|
|
391
|
+
rectangular_map.binning_grid_shape,
|
|
392
|
+
),
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
class TestHealpixSkyMap:
|
|
397
|
+
@pytest.fixture(autouse=True)
|
|
398
|
+
def _setup_ultra_l1c_pset_products(self, setup_all_pset_products):
|
|
399
|
+
"""Setup fixture data as class attributes"""
|
|
400
|
+
self.ultra_l1c_nside = setup_all_pset_products["hp_ultra_nside"]
|
|
401
|
+
self.ultra_l1c_pset_products = deepcopy(
|
|
402
|
+
setup_all_pset_products["hp_ultra_l1c_pset_products"]
|
|
403
|
+
)
|
|
404
|
+
self.ultra_psets = [
|
|
405
|
+
ena_maps.UltraPointingSet(
|
|
406
|
+
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
407
|
+
l1c_dataset=l1c_product,
|
|
408
|
+
)
|
|
409
|
+
for l1c_product in self.ultra_l1c_pset_products
|
|
410
|
+
]
|
|
411
|
+
|
|
412
|
+
@pytest.fixture(autouse=True)
|
|
413
|
+
def _setup_rectangular_l1c_pset_products(self, setup_all_pset_products):
|
|
414
|
+
"""Setup fixture data as class attributes"""
|
|
415
|
+
self.rectangular_l1c_spacing_deg = setup_all_pset_products["rect_spacing"]
|
|
416
|
+
self.rectangular_l1c_pset_products = deepcopy(
|
|
417
|
+
setup_all_pset_products["rect_rectangular_l1c_pset_products"]
|
|
418
|
+
)
|
|
419
|
+
self.rectangular_psets = [
|
|
420
|
+
ena_maps.RectangularPointingSet(
|
|
421
|
+
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
422
|
+
l1c_dataset=l1c_product,
|
|
423
|
+
)
|
|
424
|
+
for l1c_product in self.rectangular_l1c_pset_products
|
|
425
|
+
]
|
|
426
|
+
|
|
427
|
+
@pytest.mark.parametrize(
|
|
428
|
+
"nside",
|
|
429
|
+
[8, 16, 32],
|
|
430
|
+
)
|
|
431
|
+
@pytest.mark.parametrize("nested", [True, False], ids=["nested", "ring"])
|
|
432
|
+
def test_instantiate(self, nside, nested):
|
|
433
|
+
"""Test instantiation of HealpixSkyMap"""
|
|
434
|
+
hp_map = ena_maps.HealpixSkyMap(
|
|
435
|
+
nside=nside,
|
|
436
|
+
spice_frame=geometry.SpiceFrame.ECLIPJ2000,
|
|
437
|
+
nested=nested,
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
# Check that the map data is an empty xarray Dataset
|
|
441
|
+
assert isinstance(hp_map.data_1d, xr.Dataset)
|
|
442
|
+
assert hp_map.data_1d.data_vars == {}
|
|
443
|
+
|
|
444
|
+
# Check that the reference frame is correctly set
|
|
445
|
+
assert hp_map.spice_reference_frame is geometry.SpiceFrame.ECLIPJ2000
|
|
446
|
+
# Check that the nside and nested properties are set correctly
|
|
447
|
+
np.testing.assert_equal(hp_map.nside, nside)
|
|
448
|
+
np.testing.assert_equal(hp_map.nested, nested)
|
|
449
|
+
# Check the number of points is 12 * nside^2
|
|
450
|
+
np.testing.assert_equal(hp_map.num_points, 12 * nside**2)
|
|
451
|
+
# There will be az, el values for each pixel
|
|
452
|
+
assert hp_map.az_el_points.shape == (hp_map.num_points, 2)
|
|
453
|
+
# The az must be in the range [0, 360) degrees
|
|
454
|
+
# and el in the range [-90, 90)
|
|
455
|
+
assert np.all(hp_map.az_el_points[:, 0] >= 0)
|
|
456
|
+
assert np.all(hp_map.az_el_points[:, 0] < 360)
|
|
457
|
+
assert np.all(hp_map.az_el_points[:, 1] >= -90)
|
|
458
|
+
assert np.all(hp_map.az_el_points[:, 1] < 90)
|
|
459
|
+
|
|
460
|
+
# Check that the binning grid shape is just a tuple of num_points
|
|
461
|
+
np.testing.assert_equal(hp_map.binning_grid_shape, (hp_map.num_points,))
|
|
462
|
+
|
|
463
|
+
@pytest.mark.usefixtures("_setup_ultra_l1c_pset_products")
|
|
464
|
+
@pytest.mark.parametrize(
|
|
465
|
+
"nside,degree_tolerance",
|
|
466
|
+
[
|
|
467
|
+
(8, 6),
|
|
468
|
+
(16, 3),
|
|
469
|
+
(32, 2),
|
|
470
|
+
],
|
|
471
|
+
)
|
|
472
|
+
@pytest.mark.parametrize("nested", [True, False], ids=["nested", "ring"])
|
|
473
|
+
@mock.patch("imap_processing.spice.geometry.frame_transform_az_el")
|
|
474
|
+
def test_project_healpix_pset_values_to_map_push_method(
|
|
475
|
+
self, mock_frame_transform_az_el, nside, degree_tolerance, nested
|
|
476
|
+
):
|
|
477
|
+
"""
|
|
478
|
+
Test that PointingSet which contains bright spot pushes to correct spot in map.
|
|
479
|
+
|
|
480
|
+
Parameterized over nside (of the map, not the PSET), nested.
|
|
481
|
+
The tolerance for lower nsides must be higher because the
|
|
482
|
+
Healpix pixels are larger.
|
|
483
|
+
"""
|
|
484
|
+
|
|
485
|
+
# Mock frame_transform to return the az and el unchanged
|
|
486
|
+
mock_frame_transform_az_el.side_effect = (
|
|
487
|
+
lambda et, az_el, from_frame, to_frame, degrees: az_el
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
index_matching_method = ena_maps.IndexMatchMethod.PUSH
|
|
491
|
+
|
|
492
|
+
# Create a PointingSet with a bright spot
|
|
493
|
+
mock_pset_input_frame = ena_maps.UltraPointingSet(
|
|
494
|
+
l1c_dataset=self.ultra_l1c_pset_products[0],
|
|
495
|
+
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
496
|
+
)
|
|
497
|
+
mock_pset_input_frame.data["counts"].values = np.zeros_like(
|
|
498
|
+
mock_pset_input_frame.data["counts"].values
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
input_bright_pixel_number = hp.ang2pix(
|
|
502
|
+
nside=mock_pset_input_frame.nside,
|
|
503
|
+
theta=180,
|
|
504
|
+
phi=0,
|
|
505
|
+
nest=mock_pset_input_frame.nested,
|
|
506
|
+
lonlat=True,
|
|
507
|
+
)
|
|
508
|
+
input_bright_pixel_az_el_deg = mock_pset_input_frame.az_el_points[
|
|
509
|
+
input_bright_pixel_number
|
|
510
|
+
]
|
|
511
|
+
mock_pset_input_frame.data["counts"].values[
|
|
512
|
+
:,
|
|
513
|
+
:,
|
|
514
|
+
input_bright_pixel_number,
|
|
515
|
+
] = 1
|
|
516
|
+
|
|
517
|
+
# Create a Healpix map
|
|
518
|
+
hp_map = ena_maps.HealpixSkyMap(
|
|
519
|
+
nside=nside,
|
|
520
|
+
spice_frame=geometry.SpiceFrame.ECLIPJ2000,
|
|
521
|
+
nested=nested,
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
# Project the PointingSet to the Healpix map
|
|
525
|
+
hp_map.project_pset_values_to_map(
|
|
526
|
+
mock_pset_input_frame,
|
|
527
|
+
value_keys=[
|
|
528
|
+
"counts",
|
|
529
|
+
],
|
|
530
|
+
index_match_method=index_matching_method,
|
|
531
|
+
)
|
|
532
|
+
|
|
533
|
+
# Check that the map has been updated
|
|
534
|
+
assert "counts" in hp_map.data_1d.data_vars
|
|
535
|
+
|
|
536
|
+
# Find the maximum value in the spatial pixel dimension of the healpix map
|
|
537
|
+
bright_hp_pixel_index = hp_map.data_1d["counts"][0, :].argmax()
|
|
538
|
+
bright_hp_pixel_az_el = hp_map.az_el_points[bright_hp_pixel_index]
|
|
539
|
+
|
|
540
|
+
np.testing.assert_allclose(
|
|
541
|
+
bright_hp_pixel_az_el,
|
|
542
|
+
input_bright_pixel_az_el_deg,
|
|
543
|
+
atol=degree_tolerance,
|
|
544
|
+
)
|
|
545
|
+
|
|
546
|
+
@pytest.mark.usefixtures("_setup_rectangular_l1c_pset_products")
|
|
547
|
+
@pytest.mark.parametrize(
|
|
548
|
+
"nside,degree_tolerance",
|
|
549
|
+
[
|
|
550
|
+
(8, 6),
|
|
551
|
+
(16, 3),
|
|
552
|
+
(32, 2),
|
|
553
|
+
],
|
|
554
|
+
)
|
|
555
|
+
@pytest.mark.parametrize("nested", [True, False], ids=["nested", "ring"])
|
|
556
|
+
@mock.patch("imap_processing.spice.geometry.frame_transform_az_el")
|
|
557
|
+
def test_project_rect_pset_values_to_map_push_method(
|
|
558
|
+
self, mock_frame_transform_az_el, nside, degree_tolerance, nested
|
|
559
|
+
):
|
|
560
|
+
"""
|
|
561
|
+
Test that PointingSet which contains bright spot pushes to correct spot in map.
|
|
562
|
+
|
|
563
|
+
Parameterized over nside, nested. The tolerance for lower nsides must be higher
|
|
564
|
+
because the Healpix pixels are larger.
|
|
565
|
+
"""
|
|
566
|
+
|
|
567
|
+
# Mock frame_transform to return the az and el unchanged
|
|
568
|
+
mock_frame_transform_az_el.side_effect = (
|
|
569
|
+
lambda et, az_el, from_frame, to_frame, degrees: az_el
|
|
570
|
+
)
|
|
571
|
+
|
|
572
|
+
index_matching_method = ena_maps.IndexMatchMethod.PUSH
|
|
573
|
+
|
|
574
|
+
# Create a PointingSet with a bright spot
|
|
575
|
+
mock_pset_input_frame = ena_maps.RectangularPointingSet(
|
|
576
|
+
l1c_dataset=self.rectangular_l1c_pset_products[0],
|
|
577
|
+
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
578
|
+
)
|
|
579
|
+
mock_pset_input_frame.data["counts"].values = np.zeros_like(
|
|
580
|
+
mock_pset_input_frame.data["counts"].values
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
input_bright_pixel_az_el_deg = (110, 55)
|
|
584
|
+
mock_pset_input_frame.data["counts"].values[
|
|
585
|
+
:,
|
|
586
|
+
:,
|
|
587
|
+
int(input_bright_pixel_az_el_deg[0] // mock_pset_input_frame.spacing_deg),
|
|
588
|
+
int(
|
|
589
|
+
(90 + input_bright_pixel_az_el_deg[1])
|
|
590
|
+
// mock_pset_input_frame.spacing_deg
|
|
591
|
+
),
|
|
592
|
+
] = 1
|
|
593
|
+
|
|
594
|
+
# Create a Healpix map
|
|
595
|
+
hp_map = ena_maps.HealpixSkyMap(
|
|
596
|
+
nside=nside,
|
|
597
|
+
spice_frame=geometry.SpiceFrame.ECLIPJ2000,
|
|
598
|
+
nested=nested,
|
|
599
|
+
)
|
|
600
|
+
|
|
601
|
+
# Project the PointingSet to the Healpix map
|
|
602
|
+
hp_map.project_pset_values_to_map(
|
|
603
|
+
mock_pset_input_frame,
|
|
604
|
+
value_keys=[
|
|
605
|
+
"counts",
|
|
606
|
+
],
|
|
607
|
+
index_match_method=index_matching_method,
|
|
608
|
+
)
|
|
609
|
+
|
|
610
|
+
# Check that the map has been updated
|
|
611
|
+
assert "counts" in hp_map.data_1d.data_vars
|
|
612
|
+
|
|
613
|
+
# Find the maximum value in the spatial pixel dimension of the healpix map
|
|
614
|
+
bright_hp_pixel_index = hp_map.data_1d["counts"][0, 0].argmax(dim="pixel")
|
|
615
|
+
bright_hp_pixel_az_el = hp_map.az_el_points[bright_hp_pixel_index]
|
|
616
|
+
|
|
617
|
+
np.testing.assert_allclose(
|
|
618
|
+
bright_hp_pixel_az_el,
|
|
619
|
+
input_bright_pixel_az_el_deg,
|
|
620
|
+
atol=degree_tolerance,
|
|
621
|
+
)
|
|
622
|
+
|
|
623
|
+
# Convert to xarray Dataset and check the data is as expected
|
|
624
|
+
hp_map_ds = hp_map.to_dataset()
|
|
625
|
+
assert "counts" in hp_map_ds.data_vars
|
|
626
|
+
assert hp_map_ds["counts"].shape == (
|
|
627
|
+
1,
|
|
628
|
+
mock_pset_input_frame.data["counts"].sizes[CoordNames.ENERGY.value],
|
|
629
|
+
hp_map.num_points,
|
|
630
|
+
)
|
|
631
|
+
assert hp_map_ds["counts"].dims == (
|
|
632
|
+
CoordNames.TIME.value,
|
|
633
|
+
CoordNames.ENERGY.value,
|
|
634
|
+
CoordNames.HEALPIX_INDEX.value,
|
|
635
|
+
)
|
|
636
|
+
np.testing.assert_array_equal(
|
|
637
|
+
hp_map_ds["counts"].values,
|
|
638
|
+
hp_map.data_1d["counts"].values,
|
|
639
|
+
)
|
|
207
640
|
|
|
208
641
|
|
|
209
642
|
class TestIndexMatching:
|
|
210
643
|
@pytest.fixture(autouse=True)
|
|
211
|
-
def
|
|
644
|
+
def _setup_rectangular_l1c_pset_products(self, setup_all_pset_products):
|
|
212
645
|
"""Setup fixture data as class attributes"""
|
|
213
|
-
self.
|
|
214
|
-
self.
|
|
646
|
+
self.rectangular_l1c_spacing_deg = setup_all_pset_products["rect_spacing"]
|
|
647
|
+
self.rectangular_l1c_pset_products = deepcopy(
|
|
648
|
+
setup_all_pset_products["rect_rectangular_l1c_pset_products"]
|
|
649
|
+
)
|
|
650
|
+
self.rectangular_psets = [
|
|
651
|
+
ena_maps.RectangularPointingSet(
|
|
652
|
+
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
653
|
+
l1c_dataset=l1c_product,
|
|
654
|
+
)
|
|
655
|
+
for l1c_product in self.rectangular_l1c_pset_products
|
|
656
|
+
]
|
|
215
657
|
|
|
216
658
|
@pytest.mark.parametrize(
|
|
217
659
|
"map_spacing_deg",
|
|
218
660
|
[0.5, 1, 10],
|
|
219
661
|
)
|
|
220
662
|
@mock.patch("imap_processing.spice.geometry.frame_transform_az_el")
|
|
221
|
-
def
|
|
663
|
+
def test_match_coords_to_indices_rect_pset_to_rect_map(
|
|
222
664
|
self, mock_frame_transform_az_el, map_spacing_deg
|
|
223
665
|
):
|
|
224
666
|
# Mock frame_transform to return the az and el unchanged
|
|
@@ -227,8 +669,8 @@ class TestIndexMatching:
|
|
|
227
669
|
)
|
|
228
670
|
|
|
229
671
|
# Mock a PSET, overriding the az/el points
|
|
230
|
-
mock_pset_input_frame = ena_maps.
|
|
231
|
-
l1c_dataset=self.
|
|
672
|
+
mock_pset_input_frame = ena_maps.RectangularPointingSet(
|
|
673
|
+
l1c_dataset=self.rectangular_l1c_pset_products[0],
|
|
232
674
|
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
233
675
|
)
|
|
234
676
|
manual_az_el_coords = np.array(
|
|
@@ -246,7 +688,7 @@ class TestIndexMatching:
|
|
|
246
688
|
[359.999999, 89.99999],
|
|
247
689
|
]
|
|
248
690
|
)
|
|
249
|
-
mock_pset_input_frame.az_el_points =
|
|
691
|
+
mock_pset_input_frame.az_el_points = manual_az_el_coords
|
|
250
692
|
|
|
251
693
|
# Manually calculate the resulting 1D pixel indices for each az/el pair
|
|
252
694
|
# (num of pixels in an az row spanning 180 deg of elevation) * (current az row)
|
|
@@ -259,15 +701,15 @@ class TestIndexMatching:
|
|
|
259
701
|
]
|
|
260
702
|
)
|
|
261
703
|
|
|
262
|
-
#
|
|
263
|
-
|
|
704
|
+
# Create the rectangular map and check the output values
|
|
705
|
+
rect_map = ena_maps.RectangularSkyMap(
|
|
264
706
|
spacing_deg=map_spacing_deg,
|
|
265
707
|
spice_frame=geometry.SpiceFrame.ECLIPJ2000,
|
|
266
708
|
)
|
|
267
709
|
flat_indices_input_grid_output_frame = ena_maps.match_coords_to_indices(
|
|
268
|
-
mock_pset_input_frame,
|
|
710
|
+
mock_pset_input_frame, rect_map
|
|
269
711
|
)
|
|
270
|
-
assert
|
|
712
|
+
assert rect_map.num_points == 360 * 180 / map_spacing_deg**2
|
|
271
713
|
assert len(flat_indices_input_grid_output_frame) == len(manual_az_el_coords)
|
|
272
714
|
np.testing.assert_equal(
|
|
273
715
|
flat_indices_input_grid_output_frame, expected_output_pixel
|
|
@@ -275,35 +717,122 @@ class TestIndexMatching:
|
|
|
275
717
|
|
|
276
718
|
# Check that the map's az/el points at the matched indices
|
|
277
719
|
# are the same as the input az/el points to within the spacing of the map
|
|
278
|
-
matched_map_az_el =
|
|
279
|
-
flat_indices_input_grid_output_frame
|
|
280
|
-
]
|
|
720
|
+
matched_map_az_el = rect_map.az_el_points[flat_indices_input_grid_output_frame]
|
|
281
721
|
np.testing.assert_allclose(
|
|
282
722
|
matched_map_az_el[:, 0],
|
|
283
723
|
mock_pset_input_frame.az_el_points[:, 0],
|
|
284
|
-
atol=
|
|
724
|
+
atol=map_spacing_deg,
|
|
725
|
+
)
|
|
726
|
+
|
|
727
|
+
@pytest.mark.parametrize(
|
|
728
|
+
"nside,degree_tolerance",
|
|
729
|
+
[
|
|
730
|
+
(8, 12),
|
|
731
|
+
(16, 6),
|
|
732
|
+
(32, 3),
|
|
733
|
+
],
|
|
734
|
+
ids=["nside8", "nside16", "nside32"],
|
|
735
|
+
)
|
|
736
|
+
@pytest.mark.parametrize("nested", [True, False], ids=["nested", "ring"])
|
|
737
|
+
@mock.patch("imap_processing.spice.geometry.frame_transform_az_el")
|
|
738
|
+
def test_match_coords_to_indices_rect_pset_to_healpix_map(
|
|
739
|
+
self, mock_frame_transform_az_el, nside, degree_tolerance, nested
|
|
740
|
+
):
|
|
741
|
+
# Mock frame_transform to return the az and el unchanged
|
|
742
|
+
mock_frame_transform_az_el.side_effect = (
|
|
743
|
+
lambda et, az_el, from_frame, to_frame, degrees: az_el
|
|
744
|
+
)
|
|
745
|
+
hp_map = ena_maps.HealpixSkyMap(
|
|
746
|
+
nside=nside, spice_frame=geometry.SpiceFrame.ECLIPJ2000, nested=nested
|
|
747
|
+
)
|
|
748
|
+
|
|
749
|
+
# Make a PointingSet
|
|
750
|
+
mock_pset_input_frame = ena_maps.RectangularPointingSet(
|
|
751
|
+
l1c_dataset=self.rectangular_l1c_pset_products[0],
|
|
752
|
+
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
285
753
|
)
|
|
286
754
|
|
|
287
|
-
|
|
755
|
+
# Match the PSET to the Healpix map
|
|
756
|
+
healpix_indices_of_rect_pixels = ena_maps.match_coords_to_indices(
|
|
757
|
+
mock_pset_input_frame, hp_map
|
|
758
|
+
)
|
|
759
|
+
|
|
760
|
+
# Check that the map's az/el points at the matched indices
|
|
761
|
+
# are the same as the input az/el points to within degree_tolerance,
|
|
762
|
+
# but we must ignore the polar regions and azimuthal wrap-around regions
|
|
763
|
+
rect_equatorial_elevations_mask = (
|
|
764
|
+
np.abs(mock_pset_input_frame.az_el_points[:, 1]) < 60
|
|
765
|
+
)
|
|
766
|
+
rect_az_non_wraparound_mask = (
|
|
767
|
+
mock_pset_input_frame.az_el_points[:, 0] < 340
|
|
768
|
+
) & (mock_pset_input_frame.az_el_points[:, 0] > 20)
|
|
769
|
+
rect_good_az_el_mask = (
|
|
770
|
+
rect_equatorial_elevations_mask & rect_az_non_wraparound_mask
|
|
771
|
+
)
|
|
772
|
+
matched_map_az_el = np.column_stack(
|
|
773
|
+
hp.pix2ang(
|
|
774
|
+
nside=nside,
|
|
775
|
+
ipix=healpix_indices_of_rect_pixels,
|
|
776
|
+
nest=nested,
|
|
777
|
+
lonlat=True,
|
|
778
|
+
)
|
|
779
|
+
)
|
|
780
|
+
np.testing.assert_allclose(
|
|
781
|
+
matched_map_az_el[rect_good_az_el_mask, 0],
|
|
782
|
+
mock_pset_input_frame.az_el_points[rect_good_az_el_mask, 0],
|
|
783
|
+
atol=degree_tolerance,
|
|
784
|
+
)
|
|
785
|
+
|
|
786
|
+
def test_match_coords_to_indices_pset_to_invalid_map(
|
|
288
787
|
self,
|
|
289
788
|
):
|
|
290
|
-
mock_pset_input_frame = ena_maps.
|
|
291
|
-
l1c_dataset=self.
|
|
789
|
+
mock_pset_input_frame = ena_maps.RectangularPointingSet(
|
|
790
|
+
l1c_dataset=self.rectangular_l1c_pset_products[0],
|
|
292
791
|
spice_reference_frame=geometry.SpiceFrame.ECLIPJ2000,
|
|
293
792
|
)
|
|
294
|
-
|
|
295
793
|
# Until implemented, just change the tiling on a RectangularSkyMap
|
|
296
|
-
|
|
794
|
+
mock_invalid_map = ena_maps.RectangularSkyMap(
|
|
297
795
|
spacing_deg=2,
|
|
298
796
|
spice_frame=geometry.SpiceFrame.ECLIPJ2000,
|
|
299
797
|
)
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
# Should raise NotImplementedError
|
|
303
|
-
with pytest.raises(NotImplementedError):
|
|
304
|
-
ena_maps.match_coords_to_indices(mock_pset_input_frame, mock_hp_map)
|
|
798
|
+
mock_invalid_map.tiling_type = "INVALID"
|
|
305
799
|
|
|
306
|
-
|
|
307
|
-
mock_other_map.tiling_type = "INVALID"
|
|
800
|
+
# Should raise ValueError if the tiling type is invalid
|
|
308
801
|
with pytest.raises(ValueError, match="Tiling type of the output frame"):
|
|
309
|
-
ena_maps.match_coords_to_indices(mock_pset_input_frame,
|
|
802
|
+
ena_maps.match_coords_to_indices(mock_pset_input_frame, mock_invalid_map)
|
|
803
|
+
|
|
804
|
+
def test_match_coords_to_indices_pset_to_pset_error(self):
|
|
805
|
+
mock_pset_input_frame = ena_maps.RectangularPointingSet(
|
|
806
|
+
l1c_dataset=self.rectangular_l1c_pset_products[0],
|
|
807
|
+
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
808
|
+
)
|
|
809
|
+
mock_pset_output_frame = ena_maps.RectangularPointingSet(
|
|
810
|
+
l1c_dataset=self.rectangular_l1c_pset_products[1],
|
|
811
|
+
spice_reference_frame=geometry.SpiceFrame.IMAP_DPS,
|
|
812
|
+
)
|
|
813
|
+
with pytest.raises(
|
|
814
|
+
ValueError, match="Cannot match indices between two PointingSet objects"
|
|
815
|
+
):
|
|
816
|
+
ena_maps.match_coords_to_indices(
|
|
817
|
+
mock_pset_input_frame, mock_pset_output_frame
|
|
818
|
+
)
|
|
819
|
+
|
|
820
|
+
def test_match_coords_to_indices_map_to_map_no_et_error(self):
|
|
821
|
+
mock_rect_map_1 = ena_maps.RectangularSkyMap(
|
|
822
|
+
spacing_deg=2,
|
|
823
|
+
spice_frame=geometry.SpiceFrame.ECLIPJ2000,
|
|
824
|
+
)
|
|
825
|
+
mock_rect_map_2 = ena_maps.RectangularSkyMap(
|
|
826
|
+
spacing_deg=4,
|
|
827
|
+
spice_frame=geometry.SpiceFrame.ECLIPJ2000,
|
|
828
|
+
)
|
|
829
|
+
with pytest.raises(
|
|
830
|
+
ValueError,
|
|
831
|
+
match="Event time must be specified if both objects are SkyMaps.",
|
|
832
|
+
):
|
|
833
|
+
ena_maps.match_coords_to_indices(mock_rect_map_1, mock_rect_map_2)
|
|
834
|
+
|
|
835
|
+
# No error if event time is specified
|
|
836
|
+
_ = ena_maps.match_coords_to_indices(
|
|
837
|
+
mock_rect_map_1, mock_rect_map_2, event_et=0
|
|
838
|
+
)
|