-
Notifications
You must be signed in to change notification settings - Fork 99
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add data extension class for FCCee Drift Chamber
- Loading branch information
1 parent
58fa8c0
commit 20db18f
Showing
1 changed file
with
348 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,348 @@ | ||
#ifndef DCH_INFO_H_INCLUDED | ||
#define DCH_INFO_H_INCLUDED | ||
|
||
#include "TMath.h" | ||
|
||
#include "DDRec/DetectorData.h" | ||
#include <map> | ||
|
||
namespace dd4hep { namespace rec { | ||
|
||
|
||
/* Data structure to store DCH geometry parameters | ||
* | ||
* Global parameters are members of this class | ||
* | ||
* Parameters of each layer are stored in the helper | ||
* class DCH_info_layer. | ||
* | ||
* The member database is a container which stores one | ||
* DCH_info_layer object per layer. | ||
* | ||
* To use this class, instantiate an object and define the global parameters. | ||
* Then call the method BuildLayerDatabase, which calculates | ||
* the parameters of each layer based on the global parameters. | ||
* | ||
* @author A. Tolosa Delgado, CERN | ||
* @date April 2024 | ||
* @version Drift chamber v2 | ||
* | ||
*/ | ||
struct DCH_info_struct | ||
{ | ||
public: | ||
// use alias of types to show more clearly what the variable is | ||
// if everything is double, the code is not readable | ||
/// type for layer number | ||
using DCH_layer = int; | ||
/// tpye for lengths | ||
using DCH_length_t = double; | ||
/// tpye for angles | ||
using DCH_angle_t = double; | ||
/// Half length of the active volume | ||
DCH_length_t Lhalf = {0}; | ||
/// Inner radius of the active volume | ||
DCH_length_t rin = {0}; | ||
/// Outer radius of the active volume | ||
DCH_length_t rout = {0}; | ||
|
||
/// Inner guard wires radius | ||
DCH_length_t guard_inner_r_at_z0 = {0}; | ||
/// Outer guard wires radius | ||
DCH_length_t guard_outer_r_at_zL2 = {0}; | ||
|
||
/// number of cells of first layer | ||
int ncell0 = {0}; | ||
/// increment the number of cells for each superlayer as: | ||
/// ncells(ilayer) = dch_ncell0 + increment*superlayer(ilayer) | ||
/// See DCH_info::Get_nsuperlayer_minus_1(ilayer) | ||
int ncell_increment = {0}; | ||
|
||
/// cells within the same layer may be grouped into sectors, not in use atm | ||
int ncell_per_sector = {0}; | ||
|
||
/// input number of layers in each superlayer | ||
DCH_layer nlayersPerSuperlayer = {0}; | ||
/// input number of superlayers | ||
/// superlayer is an abstract level of grouping layers used to | ||
/// parametrize the increment of cells in each layer | ||
DCH_layer nsuperlayers = {0}; | ||
/// Calculated as dch_nlayersPerSuperlayer * dch_nsuperlayers | ||
DCH_layer nlayers = {0}; | ||
|
||
/// global twist angle | ||
/// alternating layers will change its sign | ||
DCH_angle_t twist_angle = {0}; | ||
|
||
/// Cell width for the first layer | ||
double first_width = {0}; | ||
/// Cell radius for the first layer | ||
DCH_length_t first_sense_r = {0}; | ||
|
||
void Set_lhalf(DCH_length_t _dch_Lhalf){Lhalf=_dch_Lhalf;} | ||
void Set_rin (DCH_length_t _dch_rin ){rin = _dch_rin; } | ||
void Set_rout (DCH_length_t _dch_rout ){rout = _dch_rout;} | ||
|
||
void Set_guard_rin_at_z0 (DCH_length_t _dch_rin_z0_guard ){guard_inner_r_at_z0 = _dch_rin_z0_guard; } | ||
void Set_guard_rout_at_zL2(DCH_length_t _dch_rout_zL2_guard){guard_outer_r_at_zL2 = _dch_rout_zL2_guard; } | ||
|
||
void Set_ncell0 (int _ncell0 ){ncell0 = _ncell0; } | ||
void Set_ncell_increment (int _ncell_increment ){ncell_increment = _ncell_increment; } | ||
|
||
void Set_nlayersPerSuperlayer(int _nlayersPerSuperlayer){nlayersPerSuperlayer = _nlayersPerSuperlayer;} | ||
void Set_nsuperlayers (int _nsuperlayers ){nsuperlayers = _nsuperlayers; } | ||
|
||
void Set_ncell_per_sector(int _ncell_per_sector){ncell_per_sector = _ncell_per_sector;} | ||
|
||
void Set_twist_angle (DCH_length_t _dch_twist_angle ){twist_angle = _dch_twist_angle;} | ||
|
||
void Set_first_width (double _first_width ){first_width = _first_width; } | ||
void Set_first_sense_r(double _first_sense_r){first_sense_r = _first_sense_r; } | ||
|
||
|
||
/// Get number of cells in a given layer | ||
/// ncells = number of wires/2 | ||
int Get_ncells(int ilayer){return database.at(ilayer).nwires/2;} | ||
|
||
/// Get phi width for the twisted tube and the step (phi distance between cells) | ||
DCH_angle_t Get_phi_width(int ilayer){return (TMath::TwoPi()/Get_ncells(ilayer))*dd4hep::rad;} | ||
|
||
/// phi positioning, adding offset for odd ilayers | ||
/// there is a staggering in phi for alternating layers, 0.25*cell_phi_width*(ilayer%2); | ||
DCH_angle_t Get_cell_phi_angle(int ilayer, int nphi){ return (Get_phi_width(ilayer) * (nphi + 0.25*(ilayer%2)));} | ||
|
||
/// calculate superlayer for a given ilayer. | ||
/// WARNING: division of integers on purpose! | ||
int Get_nsuperlayer_minus_1(int ilayer){ return int((ilayer-1)/nlayersPerSuperlayer);} | ||
|
||
/// Calculate radius at z=L/2 given at z=0 | ||
DCH_length_t Radius_zLhalf(DCH_length_t r_z0) const { | ||
return r_z0/cos(twist_angle/2/dd4hep::rad); | ||
} | ||
|
||
/// tan(stereoangle) = R(z=0) / (L/2) * tan( twist_angle/2) | ||
DCH_angle_t stereoangle_z0(DCH_length_t r_z0) const { | ||
return atan( r_z0/Lhalf*tan(twist_angle/2)); | ||
} | ||
|
||
/// tan(stereoangle) = R(z=L/2) / (L/2) * sin( twist_angle/2) | ||
DCH_angle_t stereoangle_zLhalf(DCH_length_t r_zLhalf) const { | ||
return atan( r_zLhalf/Lhalf*sin(twist_angle/2)); | ||
} | ||
|
||
/// WireLength = 2*dch_Lhalf/cos(atan(Pitch_z0(r_z0)/(2*dch_Lhalf)))/cos(stereoangle_z0(r_z0)) | ||
DCH_length_t WireLength(int nlayer, DCH_length_t r_z0) const { | ||
auto Pitch_z0 = database.at(nlayer).Pitch_z0(r_z0); | ||
return 2*Lhalf/cos(atan(Pitch_z0/(2*Lhalf)))/cos(stereoangle_z0(r_z0)) ; | ||
}; | ||
|
||
/// Internal helper struct for defining the layer layout | ||
struct DCH_info_layer | ||
{ | ||
/// layer number | ||
DCH_layer layer = {0}; | ||
/// 2x number of cells in that layer | ||
int nwires = {0}; | ||
/// cell parameter | ||
double height_z0 = {0.}; | ||
/// cell parameter | ||
double width_z0 = {0.}; | ||
|
||
/// radius (cylindral coord) of sensitive wire | ||
DCH_length_t radius_sw_z0 = {0.}; | ||
|
||
/// radius (cylindral coord) of 'down' field wires | ||
DCH_length_t radius_fdw_z0 = {0.}; | ||
|
||
/// radius (cylindral coord) of 'up' field wires | ||
DCH_length_t radius_fuw_z0 = {0.}; | ||
|
||
/// some quantities are derived from previous-layer ones | ||
/// stereo angle is positive for odd layer number | ||
bool IsStereoPositive() const { | ||
return (1 == layer%2); | ||
} | ||
/// calculate sign based on IsStereoPositive | ||
int StereoSign() const { | ||
return (IsStereoPositive()*2 - 1); | ||
} | ||
|
||
/// separation between wires (along the circle) | ||
DCH_length_t Pitch_z0(DCH_length_t r_z0) const { | ||
return TMath::TwoPi()*r_z0/nwires; | ||
}; | ||
|
||
}; | ||
/// map to store parameters for each layer | ||
std::map<DCH_layer, DCH_info_layer> database; | ||
bool IsDatabaseEmpty() const { return database.empty(); } | ||
|
||
inline void BuildLayerDatabase(); | ||
inline void Show_DCH_info_database(std::ostream& io) const; | ||
|
||
|
||
}; | ||
typedef StructExtension<DCH_info_struct> DCH_info ; | ||
std::ostream& operator<<( std::ostream& io , const DCH_info& d ){d.Show_DCH_info_database(io); return io;} | ||
|
||
inline void DCH_info_struct::BuildLayerDatabase() | ||
{ | ||
// do not fill twice the database | ||
if( not this->IsDatabaseEmpty() ) return; | ||
|
||
auto ff_check_positive_parameter = [](double p, std::string pname) -> void { | ||
if(p<=0)throw std::runtime_error(Form("DCH: %s must be positive",pname.c_str())); | ||
return; | ||
}; | ||
ff_check_positive_parameter(this->rin ,"inner radius"); | ||
ff_check_positive_parameter(this->rout ,"outer radius"); | ||
ff_check_positive_parameter(this->Lhalf,"half length" ); | ||
|
||
ff_check_positive_parameter(this->guard_inner_r_at_z0 ,"inner radius of guard wires" ); | ||
ff_check_positive_parameter(this->guard_outer_r_at_zL2,"outer radius of guard wires" ); | ||
|
||
|
||
ff_check_positive_parameter(this->ncell0,"ncells in the first layer" ); | ||
ff_check_positive_parameter(this->ncell_increment,"ncells increment per superlayer" ); | ||
ff_check_positive_parameter(this->ncell_per_sector,"ncells per sector" ); | ||
|
||
// if dch_ncell_per_sector is not divisor of dch_ncell0 and dch_ncell_increment | ||
// throw an error | ||
if( 0 != (ncell0 % ncell_per_sector) || 0 != (ncell_increment % ncell_per_sector) ) | ||
throw std::runtime_error("dch_ncell_per_sector is not divisor of dch_ncell0 or dch_ncell_increment"); | ||
|
||
ff_check_positive_parameter(this->nsuperlayers,"number of superlayers" ); | ||
ff_check_positive_parameter(this->nlayersPerSuperlayer,"number of layers per superlayer" ); | ||
|
||
/// nlayers = nsuperlayers * nlayersPerSuperlayer | ||
/// default: 112 = 14 * 8 | ||
this->nlayers = this->nsuperlayers * this->nlayersPerSuperlayer; | ||
|
||
ff_check_positive_parameter(this->first_width,"width of first layer cells" ); | ||
ff_check_positive_parameter(this->first_sense_r,"radius of first layer cells" ); | ||
|
||
|
||
// intialize layer 1 from input parameters | ||
{ | ||
DCH_info_layer layer1_info; | ||
layer1_info.layer = 1; | ||
layer1_info.nwires = 2*this->ncell0; | ||
layer1_info.height_z0 = first_width; | ||
layer1_info.radius_sw_z0 = first_sense_r; | ||
layer1_info.radius_fdw_z0 = first_sense_r - 0.5*first_width; | ||
layer1_info.radius_fuw_z0 = first_sense_r + 0.5*first_width; | ||
layer1_info.width_z0 = TMath::TwoPi()*first_sense_r/this->ncell0; | ||
|
||
this->database.emplace(layer1_info.layer, layer1_info); | ||
} | ||
|
||
// some parameters of the following layer are calculated based on the previous ones | ||
// the rest are left as methods of DCH_info or DCH_info_layer class | ||
// loop over all layers, skipping the first one | ||
for(int ilayer = 2; ilayer<= this->nlayers; ++ilayer) | ||
{ | ||
// initialize empty object, parameters are set later | ||
DCH_info_layer layer_info; | ||
|
||
// the loop counter actually corresponds to the layer number | ||
layer_info.layer = ilayer; | ||
// nwires is twice the number of cells in this particular layer (ilayer) | ||
layer_info.nwires = 2*(this->ncell0 + this->ncell_increment*Get_nsuperlayer_minus_1(ilayer) ); | ||
|
||
// the previous layer info is needed to calculate parameters of current layer | ||
const auto& previousLayer = this->database.at(ilayer-1); | ||
|
||
//calculate height_z0, radius_sw_z0 | ||
{ | ||
double h = previousLayer.height_z0; | ||
double ru = previousLayer.radius_fuw_z0; | ||
double rd = previousLayer.radius_fdw_z0; | ||
|
||
if(0 == Get_nsuperlayer_minus_1(ilayer)) | ||
layer_info.height_z0 = h*ru/rd; | ||
else | ||
layer_info.height_z0 = TMath::TwoPi()*ru/(0.5*layer_info.nwires - TMath::Pi()); | ||
|
||
layer_info.radius_sw_z0 = 0.5*layer_info.height_z0 + ru; | ||
} | ||
|
||
//calculate radius_fdw_z0, radius_fuw_z0, width_z0 | ||
layer_info.radius_fdw_z0 = previousLayer.radius_fuw_z0; | ||
layer_info.radius_fuw_z0 = previousLayer.radius_fuw_z0 + layer_info.height_z0; | ||
layer_info.width_z0 = TMath::TwoPi()*layer_info.radius_sw_z0/(0.5*layer_info.nwires); | ||
|
||
// according to expert prescription, width_z0 == height_z0 | ||
if(fabs(layer_info.width_z0 - layer_info.height_z0)>1e-4) | ||
throw std::runtime_error("fabs(l.width_z0 - l.height_z0)>1e-4"); | ||
|
||
|
||
|
||
this->database.emplace(ilayer, layer_info); | ||
} | ||
|
||
std::cout << "\t+ Total size of DCH database = " << database.size() << std::endl; | ||
return; | ||
} | ||
|
||
inline void DCH_info_struct::Show_DCH_info_database(std::ostream & oss) const | ||
{ | ||
oss << "\n"; | ||
oss << "Global parameters of DCH:\n"; | ||
oss << "\tGas, half length/mm = " << Lhalf/dd4hep::mm << '\n'; | ||
oss << "\tGas, radius in/mm = " << rin/dd4hep::mm << '\n'; | ||
oss << "\tGas, radius out/mm = " << rout/dd4hep::mm<< '\n'; | ||
oss << "\tGuard, radius in(z=0)/mm = " << guard_inner_r_at_z0/dd4hep::mm << '\n'; | ||
oss << "\tGuard, radius out(z=L/2)/mm = " << guard_outer_r_at_zL2/dd4hep::mm << '\n'; | ||
oss << "\n"; | ||
oss << "\tTwist angle (2*alpha) / deg = " << twist_angle/dd4hep::deg << '\n'; | ||
oss << "\n"; | ||
oss << "\tN superlayers = " << nsuperlayers << '\n'; | ||
oss << "\tN layers per superlayer = " << nlayersPerSuperlayer << '\n'; | ||
oss << "\tN layers = " << nlayers << '\n'; | ||
oss << "\n"; | ||
oss << "\tN cells layer1 = " << ncell0 << '\n'; | ||
oss << "\tN cells increment per superlayer = " << ncell_increment << '\n'; | ||
oss << "\tN cells per sector = " << ncell_per_sector << '\n'; | ||
oss << "\n"; | ||
oss << "Layer parameters of DCH:\n"; | ||
oss | ||
<< "\t" << "layer" | ||
<< "\t" << "nwires" | ||
<< "\t" << "height_z0/mm" | ||
<< "\t" << "width_z0/mm" | ||
<< "\t" << "radius_fdw_z0/mm" | ||
<< "\t" << "radius_sw_z0/mm" | ||
<< "\t" << "radius_fuw_z0/mm" | ||
<< "\t" << "stereoangle_z0/deg" | ||
<< "\t" << "Pitch_z0/mm" | ||
<< "\t" << "radius_sw_zLhalf/mm" | ||
<< "\t" << "WireLength/mm" | ||
<< "\n" << std::endl; | ||
|
||
if( this->IsDatabaseEmpty() ) | ||
{ | ||
oss << "\nDatabase empty\n"; | ||
return; | ||
} | ||
|
||
for(const auto& [nlayer, l] : database ) | ||
{ | ||
oss | ||
<< "\t" << l.layer | ||
<< "\t" << l.nwires | ||
<< "\t" << l.height_z0/dd4hep::mm | ||
<< "\t" << l.width_z0/dd4hep::mm | ||
<< "\t" << l.radius_fdw_z0/dd4hep::mm | ||
<< "\t" << l.radius_sw_z0/dd4hep::mm | ||
<< "\t" << l.radius_fuw_z0/dd4hep::mm | ||
<< "\t" << l.StereoSign()*this->stereoangle_z0(l.radius_sw_z0)/dd4hep::deg | ||
<< "\t" << l.Pitch_z0(l.radius_sw_z0)/dd4hep::mm | ||
<< "\t" << this->Radius_zLhalf(l.radius_sw_z0)/dd4hep::mm | ||
<< "\t" << this->WireLength(l.layer,l.radius_sw_z0)/dd4hep::mm | ||
<< "\n" << std::endl; | ||
} | ||
return; | ||
} | ||
}} // end namespace dd4hep::rec:: | ||
|
||
#endif // DCH_INFO_H_INCLUDED |