Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic silicon pulse generation #1757

Closed
wants to merge 14 commits into from
52 changes: 52 additions & 0 deletions src/algorithms/digi/PulseShapeFunctors.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright 2025, Simon Gardner
// Subject to the terms in the LICENSE file found in the top-level directory.
//
#pragma once

#include <TMath.h>

namespace eicrecon {

class SignalPulse {

public:
virtual double operator()(double time) const = 0;

void setHitCharge(double charge) {
m_charge = charge;
}

void setHitTime(double hit_time) {
m_hit_time = hit_time;
}

virtual float getMaximumTime() const = 0;

protected:
double m_charge;
double m_hit_time;

};

// ----------------------------------------------------------------------------
// Landau Pulse Shape Functor
// ----------------------------------------------------------------------------
class LandauPulse: public SignalPulse {
public:

LandauPulse(double gain, double sigma_analog) : m_gain(gain), m_sigma_analog(sigma_analog) {};

double operator()(double time) const {
return m_charge * m_gain * TMath::Landau(time, m_hit_time+3.5*m_sigma_analog, m_sigma_analog, kTRUE);
}

float getMaximumTime() const {
return m_hit_time+3.5*m_sigma_analog;
}

private:
const double m_gain;
const double m_sigma_analog;
};

} // eicrecon
59 changes: 59 additions & 0 deletions src/algorithms/digi/SiliconPulseGeneration.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright (C) 2025 Simon Gardner
//
// Convert energy deposition into ADC pulses
// ADC pulses are assumed to follow the shape of landau function

#include <Evaluator/DD4hepUnits.h>

#include "SiliconPulseGeneration.h"

namespace eicrecon {

void SiliconPulseGeneration::init() {
m_pulse = m_cfg.pulse_shape_function;
}

void SiliconPulseGeneration::process(const SiliconPulseGeneration::Input& input,
const SiliconPulseGeneration::Output& output) const {
const auto [simhits] = input;
auto [rawPulses] = output;

for (const auto& hit : *simhits) {

auto cellID = hit.getCellID();
double time = hit.getTime() * dd4hep::ns;
double charge = hit.getEDep();

// Calculate nearest timestep to the hit time rounded down (assume clocks aligned with time 0)
double signal_time = m_cfg.timestep*static_cast<int>(time / m_cfg.timestep);

auto time_series = rawPulses->create();
time_series.setCellID(cellID);
time_series.setInterval(m_cfg.timestep);

m_pulse->setHitCharge(charge);
m_pulse->setHitTime(time);

float maxSignalTime = m_pulse->getMaximumTime();

for(int i = 0; i < m_max_time_bins; i ++) {
double t = signal_time + i*m_cfg.timestep;
auto signal = (*m_pulse)(t);
if (signal < m_cfg.ignore_thres) {
if (t > maxSignalTime) {
break;
} else {
signal_time = t;
continue;
}
}
time_series.addToAmplitude(signal);
}

time_series.setTime(signal_time);

}

} // SiliconPulseGeneration:process
} // namespace eicrecon
40 changes: 40 additions & 0 deletions src/algorithms/digi/SiliconPulseGeneration.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright (C) 2025 Simon Gardner
//
// Convert energy deposition into analog pulses

#pragma once

#include <algorithms/algorithm.h>
#include <edm4hep/TimeSeriesCollection.h>
#include <edm4hep/SimTrackerHitCollection.h>
#include <memory>
#include <string>
#include <string_view>

#include "algorithms/digi/SiliconPulseGenerationConfig.h"
#include "algorithms/interfaces/WithPodConfig.h"

namespace eicrecon {

using SiliconPulseGenerationAlgorithm =
algorithms::Algorithm<algorithms::Input<edm4hep::SimTrackerHitCollection>,
algorithms::Output<edm4hep::TimeSeriesCollection>>;

class SiliconPulseGeneration : public SiliconPulseGenerationAlgorithm,
public WithPodConfig<SiliconPulseGenerationConfig> {

public:
SiliconPulseGeneration(std::string_view name)
: SiliconPulseGenerationAlgorithm{name, {"RawHits"}, {"OutputPulses"}, {}} {}
virtual void init() final;
void process(const Input&, const Output&) const final;

private:

std::shared_ptr<SignalPulse> m_pulse;
int m_max_time_bins = 10000;

};

} // namespace eicrecon
19 changes: 19 additions & 0 deletions src/algorithms/digi/SiliconPulseGenerationConfig.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright (C) 2025 Simon Gardner

#pragma once

#include <DD4hep/DD4hepUnits.h>
#include "PulseShapeFunctors.h"

namespace eicrecon {

struct SiliconPulseGenerationConfig {
// Parameters of Silicon signal generation
std::shared_ptr<SignalPulse> pulse_shape_function;
double ignore_thres = 10; // When EDep drops below this value pulse stops
double timestep = 0.2 * dd4hep::ns; // Minimum digitization time step

};

} // namespace eicrecon
14 changes: 13 additions & 1 deletion src/detectors/LOWQ2/LOWQ2.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@

#include "algorithms/interfaces/WithPodConfig.h"
#include "algorithms/meta/SubDivideFunctors.h"
#include "algorithms/digi/PulseShapeFunctors.h"
#include "extensions/jana/JOmniFactoryGeneratorT.h"
#include "factories/digi/SiliconTrackerDigi_factory.h"
#include "factories/digi/SiliconPulseGeneration_factory.h"
#include "factories/fardetectors/FarDetectorLinearProjection_factory.h"
#include "factories/fardetectors/FarDetectorLinearTracking_factory.h"
#if EDM4EIC_VERSION_MAJOR >= 8
Expand All @@ -40,7 +42,17 @@ extern "C" {

using namespace eicrecon;

std::string tracker_readout = "TaggerTrackerHits";
app->Add(new JOmniFactoryGeneratorT<SiliconPulseGeneration_factory>(
"TaggerTrackerPulseGeneration",
{"TaggerTrackerHits"},
{"TaggerTrackerHitPulses"},
{
.pulse_shape_function = std::make_shared<LandauPulse>(1, 2 * dd4hep::ns),
.ignore_thres = 150.0,
.timestep = 0.2 * dd4hep::ns,
},
app
));

// Digitization of silicon hits
app->Add(new JOmniFactoryGeneratorT<SiliconTrackerDigi_factory>(
Expand Down
46 changes: 46 additions & 0 deletions src/factories/digi/SiliconPulseGeneration_factory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright (C) 2025 Simon Gardner
//

#pragma once

#include "extensions/jana/JOmniFactory.h"

#include "algorithms/digi/SiliconPulseGeneration.h"
#include <iostream>

namespace eicrecon {

class SiliconPulseGeneration_factory
: public JOmniFactory<SiliconPulseGeneration_factory, SiliconPulseGenerationConfig> {
public:
using AlgoT = eicrecon::SiliconPulseGeneration;

private:
std::unique_ptr<AlgoT> m_algo;

PodioInput<edm4hep::SimTrackerHit> m_in_sim_hits{this};

PodioOutput<edm4hep::TimeSeries> m_out_pulses{this};

ParameterRef<double> m_timestep{this, "timestep", config().timestep};
ParameterRef<double> m_ignore_thres{this, "ignoreThreshold", config().ignore_thres};

Service<AlgorithmsInit_service> m_algorithmsInit{this};

public:
void Configure() {
m_algo = std::make_unique<eicrecon::SiliconPulseGeneration>(GetPrefix());
m_algo->level(static_cast<algorithms::LogLevel>(logger()->level()));
m_algo->applyConfig(config());
m_algo->init();
}

void ChangeRun(int64_t run_number) {}

void Process(int64_t run_number, uint64_t event_number) {
m_algo->process({m_in_sim_hits()}, {m_out_pulses().get()});
}
};

} // namespace eicrecon
2 changes: 2 additions & 0 deletions src/services/io/podio/JEventProcessorPODIO.cc
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ JEventProcessorPODIO::JEventProcessorPODIO() {
"ForwardMPGDEndcapRawHitAssociations",

// LOWQ2 hits
"TaggerTrackerHits",
"TaggerTrackerHitPulses",
"TaggerTrackerRawHits",
"TaggerTrackerRawHitAssociations",
"TaggerTrackerM1L0ClusterPositions",
Expand Down
Loading