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!! Read out the event time and store it.
 
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 > 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][2] = {}
 Radius of the CDC layers with priority wires (2 per super layer)
 
unsigned m_TSoffset [10] = {}
 Number of track segments up to super layer.
 
double m_idRef [9][2] = {}
 2D phi position of current track scaled to number of wires
 
double m_alpha [9][2] = {}
 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.
 
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); }
std::vector< CDCTriggerMLP > m_MLPs
List of networks.
Definition: NeuroTrigger.h:293

◆ 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 }
std::string get_et_option()
Return value of m_et_option.
Definition: NeuroTrigger.h:229

◆ 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 679 of file NeuroTrigger.cc.

680{
681 const CDCTriggerMLP& expert = m_MLPs[isector];
682 unsigned long chitPattern = 0;
683 vector<unsigned> nHits;
684 nHits.assign(9, 0);
685 // loop over axial hits related to input track
687 track.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName);
688 for (unsigned ihit = 0; ihit < axialHits.size(); ++ ihit) {
689 // skip hits with negative relation weight (not selected in finder)
690 if (axialHits.weight(ihit) < 0) {
691 continue;
692 }
693 unsigned short iSL = axialHits[ihit]->getISuperLayer();
694 // // skip stereo hits (should not be related to track, but check anyway)
695 if ((!neurotrackinputmode) && (iSL % 2 == 1)) continue;
696 double relId = getRelId(*axialHits[ihit]);
697 if (expert.isRelevant(relId, iSL)) {
698 if (nHits[iSL] < expert.getMaxHitsPerSL()) {
699 chitPattern |= 1 << (iSL + 9 * nHits[iSL]);
700 ++nHits[iSL];
701 }
702 }
703 }
704 if (!neurotrackinputmode) {
705 // loop over stereo hits
706 for (int ihit = 0; ihit < m_segmentHits.getEntries(); ++ ihit) {
707 unsigned short iSL = m_segmentHits[ihit]->getISuperLayer();
708 // skip axial hits
709 if (iSL % 2 == 0) continue;
710 // get priority time
711 double relId = getRelId(*m_segmentHits[ihit]);
712 if (expert.isRelevant(relId, iSL)) {
713 if (nHits[iSL] < expert.getMaxHitsPerSL()) {
714 chitPattern |= 1 << (iSL + 9 * nHits[iSL]);
715 ++nHits[iSL];
716 }
717 }
718 }
719 }
720 return chitPattern;
721}
Class to keep all parameters of an expert MLP for the neuro trigger.
Definition: CDCTriggerMLP.h:20
Combination of several CDCHits to a track segment hit for the trigger.
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.
Definition: NeuroTrigger.h:316
std::string m_hitCollectionName
Name of the StoreArray containing the input track segment hits.
Definition: NeuroTrigger.h:320
Class for type safe access to objects that are referred to in relations.
size_t size() const
Get number of relations.
float weight(int index) const
Get weight with index.
int getEntries() const
Get the number of objects in the array.
Definition: StoreArray.h:216

◆ 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 485 of file NeuroTrigger.cc.

486{
487
488 if (et_option != m_MLPs[isector].get_et_option()) {
489 B2DEBUG(20, "Used event time option is different to the one set in the MLP"
490 << LogVar("et_option", et_option) << LogVar("isector", isector)
491 << LogVar("et_option_mlp", m_MLPs[isector].get_et_option()));
492 }
493 if (et_option == "fastestpriority") {
494 B2DEBUG(200, "et_option is 'fastestpriority'");
495 m_T0 = 9999;
496 // find shortest time of related and relevant axial hits
498 track.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName);
499 m_T0 = getLowestTime(isector, Hits, false);
500 if (m_T0 < 9999) {
501 m_hasT0 = true;
502 } else {
503 m_T0 = 0;
504 m_hasT0 = false;
505 }
506
507 } else if (et_option == "fastest2d") {
508 m_T0 = 9999;
509 // find shortest time of related and relevant axial hits
511 track.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName);
512 m_T0 = getLowestTime(isector, Hits, true);
513 if (m_T0 < 9999) {
514 m_hasT0 = true;
515 } else {
516 m_T0 = 0;
517 m_hasT0 = false;
518 }
519 } else if (et_option == "zero") {
520 m_hasT0 = true;
521 m_T0 = 0;
522 } else if (et_option == "etf_only") {
523 bool hasT0 = (m_eventTime.isValid()) ? m_eventTime->hasBinnedEventT0(Const::CDC) : false;
524 if (hasT0) {
525 m_T0 = m_eventTime->getBinnedEventT0(Const::CDC);
526 m_hasT0 = true;
527 } else {
528 B2ERROR("No ETF output, but forced to use ETF!");
529 m_hasT0 = false;
530 m_T0 = 0;
531 }
532 } else if (et_option == "etf_or_fastestpriority") {
533 bool hasT0 = (m_eventTime.isValid()) ? m_eventTime->hasBinnedEventT0(Const::CDC) : false;
534 if (hasT0) {
535 m_T0 = m_eventTime->getBinnedEventT0(Const::CDC);
536 m_hasT0 = true;
537 } else {
538 getEventTime(isector, track, "fastestpriority", neuroinputmode);
551 }
552 } else if (et_option == "min_etf_fastestpriority") {
553 bool hasT0 = (m_eventTime.isValid()) ? m_eventTime->hasBinnedEventT0(Const::CDC) : false;
554 int T0_etf = 9999;
555 if (hasT0) {
556 T0_etf = m_eventTime->getBinnedEventT0(Const::CDC);
557 m_hasT0 = true;
558 }
559 getEventTime(isector, track, "fastestpriority", neuroinputmode);
560 if (m_T0 > T0_etf) {
561 m_T0 = T0_etf;
562 }
563 } else if (et_option == "etf_or_fastest2d") {
564 bool hasT0 = (m_eventTime.isValid()) ? m_eventTime->hasBinnedEventT0(Const::CDC) : false;
565 if (hasT0) {
566 m_T0 = m_eventTime->getBinnedEventT0(Const::CDC);
567 m_hasT0 = true;
568 } else {
569 getEventTime(isector, track, "fastest2d", neuroinputmode);
582 }
583 } else if (et_option == "etf_or_zero") {
584 bool hasT0 = (m_eventTime.isValid()) ? m_eventTime->hasBinnedEventT0(Const::CDC) : false;
585 if (hasT0) {
586 m_T0 = m_eventTime->getBinnedEventT0(Const::CDC);
587 m_hasT0 = true;
588 } else {
589 m_hasT0 = true;
590 m_T0 = 0;
591 }
592 } else if (et_option == "etfcc") {
593 if (track.getHasETFTime()) {
594 m_T0 = track.getETF_unpacked();
595 m_hasT0 = true;
596 } else {
597 m_T0 = 0;
598 m_hasT0 = false;
599 }
600
601 } else if (et_option == "etfcc_or_zero") {
602 if (track.getHasETFTime()) {
603 m_T0 = track.getETF_unpacked();
604 m_hasT0 = true;
605 } else {
606 m_T0 = 0;
607 m_hasT0 = true;
608 }
609
610 } else if (et_option == "etfcc_or_fastestpriority") {
611 if (track.getHasETFTime()) {
612 m_T0 = track.getETF_unpacked();
613 m_hasT0 = true;
614 } else {
615 getEventTime(isector, track, "fastestpriority", neuroinputmode);
616 }
617
618 } else if (et_option == "etfhwin") {
619 m_T0 = track.getETF_recalced();
620 m_hasT0 = true;
621
622 } else {
623 B2ERROR("No valid parameter for et_option (" << et_option << " )!");
624 }
625
626}
bool hasBinnedEventT0(const Const::EDetector detector) const
Check if one of the detectors in the given set has a binned t0 estimation.
int getBinnedEventT0(const Const::EDetector detector) const
Return the stored binned event t0 for the given detector or 0 if nothing stored.
int m_T0
Event time of current event / track.
Definition: NeuroTrigger.h:303
StoreObjPtr< BinnedEventT0 > m_eventTime
StoreObjPtr containing the event time.
Definition: NeuroTrigger.h:318
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.
Definition: NeuroTrigger.h:305
bool isValid() const
Check whether the object was created.
Definition: StoreObjPtr.h:111
Class to store variables with their name which were sent to the logging service.

◆ 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 724 of file NeuroTrigger.cc.

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

◆ 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)

Definition at line 993 of file NeuroTrigger.cc.

994{
995 const CDCTriggerMLP& expert = m_MLPs[isector];
996 // prepare empty input vector and vectors to keep best drift times
997 vector<float> inputVector;
998 inputVector.assign(expert.nNodesLayer(0), 0.);
999 // convert hits to input values
1000 vector<unsigned> nHits;
1001 nHits.assign(9, 0);
1002 for (unsigned ii = 0; ii < hitIds.size(); ++ii) {
1003 int ihit = hitIds[ii];
1004 unsigned short iSL = m_segmentHits[ihit]->getISuperLayer();
1005 unsigned short iRef = iSL + 9 * nHits[iSL];
1006 ++nHits[iSL];
1007 int priot = m_segmentHits[ihit]->priorityTime();
1008 int t = (m_hasT0) ? priot - m_T0 : 0;
1009 if (t < 0) {
1010 t = 0;
1011 } else if (t > expert.getTMax()) {
1012 t = expert.getTMax();
1013 }
1014 int LR = m_segmentHits[ihit]->getLeftRight();
1015 double relId = getRelId(*m_segmentHits[ihit]);
1016 int priority = m_segmentHits[ihit]->getPriorityPosition();
1017 // get scaled input values (scaling such that the absolute value of all inputs is < 1)
1018 inputVector[3 * iRef] = expert.scaleId(relId, iSL);
1019 float scaleT = pow(2, floor(log2(1. / expert.getTMax())));
1020 inputVector[3 * iRef + 1] = (((LR >> 1) & 1) - (LR & 1)) * t * scaleT;
1021 inputVector[3 * iRef + 2] = m_alpha[iSL][int(priority < 3)] * 0.5;
1022 }
1023 return inputVector;
1024}
double m_alpha[9][2]
2D crossing angle of current track
Definition: NeuroTrigger.h:301

◆ 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 461 of file NeuroTrigger.cc.

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

◆ 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 629 of file NeuroTrigger.cc.

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

◆ getRangeIndices()

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

Get indices for sector ranges in parameter lists.

Definition at line 260 of file NeuroTrigger.cc.

261{
262 // 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).
263 std::vector<unsigned> indices = {0, 0, 0, 0};
264 if (p.phiRangeUse.size() * p.invptRangeUse.size() * p.thetaRangeUse.size() * p.SLpattern.size() == p.nMLP) {
265 indices[0] = isector % p.phiRangeUse.size();
266 indices[1] = (isector / p.phiRangeUse.size()) % p.invptRangeUse.size();
267 indices[2] = (isector / (p.phiRangeUse.size() * p.invptRangeUse.size())) % p.thetaRangeUse.size();
268 indices[3] = isector / (p.phiRangeUse.size() * p.invptRangeUse.size() * p.thetaRangeUse.size());
269 } else {
270 indices[0] = (p.phiRangeUse.size() == 1) ? 0 : isector;
271 indices[1] = (p.invptRangeUse.size() == 1) ? 0 : isector;
272 indices[2] = (p.thetaRangeUse.size() == 1) ? 0 : isector;
273 indices[3] = (p.SLpattern.size() == 1) ? 0 : isector;
274 }
275 return indices;
276}

◆ getRelId()

double getRelId ( const CDCTriggerSegmentHit hit)

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

Definition at line 448 of file NeuroTrigger.cc.

449{
450 int iSL = hit.getISuperLayer();
451 int priority = hit.getPriorityPosition();
452 // (((priority >> 1) & 1) - (priority & 1)) is 0, -1, 1, 0 for priority = 0, 1, 2, 3
453 double relId = hit.getSegmentID() + 0.5 * (((priority >> 1) & 1) - (priority & 1))
454 - m_TSoffset[iSL] - m_idRef[iSL][int(priority < 3)];
455 relId = remainder(relId, (m_TSoffset[iSL + 1] - m_TSoffset[iSL]));
456 return relId;
457}
unsigned m_TSoffset[10]
Number of track segments up to super layer.
Definition: NeuroTrigger.h:297
double m_idRef[9][2]
2D phi position of current track scaled to number of wires
Definition: NeuroTrigger.h:299

◆ 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
186 // initialize MLPs
187 m_MLPs.clear();
188 for (unsigned iMLP = 0; iMLP < p.nMLP; ++iMLP) {
189 //get indices for sector parameters
190 //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>]
191 vector<unsigned> indices = getRangeIndices(p, iMLP);
192 //get number of nodes for each layer
193 unsigned short maxHits = (p.maxHitsPerSL.size() == 1) ? p.maxHitsPerSL[0] : p.maxHitsPerSL[indices[3]];
194 unsigned long SLpattern = p.SLpattern[indices[3]];
195 unsigned long SLpatternMask = (p.SLpatternMask.size() == 1) ? p.SLpatternMask[0] : p.SLpatternMask[indices[3]];
196 unsigned short nInput = 27 * maxHits;
197 vector<NNTParam<float>> nHidden = (p.nHidden.size() == 1) ? p.nHidden[0] : p.nHidden[iMLP];
198 vector<unsigned short> nNodes = {nInput};
199 for (unsigned iHid = 0; iHid < nHidden.size(); ++iHid) {
200 if (p.multiplyHidden) {
201 nNodes.push_back(nHidden[iHid] * nNodes[0]);
202 } else {
203 nNodes.push_back(nHidden[iHid]);
204 }
205 }
206 nNodes.push_back(nTarget);
207 unsigned short targetVars = int(p.targetZ) + (int(p.targetTheta) << 1);
208 // 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.
209 vector<float> phiRangeUse = p.tcastvector<float>(p.phiRangeUse)[indices[0]];
210 vector<float> invptRangeUse = p.tcastvector<float>(p.invptRangeUse)[indices[1]];
211 vector<float> thetaRangeUse = p.tcastvector<float>(p.thetaRangeUse)[indices[2]];
212 vector<float> phiRangeTrain = p.tcastvector<float>(p.phiRangeTrain)[indices[0]];
213 vector<float> invptRangeTrain = p.tcastvector<float>(p.invptRangeTrain)[indices[1]];
214 vector<float> thetaRangeTrain = p.tcastvector<float>(p.thetaRangeTrain)[indices[2]];
215 B2DEBUG(50, "Ranges for sector " << iMLP
216 << ": phiRange [" << phiRangeUse[0] << ", " << phiRangeUse[1]
217 << "], invptRange [" << invptRangeUse[0] << ", " << invptRangeUse[1]
218 << "], thetaRange [" << thetaRangeUse[0] << ", " << thetaRangeUse[1]
219 << "], SLpattern " << SLpattern);
220 //get scaling values
221 vector<float> outputScale = (p.outputScale.size() == 1) ? p.tcastvector<float>(p.outputScale)[0] : p.tcastvector<float>
222 (p.outputScale)[iMLP];
223 //convert phi and theta from degree to radian
224 phiRangeUse[0] *= Unit::deg;
225 phiRangeUse[1] *= Unit::deg;
226 thetaRangeUse[0] *= Unit::deg;
227 thetaRangeUse[1] *= Unit::deg;
228 phiRangeTrain[0] *= Unit::deg;
229 phiRangeTrain[1] *= Unit::deg;
230 thetaRangeTrain[0] *= Unit::deg;
231 thetaRangeTrain[1] *= Unit::deg;
232 if (p.targetTheta) {
233 outputScale[2 * int(p.targetZ)] *= Unit::deg;
234 outputScale[2 * int(p.targetZ) + 1] *= Unit::deg;
235 }
236 //create new MLP
237 m_MLPs.push_back(CDCTriggerMLP(nNodes, targetVars, outputScale,
238 phiRangeUse, invptRangeUse, thetaRangeUse,
239 phiRangeTrain, invptRangeTrain, thetaRangeTrain,
240 maxHits, SLpattern, SLpatternMask, p.tMax,
241 p.et_option()));
242 }
243
244 if (p.IDRanges.size() == p.nMLP) {
245 for (auto exp : p.IDRanges) {
246 // first entry is the expert number, after that follow the idranges for all the superlayers
247 std::vector<float> irange = {exp.begin() + 1, exp.end()};
248 m_MLPs[static_cast<int>(exp[0])].setRelID(irange);
249 }
250 } else if (p.IDRanges.size() == 0) {
251 B2WARNING("idranges have not been initialized yet, did you forget it?");
252 } else {
253 B2ERROR("number of idranges should match the number of experts!");
254 }
255 // load some values from the geometry that will be needed for the input
256 setConstants();
257}
Class to represent a complete set to describe a Neurotrigger.
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 307 of file NeuroTrigger.cc.

308{
309 m_segmentHits.isRequired(hitCollectionName);
310 m_hitCollectionName = hitCollectionName;
311}
bool isRequired(const std::string &name="")
Ensure this array/object has been registered previously.

◆ 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 297 of file NeuroTrigger.cc.

298{
299 m_segmentHits.isRequired(hitCollectionName);
300 if (!((et_option == "fastestpriority") || (et_option == "etfhwin") || (et_option == "zero") || (et_option == "fastest2d"))) {
301 m_eventTime.isRequired(eventTimeName);
302 }
303 m_hitCollectionName = hitCollectionName;
304}

◆ 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 1148 of file NeuroTrigger.cc.

1149{
1150 if (filename.size() < 1) {
1151 m_MLPs.clear();
1152 m_MLPs = m_cdctriggerneuroconfig->getMLPs();
1153 if (m_MLPs.size() == 0) {
1154 B2ERROR("Could not load Neurotrigger weights from database!");
1155 return false;
1156 }
1157 B2INFO("Loaded Neurotrigger MLP weights from database: " + m_cdctriggerneuroconfig->getNNName());
1158 B2DEBUG(100, "loaded " << m_MLPs.size() << " networks from database");
1159 // load some values from the geometry that will be needed for the input
1160 setConstants();
1161 return true;
1162 } else {
1163 TFile datafile(filename.c_str(), "READ");
1164 if (!datafile.IsOpen()) {
1165 B2WARNING("Could not open file " << filename);
1166 return false;
1167 }
1168
1169 TObjArray* MLPs = (TObjArray*)datafile.Get(arrayname.c_str());
1170 if (!MLPs) {
1171 datafile.Close();
1172 B2WARNING("File " << filename << " does not contain key " << arrayname);
1173 return false;
1174 }
1175 m_MLPs.clear();
1176 for (int isector = 0; isector < MLPs->GetEntriesFast(); ++isector) {
1177 CDCTriggerMLP* expert = dynamic_cast<CDCTriggerMLP*>(MLPs->At(isector));
1178 if (expert) m_MLPs.push_back(*expert);
1179 else B2WARNING("Wrong type " << MLPs->At(isector)->ClassName() << ", ignoring this entry.");
1180 }
1181 MLPs->Clear();
1182 delete MLPs;
1183 datafile.Close();
1184 B2DEBUG(100, "loaded " << m_MLPs.size() << " networks");
1185
1186 B2INFO("Loaded Neurotrigger MLP weights from file: " + filename);
1187 // load some values from the geometry that will be needed for the input
1188 setConstants();
1189
1190 return true;
1191 }
1192}
DBObjPtr< CDCTriggerNeuroConfig > m_cdctriggerneuroconfig
get NNT payload from database.
Definition: NeuroTrigger.h:322

◆ loadIDHist()

bool loadIDHist ( const std::string &  filename)

function to load idhist from file

Definition at line 1129 of file NeuroTrigger.cc.

1130{
1131 std::ifstream gzipfile(filename, ios_base::in | ios_base::binary);
1132 boost::iostreams::filtering_istream arrayStream;
1133 arrayStream.push(boost::iostreams::gzip_decompressor());
1134 arrayStream.push(gzipfile);
1136 if (gzipfile.is_open()) {
1137 while (arrayStream >> hline) {
1138 for (unsigned i = 0; i < 18; ++i) {
1139 m_MLPs[hline.exPert].relevantID[i] = hline.relID[i];
1140 }
1141 }
1142 } else { return false;}
1143 return true;
1144}

◆ 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 1027 of file NeuroTrigger.cc.

1028{
1029 const CDCTriggerMLP& expert = m_MLPs[isector];
1030 vector<float> weights = expert.getWeights();
1031 vector<float> layerinput = input;
1032 vector<float> layeroutput = {};
1033 unsigned iw = 0;
1034 for (unsigned il = 1; il < expert.nLayers(); ++il) {
1035 //add bias input
1036 layerinput.push_back(1.);
1037 //prepare output
1038 layeroutput.clear();
1039 layeroutput.assign(expert.nNodesLayer(il), 0.);
1040 //loop over outputs
1041 for (unsigned io = 0; io < layeroutput.size(); ++io) {
1042 //loop over inputs
1043 for (unsigned ii = 0; ii < layerinput.size(); ++ii) {
1044 layeroutput[io] += layerinput[ii] * weights[iw++];
1045 }
1046 //apply activation function
1047 layeroutput[io] = tanh(layeroutput[io] / 2.);
1048 }
1049 //output is new input
1050 layerinput = layeroutput;
1051 }
1052 return expert.unscaleTarget(layeroutput);
1053}

◆ runMLPFix()

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

Run an expert MLP with fixed point arithmetic.

Definition at line 1056 of file NeuroTrigger.cc.

1057{
1058 unsigned precisionInput = m_precision[3];
1059 unsigned precisionWeights = m_precision[4];
1060 unsigned precisionLUT = m_precision[5];
1061 unsigned precisionTanh = m_precision[3];
1062 unsigned dp = precisionInput + precisionWeights - precisionLUT;
1063
1064 const CDCTriggerMLP& expert = m_MLPs[isector];
1065 // transform inputs to fixed point (cut off to input precision)
1066 vector<long> inputFix(input.size(), 0);
1067 for (unsigned ii = 0; ii < input.size(); ++ii) {
1068 inputFix[ii] = long(input[ii] * (1 << precisionInput));
1069 }
1070 // transform weights to fixed point (round to weight precision)
1071 vector<float> weights = expert.getWeights();
1072 vector<long> weightsFix(weights.size(), 0);
1073 for (unsigned iw = 0; iw < weights.size(); ++iw) {
1074 weightsFix[iw] = long(round(weights[iw] * (1 << precisionWeights)));
1075 }
1076 // maximum input value for the tanh LUT
1077 unsigned xMax = unsigned(ceil(atanh(1. - 1. / (1 << (precisionTanh + 1))) *
1078 (1 << (precisionLUT + 1))));
1079
1080 // run MLP
1081 vector<long> layerinput = inputFix;
1082 vector<long> layeroutput = {};
1083 unsigned iw = 0;
1084 for (unsigned il = 1; il < expert.nLayers(); ++il) {
1085 // add bias input
1086 layerinput.push_back(1 << precisionInput);
1087 // prepare output
1088 layeroutput.clear();
1089 layeroutput.assign(expert.nNodesLayer(il), 0);
1090 // loop over outputs
1091 for (unsigned io = 0; io < layeroutput.size(); ++io) {
1092 // loop over inputs
1093 for (unsigned ii = 0; ii < layerinput.size(); ++ii) {
1094 layeroutput[io] += layerinput[ii] * weightsFix[iw++];
1095 }
1096 // apply activation function -> LUT, calculated on the fly here
1097 unsigned long bin = abs(layeroutput[io]) >> dp;
1098 // correction to get symmetrical rounding errors
1099 float x = (bin + 0.5 - 1. / (1 << (dp + 1))) / (1 << precisionLUT);
1100 long tanhLUT = (bin < xMax) ? long(round(tanh(x / 2.) * (1 << precisionTanh))) : (1 << precisionTanh);
1101 layeroutput[io] = (layeroutput[io] < 0) ? -tanhLUT : tanhLUT;
1102 }
1103 // output is new input
1104 layerinput = layeroutput;
1105 }
1106
1107 // transform output back to float before unscaling
1108 vector<float> output(layeroutput.size(), 0.);
1109 for (unsigned io = 0; io < output.size(); ++io) {
1110 output[io] = layeroutput[io] / float(1 << precisionTanh);
1111 }
1112 return expert.unscaleTarget(output);
1113}
std::vector< unsigned > m_precision
Fixed point precision in bit after radix point.
Definition: NeuroTrigger.h:313

◆ 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 1116 of file NeuroTrigger.cc.

1117{
1118 B2INFO("Saving networks to file " << filename << ", array " << arrayname);
1119 TFile datafile(filename.c_str(), "UPDATE");
1120 TObjArray* MLPs = new TObjArray(m_MLPs.size());
1121 for (unsigned isector = 0; isector < m_MLPs.size(); ++isector) {
1122 MLPs->Add(&m_MLPs[isector]);
1123 }
1124 MLPs->Write(arrayname.c_str(), TObject::kSingleKey | TObject::kOverwrite);
1125 datafile.Close();
1126 MLPs->Clear();
1127 delete MLPs;
1128}

◆ 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 855 of file NeuroTrigger.cc.

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

◆ 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 776 of file NeuroTrigger.cc.

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

◆ 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 365 of file NeuroTrigger.cc.

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

◆ 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 338 of file NeuroTrigger.cc.

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

◆ 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 313 of file NeuroTrigger.cc.

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

◆ setConstants()

void setConstants ( )

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

Definition at line 280 of file NeuroTrigger.cc.

281{
283 int layerId = 3;
284 int nTS = 0;
285 for (int iSL = 0; iSL < 9; ++iSL) {
286 m_TSoffset[iSL] = nTS;
287 nTS += cdc.nWiresInLayer(layerId);
288 m_TSoffset[iSL + 1] = nTS;
289 for (int priority = 0; priority < 2; ++ priority) {
290 m_radius[iSL][priority] = cdc.senseWireR(layerId + priority);
291 }
292 layerId += (iSL > 0 ? 6 : 7);
293 }
294}
The Class for CDC Geometry Parameters.
static CDCGeometryPar & Instance(const CDCGeometry *=nullptr)
Static method to get a reference to the CDCGeometryPar instance.
double m_radius[9][2]
Radius of the CDC layers with priority wires (2 per super layer)
Definition: NeuroTrigger.h:295

◆ 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 402 of file NeuroTrigger.cc.

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

◆ updateTrackFix()

void updateTrackFix ( const CDCTriggerTrack track)

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

Definition at line 418 of file NeuroTrigger.cc.

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

Member Data Documentation

◆ m_alpha

double m_alpha[9][2] = {}
private

2D crossing angle of current track

Definition at line 301 of file NeuroTrigger.h.

◆ m_cdctriggerneuroconfig

DBObjPtr<CDCTriggerNeuroConfig> m_cdctriggerneuroconfig
private

get NNT payload from database.

Definition at line 322 of file NeuroTrigger.h.

◆ m_eventTime

StoreObjPtr<BinnedEventT0> m_eventTime
private

StoreObjPtr containing the event time.

Definition at line 318 of file NeuroTrigger.h.

◆ m_hasT0

bool m_hasT0 = false
private

Flag to show if stored event time is valid.

Definition at line 305 of file NeuroTrigger.h.

◆ m_hitCollectionName

std::string m_hitCollectionName
private

Name of the StoreArray containing the input track segment hits.

Definition at line 320 of file NeuroTrigger.h.

◆ m_idRef

double m_idRef[9][2] = {}
private

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

Definition at line 299 of file NeuroTrigger.h.

◆ m_MLPs

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

List of networks.

Definition at line 293 of file NeuroTrigger.h.

◆ 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 313 of file NeuroTrigger.h.

◆ m_radius

double m_radius[9][2] = {}
private

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

Definition at line 295 of file NeuroTrigger.h.

◆ m_segmentHits

StoreArray<CDCTriggerSegmentHit> m_segmentHits
private

StoreArray containing the input track segment hits.

Definition at line 316 of file NeuroTrigger.h.

◆ m_T0

int m_T0 = 0
private

Event time of current event / track.

Definition at line 303 of file NeuroTrigger.h.

◆ m_TSoffset

unsigned m_TSoffset[10] = {}
private

Number of track segments up to super layer.

Definition at line 297 of file NeuroTrigger.h.


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