12 #include <torch/script.h> 13 #include <torch/torch.h> 30 DlVertexingAlgorithm::DlVertexingAlgorithm() :
34 m_rng(static_cast<std::mt19937::result_type>(std::chrono::high_resolution_clock::now().time_since_epoch().count()))
46 catch (StatusCodeException
e)
48 std::cout <<
"VertexAssessmentAlgorithm: Unable to write to ROOT tree" << std::endl;
62 return STATUS_CODE_SUCCESS;
67 const CaloHitList *pCaloHitList2D(
nullptr);
68 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetList(*
this,
"CaloHitList2D", pCaloHitList2D));
69 const MCParticleList *pMCParticleList(
nullptr);
70 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetCurrentList(*
this, pMCParticleList));
72 LArMCParticleHelper::GetMCToHitsMap(pCaloHitList2D, pMCParticleList, mcToHitsMap);
73 MCParticleList hierarchy;
74 LArMCParticleHelper::CompleteMCHierarchy(mcToHitsMap, hierarchy);
77 std::map<HitType, float> wireMin, wireMax;
78 float driftMin{std::numeric_limits<float>::max()}, driftMax{-std::numeric_limits<float>::max()};
81 const CaloHitList *pCaloHitList{
nullptr};
82 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetList(*
this, listname, pCaloHitList));
83 if (pCaloHitList->empty())
86 HitType view{pCaloHitList->front()->GetHitType()};
87 float viewDriftMin{driftMin}, viewDriftMax{driftMax};
88 this->
GetHitRegion(*pCaloHitList, viewDriftMin, viewDriftMax, wireMin[view], wireMax[view]);
89 driftMin = std::min(viewDriftMin, driftMin);
90 driftMax = std::max(viewDriftMax, driftMax);
92 for (
const std::string &listname : m_caloHitListNames)
94 const CaloHitList *pCaloHitList(
nullptr);
95 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetList(*
this, listname, pCaloHitList));
96 if (pCaloHitList->empty())
99 HitType view{pCaloHitList->front()->GetHitType()};
100 const bool isU{view == TPC_VIEW_U}, isV{view == TPC_VIEW_V}, isW{view == TPC_VIEW_W};
101 if (!(isU || isV || isW))
102 return STATUS_CODE_NOT_ALLOWED;
104 CartesianPointVector vertices;
105 for (
const MCParticle *mc : hierarchy)
107 if (LArMCParticleHelper::IsNeutrino(mc))
108 vertices.push_back(mc->GetVertex());
110 if (vertices.empty())
112 const CartesianVector &
vertex{vertices.front()};
114 const unsigned long nVertices{1};
115 unsigned long nHits{0};
116 const unsigned int nuance{LArMCParticleHelper::GetNuanceCode(hierarchy.front())};
119 const LArTransformationPlugin *transform{this->GetPandora().GetPlugins()->GetLArTransformationPlugin()};
120 const double xVtx{
vertex.GetX()};
130 double xMin{driftMin}, xMax{driftMax}, zMin{wireMin[view]}, zMax{wireMax[view]};
133 if (!(xVtx > (xMin - 1.
f) && xVtx < (xMax + 1.
f) && zVtx > (zMin - 1.
f) && zVtx < (zMax + 1.
f)))
137 featureVector.emplace_back(static_cast<double>(nuance));
138 featureVector.emplace_back(static_cast<double>(nVertices));
139 featureVector.emplace_back(xVtx);
140 featureVector.emplace_back(zVtx);
142 featureVector.emplace_back(xMin);
143 featureVector.emplace_back(xMax);
144 featureVector.emplace_back(zMin);
145 featureVector.emplace_back(zMax);
147 for (
const CaloHit *pCaloHit : *pCaloHitList)
149 const float x{pCaloHit->GetPositionVector().GetX()},
z{pCaloHit->GetPositionVector().GetZ()}, adc{pCaloHit->GetMipEquivalentEnergy()};
151 if (
m_pass > 1 && (x < xMin || x > xMax || z < zMin || z > zMax))
153 featureVector.emplace_back(static_cast<double>(
x));
154 featureVector.emplace_back(static_cast<double>(
z));
155 featureVector.emplace_back(static_cast<double>(adc));
158 featureVector.insert(featureVector.begin() + 8,
static_cast<double>(nHits));
161 LArMvaHelper::ProduceTrainingExample(trainingFilename,
true, featureVector);
164 return STATUS_CODE_SUCCESS;
180 if (pVertexList ==
nullptr || pVertexList->empty())
182 std::cout <<
"DLVertexing: Input vertex list is empty! Can't perform pass " <<
m_pass << std::endl;
183 return STATUS_CODE_SUCCESS;
187 std::map<HitType, float> wireMin, wireMax;
188 float driftMin{std::numeric_limits<float>::max()}, driftMax{-std::numeric_limits<float>::max()};
191 const CaloHitList *pCaloHitList{
nullptr};
192 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetList(*
this, listname, pCaloHitList));
193 if (pCaloHitList->empty())
196 HitType view{pCaloHitList->front()->GetHitType()};
197 float viewDriftMin{driftMin}, viewDriftMax{driftMax};
198 this->
GetHitRegion(*pCaloHitList, viewDriftMin, viewDriftMax, wireMin[view], wireMax[view]);
199 driftMin = std::min(viewDriftMin, driftMin);
200 driftMax = std::max(viewDriftMax, driftMax);
203 CartesianPointVector vertexCandidatesU, vertexCandidatesV, vertexCandidatesW;
204 for (
const std::string &listName : m_caloHitListNames)
206 const CaloHitList *pCaloHitList{
nullptr};
207 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetList(*
this, listName, pCaloHitList));
209 HitType view{pCaloHitList->front()->GetHitType()};
210 const bool isU{view == TPC_VIEW_U}, isV{view == TPC_VIEW_V}, isW{view == TPC_VIEW_W};
211 if (!isU && !isV && !isW)
212 return STATUS_CODE_NOT_ALLOWED;
216 this->
MakeNetworkInputFromHits(*pCaloHitList, view, driftMin, driftMax, wireMin[view], wireMax[view], input, pixelVector);
220 inputs.push_back(input);
229 int colOffset{0}, rowOffset{0}, canvasWidth{
m_width}, canvasHeight{
m_height};
230 this->
GetCanvasParameters(output, pixelVector, colOffset, rowOffset, canvasWidth, canvasHeight);
232 float **canvas{
new float *[canvasHeight]};
233 for (
int row = 0; row < canvasHeight; ++row)
234 canvas[row] =
new float[canvasWidth]{};
237 auto classes{torch::argmax(output, 1)};
239 auto classesAccessor{classes.accessor<int64_t, 3>()};
241 std::map<int, bool> haveSeenMap;
242 for (
const auto &[row,
col] : pixelVector)
244 const auto cls{classesAccessor[0][row][
col]};
247 const int inner{
static_cast<int>(std::round(std::ceil(scaleFactor *
m_thresholds[cls - 1])))};
248 const int outer{
static_cast<int>(std::round(std::ceil(scaleFactor *
m_thresholds[cls])))};
253 CartesianPointVector positionVector;
255 canvas, canvasWidth, canvasHeight, colOffset, rowOffset, view, driftMin, driftMax, wireMin[view], wireMax[view], positionVector);
257 vertexCandidatesU.emplace_back(positionVector.front());
259 vertexCandidatesV.emplace_back(positionVector.front());
261 vertexCandidatesW.emplace_back(positionVector.front());
266 PANDORA_MONITORING_API(SetEveDisplayParameters(this->GetPandora(),
true, DETECTOR_VIEW_XZ, -1.
f, 1.
f, 1.
f));
267 const MCParticleList *pMCParticleList{
nullptr};
268 PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetCurrentList(*
this, pMCParticleList));
270 CartesianVector trueVertex3D(0, 0, 0);
271 if (LArMCParticleHelper::GetTrueVertex(pMCParticleList, trueVertex3D))
273 float x{0.f}, u{0.f}, v{0.f},
w{0.f};
274 const LArTransformationPlugin *transform{this->GetPandora().GetPlugins()->GetLArTransformationPlugin()};
275 LArVertexHelper::GetTrueVertexPosition(trueVertex3D, transform,
x, u, v,
w);
278 const CartesianVector trueVertex(
x, 0.
f, u);
279 PANDORA_MONITORING_API(AddMarkerToVisualization(this->GetPandora(), &trueVertex,
"U(true)", BLUE, 3));
283 const CartesianVector trueVertex(
x, 0.
f, v);
284 PANDORA_MONITORING_API(AddMarkerToVisualization(this->GetPandora(), &trueVertex,
"V(true)", BLUE, 3));
288 const CartesianVector trueVertex(
x, 0.
f,
w);
289 PANDORA_MONITORING_API(AddMarkerToVisualization(this->GetPandora(), &trueVertex,
"W(true)", BLUE, 3));
291 for (
const auto &pos : positionVector)
293 std::string label{isU ?
"U" : isV ?
"V" :
"W"};
294 PANDORA_MONITORING_API(AddMarkerToVisualization(this->GetPandora(), &pos, label, RED, 3));
297 PANDORA_MONITORING_API(ViewEvent(this->GetPandora()));
301 for (
int row = 0; row < canvasHeight; ++row)
302 delete[] canvas[row];
307 if (vertexCandidatesU.empty())
309 if (vertexCandidatesV.empty())
311 if (vertexCandidatesW.empty())
313 std::vector<VertexTuple> vertexTuples;
314 CartesianPointVector candidates3D;
315 if (nEmptyLists == 0)
317 vertexTuples.emplace_back(
VertexTuple(this->GetPandora(), vertexCandidatesU.front(), vertexCandidatesV.front(), vertexCandidatesW.front()));
319 else if (nEmptyLists == 1)
321 if (vertexCandidatesU.empty())
323 vertexTuples.emplace_back(
VertexTuple(this->GetPandora(), vertexCandidatesV.front(), vertexCandidatesW.front(), TPC_VIEW_V, TPC_VIEW_W));
325 else if (vertexCandidatesV.empty())
327 vertexTuples.emplace_back(
VertexTuple(this->GetPandora(), vertexCandidatesU.front(), vertexCandidatesW.front(), TPC_VIEW_U, TPC_VIEW_W));
331 vertexTuples.emplace_back(
VertexTuple(this->GetPandora(), vertexCandidatesU.front(), vertexCandidatesV.front(), TPC_VIEW_U, TPC_VIEW_V));
336 std::cout <<
"Insufficient 2D vertices to reconstruct a 3D vertex" << std::endl;
337 return STATUS_CODE_NOT_FOUND;
342 PANDORA_MONITORING_API(AddMarkerToVisualization(this->GetPandora(), &vertexTuples.front().GetPosition(),
"candidate", GREEN, 1));
345 if (!vertexTuples.empty())
347 const CartesianVector &
vertex{vertexTuples.front().GetPosition()};
348 CartesianPointVector vertexCandidates;
349 vertexCandidates.emplace_back(
vertex);
354 std::cout <<
"Insufficient 2D vertices to reconstruct a 3D vertex" << std::endl;
355 return STATUS_CODE_NOT_FOUND;
360 PANDORA_MONITORING_API(ViewEvent(this->GetPandora()));
363 return STATUS_CODE_SUCCESS;
372 const LArTPC *
const pTPC(this->GetPandora().GetGeometry()->GetLArTPCMap().
begin()->
second);
373 const float pitch(view == TPC_VIEW_U ? pTPC->GetWirePitchU() : view == TPC_VIEW_V ? pTPC->GetWirePitchV() : pTPC->GetWirePitchW());
376 std::vector<double> xBinEdges(
m_width + 1);
377 std::vector<double> zBinEdges(
m_height + 1);
380 for (
int i = 1; i <
m_width + 1; ++i)
381 xBinEdges[i] = xBinEdges[i - 1] + dx;
382 zBinEdges[0] = zMin - 0.5f * pitch;
383 const double dz = ((zMax + 0.5f * pitch) - zBinEdges[0]) /
m_height;
384 for (
int i = 1; i <
m_height + 1; ++i)
385 zBinEdges[i] = zBinEdges[i - 1] + dz;
388 auto accessor = networkInput.accessor<float, 4>();
390 for (
const CaloHit *pCaloHit : caloHits)
392 const float x{pCaloHit->GetPositionVector().GetX()};
393 const float z{pCaloHit->GetPositionVector().GetZ()};
396 if (x < xMin || x > xMax || z < zMin || z > zMax)
399 const float adc{pCaloHit->GetMipEquivalentEnergy()};
400 const int pixelX{
static_cast<int>(std::floor((
x - xBinEdges[0]) / dx))};
401 const int pixelZ{
static_cast<int>(std::floor((
z - zBinEdges[0]) / dz))};
402 accessor[0][0][pixelZ][pixelX] += adc;
404 for (
int row = 0; row <
m_height; ++row)
408 const float value{accessor[0][0][row][
col]};
410 pixelVector.emplace_back(std::make_pair(row,
col));
414 return STATUS_CODE_SUCCESS;
420 const int columnOffset,
const int rowOffset,
const HitType view,
const float xMin,
const float xMax,
const float zMin,
const float zMax,
421 CartesianPointVector &positionVector)
const 424 const LArTPC *
const pTPC(this->GetPandora().GetGeometry()->GetLArTPCMap().
begin()->
second);
425 const float pitch(view == TPC_VIEW_U ? pTPC->GetWirePitchU() : view == TPC_VIEW_V ? pTPC->GetWirePitchV() : pTPC->GetWirePitchW());
428 const double dz = ((zMax + 0.5f * pitch) - (zMin - 0.5
f * pitch)) /
m_height;
431 int rowBest{0}, colBest{0};
432 for (
int row = 0; row < canvasHeight; ++row)
433 for (
int col = 0;
col < canvasWidth; ++
col)
434 if (canvas[row][
col] > 0 && canvas[row][
col] > best)
436 best = canvas[row][
col];
441 const float x{
static_cast<float>((colBest - columnOffset) * dx + xMin)};
442 const float z{
static_cast<float>((rowBest - rowOffset) * dz + zMin)};
444 CartesianVector
pt(
x, 0.
f,
z);
445 positionVector.emplace_back(pt);
447 return STATUS_CODE_SUCCESS;
455 std::string temporaryListName;
456 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::CreateTemporaryListAndSetCurrent(*
this, pVertexList, temporaryListName));
458 for (
const CartesianVector &position : positions)
460 PandoraContentApi::Vertex::Parameters parameters;
461 parameters.m_position = position;
462 parameters.m_vertexLabel = VERTEX_INTERACTION;
463 parameters.m_vertexType = VERTEX_3D;
465 const Vertex *pVertex(
nullptr);
466 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::Vertex::Create(*
this, parameters, pVertex));
468 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::SaveList<Vertex>(*
this,
m_outputVertexListName));
469 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::ReplaceCurrentList<Vertex>(*
this,
m_outputVertexListName));
471 return STATUS_CODE_SUCCESS;
475 void DlVertexingAlgorithm::PopulateRootTree(
const std::vector<VertexTuple> &vertexTuples,
const pandora::CartesianPointVector &vertexCandidatesU,
476 const pandora::CartesianPointVector &vertexCandidatesV,
const pandora::CartesianPointVector &vertexCandidatesW)
const 480 const MCParticleList *pMCParticleList{
nullptr};
481 if (STATUS_CODE_SUCCESS == PandoraContentApi::GetCurrentList(*
this, pMCParticleList))
487 LArMCParticleHelper::GetPrimaryMCParticleList(pMCParticleList, primaries);
488 if (!primaries.empty())
490 const MCParticle *primary{primaries.front()};
491 const MCParticleList &parents{primary->GetParentList()};
492 if (parents.size() == 1)
494 const MCParticle *trueNeutrino{parents.front()};
495 if (LArMCParticleHelper::IsNeutrino(trueNeutrino))
497 const LArTransformationPlugin *transform{this->GetPandora().GetPlugins()->GetLArTransformationPlugin()};
498 const CartesianVector &trueVertex{primaries.front()->GetVertex()};
499 if (LArVertexHelper::IsInFiducialVolume(this->GetPandora(), trueVertex,
m_volumeType))
501 const CartesianVector &recoVertex{vertexTuples.front().GetPosition()};
502 const float tx{trueVertex.GetX()};
503 const float tu{
static_cast<float>(transform->YZtoU(trueVertex.GetY(), trueVertex.GetZ()))};
504 const float tv{
static_cast<float>(transform->YZtoV(trueVertex.GetY(), trueVertex.GetZ()))};
505 const float tw{
static_cast<float>(transform->YZtoW(trueVertex.GetY(), trueVertex.GetZ()))};
506 const float rx_u{vertexCandidatesU.front().GetX()};
507 const float ru{vertexCandidatesU.front().GetZ()};
508 const float rx_v{vertexCandidatesV.front().GetX()};
509 const float rv{vertexCandidatesV.front().GetZ()};
510 const float rx_w{vertexCandidatesW.front().GetX()};
511 const float rw{vertexCandidatesW.front().GetZ()};
512 const float dr_u{std::sqrt((rx_u - tx) * (rx_u - tx) + (ru - tu) * (ru - tu))};
513 const float dr_v{std::sqrt((rx_v - tx) * (rx_v - tx) + (rv - tv) * (rv - tv))};
514 const float dr_w{std::sqrt((rx_w - tx) * (rx_w - tx) + (rw - tw) * (rw - tw))};
515 const CartesianVector &dv{recoVertex - trueVertex};
516 const float dr{dv.GetMagnitude()};
517 const float dx{dv.GetX()}, dy{dv.GetY()}, dz{dv.GetZ()};
520 PANDORA_MONITORING_API(SetTreeVariable(this->GetPandora(),
m_rootTreeName,
"dr_u", dr_u));
521 PANDORA_MONITORING_API(SetTreeVariable(this->GetPandora(),
m_rootTreeName,
"dr_v", dr_v));
522 PANDORA_MONITORING_API(SetTreeVariable(this->GetPandora(),
m_rootTreeName,
"dr_w", dr_w));
523 PANDORA_MONITORING_API(SetTreeVariable(this->GetPandora(),
m_rootTreeName,
"dr", dr));
524 PANDORA_MONITORING_API(SetTreeVariable(this->GetPandora(),
m_rootTreeName,
"dx", dx));
525 PANDORA_MONITORING_API(SetTreeVariable(this->GetPandora(),
m_rootTreeName,
"dy", dy));
526 PANDORA_MONITORING_API(SetTreeVariable(this->GetPandora(),
m_rootTreeName,
"dz", dz));
527 PANDORA_MONITORING_API(FillTree(this->GetPandora(),
m_rootTreeName));
544 PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
"Visualise",
m_visualise));
548 PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
"WriteTree",
m_writeTree));
551 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ReadValue(xmlHandle,
"RootTreeName",
m_rootTreeName));
552 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ReadValue(xmlHandle,
"RootFileName",
m_rootFileName));
556 return STATUS_CODE_SUCCESS;
int m_pass
The pass of the train/infer step.
std::unordered_map< const pandora::MCParticle *, pandora::CaloHitList > MCContributionMap
Header file for the lar deep learning helper helper class.
pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
MvaTypes::MvaFeatureVector MvaFeatureVector
LArDLHelper::TorchModel m_modelU
The model for the U view.
bool m_visualise
Whether or not to visualise the candidate vertices.
int m_event
The current event number.
pandora::StatusCode PrepareTrainingSample()
std::vector< double > m_thresholds
Distance class thresholds.
int m_width
The width of the images.
float m_driftStep
The size of a pixel in the drift direction in cm (most relevant in pass 2)
std::string m_trainingOutputFile
Output file name for training examples.
std::string m_outputVertexListName
Output vertex list name.
Header file for the geometry helper class.
std::mt19937 m_rng
The random number generator.
static void DrawRing(float **canvas, const int row, const int col, const int inner, const int outer, const float weight)
Add a filled ring to the specified canvas. The ring has an inner radius based on the minimum predicte...
std::string m_rootTreeName
The ROOT tree name.
bool m_writeTree
Whether or not to write validation details to a ROOT tree.
std::vector< Pixel > PixelVector
pandora::StringVector m_caloHitListNames
Names of input calo hit lists.
std::vector< art::Ptr< simb::MCParticle > > MCParticleVector
Header file for the file helper class.
pandora::StatusCode Run()
std::string m_inputVertexListName
Input vertex list name if 2nd pass.
void GetHitRegion(const pandora::CaloHitList &caloHitList, float &xMin, float &xMax, float &zMin, float &zMax) const
static void Forward(TorchModel &model, const TorchInputVector &input, TorchOutput &output)
Run a deep learning model.
void GetCanvasParameters(const LArDLHelper::TorchOutput &networkOutput, const PixelVector &pixelVector, int &columnOffset, int &rowOffset, int &width, int &height) const
Determines the parameters of the canvas for extracting the vertex location. The network predicts the ...
Header file for the vertex helper class.
pandora::StatusCode MakeWirePlaneCoordinatesFromCanvas(float **canvas, const int canvasWidth, const int canvasHeight, const int columnOffset, const int rowOffset, const pandora::HitType view, const float xMin, const float xMax, const float zMin, const float zMax, pandora::CartesianPointVector &positionVector) const
pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
LArDLHelper::TorchModel m_modelW
The model for the W view.
pandora::StatusCode MakeNetworkInputFromHits(const pandora::CaloHitList &caloHits, const pandora::HitType view, const float xMin, const float xMax, const float zMin, const float zMax, LArDLHelper::TorchInput &networkInput, PixelVector &pixelVector) const
std::string m_rootFileName
The ROOT file name.
pandora::StatusCode Infer()
int m_height
The height of the images.
pandora::StatusCode MakeCandidateVertexList(const pandora::CartesianPointVector &positions)
Create a vertex list from the candidate vertices.
boost::graph_traits< ModuleGraph >::vertex_descriptor Vertex
decltype(auto) constexpr begin(T &&obj)
ADL-aware version of std::begin.
int m_nClasses
The number of distance classes.
second_as<> second
Type of time stored in seconds, in double precision.
static void InitialiseInput(const at::IntArrayRef dimensions, TorchInput &tensor)
Create a torch input tensor.
std::list< Vertex > VertexList
std::string m_volumeType
The name of the fiducial volume type for the monitoring output.
LArDLHelper::TorchModel m_modelV
The model for the V view.
std::vector< torch::jit::IValue > TorchInputVector
bool m_trainingMode
Training mode.