%load_ext autoreload
%autoreload 2
import numpy as np
import matplotlib.pyplot as plt
from healpix_plot.mollview import mollview

im = np.load('./heal_im.npy')
from healpix_analyse.alm_latlon import build_rings_from_latlon,anafast_latlon
import healpix_geo as hg
import torch
#im=np.random.randn(12*nside**2)
#im=hp.ud_grade(im,nside)
ilevel=int(np.log2(im.shape[0]//12)/2)

p,t=hg.ring.healpix_to_lonlat(np.arange(12*4**ilevel),ilevel,ellipsoid='sphere')
ring_theta, ring_phi_list, ring_counts, idx = build_rings_from_latlon(np.deg2rad(90-t),np.deg2rad(p))

mollview(im,nest=True)
../_images/ef3501b26c80d1ee44cc05f63b14859a309492889b46337afe9583a92b695c88.png
try:
    cl2.np.load('./heal_im_cl.npy')
except:
    import healpy as hp
    cl2 = hp.anafast(im)
    np.save('./heal_im_cl.npy',cl2)
    
cl = anafast_latlon(im[idx], ring_theta, ring_phi_list, ring_counts,lmax=3*2**ilevel-1)

plt.figure(figsize=(16,6))
plt.plot(cl2,lw=4,label='anafast healpy',color='b')
plt.plot(cl.cpu().numpy(),label='anafast_latlon',color='r')

plt.xscale('log')
plt.yscale('log')
plt.xlabel('Multipole')
plt.legend()
plt.xlabel('C(l)')
Text(0.5, 0, 'C(l)')
../_images/0b4cdeef0a9fda0f951f904104e4e9fc0373e9e413783454e601cfcafbe3a27b.png
try:
    import cdsapi
except:
    !pip install xarray cdsapi
#!/usr/bin/env python
import xarray as xr

try:
    dsG = xr.open_dataset("outputGT2m.grib")
except:
    c = cdsapi.Client()
    
    c.retrieve("reanalysis-era5-complete", {
        "class": "ea",
        "date": "2025-01-01",
        "expver": "1",
        "levtype": "sfc",
        "number": "0/1/2/3/4/5/6/7/8/9",
        "param": "167.128",
        "step": "12",
        "stream": "enda",
        "time": "09:00:00",
        "type": "4v",
        "grid":"N256",
    }, "outputGT2m.grib")
    dsG = xr.open_dataset("outputGT2m.grib")

dsG
/srv/conda/envs/notebook/lib/python3.13/site-packages/xarray/backends/plugins.py:109: RuntimeWarning: Engine 'kerchunk' loading failed:
No module named 'zarr.core.array_spec'; 'zarr.core' is not a package
  external_backend_entrypoints = backends_dict_from_pkg(entrypoints_unique)
<xarray.Dataset> Size: 20MB
Dimensions:     (number: 10, values: 348528)
Coordinates:
  * number      (number) int64 80B 0 1 2 3 4 5 6 7 8 9
    latitude    (values) float64 3MB ...
    longitude   (values) float64 3MB ...
    time        datetime64[ns] 8B ...
    step        timedelta64[ns] 8B ...
    surface     float64 8B ...
    valid_time  datetime64[ns] 8B ...
Dimensions without coordinates: values
Data variables:
    t2m         (number, values) float32 14MB ...
Attributes:
    GRIB_edition:            1
    GRIB_centre:             ecmf
    GRIB_centreDescription:  European Centre for Medium-Range Weather Forecasts
    GRIB_subCentre:          0
    Conventions:             CF-1.7
    institution:             European Centre for Medium-Range Weather Forecasts
    history:                 2026-08-03T13:04 GRIB to CDM+CF via cfgrib-0.9.1...
ring_theta, ring_phi_list, ring_counts, idx = build_rings_from_latlon(np.deg2rad(90.0-dsG.latitude.values),
                         np.deg2rad(dsG.longitude.values))
%%time
cl = anafast_latlon(torch.tensor(dsG.t2m.values[0,idx],device='cpu'),
                    ring_theta, ring_phi_list, ring_counts,quadrature='gauss_legendre',lmax=256)
CPU times: user 4.19 s, sys: 386 ms, total: 4.57 s
Wall time: 621 ms
plt.figure(figsize=(16,6))
plt.plot(cl.cpu().numpy(),label='anafast_latlon',color='r')

plt.xscale('log')
plt.yscale('log')
plt.xlabel('Multipole')
plt.legend()
plt.xlabel('C(l)')
Text(0.5, 0, 'C(l)')
../_images/f233448f39b48c1c7f39f91ff25edc95437983d2c3c80de0767b3426280189f5.png
from healpix_analyse.convol import HealPixConv
from healpix_analyse.down   import HealPixDown
from healpix_analyse.up     import HealPixUp

import torch
nside = 64

down = HealPixDown(level=int(np.log2(nside)))
conv = HealPixConv(level=int(np.log2(nside)), in_channels=1, out_channels=16)
up   = HealPixUp(level=int(np.log2(nside)) - 1)

x = torch.randn(8, 12 * nside**2)       # [B, N]
x_feat = conv(x)                         # [B, 16, N]
x_down, ids_out = down(x_feat[:, 0, :]) # [B, N/4]
x_up, ids_fine  = up(x_down)            # [B, N]
from healpix_analyse.down   import HealPixDown
from healpix_analyse.up     import HealPixUp
nside=64
import numpy as np
import matplotlib.pyplot as plt

plt.figure(figsize=(12,3))
x=np.random.randn(12*nside**2)
x_down=x
mollview(x_down,nest=True,sub=(2,5,1),hold=False,title='Input data')
lnside=nside
for k in range(4):
    down = HealPixDown(level=int(np.log2(lnside)),ellipsoid='sphere')
    x_down, _ = down(x_down)
    lnside/=2
    mollview(x_down,nest=True,sub=(2,5,2+k),hold=False,title='down %d'%(k))

x_up=x_down
for k in range(4):
    up   = HealPixUp(level=int(np.log2(lnside)),ellipsoid='sphere')
    x_up, _  = up(x_up)
    lnside*=2
    mollview(x_up,nest=True,sub=(2,5,7+k),hold=False,title='up %d'%(k))
../_images/56dfe8d2a6c77d07d51e77b5e9811d99211e4f532f52bbddf3ca4a160f5aca6d.png
import numpy as np
from healpix_analyse.convol import HealPixConv
import matplotlib.pyplot as plt
nside = 64
kernelsz=5
conv = HealPixConv(level=int(np.log2(nside)), in_channels=1, out_channels=1, n_gauges=4, kernel_sz=kernelsz,gauge_type='phi')

# 25-point Gaussian: center gets more weight than the 8 neighbours
x,y = np.meshgrid(np.arange(kernelsz)-kernelsz//2,np.arange(kernelsz)-kernelsz//2)

W = np.zeros([1,1,kernelsz*kernelsz])
W[0,0] = (np.exp(-0.25*(x**2+y**2))*np.cos(0.5*(x)*np.pi)).ravel()
W[0,0] -= W[0,0].mean()

plt.imshow(W[0,0].reshape(kernelsz,kernelsz))
conv.set_kernel(W)
HealPixConv(level=6, nside=64, in=1, out=1, kernel_sz=5, P=25, G=4, gauge='phi', partial=False)
../_images/10b3c83224c5eb129aa9d2c3dd25d20c109101c740474d61b47042975824eb0d.png
import numpy as np
sky = np.random.randn(12 * nside**2).astype(np.float32)
sky_smooth = conv(sky)     # returns np.ndarray [N]
print(sky_smooth.shape)
plt.figure(figsize=(16,9))
for k in range(4):
    mollview(sky_smooth[k],nest=True,sub=(2,2,1+k),hold=False,title=r'$\Theta=%d$'%(k))    
(4, 49152)
../_images/182db290d2de556fb343032661a3fec019fe3176166b232f28a844210d99b70e.png
nside = 64
kernelsz=7
conv = HealPixConv(level=int(np.log2(nside)), in_channels=1, out_channels=1, n_gauges=4, kernel_sz=kernelsz,gauge_type='phi')

# 25-point Gaussian: center gets more weight than the 8 neighbours
W = np.array([[0,0,0,0,1,0,0],
              [0,0,0,0,1,1,0],
              [0,0,0,0,1,1,1],
              [1,1,1,1,1,1,1],
              [0,0,0,0,1,1,1],
              [0,0,0,0,1,1,0],
              [0,0,0,0,1,0,0]]).ravel().astype('float32').reshape(1,1,kernelsz*kernelsz)
    

plt.imshow(W[0,0].reshape(kernelsz,kernelsz))
conv.set_kernel(W)
sky = np.zeros([12 * nside**2],dtype=np.float32)
tc,pc=hp.pix2ang(4,np.arange(12*4**2),nest=True)
sky[hp.ang2pix(nside,tc,pc,nest=True)]=1.0
sky_smooth = conv(sky)     # returns np.ndarray [N]
print(sky_smooth.shape)
plt.figure(figsize=(16,9))
for k in range(4):
    mollview(sky_smooth[k],nest=True,sub=(2,2,1+k),hold=False,title=r'gauge="phi" $\Theta=%d$'%(k))    # (49152,)
(4, 49152)
../_images/d30224233d5ec697fa69f3d32fa6b67a4b8ff644eb6ee01fb8bb6e44adbb4c24.png ../_images/076aa69efccc344d70bd3f3d74aa4553f932911e7ca1531dd4ec3c0cb52f4583.png
nside = 64
kernelsz=7
conv = HealPixConv(level=int(np.log2(nside)), in_channels=1, out_channels=1,device='cuda:0',
                   n_gauges=4, kernel_sz=kernelsz,gauge_type='cosmo')

# 25-point Gaussian: center gets more weight than the 8 neighbours
W = np.array([[0,0,0,0,1,0,0],
              [0,0,0,0,1,1,0],
              [0,0,0,0,1,1,1],
              [1,1,1,1,1,1,1],
              [0,0,0,0,1,1,1],
              [0,0,0,0,1,1,0],
              [0,0,0,0,1,0,0]]).ravel().astype('float32').reshape(1,1,kernelsz*kernelsz)
conv.set_kernel(W)

sky = np.zeros([12 * nside**2],dtype=np.float32)
tc,pc=hp.pix2ang(4,np.arange(12*4**2),nest=True)
sky[hp.ang2pix(nside,tc,pc,nest=True)]=1.0
sky_smooth = conv(sky)     # returns np.ndarray [N]
print(sky_smooth.shape)
plt.figure(figsize=(16,10))
for k in range(4):
    mollview(sky_smooth[k],nest=True,sub=(2,2,1+k),hold=False,title=r'gauge="cosmo" $\Theta=%d$'%(k))    # (49152,)
(4, 49152)
../_images/fb66dd39b8d9654b7616a9291e48e52feb525a294c5600ac2fbb0093df4cd9d0.png
# Placer les singularités loin de l'Europe (vers le Pacifique et l'océan Indien)
conv = HealPixConv(level=6, in_channels=1, out_channels=16,
                   gauge_type="projected_ref",
                   ref_direction=[0, 1, 0])  # singularités à φ=90° et φ=270°
kernelsz=7
# South center
lon=115 
lat=30

level=int(np.log2(nside))
center_idx,_,_=hg.nested.cone_coverage((lon,lat),5.0,level)
acenter_idx,_,_=hg.nested.cone_coverage((lon-180,-lat),5.0,level)

conv = HealPixConv(level=int(np.log2(nside)), in_channels=1, out_channels=1, n_gauges=4, 
                   kernel_sz=kernelsz,gauge_type="projected_ref",
                    singularity_lonlat=(lon,lat))

# 25-point Gaussian: center gets more weight than the 8 neighbours
W = np.array([[0,0,0,0,1,0,0],
              [0,0,0,0,1,1,0],
              [0,0,0,0,1,1,1],
              [1,1,1,1,1,1,1],
              [0,0,0,0,1,1,1],
              [0,0,0,0,1,1,0],
              [0,0,0,0,1,0,0]]).ravel().astype('float32').reshape(1,1,kernelsz*kernelsz)
    
conv.set_kernel(W)

sky = np.zeros([12 * nside**2],dtype=np.float32)
tc,pc=hp.pix2ang(4,np.arange(12*4**2),nest=True)
sky[hp.ang2pix(nside,tc,pc,nest=True)]=1.0
sky_smooth = conv(sky)     # returns np.ndarray [N]
print(sky_smooth.shape)
plt.figure(figsize=(16,10))
for k in range(4):
    tmp=sky_smooth[k]
    tmp[center_idx]=2.
    tmp[acenter_idx]=2.
    mollview(tmp,nest=True,sub=(2,2,1+k),hold=False,coastlines=True,
             title=r'gauge="projected_ref" $\Theta=%d$'%(k))    # (49152,)
(4, 49152)
../_images/9ca835c5508e6b6d8dc59f07b791f4248d8678aaff8c9b1b33facaa0d4141077.png
kernelsz=7
# South center
lon=115 
lat=40

level=int(np.log2(nside))
center_idx,_,_=hg.nested.cone_coverage((-55.0, -10.0),5.0,level)
acenter_idx,_,_=hg.nested.cone_coverage((90.0,  45.0),5.0,level)

center_idx2,_,_=hg.nested.cone_coverage((55.0, 10.0),5.0,level)
acenter_idx2,_,_=hg.nested.cone_coverage((-90.0,  -45.0),5.0,level)

conv = HealPixConv(level=int(np.log2(nside)), in_channels=1, out_channels=1, n_gauges=4, 
                   kernel_sz=kernelsz,
                   gauge_type="two_ref",
                   singularity_lonlat=[
                                (-55.0, -10.0),   # Amazonie  → antipode = (125°E, 10°N) Bornéo
                                ( 90.0,  45.0),   # Afrique   → antipode = (200°E, -10°N) Pacifique central
                                ]
                    )
print(conv.singularity_info())

# 25-point Gaussian: center gets more weight than the 8 neighbours
W = np.array([[0,0,0,0,1,0,0],
              [0,0,0,0,1,1,0],
              [0,0,0,0,1,1,1],
              [1,1,1,1,1,1,1],
              [0,0,0,0,1,1,1],
              [0,0,0,0,1,1,0],
              [0,0,0,0,1,0,0]]).ravel().astype('float32').reshape(1,1,kernelsz*kernelsz)
    
conv.set_kernel(W)

sky = np.zeros([12 * nside**2],dtype=np.float32)
tc,pc=hp.pix2ang(4,np.arange(12*4**2),nest=True)
sky[hp.ang2pix(nside,tc,pc,nest=True)]=1.0
sky_smooth = conv(sky)     # returns np.ndarray [N]
print(sky_smooth.shape)
plt.figure(figsize=(16,10))
for k in range(4):
    tmp=sky_smooth[k]
    tmp[center_idx]=2.
    tmp[acenter_idx]=2.
    tmp[center_idx2]=.5
    tmp[acenter_idx2]=.5
    mollview(tmp,nest=True,sub=(2,2,1+k),hold=False,coastlines=True,
             title=r'gauge="two_ref" $\Theta=%d$'%(k))    # (49152,)
gauge_type='two_ref':
  singularity 1  : lon=+305.00°  lat=-10.00°  (index +1, user-defined)
  singularity 1b : lon=+125.00°  lat=+10.00°  (index +1, antipode of 1)
  singularity 2  : lon=+90.00°  lat=+45.00°  (index +1, user-defined)
  singularity 2b : lon=+270.00°  lat=-45.00°  (index +1, antipode of 2)
  N/S poles      : index -1 each (side-effect, keep outside domain of interest)
  ref_directions : r1=[0.5648625214636235, -0.8067072841115988, -0.17364817766693033]
                   r2=[4.329780281177467e-17, 0.7071067811865476, 0.7071067811865475]
(4, 49152)
../_images/a94ca58929fd7905aefe44003227605c2eb826d79e76617febe05817bce08d16.png