12 #include <torch/script.h> 13 #include <torch/torch.h> 33 DlSecondaryVertexingAlgorithm::DlSecondaryVertexingAlgorithm() :
37 m_rng(static_cast<std::mt19937::result_type>(std::chrono::high_resolution_clock::now().time_since_epoch().count()))
49 catch (StatusCodeException
e)
51 std::cout <<
"VertexAssessmentAlgorithm: Unable to write to ROOT tree" << std::endl;
67 return STATUS_CODE_SUCCESS;
72 const CaloHitList *pCaloHitList2D{
nullptr};
73 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetList(*
this,
"CaloHitList2D", pCaloHitList2D));
77 CartesianPointVector vertices;
81 bool hasFiducialVertex{
false};
82 for (
const CartesianVector &
vertex : vertices)
86 hasFiducialVertex =
true;
91 if (!hasFiducialVertex)
92 return STATUS_CODE_SUCCESS;
94 const MCParticleList *pMCParticleList(
nullptr);
95 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetCurrentList(*
this, pMCParticleList));
97 LArMCParticleHelper::GetMCToHitsMap(pCaloHitList2D, pMCParticleList, mcToHitsMap);
98 MCParticleList hierarchy;
99 LArMCParticleHelper::CompleteMCHierarchy(mcToHitsMap, hierarchy);
102 std::map<HitType, float> wireMin, wireMax;
103 float driftMin{std::numeric_limits<float>::max()}, driftMax{-std::numeric_limits<float>::max()};
106 const CaloHitList *pCaloHitList{
nullptr};
107 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetList(*
this, listname, pCaloHitList));
108 if (pCaloHitList->empty())
111 HitType view{pCaloHitList->front()->GetHitType()};
112 float viewDriftMin{driftMin}, viewDriftMax{driftMax};
113 this->
GetHitRegion(*pCaloHitList, viewDriftMin, viewDriftMax, wireMin[view], wireMax[view]);
114 driftMin = std::min(viewDriftMin, driftMin);
115 driftMax = std::max(viewDriftMax, driftMax);
117 for (
const std::string &listname : m_caloHitListNames)
119 const CaloHitList *pCaloHitList(
nullptr);
120 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetList(*
this, listname, pCaloHitList));
121 if (pCaloHitList->empty())
124 HitType view{pCaloHitList->front()->GetHitType()};
127 const unsigned long nVertices{vertices.size()};
128 unsigned long nHits{0};
131 featureVector.emplace_back(static_cast<double>(
m_event));
132 featureVector.emplace_back(static_cast<double>(nVertices));
134 const LArTransformationPlugin *transform{this->GetPandora().GetPlugins()->GetLArTransformationPlugin()};
135 for (
const CartesianVector &
vertex : vertices)
140 featureVector.emplace_back(
vertex.GetX());
141 featureVector.emplace_back(transform->YZtoU(
vertex.GetY(),
vertex.GetZ()));
144 featureVector.emplace_back(
vertex.GetX());
145 featureVector.emplace_back(transform->YZtoV(
vertex.GetY(),
vertex.GetZ()));
148 featureVector.emplace_back(
vertex.GetX());
149 featureVector.emplace_back(transform->YZtoW(
vertex.GetY(),
vertex.GetZ()));
155 featureVector.emplace_back(driftMin);
156 featureVector.emplace_back(driftMax);
157 featureVector.emplace_back(wireMin[view]);
158 featureVector.emplace_back(wireMax[view]);
160 for (
const CaloHit *pCaloHit : *pCaloHitList)
162 featureVector.emplace_back(static_cast<double>(pCaloHit->GetPositionVector().GetX()));
163 featureVector.emplace_back(static_cast<double>(pCaloHit->GetPositionVector().GetZ()));
164 featureVector.emplace_back(static_cast<double>(pCaloHit->GetMipEquivalentEnergy()));
167 featureVector.insert(featureVector.begin() + 2 + 2 * nVertices + 4,
static_cast<double>(nHits));
170 LArMvaHelper::ProduceTrainingExample(trainingFilename,
true, featureVector);
173 return STATUS_CODE_SUCCESS;
181 std::map<HitType, float> wireMin, wireMax;
182 float driftMin{std::numeric_limits<float>::max()}, driftMax{-std::numeric_limits<float>::max()};
185 const CaloHitList *pCaloHitList{
nullptr};
186 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetList(*
this, listname, pCaloHitList));
187 if (pCaloHitList->empty())
190 HitType view{pCaloHitList->front()->GetHitType()};
191 float viewDriftMin{driftMin}, viewDriftMax{driftMax};
192 this->
GetHitRegion(*pCaloHitList, viewDriftMin, viewDriftMax, wireMin[view], wireMax[view]);
193 driftMin = std::min(viewDriftMin, driftMin);
194 driftMax = std::max(viewDriftMax, driftMax);
198 canvases[TPC_VIEW_U] =
nullptr;
199 canvases[TPC_VIEW_V] =
nullptr;
200 canvases[TPC_VIEW_W] =
nullptr;
201 CartesianPointVector vertexCandidatesU, vertexCandidatesV, vertexCandidatesW;
202 for (
const std::string &listname : m_caloHitListNames)
204 const CaloHitList *pCaloHitList{
nullptr};
205 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetList(*
this, listname, pCaloHitList));
206 if (pCaloHitList->empty())
209 HitType view{pCaloHitList->front()->GetHitType()};
212 this->
MakeNetworkInputFromHits(*pCaloHitList, view, driftMin, driftMax, wireMin[view], wireMax[view], input, pixelVector);
216 inputs.push_back(input);
231 int colOffset{0}, rowOffset{0}, canvasWidth{
m_width}, canvasHeight{
m_height};
232 this->
GetCanvasParameters(output, pixelVector, colOffset, rowOffset, canvasWidth, canvasHeight);
233 canvases[view] =
new Canvas(view, canvasWidth, canvasHeight, colOffset, rowOffset, driftMin, driftMax, wireMin[view], wireMax[view]);
235 auto classes{torch::argmax(output, 1)};
237 auto classesAccessor{classes.accessor<long, 3>()};
239 std::map<int, bool> haveSeenMap;
240 for (
const auto &[row,
col] : pixelVector)
242 const auto cls{classesAccessor[0][row][
col]};
245 const int inner{
static_cast<int>(std::round(std::ceil(scaleFactor *
m_thresholds[cls - 1])))};
246 const int outer{
static_cast<int>(std::round(std::ceil(scaleFactor *
m_thresholds[cls])))};
248 canvases[view]->m_canvas, row + rowOffset,
col + colOffset, inner, outer, 1.
f / (outer * outer - inner * inner));
253 CartesianPointVector vertexVector;
256 if (!vertexVector.empty())
260 for (
const HitType view : {TPC_VIEW_U, TPC_VIEW_V, TPC_VIEW_W})
263 delete canvases[view];
270 for (
const HitType view : {TPC_VIEW_U, TPC_VIEW_V, TPC_VIEW_W})
273 delete canvases[view];
276 return STATUS_CODE_SUCCESS;
286 const LArTPC *
const pTPC(this->GetPandora().GetGeometry()->GetLArTPCMap().
begin()->
second);
287 const float pitch(view == TPC_VIEW_U ? pTPC->GetWirePitchU() : view == TPC_VIEW_V ? pTPC->GetWirePitchV() : pTPC->GetWirePitchW());
288 const float driftStep{0.5f};
291 std::vector<double> xBinEdges(
m_width + 1);
292 std::vector<double> zBinEdges(
m_height + 1);
293 xBinEdges[0] = xMin - 0.5f * driftStep;
294 const double dx = ((xMax + 0.5f * driftStep) - xBinEdges[0]) /
m_width;
295 for (
int i = 1; i <
m_width + 1; ++i)
296 xBinEdges[i] = xBinEdges[i - 1] + dx;
297 zBinEdges[0] = zMin - 0.5f * pitch;
298 const double dz = ((zMax + 0.5f * pitch) - zBinEdges[0]) /
m_height;
299 for (
int i = 1; i <
m_height + 1; ++i)
300 zBinEdges[i] = zBinEdges[i - 1] + dz;
303 auto accessor = networkInput.accessor<float, 4>();
305 for (
const CaloHit *pCaloHit : caloHits)
307 const float x{pCaloHit->GetPositionVector().GetX()};
308 const float z{pCaloHit->GetPositionVector().GetZ()};
311 if (x < xMin || x > xMax || z < zMin || z > zMax)
314 const float adc{pCaloHit->GetMipEquivalentEnergy()};
315 const int pixelX{
static_cast<int>(std::floor((
x - xBinEdges[0]) / dx))};
316 const int pixelZ{
static_cast<int>(std::floor((
z - zBinEdges[0]) / dz))};
317 accessor[0][0][pixelZ][pixelX] += adc;
319 for (
int row = 0; row <
m_height; ++row)
323 const float value{accessor[0][0][row][
col]};
325 pixelVector.emplace_back(std::make_pair(row,
col));
329 return STATUS_CODE_SUCCESS;
336 CartesianPointVector verticesU, verticesV, verticesW;
341 std::vector<VertexTuple> vertexTuples;
342 int nEmptyLists{(verticesU.empty() ? 1 : 0) + (verticesV.empty() ? 1 : 0) + (verticesW.empty() ? 1 : 0)};
344 if (nEmptyLists == 0)
346 for (
const CartesianVector &vertexU : verticesU)
348 for (
const CartesianVector &vertexV : verticesV)
350 for (
const CartesianVector &vertexW : verticesW)
352 const float xMin{std::min({vertexU.GetX(), vertexV.GetX(), vertexW.GetX()})};
353 const float xMax{std::max({vertexU.GetX(), vertexV.GetX(), vertexW.GetX()})};
354 if ((xMax - xMin) < 10.f)
357 if (tuple.GetChi2() < 1)
358 vertexTuples.emplace_back(tuple);
364 else if (nEmptyLists == 1)
366 if (verticesU.empty())
368 for (
const CartesianVector &vertexV : verticesV)
370 for (
const CartesianVector &vertexW : verticesW)
372 const float xMin{std::min({vertexV.GetX(), vertexW.GetX()})};
373 const float xMax{std::max({vertexV.GetX(), vertexW.GetX()})};
374 if ((xMax - xMin) < 10.f)
377 if (tuple.GetChi2() < 1)
378 vertexTuples.emplace_back(tuple);
383 else if (verticesV.empty())
385 for (
const CartesianVector &vertexU : verticesU)
387 for (
const CartesianVector &vertexW : verticesW)
389 const float xMin{std::min({vertexU.GetX(), vertexW.GetX()})};
390 const float xMax{std::max({vertexU.GetX(), vertexW.GetX()})};
391 if ((xMax - xMin) < 10.f)
394 if (tuple.GetChi2() < 1)
395 vertexTuples.emplace_back(tuple);
402 for (
const CartesianVector &vertexU : verticesU)
404 for (
const CartesianVector &vertexV : verticesV)
406 const float xMin{std::min({vertexU.GetX(), vertexV.GetX()})};
407 const float xMax{std::max({vertexU.GetX(), vertexV.GetX()})};
408 if ((xMax - xMin) < 10.f)
411 if (tuple.GetChi2() < 1)
412 vertexTuples.emplace_back(tuple);
420 std::cout <<
"Insufficient 2D vertices to reconstruct a 3D vertex" << std::endl;
421 return STATUS_CODE_NOT_FOUND;
425 std::sort(vertexTuples.begin(), vertexTuples.end(),
428 const CartesianPointVector &components1{tuple1.
GetComponents()};
429 const CartesianPointVector &components2{tuple2.GetComponents()};
430 if (components1.size() == components2.size())
431 return tuple1.
GetChi2() < tuple2.GetChi2();
433 return components1.size() > components2.size();
436 CartesianPointVector used;
439 const CartesianPointVector &components{tuple.
GetComponents()};
440 bool isAvailable{
true};
441 for (
const CartesianVector &component : components)
443 if (std::find(used.begin(), used.end(), component) != used.end())
449 if (!isAvailable || (components.size() < 3 && tuple.GetChi2() > 0.1))
452 positionVector.emplace_back(tuple.GetPosition());
453 for (
const CartesianVector &component : components)
454 used.emplace_back(component);
457 return STATUS_CODE_SUCCESS;
465 const LArTPC *
const pTPC(this->GetPandora().GetGeometry()->GetLArTPCMap().
begin()->
second);
468 const float pitch(view == TPC_VIEW_U ? pTPC->GetWirePitchU() : view == TPC_VIEW_V ? pTPC->GetWirePitchV() : pTPC->GetWirePitchW());
469 const float driftStep{0.5f};
471 const double dx{((canvas.
m_xMax + 0.5f * driftStep) - (canvas.
m_xMin - 0.5f * driftStep)) /
m_width};
474 float maxIntensity{0.f};
475 for (
int xp = 0; xp <
m_width; ++xp)
481 for (
int zp = 0; zp <
m_height; ++zp)
487 const float localIntensity{canvas.
m_canvas[zpp][xpp]};
488 if (localIntensity > maxIntensity)
489 maxIntensity = localIntensity;
492 const float threshold{maxIntensity * 0.3f};
494 std::vector<std::vector<std::pair<int, int>>> peaks;
495 for (
int xp = 0; xp <
m_width; ++xp)
501 for (
int zp = 0; zp <
m_height; ++zp)
506 const float localIntensity{canvas.
m_canvas[zpp][xpp]};
508 std::vector<std::pair<int, int>> peak;
509 bool hasLowNeighbour{
false};
510 for (
int dr = -1; dr <= 1; ++dr)
512 for (
int dc = -1; dc <= 1; ++dc)
514 if (dr == 0 && dc == 0)
516 const int r{zpp + dr}, c{xpp + dc};
520 const float neighborIntensity{canvas.
m_canvas[
r][c]};
521 if (localIntensity > neighborIntensity)
523 hasLowNeighbour =
true;
525 else if (localIntensity < neighborIntensity)
527 hasLowNeighbour =
false;
532 if (hasLowNeighbour && localIntensity > threshold)
533 this->
GrowPeak(canvas, xpp, zpp, localIntensity, peak);
535 peaks.emplace_back(peak);
539 for (
const auto &peak : peaks)
541 float row{0},
col{0};
542 for (
const auto &pixel : peak)
550 const float x{
static_cast<float>(
col * dx + canvas.
m_xMin)};
551 const float z{
static_cast<float>(row * dz + canvas.
m_zMin)};
552 CartesianVector
pt(
x, 0,
z);
553 vertices.emplace_back(pt);
556 return STATUS_CODE_SUCCESS;
571 for (
int dc = -1; dc <= 1; ++dc)
573 const int c{col + dc};
574 if (c < 0 || c >= canvas.
m_width)
577 for (
int dr = -1; dr <= 1; ++dr)
579 const int r{row + dr};
583 if (dr == 0 && dc == 0)
586 const float neighborIntensity{canvas.
m_canvas[
r][c]};
587 if (neighborIntensity > intensity)
594 if (localIntensity > intensity)
596 intensity = localIntensity;
597 for (
const auto &pixel : peak)
598 canvas.
m_visited[pixel.second][pixel.first] =
false;
601 this->
GrowPeak(canvas, col, row, intensity, peak);
607 peak.emplace_back(std::make_pair(col, row));
610 for (
int dc = -1; dc <= 1; ++dc)
612 for (
int dr = -1; dr <= 1; ++dr)
614 if (dr == 0 && dc == 0)
617 bool reset{this->
GrowPeak(canvas, col + dc, row + dr, intensity, peak)};
632 std::string temporaryListName;
633 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::CreateTemporaryListAndSetCurrent(*
this, pVertexList, temporaryListName));
635 for (
const CartesianVector &position : positions)
637 PandoraContentApi::Vertex::Parameters parameters;
638 parameters.m_position = position;
639 parameters.m_vertexLabel = VERTEX_INTERACTION;
640 parameters.m_vertexType = VERTEX_3D;
642 const Vertex *pVertex(
nullptr);
643 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::Vertex::Create(*
this, parameters, pVertex));
645 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::SaveList<Vertex>(*
this,
m_outputVertexListName));
647 return STATUS_CODE_SUCCESS;
656 PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
"Visualise",
m_visualise));
660 PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
"WriteTree",
m_writeTree));
663 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ReadValue(xmlHandle,
"RootTreeName",
m_rootTreeName));
664 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ReadValue(xmlHandle,
"RootFileName",
m_rootFileName));
668 return STATUS_CODE_SUCCESS;
675 const int rowOffset,
const float xMin,
const float xMax,
const float zMin,
const float zMax) :
pandora::StatusCode PrepareTrainingSample()
~DlSecondaryVertexingAlgorithm()
int m_event
The current event number.
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
Header file for the vertex tuple object.
pandora::StatusCode GetNetworkVertices(const CanvasViewMap &canvases, pandora::CartesianPointVector &positionVector) const
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
LArDLHelper::TorchModel m_modelU
The model for the U view.
const pandora::CartesianPointVector & GetComponents() const
pandora::StatusCode Infer()
void GetVertices(pandora::CartesianPointVector &vertices) const
Extract the clear topological vertices from the event.
pandora::StatusCode GetVerticesFromCanvas(const Canvas &canvas, pandora::CartesianPointVector &vertices) const
std::vector< double > m_thresholds
Distance class thresholds.
int m_width
The width of the images.
void ConstructVisibleHierarchy()
Construct a particle hierarchy based on the key topological features in an event. ...
Canvas(const pandora::HitType view, const int width, const int height, const int colOffset, const int rowOffset, const float xMin, const float xMax, const float zMin, const float zMax)
Default constructor.
Header file for lar event topology.
pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
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.
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
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::mt19937 m_rng
The random number generator.
std::vector< Pixel > PixelVector
pandora::StringVector m_caloHitListNames
Names of input calo hit lists.
bool m_visualise
Whether or not to visualise the candidate vertices.
Header file for the file helper class.
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 MakeCandidateVertexList(const pandora::CartesianPointVector &positions)
Create a vertex list from the candidate vertices.
LArDLHelper::TorchModel m_modelW
The model for the W view.
bool m_writeTree
Whether or not to write validation details to a ROOT tree.
int m_height
The height of the images.
std::map< pandora::HitType, Canvas * > CanvasViewMap
pandora::StatusCode Run()
void PruneHierarchy()
Fold or remove particles that aren't substantive parts of the hierarchy.
boost::graph_traits< ModuleGraph >::vertex_descriptor Vertex
decltype(auto) constexpr begin(T &&obj)
ADL-aware version of std::begin.
std::string m_rootTreeName
The ROOT tree name.
int m_nClasses
The number of distance classes.
second_as<> second
Type of time stored in seconds, in double precision.
bool GrowPeak(const Canvas &canvas, int col, int row, float intensity, std::vector< std::pair< int, int >> &peak) const
Determine if the pixel under consideration is part of a peak and grow that peak to include all connec...
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.
std::string m_rootFileName
The ROOT file name.