Problems reading unsigned char from a tree

I have a tree with unsigned char something[2304]. When I read this tree with pyroot, it thinks that the something is an str() of a null size. I can call TTree.Scan(“something”) and it shows the values of it properly. So it seems that pyroot interprets arrays of chars in a tree in a wrong way. Can you help?

Hi,

is the something[2304] part of a struct or class, or is the size only defined in the TBranch? The question is mainly whether the “2304” part is known to CINT, for PyROOT to pick up. It is for structs/classes.

There’s an example $ROOTSYS/tutorials/staff.py that uses Char_t (not unsigned, but should be the same thing if it is non-const), where this is the case. Is your code similar?

In the converters there’s a variety of paths and choices have been made differently for char*, unsigned char*, and both their const versions. It tries to catch the most common use cases for the dual-purposes that these types have in C(++): strings and byte-buffers.

If the staff.py example does not help, can you send a small test file that I can run and figure out if this can be another special-cased path? Even if the general converter may not make the assumption that it is a buffer (rather than string) in this case, highly likely it is fine to make this assumption when coming in from a TTree.

Otherwise, the best bet for a work-around would be to use an array from module array and SetBranchAddress().

Thanks,
Wim

In a moment I will look into staff.py, but for now, my code is like that. This is creation of the Tree in C++:

	tcal->Branch("cpu_packet",&packet,"header/I:evt_number/I:time/I:pkt_type/s:datalength/i");
	TBranch *clkb_branch = tcal->Branch("payload_clkb_event",NULL,"pkt_number/i:gtu_trigger/i:gps_data[512]/b:live_time/i:dead_time[3]/b:crc/i");
	TBranch *ccb_branch = tcal->Branch("payload_ccb_event",NULL,"msg_type/b:device_id/b:trigger_time/i");
	TBranch *pdm_branch = tcal->Branch("payload_pdm_event",NULL,"event_header[36]/b:event_summary[216]/b");

	unsigned char *frame_header[128], *pcd[128], *kid[128];

	tcal->Branch("frame_header",frame_header,"frame_header[128][34]/b");
	tcal->Branch("photon_count_data",pcd,"photon_count_data[128][2304]/b");
	tcal->Branch("ki_data",kid,"ki_data[128][324]/b");
	unsigned short wpe[128][2304];

	TBranch *workaround_branch = tcal->Branch("wpe", wpe, "wpe[128][2304]/s");

	while(fread_euso_cpu_pkt((FILE*)f, &packet))
	{
		struct payload_full_event *p = (payload_full_event*)packet.payload;
		clkb_branch->SetAddress(p->clkb_event);
		ccb_branch->SetAddress(p->ccb_event);
		pdm_branch->SetAddress(&p->ccb_event->pdm_event[0]);
//		gtu_branch->SetAddress(&p->ccb_event->pdm_event[0].frame[0]);
		for(int i=0; i<128; i++)
		{
			frame_header[i]=p->ccb_event->pdm_event[0].frame[i].frame_header;
			pcd[i]=p->ccb_event->pdm_event[0].frame[i].photon_count_data;
			kid[i]=p->ccb_event->pdm_event[0].frame[i].ki_data;
			for(int j=0; j<2304; ++j)
			{
				wpe[i][j] = p->ccb_event->pdm_event[0].frame[i].photon_count_data[j];
//				cout << (int)wpe[i][j] << endl;
			}
		}
		tcal->Fill();
	}

I added this long part also to point out, that when I change the “workaround” variable wpe to unsigned char, it behaves as bad as photon_count_data in python - it is an empty str().

From the python side I think no code is needed - for test purposes I also just opened the file in ipython, drawn the uchar fields successfully, but tcal.photon_count_data[something] was returning only empty str() almost all the time.

I attach C file to generate the TTree and a python code that attempts to read it but crashes when trying to print char or unsigned char (properly diplays int and char, uchar and int are arrays of the same size filled in the same way).
test.py (247 Bytes)
fill.C (514 Bytes)

Hi,

just checked in some code (v5-34-00-patches) that may be a decent compromise: the returned value is still a string, but with the size given, it will make sure that the string is of that size, so no truncation if the data inside the string contains a \0.

Note that the string is read-only (as all python strings are).

There’s still the problem of accessing the 2D array as 1D, but I don’t have a good solution for that (w/o reworking much more code), as python has no standard module offering more than 1D arrays.

Cheers,
Wim

I cannot check it right now easly, it seems. PyRoot with cling is incompatible with earlier scripts. When I try to make TTree::GetEntry(1), I get

TypeError: Int_t TTree::GetEntry(Long64_t entry = 0, Int_t getall = 0) =>
    could not convert argument 1 (void/unknown arguments can't be set)

I hope this is a simple bug, not a general change that would require updating all the scripts…

As for the 2D arrays. I understand there is no official standard in python, but perhaps ROOT could decide on numpy? It is an unofficial standard, I think, and is probably the best choice. I, personally, always use PyROOT with numpy.

Hi,

PyROOT in trunk (i.e. w/ Cling) is not yet feature-complete; will get there, but is taking some time and effort.

A dependency on numpy would be too much to maintain (the various python versions, the various ways people install them, the various distribution systems, and the 3 major platforms is already rather taxing). However, if a minimum requirement of p2.6 (technically, PyROOT still supports python all the way down to p2.2) would be acceptable, then the buffer interface of numpy could be supported, which should allow an easy conversion on the python side (with the actual conversion/copying in C, and hence it’d be both clean and fast).

Cheers,
Wim

[quote=“wlav”]Hi,

PyROOT in trunk (i.e. w/ Cling) is not yet feature-complete; will get there, but is taking some time and effort.
[/quote]

OK, so for now I stick with numpy and setbranchaddress…

[quote]
A dependency on numpy would be too much to maintain (the various python versions, the various ways people install them, the various distribution systems, and the 3 major platforms is already rather taxing). However, if a minimum requirement of p2.6 (technically, PyROOT still supports python all the way down to p2.2) would be acceptable, then the buffer interface of numpy could be supported, which should allow an easy conversion on the python side (with the actual conversion/copying in C, and hence it’d be both clean and fast).

Cheers,
Wim[/quote]

I don’t know how about ROOT policy, but I assume that most if not all people using pyroot use rather new versions of python, so that souns resonable…