-
Notifications
You must be signed in to change notification settings - Fork 105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implementation of the Tprev/Tnext task. #892
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your PR.
Besides some minor issues I mention below, from my understanding by reading the code, some values of tprev and tnext are reserved for special meanings. So if the value is equal to 5e8, it means something. If the value is equal to 5e7, it means other things. I'm not saying this is the wrong way but clearly there is a better way to do this. For example, you could use an enumerator to indicate the validity of the value:
enum class TprevTnextStatus
{
valid,
ms_hit,
one_hit,
bad_record
};
and then store this enumerator with your tprev or tnext values.
r3bbase/R3BTprevTnext.cxx
Outdated
R3BTprevTnext::R3BTprevTnext() | ||
: R3BTprevTnext("R3B Tprev/Tnext", 1) | ||
{ | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How could this pass our clang-format checking? @klenze @jose-luis-rs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the problem is the lack of a semicolon in the ClassImp macro line.
For clang-format, this does not look like a finished statement.
So whatever comes next must be part of that statement and thus gets indented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clang-format-check --fix-all did it, so...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May be put the ClassImp at the last line of the source file? This is how we usually do it.
r3bbase/R3BTprevTnext.cxx
Outdated
: FairTask(name, iVerbose) | ||
, fMSOffsetPar(nullptr) | ||
, fTprev(0.0) | ||
, fTnext(0.0) | ||
, fSamplerMapped(nullptr) | ||
, fSamplerMSMapped(nullptr) | ||
, fR3BEventHeader(nullptr) | ||
, fTpat(1024.0) | ||
, delta_clk(1.0) | ||
{ | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As suggested in the last PR, please use the inline-initialization format.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I agree with @YanzhaoW in principle, I would also like to point out that this is among the bad practices used in the ./template/ directory. I think it would be worthwhile to clean that up, then we are in a much better position to display rightful indignation if PRs still use that :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the inline initialization here only changing the comma position to the end of the lines instead of the beginning?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could see the correct way here (second one is preferred). It's very simple. You just need to define the default values inside the class definition. ;D
r3bbase/R3BTprevTnext.cxx
Outdated
{ | ||
} | ||
|
||
R3BTprevTnext::~R3BTprevTnext() { LOG(info) << "R3BTprevTnext: Delete instance"; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need a log for the destructor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. We also do not need a destructor. But again, this should be fixed in ./template/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the code needs a lot of updates
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can remove this destructor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also don't forget to remove copy & move constructor and assignmentl. See rule of 5.
r3bbase/R3BTprevTnext.cxx
Outdated
fR3BEventHeader->SetTprev(fTprev); | ||
fR3BEventHeader->SetTnext(fTnext); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit unconventional. We usually don't change the data read from the TTree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is probably the least painful way to go about it.
IMHO it makes much more sense to store these values in the event header than to create yet another TCA containing just a single entry of yet another data class, which everyone would have to fetch from FRM separately.
The other alternative would be to squeeze this code into whatever fills the rest of the event header (probably in r3bsource), which would also be messy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@klenze made his point here
r3bbase/R3BTprevTnext.cxx
Outdated
{ | ||
fTprev = tpn * 10.0; // in ns |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the reason behind this magic number 10.0?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. It's correct
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The trloii sampler counts clock cycles of a 100MHz clock.
That means that one clock cycle is 10ns.
(We have discussed using a uniform unit system before, but I don't think this is the correct place to start with that.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again @klenze explained it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case, maybe define a named constant on the top:
constexpr auto CLOCK_PERIOD = 10.; // ns
And then use this variable in the code:
fTprev = tpn * CLOCK_PERIOD;
fTprev = 1e9; // Initialization with a different value after the first tests | ||
fTnext = 1e9; // If you have this value in your tprev/tnext is because the previous or next hit is inside the | ||
// delta_clk range but is not a MS | ||
for (Int_t i = 0; i < sampHits; ++i) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe put this for-loop into another member function and give it a nice name indicating the meaning?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The question which values we should use to indicate invalid time values deserves some consideration.
I think people will mostly use Tprev and Tnext in cuts where they select events which are isolated:
if (Tprev>1000 && Tnext > 1000) { // Fill your histograms or whatever }
The default values you use for ((sampHits < 1) || (sampmsHits != 1))
are nice invalid values which will clearly indicate an unsafe event.
However, all of your other failure values are big positive numbers, claiming that there is no event for at least half a second around the one in question. I think this is overly optimistic.
For an initial value of fTPrev, you could remember the value fLastWrts=GetWRTS()-10*uint64_t(tpn)
(with your sign convention) of the last Exec call in a member variable. Then your fTPrev simply is GetWrts()-fLastWrts()
. (If fLastWrts has never been set, just invalidate fTPrev by setting it negative.)
Your fTnext is more troublesome, because with a single pass over the tree you can't know what comes next. A defensible value would be (offset-1)*10.0. After all, it took at least that long between the trigger which caused the readout to hit the sampler and the sampler actually bank switching (or whatever samplers do) for the readout.
Are you aware that the sampler clock wraps around every 43 seconds, and are ok with your difference values being widely off whenever that happens?
r3bbase/R3BTprevTnext.cxx
Outdated
R3BTprevTnext::R3BTprevTnext() | ||
: R3BTprevTnext("R3B Tprev/Tnext", 1) | ||
{ | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the problem is the lack of a semicolon in the ClassImp macro line.
For clang-format, this does not look like a finished statement.
So whatever comes next must be part of that statement and thus gets indented.
r3bbase/R3BTprevTnext.cxx
Outdated
: FairTask(name, iVerbose) | ||
, fMSOffsetPar(nullptr) | ||
, fTprev(0.0) | ||
, fTnext(0.0) | ||
, fSamplerMapped(nullptr) | ||
, fSamplerMSMapped(nullptr) | ||
, fR3BEventHeader(nullptr) | ||
, fTpat(1024.0) | ||
, delta_clk(1.0) | ||
{ | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I agree with @YanzhaoW in principle, I would also like to point out that this is among the bad practices used in the ./template/ directory. I think it would be worthwhile to clean that up, then we are in a much better position to display rightful indignation if PRs still use that :-)
r3bbase/R3BTprevTnext.cxx
Outdated
{ | ||
} | ||
|
||
R3BTprevTnext::~R3BTprevTnext() { LOG(info) << "R3BTprevTnext: Delete instance"; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. We also do not need a destructor. But again, this should be fixed in ./template/
r3bbase/R3BTprevTnext.cxx
Outdated
{ | ||
|
||
fTprev = -1.0e3; // Initializaton of Tprev and Tnext | ||
fTnext = -1.0e3; // If in the end you have this value is because this is an offspill or SAMP and/or SAMPMS are not |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should be local variables.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right...
...again
r3bbase/R3BTprevTnext.cxx
Outdated
fR3BEventHeader->SetTprev(fTprev); | ||
fR3BEventHeader->SetTnext(fTnext); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is probably the least painful way to go about it.
IMHO it makes much more sense to store these values in the event header than to create yet another TCA containing just a single entry of yet another data class, which everyone would have to fetch from FRM separately.
The other alternative would be to squeeze this code into whatever fills the rest of the event header (probably in r3bsource), which would also be messy.
r3bbase/R3BTprevTnext.cxx
Outdated
{ | ||
fTprev = tpn * 10.0; // in ns |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The trloii sampler counts clock cycles of a 100MHz clock.
That means that one clock cycle is 10ns.
(We have discussed using a uniform unit system before, but I don't think this is the correct place to start with that.)
fTnext = 5e8; // If your Tnext==5e8, your last hit is MS | ||
break; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I am not sure that the above code is correct as is, I am sure that the code could be simplified.
First, initialize both fTnext and fTprev to invalid. bool sawMS=false;
Then, loop.
Calculate auto tpn = SAMPTime - SAMPMSTime - MSOffset;
(so it increases).
As long as tpn is smaller than -1, upate fTprev to -10*tpn.
If abs(tpn)<=1
, set a bool sawMS to one.
If tpn>1, set fTnext and break.
This way, iff fTPrev or fTNext or both are never set, they automatically contain the invalid value.
Also, if sawMS is false for more than a small fraction of events, then something terrible has happened (likely the offset is wrong). We should complain periodically with LOG(error).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, seems like a good idea, but again, I need some time to understand completely. But I will come with something
} | ||
} | ||
else | ||
{ |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right
r3bbase/R3BTprevTnext.cxx
Outdated
|
||
if (fR3BEventHeader->GetTpat() < fTpat && fR3BEventHeader->GetTrigger() == 1 && fR3BEventHeader->GetTpat() > 0) | ||
{ | ||
if (sampHits == 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to handle that case specially. Just let the for loop roll over one entry.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one I need to think more about to fully understand
I will try to do that, good idea, thanks. |
Fully agree, this is one of the things I really want to be discussed here before the code is accepted.
You are right here, I just used the numbers I used based on the test cases I used, but we can discuss what is better for general purposes.
I really like your idea, but I will need more time to fully understand, maybe we can discuss that at GSI, the problem is, I am out on vacation for 3 weeks starting tomorrow (I was not expecting to have 5, 6 weeks of wait for the "Offset part" of the code).
Need more time here again to process the information.
Yes, initially I made some checks and corrections related to that, but, again, maybe this is a good thing to discuss with you in person, but, again (again), I am going on vacation tomorrow, so we can discuss that after I return. Thank you very much for this discussion, this is the kind of thing I was expecting to discuss here. |
Hello @leandrofloyd |
Hello @jose-luis-rs |
ec2433c
to
ae0231f
Compare
@jose-luis-rs @klenze @YanzhaoW Please check this new code. There are some more things I want to discuss, like the values for the cases, the use of WR, etc... But please take a look at how the code looks now and we can start discussions. |
r3bbase/R3BTprevTnext.cxx
Outdated
if (fR3BEventHeader->GetTpat() < fTpat && fR3BEventHeader->GetTrigger() == 1 && fR3BEventHeader->GetTpat() > 0) | ||
{ | ||
if (sampmsHits == 1 && sampHits > 0) | ||
{ | ||
SAMPMSMapped = dynamic_cast<R3BSamplerMappedData*>(fSamplerMSMapped->At(0)); | ||
SAMPMSTime = SAMPMSMapped->GetTime(); | ||
for (Int_t i = 0; i < sampHits; ++i) | ||
{ | ||
SAMPMapped = dynamic_cast<R3BSamplerMappedData*>(fSamplerMapped->At(i)); | ||
SAMPTime = SAMPMapped->GetTime(); | ||
auto tpn = SAMPTime - SAMPMSTime - MSOffset; | ||
if (tpn < -delta_clk) | ||
{ | ||
fTprev = -clock_period * tpn; | ||
} | ||
if (abs(tpn) <= 1) | ||
{ | ||
sawMS = true; | ||
++dMScounter; | ||
} | ||
if (tpn > delta_clk) | ||
{ | ||
fTnext = clock_period * tpn; | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
if (sawMS) | ||
{ | ||
if (fTprev == -10.0) | ||
{ | ||
fTprev = 43.1e9; // MS is the first hit | ||
} | ||
if (fTnext == -10.0) | ||
{ | ||
fTnext = 43.1e9; // MS is the last hit | ||
} | ||
if (dMScounter > 1) | ||
{ | ||
if (fTprev == 43.1e9) | ||
{ | ||
fTprev = 0; // Tprev inside the delta_clk | ||
} | ||
if (fTnext == 43.1e9) | ||
{ | ||
fTnext = 0; // Tnext inside the delta_clk | ||
} | ||
} | ||
if (dMScounter == 1 && fTprev == 43.1e9 && fTnext == 43.1e9) | ||
{ | ||
fTprev = -20.0; // Only one hit in the event | ||
fTnext = -20.0; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(ignore this if you don't have time to fix)
Personally when I read this section of code, I feel very difficult to understand what's going on. This could be caused by lots of if statements and magic floating point values. Normally a function contains at most 2 to 3 if statements and anything more than that makes the code hard to read and understand. Unfortunately, I can't tell you exactly how to solve this issue as it requires lots of advanced experiences such as code design and architeture. I'm curious how @klenze feels about this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this is ready, other improvements can be done later in a new PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
General questions:
Why do you refuse to handle certain tPats?
The way I see it, either we have a main DAQ trigger, in which case Tprev/Tnext can always be set as usual, or we don't, in which case all the scalers will be empty and we will default to setting Tprev/Tnext to something invalid.
Also, did you double-check that our event header resets the Tprev/Tnext to something invalid (zero?) for each new event?
Sorry for taking so long to get back to you.
fSamplerMapped = dynamic_cast<TClonesArray*>(rootManager->GetObject("SamplerMapped")); | ||
if (fSamplerMapped == nullptr) | ||
{ | ||
return kFATAL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer if you did one of the following:
assert(fSamplerMapped)
- if () { LOG(fatal)<< "SamplerMapped not found or wrong type.";}
- if () {LOG(error) << "..."; return kFATAL; }
If you just return kFATAL, FairRoot is (hopefully) going to report which initialization of a Task failed, but not why exactly.
{ | ||
Double_t fTprev = -10.0; // Initialization with invalid values. | ||
Double_t fTnext = -10.0; | ||
Int_t dMScounter = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First, please add // number of sampler hits in coincidence with MS
Second, if we use this, sawMS should be removed and replaced by dMScounter>0.
Third, do we actually need to handle the special case where LOS saw two ions within 20ns? My feeling is no. In that case, running the algorithm as usual should result in Tprev or Tnext being really small, which will in turn make the users reject the hit as an obvious pileup.
r3bbase/R3BTprevTnext.cxx
Outdated
Double_t SAMPTime = 0; | ||
Double_t SAMPMSTime = 0; | ||
R3BSamplerMappedData* SAMPMapped = nullptr; | ||
R3BSamplerMappedData* SAMPMSMapped = nullptr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please declare SAMPTime to SAMPMSMapped in the loop when you assign them.
(@YanzhaoW would probably want you to declare them auto and auto*)
r3bbase/R3BTprevTnext.cxx
Outdated
{ | ||
fTprev = -clock_period * tpn; | ||
} | ||
if (abs(tpn) <= 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (abs(tpn) <= delta_clk)
} | ||
|
||
void R3BTprevTnext::Exec(Option_t* /*opt*/) | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
constexpr auto NOTFOUND = -10.0;
constexpr double WRAP_PERIOD = (1L<<32)*CLOCK_PERIOD; //42.9 seconds
constexpr auto ONLYHIT = -20.0;
If you ever return these, I would make the constants a public part of the class interface. If you only use them internally, put them with CLOCK_PERIOD.
r3bbase/R3BTprevTnext.cxx
Outdated
} | ||
} | ||
} | ||
if (sawMS) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am still not happy with the interpretation of sawMS, fTprev and fTnext here.
- If sawMS is false, we should probably set Tnext and Tprev to some negative header constant INVALID_NOMS, it it keeps happening, we should tell the user that their offset is likely wrong.
- Otherwise, if fTprev is invalid, we could set it to an estimate on how long our DAQ dead time is minimally. (The fancier way to track samplers over multiple readouts can be implemented later.) Perhaps make it a header constant on the order of 2000ns?
- If fTnext is invalid, the defensible value is the offset, which is the delay through the trigger matrix. (If another LOS trigger had appeared before the MS arrived at the sampler, it would have been recorded, after all.)
43.1e9 aka "no triggers for 43 seconds" should not be used as an output ever.
This version was made after a meeting with @klenze where he explained some of his comments and presented me with some ideas. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing to consider might be to make the value for "no (prev/next) hit found" part of the calibration process (possibly as two separate values).
If I remember correctly, there is a clear edge in the tnext histogram. For Tprev, things are less clear cut (but also less critical, because we were recording for a substantial fraction of the dead time, I hope. Or we just use the info from the last readout together with the WRTS, eventually.)
But this does not feel critical and can be done in a later PR. Please consider changing my small nitpicks in the code, write an explanation when the NoRecord value will be used and what it should be set to as a comment to the Setter method, and this commit is a-ok for merging as far as I am concerned.
r3bbase/R3BTprevTnext.h
Outdated
/** Virtual method Exec **/ | ||
void Exec(Option_t* opt) override; | ||
|
||
void SetOne_hit(Double_t One_hit_position) { fOne_hit = One_hit_position; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SetNoRecordTimeGap
?
r3bbase/R3BTprevTnext.h
Outdated
TClonesArray* fSamplerMSMapped = nullptr; | ||
R3BEventHeader* fR3BEventHeader = nullptr; /**< Event header - input data. */ | ||
Double_t fDelta_clk = 1.0; | ||
Double_t fOne_hit = 3e6; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
=2e3; // 2us
If the user does not provide a safe value, assume a smallish value which will make them reject more events rather than a gargantuan value which will see them include these hits in every cut.
r3bbase/R3BTprevTnext.cxx
Outdated
constexpr auto INVALID_TPTN = -10; // Assigned value for invalid or nonexistent TPrev or TNext | ||
constexpr auto ERROR_MULTI_MS = -20; // Assigned value for events where there are multiple MS | ||
constexpr auto INVALID_EVENT = -40; // Assigned value for events where the MS was not correctly written | ||
constexpr auto FIRST_LAST_HIT = 0; // Assigned value for events where the first/last hit is the MS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any of these constants which are visible to the user should be placed as public static constexpr members of the class in the header.
if (fTprev > 0 && fTnext < 0) | ||
{ | ||
fTnext = FIRST_LAST_HIT; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would replace the last if block with:
else if (dMScounter == 1)
{
if (fTprev == INVALID_TPTN)
fTprev = fOne_hit;
if (fTnext == INVALID_TPTN)
fTnext = fOne_hit;
}
fR3BEventHeader->SetTprev(fTprev);
fR3BEventHeader->SetTnext(fTnext);
That way, whenever there is no previous/next hit, the "NoRecord" value will be used. For the special case of one hit (no previous and no next hit), both of these will automatically be set to that value.
Great, thanks! @jose-luis-rs: This is ready for merging as far as I am concerned. |
Thanks! |
Task to calculate and store the values of time difference between the Master Start hit and the previous/next hit.
Checklist:
dev
branch