@@ -438,7 +438,7 @@ def _get_esa_mode(histrates: xr.Dataset) -> int:
438438# =============================================================================
439439
440440
441- class LoSpinAnglePointingSet (PointingSet ):
441+ class LoSpinAnglePointingSet (PointingSet ): # type: ignore[misc]
442442 """
443443 The spin-angle bins of one pointing, as an in-memory pointing set.
444444
@@ -809,7 +809,7 @@ def _esa_calibration(species: str, esa_mode: int) -> EsaCalibration:
809809
810810def _calculate_rates_and_intensities (
811811 sky_map : RectangularSkyMap , calibration : EsaCalibration
812- ) -> dict [str , np . ndarray ]:
812+ ) -> dict [str , xr . DataArray ]:
813813 """
814814 Turn the accumulated counts and exposure into rates and intensities.
815815
@@ -825,43 +825,40 @@ def _calculate_rates_and_intensities(
825825
826826 Returns
827827 -------
828- dict[str, np.ndarray ]
828+ dict[str, xr.DataArray ]
829829 The map variables, each of shape (epoch, esa level, pixel).
830830 """
831- counts = sky_map .data_1d ["ena_count" ]. values
832- exposure = sky_map .data_1d ["exposure_factor" ]. values
833- bg_rate_exposure = sky_map .data_1d ["bg_rate_exposure" ]. values
831+ counts = sky_map .data_1d ["ena_count" ]
832+ exposure = sky_map .data_1d ["exposure_factor" ]
833+ bg_rate_exposure = sky_map .data_1d ["bg_rate_exposure" ]
834834
835- # Every ESA level quantity gets a pixel axis to broadcast over the map.
836- energy = calibration .energy [:, np .newaxis ]
837- geometric_factor = calibration .geometric_factor [:, np .newaxis ]
838- gf_low = calibration .geometric_factor_low [:, np .newaxis ]
839- gf_high = calibration .geometric_factor_high [:, np .newaxis ]
835+ # Naming the energy dimension lets xarray broadcast during division
836+ # without the need to introduce new dimensions
837+ energy_dim = CoordNames .ENERGY_L2 .value
838+ energy = xr .DataArray (calibration .energy , dims = [energy_dim ])
839+ geometric_factor = xr .DataArray (calibration .geometric_factor , dims = [energy_dim ])
840+ gf_low = xr .DataArray (calibration .geometric_factor_low , dims = [energy_dim ])
841+ gf_high = xr .DataArray (calibration .geometric_factor_high , dims = [energy_dim ])
840842
841843 exposed = exposure > 0
842844
843- def _divide (numerator : np . ndarray , denominator : np . ndarray ) -> np . ndarray :
845+ def _divide (numerator : xr . DataArray , denominator : xr . DataArray ) -> xr . DataArray :
844846 """
845847 Divide only where the map was exposed, zero elsewhere.
846848
847849 Parameters
848850 ----------
849- numerator : np.ndarray
851+ numerator : xr.DataArray
850852 The array being divided.
851- denominator : np.ndarray
853+ denominator : xr.DataArray
852854 The array to divide it by.
853855
854856 Returns
855857 -------
856- np.ndarray
858+ xr.DataArray
857859 The quotient, zero in the pixels that were never exposed.
858860 """
859- return np .divide (
860- numerator ,
861- denominator ,
862- out = np .zeros_like (exposure ),
863- where = exposed ,
864- )
861+ return (numerator / denominator .where (exposed )).where (exposed , 0 )
865862
866863 count_rate = _divide (counts , exposure )
867864 # Poisson uncertainty on the counts, propagated to the rate
@@ -878,13 +875,13 @@ def _divide(numerator: np.ndarray, denominator: np.ndarray) -> np.ndarray:
878875 if not valid .all ():
879876 logger .warning (
880877 "The geometric factor of ESA levels "
881- f"{ (np .flatnonzero (~ valid [:, 0 ] ) + 1 ).tolist ()} is below its lower "
878+ f"{ (np .flatnonzero (~ valid . values ) + 1 ).tolist ()} is below its lower "
882879 f"error bound; their systematic errors are left at zero."
883880 )
884- intensity_upper = _divide (count_rate , np .where (valid , gf_low , 1.0 ) * energy )
881+ intensity_upper = _divide (count_rate , gf_low .where (valid , 1.0 ) * energy )
885882 intensity_lower = _divide (count_rate , gf_high * energy )
886- intensity_sys_err_plus = np . where ( valid , intensity_upper - intensity , 0.0 )
887- intensity_sys_err_minus = np . where ( valid , intensity - intensity_lower , 0.0 )
883+ intensity_sys_err_plus = ( intensity_upper - intensity ). where ( valid , 0.0 )
884+ intensity_sys_err_minus = ( intensity - intensity_lower ). where ( valid , 0.0 )
888885
889886 bg_rate = _divide (bg_rate_exposure , exposure )
890887 bg_rate_stat_uncert = np .sqrt (_divide (bg_rate , exposure ))
@@ -912,7 +909,7 @@ def _divide(numerator: np.ndarray, denominator: np.ndarray) -> np.ndarray:
912909
913910def _build_map_dataset (
914911 sky_map : RectangularSkyMap ,
915- variables : dict [str , np . ndarray ],
912+ variables : dict [str , xr . DataArray ],
916913 calibration : EsaCalibration ,
917914) -> xr .Dataset :
918915 """
@@ -925,7 +922,7 @@ def _build_map_dataset(
925922 ----------
926923 sky_map : RectangularSkyMap
927924 The map being built.
928- variables : dict[str, np.ndarray ]
925+ variables : dict[str, xr.DataArray ]
929926 The map variables, each of shape (epoch, esa level, pixel).
930927 calibration : EsaCalibration
931928 The energy response the map is binned in, read for the widths of the
@@ -937,9 +934,8 @@ def _build_map_dataset(
937934 The map variables on the (epoch, energy, longitude, latitude) grid,
938935 with the energy coordinate and its widths.
939936 """
940- dims = sky_map .data_1d ["ena_count" ].dims
941937 for name , values in variables .items ():
942- sky_map .data_1d [name ] = xr . DataArray ( values .astype (np .float32 ), dims = dims )
938+ sky_map .data_1d [name ] = values .astype (np .float32 )
943939 # `bg_rate_exposure` is an accumulator, not a map variable.
944940 sky_map .data_1d = sky_map .data_1d .drop_vars ("bg_rate_exposure" )
945941
0 commit comments