Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-06-26 07:49:48

0001 /**
0002 qbnd_boundary_lookup_test.cc
0003 ===============================
0004 
0005 Regression test for qbnd::boundary_lookup with POINT texture filtering
0006 and manual wavelength-axis interpolation.
0007 
0008 Verifies:
0009   1. Row isolation — adjacent material/surface rows with distinct values
0010      return exact per-row data with no cross-row blending.
0011   2. Wavelength interpolation — lookup at a wavelength between two bins
0012      returns the correct linear interpolant.
0013   3. Boundary clamping — wavelengths outside the domain clamp correctly.
0014 
0015 CPU-only: compiled with MOCK_TEXTURE so tex2D is software nearest-neighbor.
0016 **/
0017 
0018 #include <cmath>
0019 #include <cstdio>
0020 #include <cstring>
0021 
0022 #define MOCK_TEXTURE
0023 
0024 #include "NP.hh"
0025 #include "s_mock_texture.h"
0026 #include "scuda.h"
0027 #include "squad.h"
0028 #include "sstate.h"
0029 
0030 MockTextureManager* MockTextureManager::INSTANCE = nullptr;
0031 
0032 #include "qbnd.h"
0033 
0034 static int g_fail = 0;
0035 
0036 static void check(const char* tag, bool cond)
0037 {
0038     printf("  %-60s %s\n", tag, cond ? "PASS" : "*** FAIL ***");
0039     if (!cond)
0040         g_fail++;
0041 }
0042 
0043 static bool feq(float a, float b, float tol = 1e-5f)
0044 {
0045     return fabsf(a - b) <= tol * (1.f + fabsf(b));
0046 }
0047 
0048 int main()
0049 {
0050     // Build a small boundary texture: 2 boundaries, 4 species, 2 groups, 4 wavelength bins.
0051     // Layout: shape (ni, nj, nk, nl, nm) = (2, 4, 2, 4, 4)
0052     //   ni=2  boundaries
0053     //   nj=4  OMAT/OSUR/ISUR/IMAT
0054     //   nk=2  property groups (k=0, k=1)
0055     //   nl=4  wavelength samples
0056     //   nm=4  float4 components
0057     const unsigned ni = 2, nj = 4, nk = 2, nl = 4, nm = 4;
0058     const unsigned ny = ni * nj * nk; // 16 rows
0059     const unsigned nx = nl;           // 4 wavelength bins
0060 
0061     NP*    bnd = NP::Make<float>(ni, nj, nk, nl, nm);
0062     float* vv = bnd->values<float>();
0063     memset(vv, 0, bnd->arr_bytes());
0064 
0065     // Fill each row with a distinct recognizable value.
0066     // Row iy gets float4(100*iy+0, 100*iy+1, 100*iy+2, 100*iy+3) at each wavelength bin,
0067     // EXCEPT we make the wavelength bins differ so we can test interpolation:
0068     //   bin ix gets an extra offset of ix*10 in the .x component.
0069     for (unsigned iy = 0; iy < ny; iy++)
0070     {
0071         for (unsigned ix = 0; ix < nx; ix++)
0072         {
0073             unsigned idx = (iy * nx + ix) * nm;
0074             vv[idx + 0] = float(100 * iy + ix * 10); // .x varies with wavelength bin
0075             vv[idx + 1] = float(100 * iy + 1);       // .y constant per row
0076             vv[idx + 2] = float(100 * iy + 2);       // .z constant per row
0077             vv[idx + 3] = float(100 * iy + 3);       // .w constant per row
0078         }
0079     }
0080 
0081     // Set domain metadata: wavelength 100..400 nm, step 100 nm (4 bins)
0082     float nm0 = 100.f;
0083     float nm_high = 400.f;
0084     float nm_step = 100.f;
0085     bnd->set_meta<float>("domain_low", nm0);
0086     bnd->set_meta<float>("domain_high", nm_high);
0087     bnd->set_meta<float>("domain_step", nm_step);
0088     bnd->set_meta<float>("domain_range", nm_high - nm0);
0089 
0090     // Register mock texture
0091     cudaTextureObject_t texObj = MockTextureManager::Add(bnd);
0092 
0093     // Set up metadata quad (normally uploaded by QTex)
0094     quad4 meta;
0095     memset(&meta, 0, sizeof(meta));
0096     meta.q0.u.x = nx;
0097     meta.q0.u.y = ny;
0098     meta.q1.f.x = nm0;
0099     meta.q1.f.z = nm_step;
0100 
0101     // Build qbnd struct
0102     qbnd qb;
0103     qb.boundary_tex = texObj;
0104     qb.boundary_meta = &meta;
0105 
0106     printf("qbnd_boundary_lookup_test\n");
0107 
0108     // --- Test 1: Row isolation ---
0109     // Look up boundary 0, OMAT (line=0), k=0 at bin-center wavelengths.
0110     // Row iy = _BOUNDARY_NUM_FLOAT4 * line + k = 2*0 + 0 = 0.
0111     // At wavelength = 100 nm (bin 0): expect .x = 100*0 + 0*10 = 0
0112     {
0113         float4 p = qb.boundary_lookup(100.f, 0, 0);
0114         check("row0 bin0: .x == 0", feq(p.x, 0.f));
0115         check("row0 bin0: .y == 1", feq(p.y, 1.f));
0116     }
0117 
0118     // Row iy = 2*0 + 1 = 1 (boundary 0, OMAT, k=1).
0119     // At wavelength = 100 nm: expect .x = 100*1 + 0*10 = 100
0120     {
0121         float4 p = qb.boundary_lookup(100.f, 0, 1);
0122         check("row1 bin0: .x == 100 (distinct from row0)", feq(p.x, 100.f));
0123         check("row1 bin0: .y == 101", feq(p.y, 101.f));
0124     }
0125 
0126     // Boundary 1, IMAT (line = 1*4+3 = 7), k=0 → row iy = 2*7+0 = 14.
0127     // At wavelength = 200 nm (bin 1): expect .x = 100*14 + 1*10 = 1410
0128     {
0129         float4 p = qb.boundary_lookup(200.f, 7, 0);
0130         check("row14 bin1: .x == 1410", feq(p.x, 1410.f));
0131         check("row14 bin1: .z == 1402", feq(p.z, 1402.f));
0132     }
0133 
0134     // --- Test 2: Wavelength interpolation ---
0135     // Look up row 0 at wavelength 150 nm (halfway between bin 0 and bin 1).
0136     // bin 0 .x = 0, bin 1 .x = 10. Expect lerp = 5.
0137     {
0138         float4 p = qb.boundary_lookup(150.f, 0, 0);
0139         check("row0 lerp 150nm: .x == 5 (midpoint)", feq(p.x, 5.f));
0140         check("row0 lerp 150nm: .y == 1 (constant)", feq(p.y, 1.f));
0141     }
0142 
0143     // 25% between bin 1 and bin 2: wavelength = 225 nm.
0144     // bin 1 .x = 10, bin 2 .x = 20. Expect lerp = 12.5.
0145     {
0146         float4 p = qb.boundary_lookup(225.f, 0, 0);
0147         check("row0 lerp 225nm: .x == 12.5", feq(p.x, 12.5f));
0148     }
0149 
0150     // --- Test 3: Boundary clamping ---
0151     // Wavelength below domain (50 nm) — should clamp to bin 0.
0152     {
0153         float4 p = qb.boundary_lookup(50.f, 0, 0);
0154         check("row0 clamp below: .x == 0 (bin 0)", feq(p.x, 0.f));
0155     }
0156 
0157     // Wavelength above domain (500 nm) — should clamp to last bin.
0158     {
0159         float4 p = qb.boundary_lookup(500.f, 0, 0);
0160         check("row0 clamp above: .x == 30 (bin 3)", feq(p.x, 30.f));
0161     }
0162 
0163     printf("qbnd_boundary_lookup_test: %s (%d failure%s)\n",
0164            g_fail == 0 ? "PASS" : "FAIL", g_fail, g_fail == 1 ? "" : "s");
0165     return g_fail == 0 ? 0 : 1;
0166 }