geopro/tests/data/test_streaming_builder.cpp

173 lines
6.0 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "data/StreamingVolumeBuilder.hpp"
#include <gtest/gtest.h>
#include <cstdint>
#include <filesystem>
#include <fstream>
#include <string>
#include <vector>
#include "core/algo/GprVolumeBuilder.hpp"
#include "core/algo/IInterpolator.hpp" // GridSpec
#include "data/store/ChunkedVolumeStore.hpp"
#include "io/gpr/GprSurveyAssembler.hpp"
using namespace geopro::data;
namespace {
void writeText(const std::string& p, const std::string& s) {
std::ofstream f(p);
f << s;
}
void writeI16(const std::string& p, const std::vector<std::int16_t>& v) {
std::ofstream f(p, std::ios::binary);
f.write(reinterpret_cast<const char*>(v.data()),
static_cast<std::streamsize>(v.size() * sizeof(std::int16_t)));
}
std::string tmpDir(const char* name) {
return (std::filesystem::temp_directory_path() / name).string();
}
// 写两通道 .iprb/.ord(+.iprh) 到 dir每通道 nTraces 道、nSamples 采样。
// 值随 (channel,trace,sample) 变化,确保块内容彼此不同(真正区分逐块对拍)。
// 返回各通道 .iprb 路径 + .ord 路径。
struct SurveyFiles {
std::vector<std::string> iprb;
std::string ord;
};
SurveyFiles makeTwoChannelSurveyFiles(const std::string& dir, int nTraces,
int nSamples) {
std::filesystem::create_directories(dir);
const int lastTrace = nTraces - 1;
const std::string hdr =
"SAMPLES: " + std::to_string(nSamples) +
"\nLAST TRACE: " + std::to_string(lastTrace) +
"\nCHANNELS: 2\nTIMEWINDOW: 4.0\n"
"SOIL VELOCITY: 100.000000\nDISTANCE INTERVAL: 0.05\n";
auto gen = [&](int chan) {
std::vector<std::int16_t> v(static_cast<std::size_t>(nTraces) * nSamples);
for (int t = 0; t < nTraces; ++t)
for (int s = 0; s < nSamples; ++s)
v[static_cast<std::size_t>(t) * nSamples + s] = static_cast<std::int16_t>(
(chan * 1000 + t * 7 + s * 3) % 251 - 50); // 含负值
return v;
};
writeText(dir + "/A.iprh", hdr);
writeI16(dir + "/A.iprb", gen(0));
writeText(dir + "/B.iprh", hdr);
writeI16(dir + "/B.iprb", gen(1));
// A->横偏 1.0、B->横偏 0.0B 的 Y 更小,验证 Y 升序重排不影响一致性)。
writeText(dir + "/x.ord", "0 1.0 -1.5 1\n1 0.0 -1.5 1\n");
return {{dir + "/A.iprb", dir + "/B.iprb"}, dir + "/x.ord"};
}
geopro::core::GridSpec makeSpec() {
geopro::core::GridSpec spec{};
spec.nx = 200; // > 128 → sliceXBricks=2 时跨多个 slab
spec.ny = 5;
spec.nz = 8;
spec.ox = 0.0;
spec.oy = 0.0;
spec.oz = 0.0;
spec.dx = 0.05; // 与 survey.dx 同步,使网格 X 对齐道
spec.dy = 0.25;
spec.dz = 0.2;
spec.power = 2.0;
spec.maxDist = 2.0;
return spec;
}
} // namespace
// 核心验收:流式逐 slab 建体 vs 非流式整卷 buildGprVolume+write逐块 + meta 一致。
TEST(StreamingVolumeBuilder, MatchesNonStreaming) {
const std::string srcDir = tmpDir("svb_src");
std::filesystem::remove_all(srcDir);
auto files = makeTwoChannelSurveyFiles(srcDir, /*nTraces*/ 250, /*nSamples*/ 8);
const auto spec = makeSpec();
const int brick = 64;
const std::string dirA = tmpDir("svb_nsA");
const std::string dirB = tmpDir("svb_strB");
std::filesystem::remove_all(dirA);
std::filesystem::remove_all(dirB);
// 金标准:全装配 → buildGprVolume → write。
auto full = geopro::io::gpr::assembleGprSurvey(files.iprb, files.ord);
auto built = geopro::core::buildGprVolume(full, spec);
ChunkedVolumeStore::write(dirA, built, brick);
// 流式sliceXBricks=2 强制多 slab。
buildGprVolumeStreaming(files.iprb, files.ord, spec, dirB, /*sliceXBricks*/ 2);
ChunkedVolumeStore A(dirA), B(dirB);
// 全 meta 字段一致。
EXPECT_EQ(B.meta().nx, A.meta().nx);
EXPECT_EQ(B.meta().ny, A.meta().ny);
EXPECT_EQ(B.meta().nz, A.meta().nz);
EXPECT_EQ(B.meta().brick, A.meta().brick);
EXPECT_EQ(B.meta().origin, A.meta().origin);
EXPECT_EQ(B.meta().spacing, A.meta().spacing);
EXPECT_EQ(B.meta().quant.scale, A.meta().quant.scale);
EXPECT_EQ(B.meta().quant.offset, A.meta().quant.offset);
EXPECT_EQ(B.meta().vminPhys, A.meta().vminPhys);
EXPECT_EQ(B.meta().vmaxPhys, A.meta().vmaxPhys);
// 逐 brick 完全一致。
const int bX = A.bricksX(), bY = A.bricksY(), bZ = A.bricksZ();
EXPECT_EQ(B.bricksX(), bX);
EXPECT_EQ(B.bricksY(), bY);
EXPECT_EQ(B.bricksZ(), bZ);
for (int bz = 0; bz < bZ; ++bz)
for (int by = 0; by < bY; ++by)
for (int bx = 0; bx < bX; ++bx)
EXPECT_EQ(A.readBrick(bx, by, bz), B.readBrick(bx, by, bz))
<< "brick mismatch at " << bx << "," << by << "," << bz;
std::filesystem::remove_all(srcDir);
std::filesystem::remove_all(dirA);
std::filesystem::remove_all(dirB);
}
// 不同 sliceXBricks 都与非流式一致slab 边界划分不影响结果)。
TEST(StreamingVolumeBuilder, SliceCountInvariant) {
const std::string srcDir = tmpDir("svb_inv_src");
std::filesystem::remove_all(srcDir);
auto files = makeTwoChannelSurveyFiles(srcDir, /*nTraces*/ 250, /*nSamples*/ 8);
const auto spec = makeSpec();
const std::string dirA = tmpDir("svb_inv_A");
std::filesystem::remove_all(dirA);
auto full = geopro::io::gpr::assembleGprSurvey(files.iprb, files.ord);
auto built = geopro::core::buildGprVolume(full, spec);
ChunkedVolumeStore::write(dirA, built, 64);
ChunkedVolumeStore A(dirA);
for (int slice : {1, 3, 8}) {
const std::string dirB = tmpDir("svb_inv_B");
std::filesystem::remove_all(dirB);
buildGprVolumeStreaming(files.iprb, files.ord, spec, dirB, slice);
ChunkedVolumeStore B(dirB);
EXPECT_EQ(B.meta().quant.scale, A.meta().quant.scale) << "slice=" << slice;
EXPECT_EQ(B.meta().vminPhys, A.meta().vminPhys) << "slice=" << slice;
for (int bz = 0; bz < A.bricksZ(); ++bz)
for (int by = 0; by < A.bricksY(); ++by)
for (int bx = 0; bx < A.bricksX(); ++bx)
EXPECT_EQ(A.readBrick(bx, by, bz), B.readBrick(bx, by, bz))
<< "slice=" << slice << " brick " << bx << "," << by << "," << bz;
std::filesystem::remove_all(dirB);
}
std::filesystem::remove_all(srcDir);
std::filesystem::remove_all(dirA);
}