Fully filled THnSparse cannot do 3D projection

I am getting following error when i want to do 3D projection on fully filled THnSparse object

$ root m.C                                                                                                                                                                                                                                                                                           
root [0]                                                                                                                                                                                                                                                                                                                       
Processing m.C...                                                                                                                                                                                                                                                                                                              
Info in <TCanvas::MakeDefCanvas>:  created default TCanvas with name c1                                                                                                                                                                                                                                                        
/usr/include/c++/15/bits/stl_vector.h:1262: std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::operator[](size_type) [with _Tp = double; _Alloc = std::allocator<double>; reference = double&; size_type = long unsigned int]: Assertion '__n < this->size()' failed.

Macro is here

void m() {
  Int_t point[3] = {1, 1, 1};
  Int_t bins[3] = {1, 1, 2};
  Double_t mins[3] = {0, 0, 0};
  Double_t maxs[3] = {10, 10, 10};
  THnSparse *s = new THnSparseC("s", "sss", 3, bins, mins, maxs);
  s->SetBinContent(s->GetBin(point), 1);
  point[0] = 1;
  point[1] = 1;
  point[2] = 2;
  // comment this line and it will work ok.
  s->SetBinContent(s->GetBin(point), 1);
  s->Projection(0, 1, 2)->Draw();
}

ROOT Version: 6.34.06
Platform: linuxx8664gcc
Compiler: g++ (GCC) 15.0.1 20250228 (Red Hat 15.0.1-0)


With root 6.32.12 on Linux Mint I get the plot below and no errors:


In your case it could be some bug in the dev version, which may get sorted out, but if you need it now, try the latest stable version instead, or the newer one from 6.34 (08 now).

I can confirm that it is not crashing with v6.32.12, but as you can see in your image also bins are not drawn. When i fill one bin less i see following

With 2 bins filled it is empty.

When i define bins like this (i added one more bin in 3rd dim) and fill these two points it is fine

Int_t bins[3] = {1, 1, 3};

But then again when i fill last bin it is empty. Macro for last case is here

void m() {
  Int_t point[3] = {1, 1, 1};
  Int_t bins[3] = {1, 1, 3};
  Double_t mins[3] = {0, 0, 0};
  Double_t maxs[3] = {10, 10, 10};
  THnSparse *s = new THnSparseC("s", "sss", 3, bins, mins, maxs);
  s->SetBinContent(s->GetBin(point), 1);
  point[0] = 1;
  point[1] = 1;
  point[2] = 2;
  s->SetBinContent(s->GetBin(point), 1);
  point[2] = 3;
  // comment this line and it will work ok.
  s->SetBinContent(s->GetBin(point), 1);
  s->Projection(0, 1, 2)->Draw();
}

It appears that there’s a bug somewhere in the ROOT code. A developer can confirm and check :slight_smile:

If we draw the projected histogram with the option “colz”, we can see that the lowest bin content falls outside the colour scale, as the minimum is probably set to be exactly equal to the lowest value.
One workaround can be to set a minimum value to the projected histo (lower than the actual minimum of all the non-empty bin contents) to force the actual minimum to fall within the coloured bins. Based on your example:

{

  Int_t bins[3] = {1, 1, 3};
  Double_t mins[3] = {0, 0, 0};
  Double_t maxs[3] = {10, 10, 10};
  THnSparse *s = new THnSparseC("s", "sss", 3, bins, mins, maxs);

  Int_t point[3] = {1, 1, 1};

  // 1,1,1
  Int_t x = 7;
  s->SetBinContent(s->GetBin(point), x);

  // 1,1,2
  point[2] = 2;
  s->SetBinContent(s->GetBin(point), x+1);

  // 1,1,3
  point[2] = 3;
  s->SetBinContent(s->GetBin(point), x);

  auto p = s->Projection(0, 1, 2);
  p->SetMinimum(0);   // comment or uncomment this line to see the difference!
  p->Draw("colz");

  for (int i=0; i<3; ++i) {
    point[0] = 1;
    point[1] = 1;
    point[2] = i+1;
    cout << point[0]<<","<<point[1]<<","<<point[2]<<" (bin "<<s->GetBin(point) << ")";
    cout << " = " << s->GetBinContent(point) << endl;
  }

  // see https://root.cern/doc/v632/classTH1.html#ae8a3c167783475085587f08dcb59c63d
  cout << p->GetMinimum() << endl;
  cout << p->GetBinContent(p->GetMinimumBin()) << endl;
}

If we include/exclude the SetMinimum() line (I used zero, but you can get the real minimum from the last printed output – see the difference between the two printouts here: ROOT: TH1 Class Reference ), we get:

Works

Final macro works with v6.34.08

void m() {
  Int_t point[3] = {1, 1, 1};
  Int_t bins[3] = {1, 1, 3};
  Double_t mins[3] = {0, 0, 0};
  Double_t maxs[3] = {10, 10, 10};
  THnSparse *s = new THnSparseC("s", "sss", 3, bins, mins, maxs);
  s->SetBinContent(s->GetBin(point), 1);
  point[0] = 1;
  point[1] = 1;
  point[2] = 2;
  // comment this line and it will work ok.
  s->SetBinContent(s->GetBin(point), 1);
  point[2] = 3;
  // comment this line and it will work ok.
  s->SetBinContent(s->GetBin(point), 1);
  // s->Projection(0, 1, 2)->Draw();
  auto p = s->Projection(0, 1, 2);
  p->SetMinimum(0); // comment or uncomment this line to see the difference!
  p->Draw("colz");
}

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.