diff --git a/benchmarks/tracking_detectors/scripts/matscan_plot.py b/benchmarks/tracking_detectors/scripts/matscan_plot.py new file mode 100644 index 0000000000000000000000000000000000000000..7cd5353dbf79aa1e9cf3c2159afb5c3980ffe210 --- /dev/null +++ b/benchmarks/tracking_detectors/scripts/matscan_plot.py @@ -0,0 +1,218 @@ +## plot test_matscan.cxx results +## Shujie Li, 08, 2021 +## usage: python matscan_plot.py zrange rrange +import pandas as pd +import matplotlib.pyplot as plt +import numpy as np +import math as m +import sys +from matplotlib import colors +from scipy import stats +from matplotlib.colors import LogNorm + + +plt.rcParams['figure.figsize'] = [10.0, 6.0] +# plt.rcParams['fure.dpi'] = 80 + +SMALL_SIZE = 14 +MEDIUM_SIZE = 16 +BIGGER_SIZE = 18 + +plt.rc('font', size=SMALL_SIZE) # controls default text sizes +plt.rc('axes', titlesize=SMALL_SIZE) # fontsize of the axes title +plt.rc('axes', labelsize=MEDIUM_SIZE) # fontsize of the x and y labels +plt.rc('xtick', labelsize=SMALL_SIZE) # fontsize of the tick labels +plt.rc('ytick', labelsize=SMALL_SIZE) # fontsize of the tick labels +plt.rc('legend', fontsize=SMALL_SIZE) # legend fontsize +plt.rc('figure', titlesize=BIGGER_SIZE) # fontsize of the figure title] + + +def cart2sph(x,y,z): + x=np.array(x) + y=np.array(y) + z=np.array(z) + XsqPlusYsq = x**2 + y**2 + r = np.sqrt(XsqPlusYsq + z**2) # r + elev = np.arctan2(np.sqrt(XsqPlusYsq),z) # theta (polar to z axis) + az = np.arctan2(y,x) # phi (azimuthal) + return r, elev, az + +## read a single scan table for cylinder volume +def read_table_cylind(zmax=140,rmax=60,indir='./'): + header = np.array(['x', 'y', 'z', 'ind', 'material', 'Z', 'density', 'rad_length', 'thickness', 'path_length', 'sum_x0',"end_x","end_y","end_z"]) + df = pd.read_csv(indir+"all_z%g_r%g.dat" %(zmax,rmax),names=header,delim_whitespace=True) + df["x0"]=np.array(df["thickness"])/np.array(df["rad_length"]) + df=df.reset_index(drop=True).drop_duplicates() + + # calcualte polar angle then eta + ax = df["x"] + ay = df["y"] + az = df["z"] + r,polar,azi=cart2sph(ax,ay,az) + eta = -np.log(np.tan(polar/2.0)) + df["eta"] = eta + df["azi_angle"] = azi + df["pol_angle"] = polar + + print(df.head()) + print(df.material.unique()) + +# df=df[np.isfinite(df).all(1)] #drop in + return df + +def add_kin(df): + # calcualte polar angle then eta + ax = df["x"] + ay = df["y"] + az = df["z"] + r,polar,azi=cart2sph(ax,ay,az) + eta = -np.log(np.tan(polar/2.0)) + df["eta"] = eta + df["azi_angle"] = azi + df["pol_angle"] = polar + return df + +## group by xyz coordinate to get sum x0 per track with selected materials +def get_x0(df0,mat_name=""): + if len(mat_name)>0: + df=df0[df0["material"]==mat_name] + else: + df=df0 + # df=df[df["material"]!="CarbonFiber_25percent"] + dfg=df.groupby(["x","y","z"])["x0"].sum() + dfg=dfg.reset_index() + dfg=dfg[np.isfinite(dfg).all(1)] #drop inf + dfg=add_kin(dfg) + return dfg + + +## plot mean,min,max X0 of each eta bin +def plot_1d(dfgz,mat_name="",ax="eta"): + xlow = 0 + values=np.array(dfgz["x0"])*100 + xx = np.array(dfgz[ax]) + if ax=="eta": + xhi=3.5 + xlow = -xhi + elif ax=="pol_angle": + xhi=90 + xx=180/2-xx*180/np.pi + elif ax=="z": # z projected at barrel radius + xhi=140 + xx*=(43.23/60) + ## with bin stats + nbin = 50 + x0mean,binedge,binnumber=stats.binned_statistic(xx,values, 'mean', bins=nbin,range=[xlow,xhi]) + ## !!! possible bugs in this error bar calculation + x0max ,binedge,binnumber=stats.binned_statistic(xx,values, 'max', bins=nbin,range=[xlow,xhi]) + x0min ,binedge,binnumber=stats.binned_statistic(xx,values, 'min', bins=nbin,range=[xlow,xhi]) + + bin_center = (binedge[0:-1]+binedge[1:])/2 + plt.plot(bin_center,x0mean,label=mat_name) + plt.fill_between(bin_center,x0min,x0max,alpha=0.2) + plt.xlim(xlow,xhi) + plt.grid() + plt.suptitle("total X0") + plt.xlabel(ax) + + return bin_center, x0mean, x0max, x0min + + + +if __name__ == "__main__": + + ## corresponding to the scan output filenmae from test_matscan.cxx, in cm + zrange = int(sys.argv[1]) + rrange = int(sys.argv[2]) + outdir = './' + indir = './' + cols = np.array(["x","y","z","material","thickness","path_length"]) + df = read_table_cylind(zrange,rrange,indir) + + + ## -----------------plot side view of geometry, z v.s. R------------- + plt.figure() + xe = df["end_x"] + ye = df["end_y"] + ze = df["end_z"] + re = np.sqrt(xe**2+ye**2) + # c5=(df["end_x"]**2+df["end_y"]**2)<11**2 + # c6=df["material"]=="Aluminum" + plt.scatter(ze,re,s=0.001,marker=".") + + # plt.xlabel("$\eta$") + plt.xlabel("z [cm]") + plt.ylabel("R [cm]") + # plt.xlim(0,4) + plt.grid() + plt.savefig(outdir+"/matscan_geo_z_z%g_r%g.png" %(zrange,rrange)) + + ## -----------------plot side view of geometry, eta v.s. R------------- + plt.figure() + xe = df["end_x"] + ye = df["end_y"] + ze = df["end_z"] + re = np.sqrt(xe**2+ye**2) + plt.scatter(df["eta"],re,s=0.001,marker=".") + + plt.xlabel("$\eta$") + plt.ylabel("R [cm]") + # plt.xlim(0,4) + plt.grid() + plt.savefig(outdir+"/matscan_geo_eta_z%g_r%g.png" %(zrange,rrange)) + + + ## -----------------plot 1 d matscan z v.s. X0------------- + df_all=get_x0(df) + plt.figure() + ax="eta" + print(df.columns,df_all.columns) + print(df_all['eta']) + _=plot_1d(df_all,"Total",ax) + + mat_list=np.delete(df.material.unique(),0) + # mat_list.drop("Vacuum") + # mat_list=['NONE','Air','Beryllium','Aluminum','Silicon','CarbonFiber'] + # mat_list=["Aluminum","Silicon","CarbonFiber"] + for mat_name in mat_list: + df_mat = get_x0(df,mat_name) + _=plot_1d(df_mat,mat_name,ax) + + plt.legend(loc="upper right") + plt.xlabel("$\eta$") + plt.ylabel("X/X0 (%)") + plt.savefig(outdir+"/matscan_1d_z%g_r%g.png" %(zrange,rrange)) + + ## -----------------plot 2 d matscan z v.s. phi------------- + + dfgz=df_all + values=dfgz["x0"] + xx = dfgz["eta"] + yy = dfgz["azi_angle"] # in rad + ## with bin stats + nbin = 50 + x0mean_2d,x_edge,y_edge,binnumber_2d=stats.binned_statistic_2d(xx,yy, values, 'mean', bins=[nbin,nbin],range=[[-5,5],[-5,5]]) + # x0max_2d ,_,_,_=stats.binned_statistic_2d(xx,yy,values, 'max', bins=[nbin,nbin],range=[[-5,5],[-5,5]]) + # x0min_2d ,_,_,_=stats.binned_statistic_2d(xx,yy,values, 'min', bins=[nbin,nbin],range=[[-5,5],[-5,5]]) + + x_center = (x_edge[0:-1]+x_edge[1:])/2 + y_center = (y_edge[0:-1]+y_edge[1:])/2 + # plt.contour(x0mean_2d.transpose(),extent=[x_edge[0],x_edge[-1],y_edge[0],y_edge[-1]], + # linewidths=3, cmap = plt.cm.rainbow) + # plt.colorbar() + + + fig=plt.figure(figsize=(7, 6)) + c = plt.pcolormesh(x_edge,y_edge/np.pi*180,x0mean_2d.transpose(),norm=LogNorm(vmin=0.001, vmax=10)) + fig.colorbar(c) + # plt.colorbar() + # plt.ylim(-np.pi,np.pi) + plt.ylim(-180,180) + plt.xlim(-5,5) + plt.xlabel("$\eta$") + plt.ylabel("Azimuthal angle [degree]") + plt.suptitle("total X0 [%]") + + plt.savefig(outdir+"/matscan_2d_z%g_r%g.png" %(zrange,rrange)) + + diff --git a/benchmarks/tracking_detectors/scripts/test_matscan.cxx b/benchmarks/tracking_detectors/scripts/test_matscan.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d178e50a1fbeccb20bbdf134efef52d95a5ad197 --- /dev/null +++ b/benchmarks/tracking_detectors/scripts/test_matscan.cxx @@ -0,0 +1,139 @@ +// material scan of a cylinder volume for athena +// the scan starts at 0 for now +// based on https://dd4hep.web.cern.ch/dd4hep/reference/MaterialScan_8cpp_source.html +// dd4hep also provides command line utilities materialBudget and materialScan +// +// Shujie Li, Aug 2021 + +R__LOAD_LIBRARY(libDDCore.so) +R__LOAD_LIBRARY(libDDG4.so) +R__LOAD_LIBRARY(libDDG4IO.so) +#include "DD4hep/Detector.h" +#include "DD4hep/DetElement.h" +#include "DD4hep/Objects.h" +#include "DD4hep/Detector.h" +#include "DDG4/Geant4Data.h" +#include "DDRec/CellIDPositionConverter.h" +#include "DDRec/SurfaceManager.h" +#include "DDRec/Surface.h" + +#include "TCanvas.h" +#include "TChain.h" +#include "TGeoMedium.h" +#include "TGeoManager.h" +#include "DDRec/MaterialScan.h" +#include "DDRec/MaterialManager.h" +#include "DD4hep/Detector.h" +#include "DD4hep/Printout.h" +#include "fmt/core.h" + +#include <iostream> +#include <fstream> + +using namespace dd4hep; +using namespace dd4hep::rec; + + // for silicon tracker, the outer barrel rmax ~45, outer endcap zmax~121 +void test_matscan(const char* compact = "athena.xml",double zmax=130, double rmax = 61){ + + dd4hep::Detector& detector = dd4hep::Detector::getInstance(); + detector.fromCompact(compact); + MaterialScan matscan(detector); + TString detname = "all"; + fmt::print("\n"); + fmt::print("All detector subsystem names:\n"); + for(const auto& d : detector.detectors() ) { + fmt::print(" {}\n", d.first); + } + // to do: material scan of given dtectors. + + // TString det_list[14]={"TrackerBarrel_Inner","TrackerBarrel_Outer","TrackerEndcapN_Inner","TrackerEndcapN_Outer","TrackerEndcapP_Inner","TrackerEndcapP_Outer","TrackerSubAssembly_Inner","TrackerSubAssembly_Outer","VertexBarrel","VertexBarrelSubAssembly","VertexEndcapN","VertexEndcapP","VertexEndcapSubAssembly","cb_DIRC"}; + // for (int dd=0;dd<14;dd++){ + // TString detname = det_list[dd]; + // matscan.setDetector(detname.Data()); + + const char* fmt1 = "%8.3f %8.3f %8.3f %3d %-20s %3.0f %8.4f %11.4f %11.4f %11.4f %11.4f %8.3f %8.3f %8.3f\n"; + + // the beam vacuum is missing if starting from origin and ending at negative z. need to check later + double x0=0,y0=0,z0=0.001,x1,y1,z1; // cm + double epsilon=1e-4; // (mm) default 1e-4: Materials with a thickness smaller than epsilon (default 1e-4=1mu + + const double DegToRad=3.141592653589793/180.0; + double phi,dphi=0.5; // Degree + double dz=0.5, dr=0.5; // cm + + vector<double> lx,ly,lz; + // prepare coord. for barrel + for(z1=-zmax;z1<=zmax;z1+=dz){ + for(phi=0.;phi<=360;phi+=dphi){ + x1 = cos(phi*DegToRad)*rmax; + y1 = sin(phi*DegToRad)*rmax; + lx.push_back(x1); + ly.push_back(y1); + lz.push_back(z1); + } + } + // prepare coord. for endcaps + for (double r=1; r<=rmax; r+=dr){ + for(phi=0.;phi<=360;phi+=dphi){ + x1 = cos(phi*DegToRad)*r; + y1 = sin(phi*DegToRad)*r; + lx.push_back(x1); + ly.push_back(y1); + lz.push_back(zmax); + lx.push_back(x1); + ly.push_back(y1); + lz.push_back(-zmax); + } + } + + // loop over coord ponits for material scan + long nn = lx.size(); + TString fname = Form("%s_z%g_r%g.dat",detname.Data(),zmax,rmax); + FILE * pFile; + pFile = fopen (fname,"w"); + + for(int ii=0;ii<nn;ii++){ + x1=lx[ii]; y1=ly[ii]; z1=lz[ii]; + if(ii%5000==0) + cout<<ii<<"/"<<nn<<":"<<x1<<" "<<y1<<" "<<z1<<endl; + + // if ((x1*x1+y1*y1)>(60*60)) + // continue; + Vector3D p0(x0, y0, z0), p1(x1, y1, z1); + Vector3D end, direction; + direction = (p1-p0).unit(); + const auto& placements = matscan.scan(x0,y0,z0,x1,y1,z1,epsilon); + // matscan.print(x0,y0,z0,x1,y1,z1,epsilon); + + double sum_x0 = 0; + double sum_lambda = 0; + double path_length = 0, total_length = 0; + + + for (unsigned i=0;i<placements.size();i++){ + + TGeoMaterial* mat=placements[i].first->GetMaterial(); + double length = placements[i].second; + double nx0 = length / mat->GetRadLen(); + double nLambda = length / mat->GetIntLen(); + sum_x0 += nx0; + sum_lambda += nLambda; + path_length += length; + total_length += length; + end = p0 + total_length * direction; + + + fprintf(pFile, fmt1,x1, y1, z1, i+1, mat->GetName(), mat->GetZ(), + mat->GetDensity(), mat->GetRadLen(), + length, path_length, sum_x0,end[0], end[1], end[2]); + + + + } + // cout<<detname<<" "<<x1<<","<<y1<<","<<z1<<endl; + // cout<<x1<<","<<y1<<","<<z1<<": "<<placements.size()<<" "<<sum_x0<<" "<<total_length<<endl; + } + fclose (pFile); +} +