foscat 2025.11.1__tar.gz → 2026.2.1__tar.gz

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.
Files changed (51) hide show
  1. {foscat-2025.11.1/src/foscat.egg-info → foscat-2026.2.1}/PKG-INFO +1 -69
  2. {foscat-2025.11.1 → foscat-2026.2.1}/README.md +0 -68
  3. {foscat-2025.11.1 → foscat-2026.2.1}/pyproject.toml +1 -1
  4. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/FoCUS.py +71 -16
  5. foscat-2026.2.1/src/foscat/SphereDownGeo.py +380 -0
  6. foscat-2026.2.1/src/foscat/SphereUpGeo.py +175 -0
  7. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/SphericalStencil.py +27 -246
  8. foscat-2026.2.1/src/foscat/alm_loc.py +270 -0
  9. foscat-2026.2.1/src/foscat/healpix_vit_torch-old.py +658 -0
  10. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/scat_cov.py +24 -24
  11. {foscat-2025.11.1 → foscat-2026.2.1/src/foscat.egg-info}/PKG-INFO +1 -69
  12. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat.egg-info/SOURCES.txt +4 -0
  13. {foscat-2025.11.1 → foscat-2026.2.1}/LICENSE +0 -0
  14. {foscat-2025.11.1 → foscat-2026.2.1}/setup.cfg +0 -0
  15. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/BkBase.py +0 -0
  16. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/BkNumpy.py +0 -0
  17. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/BkTensorflow.py +0 -0
  18. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/BkTorch.py +0 -0
  19. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/CNN.py +0 -0
  20. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/CircSpline.py +0 -0
  21. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/GCNN.py +0 -0
  22. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/HOrientedConvol.py +0 -0
  23. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/HealBili.py +0 -0
  24. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/HealSpline.py +0 -0
  25. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/Plot.py +0 -0
  26. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/Softmax.py +0 -0
  27. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/Spline1D.py +0 -0
  28. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/Synthesis.py +0 -0
  29. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/UNET.py +0 -0
  30. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/__init__.py +0 -0
  31. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/alm.py +0 -0
  32. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/backend.py +0 -0
  33. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/backend_tens.py +0 -0
  34. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/heal_NN.py +0 -0
  35. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/healpix_unet_torch.py +0 -0
  36. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/healpix_vit_skip.py +0 -0
  37. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/healpix_vit_torch.py +0 -0
  38. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/loss_backend_tens.py +0 -0
  39. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/loss_backend_torch.py +0 -0
  40. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/planar_vit.py +0 -0
  41. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/scat.py +0 -0
  42. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/scat1D.py +0 -0
  43. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/scat2D.py +0 -0
  44. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/scat_cov1D.py +0 -0
  45. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/scat_cov2D.py +0 -0
  46. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/scat_cov_map.py +0 -0
  47. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/scat_cov_map2D.py +0 -0
  48. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat/unet_2_d_from_healpix_params.py +0 -0
  49. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat.egg-info/dependency_links.txt +0 -0
  50. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat.egg-info/requires.txt +0 -0
  51. {foscat-2025.11.1 → foscat-2026.2.1}/src/foscat.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: foscat
3
- Version: 2025.11.1
3
+ Version: 2026.2.1
4
4
  Summary: Generate synthetic Healpix or 2D data using Cross Scattering Transform
5
5
  Author-email: Jean-Marc DELOUIS <jean.marc.delouis@ifremer.fr>
6
6
  Maintainer-email: Theo Foulquier <theo.foulquier@ifremer.fr>
@@ -91,74 +91,6 @@ git clone https://github.com/jmdelouis/FOSCAT_DEMO.git
91
91
 
92
92
  ```
93
93
 
94
- # Spherical data example
95
-
96
- ## compute a synthetic image
97
-
98
- ```
99
- python demo.py -n=32 -k -c -s=100
100
- ```
101
-
102
- The _demo.py_ script serves as a demonstration of the capabilities of the foscat library. It utilizes the Cross Wavelet Scattering Transform to generate a Healpix map that possesses the same characteristics as a specified input map.
103
-
104
- - `-n=32` computes map with nside=32.
105
- - `-k` uses 5x5 kernel.
106
- - `-c` uses Scattering Covariance.
107
- - `-l` uses LBFGS minimizer.
108
- - `-s=100` computes 100 steps.
109
-
110
- ```
111
- python demo.py -n=8 [-c|--cov][-s|--steps=3000][-S=1234|--seed=1234][-k|--k5x5][-d|--data][-o|--out][-r|--orient] [-p|--path][-a|--adam]
112
-
113
- ```
114
-
115
- - The "-n" option specifies the nside of the input map. The maximum nside value is 256 with the default map.
116
- - The "--cov" option (optional) uses scat_cov instead of scat.
117
- - The "--steps" option (optional) specifies the number of iterations. If not specified, the default value is 1000.
118
- - The "--seed" option (optional) specifies the seed of the random generator.
119
- - The "--path" option (optional) allows you to define the path where the output files will be written. The default path is "data".
120
- - The "--k5x5" option (optional) uses a 5x5 kernel instead of a 3x3.
121
- - The "--data" option (optional) specifies the input data file to be used. If not specified, the default file "LSS_map_nside128.npy" will be used.
122
- - The "--out" option (optional) specifies the output file name. If not specified, the output file will be saved in "demo".
123
- - The "--orient" option (optional) specifies the number of orientations. If not specified, the default value is 4.
124
- - The "--adam" option (optional) makes the synthesis using the ADAM optimizer instead of the L_BFGS.
125
-
126
- ## plot the result
127
-
128
- The following script generates a series of plots that showcase different aspects of the synthesis process using the _demo.py_ script.
129
-
130
- > python test2D.py
131
-
132
- ```
133
- python plotdemo.py -n=32 -c
134
- ```
135
-
136
- # 2D field demo
137
-
138
- > python test2Dplot.py
139
-
140
- # compute a synthetic turbulent field
141
-
142
- The python scripts _demo2D.py_ included in this package demonstrate how to use the foscat library to generate a 2D synthetic fields that have patterns with the same statistical properties as a specified 2D image. In this particular case, the input field is a sea surface temperature extracted from a north atlantic ocean simulation.
143
-
144
- > python testHealpix.py
145
-
146
- ```
147
- python demo2d.py -n=32 -k -c
148
- ```
149
-
150
- > python testHplot.py
151
-
152
- The following script generates a series of plots that showcase different aspects of the synthesis process using the _demo2D.py_ script.
153
-
154
- ```
155
- python plotdemo2d.py -n=32 -c
156
- ```
157
-
158
- For more information, see the [documentation](https://foscat-documentation.readthedocs.io/en/latest/index.html).
159
-
160
- > mpirun -np 3 testHealpix_mpi.py
161
-
162
94
  ## Authors and acknowledgment
163
95
 
164
96
  Authors: J.-M. Delouis, P. Campeti, T. Foulquier, J. Mangin, L. Mousset, T. Odaka, F. Paul, E. Allys
@@ -62,74 +62,6 @@ git clone https://github.com/jmdelouis/FOSCAT_DEMO.git
62
62
 
63
63
  ```
64
64
 
65
- # Spherical data example
66
-
67
- ## compute a synthetic image
68
-
69
- ```
70
- python demo.py -n=32 -k -c -s=100
71
- ```
72
-
73
- The _demo.py_ script serves as a demonstration of the capabilities of the foscat library. It utilizes the Cross Wavelet Scattering Transform to generate a Healpix map that possesses the same characteristics as a specified input map.
74
-
75
- - `-n=32` computes map with nside=32.
76
- - `-k` uses 5x5 kernel.
77
- - `-c` uses Scattering Covariance.
78
- - `-l` uses LBFGS minimizer.
79
- - `-s=100` computes 100 steps.
80
-
81
- ```
82
- python demo.py -n=8 [-c|--cov][-s|--steps=3000][-S=1234|--seed=1234][-k|--k5x5][-d|--data][-o|--out][-r|--orient] [-p|--path][-a|--adam]
83
-
84
- ```
85
-
86
- - The "-n" option specifies the nside of the input map. The maximum nside value is 256 with the default map.
87
- - The "--cov" option (optional) uses scat_cov instead of scat.
88
- - The "--steps" option (optional) specifies the number of iterations. If not specified, the default value is 1000.
89
- - The "--seed" option (optional) specifies the seed of the random generator.
90
- - The "--path" option (optional) allows you to define the path where the output files will be written. The default path is "data".
91
- - The "--k5x5" option (optional) uses a 5x5 kernel instead of a 3x3.
92
- - The "--data" option (optional) specifies the input data file to be used. If not specified, the default file "LSS_map_nside128.npy" will be used.
93
- - The "--out" option (optional) specifies the output file name. If not specified, the output file will be saved in "demo".
94
- - The "--orient" option (optional) specifies the number of orientations. If not specified, the default value is 4.
95
- - The "--adam" option (optional) makes the synthesis using the ADAM optimizer instead of the L_BFGS.
96
-
97
- ## plot the result
98
-
99
- The following script generates a series of plots that showcase different aspects of the synthesis process using the _demo.py_ script.
100
-
101
- > python test2D.py
102
-
103
- ```
104
- python plotdemo.py -n=32 -c
105
- ```
106
-
107
- # 2D field demo
108
-
109
- > python test2Dplot.py
110
-
111
- # compute a synthetic turbulent field
112
-
113
- The python scripts _demo2D.py_ included in this package demonstrate how to use the foscat library to generate a 2D synthetic fields that have patterns with the same statistical properties as a specified 2D image. In this particular case, the input field is a sea surface temperature extracted from a north atlantic ocean simulation.
114
-
115
- > python testHealpix.py
116
-
117
- ```
118
- python demo2d.py -n=32 -k -c
119
- ```
120
-
121
- > python testHplot.py
122
-
123
- The following script generates a series of plots that showcase different aspects of the synthesis process using the _demo2D.py_ script.
124
-
125
- ```
126
- python plotdemo2d.py -n=32 -c
127
- ```
128
-
129
- For more information, see the [documentation](https://foscat-documentation.readthedocs.io/en/latest/index.html).
130
-
131
- > mpirun -np 3 testHealpix_mpi.py
132
-
133
65
  ## Authors and acknowledgment
134
66
 
135
67
  Authors: J.-M. Delouis, P. Campeti, T. Foulquier, J. Mangin, L. Mousset, T. Odaka, F. Paul, E. Allys
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "foscat"
3
- version = "2025.11.1"
3
+ version = "2026.02.1"
4
4
  description = "Generate synthetic Healpix or 2D data using Cross Scattering Transform"
5
5
  readme = "README.md"
6
6
  license = { text = "BSD-3-Clause" }
@@ -5,8 +5,11 @@ import healpy as hp
5
5
  import numpy as np
6
6
  import foscat.HealSpline as HS
7
7
  from scipy.interpolate import griddata
8
+ from foscat.SphereDownGeo import SphereDownGeo
9
+ from foscat.SphereUpGeo import SphereUpGeo
10
+ import torch
8
11
 
9
- TMPFILE_VERSION = "V10_0"
12
+ TMPFILE_VERSION = "V13_0"
10
13
 
11
14
 
12
15
  class FoCUS:
@@ -36,7 +39,7 @@ class FoCUS:
36
39
  mpi_rank=0
37
40
  ):
38
41
 
39
- self.__version__ = "2025.11.1"
42
+ self.__version__ = "2026.02.1"
40
43
  # P00 coeff for normalization for scat_cov
41
44
  self.TMPFILE_VERSION = TMPFILE_VERSION
42
45
  self.P1_dic = None
@@ -57,7 +60,8 @@ class FoCUS:
57
60
  self.kernelR_conv = {}
58
61
  self.kernelI_conv = {}
59
62
  self.padding_conv = {}
60
-
63
+ self.down = {}
64
+ self.up = {}
61
65
  if not self.silent:
62
66
  print("================================================")
63
67
  print(" START FOSCAT CONFIGURATION")
@@ -648,6 +652,7 @@ class FoCUS:
648
652
  return rim
649
653
 
650
654
  # --------------------------------------------------------
655
+
651
656
  def ud_grade_2(self, im, axis=0, cell_ids=None, nside=None,max_poll=False):
652
657
 
653
658
  if self.use_2D:
@@ -721,6 +726,22 @@ class FoCUS:
721
726
 
722
727
  else:
723
728
  shape = list(im.shape)
729
+ if nside is None:
730
+ l_nside=int(np.sqrt(shape[-1]//12))
731
+ else:
732
+ l_nside=nside
733
+
734
+ nbatch=1
735
+ for k in range(len(shape)-1):
736
+ nbatch*=shape[k]
737
+ if l_nside not in self.down:
738
+ print('initialise down', l_nside)
739
+ self.down[l_nside] = SphereDownGeo(nside_in=l_nside, dtype=self.all_bk_type,mode="smooth", in_cell_ids=cell_ids)
740
+
741
+ res,out_cell=self.down[l_nside](self.backend.bk_reshape(im,[nbatch,1,shape[-1]]))
742
+
743
+ return self.backend.bk_reshape(res,shape[:-1]+[out_cell.shape[0]]),out_cell
744
+ '''
724
745
  if self.use_median:
725
746
  if cell_ids is not None:
726
747
  sim, new_cell_ids = self.backend.binned_mean(im, cell_ids,reduce='median')
@@ -747,6 +768,7 @@ class FoCUS:
747
768
  return self.backend.bk_reduce_mean(
748
769
  self.backend.bk_reshape(im, shape[0:-1]+[shape[-1]//4,4]), axis=-1
749
770
  ),None
771
+ '''
750
772
 
751
773
  # --------------------------------------------------------
752
774
  def up_grade(self, im, nout,
@@ -836,6 +858,7 @@ class FoCUS:
836
858
  else:
837
859
  lout = nside
838
860
 
861
+ '''
839
862
  if (lout,nout) not in self.pix_interp_val or force_init_index:
840
863
  if not self.silent:
841
864
  print("compute lout nout", lout, nout)
@@ -926,12 +949,32 @@ class FoCUS:
926
949
 
927
950
  del w
928
951
  del p
929
-
930
- if lout == nout:
931
- imout = im
932
- else:
933
- # work only on the last column
934
-
952
+ '''
953
+ shape=list(im.shape)
954
+ nbatch=1
955
+ for k in range(len(shape)-1):
956
+ nbatch*=shape[k]
957
+
958
+ im=self.backend.bk_reshape(im,[nbatch,1,shape[-1]])
959
+
960
+ while lout<nout:
961
+ if lout not in self.up:
962
+ if o_cell_ids is None:
963
+ l_o_cell_ids=torch.tensor(np.arange(12*(lout**2),dtype='int'),device=im.device)
964
+ else:
965
+ l_o_cell_ids=o_cell_ids
966
+ self.up[lout] = SphereUpGeo(nside_out=lout,
967
+ dtype=self.all_bk_type,
968
+ cell_ids_out=l_o_cell_ids,
969
+ up_norm="col_l1")
970
+ im, fine_ids = self.up[lout](self.backend.bk_cast(im))
971
+ lout*=2
972
+ if lout<nout and o_cell_ids is not None:
973
+ o_cell_ids=torch.repeat(fine_ids,4)*4+ \
974
+ torch.tile(torch.tensor([0,1,2,3],device=fine_ids.device,dtype=fine_ids.dtype),fine_ids.shape[0])
975
+
976
+ return self.backend.bk_reshape(im,shape[:-1]+[im.shape[-1]])
977
+ '''
935
978
  ndata = 1
936
979
  for k in range(len(ishape)-1):
937
980
  ndata = ndata * ishape[k]
@@ -960,6 +1003,7 @@ class FoCUS:
960
1003
  return self.backend.bk_reshape(
961
1004
  imout, ishape[0:-1]+[imout.shape[-1]]
962
1005
  )
1006
+ '''
963
1007
  return imout
964
1008
 
965
1009
  # --------------------------------------------------------
@@ -1354,7 +1398,9 @@ class FoCUS:
1354
1398
  else:
1355
1399
  l_cell_ids=cell_ids
1356
1400
 
1357
- nvalid=self.KERNELSZ**2
1401
+ nvalid=4*self.KERNELSZ**2
1402
+ if nvalid>12*nside**2:
1403
+ nvalid=12*nside**2
1358
1404
  idxEB=hconvol.idx_nn[:,0:nvalid]
1359
1405
  tmpEB=np.zeros([self.NORIENT,4,l_cell_ids.shape[0],nvalid],dtype='complex')
1360
1406
  tmpS=np.zeros([4,l_cell_ids.shape[0],nvalid],dtype='float')
@@ -1500,7 +1546,7 @@ class FoCUS:
1500
1546
 
1501
1547
  else:
1502
1548
  if l_kernel == 5:
1503
- pw = 0.5
1549
+ pw = 0.75
1504
1550
  pw2 = 0.5
1505
1551
  threshold = 2e-5
1506
1552
 
@@ -1533,11 +1579,20 @@ class FoCUS:
1533
1579
 
1534
1580
  xx=np.tile(np.arange(self.KERNELSZ)-self.KERNELSZ//2,self.KERNELSZ).reshape(self.KERNELSZ,self.KERNELSZ)
1535
1581
 
1536
- wwr=(np.exp(-pw2*(xx**2+(xx.T)**2))*np.cos(pw*xx*np.pi)).reshape(1,1,self.KERNELSZ*self.KERNELSZ)
1537
- wwr-=wwr.mean()
1538
- wwi=(np.exp(-pw2*(xx**2+(xx.T)**2))*np.sin(pw*xx*np.pi)).reshape(1,1,self.KERNELSZ*self.KERNELSZ)
1539
- wwi-=wwi.mean()
1540
- amp=np.sum(abs(wwr+1J*wwi))
1582
+ if nside>2:
1583
+ wwr=(np.exp(-pw2*(xx**2+(xx.T)**2))*np.cos(pw*xx*np.pi)).reshape(1,1,self.KERNELSZ*self.KERNELSZ)
1584
+ wwr-=wwr.mean()
1585
+ wwi=(np.exp(-pw2*(xx**2+(xx.T)**2))*np.sin(pw*xx*np.pi)).reshape(1,1,self.KERNELSZ*self.KERNELSZ)
1586
+ wwi-=wwi.mean()
1587
+ amp=np.sum(abs(wwr+1J*wwi))
1588
+ else:
1589
+ #asymetric kernels
1590
+ wwr=(np.exp(-2*pw2*(xx**2+self.NORIENT*(xx.T)**2))).reshape(1,1,self.KERNELSZ*self.KERNELSZ)
1591
+ #wwr-=wwr.mean()
1592
+ #wwi=(np.exp(-pw2*(xx**2+(xx.T)**2))*np.sin(pw*xx*np.pi)).reshape(1,1,self.KERNELSZ*self.KERNELSZ)
1593
+ #wwi-=wwi.mean()
1594
+ wwi=0.0*wwr
1595
+ amp=self.NORIENT*np.sum(abs(wwr+1J*wwi))
1541
1596
 
1542
1597
  wwr/=amp
1543
1598
  wwi/=amp