From ff9d65ffad6745355f8fd962feaa6e7bba594d4b Mon Sep 17 00:00:00 2001 From: Elvand Lie Nababan Date: Sun, 18 Oct 2026 17:57:25 +0700 Subject: [PATCH] [ntuple] Use stored bit width when recompressing merged pages Apply the source column's stored bit width before computing packed page sizes during recompression. This allows truncated and quantized float pages to be merged with a different output compression. Compare both fields against their stored source values in a regression. Assisted-by: codex:gpt-6.1-sol Assisted-by: claude-code:opus --- tree/ntuple/src/RNTupleMerger.cxx | 1 + tree/ntuple/test/ntuple_merger.cxx | 48 ++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/tree/ntuple/src/RNTupleMerger.cxx b/tree/ntuple/src/RNTupleMerger.cxx index b44247301b4af..08fd9aae66766 100644 --- a/tree/ntuple/src/RNTupleMerger.cxx +++ b/tree/ntuple/src/RNTupleMerger.cxx @@ -1248,6 +1248,7 @@ ROOT::RResult RNTupleMergeStrategyDefault::MergeCommonColumns(ROOT::Intern const auto srcColElement = column.fInMemoryType ? ROOT::Internal::GenerateColumnElement(*column.fInMemoryType, columnDesc.GetType()) : RColumnElementBase::Generate(columnDesc.GetType()); + srcColElement->SetBitsOnStorage(columnDesc.GetBitsOnStorage()); // Now get the pages for this column in this cluster const auto &pages = clusterDesc.GetPageRange(columnId); diff --git a/tree/ntuple/test/ntuple_merger.cxx b/tree/ntuple/test/ntuple_merger.cxx index 76912f77d9346..e3e5f2d29adbb 100644 --- a/tree/ntuple/test/ntuple_merger.cxx +++ b/tree/ntuple/test/ntuple_merger.cxx @@ -939,6 +939,54 @@ TEST(RNTupleMerger, ChangeCompression) EXPECT_TRUE(VerifyPageCompression(fileGuardOutUncomp.GetPath(), 0)); } +TEST(RNTupleMerger, ChangeCompressionLowPrecision) +{ + FileRaii input("test_ntuple_merge_changecomp_lowprecision_in.root"); + FileRaii output("test_ntuple_merge_changecomp_lowprecision_out.root"); + { + auto model = RNTupleModel::Create(); + auto truncated = std::make_unique>("truncated"); + truncated->SetTruncated(20); + model->AddField(std::move(truncated)); + auto quantized = std::make_unique>("quantized"); + quantized->SetQuantized(20, {0., 20.}); + model->AddField(std::move(quantized)); + RNTupleWriteOptions writeOptions; + writeOptions.SetCompression(505); + auto writer = RNTupleWriter::Recreate(std::move(model), "ntuple", input.GetPath(), writeOptions); + auto truncatedValue = writer->GetModel().GetDefaultEntry().GetPtr("truncated"); + auto quantizedValue = writer->GetModel().GetDefaultEntry().GetPtr("quantized"); + for (unsigned int i = 0; i < 1000; ++i) { + *truncatedValue = *quantizedValue = (i % 100) / 7.f; + writer->Fill(); + } + } + + auto source = RPageSource::Create("ntuple", input.GetPath()); + std::vector sources{source.get()}; + RNTupleWriteOptions writeOptions; + writeOptions.SetCompression(101); + auto destination = std::make_unique("ntuple", output.GetPath(), writeOptions); + RNTupleMerger merger{std::move(destination)}; + RNTupleMergeOptions mergeOptions; + mergeOptions.fCompressionSettings = 101; + auto result = merger.Merge(sources, mergeOptions); + ASSERT_TRUE(bool(result)) << result.GetError()->GetReport(); + + auto original = RNTupleReader::Open("ntuple", input.GetPath()); + auto merged = RNTupleReader::Open("ntuple", output.GetPath()); + ASSERT_EQ(original->GetNEntries(), merged->GetNEntries()); + for (const auto *name : {"truncated", "quantized"}) { + SCOPED_TRACE(name); + auto originalValue = original->GetView(name); + auto mergedValue = merged->GetView(name); + for (auto i : original->GetEntryRange()) { + SCOPED_TRACE(i); + EXPECT_EQ(originalValue(i), mergedValue(i)); + } + } +} + TEST(RNTupleMerger, ChangeCompressionMixed) { FileRaii fileGuard("test_ntuple_merge_changecomp_mixed_in.root");