Skip to content

Commit c9c2b54

Browse files
authored
[Common] pidTPCModule::createNetworkPrediction(): refactor and add NN version 5 (#17912)
1 parent a183d02 commit c9c2b54

1 file changed

Lines changed: 112 additions & 87 deletions

File tree

‎Common/Tools/PID/pidTPCModule.h‎

Lines changed: 112 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,15 @@
2424
#include "Common/CCDB/ctpRateFetcher.h"
2525
#include "Common/Core/CollisionTypeHelper.h"
2626
#include "Common/Core/PID/TPCPIDResponse.h"
27+
#include "Common/Core/RecoDecay.h"
2728
#include "Common/Core/TableHelper.h"
2829
#include "Common/DataModel/EventSelection.h"
2930
#include "Common/DataModel/PIDResponseTPC.h"
3031
#include "Common/TableProducer/PID/pidTPCBase.h" // IWYU pragma: keep
3132
#include "Tools/ML/model.h"
3233

34+
#include <CommonConstants/MathConstants.h>
35+
#include <CommonConstants/PhysicsConstants.h>
3336
#include <DataFormatsParameters/GRPLHCIFData.h>
3437
#include <Framework/AnalysisDataModel.h>
3538
#include <Framework/AnalysisHelpers.h>
@@ -46,6 +49,7 @@
4649
#include <TRandom.h>
4750
#include <TString.h>
4851

52+
#include <array>
4953
#include <chrono>
5054
#include <cstddef>
5155
#include <cstdint>
@@ -54,6 +58,7 @@
5458
#include <memory>
5559
#include <ratio>
5660
#include <string>
61+
#include <string_view>
5762
#include <vector>
5863

5964
#include <math.h>
@@ -139,6 +144,9 @@ struct pidTPCConfigurables : o2::framework::ConfigurableGroup {
139144
o2::framework::Configurable<int> useNetworkAl{"useNetworkAl", 1, {"Switch for applying neural network on the alpha mass hypothesis (if network enabled) (set to 0 to disable)"}};
140145
o2::framework::Configurable<float> networkBetaGammaCutoff{"networkBetaGammaCutoff", 0.45, {"Lower value of beta-gamma to override the NN application"}};
141146
o2::framework::Configurable<std::string> cfgPathGrpLhcIf{"ccdb-path-grplhcif", "GLO/Config/GRPLHCIF", "Path on the CCDB for the GRPLHCIF object"};
147+
148+
o2::framework::Configurable<float> phiEntranceCoeff1{"phiEntranceCoeff1", 1.026f, "phiEntrance = phi + phiEntranceCoeff1 * LightSpeedDm2PS * 0.5 * phiEntranceCoeff2 * 1/pT[GeV/c]"};
149+
o2::framework::Configurable<float> phiEntranceCoeff2{"phiEntranceCoeff2", 85.f, "phiEntrance = phi + phiEntranceCoeff1 * LightSpeedDm2PS * 0.5 * phiEntranceCoeff2 * 1/pT[GeV/c]"};
142150
};
143151

144152
// helper getter - FIXME should be separate
@@ -445,7 +453,48 @@ class pidTPCModule
445453
template <typename TCCDB, typename M, typename T, typename B>
446454
std::unique_ptr<float[]> createNetworkPrediction(TCCDB& ccdb, soa::Join<aod::Collisions, aod::EvSels> const& collisions, M const& mults, T const& tracks, B const& bcs, const size_t size)
447455
{
448-
auto start_network_total = std::chrono::high_resolution_clock::now();
456+
constexpr int NParticleTypes = 9;
457+
constexpr double OneToKilo = 1.e-3;
458+
constexpr int NanoToOne = 1000000000;
459+
constexpr double MultiplicityNorm = 11000.;
460+
constexpr double HadronicRateNormPp = 1500.;
461+
constexpr double HadronicRateNormAa = 50.;
462+
constexpr double Ft0cOccupancyNorm = 60000.;
463+
constexpr float LightSpeedDm2PS = o2::constants::physics::LightSpeedCm2PS / 10.f;
464+
465+
struct NNVersionEntry {
466+
std::string_view versionName{};
467+
int numberOfFeatures{};
468+
int versionNumber{};
469+
};
470+
471+
constexpr std::array<NNVersionEntry, 6> NNVersionsDictionary{
472+
{{"", 6, 1},
473+
{"1", 6, 1},
474+
{"2", 7, 2},
475+
{"3", 8, 3},
476+
{"4", 9, 4},
477+
{"5", 9, 5}}};
478+
479+
enum IndexNnFeature : int {
480+
IdxTpcInnerParam = 0,
481+
IdxTgl,
482+
IdxSigned1Pt,
483+
IdxMass,
484+
IdxMultiplicity,
485+
IdxNClusters,
486+
IdxFt0cOcc,
487+
IdxHadronicRate,
488+
IdxModPhi
489+
};
490+
491+
constexpr int OldestNNVersionWithFt0c{2};
492+
constexpr int OldestNNVersionWithHadronicRate{3};
493+
constexpr int OldestNNVersionWithModPhi{4};
494+
constexpr int NNVersionWithModPhiEntrance{5};
495+
496+
const auto startNetworkTotal = std::chrono::high_resolution_clock::now();
497+
449498
if (pidTPCopts.autofetchNetworks) {
450499
const auto& bc = bcs.begin();
451500
// Initialise correct TPC response object before NN setup (for NCl normalisation)
@@ -483,7 +532,7 @@ class pidTPCModule
483532

484533
if (bc.timestamp() < network.getValidityFrom() || bc.timestamp() > network.getValidityUntil()) { // fetches network only if the runnumbers change
485534
LOG(info) << "Fetching network for timestamp: " << bc.timestamp();
486-
bool retrieveSuccess = ccdb->getCCDBAccessor().retrieveBlob(pidTPCopts.networkPathCCDB.value, ".", metadata, bc.timestamp(), false, pidTPCopts.networkPathLocally.value, "", "", &headers);
535+
const bool retrieveSuccess = ccdb->getCCDBAccessor().retrieveBlob(pidTPCopts.networkPathCCDB.value, ".", metadata, bc.timestamp(), false, pidTPCopts.networkPathLocally.value, "", "", &headers);
487536
networkVersion = headers["NN-Version"];
488537
if (retrieveSuccess) {
489538
network.initModel(pidTPCopts.networkPathLocally.value, pidTPCopts.enableNetworkOptimizations.value, pidTPCopts.networkSetNumThreads.value, strtoul(headers["Valid-From"].c_str(), NULL, 0), strtoul(headers["Valid-Until"].c_str(), NULL, 0));
@@ -497,52 +546,52 @@ class pidTPCModule
497546
}
498547

499548
// Defining some network parameters
500-
int input_dimensions = network.getNumInputNodes();
501-
int output_dimensions = network.getNumOutputNodes();
502-
const uint64_t track_prop_size = input_dimensions * size;
503-
const uint64_t prediction_size = output_dimensions * size;
549+
const int inputDimensions = network.getNumInputNodes();
550+
const int outputDimensions = network.getNumOutputNodes();
551+
const uint64_t trackPropSize = inputDimensions * size;
552+
const uint64_t predictionSize = outputDimensions * size;
553+
554+
int nnVersion{0};
555+
for (const auto& nnVersionEntry : NNVersionsDictionary) {
556+
if (networkVersion == nnVersionEntry.versionName && inputDimensions == nnVersionEntry.numberOfFeatures) {
557+
nnVersion = nnVersionEntry.versionNumber;
558+
break;
559+
}
560+
}
561+
if (nnVersion == 0) {
562+
LOG(fatal) << "createNetworkPrediction(): networkVersion '" << networkVersion << "' and number of features " << inputDimensions << " are not compatible according to NNVersionsDictionary";
563+
}
564+
565+
const int hadronicRateNorm = collsys == CollisionSystemType::kCollSyspp ? HadronicRateNormPp : HadronicRateNormAa;
504566

505567
// Deliberately uninitialised: the evaluation loop below writes every element
506568
// (one block per mass hypothesis), so zero-initialising would only touch
507569
// every page of an O(100 MB) buffer twice.
508-
std::unique_ptr<float[]> network_prediction(new float[prediction_size * 9]); // For each mass hypotheses
570+
std::unique_ptr<float[]> networkPrediction(new float[predictionSize * NParticleTypes]); // For each mass hypotheses
571+
509572
const float nNclNormalization = response->GetNClNormalization();
510-
float duration_network = 0;
573+
float durationNetwork = 0;
511574

512-
std::vector<float> track_properties(track_prop_size);
513-
uint64_t counter_track_props = 0;
514-
int loop_counter = 0;
575+
std::vector<float> trackProperties(trackPropSize);
576+
uint64_t counterTrackProps = 0;
577+
int loopCounter = 0;
515578

516579
// To load the Hadronic rate once for each collision
517-
float hadronicRateBegin = 0.;
518580
std::vector<float> hadronicRateForCollision(collisions.size(), 0.0f);
519-
size_t i = 0;
581+
size_t iCollision = 0;
520582
for (const auto& collision : collisions) {
521583
const auto& bc = collision.template bc_as<B>();
522584
if (irSource.compare("") != 0) {
523-
hadronicRateForCollision[i] = mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3;
524-
} else {
525-
hadronicRateForCollision[i] = 0.0f;
585+
hadronicRateForCollision[iCollision] = mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * OneToKilo;
526586
}
527-
i++;
528-
}
529-
auto bc = bcs.begin();
530-
if (irSource.compare("") != 0) {
531-
hadronicRateBegin = mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3; // kHz
532-
} else {
533-
hadronicRateBegin = 0.0f;
587+
++iCollision;
534588
}
589+
const auto bc = bcs.begin();
590+
const float hadronicRateBegin = irSource.compare("") != 0 ? mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * OneToKilo : 0.f;
535591

536592
// Filling a std::vector<float> to be evaluated by the network
537593
// Evaluation on single tracks brings huge overhead: Thus evaluation is done on one large vector
538-
static constexpr int NParticleTypes = 9;
539-
constexpr int ExpectedInputDimensionsNNV2 = 7;
540-
constexpr int ExpectedInputDimensionsNNV3 = 8;
541-
constexpr int ExpectedInputDimensionsNNV4 = 9;
542-
constexpr auto NetworkVersionV2 = "2";
543-
constexpr auto NetworkVersionV3 = "3";
544-
constexpr auto NetworkVersionV4 = "4";
545-
for (int j = 0; j < NParticleTypes; j++) { // Loop over particle number for which network correction is used
594+
for (int jParticleType = 0; jParticleType < NParticleTypes; ++jParticleType) { // Loop over particle number for which network correction is used
546595
for (auto const& trk : tracks) {
547596
if (!trk.hasTPC()) {
548597
continue;
@@ -552,74 +601,50 @@ class pidTPCModule
552601
continue;
553602
}
554603
}
555-
track_properties[counter_track_props] = trk.tpcInnerParam();
556-
track_properties[counter_track_props + 1] = trk.tgl();
557-
track_properties[counter_track_props + 2] = trk.signed1Pt();
558-
track_properties[counter_track_props + 3] = o2::track::pid_constants::sMasses[j];
559-
track_properties[counter_track_props + 4] = (trk.has_collision() && mults.size() > 0) ? mults[trk.collisionId()] / 11000. : 1.;
560-
track_properties[counter_track_props + 5] = std::sqrt(nNclNormalization / trk.tpcNClsFound());
561-
if (input_dimensions == ExpectedInputDimensionsNNV2 && networkVersion == NetworkVersionV2) {
562-
track_properties[counter_track_props + 6] = (trk.has_collision() && mults.size() > 0) ? collisions.iteratorAt(trk.collisionId()).ft0cOccupancyInTimeRange() / 60000. : 1.;
604+
const bool isGoodTrack = trk.has_collision() && mults.size() > 0;
605+
trackProperties[counterTrackProps + IdxTpcInnerParam] = trk.tpcInnerParam();
606+
trackProperties[counterTrackProps + IdxTgl] = trk.tgl();
607+
trackProperties[counterTrackProps + IdxSigned1Pt] = trk.signed1Pt();
608+
trackProperties[counterTrackProps + IdxMass] = o2::track::pid_constants::sMasses[jParticleType];
609+
trackProperties[counterTrackProps + IdxMultiplicity] = isGoodTrack ? mults[trk.collisionId()] / MultiplicityNorm : 1.;
610+
trackProperties[counterTrackProps + IdxNClusters] = std::sqrt(nNclNormalization / trk.tpcNClsFound());
611+
if (nnVersion >= OldestNNVersionWithFt0c) {
612+
trackProperties[counterTrackProps + IdxFt0cOcc] = isGoodTrack ? collisions.iteratorAt(trk.collisionId()).ft0cOccupancyInTimeRange() / Ft0cOccupancyNorm : 1.;
563613
}
564-
if (input_dimensions == ExpectedInputDimensionsNNV3 && networkVersion == NetworkVersionV3) {
565-
track_properties[counter_track_props + 6] = (trk.has_collision() && mults.size() > 0) ? collisions.iteratorAt(trk.collisionId()).ft0cOccupancyInTimeRange() / 60000. : 1.;
566-
if (trk.has_collision() && mults.size() > 0) {
567-
if (collsys == CollisionSystemType::kCollSyspp) {
568-
track_properties[counter_track_props + 7] = hadronicRateForCollision[trk.collisionId()] / 1500.;
569-
} else {
570-
track_properties[counter_track_props + 7] = hadronicRateForCollision[trk.collisionId()] / 50.;
571-
}
572-
} else {
573-
// asign Hadronic Rate at beginning of run if track does not belong to a collision
574-
if (collsys == CollisionSystemType::kCollSyspp) {
575-
track_properties[counter_track_props + 7] = hadronicRateBegin / 1500.;
576-
} else {
577-
track_properties[counter_track_props + 7] = hadronicRateBegin / 50.;
578-
}
579-
}
614+
if (nnVersion >= OldestNNVersionWithHadronicRate) {
615+
const float hadronicRate = isGoodTrack ? hadronicRateForCollision[trk.collisionId()] : hadronicRateBegin;
616+
trackProperties[counterTrackProps + IdxHadronicRate] = hadronicRate / hadronicRateNorm;
580617
}
581-
582-
if (input_dimensions == ExpectedInputDimensionsNNV4 && networkVersion == NetworkVersionV4) {
583-
track_properties[counter_track_props + 6] = (trk.has_collision() && mults.size() > 0) ? collisions.iteratorAt(trk.collisionId()).ft0cOccupancyInTimeRange() / 60000. : 1.;
584-
if (trk.has_collision() && mults.size() > 0) {
585-
if (collsys == CollisionSystemType::kCollSyspp) {
586-
track_properties[counter_track_props + 7] = hadronicRateForCollision[trk.collisionId()] / 1500.;
587-
} else {
588-
track_properties[counter_track_props + 7] = hadronicRateForCollision[trk.collisionId()] / 50.;
589-
}
590-
} else {
591-
// asign Hadronic Rate at beginning of run if track does not belong to a collision
592-
if (collsys == CollisionSystemType::kCollSyspp) {
593-
track_properties[counter_track_props + 7] = hadronicRateBegin / 1500.;
594-
} else {
595-
track_properties[counter_track_props + 7] = hadronicRateBegin / 50.;
596-
}
618+
if (nnVersion >= OldestNNVersionWithModPhi) {
619+
float phi = trk.phi();
620+
if (nnVersion == NNVersionWithModPhiEntrance) {
621+
phi += pidTPCopts.phiEntranceCoeff1 * LightSpeedDm2PS * 0.5 * pidTPCopts.phiEntranceCoeff2 * trk.signed1Pt();
597622
}
598-
track_properties[counter_track_props + 8] = std::fmod(std::fmod(trk.phi(), 2 * M_PI) + 2 * M_PI, M_PI / 9.0);
623+
trackProperties[counterTrackProps + IdxModPhi] = RecoDecay::constrainAngle(phi, 0.f, o2::constants::math::NSectors);
599624
}
600-
counter_track_props += input_dimensions;
625+
counterTrackProps += inputDimensions;
601626
}
602627

603-
auto start_network_eval = std::chrono::high_resolution_clock::now();
604-
float* output_network = network.evalModel(track_properties);
605-
auto stop_network_eval = std::chrono::high_resolution_clock::now();
606-
duration_network += std::chrono::duration<float, std::ratio<1, 1000000000>>(stop_network_eval - start_network_eval).count();
607-
for (uint64_t k = 0; k < prediction_size; k += output_dimensions) {
608-
for (int l = 0; l < output_dimensions; l++) {
609-
network_prediction[k + l + prediction_size * loop_counter] = output_network[k + l];
628+
const auto startNetworkEval = std::chrono::high_resolution_clock::now();
629+
const float* const outputNetwork = network.evalModel(trackProperties);
630+
const auto stopNetworkEval = std::chrono::high_resolution_clock::now();
631+
durationNetwork += std::chrono::duration<float, std::ratio<1, NanoToOne>>(stopNetworkEval - startNetworkEval).count();
632+
for (uint64_t kPrediction = 0; kPrediction < predictionSize; kPrediction += outputDimensions) {
633+
for (int lOutputDim = 0; lOutputDim < outputDimensions; ++lOutputDim) {
634+
networkPrediction[kPrediction + lOutputDim + predictionSize * loopCounter] = outputNetwork[kPrediction + lOutputDim];
610635
}
611636
}
612637

613-
counter_track_props = 0;
614-
loop_counter += 1;
638+
counterTrackProps = 0;
639+
++loopCounter;
615640
}
616-
track_properties.clear();
641+
trackProperties.clear();
617642

618-
auto stop_network_total = std::chrono::high_resolution_clock::now();
619-
LOG(debug) << "Neural Network for the TPC PID response correction: Time per track (eval ONNX): " << duration_network / (size * 9) << "ns ; Total time (eval ONNX): " << duration_network / 1000000000 << " s";
620-
LOG(debug) << "Neural Network for the TPC PID response correction: Time per track (eval + overhead): " << std::chrono::duration<float, std::ratio<1, 1000000000>>(stop_network_total - start_network_total).count() / (size * 9) << "ns ; Total time (eval + overhead): " << std::chrono::duration<float, std::ratio<1, 1000000000>>(stop_network_total - start_network_total).count() / 1000000000 << " s";
643+
const auto stopNetworkTotal = std::chrono::high_resolution_clock::now();
644+
LOG(debug) << "Neural Network for the TPC PID response correction: Time per track (eval ONNX): " << durationNetwork / (size * NParticleTypes) << "ns ; Total time (eval ONNX): " << durationNetwork / NanoToOne << " s";
645+
LOG(debug) << "Neural Network for the TPC PID response correction: Time per track (eval + overhead): " << std::chrono::duration<float, std::ratio<1, NanoToOne>>(stopNetworkTotal - startNetworkTotal).count() / (size * NParticleTypes) << "ns ; Total time (eval + overhead): " << std::chrono::duration<float, std::ratio<1, NanoToOne>>(stopNetworkTotal - startNetworkTotal).count() / NanoToOne << " s";
621646

622-
return network_prediction;
647+
return networkPrediction;
623648
}
624649

625650
//__________________________________________________

0 commit comments

Comments
 (0)