Belle II Software development
NeuroTrigger Class Reference

Class to represent the CDC Neurotrigger. More...

#include <NeuroTrigger.h>

Classes

struct  Parameters
 Struct to keep neurotrigger parameters. More...
 

Public Member Functions

 NeuroTrigger ()
 Default constructor.
 
virtual ~NeuroTrigger ()
 Default destructor.
 
void initialize (const Parameters &p)
 Set parameters and get some network independent parameters.
 
void initialize (const NeuroTriggerParameters &p)
 
std::vector< unsigned > getRangeIndices (const NeuroTriggerParameters &p, unsigned isector)
 Get indices for sector ranges in parameter lists.
 
std::vector< unsigned > getRangeIndices (const Parameters &p, unsigned isector)
 
void save (const std::string &filename, const std::string &arrayname="MLPs")
 Save MLPs to file.
 
bool loadIDHist (const std::string &filename)
 function to load idhist from file
 
bool load (const std::string &filename, const std::string &arrayname="MLPs")
 Load MLPs from file.
 
void setConstants ()
 Loads parameters from the geometry and precalculates some constants that will be needed.
 
void setPrecision (const std::vector< unsigned > &precision)
 set fixed point precision
 
void initializeCollections (std::string hitCollectionName, std::string eventTimeName, const std::string &et_option)
 set the hit collection and event time to required and store the hit collection name
 
void initializeCollections (std::string hitCollectionName)
 
CDCTriggerMLPoperator[] (unsigned index)
 return reference to a neural network
 
const CDCTriggerMLPoperator[] (unsigned index) const
 return const reference to a neural network
 
unsigned nSectors () const
 return number of neural networks
 
void addMLP (const CDCTriggerMLP &newMLP)
 add an MLP to the list of networks
 
std::vector< int > selectMLPs (float phi0, float invpt, float theta)
 Select all matching expert MLPs based on the given track parameters.
 
std::vector< int > selectMLPsTrain (float phi0, float invpt, float theta)
 Select all matching expert MLPs based on the given track parameters.
 
int selectMLPbyPattern (std::vector< int > &MLPs, unsigned long pattern, const bool neurotrackinputmode)
 Select one MLP from a list of sector indices.
 
void updateTrack (const CDCTriggerTrack &track)
 Calculate 2D phi position and arclength for the given track and store them.
 
void updateTrackFix (const CDCTriggerTrack &track)
 Calculate 2D phi position and arclength for the given track and store them.
 
double getRelId (const CDCTriggerSegmentHit &hit)
 Calculate phi position of a hit relative to 2D track (scaled to number of wires).
 
int getLowestTime (unsigned isector, RelationVector< CDCTriggerSegmentHit > Hits, bool onlyAxials)
 helper function to get the fastest priority time of given ts array
 
void getEventTime (unsigned isector, const CDCTriggerTrack &track, std::string et_option, const bool)
 Read out the event time and store it.
 
void getEventTime (unsigned isector, const CDCTriggerTrack &track)
 DEPRECATED!
 
std::string get_et_option ()
 Return value of m_et_option.
 
unsigned long getInputPattern (unsigned isector, const CDCTriggerTrack &track, const bool neurotrackinputmode)
 Calculate input pattern for MLP.
 
unsigned long getCompleteHitPattern (unsigned isector, const CDCTriggerTrack &track, const bool neurotrackinputmode)
 Get complete hit pattern of neurotrack.
 
unsigned long getPureDriftThreshold (unsigned isector, const CDCTriggerTrack &track, const bool neurotrackinputmode)
 Get the drift threshold bits, where the time of the TS was outside of the accepted time window and thus shifted to the allowed maximum within the borders.
 
std::vector< unsigned > selectHitsHWSim (unsigned isector, const CDCTriggerTrack &track)
 Select hits for each super layer from the ones related to input track.
 
std::vector< unsigned > selectHits (unsigned isector, const CDCTriggerTrack &track, bool returnAllRelevant=false)
 Select best hits for each super layer.
 
std::vector< float > getInputVector (unsigned isector, const std::vector< unsigned > &hitIds)
 Calculate input values for MLP.
 
std::vector< float > getInputVectorDefault (unsigned isector, const std::vector< unsigned > &hitIds)
 Calculate input values for MLP Default mode.
 
std::vector< float > getInputVector_fullhit (unsigned isector, const std::vector< unsigned > &hitIds)
 Calculate input values for MLP in full hit mode.
 
std::vector< float > getInputVector_extrahit (unsigned isector, const std::vector< unsigned > &hitIds)
 Calculate input values for MLP in extra hit mode.
 
std::vector< float > runMLP (unsigned isector, const std::vector< float > &input)
 Run an expert MLP.
 
std::vector< float > runMLPFix (unsigned isector, std::vector< float > input)
 Run an expert MLP with fixed point arithmetic.
 

Private Attributes

std::vector< CDCTriggerMLPm_MLPs = {}
 List of networks.
 
double m_radius [9][5] = {{0}}
 Radius of the CDC layers with priority wires (2 per super layer)
 
unsigned m_TSoffset [10] = {0}
 Number of track segments up to super layer.
 
double m_idRef [9][5] = {{0}}
 2D phi position of current track scaled to number of wires
 
double m_alpha [9][5] = {{0}}
 2D crossing angle of current track
 
int m_T0 = 0
 Event time of current event / track.
 
bool m_hasT0 = false
 Flag to show if stored event time is valid.
 
int m_AdditionWireMode = 0
 Switch for addtional input modes.
 
int m_AdditionInputPerSL = 0
 Value for additional input.
 
int m_TMin = -20
 Min Drift time limitation for ETF case only.
 
std::vector< unsigned > m_precision
 Fixed point precision in bit after radix point.
 
StoreArray< CDCTriggerSegmentHitm_segmentHits
 StoreArray containing the input track segment hits.
 
StoreObjPtr< BinnedEventT0m_eventTime
 StoreObjPtr containing the event time.
 
std::string m_hitCollectionName
 Name of the StoreArray containing the input track segment hits.
 
DBObjPtr< CDCTriggerNeuroConfigm_cdctriggerneuroconfig
 get NNT payload from database.
 

Detailed Description

Class to represent the CDC Neurotrigger.

The Neurotrigger consists of one or several Multi Layer Perceptrons. The input values are calculated from track segment hits and a 2D track estimate. The output is a scaled estimate of the z-vertex of the track. In case of several MLPs, each is an expert for a different track parameter region.

See also
Neurotrigger Modules:
NeuroTriggerTrainerModule for preparing training data and training,
NeuroTriggerModule for loading trained networks and using them.

Definition at line 40 of file NeuroTrigger.h.

Constructor & Destructor Documentation

◆ NeuroTrigger()

NeuroTrigger ( )
inline

Default constructor.

Definition at line 118 of file NeuroTrigger.h.

118{}

◆ ~NeuroTrigger()

virtual ~NeuroTrigger ( )
inlinevirtual

Default destructor.

Definition at line 121 of file NeuroTrigger.h.

121{}

Member Function Documentation

◆ addMLP()

void addMLP ( const CDCTriggerMLP & newMLP)
inline

add an MLP to the list of networks

Definition at line 167 of file NeuroTrigger.h.

167{ m_MLPs.push_back(newMLP); }

◆ get_et_option()

std::string get_et_option ( )
inline

Return value of m_et_option.

Definition at line 229 of file NeuroTrigger.h.

230 {
231 std::string eto = m_MLPs[0].get_et_option();
232 for (unsigned int i = 0; i < m_MLPs.size(); ++i) {
233 if (m_MLPs[i].get_et_option() != eto) {
234 B2ERROR("Timing options in the expert networks in the CDC Neurotrigger differ!");
235 }
236 }
237 return eto;
238
239 }

◆ getCompleteHitPattern()

unsigned long getCompleteHitPattern ( unsigned isector,
const CDCTriggerTrack & track,
const bool neurotrackinputmode )

Get complete hit pattern of neurotrack.

This does the same as the getInputPattern function, but also shows the axial hit bits. This function was made for the simulation of the hardware debug information "TSVector".

Definition at line 684 of file NeuroTrigger.cc.

685{
686 const CDCTriggerMLP& expert = m_MLPs[isector];
687 unsigned long chitPattern = 0;
688 vector<unsigned> nHits;
689 nHits.assign(9, 0);
690 // loop over axial hits related to input track
691 RelationVector<CDCTriggerSegmentHit> axialHits =
692 track.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName);
693 for (unsigned ihit = 0; ihit < axialHits.size(); ++ ihit) {
694 // skip hits with negative relation weight (not selected in finder)
695 if (axialHits.weight(ihit) < 0) {
696 continue;
697 }
698 unsigned short iSL = axialHits[ihit]->getISuperLayer();
699 // // skip stereo hits (should not be related to track, but check anyway)
700 if ((!neurotrackinputmode) && (iSL % 2 == 1)) continue;
701 double relId = getRelId(*axialHits[ihit]);
702 if (expert.isRelevant(relId, iSL)) {
703 if (nHits[iSL] < expert.getMaxHitsPerSL()) {
704 chitPattern |= 1 << (iSL + 9 * nHits[iSL]);
705 ++nHits[iSL];
706 }
707 }
708 }
709 if (!neurotrackinputmode) {
710 // loop over stereo hits
711 for (int ihit = 0; ihit < m_segmentHits.getEntries(); ++ ihit) {
712 unsigned short iSL = m_segmentHits[ihit]->getISuperLayer();
713 // skip axial hits
714 if (iSL % 2 == 0) continue;
715 // get priority time
716 double relId = getRelId(*m_segmentHits[ihit]);
717 if (expert.isRelevant(relId, iSL)) {
718 if (nHits[iSL] < expert.getMaxHitsPerSL()) {
719 chitPattern |= 1 << (iSL + 9 * nHits[iSL]);
720 ++nHits[iSL];
721 }
722 }
723 }
724 }
725 return chitPattern;
726}
std::vector< CDCTriggerMLP > m_MLPs
List of networks.
double getRelId(const CDCTriggerSegmentHit &hit)
Calculate phi position of a hit relative to 2D track (scaled to number of wires).
StoreArray< CDCTriggerSegmentHit > m_segmentHits
StoreArray containing the input track segment hits.
std::string m_hitCollectionName
Name of the StoreArray containing the input track segment hits.
size_t size() const
Get number of relations.
float weight(int index) const
Get weight with index.

◆ getEventTime() [1/2]

void getEventTime ( unsigned isector,
const CDCTriggerTrack & track )

DEPRECATED!

! Read out the event time and store it. If there is no valid event time, it can be determined from the shortest priority time of all hit candidates, if the option is enabled for the given sector.

◆ getEventTime() [2/2]

void getEventTime ( unsigned isector,
const CDCTriggerTrack & track,
std::string et_option,
const bool neuroinputmode = false )

Read out the event time and store it.

It can be given different options in the et_option ("EventTime option") parameter. The different options are: "etf_only" : only ETF info is used, otherwise an error is thrown. "fastestpriority" : event time is estimated by fastest priority time in selected track segments. if something fails, it is set to 0. "zero" : the event time is set to 0. "etf_or_fastestpriority" : the event time is obtained by the ETF, if not possible, the flag "fastestppriority" is used. "etf_or_zero" : the event time is obtained by the ETF, if

m_T0 = 9999;

find shortest time of related and relevant axial hits RelationVector<CDCTriggerSegmentHit> Hits = track.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName); m_T0 = getLowestTime(isector, Hits, false); if (m_T0 < 9999) { m_hasT0 = true; } else { m_T0 = 0; m_hasT0 = false; }

m_T0 = 9999;

find shortest time of related and relevant axial hits RelationVector<CDCTriggerSegmentHit> Hits = track.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName); m_T0 = getLowestTime(isector, Hits, true); if (m_T0 < 9999) { m_hasT0 = true; } else { m_T0 = 0; m_hasT0 = false; }

Definition at line 490 of file NeuroTrigger.cc.

491{
492
493 if (et_option != m_MLPs[isector].get_et_option()) {
494 B2DEBUG(20, "Used event time option is different to the one set in the MLP"
495 << LogVar("et_option", et_option) << LogVar("isector", isector)
496 << LogVar("et_option_mlp", m_MLPs[isector].get_et_option()));
497 }
498 if (et_option == "fastestpriority") {
499 B2DEBUG(200, "et_option is 'fastestpriority'");
500 m_T0 = 9999;
501 // find shortest time of related and relevant axial hits
502 RelationVector<CDCTriggerSegmentHit> Hits =
503 track.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName);
504 m_T0 = getLowestTime(isector, Hits, false);
505 if (m_T0 < 9999) {
506 m_hasT0 = true;
507 } else {
508 m_T0 = 0;
509 m_hasT0 = false;
510 }
511
512 } else if (et_option == "fastest2d") {
513 m_T0 = 9999;
514 // find shortest time of related and relevant axial hits
515 RelationVector<CDCTriggerSegmentHit> Hits =
516 track.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName);
517 m_T0 = getLowestTime(isector, Hits, true);
518 if (m_T0 < 9999) {
519 m_hasT0 = true;
520 } else {
521 m_T0 = 0;
522 m_hasT0 = false;
523 }
524 } else if (et_option == "zero") {
525 m_hasT0 = true;
526 m_T0 = 0;
527 } else if (et_option == "etf_only") {
528 bool hasT0 = (m_eventTime.isValid()) ? m_eventTime->hasBinnedEventT0(Const::CDC) : false;
529 if (hasT0) {
530 m_T0 = m_eventTime->getBinnedEventT0(Const::CDC) / 2;
531 m_hasT0 = true;
532 } else {
533 B2ERROR("No ETF output, but forced to use ETF!");
534 m_hasT0 = false;
535 m_T0 = 0;
536 }
537 } else if (et_option == "etf_or_fastestpriority") {
538 bool hasT0 = (m_eventTime.isValid()) ? m_eventTime->hasBinnedEventT0(Const::CDC) : false;
539 if (hasT0) {
540 m_T0 = m_eventTime->getBinnedEventT0(Const::CDC) / 2;
541 m_hasT0 = true;
542 } else {
543 getEventTime(isector, track, "fastestpriority", neuroinputmode);
556 }
557 } else if (et_option == "min_etf_fastestpriority") {
558 bool hasT0 = (m_eventTime.isValid()) ? m_eventTime->hasBinnedEventT0(Const::CDC) : false;
559 int T0_etf = 9999;
560 if (hasT0) {
561 T0_etf = m_eventTime->getBinnedEventT0(Const::CDC) / 2;
562 m_hasT0 = true;
563 }
564 getEventTime(isector, track, "fastestpriority", neuroinputmode);
565 if (m_T0 > T0_etf) {
566 m_T0 = T0_etf;
567 }
568 } else if (et_option == "etf_or_fastest2d") {
569 bool hasT0 = (m_eventTime.isValid()) ? m_eventTime->hasBinnedEventT0(Const::CDC) : false;
570 if (hasT0) {
571 m_T0 = m_eventTime->getBinnedEventT0(Const::CDC) / 2;
572 m_hasT0 = true;
573 } else {
574 getEventTime(isector, track, "fastest2d", neuroinputmode);
587 }
588 } else if (et_option == "etf_or_zero") {
589 bool hasT0 = (m_eventTime.isValid()) ? m_eventTime->hasBinnedEventT0(Const::CDC) : false;
590 if (hasT0) {
591 m_T0 = m_eventTime->getBinnedEventT0(Const::CDC) / 2;
592 m_hasT0 = true;
593 } else {
594 m_hasT0 = true;
595 m_T0 = 0;
596 }
597 } else if (et_option == "etfcc") {
598 if (track.getHasETFTime()) {
599 m_T0 = track.getETF_unpacked();
600 m_hasT0 = true;
601 } else {
602 m_T0 = 0;
603 m_hasT0 = false;
604 }
605
606 } else if (et_option == "etfcc_or_zero") {
607 if (track.getHasETFTime()) {
608 m_T0 = track.getETF_unpacked();
609 m_hasT0 = true;
610 } else {
611 m_T0 = 0;
612 m_hasT0 = true;
613 }
614
615 } else if (et_option == "etfcc_or_fastestpriority") {
616 if (track.getHasETFTime()) {
617 m_T0 = track.getETF_unpacked();
618 m_hasT0 = true;
619 } else {
620 getEventTime(isector, track, "fastestpriority", neuroinputmode);
621 }
622
623 } else if (et_option == "etfhwin") {
624 m_T0 = track.getETF_recalced();
625 m_hasT0 = true;
626
627 } else {
628 B2ERROR("No valid parameter for et_option (" << et_option << " )!");
629 }
630
631}
std::string get_et_option()
Return value of m_et_option.
int m_T0
Event time of current event / track.
StoreObjPtr< BinnedEventT0 > m_eventTime
StoreObjPtr containing the event time.
void getEventTime(unsigned isector, const CDCTriggerTrack &track, std::string et_option, const bool)
Read out the event time and store it.
int getLowestTime(unsigned isector, RelationVector< CDCTriggerSegmentHit > Hits, bool onlyAxials)
helper function to get the fastest priority time of given ts array
bool m_hasT0
Flag to show if stored event time is valid.

◆ getInputPattern()

unsigned long getInputPattern ( unsigned isector,
const CDCTriggerTrack & track,
const bool neurotrackinputmode )

Calculate input pattern for MLP.

Parameters
isectorindex of the MLP that will use the input
trackaxial hit relations are taken from given track
neurotrackinputmodeinput mode
Returns
super layer pattern of hits in the current track

Definition at line 729 of file NeuroTrigger.cc.

730{
731 const CDCTriggerMLP& expert = m_MLPs[isector];
732 unsigned long hitPattern = 0;
733 vector<unsigned> nHits;
734 nHits.assign(9, 0);
735 // loop over axial hits related to input track
736 RelationVector<CDCTriggerSegmentHit> axialHits =
737 track.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName);
738 for (unsigned ihit = 0; ihit < axialHits.size(); ++ ihit) {
739 // skip hits with negative relation weight (not selected in finder)
740 if (axialHits.weight(ihit) < 0) {
741 continue;
742 }
743 unsigned short iSL = axialHits[ihit]->getISuperLayer();
744 // // skip stereo hits (should not be related to track, but check anyway)
745 if ((!neurotrackinputmode) && (iSL % 2 == 1)) continue;
746 double relId = getRelId(*axialHits[ihit]);
747
748 if (expert.isRelevant(relId, iSL)) {
749 if (nHits[iSL] < expert.getMaxHitsPerSL()) {
750 hitPattern |= 1 << (iSL + 9 * nHits[iSL]);
751 ++nHits[iSL];
752 }
753 B2DEBUG(250, "hit in SL " << iSL);
754 } else {
755 B2DEBUG(250, "hit in SL " << iSL << " not relevant (relId = " << relId << ")");
756 }
757 }
758 if (!neurotrackinputmode) {
759 // loop over stereo hits
760 for (int ihit = 0; ihit < m_segmentHits.getEntries(); ++ ihit) {
761 unsigned short iSL = m_segmentHits[ihit]->getISuperLayer();
762 // skip axial hits
763 if (iSL % 2 == 0) continue;
764 double relId = getRelId(*m_segmentHits[ihit]);
765 if (expert.isRelevant(relId, iSL)) {
766 if (nHits[iSL] < expert.getMaxHitsPerSL()) {
767 hitPattern |= 1 << (iSL + 9 * nHits[iSL]);
768 ++nHits[iSL];
769 }
770 B2DEBUG(250, "hit in SL " << iSL);
771 } else {
772 B2DEBUG(250, "hit in SL " << iSL << " not relevant (relId = " << relId << ")");
773 }
774 }
775 }
776 B2DEBUG(250, "hitPattern " << hitPattern);
777 return hitPattern & expert.getSLpatternMask();
778}

◆ getInputVector()

vector< float > getInputVector ( unsigned isector,
const std::vector< unsigned > & hitIds )

Calculate input values for MLP.

Parameters
isectorindex of the MLP that will use the input
hitIdshit indices to be used for the input
Returns
scaled vector of input values (1 for each input node) based on the initial parameters , will choose different modes

Definition at line 1206 of file NeuroTrigger.cc.

1207{
1208 if (m_AdditionWireMode == 0)
1209 return getInputVectorDefault(isector, hitIds);
1210 if (m_AdditionWireMode == 1)
1211 return getInputVector_fullhit(isector, hitIds);
1212 if (m_AdditionWireMode == 2)
1213 return getInputVector_extrahit(isector, hitIds);
1214 else // to avoid zero return
1215 return getInputVectorDefault(isector, hitIds);
1216
1217}
std::vector< float > getInputVectorDefault(unsigned isector, const std::vector< unsigned > &hitIds)
Calculate input values for MLP Default mode.
int m_AdditionWireMode
Switch for addtional input modes.
std::vector< float > getInputVector_fullhit(unsigned isector, const std::vector< unsigned > &hitIds)
Calculate input values for MLP in full hit mode.
std::vector< float > getInputVector_extrahit(unsigned isector, const std::vector< unsigned > &hitIds)
Calculate input values for MLP in extra hit mode.

◆ getInputVector_extrahit()

vector< float > getInputVector_extrahit ( unsigned isector,
const std::vector< unsigned > & hitIds )

Calculate input values for MLP in extra hit mode.

Parameters
isectorindex of the MLP that will use the input
hitIdshit indices to be used for the input
Returns
scaled vector of input values (1 for each input node)

Full LR table do not show large difference, comment for now if want to include, please also add LUT which not include here if(UseFullLR) { unsigned int lutpattern = hitpattern*2; if(m_segmentHits[ihit]->getPriorityPosition()==2) lutpattern +=1; LRV = (iSL==0)?m_innerLUT[lutpattern]:m_outerLUT[lutpattern]; } else

Definition at line 999 of file NeuroTrigger.cc.

1000{
1001 const CDCTriggerMLP& expert = m_MLPs[isector];
1002 // prepare empty input vector and vectors to keep best drift times
1003 vector<float> inputVector;
1004 inputVector.assign(expert.nNodesLayer(0), 0.);
1005 // convert hits to input values
1006 vector<unsigned> nHits;
1007 nHits.assign(9, 0);
1008 //Only for test
1009 for (unsigned ii = 0; ii < hitIds.size(); ++ii) {
1010 int ihit = hitIds[ii];
1011 unsigned short iSL = m_segmentHits[ihit]->getISuperLayer();
1012 unsigned short iRef = iSL + 9 * nHits[iSL];
1013 ++nHits[iSL];
1014 //Set cut for T_0 input
1015 int priot = m_segmentHits[ihit]->priorityTime();
1016 int t = (m_hasT0) ? priot - m_T0 : 0;
1017 if (t < 0) {
1018 t = 0;
1019 } else if (t > expert.getTMax()) {
1020 t = expert.getTMax();
1021 }
1022
1023 //Get TS pattern
1024 unsigned hitpattern = m_segmentHits[ihit]->gethitpattern();
1025 vector<float> hitpatterntime = m_segmentHits[ihit]->gethittime();
1026 vector<int> HitIDs;
1027 HitIDs.assign(hitpatterntime.size(), -1);
1028 vector<float> HitTimes;
1029 HitTimes.assign(hitpatterntime.size(), -1);
1030 int LR = m_segmentHits[ihit]->getLeftRight();
1031 double relId = getRelId(*m_segmentHits[ihit]);
1032 int priority = m_segmentHits[ihit]->getPriorityPosition();
1033 // get scaled input values (scaling such that the absolute value of all inputs is < 1)
1034
1035 inputVector[(3 + m_AdditionInputPerSL) * iRef] = expert.scaleId(relId, iSL);
1036 float scaleT = pow(2, floor(log2(1. / expert.getTMax())));
1037 inputVector[(3 + m_AdditionInputPerSL) * iRef + 1] = (((LR >> 1) & 1) - (LR & 1)) * t * scaleT;
1038 inputVector[(3 + m_AdditionInputPerSL) * iRef + 2] = m_alpha[iSL][int(priority < 3)] * 0.5;
1039
1040
1041 //Loop over all hits in selected TS for selection
1042 if (m_AdditionInputPerSL % 3 != 0)
1043 B2ERROR("For extra wire case, please set m_AdditionInputPerSL to 3 * n, where n is the number of extra wire want to include ");
1044 const int nwires = (int)m_AdditionInputPerSL / 3;
1045 vector<int> LRV;
1057 LRV.assign((int)hitpatterntime.size(), 2); // Set all extra drift time to positive
1058
1059 for (int j = 0; j < (int) hitpatterntime.size(); j++) {
1060 HitTimes[j] = (((hitpattern >> j) % 2 == 1) ? ((m_hasT0) ? hitpatterntime[j] - m_T0 : 0) : 0);
1061 //Try to set the one with small drift time contribute a lot
1062 if (HitTimes[j] > expert.getTMax()) HitTimes[j] = expert.getTMax();
1063 if (HitTimes[j] < m_TMin)LRV[j] = 3; //Always set low priority for unexpected low t_drift
1064 if (HitTimes[j] < 0)HitTimes[j] = 0;
1065 HitIDs[j] = j;
1066 B2DEBUG(150, "wire Find hit: " << j << " " << hitpatterntime[j] << std::endl);
1067 }
1068 int priorID = (iSL == 0) ? (priority == 3 ? 0 : 0 + priority) : (priority == 3 ? 5 : 5 + priority);
1069 std::vector<int> BestID(nwires, -1);
1070 std::vector<int> BestLR(nwires, 3);
1071 std::vector<double> BestDriftTime(nwires, expert.getTMax());
1072
1073
1074 for (int i = 0; i < (int)hitpatterntime.size(); i++) {
1075 bool tag0 = false;
1076 int remove_id = -1;
1077 for (int j = 0; j < nwires; j++) {
1078 if ((hitpattern >> i) % 2 == 0 || i == priorID)
1079 continue;
1080 if (LRV[i] == 3 && (BestLR[j] != 3))
1081 continue;
1082 else if ((LRV[i] != 3 && BestLR[j] == 3) || (HitTimes[i] < BestDriftTime[j])) {
1083 tag0 = true;
1084 remove_id = j;
1085 break;
1086 }
1087 }
1088 if (!tag0)
1089 continue;
1090 for (int j = 0; j < nwires - remove_id - 1; j++) {
1091 BestID[nwires - j - 1] = BestID[nwires - j - 2];
1092 BestLR[nwires - j - 1] = BestLR[nwires - j - 2];
1093 BestDriftTime[nwires - j - 1] = BestDriftTime[nwires - j - 2];
1094 }
1095 BestID[remove_id] = i;
1096 BestLR[remove_id] = LRV[i];
1097 BestDriftTime[remove_id] = HitTimes[i];
1098 }
1099 //Do not return input vector in case not enough hits find.
1100 for (int j = 0; j < nwires; j++) {
1101 if (BestID[j] == -1) {
1102 vector<float> tmpvector = {};
1103 return tmpvector;
1104 }
1105 }
1106 for (int i = 0; i < nwires; i++) {
1107 int prior = 0;
1108 double deltaPhi = 0;
1109 //Based on the sysmetry shape, build a table for alpha and id delta phi
1110 //Following same method as previous, use delta phi instead of true id for other priority
1111 const int outerpriorList[11] = {4, 4, 4, 2, 2, 0, 1, 1, 3, 3, 3};
1112 const int innerpriorList[15] = {0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4};
1113 const double outerdeltaPhiList[11] = {-1, 0, 1, -0.5, 0.5, 0, -0.5, 0.5, -1, 0, 1};
1114 const double innerdeltaPhiList[15] = {-0.5, 0.5, -1, 0, 1, -1.5, -0.5, 0.5, 1.5, -2, -1, 0, 1, 2};
1115 if (iSL != 0) {
1116 prior = outerpriorList[BestID[i]];
1117 deltaPhi = outerdeltaPhiList[BestID[i]];
1118 }
1119 if (iSL == 0) {
1120 prior = innerpriorList[BestID[i]];
1121 deltaPhi = innerdeltaPhiList[BestID[i]];
1122 }
1123 B2DEBUG(10, "BestID:" << BestID[i] << "Prior:" << prior << "DeltaPhi:" << deltaPhi << "Drift Time:" << BestDriftTime[i] <<
1124 "Used Drift Time: " << BestDriftTime[i]*scaleT * (((BestLR[i] >> 1) & 1) - (BestLR[i] & 1)));
1125 double relIdI = m_segmentHits[ihit]->getSegmentID() - m_TSoffset[iSL] - m_idRef[iSL][prior] + deltaPhi;
1126 relIdI = remainder(relIdI, (m_TSoffset[iSL + 1] - m_TSoffset[iSL]));
1127 inputVector[(3 + m_AdditionInputPerSL) * iRef + 3 + 4 * i ] = expert.scaleId(relIdI, iSL);
1128 inputVector[(3 + m_AdditionInputPerSL) * iRef + 4 + 4 * i] = BestDriftTime[i] * scaleT * (((BestLR[i] >> 1) & 1) - (BestLR[i] & 1));
1129 inputVector[(3 + m_AdditionInputPerSL) * iRef + 5 + 4 * i] = m_alpha[iSL][prior] * 0.5;
1130 // Though radius should be different for those case, we did not see any difference after include. Comment first
1131 //inputVector[(3 + m_AdditionInputPerSL) * iRef + 6 + 4 * i] = m_radius[iSL][prior]/120.;
1132 }
1133 }
1134 return inputVector;
1135}
double m_idRef[9][5]
2D phi position of current track scaled to number of wires
double m_alpha[9][5]
2D crossing angle of current track
unsigned m_TSoffset[10]
Number of track segments up to super layer.
int m_AdditionInputPerSL
Value for additional input.
int m_TMin
Min Drift time limitation for ETF case only.

◆ getInputVector_fullhit()

vector< float > getInputVector_fullhit ( unsigned isector,
const std::vector< unsigned > & hitIds )

Calculate input values for MLP in full hit mode.

Parameters
isectorindex of the MLP that will use the input
hitIdshit indices to be used for the input
Returns
scaled vector of input values (1 for each input node)

Definition at line 1139 of file NeuroTrigger.cc.

1140{
1141 const CDCTriggerMLP& expert = m_MLPs[isector];
1142 // prepare empty input vector and vectors to keep best drift times
1143 vector<float> inputVector;
1144 inputVector.assign(expert.nNodesLayer(0), 0.);
1145 // convert hits to input values
1146 vector<unsigned> nHits;
1147 nHits.assign(9, 0);
1148 //Only for test
1149 for (unsigned ii = 0; ii < hitIds.size(); ++ii) {
1150 int ihit = hitIds[ii];
1151 unsigned short iSL = m_segmentHits[ihit]->getISuperLayer();
1152 unsigned short iRef = iSL + 9 * nHits[iSL];
1153 ++nHits[iSL];
1154 //Set cut for T_0 input
1155 int priot = m_segmentHits[ihit]->priorityTime();
1156 int t = (m_hasT0) ? priot - m_T0 : 0;
1157 if (t < 0) {
1158 t = 0;
1159 } else if (t > expert.getTMax()) {
1160 t = expert.getTMax();
1161 }
1162
1163 //Get TS pattern
1164 unsigned hitpattern = m_segmentHits[ihit]->gethitpattern();
1165 vector<float> hitpatterntime = m_segmentHits[ihit]->gethittime();
1166 vector<int> HitIDs;
1167 HitIDs.assign(hitpatterntime.size(), -1);
1168 vector<float> HitTimes;
1169 HitTimes.assign(hitpatterntime.size(), -1);
1170 int LR = m_segmentHits[ihit]->getLeftRight();
1171 double relId = getRelId(*m_segmentHits[ihit]);
1172 int priority = m_segmentHits[ihit]->getPriorityPosition();
1173 // get scaled input values (scaling such that the absolute value of all inputs is < 1)
1174
1175 inputVector[(3 + m_AdditionInputPerSL) * iRef] = expert.scaleId(relId, iSL);
1176 float scaleT = pow(2, floor(log2(1. / expert.getTMax())));
1177 inputVector[(3 + m_AdditionInputPerSL) * iRef + 1] = (((LR >> 1) & 1) - (LR & 1)) * t * scaleT;
1178 inputVector[(3 + m_AdditionInputPerSL) * iRef + 2] = m_alpha[iSL][int(priority < 3)] * 0.5;
1179
1180 //case 2: 0 drift time -> 0 and max drift time -> 1 and no event -> -1
1181 if (m_AdditionInputPerSL != 11
1182 && m_AdditionInputPerSL != 22)
1183 B2ERROR("Not correct AdditionInputPerSL value for full wire case, please set 11 (TDC only) or 22 (TDC & ADC)");
1184 for (int j = 0; j < (int) hitpatterntime.size(); j++) {
1185 HitTimes[j] = ((hitpattern >> j) % 2 == 1) ? ((m_hasT0) ? hitpatterntime[j] - m_T0 : 0) : -expert.getTMax();
1186 //Try to set the one with small drift time contribute a lot
1187 if (HitTimes[j] > expert.getTMax()) HitTimes[j] = expert.getTMax();
1188 if (HitTimes[j] < m_TMin)HitTimes[j] = -expert.getTMax();
1189 if (HitTimes[j] < 0 && HitTimes[j] >= m_TMin)HitTimes[j] = 0;
1190 //else HitTimes[j]=expert.getTMax()-HitTimes[j];
1191 HitIDs[j] = j;
1192 }
1193 for (int j = 0; j < 11; j++) {
1194 inputVector[(3 + m_AdditionInputPerSL) * iRef + 3 + j] = HitTimes[j] * scaleT;
1195 }
1196 if (m_AdditionInputPerSL == 22) {
1197 std::vector<float> adc = m_segmentHits[ihit]->getadc();
1198 for (int j = 0; j < 11; j++) {
1199 inputVector[(3 + m_AdditionInputPerSL) * iRef + 14 + j] = adc[j];
1200 }
1201 }
1202 }
1203 return inputVector;
1204}

◆ getInputVectorDefault()

vector< float > getInputVectorDefault ( unsigned isector,
const std::vector< unsigned > & hitIds )

Calculate input values for MLP Default mode.

Parameters
isectorindex of the MLP that will use the input
hitIdshit indices to be used for the input
Returns
scaled vector of input values (1 for each input node)

Definition at line 1219 of file NeuroTrigger.cc.

1220{
1221 const CDCTriggerMLP& expert = m_MLPs[isector];
1222 // prepare empty input vector and vectors to keep best drift times
1223 vector<float> inputVector;
1224 inputVector.assign(expert.nNodesLayer(0), 0.);
1225 // convert hits to input values
1226 vector<unsigned> nHits;
1227 nHits.assign(9, 0);
1228 for (unsigned ii = 0; ii < hitIds.size(); ++ii) {
1229 int ihit = hitIds[ii];
1230 unsigned short iSL = m_segmentHits[ihit]->getISuperLayer();
1231 unsigned short iRef = iSL + 9 * nHits[iSL];
1232 ++nHits[iSL];
1233 int priot = m_segmentHits[ihit]->priorityTime();
1234 int t = (m_hasT0) ? priot - m_T0 : 0;
1235 if (t < 0) {
1236 t = 0;
1237 } else if (t > expert.getTMax()) {
1238 t = expert.getTMax();
1239 }
1240 int LR = m_segmentHits[ihit]->getLeftRight();
1241 double relId = getRelId(*m_segmentHits[ihit]);
1242 int priority = m_segmentHits[ihit]->getPriorityPosition();
1243 // get scaled input values (scaling such that the absolute value of all inputs is < 1)
1244 inputVector[3 * iRef] = expert.scaleId(relId, iSL);
1245 float scaleT = pow(2, floor(log2(1. / expert.getTMax())));
1246 inputVector[3 * iRef + 1] = (((LR >> 1) & 1) - (LR & 1)) * t * scaleT;
1247 inputVector[3 * iRef + 2] = m_alpha[iSL][int(priority < 3)] * 0.5;
1248 }
1249 return inputVector;
1250}

◆ getLowestTime()

int getLowestTime ( unsigned isector,
RelationVector< CDCTriggerSegmentHit > Hits,
bool onlyAxials = false )

helper function to get the fastest priority time of given ts array

Definition at line 466 of file NeuroTrigger.cc.

467{
468 int tlow = 9999;
469 B2DEBUG(200, "looping over axials:");
470 for (unsigned ihit = 0; ihit < Hits.size(); ++ihit) {
471 // skip hits with negative relation weight (not selected in finder)
472 if (Hits.weight(ihit) < 0) continue;
473 unsigned short iSL = Hits[ihit]->getISuperLayer();
474 if (iSL % 2 == 1 && onlyAxials) {continue;}
475 // get shortest time of relevant hits
476 B2DEBUG(200, " check drifttime: SL" + std::to_string(iSL) + ",ID = " + std::to_string(Hits[ihit]->getSegmentID()) + ", t = " +
477 std::to_string(Hits[ihit]->priorityTime()));
478 double relId = getRelId(*Hits[ihit]);
479 if (m_MLPs[isector].isRelevant(relId, iSL) &&
480 Hits[ihit]->priorityTime() < tlow) {
481 tlow = Hits[ihit]->priorityTime();
482 B2DEBUG(200, " new tlow: " << std::to_string(tlow));
483 }
484 }
485 return tlow;
486}

◆ getPureDriftThreshold()

unsigned long getPureDriftThreshold ( unsigned isector,
const CDCTriggerTrack & track,
const bool neurotrackinputmode )

Get the drift threshold bits, where the time of the TS was outside of the accepted time window and thus shifted to the allowed maximum within the borders.

Note, that to get the same values as from the unpacker, this value has to be combined with the (complement of the) TSVector.

Definition at line 634 of file NeuroTrigger.cc.

635{
636 const CDCTriggerMLP& expert = m_MLPs[isector];
637 unsigned long driftth = 0;
638 vector<unsigned> nHits;
639 nHits.assign(9, 0);
640 // loop over axial hits related to input track
641 RelationVector<CDCTriggerSegmentHit> axialHits =
642 track.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName);
643 for (unsigned ihit = 0; ihit < axialHits.size(); ++ ihit) {
644 // skip hits with negative relation weight (not selected in finder)
645 if (axialHits.weight(ihit) < 0) {
646 continue;
647 }
648 unsigned short iSL = axialHits[ihit]->getISuperLayer();
649 // // skip stereo hits (should not be related to track, but check anyway)
650 if ((!neurotrackinputmode) && (iSL % 2 == 1)) continue;
651 // get priority time
652 int t = (m_hasT0) ? axialHits[ihit]->priorityTime() - m_T0 : 0;
653 double relId = getRelId(*axialHits[ihit]);
654 if (t < 0 || t > expert.getTMax()) {
655 if (expert.isRelevant(relId, iSL)) {
656 if (nHits[iSL] < expert.getMaxHitsPerSL()) {
657 driftth |= 1 << (8 - iSL + 9 * nHits[iSL]);
658 ++nHits[iSL];
659 }
660 }
661 }
662 }
663 if (!neurotrackinputmode) {
664 // loop over stereo hits
665 for (int ihit = 0; ihit < m_segmentHits.getEntries(); ++ ihit) {
666 unsigned short iSL = m_segmentHits[ihit]->getISuperLayer();
667 // skip axial hits
668 if (iSL % 2 == 0) continue;
669 // get priority time
670 int t = (m_hasT0) ? m_segmentHits[ihit]->priorityTime() - m_T0 : 0;
671 double relId = getRelId(*m_segmentHits[ihit]);
672 if (t < 0 || t > expert.getTMax()) {
673 if (expert.isRelevant(relId, iSL)) {
674 if (nHits[iSL] < expert.getMaxHitsPerSL()) {
675 driftth |= 1 << (8 - iSL + 9 * nHits[iSL]);
676 ++nHits[iSL];
677 }
678 }
679 }
680 }
681 }
682 return driftth;
683}

◆ getRangeIndices()

vector< unsigned > getRangeIndices ( const NeuroTriggerParameters & p,
unsigned isector )

Get indices for sector ranges in parameter lists.

Definition at line 264 of file NeuroTrigger.cc.

265{
266 // the indices can be used for both rangeuse and rangetrain, because the size of those arrays should be the same (it is checked in the initialize function).
267 std::vector<unsigned> indices = {0, 0, 0, 0};
268 if (p.phiRangeUse.size() * p.invptRangeUse.size() * p.thetaRangeUse.size() * p.SLpattern.size() == p.nMLP) {
269 indices[0] = isector % p.phiRangeUse.size();
270 indices[1] = (isector / p.phiRangeUse.size()) % p.invptRangeUse.size();
271 indices[2] = (isector / (p.phiRangeUse.size() * p.invptRangeUse.size())) % p.thetaRangeUse.size();
272 indices[3] = isector / (p.phiRangeUse.size() * p.invptRangeUse.size() * p.thetaRangeUse.size());
273 } else {
274 indices[0] = (p.phiRangeUse.size() == 1) ? 0 : isector;
275 indices[1] = (p.invptRangeUse.size() == 1) ? 0 : isector;
276 indices[2] = (p.thetaRangeUse.size() == 1) ? 0 : isector;
277 indices[3] = (p.SLpattern.size() == 1) ? 0 : isector;
278 }
279 return indices;
280}

◆ getRelId()

double getRelId ( const CDCTriggerSegmentHit & hit)

Calculate phi position of a hit relative to 2D track (scaled to number of wires).

Definition at line 453 of file NeuroTrigger.cc.

454{
455 int iSL = hit.getISuperLayer();
456 int priority = hit.getPriorityPosition();
457 // (((priority >> 1) & 1) - (priority & 1)) is 0, -1, 1, 0 for priority = 0, 1, 2, 3
458 double relId = hit.getSegmentID() + 0.5 * (((priority >> 1) & 1) - (priority & 1))
459 - m_TSoffset[iSL] - m_idRef[iSL][int(priority < 3)];
460 relId = remainder(relId, (m_TSoffset[iSL + 1] - m_TSoffset[iSL]));
461 return relId;
462}

◆ initialize()

void initialize ( const NeuroTriggerParameters & p)

Definition at line 35 of file NeuroTrigger.cc.

36{
37 // check parameters
38 bool okay = true;
39 // ensure that length of lists matches number of sectors
40 if (p.nHidden.size() != 1 && p.nHidden.size() != p.nMLP) {
41 B2ERROR("Number of nHidden lists should be 1 or " << p.nMLP);
42 okay = false;
43 }
44 if (p.outputScale.size() != 1 && p.outputScale.size() != p.nMLP) {
45 B2ERROR("Number of outputScale lists should be 1 or " << p.nMLP);
46 okay = false;
47 }
48 bool rangeProduct = (p.phiRangeUse.size() * p.invptRangeUse.size() * p.thetaRangeUse.size() * p.SLpattern.size() == p.nMLP);
49 if (!rangeProduct) {
50 if (p.phiRangeUse.size() != 1 && p.phiRangeUse.size() != p.nMLP) {
51 B2ERROR("Number of phiRangeUse.lists should be 1 or " << p.nMLP);
52 okay = false;
53 }
54 if (p.invptRangeUse.size() != 1 && p.invptRangeUse.size() != p.nMLP) {
55 B2ERROR("Number of invptRangeUse.lists should be 1 or " << p.nMLP);
56 okay = false;
57 }
58 if (p.thetaRangeUse.size() != 1 && p.thetaRangeUse.size() != p.nMLP) {
59 B2ERROR("Number of thetaRangeUse.lists should be 1 or " << p.nMLP);
60 okay = false;
61 }
62 if (p.SLpattern.size() != 1 && p.SLpattern.size() != p.nMLP) {
63 B2ERROR("Number of SLpattern lists should be 1 or " << p.nMLP);
64 okay = false;
65 }
66 }
67 // ensure that length of maxHitsPerSL and SLpatternMask lists matches SLpattern
68 if (p.maxHitsPerSL.size() != 1 && p.maxHitsPerSL.size() != p.SLpattern.size()) {
69 B2ERROR("Number of maxHitsPerSL lists should be 1 or " << p.SLpattern.size());
70 okay = false;
71 }
72 if (p.SLpatternMask.size() != 1 && p.SLpatternMask.size() != p.SLpattern.size()) {
73 B2ERROR("Number of SLpatternMask lists should be 1 or " << p.SLpattern.size());
74 okay = false;
75 }
76 // ensure that number of target nodes is valid
77 if (p.targetZ.isSet() || p.targetTheta.isSet()) {
78 unsigned short nTarget = int(p.targetZ) + int(p.targetTheta);
79 if (nTarget < 1) {
80 B2ERROR("No outputs! Turn on either targetZ or targetTheta.");
81 okay = false;
82 }
83 }
84 // ensure that sector ranges are valid
85 for (unsigned iPhi = 0; iPhi < p.phiRangeUse.size(); ++iPhi) {
86 if (p.phiRangeUse[iPhi].size() != 2) {
87 B2ERROR("phiRangeUse should be exactly 2 values");
88 okay = false;
89 continue;
90 }
91 if (p.phiRangeUse[iPhi][0] >= p.phiRangeUse[iPhi][1]) {
92 B2ERROR("phiRangeUse should be smaller than phiRangeUse");
93 okay = false;
94 }
95 if (p.phiRangeUse[iPhi][0] < -360. || p.phiRangeUse[iPhi][1] > 360. ||
96 (p.phiRangeUse[iPhi][1] - p.phiRangeUse[iPhi][0]) > 360.) {
97 B2ERROR("phiRangeUse should be in [-360, 360], with maximal width of 360");
98 okay = false;
99 }
100 }
101 for (unsigned iPt = 0; iPt < p.invptRangeUse.size(); ++iPt) {
102 if (p.invptRangeUse[iPt].size() != 2) {
103 B2ERROR("invptRangeUse should be exactly 2 values");
104 okay = false;
105 }
106 if (p.invptRangeUse[iPt][0] >= p.invptRangeUse[iPt][1]) {
107 B2ERROR("invptRangeUse should be smaller than invptRangeUse");
108 okay = false;
109 }
110 }
111 for (unsigned iTheta = 0; iTheta < p.thetaRangeUse.size(); ++iTheta) {
112 if (p.thetaRangeUse[iTheta].size() != 2) {
113 B2ERROR("thetaRangeUse should be exactly 2 values");
114 okay = false;
115 continue;
116 }
117 if (p.thetaRangeUse[iTheta][0] >= p.thetaRangeUse[iTheta][1]) {
118 B2ERROR("thetaRangeUse should be smaller than thetaRangeUse");
119 okay = false;
120 }
121 if (p.thetaRangeUse[iTheta][0] < 0. || p.thetaRangeUse[iTheta][1] > 180.) {
122 B2ERROR("thetaRangeUse should be in [0, 180]");
123 okay = false;
124 }
125 }
126 int nTarget = (int) p.targetZ + (int) p.targetTheta;
127 if (p.outputScale.size() == p.nMLP || p.outputScale.size() == 1) {
128 for (unsigned iScale = 0; iScale < p.outputScale.size(); ++iScale) {
129 if (p.outputScale[iScale].size() != 2 * static_cast<unsigned int>(nTarget)) {
130 B2ERROR("outputScale should be exactly " << 2 * nTarget << " values");
131 okay = false;
132 }
133 }
134 } else {
135 B2ERROR("the size of outputscale should be 1 or match the number of experts");
136 }
137 // ensure that train sectors are valid
138 if (p.phiRangeUse.size() != p.phiRangeTrain.size()) {
139 B2ERROR("Number of phiRangeUse.lists and phiRangeTrain lists should be equal.");
140 okay = false;
141 } else {
142 for (unsigned iPhi = 0; iPhi < p.phiRangeUse.size(); ++iPhi) {
143 if (p.phiRangeTrain[iPhi].size() != 2) {
144 B2ERROR("phiRangeTrain should be exactly 2 values.");
145 okay = false;
146 } else if (p.phiRangeTrain[iPhi][0] > p.phiRangeUse[iPhi][0] ||
147 p.phiRangeTrain[iPhi][1] < p.phiRangeUse[iPhi][1]) {
148 B2ERROR("phiRangeTrain should be wider than phiRangeUse.or equal.");
149 okay = false;
150 }
151 }
152 }
153 if (p.invptRangeUse.size() != p.invptRangeTrain.size()) {
154 B2ERROR("Number of invptRangeUse.lists and invptRangeTrain lists should be equal.");
155 okay = false;
156 } else {
157 for (unsigned iPt = 0; iPt < p.invptRangeUse.size(); ++iPt) {
158 if (p.invptRangeTrain[iPt].size() != 2) {
159 B2ERROR("invptRangeTrain should be exactly 2 values.");
160 okay = false;
161 } else if (p.invptRangeTrain[iPt][0] > p.invptRangeUse[iPt][0] ||
162 p.invptRangeTrain[iPt][1] < p.invptRangeUse[iPt][1]) {
163 B2ERROR("invptRangeTrain should be wider than invptRangeUse.or equal.");
164 okay = false;
165 }
166 }
167 }
168 if (p.thetaRangeUse.size() != p.thetaRangeTrain.size()) {
169 B2ERROR("Number of thetaRangeUse.lists and thetaRangeTrain lists should be equal.");
170 okay = false;
171 } else {
172 for (unsigned iTheta = 0; iTheta < p.thetaRangeUse.size(); ++iTheta) {
173 if (p.thetaRangeTrain[iTheta].size() != 2) {
174 B2ERROR("thetaRangeTrain should be exactly 2 values.");
175 okay = false;
176 } else if (p.thetaRangeTrain[iTheta][0] > p.thetaRangeUse[iTheta][0] ||
177 p.thetaRangeTrain[iTheta][1] < p.thetaRangeUse[iTheta][1]) {
178 B2ERROR("thetaRangeTrain should be wider than thetaRangeUse.or equal.");
179 okay = false;
180 }
181 }
182 }
183
184 if (!okay) return;
185 // Save flag from parameters
186 m_AdditionWireMode = p.AdditionWireMode;
187 m_AdditionInputPerSL = p.AdditionInputPerSL;
188 // initialize MLPs
189 m_MLPs.clear();
190 for (unsigned iMLP = 0; iMLP < p.nMLP; ++iMLP) {
191 //get indices for sector parameters
192 //this is important for cases, where we have experts specialized on different geometrical sectors as well as the pattern mask. since they are all in one array, we need the specific index of the expert. E.g. p.maxhitspersl cloud look like: [<expert-trained-on-slpattern0+thetabigger90>,<expert-trained-on-slpattern1+thetabigger90>,<expert-trained-on-slpattern0+thetasmaller90>,<expert-trained-on-slpattern1+thetasmaller90>]
193 vector<unsigned> indices = getRangeIndices(p, iMLP);
194 //get number of nodes for each layer
195 unsigned short maxHits = (p.maxHitsPerSL.size() == 1) ? p.maxHitsPerSL[0] : p.maxHitsPerSL[indices[3]];
196 unsigned long SLpattern = p.SLpattern[indices[3]];
197 unsigned long SLpatternMask = (p.SLpatternMask.size() == 1) ? p.SLpatternMask[0] : p.SLpatternMask[indices[3]];
198 unsigned short nInput = 27 * maxHits;
199 if (p.AdditionWireMode != 0)
200 nInput = 9 * (3 + p.AdditionInputPerSL) * maxHits;
201 vector<NNTParam<float>> nHidden = (p.nHidden.size() == 1) ? p.nHidden[0] : p.nHidden[iMLP];
202 vector<unsigned short> nNodes = {nInput};
203 for (unsigned iHid = 0; iHid < nHidden.size(); ++iHid) {
204 if (p.multiplyHidden) {
205 nNodes.push_back(nHidden[iHid] * nNodes[0]);
206 } else {
207 nNodes.push_back(nHidden[iHid]);
208 }
209 }
210 nNodes.push_back(nTarget);
211 unsigned short targetVars = int(p.targetZ) + (int(p.targetTheta) << 1);
212 // the parameters stored in the parameterset are not advanced enough to be vectors, they can only be single data types. the workaround was to make every variable contained in the (nested) vector an NNTParam. for the further use, those have to be converted to float vecors, which is done by the tcastvector function.
213 vector<float> phiRangeUse = p.tcastvector<float>(p.phiRangeUse)[indices[0]];
214 vector<float> invptRangeUse = p.tcastvector<float>(p.invptRangeUse)[indices[1]];
215 vector<float> thetaRangeUse = p.tcastvector<float>(p.thetaRangeUse)[indices[2]];
216 vector<float> phiRangeTrain = p.tcastvector<float>(p.phiRangeTrain)[indices[0]];
217 vector<float> invptRangeTrain = p.tcastvector<float>(p.invptRangeTrain)[indices[1]];
218 vector<float> thetaRangeTrain = p.tcastvector<float>(p.thetaRangeTrain)[indices[2]];
219 B2DEBUG(50, "Ranges for sector " << iMLP
220 << ": phiRange [" << phiRangeUse[0] << ", " << phiRangeUse[1]
221 << "], invptRange [" << invptRangeUse[0] << ", " << invptRangeUse[1]
222 << "], thetaRange [" << thetaRangeUse[0] << ", " << thetaRangeUse[1]
223 << "], SLpattern " << SLpattern);
224 //get scaling values
225 vector<float> outputScale = (p.outputScale.size() == 1) ? p.tcastvector<float>(p.outputScale)[0] : p.tcastvector<float>
226 (p.outputScale)[iMLP];
227 //convert phi and theta from degree to radian
228 phiRangeUse[0] *= Unit::deg;
229 phiRangeUse[1] *= Unit::deg;
230 thetaRangeUse[0] *= Unit::deg;
231 thetaRangeUse[1] *= Unit::deg;
232 phiRangeTrain[0] *= Unit::deg;
233 phiRangeTrain[1] *= Unit::deg;
234 thetaRangeTrain[0] *= Unit::deg;
235 thetaRangeTrain[1] *= Unit::deg;
236 if (p.targetTheta) {
237 outputScale[2 * int(p.targetZ)] *= Unit::deg;
238 outputScale[2 * int(p.targetZ) + 1] *= Unit::deg;
239 }
240 //create new MLP
241 m_MLPs.push_back(CDCTriggerMLP(nNodes, targetVars, outputScale,
242 phiRangeUse, invptRangeUse, thetaRangeUse,
243 phiRangeTrain, invptRangeTrain, thetaRangeTrain,
244 maxHits, SLpattern, SLpatternMask, p.tMax,
245 p.et_option()));
246 }
247
248 if (p.IDRanges.size() == p.nMLP) {
249 for (auto exp : p.IDRanges) {
250 // first entry is the expert number, after that follow the idranges for all the superlayers
251 std::vector<float> irange = {exp.begin() + 1, exp.end()};
252 m_MLPs[static_cast<int>(exp[0])].setRelID(irange);
253 }
254 } else if (p.IDRanges.size() == 0) {
255 B2WARNING("idranges have not been initialized yet, did you forget it?");
256 } else {
257 B2ERROR("number of idranges should match the number of experts!");
258 }
259 // load some values from the geometry that will be needed for the input
260 setConstants();
261}
std::vector< unsigned > getRangeIndices(const NeuroTriggerParameters &p, unsigned isector)
Get indices for sector ranges in parameter lists.
void setConstants()
Loads parameters from the geometry and precalculates some constants that will be needed.
static const double deg
degree to radians
Definition Unit.h:109

◆ initializeCollections() [1/2]

void initializeCollections ( std::string hitCollectionName)

Definition at line 312 of file NeuroTrigger.cc.

313{
314 m_segmentHits.isRequired(hitCollectionName);
315 m_hitCollectionName = hitCollectionName;
316}

◆ initializeCollections() [2/2]

void initializeCollections ( std::string hitCollectionName,
std::string eventTimeName,
const std::string & et_option )

set the hit collection and event time to required and store the hit collection name

Definition at line 302 of file NeuroTrigger.cc.

303{
304 m_segmentHits.isRequired(hitCollectionName);
305 if (!((et_option == "fastestpriority") || (et_option == "etfhwin") || (et_option == "zero") || (et_option == "fastest2d"))) {
306 m_eventTime.isRequired(eventTimeName);
307 }
308 m_hitCollectionName = hitCollectionName;
309}

◆ load()

bool load ( const std::string & filename,
const std::string & arrayname = "MLPs" )

Load MLPs from file.

Parameters
filenamename of the TFile to read from
arraynamename of the TObjArray holding the MLPs in the file
Returns
true if the MLPs were loaded correctly

Definition at line 1374 of file NeuroTrigger.cc.

1375{
1376 if (filename.size() < 1) {
1377 m_MLPs.clear();
1378 m_MLPs = m_cdctriggerneuroconfig->getMLPs();
1379 if (m_MLPs.size() == 0) {
1380 B2ERROR("Could not load Neurotrigger weights from database!");
1381 return false;
1382 }
1383 B2INFO("Loaded Neurotrigger MLP weights from database: " + m_cdctriggerneuroconfig->getNNName());
1384 B2DEBUG(100, "loaded " << m_MLPs.size() << " networks from database");
1385 // load some values from the geometry that will be needed for the input
1386 setConstants();
1387 return true;
1388 } else {
1389 TFile datafile(filename.c_str(), "READ");
1390 if (!datafile.IsOpen()) {
1391 B2WARNING("Could not open file " << filename);
1392 return false;
1393 }
1394
1395 TObjArray* MLPs = (TObjArray*)datafile.Get(arrayname.c_str());
1396 if (!MLPs) {
1397 datafile.Close();
1398 B2WARNING("File " << filename << " does not contain key " << arrayname);
1399 return false;
1400 }
1401 m_MLPs.clear();
1402 for (int isector = 0; isector < MLPs->GetEntriesFast(); ++isector) {
1403 CDCTriggerMLP* expert = dynamic_cast<CDCTriggerMLP*>(MLPs->At(isector));
1404 if (expert) m_MLPs.push_back(*expert);
1405 else B2WARNING("Wrong type " << MLPs->At(isector)->ClassName() << ", ignoring this entry.");
1406 }
1407 MLPs->Clear();
1408 delete MLPs;
1409 datafile.Close();
1410 B2DEBUG(100, "loaded " << m_MLPs.size() << " networks");
1411
1412 B2INFO("Loaded Neurotrigger MLP weights from file: " + filename);
1413 // load some values from the geometry that will be needed for the input
1414 setConstants();
1415
1416 return true;
1417 }
1418}
DBObjPtr< CDCTriggerNeuroConfig > m_cdctriggerneuroconfig
get NNT payload from database.

◆ loadIDHist()

bool loadIDHist ( const std::string & filename)

function to load idhist from file

Definition at line 1355 of file NeuroTrigger.cc.

1356{
1357 std::ifstream gzipfile(filename, ios_base::in | ios_base::binary);
1358 boost::iostreams::filtering_istream arrayStream;
1359 arrayStream.push(boost::iostreams::gzip_decompressor());
1360 arrayStream.push(gzipfile);
1361 CDCTriggerMLPData::HeaderSet hline;
1362 if (gzipfile.is_open()) {
1363 while (arrayStream >> hline) {
1364 for (unsigned i = 0; i < 18; ++i) {
1365 m_MLPs[hline.exPert].relevantID[i] = hline.relID[i];
1366 }
1367 }
1368 } else { return false;}
1369 return true;
1370}

◆ nSectors()

unsigned nSectors ( ) const
inline

return number of neural networks

Definition at line 164 of file NeuroTrigger.h.

164{ return m_MLPs.size(); }

◆ operator[]() [1/2]

CDCTriggerMLP & operator[] ( unsigned index)
inline

return reference to a neural network

Definition at line 159 of file NeuroTrigger.h.

159{ return m_MLPs[index]; }

◆ operator[]() [2/2]

const CDCTriggerMLP & operator[] ( unsigned index) const
inline

return const reference to a neural network

Definition at line 161 of file NeuroTrigger.h.

161{ return m_MLPs[index]; }

◆ runMLP()

vector< float > runMLP ( unsigned isector,
const std::vector< float > & input )

Run an expert MLP.

Parameters
isectorindex of the MLP
inputvector of input values
Returns
unscaled output values (z vertex in cm and/or theta in radian)

Definition at line 1253 of file NeuroTrigger.cc.

1254{
1255 const CDCTriggerMLP& expert = m_MLPs[isector];
1256 vector<float> weights = expert.getWeights();
1257 vector<float> layerinput = input;
1258 vector<float> layeroutput = {};
1259 unsigned iw = 0;
1260 for (unsigned il = 1; il < expert.nLayers(); ++il) {
1261 //add bias input
1262 layerinput.push_back(1.);
1263 //prepare output
1264 layeroutput.clear();
1265 layeroutput.assign(expert.nNodesLayer(il), 0.);
1266 //loop over outputs
1267 for (unsigned io = 0; io < layeroutput.size(); ++io) {
1268 //loop over inputs
1269 for (unsigned ii = 0; ii < layerinput.size(); ++ii) {
1270 layeroutput[io] += layerinput[ii] * weights[iw++];
1271 }
1272 //apply activation function
1273 layeroutput[io] = tanh(layeroutput[io] / 2.);
1274 }
1275 //output is new input
1276 layerinput = layeroutput;
1277 }
1278 return expert.unscaleTarget(layeroutput);
1279}

◆ runMLPFix()

vector< float > runMLPFix ( unsigned isector,
std::vector< float > input )

Run an expert MLP with fixed point arithmetic.

Definition at line 1282 of file NeuroTrigger.cc.

1283{
1284 unsigned precisionInput = m_precision[3];
1285 unsigned precisionWeights = m_precision[4];
1286 unsigned precisionLUT = m_precision[5];
1287 unsigned precisionTanh = m_precision[3];
1288 unsigned dp = precisionInput + precisionWeights - precisionLUT;
1289
1290 const CDCTriggerMLP& expert = m_MLPs[isector];
1291 // transform inputs to fixed point (cut off to input precision)
1292 vector<long> inputFix(input.size(), 0);
1293 for (unsigned ii = 0; ii < input.size(); ++ii) {
1294 inputFix[ii] = long(input[ii] * (1 << precisionInput));
1295 }
1296 // transform weights to fixed point (round to weight precision)
1297 vector<float> weights = expert.getWeights();
1298 vector<long> weightsFix(weights.size(), 0);
1299 for (unsigned iw = 0; iw < weights.size(); ++iw) {
1300 weightsFix[iw] = long(round(weights[iw] * (1 << precisionWeights)));
1301 }
1302 // maximum input value for the tanh LUT
1303 unsigned xMax = unsigned(ceil(atanh(1. - 1. / (1 << (precisionTanh + 1))) *
1304 (1 << (precisionLUT + 1))));
1305
1306 // run MLP
1307 vector<long> layerinput = inputFix;
1308 vector<long> layeroutput = {};
1309 unsigned iw = 0;
1310 for (unsigned il = 1; il < expert.nLayers(); ++il) {
1311 // add bias input
1312 layerinput.push_back(1 << precisionInput);
1313 // prepare output
1314 layeroutput.clear();
1315 layeroutput.assign(expert.nNodesLayer(il), 0);
1316 // loop over outputs
1317 for (unsigned io = 0; io < layeroutput.size(); ++io) {
1318 // loop over inputs
1319 for (unsigned ii = 0; ii < layerinput.size(); ++ii) {
1320 layeroutput[io] += layerinput[ii] * weightsFix[iw++];
1321 }
1322 // apply activation function -> LUT, calculated on the fly here
1323 unsigned long bin = abs(layeroutput[io]) >> dp;
1324 // correction to get symmetrical rounding errors
1325 float x = (bin + 0.5 - 1. / (1 << (dp + 1))) / (1 << precisionLUT);
1326 long tanhLUT = (bin < xMax) ? long(round(tanh(x / 2.) * (1 << precisionTanh))) : (1 << precisionTanh);
1327 layeroutput[io] = (layeroutput[io] < 0) ? -tanhLUT : tanhLUT;
1328 }
1329 // output is new input
1330 layerinput = layeroutput;
1331 }
1332
1333 // transform output back to float before unscaling
1334 vector<float> output(layeroutput.size(), 0.);
1335 for (unsigned io = 0; io < output.size(); ++io) {
1336 output[io] = layeroutput[io] / float(1 << precisionTanh);
1337 }
1338 return expert.unscaleTarget(output);
1339}
std::vector< unsigned > m_precision
Fixed point precision in bit after radix point.

◆ save()

void save ( const std::string & filename,
const std::string & arrayname = "MLPs" )

Save MLPs to file.

Parameters
filenamename of the TFile to write to
arraynamename of the TObjArray holding the MLPs in the file

Definition at line 1342 of file NeuroTrigger.cc.

1343{
1344 B2INFO("Saving networks to file " << filename << ", array " << arrayname);
1345 TFile datafile(filename.c_str(), "UPDATE");
1346 TObjArray* MLPs = new TObjArray(m_MLPs.size());
1347 for (unsigned isector = 0; isector < m_MLPs.size(); ++isector) {
1348 MLPs->Add(&m_MLPs[isector]);
1349 }
1350 MLPs->Write(arrayname.c_str(), TObject::kSingleKey | TObject::kOverwrite);
1351 datafile.Close();
1352 MLPs->Clear();
1353 delete MLPs;
1354}

◆ selectHits()

vector< unsigned > selectHits ( unsigned isector,
const CDCTriggerTrack & track,
bool returnAllRelevant = false )

Select best hits for each super layer.

Parameters
isectorindex of the MLP that will use the input
trackaxial hit relations are taken from given track
returnAllRelevantif true, return all relevant hits instead of selecting the best (for making relations)
Returns
list of selected hit indices

Definition at line 860 of file NeuroTrigger.cc.

862{
863 const CDCTriggerMLP& expert = m_MLPs[isector];
864 vector<unsigned> selectedHitIds = {};
865 // prepare vectors to keep best drift times, left/right and selected hit IDs
866 vector<int> tMin;
867 tMin.assign(expert.nNodesLayer(0), expert.getTMax());
868 vector<bool> LRknown;
869 LRknown.assign(expert.nNodesLayer(0), false);
870 vector<int> hitIds;
871 hitIds.assign(expert.nNodesLayer(0), -1);
872 vector<unsigned> nHits;
873 nHits.assign(9, 0);
874
875 // loop over axial hits related to input track
876 RelationVector<CDCTriggerSegmentHit> axialHits =
877 track.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName);
878 B2DEBUG(250, "start axial hit loop");
879 for (unsigned ihit = 0; ihit < axialHits.size(); ++ihit) {
880 // skip hits with negative relation weight (not selected in finder)
881 if (axialHits.weight(ihit) < 0) continue;
882 unsigned short iSL = axialHits[ihit]->getISuperLayer();
883 // skip stereo hits (should not be related to track, but check anyway)
884 if (iSL % 2 == 1) continue;
885 if (expert.getSLpatternUnmasked() != 0 &&
886 !((expert.getSLpatternUnmasked() >> iSL) & 1)) {
887 B2DEBUG(250, "skipping hit in SL " << iSL);
888 continue;
889 }
890 // get priority time and apply time window cut
891
892 int t = (m_hasT0) ? axialHits[ihit]->priorityTime() - m_T0 : 0;
893 if (t < 0) {
894 t = 0;
895 } else if (t > expert.getTMax()) {
896 t = expert.getTMax();
897 }
898 double relId = getRelId(*axialHits[ihit]);
899 if (expert.isRelevant(relId, iSL)) {
900 // get reference hit (worst of existing hits)
901 unsigned short iRef = iSL;
902 if (expert.getMaxHitsPerSL() > 1) {
903 if (nHits[iSL] < expert.getMaxHitsPerSL() &&
904 (expert.getSLpatternUnmasked() >> (iSL + 9 * nHits[iSL])) & 1) {
905 iRef += 9 * nHits[iSL];
906 ++nHits[iSL];
907 } else {
908 for (unsigned compare = iSL; compare < iSL + 9 * nHits[iSL]; compare += 9) {
909 if ((LRknown[iRef] && !LRknown[compare]) ||
910 (LRknown[iRef] == LRknown[compare] && tMin[iRef] < tMin[compare]))
911 iRef = compare;
912 }
913 }
914 }
915 // choose best hit (LR known before LR unknown, then shortest drift time)
916 bool useHit = false;
917 if (LRknown[iRef]) {
918 useHit = (axialHits[ihit]->LRknown() && t <= tMin[iRef]);
919 } else {
920 useHit = (axialHits[ihit]->LRknown() || t <= tMin[iRef]);
921 }
922 B2DEBUG(250, "relevant wire SL " << iSL << " LR " << axialHits[ihit]->getLeftRight()
923 << " t " << t << " iRef " << iRef << " useHit " << useHit);
924 if (useHit) {
925 // keep drift time and LR
926 LRknown[iRef] = axialHits[ihit]->LRknown();
927 tMin[iRef] = t;
928 hitIds[iRef] = axialHits[ihit]->getArrayIndex();
929 }
930 if (returnAllRelevant) selectedHitIds.push_back(axialHits[ihit]->getArrayIndex());
931 }
932 }
933
934 // loop over stereo hits, choosing up to MaxHitsPerSL per superlayer
935 B2DEBUG(250, "start stereo hit loop");
936 for (int ihit = 0; ihit < m_segmentHits.getEntries(); ++ ihit) {
937 unsigned short iSL = m_segmentHits[ihit]->getISuperLayer();
938 // skip axial hits
939 if (iSL % 2 == 0) continue;
940 if (expert.getSLpatternUnmasked() != 0 &&
941 !((expert.getSLpatternUnmasked() >> iSL) & 1)) {
942 B2DEBUG(250, "skipping hit in SL " << iSL);
943 continue;
944 }
945 // get priority time and apply time window cut
946 int t = (m_hasT0) ? m_segmentHits[ihit]->priorityTime() - m_T0 : 0;
947 if (t < 0) {
948 t = 0;
949 } else if (t > expert.getTMax()) {
950 t = expert.getTMax();
951 }
952 double relId = getRelId(*m_segmentHits[ihit]);
953 if (expert.isRelevant(relId, iSL)) {
954 // get reference hit (worst of existing hits)
955 unsigned short iRef = iSL;
956 if (expert.getMaxHitsPerSL() > 1) {
957 if (nHits[iSL] < expert.getMaxHitsPerSL() &&
958 (expert.getSLpatternUnmasked() >> (iSL + 9 * nHits[iSL])) & 1) {
959 iRef += 9 * nHits[iSL];
960 ++nHits[iSL];
961 } else {
962 for (unsigned compare = iSL; compare < iSL + 9 * nHits[iSL]; compare += 9) {
963 if ((LRknown[iRef] && !LRknown[compare]) ||
964 (LRknown[iRef] == LRknown[compare] && tMin[iRef] < tMin[compare]))
965 iRef = compare;
966 }
967 }
968 }
969 // choose best hit (LR known before LR unknown, then shortest drift time)
970 bool useHit = false;
971 if (LRknown[iRef]) {
972 useHit = (m_segmentHits[ihit]->LRknown() && t <= tMin[iRef]);
973 } else {
974 useHit = (m_segmentHits[ihit]->LRknown() || t <= tMin[iRef]);
975 }
976 B2DEBUG(250, "relevant wire SL " << iSL << " LR " << m_segmentHits[ihit]->getLeftRight()
977 << " t " << t << " iRef " << iRef << " useHit " << useHit);
978 if (useHit) {
979 // keep drift time and LR
980 LRknown[iRef] = m_segmentHits[ihit]->LRknown();
981 tMin[iRef] = t;
982 hitIds[iRef] = ihit;
983 }
984 if (returnAllRelevant) selectedHitIds.push_back(ihit);
985 }
986 }
987
988 // save selected hit Ids
989 if (!returnAllRelevant) {
990 for (unsigned iHit = 0; iHit < hitIds.size(); ++iHit) {
991 if (hitIds[iHit] >= 0) selectedHitIds.push_back(hitIds[iHit]);
992 }
993 }
994 return selectedHitIds;
995}

◆ selectHitsHWSim()

vector< unsigned > selectHitsHWSim ( unsigned isector,
const CDCTriggerTrack & track )

Select hits for each super layer from the ones related to input track.

Parameters
isectorindex of the MLP that will use the input
trackall hit relations are taken from given track
Returns
list of selected hit indices

Definition at line 781 of file NeuroTrigger.cc.

782{
783 const CDCTriggerMLP& expert = m_MLPs[isector];
784 vector<unsigned> selectedHitIds = {};
785 // prepare vectors to keep best drift times, left/right and selected hit IDs
786 vector<int> tMin;
787 tMin.assign(expert.nNodesLayer(0), expert.getTMax());
788 vector<bool> LRknown;
789 LRknown.assign(expert.nNodesLayer(0), false);
790 vector<int> hitIds;
791 hitIds.assign(expert.nNodesLayer(0), -1);
792 vector<unsigned> nHits;
793 nHits.assign(9, 0);
794
795 // loop over all hits related to input track
796 RelationVector<CDCTriggerSegmentHit> allHits =
797 track.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName);
798 B2DEBUG(250, "start hit loop over all related hits");
803 for (unsigned ihit = 0; ihit < allHits.size(); ++ihit) {
804 // skip hits with negative relation weight (not selected in finder)
805 if (allHits.weight(ihit) < 0) continue;
806 unsigned short iSL = allHits[ihit]->getISuperLayer();
807 if (expert.getSLpatternUnmasked() != 0 &&
808 !((expert.getSLpatternUnmasked() >> iSL) & 1)) {
809 B2DEBUG(250, "skipping hit in SL " << iSL);
810 continue;
811 }
812 // get priority time and apply time window cut
813 int t = (m_hasT0) ? allHits[ihit]->priorityTime() - m_T0 : 0;
814 if (t < 0) {
815 t = 0;
816 } else if (t > expert.getTMax()) {
817 t = expert.getTMax();
818 }
819 double relId = getRelId(*allHits[ihit]);
820 if (expert.isRelevant(relId, iSL)) {
821 // get reference hit (worst of existing hits)
822 unsigned short iRef = iSL;
823 if (expert.getMaxHitsPerSL() > 1) {
824 if (nHits[iSL] < expert.getMaxHitsPerSL() &&
825 (expert.getSLpatternUnmasked() >> (iSL + 9 * nHits[iSL])) & 1) {
826 iRef += 9 * nHits[iSL];
827 ++nHits[iSL];
828 } else {
829 for (unsigned compare = iSL; compare < iSL + 9 * nHits[iSL]; compare += 9) {
830 if ((LRknown[iRef] && !LRknown[compare]) ||
831 (LRknown[iRef] == LRknown[compare] && tMin[iRef] < tMin[compare]))
832 iRef = compare;
833 }
834 }
835 }
836 // choose best hit (LR known before LR unknown, then shortest drift time)
837 bool useHit = false;
838 if (LRknown[iRef]) {
839 useHit = (allHits[ihit]->LRknown() && t <= tMin[iRef]);
840 } else {
841 useHit = (allHits[ihit]->LRknown() || t <= tMin[iRef]);
842 }
843 B2DEBUG(250, "relevant wire SL " << iSL << " LR " << allHits[ihit]->getLeftRight()
844 << " t " << t << " iRef " << iRef << " useHit " << useHit);
845 if (useHit) {
846 // keep drift time and LR
847 LRknown[iRef] = allHits[ihit]->LRknown();
848 tMin[iRef] = t;
849 hitIds[iRef] = allHits[ihit]->getArrayIndex();
850 }
851 }
852 }
853 // save selected hit Ids
854 for (unsigned iHit = 0; iHit < hitIds.size(); ++iHit) {
855 if (hitIds[iHit] >= 0) selectedHitIds.push_back(hitIds[iHit]);
856 }
857 return selectedHitIds;
858}

◆ selectMLPbyPattern()

int selectMLPbyPattern ( std::vector< int > & MLPs,
unsigned long pattern,
const bool neurotrackinputmode )

Select one MLP from a list of sector indices.

The selected expert either matches the given sector pattern, or has no pattern restriction. An unrestricted expert is returned only if there is no exactly matching expert.

Returns
index of the selected MLP, -1 if no matching MLP is found

Definition at line 370 of file NeuroTrigger.cc.

371{
372 if (MLPs.size() == 0) {
373 return -1;
374 }
375
376 int bestIndex = -1;
377 for (unsigned i = 0; i < MLPs.size(); ++i) {
378 int isector = MLPs[i];
379 unsigned long sectorPattern = m_MLPs[isector].getSLpattern();
380 B2DEBUG(250, "hitPattern " << pattern << " sectorPattern " << sectorPattern);
381 // no hit pattern restriction -> keep looking for exact match
382 if (sectorPattern == 0) {
383 B2DEBUG(250, "found match for general sector");
384 bestIndex = isector;
385 }
386 // exact match -> keep this sector and stop searching
387 if (pattern == sectorPattern) {
388 B2DEBUG(250, "found match for hit pattern " << pattern);
389 bestIndex = isector;
390 break;
391 }
392 }
393
394 if (bestIndex < 0) {
395 if (neurotrackinputmode) {
396 B2DEBUG(150, "No sector found to match pattern, using sector 0" << pattern << ".");
397 bestIndex = 0;
398 } else {
399 B2DEBUG(150, "No sector found to match pattern " << pattern << ".");
400 }
401 }
402 return bestIndex;
403}

◆ selectMLPs()

vector< int > selectMLPs ( float phi0,
float invpt,
float theta )

Select all matching expert MLPs based on the given track parameters.

If the sectors are overlapping, there may be more than one matching expert. During training this is intended, afterwards sectors should be redefined to be unique. For unique geometrical sectors, this function can still find several experts with different sector patterns.

Returns
indices of the selected MLPs, empty if the track does not fit any sector

Definition at line 343 of file NeuroTrigger.cc.

344{
345 vector<int> indices = {};
346
347 if (m_MLPs.size() == 0) {
348 B2WARNING("Trying to select MLP before initializing MLPs.");
349 return indices;
350 }
351
352 // find all matching sectors
353 for (unsigned isector = 0; isector < m_MLPs.size(); ++isector) {
354 if (m_MLPs[isector].inPhiRangeUse(phi0) && m_MLPs[isector].inInvptRangeUse(invpt)
355 && m_MLPs[isector].inThetaRangeUse(theta)) {
356 indices.push_back(isector);
357 }
358 }
359
360 if (indices.size() == 0) {
361 B2DEBUG(150, "Track does not match any sector.");
362 B2DEBUG(150, "invpt=" << invpt << ", phi=" << phi0 * 180. / M_PI << ", theta=" << theta * 180. / M_PI);
363 }
364
365 return indices;
366}

◆ selectMLPsTrain()

vector< int > selectMLPsTrain ( float phi0,
float invpt,
float theta )

Select all matching expert MLPs based on the given track parameters.

If the sectors are overlapping, there may be more than one matching expert. During training this is intended, afterwards sectors should be redefined to be unique. For unique geometrical sectors, this function can still find several experts with different sector patterns.

Returns
indices of the selected MLPs, empty if the track does not fit any sector

Definition at line 318 of file NeuroTrigger.cc.

319{
320 vector<int> indices = {};
321
322 if (m_MLPs.size() == 0) {
323 B2WARNING("Trying to select MLP before initializing MLPs.");
324 return indices;
325 }
326
327 // find all matching sectors
328 for (unsigned isector = 0; isector < m_MLPs.size(); ++isector) {
329 if (m_MLPs[isector].inPhiRangeTrain(phi0) && m_MLPs[isector].inInvptRangeTrain(invpt)
330 && m_MLPs[isector].inThetaRangeTrain(theta)) {
331 indices.push_back(isector);
332 }
333 }
334
335 if (indices.size() == 0) {
336 B2DEBUG(150, "Track does not match any sector.");
337 B2DEBUG(150, "invpt=" << invpt << ", phi=" << phi0 * 180. / M_PI << ", theta=" << theta * 180. / M_PI);
338 }
339
340 return indices;
341}

◆ setConstants()

void setConstants ( )

Loads parameters from the geometry and precalculates some constants that will be needed.

Definition at line 284 of file NeuroTrigger.cc.

285{
286 CDCGeometryPar& cdc = CDCGeometryPar::Instance();
287 int layerId = 3;
288 int nTS = 0;
289 for (int iSL = 0; iSL < 9; ++iSL) {
290 m_TSoffset[iSL] = nTS;
291 nTS += cdc.nWiresInLayer(layerId);
292 m_TSoffset[iSL + 1] = nTS;
293 for (int priority = 0; priority < 5; ++ priority) {
294 m_radius[iSL][priority] = (iSL == 0 ? cdc.senseWireR(layerId + priority) : ((priority & 1) == 1 ? cdc.senseWireR(layerId + ceil(
295 priority / 2.0)) : cdc.senseWireR(layerId - ceil(priority / 2.0))));
296 }
297 layerId += (iSL > 0 ? 6 : 7);
298 }
299}
static CDCGeometryPar & Instance(const CDCGeometry *=nullptr)
Static method to get a reference to the CDCGeometryPar instance.
double m_radius[9][5]
Radius of the CDC layers with priority wires (2 per super layer)

◆ setPrecision()

void setPrecision ( const std::vector< unsigned > & precision)
inline

set fixed point precision

Definition at line 151 of file NeuroTrigger.h.

151{ m_precision = precision; }

◆ updateTrack()

void updateTrack ( const CDCTriggerTrack & track)

Calculate 2D phi position and arclength for the given track and store them.

Definition at line 407 of file NeuroTrigger.cc.

408{
409 double omega = track.getOmega(); // signed track curvature
410 for (int iSL = 0; iSL < 9; ++iSL) {
411 for (int priority = 0; priority < 5; ++priority) {
412 m_alpha[iSL][priority] = (m_radius[iSL][priority] * abs(omega) < 2.) ?
413 asin(m_radius[iSL][priority] * omega / 2.) :
414 M_PI_2;
415 m_idRef[iSL][priority] = remainder(((track.getPhi0() - m_alpha[iSL][priority]) *
416 (m_TSoffset[iSL + 1] - m_TSoffset[iSL]) / 2. / M_PI),
417 (m_TSoffset[iSL + 1] - m_TSoffset[iSL]));
418 }
419 }
420}

◆ updateTrackFix()

void updateTrackFix ( const CDCTriggerTrack & track)

Calculate 2D phi position and arclength for the given track and store them.

Definition at line 423 of file NeuroTrigger.cc.

424{
425 //unsigned precisionPhi = m_precision[0];
426 //unsigned precisionAlpha = m_precision[0];
427 unsigned precisionPhiAlpha = m_precision[0];
428 unsigned precisionScale = m_precision[1];
429 unsigned precisionId = m_precision[2];
430
431 double omega = track.getOmega();
432 long phi = round(track.getPhi0() * (1 << precisionPhiAlpha));
433
434 for (int iSL = 0; iSL < 9; ++iSL) {
435 for (int priority = 0; priority < 2; ++priority) {
436 // LUT, calculated on the fly here
437 m_alpha[iSL][priority] =
438 (m_radius[iSL][priority] * abs(omega) < 2) ?
439 round(asin(m_radius[iSL][priority] * omega / 2) * (1 << precisionPhiAlpha)) :
440 round(M_PI_2 * (1 << precisionPhiAlpha));
441 long dphi = phi - (long(m_alpha[iSL][priority]));
442 m_idRef[iSL][priority] =
443 long(dphi * round((m_TSoffset[iSL + 1] - m_TSoffset[iSL]) / 2. / M_PI * (1 << precisionScale)) /
444 (long(1) << (precisionPhiAlpha + precisionScale - precisionId)));
445 // unscale after rounding
446 m_alpha[iSL][priority] /= (1 << precisionPhiAlpha);
447 m_idRef[iSL][priority] /= (1 << precisionId);
448 }
449 }
450}

Member Data Documentation

◆ m_AdditionInputPerSL

int m_AdditionInputPerSL = 0
private

Value for additional input.

Definition at line 330 of file NeuroTrigger.h.

◆ m_AdditionWireMode

int m_AdditionWireMode = 0
private

Switch for addtional input modes.

Definition at line 328 of file NeuroTrigger.h.

◆ m_alpha

double m_alpha[9][5] = {{0}}
private

2D crossing angle of current track

Definition at line 322 of file NeuroTrigger.h.

322{{0}};

◆ m_cdctriggerneuroconfig

DBObjPtr<CDCTriggerNeuroConfig> m_cdctriggerneuroconfig
private

get NNT payload from database.

Definition at line 352 of file NeuroTrigger.h.

◆ m_eventTime

StoreObjPtr<BinnedEventT0> m_eventTime
private

StoreObjPtr containing the event time.

Definition at line 348 of file NeuroTrigger.h.

◆ m_hasT0

bool m_hasT0 = false
private

Flag to show if stored event time is valid.

Definition at line 326 of file NeuroTrigger.h.

◆ m_hitCollectionName

std::string m_hitCollectionName
private

Name of the StoreArray containing the input track segment hits.

Definition at line 350 of file NeuroTrigger.h.

◆ m_idRef

double m_idRef[9][5] = {{0}}
private

2D phi position of current track scaled to number of wires

Definition at line 320 of file NeuroTrigger.h.

320{{0}};

◆ m_MLPs

std::vector<CDCTriggerMLP> m_MLPs = {}
private

List of networks.

Definition at line 314 of file NeuroTrigger.h.

314{};

◆ m_precision

std::vector<unsigned> m_precision
private

Fixed point precision in bit after radix point.

8 values:

  • 2D track parameters: omega, phi
  • geometrical values derived from track: crossing angle, reference wire ID
  • scale factor: radian to wire ID
  • MLP values: nodes, weights, activation function LUT input (LUT output = nodes)

Definition at line 343 of file NeuroTrigger.h.

◆ m_radius

double m_radius[9][5] = {{0}}
private

Radius of the CDC layers with priority wires (2 per super layer)

Definition at line 316 of file NeuroTrigger.h.

316{{0}};

◆ m_segmentHits

StoreArray<CDCTriggerSegmentHit> m_segmentHits
private

StoreArray containing the input track segment hits.

Definition at line 346 of file NeuroTrigger.h.

◆ m_T0

int m_T0 = 0
private

Event time of current event / track.

Definition at line 324 of file NeuroTrigger.h.

◆ m_TMin

int m_TMin = -20
private

Min Drift time limitation for ETF case only.

Definition at line 332 of file NeuroTrigger.h.

◆ m_TSoffset

unsigned m_TSoffset[10] = {0}
private

Number of track segments up to super layer.

Definition at line 318 of file NeuroTrigger.h.

318{0};

The documentation for this class was generated from the following files: