src/modules/SimpleTransfer/SimpleTransferModule.cpp
Implementation of simple charge transfer module. More…
Detailed Description
Implementation of simple charge transfer module.
Copyright: Copyright (c) 2017-2024 CERN and the Allpix Squared authors. This software is distributed under the terms of the MIT License, copied verbatim in the file “LICENSE.md”. In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an Intergovernmental Organization or submit itself to any jurisdiction. SPDX-License-Identifier: MIT
Source code
#include "SimpleTransferModule.hpp"
#include <fstream>
#include <limits>
#include <memory>
#include <new>
#include <stdexcept>
#include <string>
#include <utility>
#include "core/config/exceptions.h"
#include "core/utils/log.h"
#include "core/utils/unit.h"
#include "tools/ROOT.h"
#include "objects/PixelCharge.hpp"
using namespace allpix;
SimpleTransferModule::SimpleTransferModule(Configuration& config, Messenger* messenger, std::shared_ptr<Detector> detector)
: Module(config, detector), messenger_(messenger), detector_(std::move(detector)) {
// Enable multithreading of this module if multithreading is enabled
allow_multithreading();
// Set default value for the maximum depth distance to transfer
config_.setDefault("max_depth_distance", Units::get(5.0, "um"));
// By default, collect from the full sensor surface, not the implant region
config_.setDefault("collect_from_implant", false);
// Plotting parameters
config_.setDefault<bool>("output_plots", false);
config_.setDefault<double>("output_plots_step", Units::get(0.1, "ns"));
config_.setDefault<double>("output_plots_range", Units::get(100, "ns"));
// Save detector model
model_ = detector_->getModel();
// Cache config parameters:
max_depth_distance_ = config_.get<double>("max_depth_distance");
collect_from_implant_ = config_.get<bool>("collect_from_implant");
// Cache flag for output plots:
output_plots_ = config_.get<bool>("output_plots");
// Require propagated deposits for single detector
messenger_->bindSingle<PropagatedChargeMessage>(this, MsgFlags::REQUIRED);
}
void SimpleTransferModule::initialize() {
auto model = detector_->getModel();
if(collect_from_implant_) {
if(model->getImplants().empty()) {
throw InvalidValueError(config_,
"collect_from_implant",
"Detector model does not have implants defined, but collection requested from implants");
}
if(detector_->getElectricFieldType() == FieldType::LINEAR) {
throw ModuleError("Charge collection from implant region should not be used with linear electric fields.");
}
LOG(INFO) << "Collecting charges from implants";
} else if(!model->getImplants().empty()) {
LOG(WARNING) << "Detector " << detector_->getName() << " of type " << model->getType()
<< " has implants defined but collecting charge carriers from full sensor surface";
}
if(output_plots_) {
auto time_bins =
static_cast<int>(config_.get<double>("output_plots_range") / config_.get<double>("output_plots_step"));
drift_time_histo = CreateHistogram<TH1D>("drift_time_histo",
"Charge carrier arrival time;t[ns];charge carriers",
time_bins,
0.,
config_.get<double>("output_plots_range"));
}
}
void SimpleTransferModule::run(Event* event) {
auto propagated_message = messenger_->fetchMessage<PropagatedChargeMessage>(this, event);
// Find corresponding pixels for all propagated charges
LOG(TRACE) << "Transferring charges to pixels";
unsigned int transferred_charges_count = 0;
std::map<Pixel::Index, std::vector<const PropagatedCharge*>> pixel_map;
for(const auto& propagated_charge : propagated_message->getData()) {
auto position = propagated_charge.getLocalPosition();
if(collect_from_implant_) {
// Ignore if outside the implant region:
auto implant = model_->isWithinImplant(position);
if(!implant.has_value()) {
LOG(TRACE) << "Skipping set of " << propagated_charge.getCharge() << " propagated charges at "
<< Units::display(propagated_charge.getLocalPosition(), {"mm", "um"})
<< " because their local position is outside the pixel implant";
continue;
}
if(implant->getType() != DetectorModel::Implant::Type::FRONTSIDE) {
LOG(TRACE) << "Skipping set of " << propagated_charge.getCharge() << " propagated charges at "
<< Units::display(propagated_charge.getLocalPosition(), {"mm", "um"})
<< " because the pixel implant is located at " << allpix::to_string(implant->getType());
continue;
}
} else if(std::fabs(position.z() - (model_->getSensorCenter().z() + model_->getSensorSize().z() / 2.0)) >
max_depth_distance_) {
// Ignore if not close to the sensor surface:
LOG(TRACE) << "Skipping set of " << propagated_charge.getCharge() << " propagated charges at "
<< Units::display(propagated_charge.getLocalPosition(), {"mm", "um"})
<< " because their local position is not near sensor surface";
continue;
}
// Find the nearest pixel
auto [xpixel, ypixel] = model_->getPixelIndex(position);
// Ignore if out of pixel grid
if(!model_->isWithinMatrix(xpixel, ypixel)) {
LOG(TRACE) << "Skipping set of " << propagated_charge.getCharge() << " propagated charges at "
<< Units::display(propagated_charge.getLocalPosition(), {"mm", "um"})
<< " because their nearest pixel (" << xpixel << "," << ypixel << ") is outside the grid";
continue;
}
Pixel::Index pixel_index(xpixel, ypixel);
// Update statistics
transferred_charges_count += propagated_charge.getCharge();
if(output_plots_) {
drift_time_histo->Fill(propagated_charge.getGlobalTime(), propagated_charge.getCharge());
}
LOG(TRACE) << "Set of " << propagated_charge.getCharge() << " propagated charges at "
<< Units::display(propagated_charge.getLocalPosition(), {"mm", "um"}) << " brought to pixel "
<< pixel_index;
// Add the pixel the list of hit pixels
pixel_map[pixel_index].emplace_back(&propagated_charge);
}
// Create pixel charges
LOG(TRACE) << "Combining charges at same pixel";
std::vector<PixelCharge> pixel_charges;
for(auto& pixel_index_charge : pixel_map) {
long charge = 0;
for(auto& propagated_charge : pixel_index_charge.second) {
charge += propagated_charge->getSign() * propagated_charge->getCharge();
}
// Get pixel object from detector
auto pixel = detector_->getPixel(pixel_index_charge.first.x(), pixel_index_charge.first.y());
pixel_charges.emplace_back(pixel, charge, pixel_index_charge.second);
LOG(DEBUG) << "Set of " << charge << " charges combined at " << pixel.getIndex();
}
// Writing summary and update statistics
LOG(INFO) << "Transferred " << transferred_charges_count << " charges to " << pixel_map.size() << " pixels";
total_transferred_charges_ += transferred_charges_count;
// Dispatch message of pixel charges
auto pixel_message = std::make_shared<PixelChargeMessage>(pixel_charges, detector_);
messenger_->dispatchMessage(this, pixel_message, event);
}
void SimpleTransferModule::finalize() {
// Print statistics
LOG(INFO) << "Transferred total of " << total_transferred_charges_ << " charges";
if(output_plots_) {
drift_time_histo->Write();
}
}
Updated on 2025-02-27 at 14:14:46 +0000