sandbox/acastillo/output_fields/spectra/spectra_sample.h

    Sampling a plane onto a regular lattice

    sample_scalar_plane() evaluates every field in list on a regular m1 x m2 lattice in the plane z = h and assembles the result, across ranks, into one contiguous array. 3D only.

    This is the piece a horizontal FFT needs: foreach_region() returns the cell containing each query point, so on a tree grid the lattice is a uniform view of an adaptive field. With xmin = X0, xmax = X0 + L0 and m1 equal to the finest grid size, the query points are the finest cells’ centres and they cover [X_0, X_0+L_0) exactly once – one period, no repeated endpoint.

    Follows output_field() in output.h: the array starts at nodata and is combined with a min reduction, so a point no rank owns stays nodata and one located on several ranks resolves to the same value either way. Values are read as s[] rather than interpolated, matching profiles_scalar.h; away from the finest level that means the containing cell’s value, so a lattice finer than the local grid low-passes the field.

    h on a cell face is ambiguous for a z-dependent field – locate() picks one side. Offset by half a cell to sample cell centres.

    The caller allocates plane with m1*m2*len doubles, laid out as plane[(i*m2 + j)*len + k] for field k at lattice point (i,j). Returns the number of unfilled points, 0 for a complete plane.

    #if dimension != 3
      #error sample_scalar_plane() is 3D only
    #endif
    
    #include "spectra_utils.h"
    
    int sample_scalar_plane (scalar * list, double * plane, double h,
                             double xmin, double xmax,
                             double ymin, double ymax,
                             int m1, int m2)
    {
      int len = list_len (list), n = m1*m2*len;
      coord box[2] = {{xmin, ymin, h}, {xmax, ymax, h}};
      coord nsamples = {m1, m2, 1};
      for (int i = 0; i < n; i++)
        plane[i] = nodata;
    
      coord p;
      foreach_region (p, box, nsamples, reduction(min:plane[:n])){
        double * alias = plane; // so that qcc considers 'plane' a local variable
        int i = (p.x - box[0].x)/(box[1].x - box[0].x)*nsamples.x;
        int j = (p.y - box[0].y)/(box[1].y - box[0].y)*nsamples.y;
        int k = 0;
        for (scalar s in list)
          alias[(i*m2 + j)*len + k++] = s[];
      }
    
      int holes = 0;
      for (int i = 0; i < n; i++)
        if (plane[i] == nodata)
          holes++;
      return holes;
    }

    Foliating a stack of planes

    Sum, over nz planes evenly spaced across [hmin, hmax], the anomaly of each field to a caller-supplied per-slab mean – the z-integration a foliated spectrum needs (Poujade & Peybernes 2010, Soulard 2024). Slab heights follow the same snap_to_cell() convention as spectrum_scalar_stack() in spectra.h, and are returned through z so the caller can compute means at the matching heights; this function does not compute means itself, so it stays agnostic of whatever profile reduction the caller uses.

    means is laid out as means[iz*len + k] for field k at slab iz, the same convention spectrum_scalar_stack() uses for E. plane accumulates sum_iz (q(x,y,z_iz) - means[iz,k]), laid out as sample_scalar_plane()’s output. Returns the number of unfilled lattice points, summed over slabs.

    int sample_scalar_stack_sum (scalar * list, double * plane,
                                 const double * means, double * z,
                                 double hmin, double hmax, int nz,
                                 double xmin, double xmax,
                                 double ymin, double ymax,
                                 int m1, int m2)
    {
      int len = list_len (list), n = m1*m2*len;
      for (int i = 0; i < n; i++)
        plane[i] = 0.;
    
      double * slab = malloc (n*sizeof(double));
      int holes = 0;
      for (int iz = 0; iz < nz; iz++) {
        z[iz] = snap_to_cell (hmin + (hmax - hmin)*(iz + 0.5)/nz,
                              m1);
        holes += sample_scalar_plane (list, slab, z[iz],
                                      xmin, xmax, ymin, ymax, m1, m2);
        for (int i = 0; i < m1*m2; i++)
          for (int k = 0; k < len; k++)
            plane[i*len + k] += slab[i*len + k] - means[iz*len + k];
      }
      free (slab);
      return holes;
    }