Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • EIC/detectors/athena
  • zwzhao/athena
  • FernandoTA/athena
  • palspeic/athena
4 results
Show changes
#include <DD4hep/DetFactoryHelper.h>
#include <DD4hep/FieldTypes.h>
#include <DD4hep/Printout.h>
#include <XML/Utilities.h>
#include <cstdlib>
......@@ -12,6 +13,8 @@
#include <tuple>
namespace fs = std::filesystem;
#include "FileLoaderHelper.h"
using namespace dd4hep;
......@@ -19,7 +22,7 @@ using namespace dd4hep;
class FieldMapBrBz : public dd4hep::CartesianField::Object
{
public:
FieldMapBrBz(const std::string &field_type);
FieldMapBrBz(const std::string &field_type = "magnetic");
void Configure(double rmin, double rmax, double rstep, double zmin, double zmax, double zstep);
void LoadMap(const std::string &map_file, double scale);
void GetIndices(double r, double z, int &ir, int &iz, double &dr, double &dz);
......@@ -35,7 +38,7 @@ private:
};
// constructor
FieldMapBrBz::FieldMapBrBz(const std::string &field_type = "magnetic")
FieldMapBrBz::FieldMapBrBz(const std::string &field_type)
{
std::string ftype = field_type;
for (auto &c : ftype) { c = tolower(c); }
......@@ -185,20 +188,16 @@ static Ref_t create_field_map_brbz(Detector & /*lcdd*/, xml::Handle_t handle)
std::string field_map_file = x_par.attr<std::string>(_Unicode(field_map));
std::string field_map_url = x_par.attr<std::string>(_Unicode(url));
EnsureFileFromURLExists(field_map_url,field_map_file);
double field_map_scale = x_par.attr<double>(_Unicode(scale));
if( !fs::exists(fs::path(field_map_file)) ) {
auto ret = std::system(("mkdir -p fieldmaps && "
"wget " +
field_map_url + " -O " + field_map_file).c_str());
if (!fs::exists(fs::path(field_map_file))) {
std::cerr << "ERROR: file, " << field_map_file << ", does not exist\n";
std::quick_exit(1);
}
printout(ERROR, "FieldMapBrBz", "file " + field_map_file + " does not exist");
printout(ERROR, "FieldMapBrBz", "use a FileLoader plugin before the field element");
std::_Exit(EXIT_FAILURE);
}
auto map = new FieldMapBrBz(field_type);
map->Configure(r_dim.rmin(), r_dim.rmax(), r_dim.step(), z_dim.zmin(), z_dim.zmax(), z_dim.step());
......
#include <DD4hep/DetFactoryHelper.h>
#include <DD4hep/Primitives.h>
#include <DD4hep/Factories.h>
#include <DD4hep/Printout.h>
#include <XML/Utilities.h>
#include <fmt/core.h>
#include <filesystem>
#include <iostream>
#include <cstdlib>
#include <string>
#include "FileLoaderHelper.h"
using namespace dd4hep;
void usage(int argc, char** argv) {
std::cout <<
"Usage: -plugin <name> -arg [-arg] \n"
" name: factory name FileLoader \n"
" cache:<string> cache location (may be read-only) \n"
" file:<string> file location \n"
" url:<string> url location \n"
" cmd:<string> download command with {0} for url, {1} for output \n"
"\tArguments given: " << arguments(argc,argv) << std::endl;
std::exit(EINVAL);
}
// Plugin to download files
long load_file(
Detector& /* desc */,
int argc,
char** argv
) {
// argument parsing
std::string cache, file, url;
std::string cmd("curl --retry 5 -f {0} -o {1}");
for (int i = 0; i < argc && argv[i]; ++i) {
if (0 == std::strncmp("cache:", argv[i], 6)) cache = (argv[i] + 6);
else if (0 == std::strncmp("file:", argv[i], 5)) file = (argv[i] + 5);
else if (0 == std::strncmp("url:", argv[i], 4)) url = (argv[i] + 4);
else if (0 == std::strncmp("cmd:", argv[i], 4)) cmd = (argv[i] + 4);
else usage(argc, argv);
}
printout(DEBUG, "FileLoader", "arg cache: " + cache);
printout(DEBUG, "FileLoader", "arg file: " + file);
printout(DEBUG, "FileLoader", "arg url: " + url);
printout(DEBUG, "FileLoader", "arg cmd: " + cmd);
// if file or url is empty, do nothing
if (file.empty()) {
printout(WARNING, "FileLoader", "no file specified");
return 0;
}
if (url.empty()) {
printout(WARNING, "FileLoader", "no url specified");
return 0;
}
EnsureFileFromURLExists(url, file, cache, cmd);
return 0;
}
DECLARE_APPLY(FileLoader, load_file)
#pragma once
#include <DD4hep/DetFactoryHelper.h>
#include <DD4hep/Primitives.h>
#include <DD4hep/Factories.h>
#include <DD4hep/Printout.h>
#include <fmt/core.h>
#include <filesystem>
#include <iostream>
#include <cstdlib>
#include <string>
namespace fs = std::filesystem;
using namespace dd4hep;
// Function to download files
inline void
EnsureFileFromURLExists(
std::string url,
std::string file,
std::string cache = "",
std::string cmd = "curl --retry 5 -f {0} -o {1}"
) {
// parse cache for environment variables
auto pos = std::string::npos;
while ((pos = cache.find('$')) != std::string::npos) {
auto after = cache.find_first_not_of(
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789"
"_",
pos + 1);
if (after == std::string::npos) after = cache.size(); // cache ends on env var
const std::string env_name(cache.substr(pos + 1, after - pos - 1));
auto env_ptr = std::getenv(env_name.c_str());
const std::string env_value(env_ptr != nullptr ? env_ptr : "");
cache.erase(pos, after - pos);
cache.insert(pos, env_value);
printout(INFO, "FileLoader", "$" + env_name + " -> " + env_value);
}
// create file path
fs::path file_path(file);
// create hash from url, hex of unsigned long long
std::string hash = fmt::format("{:016x}", dd4hep::detail::hash64(url)); // TODO: Use c++20 std::fmt
// create file parent path, if not exists
fs::path parent_path = file_path.parent_path();
if (!fs::exists(parent_path)) {
if (fs::create_directories(parent_path) == false) {
printout(ERROR, "FileLoader", "parent path " + parent_path.string() + " cannot be created");
printout(ERROR, "FileLoader", "check permissions and retry");
std::_Exit(EXIT_FAILURE);
}
}
// if file exists and is symlink to correct hash
fs::path hash_path(parent_path / hash);
if (fs::exists(file_path)
&& fs::equivalent(file_path, hash_path)) {
printout(INFO, "FileLoader", "Link " + file + " -> hash " + hash + " already exists");
return;
}
// if hash does not exist, we try to retrieve file from cache
if (!fs::exists(hash_path)) {
// recursive loop into cache directory
fs::path cache_path(cache);
printout(INFO, "FileLoader", "Cache " + cache_path.string());
if (fs::exists(cache_path)) {
for (auto const& dir_entry: fs::recursive_directory_iterator(cache_path)) {
if (!dir_entry.is_directory()) continue;
fs::path cache_dir_path = cache_path / dir_entry;
printout(INFO, "FileLoader", "Checking " + cache_dir_path.string());
fs::path cache_hash_path = cache_dir_path / hash;
if (fs::exists(cache_hash_path)) {
// symlink hash to cache/.../hash
printout(INFO, "FileLoader", "File " + file + " with hash " + hash + " found in " + cache_hash_path.string());
try {
fs::create_symlink(cache_hash_path, hash_path);
} catch (const fs::filesystem_error&) {
printout(ERROR, "FileLoader", "unable to link from " + hash_path.string() + " to " + cache_hash_path.string());
printout(ERROR, "FileLoader", "check permissions and retry");
std::_Exit(EXIT_FAILURE);
}
break;
}
}
}
}
// if hash does not exist, we try to retrieve file from url
if (!fs::exists(hash_path)) {
cmd = fmt::format(cmd, url, hash_path.c_str()); // TODO: Use c++20 std::fmt
printout(INFO, "FileLoader", "Downloading " + file + " as hash " + hash + " with " + cmd);
// run cmd
auto ret = std::system(cmd.c_str());
if (!fs::exists(hash_path)) {
printout(ERROR, "FileLoader", "unable to run cmd " + cmd);
printout(ERROR, "FileLoader", "check command and retry");
printout(ERROR, "FileLoader", "hint: allow insecure connections with -k");
std::_Exit(EXIT_FAILURE);
}
}
// check if file already exists
if (fs::exists(file_path)) {
// file already exists
if (fs::is_symlink(file_path)) {
// file is symlink
if (fs::equivalent(hash_path, fs::read_symlink(file_path))) {
// link points to correct path
return;
} else {
// link points to incorrect path
if (fs::remove(file_path) == false) {
printout(ERROR, "FileLoader", "unable to remove symlink " + file_path.string());
printout(ERROR, "FileLoader", "check permissions or remove manually");
std::_Exit(EXIT_FAILURE);
}
}
} else {
// file exists but not symlink
printout(ERROR, "FileLoader", "will not remove actual file " + file_path.string());
printout(ERROR, "FileLoader", "check content, remove manually, and retry");
std::_Exit(EXIT_FAILURE);
}
}
// file_path now does not exist
// symlink file_path to hash_path
try {
// use new path from hash so file link is local
fs::create_symlink(fs::path(hash), file_path);
} catch (const fs::filesystem_error&) {
printout(ERROR, "FileLoader", "unable to link from " + file_path.string() + " to " + hash_path.string());
printout(ERROR, "FileLoader", "check permissions and retry");
std::_Exit(EXIT_FAILURE);
}
}
......@@ -75,7 +75,7 @@ static Ref_t createDetector(Detector& desc, xml::Handle_t handle, SensitiveDetec
envVol.setVisAttributes(desc.visAttributes(detElem.visStr()));
// sensitive detector type
sens.setType("photoncounter");
sens.setType("tracker");
// @TODO: place a radiator
// build_radiator(desc, envVol, detElement.child(_Unicode(radiator)), snout_front);
......
......@@ -35,54 +35,69 @@ static Ref_t create_detector(Detector& desc, xml::Handle_t handle, SensitiveDete
DetElement det(detName, detID);
sens.setType("calorimeter");
auto glass_material = desc.material("PbGlass");
auto glass_material = desc.material("SciGlass");
auto crystal_material = desc.material("PbWO4");
auto air_material = desc.material("Air");
double ROut = desc.constantAsDouble("EcalEndcapN_rmax");
double RIn = desc.constantAsDouble("EcalEndcapN_rmin");
double RIn_el = desc.constantAsDouble("EcalEndcapN_rmin1");
double ionCutout_dphi = desc.constantAsDouble("EcalEndcapNIonCutout_dphi");
double RIn = desc.constantAsDouble("EcalEndcapN_rmin2");
double SizeZ = desc.constantAsDouble("EcalEndcapN_thickness");
double glass_shift_z = desc.constantAsDouble("GlassModule_z0");
double Thickness = desc.constantAsDouble("EcalEndcapN_thickness");
double thickness = desc.constantAsDouble("EcalEndcapN_thickness");
double trans_radius = desc.constantAsDouble("EcalEndcapNCrystal_rmax");
double Glass_ShiftZ = desc.constantAsDouble("GlassModule_z0");
double Glass_z0 = desc.constantAsDouble("GlassModule_z0");
double Glass_Width = desc.constantAsDouble("GlassModule_width");
double Glass_Thickness = desc.constantAsDouble("GlassModule_length");
double Glass_thickness = desc.constantAsDouble("GlassModule_length");
double Glass_Gap = desc.constantAsDouble("GlassModule_wrap");
double glass_distance = desc.constantAsDouble("GlassModule_distance");
double Crystal_Width = desc.constantAsDouble("CrystalModule_width");
double Crystal_Thickness = desc.constantAsDouble("CrystalModule_length");
double Crystal_thickness = desc.constantAsDouble("CrystalModule_length");
double Crystal_Gap = desc.constantAsDouble("CrystalModule_wrap");
double crystal_distance = desc.constantAsDouble("CrystalModule_distance");
double Crystal_shift_z = desc.constantAsDouble("CrystalModule_z0");
double Crystal_z0 = desc.constantAsDouble("CrystalModule_z0");
// RIn and ROut will define outer tube embedding the calorimeter
// centers_rin/out define the maximum radius of module centers
// centers_rmin/out define the maximum radius of module centers
// so that modules are not overlapping with mother tube volume
double hypotenuse = sqrt(0.5 * glass_distance * glass_distance);
double centers_rin = RIn + hypotenuse + 1*mm;
double centers_rout = ROut - hypotenuse - 1*mm;
const double glassHypotenuse = std::hypot(glass_distance, glass_distance)/2;
const double crystalHypotenuse = glassHypotenuse/2;
// Offset these values a bit so we don't miss edge-blocks
const double glassCenters_rmax = ROut - glassHypotenuse + 1 * mm;
const double crystalCenters_rmin = RIn + crystalHypotenuse - 1 * mm;
// Also limits of the inner crystal blocks fill
const double cutout_tan = tan(ionCutout_dphi/2);
const double cutout_rmin = RIn_el + crystalHypotenuse - 1 * mm;
// Offset to align the modules at the zmin of the endcap,
const double Crystal_offset = -0.5 * (Crystal_thickness - thickness);
const double Glass_offset = -0.5*(Glass_thickness - thickness);
// envelope
// Assembly assembly(detName);
Tube outer_tube(RIn, ROut, SizeZ / 2.0, 0., 360.0 * deg);
Volume ecal_vol("negative_ecal", outer_tube, air_material);
// consists of an glass tube of the full thickness, and a crystal inner tube
// for the crystal that allows us to get closet to the beampipe without
// overlaps, and a partial electron tube that allows us to get closer to the
// electron beampipe in the region where there is no ion beampipe
Tube glass_tube(min(RIn + glassHypotenuse*2, trans_radius), ROut, SizeZ / 2.0, 0., 360.0 * deg);
Tube crystal_tube(RIn, min(RIn + glassHypotenuse*2, trans_radius), Crystal_thickness/ 2.0, 0., 360.0 * deg);
Tube electron_tube(RIn_el, RIn, Crystal_thickness / 2., ionCutout_dphi / 2., 360.0 * deg - ionCutout_dphi / 2);
UnionSolid outer_envelope(glass_tube, crystal_tube, Position(0,0,Crystal_offset));
UnionSolid envelope(outer_envelope, electron_tube, Position(0,0,Crystal_offset));
Volume ecal_vol("negative_ecal", envelope, air_material);
ecal_vol.setVisAttributes(desc.visAttributes("HybridEcalOuterVis"));
// TODO why 1cm and not something else?
double Glass_OuterR = ROut - 1 * cm ;
double Glass_InnerR = trans_radius;
glass_shift_z = Thickness / 2. - Glass_Thickness / 2.;
// Geometry of modules
Box glass_box("glass_box", Glass_Width * 0.5, Glass_Width * 0.5, Glass_Thickness * 0.5);
Box glass_box("glass_box", Glass_Width * 0.5, Glass_Width * 0.5, Glass_thickness * 0.5);
Volume glass_module("glass_module", glass_box, glass_material);
glass_module.setVisAttributes(desc.visAttributes("EcalEndcapNModuleVis"));
glass_module.setSensitiveDetector(sens);
Box crystal_box("crystal_box", Crystal_Width* 0.5, Crystal_Width * 0.5, Crystal_Thickness * 0.5);
Box crystal_box("crystal_box", Crystal_Width* 0.5, Crystal_Width * 0.5, Crystal_thickness * 0.5);
Volume crystal_module("crystal_module", crystal_box, crystal_material);
crystal_module.setVisAttributes(desc.visAttributes("EcalEndcapNModuleVis"));
crystal_module.setSensitiveDetector(sens);
......@@ -90,9 +105,11 @@ static Ref_t create_detector(Detector& desc, xml::Handle_t handle, SensitiveDete
// GLASS
double diameter = 2 * Glass_OuterR;
// How many towers do we have per row/columnt?
// Add a gap + diameter as if we have N towers, we have N-1 gaps;
int towersInRow = std::ceil((diameter + Glass_Gap) / (Glass_Width + Glass_Gap));
// Can we fit an even or odd amount of glass blocks within our rmax?
// This determines the transition points between crystal and glass as we need the
// outer crystal arangement to be in multiples of 2 (aligned with glass)
const int towersInRow = std::ceil((diameter + Glass_Gap) / (Glass_Width + Glass_Gap));
const bool align_even = (towersInRow % 2);
// Is it odd or even number of towersInRow
double leftTowerPos, topTowerPos;
......@@ -113,109 +130,39 @@ static Ref_t create_detector(Detector& desc, xml::Handle_t handle, SensitiveDete
int moduleIndex = 0;
// fmt::print("\nCE EMCAL GLASS SQUARE START\n");
// fmt::print("Glass_Thickness = {} cm;\n", Glass_Thickness / cm);
// fmt::print("Glass_Width = {} cm;\n", Glass_Width / cm);
// fmt::print("Glass_Gap = {} cm;\n", Glass_Gap / cm);
// fmt::print("Glass_InnerR = {} cm;\n", Glass_InnerR / cm);
// fmt::print("Glass_OuterR = {} cm;\n", Glass_OuterR / cm);
// fmt::print("Glass_PosZ = {} cm;\n", glass_shift_z / cm);
// fmt::print("Towers in Row/Col = {} cm;\n", glass_shift_z / cm);
// fmt::print("Top left tower pos = {:<10} {:<10} cm;\n", -leftTowerPos / cm, topTowerPos / cm);
// fmt::print("#Towers info:\n");
// fmt::print("#{:<5} {:<6} {:<3} {:<3} {:>10} {:>10} {}\n", "idx", "code", "col", "row", "x", "y", "name");
// We first do a "dry run", not really placing modules,
// but figuring out the ID scheme, number of modules, etc.
int glassModuleCount = 0;
int crystalModuleCount = 0;
int firstCrystRow = 1000000; // The first row, where crystals are started
int firstCrystCol = 1000000; // The fist column, where crystals are started
for(int rowIndex=0; rowIndex < towersInRow; rowIndex++) {
for(int colIndex=0; colIndex < towersInRow; colIndex++) {
double glass_x = leftTowerPos + colIndex * glass_distance;
double glass_y = topTowerPos + rowIndex * glass_distance;
double r = sqrt(glass_x * glass_x + glass_y * glass_y);
if (r < centers_rout && r > centers_rin) {
// we are placing something
if(r<trans_radius) {
// 4 crystal modules will be placed
crystalModuleCount+=4;
// Finding the first col and row where crystals start
// is the same algorithm as finding a minimum in array
if(colIndex<firstCrystCol) {
firstCrystCol = colIndex;
}
if(rowIndex<firstCrystRow) {
firstCrystRow = rowIndex;
}
}
else
{
// glass module will be places
glassModuleCount++;
}
}
}
}
// fmt::print("#Towers info:\n");
// fmt::print("#{:<5} {:<6} {:<3} {:<3} {:>10} {:>10} {}\n", "idx", "code", "col", "row", "x", "y", "name");
int glass_module_index = 0;
int cryst_module_index = 0;
for(int rowIndex=0; rowIndex < towersInRow; rowIndex++) {
for(int colIndex=0; colIndex < towersInRow; colIndex++) {
double glass_x = leftTowerPos + colIndex * (Glass_Width + Glass_Gap);
double glass_y = topTowerPos + rowIndex * (Glass_Width + Glass_Gap);
double r = sqrt(glass_x * glass_x + glass_y * glass_y);
if (r < centers_rout && r > centers_rin) {
int code = 1000 * rowIndex + colIndex;
std::string name = fmt::format("ce_EMCAL_glass_phys_{}", code);
if(r<trans_radius) {
// first crystal module
double crystal_x = glass_x - crystal_distance / 2;
double crystal_y = glass_y - crystal_distance / 2;
auto placement = ecal_vol.placeVolume(crystal_module, Position(crystal_x, crystal_y, Crystal_shift_z));
placement.addPhysVolID("sector", 1);
placement.addPhysVolID("module", cryst_module_index++);
// second crystal module
crystal_x = glass_x + crystal_distance / 2;
crystal_y = glass_y - crystal_distance / 2;
placement = ecal_vol.placeVolume(crystal_module, Position(crystal_x, crystal_y, Crystal_shift_z));
placement.addPhysVolID("sector", 1);
placement.addPhysVolID("module", cryst_module_index++);
// third crystal module
crystal_x = glass_x - crystal_distance / 2;
crystal_y = glass_y + crystal_distance / 2;
placement = ecal_vol.placeVolume(crystal_module, Position(crystal_x, crystal_y, Crystal_shift_z));
placement.addPhysVolID("sector", 1);
placement.addPhysVolID("module", cryst_module_index++);
// forth crystal module
crystal_x = glass_x + crystal_distance / 2;
crystal_y = glass_y + crystal_distance / 2;
placement = ecal_vol.placeVolume(crystal_module, Position(crystal_x, crystal_y, Crystal_shift_z));
placement.addPhysVolID("sector", 1);
placement.addPhysVolID("module", cryst_module_index++);
}
else
{
// glass module
auto placement = ecal_vol.placeVolume(glass_module, Position(glass_x, glass_y, glass_shift_z));
placement.addPhysVolID("sector", 2);
placement.addPhysVolID("module", glass_module_index++);
const double glass_x = leftTowerPos + colIndex * (Glass_Width + Glass_Gap);
const double glass_y = topTowerPos + rowIndex * (Glass_Width + Glass_Gap);
const double r = std::hypot(glass_x, glass_y);
// crystal if within the transition radius (as defined by the equivalent glass
// block)
if (r < trans_radius) {
for (const auto dx : {-1, 1}) {
for (const auto& dy : {-1, 1}) {
const double crystal_x = glass_x + dx * crystal_distance / 2;
const double crystal_y = glass_y + dy * crystal_distance / 2;
const double crystal_r = std::hypot(crystal_x, crystal_y);
// check if crystal in the main crystal ring?
const bool mainRing = (crystal_r > crystalCenters_rmin);
const bool innerRing = !mainRing && crystal_r > cutout_rmin;
const bool ionCutout = crystal_x > 0 && fabs(crystal_y / crystal_x) < cutout_tan;
if (mainRing || (innerRing && !ionCutout)) {
auto placement =
ecal_vol.placeVolume(crystal_module, Position(crystal_x, crystal_y, Crystal_z0 + Crystal_offset));
placement.addPhysVolID("sector", 1);
placement.addPhysVolID("module", cryst_module_index++);
}
}
}
// fmt::print(" {:<5} {:<6} {:<3} {:<3} {:>10.4f} {:>10.4f} {}\n", towerIndex, code, colIndex, rowIndex, x / cm, y / cm, name);
//glass_module_index++;
// Glass block if within the rmax
} else if (r < glassCenters_rmax) {
// glass module
auto placement = ecal_vol.placeVolume(glass_module, Position(glass_x, glass_y, Glass_z0 + Glass_offset));
placement.addPhysVolID("sector", 2);
placement.addPhysVolID("module", glass_module_index++);
}
}
}
......
......@@ -29,21 +29,9 @@ static Ref_t createDetector(Detector& description, xml::Handle_t e, SensitiveDet
string det_name = x_det.nameStr();
DetElement sdet(det_name, x_det.id());
Assembly assembly(det_name);
sens.setType("photoncounter");
sens.setType("tracker");
OpticalSurfaceManager surfMgr = description.surfaceManager();
// read module positions
std::vector<std::pair<double,double>> positions;
for (xml_coll_t x_positions_i(x_det, _Unicode(positions)); x_positions_i; ++x_positions_i) {
xml_comp_t x_positions = x_positions_i;
for (xml_coll_t x_position_i(x_positions, _U(position)); x_position_i; ++x_position_i) {
xml_comp_t x_position = x_position_i;
positions.push_back(
std::make_pair(x_positions.scale() * x_position.x() * mm,
x_positions.scale() * x_position.y() * mm));
}
}
bool projective = getAttrOrDefault(x_det, _Unicode(projective), false);
bool reflect = x_det.reflect(true);
......@@ -56,6 +44,7 @@ static Ref_t createDetector(Detector& description, xml::Handle_t e, SensitiveDet
int n_sensor = 1;
// dimensions
xml::Component dims = x_det.dimensions();
auto rmin = dims.rmin();
auto rmax = dims.rmax();
......@@ -63,6 +52,20 @@ static Ref_t createDetector(Detector& description, xml::Handle_t e, SensitiveDet
auto zmin = dims.zmin();
auto zpos = zmin + length / 2;
// envelope
Tube envShape(rmin, rmax, length / 2., 0., 2 * M_PI);
Volume envVol("MRICH_Envelope", envShape, air);
envVol.setVisAttributes(description.visAttributes(x_det.visStr()));
if (x_det.hasChild(_Unicode(envelope))) {
xml_comp_t x_envelope = x_det.child(_Unicode(envelope));
double thickness = x_envelope.thickness();
Material material = description.material(x_envelope.materialStr());
Tube envInsideShape(rmin + thickness, rmax - thickness, length / 2. - thickness);
SubtractionSolid envShellShape(envShape, envInsideShape);
Volume envShell("MRICH_Envelope_Inside", envShellShape, material);
envVol.placeVolume(envShell);
}
// expect only one module (for now)
xml_comp_t x_mod = x_det.child(_U(module));
string mod_name = x_mod.nameStr();
......@@ -70,193 +73,231 @@ static Ref_t createDetector(Detector& description, xml::Handle_t e, SensitiveDet
double mod_height = getAttrOrDefault(x_mod, _U(height), 130.0 * mm);
double mod_length = getAttrOrDefault(x_mod, _U(length), 130.0 * mm);
// various components
xml_comp_t x_frame = x_mod.child(_Unicode(frame));
xml_comp_t x_aerogel = x_mod.child(_Unicode(aerogel));
xml_comp_t x_lens = x_mod.child(_Unicode(lens));
xml_comp_t x_mirror = x_mod.child(_Unicode(mirror));
xml_comp_t x_photodet = x_mod.child(_Unicode(photodet));
// module
Box m_solid(mod_width / 2.0, mod_height / 2.0, mod_length / 2.0);
Volume m_volume(mod_name, m_solid, air);
m_volume.setVisAttributes(description.visAttributes(x_mod.visStr()));
DetElement mod_de( mod_name + std::string("_mod_") + std::to_string(1), 1);
double z_placement = - mod_length / 2.0;
// todo module frame
double frame_thickness = getAttrOrDefault(x_frame, _U(thickness), 2.0 * mm);
if (x_mod.hasChild(_Unicode(frame))) {
xml_comp_t x_frame = x_mod.child(_Unicode(frame));
double frame_thickness = getAttrOrDefault(x_frame, _U(thickness), 2.0 * mm);
Box frame_inside(mod_width / 2.0 - frame_thickness, mod_height / 2.0 - frame_thickness, mod_length / 2.0 - frame_thickness);
SubtractionSolid frame_solid(m_solid, frame_inside);
Material frame_mat = description.material(x_frame.materialStr());
Volume frame_vol(mod_name+"_frame", frame_solid, frame_mat);
auto frame_vis = getAttrOrDefault<std::string>(x_frame, _U(vis), std::string("GrayVis"));
frame_vol.setVisAttributes(description.visAttributes(frame_vis));
// update position
z_placement += frame_thickness / 2.0;
// place volume
m_volume.placeVolume(frame_vol);
// update position
z_placement += frame_thickness / 2.0;
}
// aerogel box
xml_comp_t x_aerogel_frame = x_aerogel.child(_Unicode(frame));
double aerogel_width = getAttrOrDefault(x_aerogel, _U(width), 130.0 * mm);
double aerogel_length = getAttrOrDefault(x_aerogel, _U(length), 130.0 * mm);
double foam_thickness = getAttrOrDefault(x_aerogel_frame, _U(thickness), 2.0 * mm);
Material foam_mat = description.material(x_aerogel_frame.materialStr());
Material aerogel_mat = description.material(x_aerogel.materialStr());
auto aerogel_vis = getAttrOrDefault<std::string>(x_aerogel, _U(vis), std::string("InvisibleWithDaughters"));
auto foam_vis = getAttrOrDefault<std::string>(x_aerogel_frame, _U(vis), std::string("RedVis"));
// aerogel foam frame
Box foam_box(aerogel_width / 2.0 + foam_thickness, aerogel_width / 2.0 + foam_thickness, (aerogel_length + foam_thickness) / 2.0);
Box aerogel_sub_box(aerogel_width / 2.0, aerogel_width / 2.0, (aerogel_length + foam_thickness) / 2.0);
SubtractionSolid foam_frame_solid(foam_box, aerogel_sub_box, Position(0, 0, foam_thickness));
Volume foam_vol(mod_name+"_aerogel_frame", foam_frame_solid, foam_mat);
foam_vol.setVisAttributes(description.visAttributes(foam_vis));
double foam_frame_zpos = -mod_length / 2.0 + frame_thickness + (aerogel_length + foam_thickness) / 2.0;
m_volume.placeVolume(foam_vol,Position(0,0,foam_frame_zpos));
// aerogel
Box aerogel_box(aerogel_width / 2.0, aerogel_width / 2.0, (aerogel_length) / 2.0);
Volume aerogel_vol(mod_name+"_aerogel", aerogel_box, aerogel_mat);
aerogel_vol.setVisAttributes(description.visAttributes(aerogel_vis));
double aerogel_zpos = foam_frame_zpos + foam_thickness / 2.0;
pv = m_volume.placeVolume(aerogel_vol,Position(0,0,aerogel_zpos));
DetElement aerogel_de(mod_de, mod_name + std::string("_aerogel_de") + std::to_string(1), 1);
aerogel_de.setPlacement(pv);
auto aerogel_surf = surfMgr.opticalSurface(dd4hep::getAttrOrDefault(x_aerogel, _Unicode(surface), "MRICH_AerogelOpticalSurface"));
SkinSurface skin0(description, aerogel_de, Form("MRICH_aerogel_skin_surface_%d", 1), aerogel_surf, aerogel_vol);
skin0.isValid();
// Fresnel Lens
// - The lens has a constant groove pitch (delta r) as opposed to fixing the groove height.
// - The lens area outside of the effective diamtere is flat.
// - The grooves are not curved, rather they are polycone shaped, ie a flat approximating the curvature.
auto lens_vis = getAttrOrDefault<std::string>(x_lens, _U(vis), std::string("AnlBlue"));
double groove_pitch = getAttrOrDefault(x_lens, _Unicode(pitch), 0.2 * mm);// 0.5 * mm);
double lens_f = getAttrOrDefault(x_lens, _Unicode(focal_length), 6.0*2.54*cm);
double eff_diameter = getAttrOrDefault(x_lens, _Unicode(effective_diameter), 152.4 * mm);
double lens_width = getAttrOrDefault(x_lens, _Unicode(width), 6.7*2.54*cm);
double center_thickness = getAttrOrDefault(x_lens, _U(thickness), 0.068 * 2.54 * cm);//2.0 * mm);
double n_acrylic = 1.49;
double lens_curvature = 1.0 / (lens_f*(n_acrylic - 1.0)); //confirmed
double full_ring_rmax = std::min(eff_diameter / 2.0, lens_width/2.0);
double N_grooves = std::ceil((full_ring_rmax) / groove_pitch);
double groove_last_rmin = (N_grooves - 1) * groove_pitch;
double groove_last_rmax = N_grooves * groove_pitch;
auto groove_sagitta = [&](double r) { return lens_curvature * std::pow(r, 2) / (1.0 + 1.0); };
double lens_thickness = groove_sagitta(groove_last_rmax) - groove_sagitta(groove_last_rmin) + center_thickness;
Material lens_mat = description.material(x_lens.materialStr());
Box lens_box(lens_width / 2.0, lens_width / 2.0, (center_thickness) / 2.0);
SubtractionSolid flat_lens(lens_box, Tube(0.0, full_ring_rmax, 2 * center_thickness));
Assembly lens_vol(mod_name + "_lens");
Volume flatpart_lens_vol( "flatpart_lens", flat_lens, lens_mat);
lens_vol.placeVolume(flatpart_lens_vol);//,Position(0,0,lens_zpos));
Solid fresnel_lens_solid;
int i_groove = 0;
double groove_rmax = groove_pitch;
double groove_rmin = 0;
while ( groove_rmax <= full_ring_rmax ) {
double dZ = groove_sagitta(groove_rmax) - groove_sagitta(groove_rmin);
Polycone groove_solid(0, 2.0 * M_PI,
{groove_rmin, groove_rmin, groove_rmin},
{groove_rmax, groove_rmax, groove_rmin},
{-lens_thickness/2.0, lens_thickness/2.0-dZ, lens_thickness/2.0});
Volume lens_groove_vol("lens_groove_" + std::to_string(i_groove), groove_solid, lens_mat);
lens_vol.placeVolume(lens_groove_vol);
//Volume groove_vol(groove_solid, lens_mat, par->name.c_str(), 0, 0, 0);
//new G4PVPlacement(0, par->pos, Groove_log[i], par->name.c_str(), motherLV, false, 0, OverlapCheck());
//phi1 = phi1 + halfpi; //g4 pre-defined: halfpi=pi/2
//Tube sub_cylinder(r0, r1, 3*eff_diameter);
//IntersectionSolid groove_solid(lens_box,lens_sphere, Position(0,0,-eff_diameter/2.0 + lens_thickness/2.0+(t-lens_t)/2.0 ));
//IntersectionSolid lens_ring(groove_solid, sub_cylinder);
//if (i_groove == 0) {
// fresnel_lens_solid = groove_solid;
//} else {
// fresnel_lens_solid = UnionSolid(fresnel_lens_solid, groove_solid);
//}
//r0 = r1;
//if(i_groove > 3) {
// SubtractionSolid flat_lens(lens_box,Tube(0.0, r0, 3*eff_diameter));
// fresnel_lens_solid = UnionSolid(fresnel_lens_solid, flat_lens);
// break; // temporary
//}
i_groove++;
groove_rmin = (i_groove )*groove_pitch;
groove_rmax = (i_groove+1)*groove_pitch;
if (x_mod.hasChild(_Unicode(aerogel))) {
xml_comp_t x_aerogel = x_mod.child(_Unicode(aerogel));
double aerogel_width = getAttrOrDefault(x_aerogel, _U(width), 130.0 * mm);
double aerogel_length = getAttrOrDefault(x_aerogel, _U(length), 130.0 * mm);
Material aerogel_mat = description.material(x_aerogel.materialStr());
auto aerogel_vis = getAttrOrDefault<std::string>(x_aerogel, _U(vis), std::string("InvisibleWithDaughters"));
xml_comp_t x_aerogel_frame = x_aerogel.child(_Unicode(frame));
double foam_thickness = getAttrOrDefault(x_aerogel_frame, _U(thickness), 2.0 * mm);
Material foam_mat = description.material(x_aerogel_frame.materialStr());
auto foam_vis = getAttrOrDefault<std::string>(x_aerogel_frame, _U(vis), std::string("RedVis"));
// foam frame
Box foam_box(aerogel_width / 2.0 + foam_thickness, aerogel_width / 2.0 + foam_thickness, (aerogel_length + foam_thickness) / 2.0);
Box foam_sub_box(aerogel_width / 2.0, aerogel_width / 2.0, (aerogel_length + foam_thickness) / 2.0);
SubtractionSolid foam_frame_solid(foam_box, foam_sub_box, Position(0, 0, foam_thickness));
Volume foam_vol(mod_name+"_aerogel_frame", foam_frame_solid, foam_mat);
foam_vol.setVisAttributes(description.visAttributes(foam_vis));
// aerogel
Box aerogel_box(aerogel_width / 2.0, aerogel_width / 2.0, (aerogel_length) / 2.0);
Volume aerogel_vol(mod_name+"_aerogel", aerogel_box, aerogel_mat);
aerogel_vol.setVisAttributes(description.visAttributes(aerogel_vis));
// update position
z_placement += (aerogel_length + foam_thickness) / 2.0;
// place foam frame
pv = m_volume.placeVolume(foam_vol,Position(0,0,z_placement));
// place aerogel
z_placement += foam_thickness / 2.0;
pv = m_volume.placeVolume(aerogel_vol,Position(0,0,z_placement));
DetElement aerogel_de(mod_de, mod_name + std::string("_aerogel_de") + std::to_string(1), 1);
aerogel_de.setPlacement(pv);
// update position
z_placement += aerogel_length / 2.0;
// optical surfaces
auto aerogel_surf = surfMgr.opticalSurface(dd4hep::getAttrOrDefault(x_aerogel, _Unicode(surface), "MRICH_AerogelOpticalSurface"));
SkinSurface skin_surf(description, aerogel_de, Form("MRICH_aerogel_skin_surface_%d", 1), aerogel_surf, aerogel_vol);
skin_surf.isValid();
}
//fresnel_lens_solid = UnionSolid(fresnel_lens_solid, flat_lens);
//Volume lens_vol(mod_name + "_lens", fresnel_lens_solid, lens_mat);
// Fresnel Lens
if (x_mod.hasChild(_Unicode(lens))) {
xml_comp_t x_lens = x_mod.child(_Unicode(lens));
// - The lens has a constant groove pitch (delta r) as opposed to fixing the groove height.
// - The lens area outside of the effective diamtere is flat.
// - The grooves are not curved, rather they are polycone shaped, ie a flat approximating the curvature.
auto lens_vis = getAttrOrDefault<std::string>(x_lens, _U(vis), std::string("AnlBlue"));
double groove_pitch = getAttrOrDefault(x_lens, _Unicode(pitch), 0.2 * mm);// 0.5 * mm);
double lens_f = getAttrOrDefault(x_lens, _Unicode(focal_length), 6.0*2.54*cm);
double eff_diameter = getAttrOrDefault(x_lens, _Unicode(effective_diameter), 152.4 * mm);
double lens_width = getAttrOrDefault(x_lens, _Unicode(width), 6.7*2.54*cm);
double center_thickness = getAttrOrDefault(x_lens, _U(thickness), 0.068 * 2.54 * cm);//2.0 * mm);
double n_acrylic = 1.49;
double lens_curvature = 1.0 / (lens_f*(n_acrylic - 1.0)); //confirmed
double full_ring_rmax = std::min(eff_diameter / 2.0, lens_width/2.0);
double N_grooves = std::ceil((full_ring_rmax) / groove_pitch);
double groove_last_rmin = (N_grooves - 1) * groove_pitch;
double groove_last_rmax = N_grooves * groove_pitch;
auto groove_sagitta = [&](double r) { return lens_curvature * std::pow(r, 2) / (1.0 + 1.0); };
double lens_thickness = groove_sagitta(groove_last_rmax) - groove_sagitta(groove_last_rmin) + center_thickness;
Material lens_mat = description.material(x_lens.materialStr());
Box lens_box(lens_width / 2.0, lens_width / 2.0, (center_thickness) / 2.0);
SubtractionSolid flat_lens(lens_box, Tube(0.0, full_ring_rmax, 2 * center_thickness));
Assembly lens_vol(mod_name + "_lens");
Volume flatpart_lens_vol( "flatpart_lens", flat_lens, lens_mat);
lens_vol.placeVolume(flatpart_lens_vol);
int i_groove = 0;
double groove_rmax = groove_pitch;
double groove_rmin = 0;
while ( groove_rmax <= full_ring_rmax ) {
double dZ = groove_sagitta(groove_rmax) - groove_sagitta(groove_rmin);
Polycone groove_solid(0, 2.0 * M_PI,
{groove_rmin, groove_rmin, groove_rmin},
{groove_rmax, groove_rmax, groove_rmin},
{-lens_thickness/2.0, lens_thickness/2.0-dZ, lens_thickness/2.0});
Volume lens_groove_vol("lens_groove_" + std::to_string(i_groove), groove_solid, lens_mat);
lens_vol.placeVolume(lens_groove_vol);
i_groove++;
groove_rmin = (i_groove )*groove_pitch;
groove_rmax = (i_groove+1)*groove_pitch;
}
lens_vol.setVisAttributes(description.visAttributes(lens_vis));
double lens_zpos = aerogel_zpos +aerogel_length/ 2.0 + foam_thickness + lens_thickness/2.0;
pv = m_volume.placeVolume(lens_vol,Position(0,0,lens_zpos));
DetElement lens_de(mod_de, mod_name + std::string("_lens_de") + std::to_string(1), 1);
lens_de.setPlacement(pv);
lens_vol.setVisAttributes(description.visAttributes(lens_vis));
// update position
z_placement += lens_thickness/2.0;
// place volume
pv = m_volume.placeVolume(lens_vol,Position(0,0,z_placement));
DetElement lens_de(mod_de, mod_name + std::string("_lens_de") + std::to_string(1), 1);
lens_de.setPlacement(pv);
// update position
z_placement += lens_thickness/2.0;
// optical surfaces
auto lens_surf = surfMgr.opticalSurface(dd4hep::getAttrOrDefault(x_lens, _Unicode(surface), "MRICH_LensOpticalSurface"));
SkinSurface skin_surf(description, lens_de, Form("MRichFresnelLens_skin_surface_%d", 1), lens_surf, lens_vol);
skin_surf.isValid();
}
auto surf = surfMgr.opticalSurface(dd4hep::getAttrOrDefault(x_lens, _Unicode(surface), "MRICH_LensOpticalSurface"));
SkinSurface skin(description, lens_de, Form("MRichFresnelLens_skin_surface_%d", 1), surf, lens_vol);
skin.isValid();
// mirror
if (x_mod.hasChild(_Unicode(space))) {
xml_comp_t x_space = x_mod.child(_Unicode(space));
z_placement += getAttrOrDefault(x_space, _U(thickness), 0.0 * mm);
}
// mirror
auto mirror_vis = getAttrOrDefault<std::string>(x_mirror, _U(vis), std::string("AnlGray"));
double mirror_x1 = getAttrOrDefault(x_mirror, _U(x1), 100.0 * mm);
double mirror_x2 = getAttrOrDefault(x_mirror, _U(x2), 80.0 * mm);
double mirror_length = getAttrOrDefault(x_mirror, _U(length), 130.0 * mm);
double mirror_thickness = getAttrOrDefault(x_mirror, _U(thickness), 2.0 * mm);
double outer_x1 = (mirror_x1+mirror_thickness)/2.0;
double outer_x2 = (mirror_x2+mirror_thickness)/2.0;
Trd2 outer_mirror_trd(outer_x1, outer_x2, outer_x1, outer_x2, mirror_length/2.0);
Trd2 inner_mirror_trd(mirror_x1 / 2.0, mirror_x2 / 2.0, mirror_x1 / 2.0,mirror_x2 / 2.0, mirror_length/2.0+0.1*mm);
SubtractionSolid mirror_solid(outer_mirror_trd, inner_mirror_trd);
Material mirror_mat = description.material(x_mirror.materialStr());
Volume mirror_vol(mod_name+"_mirror", mirror_solid, mirror_mat);
double mirror_zpos = lens_zpos + lens_thickness/2.0 + foam_thickness + mirror_length/2.0;
pv = m_volume.placeVolume(mirror_vol,Position(0,0,mirror_zpos));
DetElement mirror_de(mod_de, mod_name + std::string("_mirror_de") + std::to_string(1), 1);
mirror_de.setPlacement(pv);
auto mirror_surf = surfMgr.opticalSurface(dd4hep::getAttrOrDefault(x_mirror, _Unicode(surface), "MRICH_MirrorOpticalSurface"));
SkinSurface skin1(description, mirror_de, Form("MRICH_mirror_skin_surface_%d", 1), mirror_surf, mirror_vol);
skin1.isValid();
if (x_mod.hasChild(_Unicode(mirror))) {
xml_comp_t x_mirror = x_mod.child(_Unicode(mirror));
auto mirror_vis = getAttrOrDefault<std::string>(x_mirror, _U(vis), std::string("AnlGray"));
double mirror_x1 = getAttrOrDefault(x_mirror, _U(x1), 100.0 * mm);
double mirror_x2 = getAttrOrDefault(x_mirror, _U(x2), 80.0 * mm);
double mirror_length = getAttrOrDefault(x_mirror, _U(length), 130.0 * mm);
double mirror_thickness = getAttrOrDefault(x_mirror, _U(thickness), 2.0 * mm);
double outer_x1 = (mirror_x1+mirror_thickness)/2.0;
double outer_x2 = (mirror_x2+mirror_thickness)/2.0;
Trd2 outer_mirror_trd(outer_x1, outer_x2, outer_x1, outer_x2, mirror_length/2.0);
Trd2 inner_mirror_trd(mirror_x1 / 2.0, mirror_x2 / 2.0, mirror_x1 / 2.0,mirror_x2 / 2.0, mirror_length/2.0+0.1*mm);
SubtractionSolid mirror_solid(outer_mirror_trd, inner_mirror_trd);
Material mirror_mat = description.material(x_mirror.materialStr());
Volume mirror_vol(mod_name+"_mirror", mirror_solid, mirror_mat);
// update position
z_placement += mirror_length/2.0;
// place volume
pv = m_volume.placeVolume(mirror_vol,Position(0,0,z_placement));
DetElement mirror_de(mod_de, mod_name + std::string("_mirror_de") + std::to_string(1), 1);
mirror_de.setPlacement(pv);
// update position
z_placement += mirror_length/2.0;
// optical surfaces
auto mirror_surf = surfMgr.opticalSurface(dd4hep::getAttrOrDefault(x_mirror, _Unicode(surface), "MRICH_MirrorOpticalSurface"));
SkinSurface skin_surf(description, mirror_de, Form("MRICH_mirror_skin_surface_%d", 1), mirror_surf, mirror_vol);
skin_surf.isValid();
}
// photon detector
xml_comp_t x_photodet_sensor = x_photodet.child(_Unicode(sensor));
auto photodet_vis = getAttrOrDefault<std::string>(x_photodet, _U(vis), std::string("AnlRed"));
double photodet_width = getAttrOrDefault(x_photodet, _U(width), 130.0 * mm);
double photodet_thickness = getAttrOrDefault(x_photodet, _U(thickness), 2.0 * mm);
double sensor_thickness = getAttrOrDefault(x_photodet_sensor, _U(thickness), 2.0 * mm);
Material photodet_mat = description.material(x_photodet.materialStr());
Material sensor_mat = description.material(x_photodet_sensor.materialStr());
int sensor_nx = getAttrOrDefault(x_photodet_sensor, _Unicode(nx), 2);
int sensor_ny = getAttrOrDefault(x_photodet_sensor, _Unicode(ny), 2);
Box window_box(photodet_width/2.0,photodet_width/2.0,photodet_thickness/2.0);
Volume window_vol(mod_name+"_window", window_box, photodet_mat);
double window_zpos = mirror_zpos + mirror_length/2.0+photodet_thickness/2.0;
pv = m_volume.placeVolume(window_vol,Position(0,0,window_zpos));
DetElement comp_de(mod_de, std::string("mod_sensor_de_") + std::to_string(1) , 1);
comp_de.setPlacement(pv);
// sensitive
pv.addPhysVolID("sensor", n_sensor);
window_vol.setSensitiveDetector(sens);
sensitives[mod_name].push_back(pv);
++n_sensor;
// photon detector electronics layers
double layer_zpos = window_zpos + photodet_thickness/2.0;
int i_layer = 1;
for (xml_coll_t li(x_photodet, _Unicode(layer)); li; ++li) {
xml_comp_t x_layer = li;
Material layer_mat = description.material(x_layer.materialStr());
double layer_thickness = x_layer.thickness();
Box layer_box(photodet_width/2.0,photodet_width/2.0,layer_thickness/2.0);
Volume layer_vol(mod_name + "_layer_" + std::to_string(i_layer), layer_box, layer_mat);
layer_zpos += layer_thickness / 2.0;
pv = m_volume.placeVolume(layer_vol,Position(0,0,layer_zpos));
DetElement layer_de(mod_de, std::string("mod_layer_de_") + std::to_string(i_layer), 1);
layer_de.setPlacement(pv);
layer_zpos += layer_thickness / 2.0;
i_layer++;
if (x_mod.hasChild(_Unicode(photodet))) {
xml_comp_t x_photodet = x_mod.child(_Unicode(photodet));
auto photodet_vis = getAttrOrDefault<std::string>(x_photodet, _U(vis), std::string("AnlRed"));
double photodet_width = getAttrOrDefault(x_photodet, _U(width), 130.0 * mm);
double photodet_thickness = getAttrOrDefault(x_photodet, _U(thickness), 2.0 * mm);
Material photodet_mat = description.material(x_photodet.materialStr());
Box window_box(photodet_width/2.0,photodet_width/2.0,photodet_thickness/2.0);
Volume window_vol(mod_name+"_window", window_box, photodet_mat);
// update position
z_placement += photodet_thickness/2.0;
// place volume
pv = m_volume.placeVolume(window_vol,Position(0,0,z_placement));
DetElement comp_de(mod_de, mod_name + std::string("_sensor_de_") + std::to_string(1) , 1);
comp_de.setPlacement(pv);
// update position
z_placement += photodet_thickness/2.0;
// sensitive
pv.addPhysVolID("sensor", n_sensor);
window_vol.setSensitiveDetector(sens);
sensitives[mod_name].push_back(pv);
++n_sensor;
// sensor
xml_comp_t x_sensor = x_photodet.child(_Unicode(sensor));
double sensor_thickness = getAttrOrDefault(x_sensor, _U(thickness), 2.0 * mm);
Material sensor_mat = description.material(x_sensor.materialStr());
int sensor_nx = getAttrOrDefault(x_sensor, _Unicode(nx), 2);
int sensor_ny = getAttrOrDefault(x_sensor, _Unicode(ny), 2);
// layers
int i_layer = 1;
for (xml_coll_t li(x_photodet, _Unicode(layer)); li; ++li) {
xml_comp_t x_layer = li;
Material layer_mat = description.material(x_layer.materialStr());
double layer_thickness = x_layer.thickness();
Box layer_box(photodet_width/2.0,photodet_width/2.0,layer_thickness/2.0);
Volume layer_vol(mod_name + "_layer_" + std::to_string(i_layer), layer_box, layer_mat);
// update position
z_placement += layer_thickness / 2.0;
// place volume
pv = m_volume.placeVolume(layer_vol,Position(0,0,z_placement));
DetElement layer_de(mod_de, mod_name + std::string("_layer_de_") + std::to_string(i_layer), 1);
layer_de.setPlacement(pv);
// update position
z_placement += layer_thickness / 2.0;
i_layer++;
}
}
//for (size_t ic = 0; ic < sensVols.size(); ++ic) {
......@@ -277,11 +318,6 @@ static Ref_t createDetector(Detector& description, xml::Handle_t e, SensitiveDet
module_assembly_delements[mod_name] = mod_de;
// end module
// detector envelope
Tube envShape(rmin, rmax, length / 2., 0., 2 * M_PI);
Volume envVol("MRICH_Envelope", envShape, air);
envVol.setVisAttributes(description.visAttributes(x_det.visStr()));
// place modules in the sectors (disk)
auto points = athena::geo::fillSquares({0., 0.}, mod_width, rmin, rmax);
......@@ -291,45 +327,51 @@ static Ref_t createDetector(Detector& description, xml::Handle_t e, SensitiveDet
// determine module direction, always facing z = 0
double roty = dims.zmin() < 0. ? -M_PI : 0 ;
// read module positions
std::vector<std::tuple<double,double,double>> positions;
for (xml_coll_t x_positions_i(x_det, _Unicode(positions)); x_positions_i; ++x_positions_i) {
xml_comp_t x_positions = x_positions_i;
for (xml_coll_t x_position_i(x_positions, _U(position)); x_position_i; ++x_position_i) {
xml_comp_t x_position = x_position_i;
positions.push_back(
std::make_tuple(x_positions.scale() * x_position.x() * mm,
x_positions.scale() * x_position.y() * mm,
-x_positions.z0()));
}
}
// if no positions, then autoplacement
if (positions.empty()) {
for (double x = mod_width / 2.0; x < rmax - mod_width / 2.0; x += mod_width) {
for (double y = mod_width / 2.0; y < rmax - mod_width / 2.0; y += mod_width) {
if (pow(x + mod_width / 2.0,2) + pow(y + mod_width / 2.0,2) > rmax*rmax) continue;
if (pow(x - mod_width / 2.0,2) + pow(y - mod_width / 2.0,2) < rmin*rmin) continue;
positions.push_back(std::make_tuple(x, y, 0));
}
}
}
// place modules
int i_mod = 1; // starts at 1
for (auto& p: positions) {
// get positions in one quadrant
double x = p.first;
double y = p.second;
double z = -zpos;
double x = std::get<0>(p);
double y = std::get<1>(p);
double z0 = std::get<2>(p);
// and place in all quadrants (intentional shadowing)
for (auto& p: decltype(positions){{x,y}, {y,-x}, {-x,-y}, {-y,x}}) {
for (auto& p: decltype(positions){{x,y,z0}, {y,-x,z0}, {-x,-y,z0}, {-y,x,z0}}) {
// get positions (intentional shadowing)
double x = p.first;
double y = p.second;
// get angles
double rotAngX = atan(y/z);
double rotAngY = -1.*atan(x/z);
/*
ROOT::Math::XYZVector x_location(p.x(), p.y(), zmin+std::signbit(zmin)*mod_length/2.0);
ROOT::Math::XYZVector z_dir(0, 0, 1);
ROOT::Math::XYZVector x_dir(1, 0, 0);
ROOT::Math::XYZVector rot_axis = x_location.Cross(z_dir);
double rot_angle = ROOT::Math::VectorUtil::Angle(z_dir,x_location);
ROOT::Math::AxisAngle proj_rot(rot_axis,-1.0*rot_angle);
ROOT::Math::AxisAngle grid_fix_rot(x_location,0.0*rot_angle);
auto new_x_dir = grid_fix_rot*x_dir;
// operations are inversely ordered
Transform3D tr = Translation3D(p.x(), p.y(), 0.) // move to position
* RotationY(roty); // facing z = 0.
*/
double x = std::get<0>(p);
double y = std::get<1>(p);
double z0 = std::get<2>(p);
Transform3D tr;
if(projective) {
tr = Translation3D(x, y, 0)
* RotationX(rotAngX)
* RotationY(rotAngY);
double rotAngX = atan(y/z0);
double rotAngY = -1.*atan(x/z0);
tr = Translation3D(x, y, 0) * RotationX(rotAngX) * RotationY(rotAngY);
} else {
tr = Translation3D(x, y, 0) * RotationX(0);
}
......@@ -346,6 +388,16 @@ static Ref_t createDetector(Detector& description, xml::Handle_t e, SensitiveDet
}
}
// additional layers
if (x_det.hasChild(_Unicode(layer))) {
xml_comp_t x_layer = x_det.child(_Unicode(layer));
double layer_thickness = x_layer.thickness();
Material layer_mat = description.material(x_layer.materialStr());
Tube frameShape(rmin, rmax, layer_thickness / 2., 0., 2 * M_PI);
Volume frameVol("MRICH_Frame", frameShape, layer_mat);
pv = envVol.placeVolume(frameVol, Position(0, 0, (length - layer_thickness) / 2.0));
}
// place envelope
Volume motherVol = description.pickMotherVolume(sdet);
if (reflect) {
......@@ -385,7 +437,7 @@ static Ref_t createDetector(Detector& description, xml::Handle_t e, SensitiveDet
// modVol.placeVolume(mVol, Position(0., 0., -0.1*mm));
//
// modVol.setVisAttributes(description.visAttributes(mods.visStr()));
// sens.setType("photoncounter");
// sens.setType("tracker");
// modVol.setSensitiveDetector(sens);
//
// // place modules in the sectors (disk)
......
//----------------------------------
// pfRICH: Proximity Focusing RICH
// Author: C. Dilks
//----------------------------------
#include "DD4hep/DetFactoryHelper.h"
#include "DD4hep/OpticalSurfaces.h"
#include "DD4hep/Printout.h"
#include "DDRec/DetectorData.h"
#include "DDRec/Surface.h"
#include "GeometryHelpers.h"
#include "Math/Point2D.h"
#include "TMath.h"
#include "TString.h"
#include <XML/Helper.h>
using namespace dd4hep;
using namespace dd4hep::rec;
// create the detector
static Ref_t createDetector(Detector& desc, xml::Handle_t handle, SensitiveDetector sens)
{
xml::DetElement detElem = handle;
std::string detName = detElem.nameStr();
int detID = detElem.id();
DetElement det(detName, detID);
xml::Component dims = detElem.dimensions();
OpticalSurfaceManager surfMgr = desc.surfaceManager();
// attributes -----------------------------------------------------------
// - vessel
double vesselLength = dims.attr<double>(_Unicode(length));
double vesselZmin = dims.attr<double>(_Unicode(zmin));
double vesselZmax = dims.attr<double>(_Unicode(zmax));
double vesselRmin0 = dims.attr<double>(_Unicode(rmin0));
double vesselRmin1 = dims.attr<double>(_Unicode(rmin1));
double vesselRmax0 = dims.attr<double>(_Unicode(rmax0));
double vesselRmax1 = dims.attr<double>(_Unicode(rmax1));
double wallThickness = dims.attr<double>(_Unicode(wall_thickness));
double windowThickness = dims.attr<double>(_Unicode(window_thickness));
auto vesselMat = desc.material(detElem.attr<std::string>(_Unicode(material)));
auto gasvolMat = desc.material(detElem.attr<std::string>(_Unicode(gas)));
auto vesselVis = desc.visAttributes(detElem.attr<std::string>(_Unicode(vis_vessel)));
auto gasvolVis = desc.visAttributes(detElem.attr<std::string>(_Unicode(vis_gas)));
// - radiator (applies to aerogel and filter)
auto radiatorElem = detElem.child(_Unicode(radiator));
double radiatorRmin = radiatorElem.attr<double>(_Unicode(rmin));
double radiatorRmax = radiatorElem.attr<double>(_Unicode(rmax));
double radiatorPhiw = radiatorElem.attr<double>(_Unicode(phiw));
double radiatorPitch = radiatorElem.attr<double>(_Unicode(pitch));
double radiatorFrontplane = radiatorElem.attr<double>(_Unicode(frontplane));
// - aerogel
auto aerogelElem = radiatorElem.child(_Unicode(aerogel));
auto aerogelMat = desc.material(aerogelElem.attr<std::string>(_Unicode(material)));
auto aerogelVis = desc.visAttributes(aerogelElem.attr<std::string>(_Unicode(vis)));
double aerogelThickness = aerogelElem.attr<double>(_Unicode(thickness));
// - filter
auto filterElem = radiatorElem.child(_Unicode(filter));
auto filterMat = desc.material(filterElem.attr<std::string>(_Unicode(material)));
auto filterVis = desc.visAttributes(filterElem.attr<std::string>(_Unicode(vis)));
double filterThickness = filterElem.attr<double>(_Unicode(thickness));
// - sensor module
auto sensorElem = detElem.child(_Unicode(sensors)).child(_Unicode(module));
auto sensorMat = desc.material(sensorElem.attr<std::string>(_Unicode(material)));
auto sensorVis = desc.visAttributes(sensorElem.attr<std::string>(_Unicode(vis)));
auto sensorSurf = surfMgr.opticalSurface(sensorElem.attr<std::string>(_Unicode(surface)));
double sensorSide = sensorElem.attr<double>(_Unicode(side));
double sensorGap = sensorElem.attr<double>(_Unicode(gap));
double sensorThickness = sensorElem.attr<double>(_Unicode(thickness));
// - sensor plane
auto sensorPlaneElem = detElem.child(_Unicode(sensors)).child(_Unicode(plane));
double sensorPlaneDist = sensorPlaneElem.attr<double>(_Unicode(sensordist));
double sensorPlaneRmin = sensorPlaneElem.attr<double>(_Unicode(rmin));
double sensorPlaneRmax = sensorPlaneElem.attr<double>(_Unicode(rmax));
// - debugging switches
int debug_optics_mode = detElem.attr<int>(_Unicode(debug_optics));
// if debugging optics, override some settings
bool debug_optics = debug_optics_mode > 0;
if (debug_optics) {
printout(WARNING, "PFRICH_geo", "DEBUGGING PFRICH OPTICS");
switch (debug_optics_mode) {
case 1:
vesselMat = aerogelMat = filterMat = sensorMat = gasvolMat = desc.material("VacuumOptical");
break;
case 2:
vesselMat = aerogelMat = filterMat = sensorMat = desc.material("VacuumOptical");
break;
default:
printout(FATAL, "PFRICH_geo", "UNKNOWN debug_optics_mode");
return det;
};
aerogelVis = sensorVis;
gasvolVis = vesselVis = desc.invisible();
};
// BUILD VESSEL //////////////////////////////////////
/* - `vessel`: aluminum enclosure, the mother volume of the pfRICH
* - `gasvol`: gas volume, which fills `vessel`; all other volumes defined below
* are children of `gasvol`
*/
// tank solids
double boreDelta = vesselRmin1 - vesselRmin0;
Cone vesselTank(vesselLength / 2.0, vesselRmin1, vesselRmax1, vesselRmin0, vesselRmax0);
Cone gasvolTank(vesselLength / 2.0 - windowThickness, vesselRmin1 + wallThickness, vesselRmax1 - wallThickness,
vesselRmin0 + wallThickness, vesselRmax0 - wallThickness);
// extra solids for `debug_optics` only
Box vesselBox(1001, 1001, 1001);
Box gasvolBox(1000, 1000, 1000);
// choose vessel and gasvol solids (depending on `debug_optics_mode` (0=disabled))
Solid vesselSolid, gasvolSolid;
switch (debug_optics_mode) {
case 0:
vesselSolid = vesselTank;
gasvolSolid = gasvolTank;
break; // `!debug_optics`
case 1:
vesselSolid = vesselBox;
gasvolSolid = gasvolBox;
break;
case 2:
vesselSolid = vesselBox;
gasvolSolid = gasvolTank;
break;
};
// volumes
Volume vesselVol(detName, vesselSolid, vesselMat);
Volume gasvolVol(detName + "_gas", gasvolSolid, gasvolMat);
vesselVol.setVisAttributes(vesselVis);
gasvolVol.setVisAttributes(gasvolVis);
// reference positions
// - the vessel is created such that the center of the cylindrical tank volume
// coincides with the origin; this is called the "origin position" of the vessel
// - when the vessel (and its children volumes) is placed, it is translated in
// the z-direction to be in the proper ATHENA-integration location
// - these reference positions are for the frontplane and backplane of the vessel,
// with respect to the vessel origin position
auto originFront = Position(0., 0., vesselLength / 2.0);
auto originBack = Position(0., 0., -vesselLength / 2.0);
// sensitive detector type
sens.setType("tracker");
// BUILD RADIATOR //////////////////////////////////////
// attributes
double airGap = 0.01*mm; // air gap between aerogel and filter (FIXME? actually it's currently a gas gap)
// solid and volume: create aerogel and filter
Cone aerogelSolid(
aerogelThickness/2,
radiatorRmin + boreDelta * aerogelThickness / vesselLength, /* at backplane */
radiatorRmax,
radiatorRmin, /* at frontplane */
radiatorRmax
);
Cone filterSolid(
filterThickness/2,
radiatorRmin + boreDelta * (aerogelThickness + airGap + filterThickness) / vesselLength, /* at backplane */
radiatorRmax,
radiatorRmin + boreDelta * (aerogelThickness + airGap) / vesselLength, /* at frontplane */
radiatorRmax
);
Volume aerogelVol(detName + "_aerogel", aerogelSolid, aerogelMat);
Volume filterVol(detName + "_filter", filterSolid, filterMat);
aerogelVol.setVisAttributes(aerogelVis);
filterVol.setVisAttributes(filterVis);
// aerogel placement and surface properties
// TODO [low-priority]: define skin properties for aerogel and filter
auto radiatorPos = Position(0., 0., radiatorFrontplane - 0.5 * aerogelThickness) + originFront;
auto aerogelPV = gasvolVol.placeVolume(
aerogelVol,
Translation3D(radiatorPos.x(), radiatorPos.y(), radiatorPos.z()) // re-center to originFront
* RotationY(radiatorPitch) // change polar angle to specified pitch // (FIXME: probably broken, currently not in use)
);
DetElement aerogelDE(det, "aerogel_de", 0);
aerogelDE.setPlacement(aerogelPV);
// SkinSurface aerogelSkin(desc, aerogelDE, "mirror_optical_surface", aerogelSurf, aerogelVol);
// aerogelSkin.isValid();
// filter placement and surface properties
if (!debug_optics) {
auto filterPV = gasvolVol.placeVolume(
filterVol,
Translation3D(0., 0., -airGap) // add an airgap (FIXME: actually a gas gap)
* Translation3D(radiatorPos.x(), radiatorPos.y(), radiatorPos.z()) // re-center to originFront
* RotationY(radiatorPitch) // change polar angle
* Translation3D(0., 0., -(aerogelThickness + filterThickness) / 2.) // move to aerogel backplane
);
DetElement filterDE(det, "filter_de", 0);
filterDE.setPlacement(filterPV);
// SkinSurface filterSkin(desc, filterDE, "mirror_optical_surface", filterSurf, filterVol);
// filterSkin.isValid();
};
// BUILD SENSORS ///////////////////////
// solid and volume: single sensor module
Box sensorSolid(sensorSide / 2., sensorSide / 2., sensorThickness / 2.);
Volume sensorVol(detName + "_sensor", sensorSolid, sensorMat);
sensorVol.setVisAttributes(sensorVis);
// sensitivity
if (!debug_optics)
sensorVol.setSensitiveDetector(sens);
// sensor plane positioning: we want `sensorPlaneDist` to be the distance between the
// aerogel backplane (i.e., aerogel/filter boundary) and the sensor active surface (e.g, photocathode)
double sensorZpos = radiatorFrontplane - aerogelThickness - sensorPlaneDist - 0.5 * sensorThickness;
auto sensorPlanePos = Position(0., 0., sensorZpos) + originFront; // reference position
// miscellaneous
int imod = 0; // module number
double tBoxMax = vesselRmax1; // sensors will be tiled in tBox, within annular limits
// SENSOR MODULE LOOP ------------------------
/* cartesian tiling loop
* - start at (x=0,y=0), to center the grid
* - loop over positive-x positions; for each, place the corresponding negative-x sensor too
* - nested similar loop over y positions
*/
double sx, sy;
for (double usx = 0; usx <= tBoxMax; usx += sensorSide + sensorGap) {
for (int sgnx = 1; sgnx >= (usx > 0 ? -1 : 1); sgnx -= 2) {
for (double usy = 0; usy <= tBoxMax; usy += sensorSide + sensorGap) {
for (int sgny = 1; sgny >= (usy > 0 ? -1 : 1); sgny -= 2) {
// sensor (x,y) center
sx = sgnx * usx;
sy = sgny * usy;
// annular cut
if (std::hypot(sx, sy) < sensorPlaneRmin || std::hypot(sx, sy) > sensorPlaneRmax)
continue;
// placement (note: transformations are in reverse order)
auto sensorPV = gasvolVol.placeVolume(
sensorVol, Transform3D(Translation3D(sensorPlanePos.x(), sensorPlanePos.y(),
sensorPlanePos.z()) // move to reference position
* Translation3D(sx, sy, 0.) // move to grid position
));
// generate LUT for module number -> sensor position, for readout mapping tests
// printf("%d %f %f\n",imod,sensorPV.position().x(),sensorPV.position().y());
// properties
sensorPV.addPhysVolID("module", imod);
DetElement sensorDE(det, Form("sensor_de_%d", imod), imod);
sensorDE.setPlacement(sensorPV);
if (!debug_optics) {
SkinSurface sensorSkin(desc, sensorDE, "sensor_optical_surface", sensorSurf,
sensorVol); // TODO: 3rd arg needs `imod`?
sensorSkin.isValid();
};
// increment sensor module number
imod++;
};
};
};
};
// END SENSOR MODULE LOOP ------------------------
//
// Add service material if desired
if (detElem.child("sensors").hasChild("services")) {
xml_comp_t x_service = detElem.child("sensors").child(_Unicode(services));
Assembly service_vol("services");
service_vol.setVisAttributes(desc, x_service.visStr());
// Compute service total thickness from components
double total_thickness = 0;
xml_coll_t ci(x_service, _Unicode(component));
for (ci.reset(), total_thickness = 0.0; ci; ++ci) {
total_thickness += xml_comp_t(ci).thickness();
}
int ncomponents = 0;
double thickness_sum = -total_thickness / 2.0;
for (xml_coll_t ci(x_service, _Unicode(component)); ci; ++ci, ncomponents++) {
xml_comp_t x_comp = ci;
double thickness = x_comp.thickness();
Tube c_tube{sensorPlaneRmin, sensorPlaneRmax, thickness/2};
Volume c_vol{_toString(ncomponents, "component%d"), c_tube, desc.material(x_comp.materialStr())};
c_vol.setVisAttributes(desc, x_comp.visStr());
service_vol.placeVolume(c_vol, Position(0, 0, thickness_sum + thickness / 2.0));
thickness_sum += thickness;
}
gasvolVol.placeVolume(service_vol,
Transform3D(Translation3D(sensorPlanePos.x(), sensorPlanePos.y(),
sensorPlanePos.z() - sensorThickness - total_thickness)));
}
// place gas volume
PlacedVolume gasvolPV = vesselVol.placeVolume(gasvolVol, Position(0, 0, 0));
DetElement gasvolDE(det, "gasvol_de", 0);
gasvolDE.setPlacement(gasvolPV);
// place mother volume (vessel)
Volume motherVol = desc.pickMotherVolume(det);
PlacedVolume vesselPV = motherVol.placeVolume(vesselVol, Position(0, 0, vesselZmin) - originFront);
vesselPV.addPhysVolID("system", detID);
det.setPlacement(vesselPV);
return det;
};
// clang-format off
DECLARE_DETELEMENT(athena_PFRICH, createDetector)
......@@ -11,7 +11,7 @@
//
//==========================================================================
//
// Modified for TOPSiDE detector
// Modified for ATHENA detector
//
//==========================================================================
#include "DD4hep/DetFactoryHelper.h"
......@@ -32,7 +32,7 @@ static Ref_t create_detector(Detector& description, xml_h e, SensitiveDetector s
int numsides = dim.numsides();
xml::Component pos = x_det.position();
double rmin = dim.rmin();
double rmax = dim.rmax() * std::cos(M_PI / numsides);
double rmax = dim.rmax();
double zmin = dim.zmin();
Layering layering(x_det);
double totalThickness = layering.totalThickness();
......@@ -129,3 +129,4 @@ static Ref_t create_detector(Detector& description, xml_h e, SensitiveDetector s
// clang-format off
DECLARE_DETELEMENT(athena_PolyhedraEndcapCalorimeter2, create_detector)
DECLARE_DETELEMENT(athena_PolyhedraEndcapCalorimeter, create_detector)
//==========================================================================
// Scintillating fiber calorimeter with tower shape blocks
// reference: https://github.com/adamjaro/lmon/blob/master/calo/src/WScFiZXv3.cxx
// Support disk placement
//--------------------------------------------------------------------------
// Author: Chao Peng (ANL)
// Date: 07/19/2021
//==========================================================================
#include "GeometryHelpers.h"
#include "DD4hep/DetFactoryHelper.h"
#include <XML/Helper.h>
#include <iostream>
#include <algorithm>
#include <tuple>
#include <math.h>
using namespace dd4hep;
using Point = ROOT::Math::XYPoint;
std::tuple<Volume, Position> build_module(const Detector &desc, const xml::Component &mod_x, SensitiveDetector &sens);
// helper function to get x, y, z if defined in a xml component
template<class XmlComp>
Position get_xml_xyz(const XmlComp &comp, dd4hep::xml::Strng_t name)
{
Position pos(0., 0., 0.);
if (comp.hasChild(name)) {
auto child = comp.child(name);
pos.SetX(dd4hep::getAttrOrDefault<double>(child, _Unicode(x), 0.));
pos.SetY(dd4hep::getAttrOrDefault<double>(child, _Unicode(y), 0.));
pos.SetZ(dd4hep::getAttrOrDefault<double>(child, _Unicode(z), 0.));
}
return pos;
}
// main
static Ref_t create_detector(Detector& desc, xml::Handle_t handle, SensitiveDetector sens)
{
xml::DetElement detElem = handle;
std::string detName = detElem.nameStr();
int detID = detElem.id();
DetElement det(detName, detID);
sens.setType("calorimeter");
auto dim = detElem.dimensions();
auto rmin = dim.rmin();
auto rmax = dim.rmax();
auto length = dim.length();
auto phimin = dd4hep::getAttrOrDefault<double>(dim, _Unicode(phimin), 0.);
auto phimax = dd4hep::getAttrOrDefault<double>(dim, _Unicode(phimax), 2.*M_PI);
// envelope
Tube envShape(rmin, rmax, length/2., phimin, phimax);
Volume env(detName + "_envelope", envShape, desc.material("Air"));
env.setVisAttributes(desc.visAttributes(detElem.visStr()));
// build module
auto [modVol, modSize] = build_module(desc, detElem.child(_Unicode(module)), sens);
double modSizeR = std::sqrt(modSize.x() * modSize.x() + modSize.y() * modSize.y());
double assembly_rwidth = modSizeR*2.;
int nas = int((rmax - rmin) / assembly_rwidth) + 1;
std::vector<Assembly> assemblies;
// calorimeter block z-offsets (as blocks are shorter than the volume length)
const double block_offset = -0.5*(length - modSize.z());
for (int i = 0; i < nas; ++i) {
Assembly assembly(detName + Form("_ring%d", i + 1));
auto assemblyPV = env.placeVolume(assembly, Position{0., 0., block_offset});
assemblyPV.addPhysVolID("ring", i + 1);
assemblies.emplace_back(std::move(assembly));
}
// std::cout << assemblies.size() << std::endl;
int modid = 1;
for (int ix = 0; ix < int(2.*rmax / modSize.x()) + 1; ++ix) {
double mx = modSize.x() * ix - rmax;
for (int iy = 0; iy < int(2.*rmax / modSize.y()) + 1; ++iy) {
double my = modSize.y() * iy - rmax;
double mr = std::sqrt(mx*mx + my*my);
if (mr - modSizeR >= rmin && mr + modSizeR <= rmax) {
int ias = int((mr - rmin) / assembly_rwidth);
auto &assembly = assemblies[ias];
auto modPV = assembly.placeVolume(modVol, Position(mx, my, 0.));
modPV.addPhysVolID("module", modid++);
}
}
}
desc.add(Constant(detName + "_NModules", std::to_string(modid - 1)));
for (auto &assembly : assemblies) {
assembly.ptr()->Voxelize("");
}
// detector position and rotation
auto pos = get_xml_xyz(detElem, _Unicode(position));
auto rot = get_xml_xyz(detElem, _Unicode(rotation));
Volume motherVol = desc.pickMotherVolume(det);
Transform3D tr = Translation3D(pos.x(), pos.y(), pos.z()) * RotationZYX(rot.z(), rot.y(), rot.x());
PlacedVolume envPV = motherVol.placeVolume(env, tr);
envPV.addPhysVolID("system", detID);
det.setPlacement(envPV);
return det;
}
// helper function to build module with scintillating fibers
std::tuple<Volume, Position> build_module(const Detector &desc, const xml::Component &mod_x, SensitiveDetector &sens)
{
auto sx = mod_x.attr<double>(_Unicode(sizex));
auto sy = mod_x.attr<double>(_Unicode(sizey));
auto sz = mod_x.attr<double>(_Unicode(sizez));
Box modShape(sx/2., sy/2., sz/2.);
auto modMat = desc.material(mod_x.attr<std::string>(_Unicode(material)));
Volume modVol("module_vol", modShape, modMat);
if (mod_x.hasAttr(_Unicode(vis))) {
modVol.setVisAttributes(desc.visAttributes(mod_x.attr<std::string>(_Unicode(vis))));
}
if (mod_x.hasChild("fiber")) {
auto fiber_x = mod_x.child(_Unicode(fiber));
auto fr = fiber_x.attr<double>(_Unicode(radius));
auto fsx = fiber_x.attr<double>(_Unicode(spacex));
auto fsy = fiber_x.attr<double>(_Unicode(spacey));
auto foff = dd4hep::getAttrOrDefault<double>(fiber_x, _Unicode(offset), 0.5*mm);
auto fiberMat = desc.material(fiber_x.attr<std::string>(_Unicode(material)));
Tube fiberShape(0., fr, sz/2.);
Volume fiberVol("fiber_vol", fiberShape, fiberMat);
fiberVol.setSensitiveDetector(sens);
// Fibers are placed in a honeycomb with the radius = sqrt(3)/2. * hexagon side length
// So each fiber is fully contained in a regular hexagon, which are placed as
// ______________________________________
// | ____ ____ |
// even: | / \ / \ |
// | ____/ \____/ \____ |
// | / \ / \ / \ |
// odd: | / \____/ \____/ \ |
// | \ / \ / \ / |
// | \____/ \____/ \____/ |
// even: | \ / \ / |
// | \____/ \____/ ___|___
// |____________________________________|___offset
// | |
// |offset
// the parameters space x and space y are used to add additional spaces between the hexagons
double fside = 2. / std::sqrt(3.) * fr;
double fdistx = 2. * fside + fsx;
double fdisty = 2. * fr + fsy;
// maximum numbers of the fibers, help narrow the loop range
int nx = int(sx / (2.*fr)) + 1;
int ny = int(sy / (2.*fr)) + 1;
// std::cout << sx << ", " << sy << ", " << fr << ", " << nx << ", " << ny << std::endl;
// place the fibers
double y0 = (foff + fside);
int nfibers = 0;
for (int iy = 0; iy < ny; ++iy) {
double y = y0 + fdisty * iy;
// about to touch the boundary
if ((sy - y) < y0) { break; }
double x0 = (iy % 2) ? (foff + fside) : (foff + fside + fdistx / 2.);
for (int ix = 0; ix < nx; ++ix) {
double x = x0 + fdistx * ix;
// about to touch the boundary
if ((sx - x) < x0) { break; }
auto fiberPV = modVol.placeVolume(fiberVol, nfibers++, Position{x - sx/2., y - sy/2., 0});
//std::cout << "(" << ix << ", " << iy << ", " << x - sx/2. << ", " << y - sy/2. << ", " << fr << "),\n";
fiberPV.addPhysVolID("fiber_x", ix + 1).addPhysVolID("fiber_y", iy + 1);
}
}
// if no fibers we make the module itself sensitive
} else {
modVol.setSensitiveDetector(sens);
}
return std::make_tuple(modVol, Position{sx, sy, sz});
}
DECLARE_DETELEMENT(ScFiCalorimeter, create_detector)
......@@ -15,14 +15,20 @@
//
//==========================================================================
#include "DD4hep/DetFactoryHelper.h"
#if defined(USE_ACTSDD4HEP)
#include "ActsDD4hep/ActsExtension.hpp"
#include "ActsDD4hep/ConvertMaterial.hpp"
#else
#include "Acts/Plugins/DD4hep/ActsExtension.hpp"
#include "Acts/Plugins/DD4hep/ConvertDD4hepMaterial.hpp"
#endif
using namespace std;
using namespace dd4hep;
using namespace dd4hep::detail;
static Ref_t create_detector(Detector& description, xml_h e, SensitiveDetector sens)
static Ref_t SimpleDiskDetector_create_detector(Detector& description, xml_h e, SensitiveDetector sens)
{
xml_det_t x_det = e;
Material air = description.air();
......@@ -126,6 +132,5 @@ static Ref_t create_detector(Detector& description, xml_h e, SensitiveDetector s
return sdet;
}
DECLARE_DETELEMENT(athena_SimpleDiskTracker, create_detector)
DECLARE_DETELEMENT(ref_DiskTracker, create_detector)
DECLARE_DETELEMENT(ref_SolenoidEndcap, create_detector)
DECLARE_DETELEMENT(ref_SolenoidEndcap, SimpleDiskDetector_create_detector)
DECLARE_DETELEMENT(athena_SolenoidEndcap, SimpleDiskDetector_create_detector)
//==========================================================================
// AIDA Detector description implementation
//--------------------------------------------------------------------------
// Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
// All rights reserved.
//
// For the licensing terms see $DD4hepINSTALL/LICENSE.
// For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
//
// Author : M.Frank
// Modified : W.Armstrong
//
//==========================================================================
//
// Specialized generic detector constructor
//
//==========================================================================
#include "DD4hep/DetFactoryHelper.h"
using namespace std;
using namespace dd4hep;
using namespace dd4hep::detail;
static Ref_t create_detector(Detector& description, xml_h e, SensitiveDetector sens) {
xml_det_t x_det = e;
Material air = description.air();
string det_name = x_det.nameStr();
bool reflect = x_det.reflect();
DetElement sdet(det_name,x_det.id());
Assembly assembly(det_name);
PlacedVolume pv;
int l_num = 0;
xml::Component pos = x_det.position();
for(xml_coll_t i(x_det,_U(layer)); i; ++i, ++l_num) {
xml_comp_t x_layer = i;
string l_nam = det_name + _toString(l_num, "_layer%d");
double x_lay = x_layer.x();
double y_lay = x_layer.y();
double z = 0;
double zmin = 0;
double layerWidth = 0.;
int s_num = 0;
for(xml_coll_t j(x_layer,_U(slice)); j; ++j) {
double thickness = xml_comp_t(j).thickness();
layerWidth += thickness;
}
Box l_box(x_lay/2.0, y_lay/2.0, layerWidth/2.0 );
Volume l_vol(l_nam, l_box, air);
l_vol.setVisAttributes(description, x_layer.visStr());
for (xml_coll_t j(x_layer, _U(slice)); j; ++j, ++s_num) {
xml_comp_t x_slice = j;
double thick = x_slice.thickness();
Material mat = description.material(x_slice.materialStr());
string s_nam = l_nam + _toString(s_num, "_slice%d");
Volume s_vol(s_nam, Box(x_lay/2.0, y_lay/2.0, thick/2.0), mat);
if (x_slice.isSensitive()) {
sens.setType("tracker");
s_vol.setSensitiveDetector(sens);
}
s_vol.setAttributes(description, x_slice.regionStr(), x_slice.limitsStr(), x_slice.visStr());
pv = l_vol.placeVolume(s_vol, Position(0, 0, z - zmin - layerWidth / 2 + thick / 2));
pv.addPhysVolID("slice", s_num);
}
DetElement layer(sdet,l_nam+"_pos",l_num);
pv = assembly.placeVolume(l_vol,Position(0,0,zmin+layerWidth/2.));
pv.addPhysVolID("layer",l_num);
pv.addPhysVolID("barrel",1);
layer.setPlacement(pv);
if ( reflect ) {
pv = assembly.placeVolume(l_vol,Transform3D(RotationY(M_PI),Position(0,0,-zmin-layerWidth/2)));
pv.addPhysVolID("layer",l_num);
pv.addPhysVolID("barrel",2);
DetElement layerR = layer.clone(l_nam+"_neg");
sdet.add(layerR.setPlacement(pv));
}
}
if ( x_det.hasAttr(_U(combineHits)) ) {
sdet.setCombineHits(x_det.attr<bool>(_U(combineHits)),sens);
}
pv = description.pickMotherVolume(sdet).placeVolume(assembly,Position(pos.x(),pos.y(),pos.z()));
pv.addPhysVolID("system", x_det.id()); // Set the subdetector system ID.
sdet.setPlacement(pv);
return sdet;
}
DECLARE_DETELEMENT(ref_RectangularTracker,create_detector)
/** \addtogroup Trackers Trackers
* \brief Type: **BarrelTrackerWithFrame**.
* \author W. Armstrong
*
* \ingroup trackers
*
* @{
*/
#include <array>
#include <map>
#include "DD4hep/DetFactoryHelper.h"
#include "Acts/Plugins/DD4hep/ActsExtension.hpp"
#include "Acts/Definitions/Units.hpp"
#include "DD4hep/Printout.h"
#include "DD4hep/Shapes.h"
#include "DDRec/Surface.h"
#include "DDRec/DetectorData.h"
#include "XML/Utilities.h"
#include "XML/Layering.h"
#if defined(USE_ACTSDD4HEP)
#include "ActsDD4hep/ActsExtension.hpp"
#include "ActsDD4hep/ConvertMaterial.hpp"
#else
#include "Acts/Plugins/DD4hep/ActsExtension.hpp"
#include "Acts/Plugins/DD4hep/ConvertDD4hepMaterial.hpp"
#endif
using namespace std;
using namespace dd4hep;
using namespace dd4hep::rec;
using namespace dd4hep::detail;
/*! Endcap Trapezoidal Tracker.
/** Endcap Trapezoidal Tracker.
*
* @author Whitney Armstrong
*
......@@ -24,19 +45,32 @@ static Ref_t create_detector(Detector& description, xml_h e, SensitiveDetector s
DetElement sdet(det_name, det_id);
Assembly assembly(det_name);
Material air = description.material("Air");
// Volume assembly (det_name,Box(10000,10000,10000),vacuum);
Volume motherVol = description.pickMotherVolume(sdet);
int m_id = 0, c_id = 0, n_sensor = 0;
map<string, Volume> modules;
map<string, Placements> sensitives;
map<string, std::vector<VolPlane>> volplane_surfaces;
map<string, std::array<double, 2>> module_thicknesses;
PlacedVolume pv;
Acts::ActsExtension* detWorldExt = new Acts::ActsExtension();
detWorldExt->addType("endcap", "detector");
sdet.addExtension<Acts::ActsExtension>(detWorldExt);
// ACTS extension
{
Acts::ActsExtension* detWorldExt = new Acts::ActsExtension();
detWorldExt->addType("endcap", "detector");
// SJJ probably need to set the envelope here, as ACTS can't figure
// that out for Assembly volumes. May also need binning to properly pick up
// on the support material @TODO
//
// Add the volume boundary material if configured
for (xml_coll_t bmat(x_det, _Unicode(boundary_material)); bmat; ++bmat) {
xml_comp_t x_boundary_material = bmat;
Acts::xmlToProtoSurfaceMaterial(x_boundary_material, *detWorldExt, "boundary_material");
}
sdet.addExtension<Acts::ActsExtension>(detWorldExt);
}
assembly.setVisAttributes(description.invisible());
sens.setType("tracker");
......@@ -88,12 +122,15 @@ static Ref_t create_detector(Detector& description, xml_h e, SensitiveDetector s
double x1 = trd.x1();
double x2 = trd.x2();
double z = trd.z();
double y1, y2, total_thickness = 0.;
double total_thickness = 0.;
xml_coll_t ci(x_mod, _U(module_component));
for (ci.reset(), total_thickness = 0.0; ci; ++ci)
total_thickness += xml_comp_t(ci).thickness();
y1 = y2 = total_thickness / 2;
double thickness_so_far = 0.0;
double thickness_sum = -total_thickness / 2.0;
double y1 = total_thickness / 2;
double y2 = total_thickness / 2;
Trapezoid m_solid(x1, x2, y1, y2, z);
Volume m_volume(m_nam, m_solid, vacuum);
m_volume.setVisAttributes(description.visAttributes(x_mod.visStr()));
......@@ -142,6 +179,7 @@ static Ref_t create_detector(Detector& description, xml_h e, SensitiveDetector s
c_vol.setVisAttributes(description.visAttributes(c.visStr()));
pv = m_volume.placeVolume(c_vol, Position(0, posY + c_thick / 2, 0));
if (c.isSensitive()) {
module_thicknesses[m_nam] = {thickness_so_far + c_thick/2.0, total_thickness-thickness_so_far - c_thick/2.0};
//std::cout << " adding sensitive volume" << c_name << "\n";
sdet.check(n_sensor > 2, "SiTrackerEndcap2::fromCompact: " + c_name + " Max of 2 modules allowed!");
pv.addPhysVolID("sensor", n_sensor);
......@@ -149,8 +187,30 @@ static Ref_t create_detector(Detector& description, xml_h e, SensitiveDetector s
c_vol.setSensitiveDetector(sens);
sensitives[m_nam].push_back(pv);
++n_sensor;
// -------- create a measurement plane for the tracking surface attched to the sensitive volume -----
Vector3D u(0., 0., -1.);
Vector3D v(-1., 0., 0.);
Vector3D n(0., 1., 0.);
// Vector3D o( 0. , 0. , 0. ) ;
// compute the inner and outer thicknesses that need to be assigned to the tracking surface
// depending on wether the support is above or below the sensor
double inner_thickness = module_thicknesses[m_nam][0];
double outer_thickness = module_thicknesses[m_nam][1];
SurfaceType type(SurfaceType::Sensitive);
// if( isStripDetector )
// type.setProperty( SurfaceType::Measurement1D , true ) ;
VolPlane surf(c_vol, type, inner_thickness, outer_thickness, u, v, n); //,o ) ;
volplane_surfaces[m_nam].push_back(surf);
//--------------------------------------------
}
posY += c_thick;
thickness_sum += c_thick;
thickness_so_far += c_thick;
}
modules[m_nam] = m_volume;
}
......@@ -182,20 +242,25 @@ static Ref_t create_detector(Detector& description, xml_h e, SensitiveDetector s
if (reflect) {
layer_pv =
assembly.placeVolume(layer_vol, Transform3D(RotationZYX(0.0, -M_PI, 0.0), Position(0, 0, -layer_center_z)));
layer_pv.addPhysVolID("barrel", 3).addPhysVolID("layer", l_id);
layer_pv.addPhysVolID("layer", l_id);
layer_name += "_N";
} else {
layer_pv = assembly.placeVolume(layer_vol, Position(0, 0, layer_center_z));
layer_pv.addPhysVolID("barrel", 2).addPhysVolID("layer", l_id);
layer_pv.addPhysVolID("layer", l_id);
layer_name += "_P";
}
DetElement layer_element(sdet, layer_name, l_id);
layer_element.setPlacement(layer_pv);
Acts::ActsExtension* layerExtension = new Acts::ActsExtension();
layerExtension->addType("layer", "layer");
layerExtension->addType("sensitive disk", "layer");
//layerExtension->addType("axes", "definitions", "XZY");
//layerExtension->addType("sensitive disk", "layer");
//layerExtension->addType("axes", "definitions", "XZY");
for (xml_coll_t lmat(x_layer, _Unicode(layer_material)); lmat; ++lmat) {
xml_comp_t x_layer_material = lmat;
xmlToProtoSurfaceMaterial(x_layer_material, *layerExtension, "layer_material");
}
layer_element.addExtension<Acts::ActsExtension>(layerExtension);
for (xml_coll_t ri(x_layer, _U(ring)); ri; ++ri) {
......@@ -220,7 +285,7 @@ static Ref_t create_detector(Detector& description, xml_h e, SensitiveDetector s
DetElement module(layer_element, m_base + "_pos", det_id);
pv = layer_vol.placeVolume(
m_vol, Transform3D(RotationZYX(0, -M_PI / 2 - phi, -M_PI / 2), Position(x, y, zstart + dz)));
pv.addPhysVolID("barrel", 1).addPhysVolID("layer", l_id).addPhysVolID("module", mod_num);
pv.addPhysVolID("module", mod_num);
module.setPlacement(pv);
for (size_t ic = 0; ic < sensVols.size(); ++ic) {
PlacedVolume sens_pv = sensVols[ic];
......@@ -229,11 +294,12 @@ static Ref_t create_detector(Detector& description, xml_h e, SensitiveDetector s
//std::cout << " adding ACTS extension" << "\n";
Acts::ActsExtension* moduleExtension = new Acts::ActsExtension("XZY");
comp_elt.addExtension<Acts::ActsExtension>(moduleExtension);
volSurfaceList(comp_elt)->push_back(volplane_surfaces[m_nam][ic]);
}
} else {
pv = layer_vol.placeVolume(
m_vol, Transform3D(RotationZYX(0, -M_PI / 2 - phi, -M_PI / 2), Position(x, y, -zstart - dz)));
pv.addPhysVolID("barrel", 2).addPhysVolID("layer", l_id).addPhysVolID("module", mod_num);
pv.addPhysVolID("module", mod_num);
DetElement r_module(layer_element, m_base + "_neg", det_id);
r_module.setPlacement(pv);
for (size_t ic = 0; ic < sensVols.size(); ++ic) {
......@@ -243,6 +309,7 @@ static Ref_t create_detector(Detector& description, xml_h e, SensitiveDetector s
//std::cout << " adding ACTS extension" << "\n";
Acts::ActsExtension* moduleExtension = new Acts::ActsExtension("XZY");
comp_elt.addExtension<Acts::ActsExtension>(moduleExtension);
volSurfaceList(comp_elt)->push_back(volplane_surfaces[m_nam][ic]);
}
}
dz = -dz;
......@@ -257,6 +324,7 @@ static Ref_t create_detector(Detector& description, xml_h e, SensitiveDetector s
return sdet;
}
//@}
// clang-format off
DECLARE_DETELEMENT(athena_TrapEndcapTracker, create_detector)
DECLARE_DETELEMENT(athena_GEMTrackerEndcap, create_detector)