sandbox/acastillo/output_fields/spectra/spectra_sample_restricted.h
Sampling a plane or slab stack at a coarser level than the local grid
Level-based twins of spectra_sample.h’s
sample_scalar_plane() and
sample_scalar_stack_sum(). That header samples through
locate(), which always descends to the finest cell under
the query point – correct when the lattice is finer than or equal to the
local grid, but not when the lattice is coarser (the diagnostic level)
while part of the domain is refined further (the interface). There a
query point lands on a child leaf, not the coarse-cell average, exactly
the bias profiles_slab_restrict.h fixes for slab
profiles.
Both functions here apply the same fix: like
profile_scalar_slab(), restrict the sampled fields first,
then bin at exactly slablevel with
foreach_level. m1 = m2 = 2^slablevel – the
lattice is the level, not a caller-chosen resolution, since
foreach_level visits cells, not points.
slablevel has no default, for the same reason
profiles_slab_restrict.h gives none:
foreach_level(l) visited where the tree is coarser than
l would come back empty, indistinguishable from a hole.
Shares snap_to_cell() with spectra_sample.h via spectra_utils.h, so a test can include both
headers without a duplicate-symbol error.
#if dimension != 3
#error sample_scalar_plane_restrict() is 3D only
#endif
#include "spectra_utils.h"Sampling a plane at a coarser level than the local grid
Restrict the sampled fields first, then bin at exactly
slablevel with foreach_level, filtered to the
row of cells whose z contains
h. The caller allocates plane with
m1*m2*len doubles, laid out as
sample_scalar_plane()’s output. Returns the number of
unfilled points, 0 for a complete plane.
int sample_scalar_plane_restrict (scalar * list, double * plane, double h,
int slablevel)
{
int m1 = 1 << slablevel, m2 = m1, len = list_len (list), n = m1*m2*len;
for (int i = 0; i < n; i++)
plane[i] = nodata;
restriction (list);
double del = L0/m1;
foreach_level (slablevel, reduction(min:plane[:n])) {
if (fabs (z - h) > Delta/2.) continue;
int i = (x - X0)/del, j = (y - Y0)/del;
if (i < 0) i = 0; if (i >= m1) i = m1 - 1;
if (j < 0) j = 0; if (j >= m2) j = m2 - 1;
double * alias = plane;
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 at a coarser level than the local grid
One restriction() and one
foreach_level(slablevel) pass over the whole box, every
cell binned by its own z into iz (alongside
x,y into i,j) rather than one
locate()-based plane per slab
(sample_scalar_stack_sum()’s approach).
hmin/hmax must align to the
slablevel lattice in z (nz slabs
spanning a whole multiple of L0/2^slablevel), so each
iz is exactly one row of cells – the same alignment
profile_scalar_slab() requires of its own
hmin/hmax/n.
means is laid out as means[iz*len + k] for
field k at slab iz, the same convention
sample_scalar_stack_sum() uses. plane
accumulates sum_iz (q(x,y,z_iz) - means[iz,k]), laid out as
sample_scalar_plane_restrict()’s output.
m1 = m2 = 2^slablevel. Returns the count of
(i,j,iz) cells with no contributing tree cell, summed over
slabs – like sample_scalar_stack_sum(), this can’t
distinguish an empty point from one whose contributions summed to
exactly zero.
int sample_scalar_stack_sum_restrict (scalar * list, double * plane,
const double * means, double * zout,
double hmin, double hmax, int nz,
int slablevel)
{
int m1 = 1 << slablevel, m2 = m1, len = list_len (list), n = m1*m2*len;
for (int i = 0; i < n; i++)
plane[i] = 0.;
for (int iz = 0; iz < nz; iz++)
zout[iz] = snap_to_cell (hmin + (hmax - hmin)*(iz + 0.5)/nz,
m1);
restriction (list);
double del = L0/m1;
double dz = nz > 1 ? (hmax - hmin)/nz : L0;
int * count = malloc (nz*sizeof(int));
for (int iz = 0; iz < nz; iz++) count[iz] = 0;
foreach_level (slablevel, reduction(+:plane[:n]) reduction(+:count[:nz])) {
int iz = nz > 1 ? (int)floor ((z - hmin)/dz) : 0;
if (iz < 0 || iz >= nz) continue;
int i = (x - X0)/del, j = (y - Y0)/del;
if (i < 0) i = 0; if (i >= m1) i = m1 - 1;
if (j < 0) j = 0; if (j >= m2) j = m2 - 1;
int idx = i*m2 + j;
count[iz]++;
double * alias = plane;
int k = 0;
for (scalar s in list)
alias[idx*len + k] += s[] - means[iz*len + k], k++;
}
int holes = 0;
for (int iz = 0; iz < nz; iz++)
if (count[iz] < m1*m2)
holes += m1*m2 - count[iz];
free (count);
return holes;
}