88from functools import partial
99
1010import numpy as np
11+ from cfdm import integer_dtype
1112from cfdm .data .dask_utils import cfdm_to_memory
1213from scipy .ndimage import convolve1d
1314
@@ -508,21 +509,21 @@ def cf_healpix_bounds(
508509 hierarchy, starting at 0 for the base tessellation with 12
509510 cells. Must be an `int` for *indexing_scheme* ``'nested'``
510511 or ``'ring'``, but is ignored for *indexing_scheme*
511- ``'nested_unique'`` ( in which case *refinement_level* may
512- be `None`) .
512+ ``'nested_unique'``, in which case *refinement_level* may
513+ be `None`.
513514
514515 latitude: `bool`, optional
515- If True then return latitude bounds.
516+ If True then return the bounds' latitudes .
516517
517518 longitude: `bool`, optional
518- If True then return longitude bounds.
519+ If True then return the bounds' longitudes .
519520
520521 pole_longitude: `None` or number
521- The longitude of coordinate bounds that lie exactly on the
522- north (south) pole. If `None` then the longitude of such a
523- vertex will be the same as the south (north) vertex of the
524- same cell. If set to a number, then the longitudes of such
525- vertices will all be given that value.
522+ The longitude of bounds that lie exactly on the north
523+ (south) pole. If `None` ( the default) then the longitude
524+ of such a vertex will be the same as the south (north)
525+ vertex of the same cell. If set to a number, then the
526+ longitudes of all such vertices will be given that value.
526527
527528 :Returns:
528529
@@ -560,8 +561,8 @@ def cf_healpix_bounds(
560561 except ImportError as e :
561562 raise ImportError (
562563 f"{ e } . Must install healpix (https://pypi.org/project/healpix) "
563- "to allow the calculation of latitude/longitude coordinate "
564- "bounds for a HEALPix grid"
564+ "for the calculation of latitude/longitude coordinate bounds "
565+ "of a HEALPix grid"
565566 )
566567
567568 a = cfdm_to_memory (a )
@@ -570,14 +571,16 @@ def cf_healpix_bounds(
570571 if a .ndim != 1 :
571572 raise ValueError (
572573 "Can only calculate HEALPix cell bounds when the "
573- f"healpix_index array has one dimension. Got shape { a .shape } "
574+ f"healpix_index array has one dimension. Got shape: { a .shape } "
574575 )
575576
576577 if latitude :
577578 pos = 1
578579 elif longitude :
579580 pos = 0
580581
582+ # Define the function that's going to calculate the bounds from
583+ # the HEALPix indices
581584 if indexing_scheme == "ring" :
582585 bounds_func = healpix ._chp .ring2ang_uv
583586 else :
@@ -596,26 +599,31 @@ def cf_healpix_bounds(
596599 b = np .empty ((a .size , 4 ), dtype = "float64" )
597600
598601 if indexing_scheme == "nested_unique" :
599- # Create bounds for 'nested_unique' cells
602+ # Create bounds for 'nested_unique' indices
600603 orders , a = healpix .uniq2pix (a , nest = True )
601604 for order in np .unique (orders ):
602605 nside = healpix .order2nside (order )
603606 indices = np .where (orders == order )[0 ]
604607 for j , (u , v ) in enumerate (vertices ):
605608 thetaphi = bounds_func (nside , a [indices ], u , v )
606609 b [indices , j ] = healpix .lonlat_from_thetaphi (* thetaphi )[pos ]
610+
611+ del orders , indices
607612 else :
608- # Create bounds for 'nested' or 'ring' cells
613+ # Create bounds for 'nested' or 'ring' indices
609614 nside = healpix .order2nside (refinement_level )
610615 for j , (u , v ) in enumerate (vertices ):
611616 thetaphi = bounds_func (nside , a , u , v )
612- b [..., j ] = healpix .lonlat_from_thetaphi (* thetaphi )[pos ]
617+ b [:, j ] = healpix .lonlat_from_thetaphi (* thetaphi )[pos ]
618+
619+ del thetaphi , a
613620
614621 if not pos :
615622 # Ensure that longitude bounds are less than 360
616623 where_ge_360 = np .where (b >= 360 )
617624 if where_ge_360 [0 ].size :
618625 b [where_ge_360 ] -= 360.0
626+ del where_ge_360
619627
620628 # Vertices on the north or south pole come out with a
621629 # longitude of NaN, so replace these with sensible values:
@@ -671,14 +679,14 @@ def cf_healpix_coordinates(
671679 hierarchy, starting at 0 for the base tessellation with 12
672680 cells. Must be an `int` for *indexing_scheme* ``'nested'``
673681 or ``'ring'``, but is ignored for *indexing_scheme*
674- ``'nested_unique'`` ( in which case *refinement_level* may
675- be `None`) .
682+ ``'nested_unique'``, in which case *refinement_level* may
683+ be `None`.
676684
677685 latitude: `bool`, optional
678- If True then return latitude coordinates .
686+ If True then return the coordinate latitudes .
679687
680688 longitude: `bool`, optional
681- If True then return longitude coordinates .
689+ If True then return the coordinate longitudes .
682690
683691 :Returns:
684692
@@ -702,16 +710,16 @@ def cf_healpix_coordinates(
702710 except ImportError as e :
703711 raise ImportError (
704712 f"{ e } . Must install healpix (https://pypi.org/project/healpix) "
705- "to allow the calculation of latitude/longitude coordinates "
706- "for a HEALPix grid"
713+ "for the calculation of latitude/longitude coordinates of a "
714+ "HEALPix grid"
707715 )
708716
709717 a = cfdm_to_memory (a )
710718
711719 if a .ndim != 1 :
712720 raise ValueError (
713721 "Can only calculate HEALPix cell coordinates when the "
714- f"healpix_index array has one dimension. Got shape { a .shape } "
722+ f"healpix_index array has one dimension. Got shape: { a .shape } "
715723 )
716724
717725 if latitude :
@@ -721,7 +729,7 @@ def cf_healpix_coordinates(
721729
722730 match indexing_scheme :
723731 case "nested_unique" :
724- # Create coordinates for 'nested_unique' cells
732+ # Create coordinates for 'nested_unique' indices
725733 c = np .empty (a .shape , dtype = "float64" )
726734
727735 nest = True
@@ -734,7 +742,7 @@ def cf_healpix_coordinates(
734742 )[pos ]
735743
736744 case "nested" | "ring" :
737- # Create coordinates for 'nested' or 'ring' cells
745+ # Create coordinates for 'nested' or 'ring' indices
738746 nest = indexing_scheme == "nested"
739747 nside = healpix .order2nside (refinement_level )
740748 c = healpix .pix2ang (
@@ -760,12 +768,14 @@ def cf_healpix_increase_refinement(a, ncells, iaxis, quantity):
760768 an "extensive" quantity only, the broadcast values are reduced to
761769 be consistent with the new smaller cell areas.
762770
763- .. warning:: The returned `numpy` array will take up at least
764- *ncells* times more memory than the input array
765- *a*. For instance, if *a* has shape ``(3600, 48)``
766- (1.3 MiB), the HEALPix axis is ``1``, and *ncells* is
767- ``4096``, then the returned array will have shape
768- ``(3600, 196608)`` (5.3 GiB), where 196608=48*4096.
771+ .. warning:: The returned `numpy` array will take up *ncells*
772+ times more memory than the input array *a* (or two
773+ times *ncells* more memory, if *a* contains 32-bit
774+ integers and *quantity* is ``'extensive'``). For
775+ instance, if 64-bit *a* has shape ``(3600, 48)``
776+ (1.318 MiB), the HEALPix axis is ``1``, and *ncells*
777+ is ``4096``, then the returned array will have shape
778+ ``(3600, 196608)`` (5.4 GiB), where 196608=48*4096.
769779
770780 K. Gorski, Eric Hivon, A. Banday, B. Wandelt, M. Bartelmann, et
771781 al.. HEALPix: A Framework for High-Resolution Discretization and
@@ -805,13 +815,19 @@ def cf_healpix_increase_refinement(a, ncells, iaxis, quantity):
805815 if quantity == "extensive" :
806816 a = a / ncells
807817
818+ # Create a new dimension over which the original HEALPix axis will
819+ # be broadcast
808820 iaxis = iaxis + 1
809821 a = np .expand_dims (a , iaxis )
810822
823+ # Define the shape of the new array, and broadcast 'a' to it.
824+ # shape
811825 shape = list (a .shape )
812826 shape [iaxis ] = ncells
813-
814827 a = np .broadcast_to (a , shape , subok = True )
828+
829+ # Reshape the new array to combine the original HEALPix and
830+ # broadcast dimensions into a single new HEALPix dimension
815831 a = a .reshape (
816832 shape [: iaxis - 1 ]
817833 + [shape [iaxis - 1 ] * shape [iaxis ]]
@@ -821,7 +837,7 @@ def cf_healpix_increase_refinement(a, ncells, iaxis, quantity):
821837 return a
822838
823839
824- def cf_healpix_increase_refinement_indices (a , ncells ):
840+ def cf_healpix_increase_refinement_indices (a , refinement_level , ncells ):
825841 """Increase the refinement level of HEALPix indices.
826842
827843 For instance, when going from refinement level 1 to refinement
@@ -831,12 +847,14 @@ def cf_healpix_increase_refinement_indices(a, ncells):
831847 ``ncells`` is the number of cells at refinement level 2 that lie
832848 inside one cell at refinement level 1, i.e. ``ncells=4**(2-1)=4``.
833849
834- .. warning:: The returned `numpy` array will take up at least
835- *ncells* times more memory than the input array
836- *a*. For instance, if *a* has shape ``(48,)`` (384
837- B), and *ncells* is ``262144``, then the returned
838- array will have shape ``(12582912,)`` (96 MiB), where
839- 12582912=48*262144.
850+ .. warning:: The returned `numpy` array will take up *ncells*
851+ times more memory than the input array *a* (or two
852+ times *ncells* more memory, if *a* is 32-bit and the
853+ output requires 64-bit integers). For instance, if
854+ 32-bit *a* has shape ``(48,)`` (192 B), and *ncells*
855+ is ``67108864``, then the returned 64-bit array will
856+ have shape ``(3221225472,)`` (24 GiB), where
857+ 3221225472=48*67108864.
840858
841859 K. Gorski, Eric Hivon, A. Banday, B. Wandelt, M. Bartelmann, et
842860 al.. HEALPix: A Framework for High-Resolution Discretization and
@@ -853,6 +871,9 @@ def cf_healpix_increase_refinement_indices(a, ncells):
853871 a: `numpy.ndarray`
854872 The array of HEALPix nested indices.
855873
874+ refinement_level: `int`
875+ The new higher HEALPix refinement level.
876+
856877 ncells: `int`
857878 The number of cells at the new refinement level which are
858879 contained in one cell at the original refinement level
@@ -865,10 +886,31 @@ def cf_healpix_increase_refinement_indices(a, ncells):
865886 """
866887 a = cfdm_to_memory (a )
867888
868- a = a * ncells
889+ # Set the data type to allow for the largest possible HEALPix
890+ # index at the new refinement level
891+ dtype = integer_dtype (12 * (4 ** refinement_level ) - 1 )
892+ if a .dtype != dtype :
893+ a = a .astype (dtype , copy = True )
894+ a *= ncells
895+ else :
896+ a = a * ncells
897+
898+ # Create a new dimension over which the original HEALPix axis will
899+ # be broadcast
869900 a = np .expand_dims (a , - 1 )
870- a = np .broadcast_to (a , (a .size , ncells ), subok = True ).copy ()
901+
902+ # Define the shape of the new array, and broadcast 'a' to it.
903+ shape = (a .size , ncells )
904+ a = np .broadcast_to (a , shape , subok = True ).copy ()
905+
906+ # Increment the broadcast values along the new dimension, so that
907+ # a[i, :] contains all of the nested indices at the higher
908+ # refinement level that correspond to index i at the original
909+ # refinement level.
871910 a += np .arange (ncells )
911+
912+ # Reshape the new array to combine the original HEALPix and
913+ # broadcast dimensions into a single new HEALPix dimension
872914 a = a .flatten ()
873915
874916 return a
0 commit comments