Skip to content

Commit 556dba8

Browse files
committed
[ntuple] Add tests and a warning for projected Real32(Quant|Trunc)
Setting a projected field to quantized or truncated works and read proper data but it doesn't do what one would probably expect. The projected field can correctly read values from any fields that can be read as float, but the value range and bits of precision set on the projected field are silently ignored (since they only make sense for physical columns that are actually stored on disk). To reduce confusion, we now emit a warning if a user tries to call AddProjectedField with a quantized or truncated field.
1 parent 7c1f0a0 commit 556dba8

File tree

2 files changed

+187
-0
lines changed

2 files changed

+187
-0
lines changed

tree/ntuple/v7/src/RNTupleModel.cxx

+14
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,20 @@ ROOT::RResult<void>
5454
ROOT::Internal::RProjectedFields::EnsureValidMapping(const ROOT::RFieldBase *target, const FieldMap_t &fieldMap)
5555
{
5656
auto source = fieldMap.at(target);
57+
58+
if (!target->GetColumnRepresentatives()[0].empty()) {
59+
const auto representative = target->GetColumnRepresentatives()[0][0];
60+
// If the user is trying to add a projected field upon which they called SetTruncated() or SetQuantized(),
61+
// they probably have the wrong expectations about what should happen. Warn them about it.
62+
if (representative == ENTupleColumnType::kReal32Trunc || representative == ENTupleColumnType::kReal32Quant) {
63+
R__LOG_WARNING(ROOT::Internal::NTupleLog())
64+
<< "calling SetQuantized() or SetTruncated() on a projected field has no effect, as the on-disk "
65+
"representation of the value is decided by the projection source field. Reading back the field will "
66+
"yield the correct values, but the value range and bits of precision you set on the projected field "
67+
"will be ignored.";
68+
}
69+
}
70+
5771
const bool hasCompatibleStructure = (source->GetStructure() == target->GetStructure()) ||
5872
((source->GetStructure() == ROOT::ENTupleStructure::kCollection) &&
5973
dynamic_cast<const ROOT::RCardinalityField *>(target));

tree/ntuple/v7/test/ntuple_project.cxx

+173
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,176 @@ TEST(RNTupleProjection, CatchReaderWithProjectedFields)
183183
EXPECT_THAT(err.what(), testing::HasSubstr("model has projected fields"));
184184
}
185185
}
186+
187+
TEST(RNTupleProjection, AliasQuantDiffPrec)
188+
{
189+
// Try creating alias columns of Real32Quant columns with a different precision
190+
191+
FileRaii fileGuard("test_ntuple_projection_quant_diff_prec.root");
192+
193+
auto model = RNTupleModel::Create();
194+
195+
auto field = std::make_unique<RField<float>>("q");
196+
field->SetQuantized(0, 1, 20); // Set quantized with 20 bits of precision
197+
model->AddField(std::move(field));
198+
199+
auto modelRead = model->Clone();
200+
201+
{
202+
ROOT::TestSupport::CheckDiagsRAII diags;
203+
diags.requiredDiag(kWarning, "RProjectedFields", "on a projected field has no effect", false);
204+
205+
auto projField = std::make_unique<RField<float>>("projq");
206+
projField->SetQuantized(0, 1, 30); // Set quantized with 30 bits of precision
207+
model->AddProjectedField(std::move(projField), [](const auto &) { return "q"; });
208+
}
209+
210+
{
211+
auto writer = RNTupleWriter::Recreate(std::move(model), "ntpl", fileGuard.GetPath());
212+
auto pq = writer->GetModel().GetDefaultEntry().GetPtr<float>("q");
213+
for (int i = 0; i < 10; ++i) {
214+
*pq = i * 0.05f;
215+
writer->Fill();
216+
}
217+
}
218+
219+
auto reader = RNTupleReader::Open("ntpl", fileGuard.GetPath());
220+
auto vq = reader->GetView<float>("q");
221+
auto vpq = reader->GetView<float>("projq");
222+
for (int i = 0; i < 10; ++i) {
223+
EXPECT_FLOAT_EQ(vq(i), vpq(i));
224+
}
225+
}
226+
227+
TEST(RNTupleProjection, AliasQuantDiffRange)
228+
{
229+
// Try creating alias columns of Real32Quant columns with a different range
230+
231+
FileRaii fileGuard("test_ntuple_projection_quant_diff_range.root");
232+
233+
auto model = RNTupleModel::Create();
234+
235+
auto field = std::make_unique<RField<float>>("q");
236+
field->SetQuantized(0, 1, 20);
237+
model->AddField(std::move(field));
238+
239+
auto modelRead = model->Clone();
240+
241+
{
242+
ROOT::TestSupport::CheckDiagsRAII diags;
243+
diags.requiredDiag(kWarning, "RProjectedFields", "on a projected field has no effect", false);
244+
245+
auto projField = std::make_unique<RField<float>>("projq");
246+
projField->SetQuantized(0, 2, 20);
247+
model->AddProjectedField(std::move(projField), [](const auto &) { return "q"; });
248+
}
249+
250+
{
251+
auto writer = RNTupleWriter::Recreate(std::move(model), "ntpl", fileGuard.GetPath());
252+
auto pq = writer->GetModel().GetDefaultEntry().GetPtr<float>("q");
253+
for (int i = 0; i < 10; ++i) {
254+
*pq = i * 0.05f;
255+
writer->Fill();
256+
}
257+
}
258+
259+
auto reader = RNTupleReader::Open("ntpl", fileGuard.GetPath());
260+
auto pq = reader->GetModel().GetDefaultEntry().GetPtr<float>("q");
261+
auto ppq = reader->GetModel().GetDefaultEntry().GetPtr<float>("projq");
262+
for (int i = 0; i < 10; ++i) {
263+
reader->LoadEntry(i);
264+
EXPECT_FLOAT_EQ(*pq, *ppq);
265+
}
266+
}
267+
268+
TEST(RNTupleProjection, AliasTruncDiffPrec)
269+
{
270+
// Try creating alias columns of Real32Trunc columns with a different precision
271+
272+
FileRaii fileGuard("test_ntuple_projection_quant_diff_prec.root");
273+
274+
auto model = RNTupleModel::Create();
275+
276+
auto field = std::make_unique<RField<float>>("q");
277+
field->SetTruncated(20);
278+
model->AddField(std::move(field));
279+
280+
auto modelRead = model->Clone();
281+
282+
{
283+
ROOT::TestSupport::CheckDiagsRAII diags;
284+
diags.requiredDiag(kWarning, "RProjectedFields", "on a projected field has no effect", false);
285+
286+
auto projField = std::make_unique<RField<float>>("projq");
287+
projField->SetTruncated(20);
288+
model->AddProjectedField(std::move(projField), [](const auto &) { return "q"; });
289+
}
290+
291+
{
292+
auto writer = RNTupleWriter::Recreate(std::move(model), "ntpl", fileGuard.GetPath());
293+
auto pq = writer->GetModel().GetDefaultEntry().GetPtr<float>("q");
294+
for (int i = 0; i < 10; ++i) {
295+
*pq = i * 0.05f;
296+
writer->Fill();
297+
}
298+
}
299+
300+
auto reader = RNTupleReader::Open("ntpl", fileGuard.GetPath());
301+
auto vq = reader->GetView<float>("q");
302+
auto vpq = reader->GetView<float>("projq");
303+
for (int i = 0; i < 10; ++i) {
304+
EXPECT_FLOAT_EQ(vq(i), vpq(i));
305+
}
306+
}
307+
308+
TEST(RNTupleProjection, AliasTruncQuantProj)
309+
{
310+
// Try creating alias columns mixing Real32Trunc and Real32Quant columns
311+
312+
FileRaii fileGuard("test_ntuple_projection_trunc_quant.root");
313+
314+
auto model = RNTupleModel::Create();
315+
316+
auto fTrunc = std::make_unique<RField<float>>("trunc");
317+
fTrunc->SetTruncated(20);
318+
model->AddField(std::move(fTrunc));
319+
auto fQuant = std::make_unique<RField<float>>("quant");
320+
fQuant->SetQuantized(-1, 1, 30);
321+
model->AddField(std::move(fQuant));
322+
323+
auto modelRead = model->Clone();
324+
325+
{
326+
ROOT::TestSupport::CheckDiagsRAII diags;
327+
diags.requiredDiag(kWarning, "RProjectedFields", "on a projected field has no effect", false);
328+
329+
auto projTruncToQuant = std::make_unique<RField<float>>("truncToQuant");
330+
projTruncToQuant->SetQuantized(0, 10, 20);
331+
model->AddProjectedField(std::move(projTruncToQuant), [](const auto &) { return "trunc"; });
332+
333+
auto projQuantToTrunc = std::make_unique<RField<float>>("quantToTrunc");
334+
projQuantToTrunc->SetTruncated(10);
335+
model->AddProjectedField(std::move(projQuantToTrunc), [](const auto &) { return "quant"; });
336+
}
337+
338+
{
339+
auto writer = RNTupleWriter::Recreate(std::move(model), "ntpl", fileGuard.GetPath());
340+
auto pQuant = writer->GetModel().GetDefaultEntry().GetPtr<float>("quant");
341+
auto pTrunc = writer->GetModel().GetDefaultEntry().GetPtr<float>("trunc");
342+
for (int i = 0; i < 10; ++i) {
343+
*pQuant = i * 0.05f;
344+
*pTrunc = i * 2.f;
345+
writer->Fill();
346+
}
347+
}
348+
349+
auto reader = RNTupleReader::Open("ntpl", fileGuard.GetPath());
350+
auto vq = reader->GetView<double>("quant");
351+
auto vqt = reader->GetView<double>("quantToTrunc");
352+
auto vt = reader->GetView<double>("trunc");
353+
auto vtq = reader->GetView<double>("truncToQuant");
354+
for (int i = 0; i < 10; ++i) {
355+
EXPECT_FLOAT_EQ(vq(i), vqt(i));
356+
EXPECT_FLOAT_EQ(vt(i), vtq(i));
357+
}
358+
}

0 commit comments

Comments
 (0)