Compare commits
278 Commits
master
...
devel_vign
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e6e101370 | ||
|
|
8be662d0d1 | ||
|
|
d4856783e7 | ||
|
|
d63a422a41 | ||
|
|
8c231120d8 | ||
|
|
8043237b89 | ||
|
|
14b1a89d65 | ||
|
|
12c4c87ba0 | ||
|
|
c9339a1165 | ||
|
|
ea79180595 | ||
|
|
8806af3f6d | ||
|
|
01783858fa | ||
|
|
018a92f647 | ||
|
|
3edbfa5bd8 | ||
|
|
aebb9854ec | ||
|
|
19262299ba | ||
|
|
f30e244d27 | ||
|
|
397c410320 | ||
|
|
cc32682de3 | ||
|
|
7ec86569fb | ||
|
|
bbf00ad3c6 | ||
|
|
7be00bcb3f | ||
|
|
d46f456fd9 | ||
|
|
97357e383e | ||
|
|
83114262db | ||
|
|
9b2c20daca | ||
|
|
38c8c7e0e0 | ||
|
|
ec43a81030 | ||
|
|
7f936f6a53 | ||
|
|
048b11fce3 | ||
|
|
d3c4c47e41 | ||
|
|
fde75b8a77 | ||
|
|
80b079b81d | ||
|
|
78add5432a | ||
|
|
ffb1c1a960 | ||
|
|
4d9145d532 | ||
|
|
fce1fb6c1d | ||
|
|
bf8d4089f1 | ||
|
|
941f450af1 | ||
|
|
461d60a50c | ||
|
|
d245fc2679 | ||
|
|
dd6b938eb2 | ||
|
|
3dd678221a | ||
|
|
b5e88a6731 | ||
|
|
d211971904 | ||
|
|
6619613db5 | ||
|
|
e83775d79f | ||
|
|
8cd98c35e1 | ||
|
|
3e60105617 | ||
|
|
c0c49be0c9 | ||
|
|
9416638af6 | ||
|
|
0a7dd1bcf6 | ||
|
|
cf584a6d7a | ||
|
|
c83557f30c | ||
|
|
ed505139b4 | ||
|
|
9b68c5b720 | ||
|
|
98c48af0b1 | ||
|
|
fbd601ec40 | ||
|
|
21fd1ca4d2 | ||
|
|
542e927049 | ||
|
|
2784671732 | ||
|
|
774050dfdf | ||
|
|
caeff0d7c5 | ||
|
|
6ff00bedc2 | ||
|
|
8e9d539653 | ||
|
|
902afaad54 | ||
|
|
07e30a5ddd | ||
|
|
00c507e0ac | ||
|
|
2d3bcc7c74 | ||
|
|
58d0ef1839 | ||
|
|
51ece5e69b | ||
|
|
bd79dda26b | ||
|
|
14999546b7 | ||
|
|
a292695a4d | ||
|
|
173fb36ea2 | ||
|
|
3e9c86d8dc | ||
|
|
3b09639c60 | ||
|
|
2bd4f4eb33 | ||
|
|
a3cab973be | ||
|
|
f17259c8cc | ||
|
|
95f15ed23d | ||
|
|
aa960fd711 | ||
|
|
356d0c65b2 | ||
|
|
ba8e8440e8 | ||
|
|
dae9ca32cc | ||
|
|
3cfecd4c29 | ||
|
|
7568431c03 | ||
|
|
2632044293 | ||
|
|
370649a7ba | ||
|
|
48eea6d70b | ||
|
|
826139a26d | ||
|
|
18b948a335 | ||
|
|
8c10235be9 | ||
|
|
88eba525e1 | ||
|
|
aa48a9d82f | ||
|
|
d19f0352af | ||
|
|
364fce5d0c | ||
|
|
d1d265835c | ||
|
|
d784650f15 | ||
|
|
4249b9b848 | ||
|
|
6bad95d228 | ||
|
|
c2a8cbefc6 | ||
|
|
3ba532843d | ||
|
|
4170adb503 | ||
|
|
385f5af204 | ||
|
|
ff21920bae | ||
|
|
9e2023a9db | ||
|
|
819a8937f7 | ||
|
|
fcd412dc59 | ||
|
|
1bbf2ae059 | ||
|
|
7861cc496c | ||
|
|
f7248f6a4e | ||
|
|
20f8ad227c | ||
|
|
8dd6526221 | ||
|
|
948c6e6ea2 | ||
|
|
5824fa7148 | ||
|
|
20dc8fef22 | ||
|
|
702399be84 | ||
|
|
9ad4e9d5d4 | ||
|
|
7891aaebca | ||
|
|
d8e4b22614 | ||
|
|
d5419bab9b | ||
|
|
66fd758554 | ||
|
|
88cf59bce3 | ||
|
|
86933ac2bf | ||
|
|
a875b17247 | ||
|
|
6104c6f7a9 | ||
|
|
1fb12d2f46 | ||
|
|
792933de25 | ||
|
|
a64d45e081 | ||
|
|
254402b4cc | ||
|
|
787b1ca498 | ||
|
|
15773d4606 | ||
|
|
07fe2a5d38 | ||
|
|
3b70b279e7 | ||
|
|
f5f7515d7a | ||
|
|
41b286da5d | ||
|
|
bb05baf89d | ||
|
|
d8593b2f55 | ||
|
|
ce0bedf704 | ||
|
|
b69dcf39d6 | ||
|
|
17f44bc12e | ||
|
|
3503953b0f | ||
|
|
c142f4b567 | ||
|
|
574506acb7 | ||
|
|
419cbd3c8c | ||
|
|
5282ef7e1c | ||
|
|
1d7b9dde47 | ||
|
|
1081e4b690 | ||
|
|
4dd157bcb8 | ||
|
|
164a3e2e57 | ||
|
|
3a42c2d137 | ||
|
|
2908da4854 | ||
|
|
9d581fa72d | ||
|
|
c8923ca870 | ||
|
|
d43edcf2ee | ||
|
|
e25a63cd14 | ||
|
|
3f55a2a81e | ||
|
|
15e60a0cc1 | ||
|
|
4a7dc22acd | ||
|
|
e41f2e14ec | ||
|
|
40ec71a216 | ||
|
|
dccea0c862 | ||
|
|
fc09dc9258 | ||
|
|
16c49c4f12 | ||
|
|
c3bb28a1a2 | ||
|
|
307d5dfc44 | ||
|
|
1a01f332e6 | ||
|
|
e3bfcda35c | ||
|
|
67bb9b6fd9 | ||
|
|
b936da724a | ||
|
|
8e2cdaae58 | ||
|
|
50a8e03eef | ||
|
|
6e0100253b | ||
|
|
4c8e3b7519 | ||
|
|
b801421d3c | ||
|
|
45ab7f9cee | ||
|
|
3999fd5b71 | ||
|
|
e407b9842e | ||
|
|
00c8a9378e | ||
|
|
652b680fb9 | ||
|
|
74dfc46d8c | ||
|
|
5b34251638 | ||
|
|
68efdadc8f | ||
|
|
e0f2fa192c | ||
|
|
456db0a9d0 | ||
|
|
ec9d25b048 | ||
|
|
65af941b39 | ||
|
|
9af7e70f26 | ||
|
|
6e6d281dd3 | ||
|
|
411ef2d9de | ||
|
|
cbd4bf42a7 | ||
|
|
601caa3881 | ||
|
|
2938411c35 | ||
|
|
8c2657255c | ||
|
|
00f8460e36 | ||
|
|
7260d42d8d | ||
|
|
4401ae8eb2 | ||
|
|
282aa5ecea | ||
|
|
9f949edd00 | ||
|
|
56cc900b61 | ||
|
|
17ab8c884a | ||
|
|
67199cdb60 | ||
|
|
ac035370b4 | ||
|
|
c4b543bdeb | ||
|
|
0a8432e4e3 | ||
|
|
0883ebdb6e | ||
|
|
9c20c4abfe | ||
|
|
13bfafe9c4 | ||
|
|
22e32c7ebc | ||
|
|
4599ad2a38 | ||
|
|
c1ffaa8340 | ||
|
|
19286055ea | ||
|
|
82c2127b4d | ||
|
|
d81e35d5e4 | ||
|
|
97880940be | ||
|
|
aee3a2467d | ||
|
|
c32215e293 | ||
|
|
535afcb704 | ||
|
|
0773b9e6cc | ||
|
|
7582731de4 | ||
|
|
49de3b64a8 | ||
|
|
61473ca14e | ||
|
|
ecd755e09c | ||
|
|
265ebd3372 | ||
|
|
afef56df12 | ||
|
|
579f4e4f6c | ||
|
|
d59b22ff78 | ||
|
|
49610e9c2f | ||
|
|
fa4b1dd2f5 | ||
|
|
877f765357 | ||
|
|
06fbc1afd9 | ||
|
|
b44ffd7fdf | ||
|
|
3d0d176f5a | ||
|
|
4fc05ea338 | ||
|
|
dd2ec66db1 | ||
|
|
a8d4e8f0f6 | ||
|
|
2864036ec8 | ||
|
|
050cb425d5 | ||
|
|
fb355a3cc4 | ||
|
|
5b43d60b30 | ||
|
|
e86ab5ed4d | ||
|
|
9cadfdd191 | ||
|
|
d4582d80ff | ||
|
|
18870ed82b | ||
|
|
5c1c5348f4 | ||
|
|
65ab69ebe6 | ||
|
|
1df7470ca1 | ||
|
|
a7a765c059 | ||
|
|
39e8f41ab1 | ||
|
|
9225620426 | ||
|
|
2225b3a942 | ||
|
|
6cfb38b564 | ||
|
|
cb72c14ca4 | ||
|
|
5995081396 | ||
|
|
d623e0cd17 | ||
|
|
8d7322cf5a | ||
|
|
699b0f8701 | ||
|
|
02213caaee | ||
|
|
56a6389b4f | ||
|
|
b99ad4e4d7 | ||
|
|
a5dfa2ecd3 | ||
|
|
26e943adc8 | ||
|
|
42e093b104 | ||
|
|
3129339647 | ||
|
|
7a70340b18 | ||
|
|
a10081ea81 | ||
|
|
4cb8a2c48c | ||
|
|
fe6dbee171 | ||
|
|
7805481ead | ||
|
|
511b4aa808 | ||
|
|
43233ceb02 | ||
|
|
68fc36a8f6 | ||
|
|
a6e754b958 | ||
|
|
48ede97992 | ||
|
|
f0a393abe2 | ||
|
|
4ba9c73b98 | ||
|
|
238ec8961e |
34
.gitignore
vendored
34
.gitignore
vendored
|
|
@ -1,13 +1,41 @@
|
|||
EventBuilder*
|
||||
*.d
|
||||
*.so
|
||||
*.pcm
|
||||
*.root
|
||||
*.exe
|
||||
|
||||
*.txt
|
||||
*.err
|
||||
*.seq
|
||||
*.png
|
||||
*.gif
|
||||
*.msh
|
||||
*.vtk
|
||||
Mapper
|
||||
AnasenMS
|
||||
|
||||
data/
|
||||
data_proton/
|
||||
root_data/
|
||||
Sudarshan/
|
||||
wires2d/
|
||||
myenv/
|
||||
Analyzer_C_ACLiC_dict0713aaa966_dictContent.h
|
||||
.gitignore
|
||||
Analyzer_C_ACLiC_dict5411fecd5c_dictUmbrella.h
|
||||
gainmatch.C
|
||||
gainmatch.h
|
||||
MakePlotsQQQ.C
|
||||
MakePlotsQQQ.h
|
||||
MakePlotsSX3.C
|
||||
MakePlotsSX3.h
|
||||
Armory/CorrelateQQQ.h
|
||||
QQQStage2.C
|
||||
anasen_fem/scalars.dat.names
|
||||
anasen_fem/He96_CO2_4_260Torr.gas
|
||||
anasen_fem/heavy_ion_track.csv
|
||||
Armory/EventBuilder
|
||||
EventBuilder
|
||||
anasen_fem/anode_to_cathode_1d_17.43.csv
|
||||
pc_calib_raw/
|
||||
AutoFit.C
|
||||
scratch/images/WedgeChVAnode.jpeg
|
||||
cross_section/out/
|
||||
15
.vscode/c_cpp_properties.json
vendored
15
.vscode/c_cpp_properties.json
vendored
|
|
@ -59,7 +59,20 @@
|
|||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"/usr/include/x86_64-linux-gnu/qt6/**",
|
||||
"/usr/local/cern/root/include/**",
|
||||
"/usr/local/cern/root/include/**"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/bin/gcc",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "gnu++17",
|
||||
"intelliSenseMode": "linux-gcc-x64"
|
||||
},
|
||||
{
|
||||
"name": "VigneshROG",
|
||||
"includePath": [
|
||||
"/home/vsitaraman/**",
|
||||
"${workspaceFolder}/**",
|
||||
"/home/vsitaraman/root/include/**"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/bin/gcc",
|
||||
|
|
|
|||
29
.vscode/settings.json
vendored
29
.vscode/settings.json
vendored
|
|
@ -100,7 +100,32 @@
|
|||
"PCPulser_All_new.C": "cpp",
|
||||
"PosCal_2.C": "cpp",
|
||||
"AutoFit.C": "cpp",
|
||||
"Fitting.C": "cpp"
|
||||
"Fitting.C": "cpp",
|
||||
"PCGainMatch.C": "cpp",
|
||||
"Analyzer1.C": "cpp",
|
||||
"FitHistogramsWithTSpectrum_Sequential_Improved.C": "cpp",
|
||||
"PlotAndFitCentroids.C": "cpp",
|
||||
"MatchAndPlotCentroids.C": "cpp",
|
||||
"GainMatch.C": "cpp",
|
||||
"GainMatchSX3.C": "cpp",
|
||||
"RelBack_Fix_new.C": "cpp",
|
||||
"SiRelativeGains_Step1_new.C": "cpp",
|
||||
"charconv": "cpp",
|
||||
"format": "cpp",
|
||||
"GainMatchSX3Front.C": "cpp",
|
||||
"GainMatchSX3Front1.C": "cpp",
|
||||
"Calibration.C": "cpp",
|
||||
"GainMatchQQQ.C": "cpp",
|
||||
"UTF-8gainmatch.C": "cpp",
|
||||
"MakePlotsQQQ.C": "cpp",
|
||||
"MakePlotsSX3.C": "cpp",
|
||||
"QQQ_Calibcheck.C": "cpp",
|
||||
"QQQ_Calcheck.C": "cpp",
|
||||
"makeplots.C": "cpp",
|
||||
"GlobalMinimizeQQQ.C": "cpp",
|
||||
"QQQStage2.C": "cpp",
|
||||
"inspect.C": "cpp"
|
||||
},
|
||||
"github-enterprise.uri": "https://fsunuc.physics.fsu.edu"
|
||||
"github-enterprise.uri": "https://fsunuc.physics.fsu.edu",
|
||||
"C_Cpp.default.compilerPath": "/usr/bin/gcc"
|
||||
}
|
||||
20
Analysis.C
20
Analysis.C
|
|
@ -1,20 +0,0 @@
|
|||
#include "TChain.h"
|
||||
#include <iostream>
|
||||
|
||||
|
||||
|
||||
void Analysis(int start, int end) {
|
||||
// Create a TChain
|
||||
TChain *chain = new TChain("tree");
|
||||
for(int i = start; i < end+1; i++) {
|
||||
chain->Add(Form("data/root_data/Run_%03d_mapped.root", i));
|
||||
}
|
||||
|
||||
// Process the chain using Analyzer.C+
|
||||
chain->Process("Analyzer.C+");
|
||||
}
|
||||
|
||||
// Define a macro with the same name as the script
|
||||
void Analysis() {
|
||||
Analysis(150, 194); // Adjust the range if needed
|
||||
}
|
||||
493
Analyzer.C
493
Analyzer.C
|
|
@ -1,493 +0,0 @@
|
|||
#define Analyzer_cxx
|
||||
|
||||
#include "Analyzer.h"
|
||||
#include <TH2.h>
|
||||
#include <TStyle.h>
|
||||
#include <TCanvas.h>
|
||||
#include <TMath.h>
|
||||
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Armory/ClassSX3.h"
|
||||
#include "Armory/ClassPW.h"
|
||||
|
||||
#include "TVector3.h"
|
||||
|
||||
TH2F *hsx3IndexVE;
|
||||
TH2F *hqqqIndexVE;
|
||||
TH2F *hpcIndexVE;
|
||||
|
||||
TH2F *hsx3Coin;
|
||||
TH2F *hqqqCoin;
|
||||
TH2F *hpcCoin;
|
||||
|
||||
TH2F *hqqqPolar;
|
||||
TH2F *hsx3VpcIndex;
|
||||
TH2F *hqqqVpcIndex;
|
||||
TH2F *hqqqVpcE;
|
||||
TH2F *hsx3VpcE;
|
||||
TH2F *hanVScatsum;
|
||||
int padID = 0;
|
||||
|
||||
SX3 sx3_contr;
|
||||
PW pw_contr;
|
||||
PW pwinstance;
|
||||
TVector3 hitPos;
|
||||
bool HitNonZero;
|
||||
|
||||
TH1F *hZProj;
|
||||
|
||||
void Analyzer::Begin(TTree * /*tree*/)
|
||||
{
|
||||
TString option = GetOption();
|
||||
|
||||
hsx3IndexVE = new TH2F("hsx3IndexVE", "SX3 index vs Energy; sx3 index ; Energy", 24 * 12, 0, 24 * 12, 400, 0, 5000);
|
||||
hsx3IndexVE->SetNdivisions(-612, "x");
|
||||
hqqqIndexVE = new TH2F("hqqqIndexVE", "QQQ index vs Energy; QQQ index ; Energy", 4 * 2 * 16, 0, 4 * 2 * 16, 400, 0, 5000);
|
||||
hqqqIndexVE->SetNdivisions(-1204, "x");
|
||||
hpcIndexVE = new TH2F("hpcIndexVE", "PC index vs Energy; PC index ; Energy", 2 * 24, 0, 2 * 24, 400, 0, 4000);
|
||||
hpcIndexVE->SetNdivisions(-1204, "x");
|
||||
|
||||
hsx3Coin = new TH2F("hsx3Coin", "SX3 Coincident", 24 * 12, 0, 24 * 12, 24 * 12, 0, 24 * 12);
|
||||
hqqqCoin = new TH2F("hqqqCoin", "QQQ Coincident", 4 * 2 * 16, 0, 4 * 2 * 16, 4 * 2 * 16, 0, 4 * 2 * 16);
|
||||
hpcCoin = new TH2F("hpcCoin", "PC Coincident", 2 * 24, 0, 2 * 24, 2 * 24, 0, 2 * 24);
|
||||
|
||||
hqqqPolar = new TH2F("hqqqPolar", "QQQ Polar ID", 16 * 4, -TMath::Pi(), TMath::Pi(), 16, 10, 50);
|
||||
|
||||
hsx3VpcIndex = new TH2F("hsx3Vpcindex", "sx3 vs pc; sx3 index; pc index", 24 * 12, 0, 24 * 12, 48, 0, 48);
|
||||
hsx3VpcIndex->SetNdivisions(-612, "x");
|
||||
hsx3VpcIndex->SetNdivisions(-12, "y");
|
||||
hqqqVpcIndex = new TH2F("hqqqVpcindex", "qqq vs pc; qqq index; pc index", 4 * 2 * 16, 0, 4 * 2 * 16, 48, 0, 48);
|
||||
hqqqVpcIndex->SetNdivisions(-612, "x");
|
||||
hqqqVpcIndex->SetNdivisions(-12, "y");
|
||||
|
||||
hqqqVpcE = new TH2F("hqqqVpcEnergy", "qqq vs pc; qqq energy; pc energy", 400, 0, 5000, 800, 0, 16000);
|
||||
hqqqVpcE->SetNdivisions(-612, "x");
|
||||
hqqqVpcE->SetNdivisions(-12, "y");
|
||||
|
||||
hsx3VpcE = new TH2F("hsx3VpcEnergy", "sx3 vs pc; sx3 energy; pc energy", 400, 0, 5000, 800, 0, 16000);
|
||||
hsx3VpcE->SetNdivisions(-612, "x");
|
||||
hsx3VpcE->SetNdivisions(-12, "y");
|
||||
|
||||
hZProj = new TH1F("hZProj", "Z Projection", 200, -600, 600);
|
||||
|
||||
hanVScatsum = new TH2F("hanVScatsum", "Anode vs Cathode Sum; Anode E; Cathode E", 400, 0, 10000, 400, 0, 16000);
|
||||
sx3_contr.ConstructGeo();
|
||||
pw_contr.ConstructGeo();
|
||||
}
|
||||
|
||||
Bool_t Analyzer::Process(Long64_t entry)
|
||||
{
|
||||
|
||||
// if ( entry > 100 ) return kTRUE;
|
||||
|
||||
hitPos.Clear();
|
||||
HitNonZero = false;
|
||||
|
||||
// if( entry > 1) return kTRUE;
|
||||
// printf("################### ev : %llu \n", entry);
|
||||
|
||||
b_sx3Multi->GetEntry(entry);
|
||||
b_sx3ID->GetEntry(entry);
|
||||
b_sx3Ch->GetEntry(entry);
|
||||
b_sx3E->GetEntry(entry);
|
||||
b_sx3T->GetEntry(entry);
|
||||
b_qqqMulti->GetEntry(entry);
|
||||
b_qqqID->GetEntry(entry);
|
||||
b_qqqCh->GetEntry(entry);
|
||||
b_qqqE->GetEntry(entry);
|
||||
b_qqqT->GetEntry(entry);
|
||||
b_pcMulti->GetEntry(entry);
|
||||
b_pcID->GetEntry(entry);
|
||||
b_pcCh->GetEntry(entry);
|
||||
b_pcE->GetEntry(entry);
|
||||
b_pcT->GetEntry(entry);
|
||||
|
||||
sx3.CalIndex();
|
||||
qqq.CalIndex();
|
||||
pc.CalIndex();
|
||||
|
||||
// sx3.Print();
|
||||
|
||||
// ########################################################### Raw data
|
||||
// //======================= SX3
|
||||
|
||||
std::vector<std::pair<int, int>> ID; // first = id, 2nd = index
|
||||
for (int i = 0; i < sx3.multi; i++)
|
||||
{
|
||||
ID.push_back(std::pair<int, int>(sx3.id[i], i));
|
||||
|
||||
hsx3IndexVE->Fill(sx3.index[i], sx3.e[i]);
|
||||
|
||||
for (int j = i + 1; j < sx3.multi; j++)
|
||||
{
|
||||
hsx3Coin->Fill(sx3.index[i], sx3.index[j]);
|
||||
}
|
||||
|
||||
for (int j = 0; j < pc.multi; j++)
|
||||
{
|
||||
hsx3VpcIndex->Fill(sx3.index[i], pc.index[j]);
|
||||
// if( sx3.ch[index] > 8 ){
|
||||
// hsx3VpcE->Fill( sx3.e[i], pc.e[j] );
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
if (ID.size() > 0)
|
||||
{
|
||||
std::sort(ID.begin(), ID.end(), [](const std::pair<int, int> &a, const std::pair<int, int> &b)
|
||||
{ return a.first < b.first; });
|
||||
// printf("##############################\n");
|
||||
// for( size_t i = 0; i < ID.size(); i++) printf("%zu | %d %d \n", i, ID[i].first, ID[i].second );
|
||||
|
||||
std::vector<std::pair<int, int>> sx3ID;
|
||||
sx3ID.push_back(ID[0]);
|
||||
bool found = false;
|
||||
for (size_t i = 1; i < ID.size(); i++)
|
||||
{
|
||||
if (ID[i].first == sx3ID.back().first)
|
||||
{
|
||||
sx3ID.push_back(ID[i]);
|
||||
if (sx3ID.size() >= 3)
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!found)
|
||||
{
|
||||
sx3ID.clear();
|
||||
sx3ID.push_back(ID[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// printf("---------- sx3ID Multi : %zu \n", sx3ID.size());
|
||||
|
||||
if (found)
|
||||
{
|
||||
int sx3ChUp, sx3ChDn, sx3ChBk;
|
||||
float sx3EUp, sx3EDn;
|
||||
// printf("------ sx3 ID : %d, multi: %zu\n", sx3ID[0].first, sx3ID.size());
|
||||
for (size_t i = 0; i < sx3ID.size(); i++)
|
||||
{
|
||||
int index = sx3ID[i].second;
|
||||
// printf(" %zu | index %d | ch : %d, energy : %d \n", i, index, sx3.ch[index], sx3.e[index]);
|
||||
|
||||
if (sx3.ch[index] < 8)
|
||||
{
|
||||
if (sx3.ch[index] % 2 == 0)
|
||||
{
|
||||
sx3ChDn = sx3.ch[index];
|
||||
sx3EDn = sx3.e[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
sx3ChUp = sx3.ch[index];
|
||||
sx3EUp = sx3.e[index];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sx3ChBk = sx3.ch[index];
|
||||
}
|
||||
for (int j = 0; j < pc.multi; j++)
|
||||
{
|
||||
// hsx3VpcIndex->Fill( sx3.index[i], pc.index[j] );
|
||||
if (sx3.ch[index] > 8)
|
||||
{
|
||||
hsx3VpcE->Fill(sx3.e[i], pc.e[j]);
|
||||
// hpcIndexVE->Fill( pc.index[i], pc.e[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sx3_contr.CalSX3Pos(sx3ID[0].first, sx3ChUp, sx3ChDn, sx3ChBk, sx3EUp, sx3EDn);
|
||||
hitPos = sx3_contr.GetHitPos();
|
||||
HitNonZero = true;
|
||||
// hitPos.Print();
|
||||
}
|
||||
}
|
||||
|
||||
// //======================= QQQ
|
||||
for (int i = 0; i < qqq.multi; i++)
|
||||
{
|
||||
// for( int j = 0; j < pc.multi; j++){
|
||||
// if(pc.index[j]==4){
|
||||
hqqqIndexVE->Fill(qqq.index[i], qqq.e[i]);
|
||||
// }
|
||||
// }
|
||||
for (int j = 0; j < qqq.multi; j++)
|
||||
{
|
||||
if (j == i)
|
||||
continue;
|
||||
hqqqCoin->Fill(qqq.index[i], qqq.index[j]);
|
||||
}
|
||||
|
||||
for (int j = i + 1; j < qqq.multi; j++)
|
||||
{
|
||||
for (int k = 0; k < pc.multi; k++)
|
||||
{
|
||||
if (pc.index[k] < 24 && pc.e[k] > 50)
|
||||
{
|
||||
hqqqVpcE->Fill(qqq.e[i], pc.e[k]);
|
||||
// hpcIndexVE->Fill( pc.index[i], pc.e[i] );
|
||||
hqqqVpcIndex->Fill(qqq.index[i], pc.index[j]);
|
||||
}
|
||||
// }
|
||||
}
|
||||
// if( qqq.used[i] == true ) continue;
|
||||
|
||||
// if( qqq.id[i] == qqq.id[j] && (16 - qqq.ch[i]) * (16 - qqq.ch[j]) < 0 ){ // must be same detector and wedge and ring
|
||||
if (qqq.id[i] == qqq.id[j])
|
||||
{ // must be same detector
|
||||
|
||||
int chWedge = -1;
|
||||
int chRing = -1;
|
||||
if (qqq.ch[i] < qqq.ch[j])
|
||||
{
|
||||
chRing = qqq.ch[j] - 16;
|
||||
chWedge = qqq.ch[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
chRing = qqq.ch[i];
|
||||
chWedge = qqq.ch[j] - 16;
|
||||
}
|
||||
|
||||
// printf(" ID : %d , chWedge : %d, chRing : %d \n", qqq.id[i], chWedge, chRing);
|
||||
|
||||
double theta = -TMath::Pi() / 2 + 2 * TMath::Pi() / 16 / 4. * (qqq.id[i] * 16 + chWedge + 0.5);
|
||||
double rho = 10. + 40. / 16. * (chRing + 0.5);
|
||||
// if(qqq.e[i]>50){
|
||||
hqqqPolar->Fill(theta, rho);
|
||||
// }
|
||||
// qqq.used[i] = true;
|
||||
// qqq.used[j] = true;
|
||||
|
||||
if (!HitNonZero)
|
||||
{
|
||||
double x = rho * TMath::Cos(theta);
|
||||
double y = rho * TMath::Sin(theta);
|
||||
hitPos.SetXYZ(x, y, 23 + 75 + 30);
|
||||
HitNonZero = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// //======================= PC
|
||||
|
||||
ID.clear();
|
||||
int counter = 0;
|
||||
std::vector<std::pair<int, double>> E;
|
||||
E.clear();
|
||||
for (int i = 0; i < pc.multi; i++)
|
||||
{
|
||||
|
||||
if (pc.e[i] > 100)
|
||||
ID.push_back(std::pair<int, int>(pc.id[i], i));
|
||||
if (pc.e[i] > 100)
|
||||
E.push_back(std::pair<int, double>(pc.index[i], pc.e[i]));
|
||||
|
||||
hpcIndexVE->Fill(pc.index[i], pc.e[i]);
|
||||
|
||||
for (int j = i + 1; j < pc.multi; j++)
|
||||
{
|
||||
hpcCoin->Fill(pc.index[i], pc.index[j]);
|
||||
}
|
||||
}
|
||||
// for( size_t i = 0; i < E.size(); i++) printf("%zu | %d %d \n", i, E[i].first, E[i].second );
|
||||
|
||||
pwinstance.ConstructGeo();
|
||||
Coord Crossover[24][24];
|
||||
TVector3 a, c, diff;
|
||||
double a2, ac, c2, adiff, cdiff, denom, alpha, beta;
|
||||
int index = 0;
|
||||
for (int i = 0; i < pwinstance.An.size(); i++)
|
||||
{
|
||||
a = pwinstance.An[i].first - pwinstance.An[i].second;
|
||||
for (int j = 0; j < pwinstance.Ca.size(); j++)
|
||||
{
|
||||
c = pwinstance.Ca[j].first - pwinstance.Ca[j].second;
|
||||
diff = pwinstance.An[i].first - pwinstance.Ca[j].first;
|
||||
a2 = a.Dot(a);
|
||||
ac = a.Dot(c);
|
||||
c2 = c.Dot(c);
|
||||
adiff = a.Dot(diff);
|
||||
cdiff = c.Dot(diff);
|
||||
denom = a2 * c2 - ac * ac;
|
||||
alpha = (ac * cdiff - c2 * adiff) / denom;
|
||||
beta = (a2 * cdiff - ac * adiff) / denom;
|
||||
Crossover[i][j].x = pwinstance.An[i].first.X() + alpha * a.X();
|
||||
Crossover[i][j].y = pwinstance.An[i].first.Y() + alpha * a.Y();
|
||||
Crossover[i][j].z = pwinstance.An[i].first.Z() + alpha * a.Z();
|
||||
if (i == 23)
|
||||
{
|
||||
if (abs(i - j) < 7 || abs(i - j) > 17)
|
||||
{
|
||||
if (alpha < 0 && alpha > -1)
|
||||
{
|
||||
printf("Anode and cathode indices and coord : %d %d %f %f %f %f\n", i, j, pwinstance.Ca[j].first.X(), pwinstance.Ca[j].first.Y(), pwinstance.Ca[j].first.Z(), alpha);
|
||||
printf("Crossover wires, points and alpha are : %f %f %f %f \n", Crossover[i][j].x, Crossover[i][j].y, Crossover[i][j].z, alpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (E.size() >= 3)
|
||||
{
|
||||
|
||||
int aID = 0;
|
||||
int cID = 0;
|
||||
|
||||
float aE = 0;
|
||||
float cE = 0;
|
||||
// if( ID[0].first < 1 ) {
|
||||
// aID = pc.ch[ID[0].second];
|
||||
// cID = pc.ch[ID[1].second];
|
||||
// }else{
|
||||
// cID = pc.ch[ID[0].second];
|
||||
// aID = pc.ch[ID[1].second];
|
||||
// }
|
||||
// printf("anode= %d, cathode = %d\n", aID, cID);
|
||||
|
||||
for (int k = 0; k < qqq.multi; k++)
|
||||
{
|
||||
if (qqq.index[k] == 75 && pc.index[k] == 2 && pc.e[k] > 100)
|
||||
{
|
||||
|
||||
int multi_an = 0;
|
||||
for (int l = 0; l < E.size(); l++)
|
||||
{
|
||||
if (E[l].first < 24)
|
||||
{
|
||||
multi_an++;
|
||||
}
|
||||
}
|
||||
|
||||
if (multi_an >= 1)
|
||||
{
|
||||
for (int l = 0; l < E.size(); l++)
|
||||
{
|
||||
if (E[l].first < 24 && E[l].first != 19 && E[l].first != 12)
|
||||
{
|
||||
aE = E[l].second;
|
||||
}
|
||||
else if (E[l].first > 24)
|
||||
{
|
||||
cE = E[l].second;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
hanVScatsum->Fill(aE, cE);
|
||||
|
||||
if (ID[0].first < 1)
|
||||
{
|
||||
aID = pc.ch[ID[0].second];
|
||||
cID = pc.ch[ID[1].second];
|
||||
}
|
||||
else
|
||||
{
|
||||
cID = pc.ch[ID[0].second];
|
||||
aID = pc.ch[ID[1].second];
|
||||
}
|
||||
|
||||
if (HitNonZero)
|
||||
{
|
||||
pw_contr.CalTrack(hitPos, aID, cID);
|
||||
hZProj->Fill(pw_contr.GetZ0());
|
||||
}
|
||||
}
|
||||
|
||||
// ########################################################### Track constrcution
|
||||
|
||||
// ############################## DO THE KINEMATICS
|
||||
|
||||
return kTRUE;
|
||||
}
|
||||
|
||||
void Analyzer::Terminate()
|
||||
{
|
||||
|
||||
gStyle->SetOptStat("neiou");
|
||||
TCanvas *canvas = new TCanvas("cANASEN", "ANASEN", 2000, 2000);
|
||||
canvas->Divide(3, 3);
|
||||
|
||||
// hsx3VpcIndex->Draw("colz");
|
||||
|
||||
//=============================================== pad-1
|
||||
padID++;
|
||||
canvas->cd(padID);
|
||||
canvas->cd(padID)->SetGrid(1);
|
||||
|
||||
hsx3IndexVE->Draw("colz");
|
||||
|
||||
//=============================================== pad-2
|
||||
padID++;
|
||||
canvas->cd(padID);
|
||||
canvas->cd(padID)->SetGrid(1);
|
||||
|
||||
hqqqIndexVE->Draw("colz");
|
||||
|
||||
//=============================================== pad-3
|
||||
padID++;
|
||||
canvas->cd(padID);
|
||||
canvas->cd(padID)->SetGrid(1);
|
||||
|
||||
hpcIndexVE->Draw("colz");
|
||||
|
||||
//=============================================== pad-4
|
||||
padID++;
|
||||
canvas->cd(padID);
|
||||
canvas->cd(padID)->SetGrid(1);
|
||||
|
||||
hsx3Coin->Draw("colz");
|
||||
|
||||
//=============================================== pad-5
|
||||
padID++;
|
||||
canvas->cd(padID);
|
||||
canvas->cd(padID)->SetGrid(1);
|
||||
|
||||
canvas->cd(padID)->SetLogz(true);
|
||||
|
||||
hqqqCoin->Draw("colz");
|
||||
|
||||
//=============================================== pad-6
|
||||
padID++;
|
||||
canvas->cd(padID);
|
||||
canvas->cd(padID)->SetGrid(1);
|
||||
|
||||
hpcCoin->Draw("colz");
|
||||
|
||||
//=============================================== pad-7
|
||||
padID++;
|
||||
canvas->cd(padID);
|
||||
canvas->cd(padID)->SetGrid(1);
|
||||
|
||||
// hsx3VpcIndex ->Draw("colz");
|
||||
hsx3VpcE->Draw("colz");
|
||||
|
||||
//=============================================== pad-8
|
||||
padID++;
|
||||
canvas->cd(padID);
|
||||
canvas->cd(padID)->SetGrid(1);
|
||||
|
||||
// hqqqVpcIndex ->Draw("colz");
|
||||
|
||||
hqqqVpcE->Draw("colz");
|
||||
//=============================================== pad-9
|
||||
padID++;
|
||||
|
||||
// canvas->cd(padID)->DrawFrame(-50, -50, 50, 50);
|
||||
// hqqqPolar->Draw("same colz pol");
|
||||
|
||||
canvas->cd(padID);
|
||||
canvas->cd(padID)->SetGrid(1);
|
||||
// hZProj->Draw();
|
||||
hanVScatsum->Draw("colz");
|
||||
}
|
||||
|
|
@ -1,283 +0,0 @@
|
|||
#ifndef ClassPW_h
|
||||
#define ClassPW_h
|
||||
|
||||
#include <cstdio>
|
||||
#include <TMath.h>
|
||||
#include <TVector3.h>
|
||||
|
||||
struct PWHitInfo{
|
||||
std::pair<short, short> nearestWire; // anode, cathode
|
||||
std::pair<double, double> nearestDist; // anode, cathode
|
||||
|
||||
std::pair<short, short> nextNearestWire; // anode, cathode
|
||||
std::pair<double, double> nextNearestDist; // anode, cathode
|
||||
|
||||
void Clear(){
|
||||
nearestWire.first = -1;
|
||||
nearestWire.second = -1;
|
||||
nearestDist.first = 999999999;
|
||||
nearestDist.second = 999999999;
|
||||
nextNearestWire.first = -1;
|
||||
nextNearestWire.second = -1;
|
||||
nextNearestDist.first = 999999999;
|
||||
nextNearestDist.second = 999999999;
|
||||
}
|
||||
};
|
||||
|
||||
//!########################################################
|
||||
class PW{ // proportional wire
|
||||
public:
|
||||
PW(){ ClearHitInfo();};
|
||||
~PW(){};
|
||||
|
||||
PWHitInfo GetHitInfo() const {return hitInfo;}
|
||||
std::pair<short, short> GetNearestID() const {return hitInfo.nearestWire;}
|
||||
std::pair<double, double> GetNearestDistance() const {return hitInfo.nearestDist;}
|
||||
std::pair<short, short> Get2ndNearestID() const {return hitInfo.nextNearestWire;}
|
||||
std::pair<double, double> Get2ndNearestDistance() const {return hitInfo.nextNearestDist;}
|
||||
|
||||
TVector3 GetTrackPos() const {return trackPos;}
|
||||
TVector3 GetTrackVec() const {return trackVec;}
|
||||
double GetTrackTheta() const {return trackVec.Theta();}
|
||||
double GetTrackPhi() const {return trackVec.Phi();}
|
||||
double GetZ0();
|
||||
|
||||
int GetNumWire() const {return nWire;}
|
||||
double GetDeltaAngle() const {return dAngle;}
|
||||
double GetAnodeLength() const {return anodeLength;}
|
||||
double GetCathodeLength() const {return cathodeLength;}
|
||||
TVector3 GetAnodeDn(short id) const {return An[id].first;}
|
||||
TVector3 GetAnodeUp(short id) const {return An[id].second;}
|
||||
TVector3 GetCathodeDn(short id) const {return Ca[id].first;}
|
||||
TVector3 GetCathodeUp(short id) const {return Ca[id].second;}
|
||||
|
||||
TVector3 GetAnodneMid(short id) const {return (An[id].first + An[id].second) * 0.5; }
|
||||
double GetAnodeTheta(short id) const {return (An[id].first - An[id].second).Theta();}
|
||||
double GetAnodePhi(short id) const {return (An[id].first - An[id].second).Phi();}
|
||||
|
||||
TVector3 GetCathodneMid(short id) const {return (Ca[id].first + Ca[id].second) * 0.5; }
|
||||
double GetCathodeTheta(short id) const {return (Ca[id].first - Ca[id].second).Theta();}
|
||||
double GetCathodePhi(short id) const {return (Ca[id].first - Ca[id].second).Phi();}
|
||||
|
||||
void ClearHitInfo();
|
||||
void ConstructGeo();
|
||||
void FindWireID(TVector3 pos, TVector3 direction, bool verbose = false);
|
||||
void CalTrack(TVector3 sx3Pos, int anodeID, int cathodeID, bool verbose = false);
|
||||
void CalTrack2(TVector3 sx3Pos, PWHitInfo hitInfo, double sigmaA = 0, double sigmaC = 0, bool verbose = false);
|
||||
|
||||
void Print(){
|
||||
printf(" The nearest | Anode: %2d(%5.2f) Cathode: %2d(%5.2f)\n", hitInfo.nearestWire.first,
|
||||
hitInfo.nearestDist.first,
|
||||
hitInfo.nearestWire.second,
|
||||
hitInfo.nearestDist.second);
|
||||
|
||||
printf(" The 2nd nearest | Anode: %2d(%5.2f) Cathode: %2d(%5.2f)\n", hitInfo.nextNearestWire.first,
|
||||
hitInfo.nextNearestDist.first,
|
||||
hitInfo.nextNearestWire.second,
|
||||
hitInfo.nextNearestDist.second);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
PWHitInfo hitInfo;
|
||||
|
||||
TVector3 trackPos;
|
||||
TVector3 trackVec;
|
||||
|
||||
const int nWire = 24;
|
||||
const int wireShift = 3;
|
||||
const float zLen = 380; //mm
|
||||
const float radiusA = 37;
|
||||
const float radiusC = 43;
|
||||
|
||||
double dAngle;
|
||||
double anodeLength;
|
||||
double cathodeLength;
|
||||
|
||||
std::vector<std::pair<TVector3,TVector3>> An; // the anode wire position vector in space
|
||||
std::vector<std::pair<TVector3,TVector3>> Ca; // the cathode wire position vector in space
|
||||
|
||||
double Distance(TVector3 a1, TVector3 a2, TVector3 b1, TVector3 b2){
|
||||
TVector3 na = a1 - a2;
|
||||
TVector3 nb = b1 - b2;
|
||||
TVector3 nd = (na.Cross(nb)).Unit();
|
||||
return TMath::Abs(nd.Dot(a1-b2));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
inline void PW::ClearHitInfo(){
|
||||
hitInfo.Clear();
|
||||
}
|
||||
|
||||
inline void PW::ConstructGeo(){
|
||||
|
||||
An.clear();
|
||||
Ca.clear();
|
||||
|
||||
std::pair<TVector3, TVector3> p1; // anode
|
||||
std::pair<TVector3, TVector3> q1; // cathode
|
||||
|
||||
//anode and cathode start at pos-Y axis and count in right-Hand
|
||||
//anode wire shift is right-hand.
|
||||
//cathode wire shift is left-hand.
|
||||
|
||||
for(int i = 0; i < nWire; i++ ){
|
||||
// Anode rotate right-hand
|
||||
p1.first.SetXYZ( radiusA * TMath::Cos( TMath::TwoPi() / nWire * (i) + TMath::PiOver2()),
|
||||
radiusA * TMath::Sin( TMath::TwoPi() / nWire * (i) + TMath::PiOver2()),
|
||||
zLen/2);
|
||||
p1.second.SetXYZ( radiusA * TMath::Cos( TMath::TwoPi() / nWire * (i + wireShift) + TMath::PiOver2()),
|
||||
radiusA * TMath::Sin( TMath::TwoPi() / nWire * (i + wireShift) + TMath::PiOver2()),
|
||||
-zLen/2);
|
||||
An.push_back(p1);
|
||||
|
||||
// Cathod rotate left-hand
|
||||
q1.first.SetXYZ( radiusC * TMath::Cos( TMath::TwoPi() / nWire * (i) + TMath::PiOver2()),
|
||||
radiusC * TMath::Sin( TMath::TwoPi() / nWire * (i) + TMath::PiOver2()),
|
||||
zLen/2);
|
||||
q1.second.SetXYZ( radiusC * TMath::Cos( TMath::TwoPi() / nWire * (i - wireShift) + TMath::PiOver2()),
|
||||
radiusC * TMath::Sin( TMath::TwoPi() / nWire * (i - wireShift) + TMath::PiOver2()),
|
||||
-zLen/2);
|
||||
Ca.push_back(q1);
|
||||
}
|
||||
|
||||
dAngle = wireShift * TMath::TwoPi() / nWire;
|
||||
anodeLength = TMath::Sqrt( zLen*zLen + TMath::Power(2* radiusA * TMath::Sin(dAngle/2),2) );
|
||||
cathodeLength = TMath::Sqrt( zLen*zLen + TMath::Power(2* radiusC * TMath::Sin(dAngle/2),2) );
|
||||
}
|
||||
|
||||
inline void PW::FindWireID(TVector3 pos, TVector3 direction, bool verbose ){
|
||||
|
||||
hitInfo.Clear();
|
||||
double phi = direction.Phi();
|
||||
|
||||
for( int i = 0; i < nWire; i++){
|
||||
|
||||
double disA = 99999999;
|
||||
double phiS = An[i].first.Phi() - TMath::PiOver4();
|
||||
double phiL = An[i].second.Phi() + TMath::PiOver4();
|
||||
// printf("A%2d: %f %f | %f\n", i, phiS * TMath::RadToDeg(), phiL * TMath::RadToDeg(), phi * TMath::RadToDeg());
|
||||
if( phi > 0 && phiS > phiL ) phiL = phiL + TMath::TwoPi();
|
||||
if( phi < 0 && phiS > phiL ) phiS = phiS - TMath::TwoPi();
|
||||
|
||||
if( phiS < phi && phi < phiL) {
|
||||
disA = Distance( pos, pos + direction, An[i].first, An[i].second);
|
||||
if( disA < hitInfo.nearestDist.first ){
|
||||
hitInfo.nearestDist.first = disA;
|
||||
hitInfo.nearestWire.first = i;
|
||||
}
|
||||
}
|
||||
|
||||
double disC = 99999999;
|
||||
phiS = Ca[i].second.Phi()- TMath::PiOver4();
|
||||
phiL = Ca[i].first.Phi() + TMath::PiOver4();
|
||||
// printf("C%2d: %f %f\n", i, phiS * TMath::RadToDeg(), phiL * TMath::RadToDeg());
|
||||
if( phi > 0 && phiS > phiL ) phiL = phiL + TMath::TwoPi();
|
||||
if( phi < 0 && phiS > phiL ) phiS = phiS - TMath::TwoPi();
|
||||
|
||||
if(phiS < phi && phi < phiL) {
|
||||
disC = Distance( pos, pos + direction, Ca[i].first, Ca[i].second);
|
||||
if( disC < hitInfo.nearestDist.second ){
|
||||
hitInfo.nearestDist.second = disC;
|
||||
hitInfo.nearestWire.second = i;
|
||||
}
|
||||
}
|
||||
|
||||
if(verbose) printf(" %2d | %8.2f, %8.2f\n", i, disA, disC);
|
||||
}
|
||||
|
||||
//==== find the 2nd nearest wire
|
||||
short anode1 = hitInfo.nearestWire.first;
|
||||
short aaa1 = anode1 - 1; if( aaa1 < 0 ) aaa1 += nWire;
|
||||
short aaa2 = (anode1 + 1) % nWire;
|
||||
|
||||
double haha1 = Distance( pos, pos + direction, An[aaa1].first, An[aaa1].second);
|
||||
double haha2 = Distance( pos, pos + direction, An[aaa2].first, An[aaa2].second);
|
||||
if( haha1 < haha2){
|
||||
hitInfo.nextNearestWire.first = aaa1;
|
||||
hitInfo.nextNearestDist.first = haha1;
|
||||
}else{
|
||||
hitInfo.nextNearestWire.first = aaa2;
|
||||
hitInfo.nextNearestDist.first = haha2;
|
||||
}
|
||||
|
||||
short cathode1 = hitInfo.nearestWire.second;
|
||||
short ccc1 = cathode1 - 1; if( ccc1 < 0 ) ccc1 += nWire;
|
||||
short ccc2 = (cathode1 + 1) % nWire;
|
||||
|
||||
haha1 = Distance( pos, pos + direction, Ca[ccc1].first, Ca[ccc1].second);
|
||||
haha2 = Distance( pos, pos + direction, Ca[ccc2].first, Ca[ccc2].second);
|
||||
if( haha1 < haha2){
|
||||
hitInfo.nextNearestWire.second = ccc1;
|
||||
hitInfo.nextNearestDist.second = haha1;
|
||||
}else{
|
||||
hitInfo.nextNearestWire.second = ccc2;
|
||||
hitInfo.nextNearestDist.second = haha2;
|
||||
}
|
||||
|
||||
if( verbose ) Print();
|
||||
}
|
||||
|
||||
inline void PW::CalTrack(TVector3 sx3Pos, int anodeID, int cathodeID, bool verbose){
|
||||
|
||||
trackPos = sx3Pos;
|
||||
|
||||
TVector3 n1 = (An[anodeID].first - An[anodeID].second).Cross((sx3Pos - An[anodeID].second)).Unit();
|
||||
TVector3 n2 = (Ca[cathodeID].first - Ca[cathodeID].second).Cross((sx3Pos - Ca[cathodeID].second)).Unit();
|
||||
|
||||
// if the handiness of anode and cathode revered, it should be n2 cross n1
|
||||
trackVec = (n2.Cross(n1)).Unit();
|
||||
|
||||
if( verbose ) printf("Theta, Phi = %f, %f \n", trackVec.Theta() *TMath::RadToDeg(), trackVec.Phi()*TMath::RadToDeg());
|
||||
|
||||
}
|
||||
|
||||
inline void PW::CalTrack2(TVector3 sx3Pos, PWHitInfo hitInfo, double sigmaA, double sigmaC, bool verbose){
|
||||
|
||||
trackPos = sx3Pos;
|
||||
|
||||
double p1 = TMath::Abs(hitInfo.nearestDist.first + gRandom->Gaus(0, sigmaA));
|
||||
double p2 = TMath::Abs(hitInfo.nextNearestDist.first + gRandom->Gaus(0, sigmaA));
|
||||
double fracA = p1 / (p1 + p2);
|
||||
short anodeID1 = hitInfo.nearestWire.first;
|
||||
short anodeID2 = hitInfo.nextNearestWire.first;
|
||||
TVector3 shiftA1 = (An[anodeID2].first - An[anodeID1].first) * fracA;
|
||||
TVector3 shiftA2 = (An[anodeID2].second - An[anodeID1].second) * fracA;
|
||||
|
||||
double q1 = TMath::Abs(hitInfo.nearestDist.second + gRandom->Gaus(0, sigmaC));
|
||||
double q2 = TMath::Abs(hitInfo.nextNearestDist.second + gRandom->Gaus(0, sigmaC));
|
||||
double fracC = q1 / (q1 + q2);
|
||||
short cathodeID1 = hitInfo.nearestWire.second;
|
||||
short cathodeID2 = hitInfo.nextNearestWire.second;
|
||||
TVector3 shiftC1 = (Ca[cathodeID2].first - Ca[cathodeID1].first) * fracC;
|
||||
TVector3 shiftC2 = (Ca[cathodeID2].second - Ca[cathodeID1].second) * fracC;
|
||||
|
||||
TVector3 a1 = An[anodeID1].first + shiftA1;
|
||||
TVector3 a2 = An[anodeID1].second + shiftA2;
|
||||
|
||||
TVector3 c1 = Ca[cathodeID1].first + shiftC1;
|
||||
TVector3 c2 = Ca[cathodeID1].second + shiftC2;
|
||||
|
||||
TVector3 n1 = (a1 - a2).Cross((sx3Pos - a2)).Unit();
|
||||
TVector3 n2 = (c1 - c2).Cross((sx3Pos - c2)).Unit();
|
||||
|
||||
// if the handiness of anode and cathode revered, it should be n2 cross n1
|
||||
trackVec = (n2.Cross(n1)).Unit();
|
||||
|
||||
if( verbose ) printf("Theta, Phi = %f, %f \n", trackVec.Theta() *TMath::RadToDeg(), trackVec.Phi()*TMath::RadToDeg());
|
||||
|
||||
}
|
||||
|
||||
inline double PW::GetZ0(){
|
||||
|
||||
double x = trackPos.X();
|
||||
double y = trackPos.Y();
|
||||
double rho = TMath::Sqrt(x*x + y*y);
|
||||
double theta = trackVec.Theta();
|
||||
|
||||
return trackPos.Z() - rho / TMath::Tan(theta);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -42,7 +42,7 @@ void ANASEN_model(int anodeID1 = -1, int anodeID2 = -1, int cathodeID1 = -1, int
|
|||
//--- making ANASEN
|
||||
const int nWire = 24;
|
||||
const int wireShift = 3;
|
||||
const int zLen = 300; //mm
|
||||
const int zLen = 350; //mm
|
||||
const int radiusA = 38;
|
||||
const int radiusC = 43;
|
||||
|
||||
|
|
@ -103,8 +103,8 @@ void ANASEN_model(int anodeID1 = -1, int anodeID2 = -1, int cathodeID1 = -1, int
|
|||
new TGeoRotation("rot1", 360/nSX3 * (i + 0.5), 0., 0.)));
|
||||
}
|
||||
|
||||
const int qqqR1 = 10;
|
||||
const int qqqR2 = 50;
|
||||
const int qqqR1 = 50;
|
||||
const int qqqR2 = 100;
|
||||
TGeoVolume *qqq = geom->MakeTubs("qqq", Al, qqqR1, qqqR2, 0.5, 5, 85);
|
||||
qqq->SetLineColor(7);
|
||||
for( int i = 0; i < 4; i++){
|
||||
|
|
|
|||
1270
Armory/ClassData.h
Normal file
1270
Armory/ClassData.h
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -5,60 +5,67 @@
|
|||
|
||||
#define MAXMULTI 1000
|
||||
|
||||
class Det{
|
||||
class Det
|
||||
{
|
||||
public:
|
||||
Det(): multi(0) {Clear(); }
|
||||
Det() : multi(0) { Clear(); }
|
||||
|
||||
unsigned short multi; // max 65535
|
||||
unsigned short id[MAXMULTI];
|
||||
unsigned short ch[MAXMULTI];
|
||||
unsigned short e[MAXMULTI];
|
||||
unsigned long long t[MAXMULTI];
|
||||
unsigned short multi; // max 65535
|
||||
unsigned short id[MAXMULTI];
|
||||
unsigned short ch[MAXMULTI];
|
||||
unsigned short e[MAXMULTI];
|
||||
unsigned long long t[MAXMULTI];
|
||||
unsigned long long tf[MAXMULTI];
|
||||
|
||||
unsigned short sn[MAXMULTI];
|
||||
unsigned short digiCh[MAXMULTI];
|
||||
unsigned short sn[MAXMULTI];
|
||||
unsigned short digiCh[MAXMULTI];
|
||||
|
||||
unsigned short index[MAXMULTI]; // id * nCh + ch;
|
||||
bool used[MAXMULTI];
|
||||
unsigned short index[MAXMULTI]; // id * nCh + ch;
|
||||
bool used[MAXMULTI];
|
||||
|
||||
void Clear(){
|
||||
void Clear()
|
||||
{
|
||||
multi = 0;
|
||||
for( int i = 0; i < MAXMULTI; i++){
|
||||
for (int i = 0; i < MAXMULTI; i++)
|
||||
{
|
||||
id[i] = 0;
|
||||
ch[i] = 0;
|
||||
e[i] = 0;
|
||||
t[i] = 0;
|
||||
index[i] = 0;
|
||||
sn[i] = 0;
|
||||
digiCh[i] = 0;
|
||||
used[i] = false;
|
||||
e[i] = 0;
|
||||
t[i] = 0;
|
||||
tf[i] = 0;
|
||||
index[i] = 0;
|
||||
sn[i] = 0;
|
||||
digiCh[i] = 0;
|
||||
used[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Print(){
|
||||
void Print()
|
||||
{
|
||||
printf("=============================== multi : %u\n", multi);
|
||||
for( int i = 0; i < multi; i++) {
|
||||
printf(" %3d | %2d-%-2d(%5d) %5u %15llu \n", i, id[i], ch[i], index[i], e[i], t[i]);
|
||||
for (int i = 0; i < multi; i++)
|
||||
{
|
||||
printf(" %3d | %2d-%-2d(%5d) %5u %15llu %15llu \n", i, id[i], ch[i], index[i], e[i], t[i], tf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void SetDetDimension(unsigned short maxID, unsigned maxCh){
|
||||
void SetDetDimension(unsigned short maxID, unsigned maxCh)
|
||||
{
|
||||
nID = maxID;
|
||||
nCh = maxCh;
|
||||
}
|
||||
|
||||
void CalIndex(){
|
||||
for( int i = 0; i < multi; i++){
|
||||
index[i] = id[i] * nCh + ch[i] ;
|
||||
void CalIndex()
|
||||
{
|
||||
for (int i = 0; i < multi; i++)
|
||||
{
|
||||
index[i] = id[i] * nCh + ch[i];
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
unsigned short nID;
|
||||
unsigned short nCh;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,246 +0,0 @@
|
|||
#ifndef ClassPC_h
|
||||
#define ClassPC_h
|
||||
|
||||
#include <cstdio>
|
||||
#include <TMath.h>
|
||||
#include <TVector3.h>
|
||||
#include <TRandom.h>
|
||||
|
||||
struct PCHit_1An{
|
||||
std::pair<short, short> nearestWire; // anode, cathode
|
||||
std::pair<double, double> nearestDist; // anode, cathode
|
||||
|
||||
short nextNearestWire; // cathode
|
||||
double nextNearestDist; // cathode
|
||||
|
||||
void Clear(){
|
||||
nearestWire.first = -1;
|
||||
nearestWire.second = -1;
|
||||
nearestDist.first = 999999999;
|
||||
nearestDist.second = 999999999;
|
||||
nextNearestWire= -1;
|
||||
nextNearestDist = 999999999;
|
||||
}
|
||||
};
|
||||
|
||||
//!########################################################
|
||||
class PC{ // proportional wire
|
||||
public:
|
||||
PC(){ ClearHitInfo();};
|
||||
~PC(){};
|
||||
|
||||
PCHit_1An GetHitInfo() const {return hitInfo;}
|
||||
std::pair<short, short> GetNearestID() const {return hitInfo.nearestWire;}
|
||||
std::pair<double, double> GetNearestDistance() const {return hitInfo.nearestDist;}
|
||||
short Get2ndNearestID() const {return hitInfo.nextNearestWire;}
|
||||
double Get2ndNearestDistance() const {return hitInfo.nextNearestDist;}
|
||||
|
||||
TVector3 GetTrackPos() const {return trackPos;}
|
||||
TVector3 GetTrackVec() const {return trackVec;}
|
||||
double GetTrackTheta() const {return trackVec.Theta();}
|
||||
double GetTrackPhi() const {return trackVec.Phi();}
|
||||
double GetZ0();
|
||||
|
||||
int GetNumWire() const {return nWire;}
|
||||
double GetDeltaAngle() const {return dAngle;}
|
||||
double GetAnodeLength() const {return anodeLength;}
|
||||
double GetCathodeLength() const {return cathodeLength;}
|
||||
TVector3 GetAnodeDn(short id) const {return An[id].first;}
|
||||
TVector3 GetAnodeUp(short id) const {return An[id].second;}
|
||||
TVector3 GetCathodeDn(short id) const {return Ca[id].first;}
|
||||
TVector3 GetCathodeUp(short id) const {return Ca[id].second;}
|
||||
|
||||
TVector3 GetAnodneMid(short id) const {return (An[id].first + An[id].second) * 0.5; }
|
||||
double GetAnodeTheta(short id) const {return (An[id].first - An[id].second).Theta();}
|
||||
double GetAnodePhi(short id) const {return (An[id].first - An[id].second).Phi();}
|
||||
|
||||
TVector3 GetCathodneMid(short id) const {return (Ca[id].first + Ca[id].second) * 0.5; }
|
||||
double GetCathodeTheta(short id) const {return (Ca[id].first - Ca[id].second).Theta();}
|
||||
double GetCathodePhi(short id) const {return (Ca[id].first - Ca[id].second).Phi();}
|
||||
|
||||
void ClearHitInfo();
|
||||
void ConstructGeo();
|
||||
void FindWireID(TVector3 pos, TVector3 direction, bool verbose = false);
|
||||
void CalTrack3(TVector3 sx3Pos, PCHit_1An hitInfo, double sigmaA = 0, double sigmaC = 0, bool verbose = false);
|
||||
|
||||
void Print(){
|
||||
printf(" The nearest | Anode: %2d(%5.2f) Cathode: %2d(%5.2f)\n", hitInfo.nearestWire.first,
|
||||
hitInfo.nearestDist.first,
|
||||
hitInfo.nearestWire.second,
|
||||
hitInfo.nearestDist.second);
|
||||
|
||||
printf(" The 2nd nearest Cathode: %2d(%5.2f)\n", hitInfo.nextNearestWire,
|
||||
hitInfo.nextNearestDist);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// PCHitInfo hitInfo;
|
||||
PCHit_1An hitInfo;
|
||||
|
||||
TVector3 trackPos;
|
||||
TVector3 trackVec;
|
||||
|
||||
const int nWire = 24;
|
||||
const int wireShift = 3;
|
||||
const float zLen = 380; //mm
|
||||
const float radiusA = 37;
|
||||
const float radiusC = 43;
|
||||
|
||||
double dAngle;
|
||||
double anodeLength;
|
||||
double cathodeLength;
|
||||
|
||||
std::vector<std::pair<TVector3,TVector3>> An; // the anode wire position vector in space
|
||||
std::vector<std::pair<TVector3,TVector3>> Ca; // the cathode wire position vector in space
|
||||
|
||||
double Distance(TVector3 a1, TVector3 a2, TVector3 b1, TVector3 b2){
|
||||
TVector3 na = a1 - a2;
|
||||
TVector3 nb = b1 - b2;
|
||||
TVector3 nd = (na.Cross(nb)).Unit();
|
||||
return TMath::Abs(nd.Dot(a1-b2));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
inline void PC::ClearHitInfo(){
|
||||
hitInfo.Clear();
|
||||
}
|
||||
|
||||
inline void PC::ConstructGeo(){
|
||||
|
||||
An.clear();
|
||||
Ca.clear();
|
||||
|
||||
std::pair<TVector3, TVector3> p1; // anode
|
||||
std::pair<TVector3, TVector3> q1; // cathode
|
||||
|
||||
//anode and cathode start at pos-Y axis and count in right-Hand
|
||||
//anode wire shift is right-hand.
|
||||
//cathode wire shift is left-hand.
|
||||
|
||||
for(int i = 0; i < nWire; i++ ){
|
||||
// Anode rotate right-hand
|
||||
p1.first.SetXYZ( radiusA * TMath::Cos( TMath::TwoPi() / nWire * (i) + TMath::PiOver2()),
|
||||
radiusA * TMath::Sin( TMath::TwoPi() / nWire * (i) + TMath::PiOver2()),
|
||||
zLen/2);
|
||||
p1.second.SetXYZ( radiusA * TMath::Cos( TMath::TwoPi() / nWire * (i + wireShift) + TMath::PiOver2()),
|
||||
radiusA * TMath::Sin( TMath::TwoPi() / nWire * (i + wireShift) + TMath::PiOver2()),
|
||||
-zLen/2);
|
||||
An.push_back(p1);
|
||||
|
||||
// Cathod rotate left-hand
|
||||
q1.first.SetXYZ( radiusC * TMath::Cos( TMath::TwoPi() / nWire * (i) + TMath::PiOver2()),
|
||||
radiusC * TMath::Sin( TMath::TwoPi() / nWire * (i) + TMath::PiOver2()),
|
||||
zLen/2);
|
||||
q1.second.SetXYZ( radiusC * TMath::Cos( TMath::TwoPi() / nWire * (i - wireShift) + TMath::PiOver2()),
|
||||
radiusC * TMath::Sin( TMath::TwoPi() / nWire * (i - wireShift) + TMath::PiOver2()),
|
||||
-zLen/2);
|
||||
Ca.push_back(q1);
|
||||
}
|
||||
|
||||
dAngle = wireShift * TMath::TwoPi() / nWire;
|
||||
anodeLength = TMath::Sqrt( zLen*zLen + TMath::Power(2* radiusA * TMath::Sin(dAngle/2),2) );
|
||||
cathodeLength = TMath::Sqrt( zLen*zLen + TMath::Power(2* radiusC * TMath::Sin(dAngle/2),2) );
|
||||
}
|
||||
|
||||
inline void PC::FindWireID(TVector3 pos, TVector3 direction, bool verbose ){
|
||||
|
||||
hitInfo.Clear();
|
||||
double phi = direction.Phi();
|
||||
|
||||
for( int i = 0; i < nWire; i++){
|
||||
|
||||
double disA = 99999999;
|
||||
double phiS = An[i].first.Phi() - TMath::PiOver4();
|
||||
double phiL = An[i].second.Phi() + TMath::PiOver4();
|
||||
// printf("A%2d: %f %f | %f\n", i, phiS * TMath::RadToDeg(), phiL * TMath::RadToDeg(), phi * TMath::RadToDeg());
|
||||
if( phi > 0 && phiS > phiL ) phiL = phiL + TMath::TwoPi();
|
||||
if( phi < 0 && phiS > phiL ) phiS = phiS - TMath::TwoPi();
|
||||
|
||||
if( phiS < phi && phi < phiL) {
|
||||
disA = Distance( pos, pos + direction, An[i].first, An[i].second);
|
||||
if( disA < hitInfo.nearestDist.first ){
|
||||
hitInfo.nearestDist.first = disA;
|
||||
hitInfo.nearestWire.first = i;
|
||||
}
|
||||
}
|
||||
|
||||
double disC = 99999999;
|
||||
phiS = Ca[i].second.Phi()- TMath::PiOver4();
|
||||
phiL = Ca[i].first.Phi() + TMath::PiOver4();
|
||||
// printf("C%2d: %f %f\n", i, phiS * TMath::RadToDeg(), phiL * TMath::RadToDeg());
|
||||
if( phi > 0 && phiS > phiL ) phiL = phiL + TMath::TwoPi();
|
||||
if( phi < 0 && phiS > phiL ) phiS = phiS - TMath::TwoPi();
|
||||
|
||||
if(phiS < phi && phi < phiL) {
|
||||
disC = Distance( pos, pos + direction, Ca[i].first, Ca[i].second);
|
||||
if( disC < hitInfo.nearestDist.second ){
|
||||
hitInfo.nearestDist.second = disC;
|
||||
hitInfo.nearestWire.second = i;
|
||||
}
|
||||
}
|
||||
|
||||
if(verbose) printf(" %2d | %8.2f, %8.2f\n", i, disA, disC);
|
||||
}
|
||||
|
||||
short cathode1 = hitInfo.nearestWire.second;
|
||||
short ccc1 = cathode1 - 1; if( ccc1 < 0 ) ccc1 += nWire;
|
||||
short ccc2 = (cathode1 + 1) % nWire;
|
||||
|
||||
double haha1 = Distance( pos, pos + direction, Ca[ccc1].first, Ca[ccc1].second);
|
||||
double haha2 = Distance( pos, pos + direction, Ca[ccc2].first, Ca[ccc2].second);
|
||||
if( haha1 < haha2){
|
||||
hitInfo.nextNearestWire = ccc1;
|
||||
hitInfo.nextNearestDist = haha1;
|
||||
}else{
|
||||
hitInfo.nextNearestWire = ccc2;
|
||||
hitInfo.nextNearestDist= haha2;
|
||||
}
|
||||
|
||||
if( verbose ) Print();
|
||||
}
|
||||
|
||||
|
||||
inline void PC::CalTrack3(TVector3 sx3Pos, PCHit_1An hitInfo, double sigmaA, double sigmaC, bool verbose){
|
||||
|
||||
trackPos = sx3Pos;
|
||||
|
||||
double p1 = TMath::Abs(hitInfo.nearestDist.first + gRandom->Gaus(0, sigmaA));
|
||||
short anodeID1 = hitInfo.nearestWire.first;
|
||||
|
||||
double q1 = TMath::Abs(hitInfo.nearestDist.second + gRandom->Gaus(0, sigmaC));
|
||||
double q2 = TMath::Abs(hitInfo.nextNearestDist+ gRandom->Gaus(0, sigmaC));
|
||||
double fracC = q1 / (q1 + q2);
|
||||
short cathodeID1 = hitInfo.nearestWire.second;
|
||||
short cathodeID2 = hitInfo.nextNearestWire;
|
||||
TVector3 shiftC1 = (Ca[cathodeID2].first - Ca[cathodeID1].first) * fracC;
|
||||
TVector3 shiftC2 = (Ca[cathodeID2].second - Ca[cathodeID1].second) * fracC;
|
||||
|
||||
TVector3 a1 = An[anodeID1].first;
|
||||
|
||||
TVector3 c1 = Ca[cathodeID1].first + shiftC1;
|
||||
TVector3 c2 = Ca[cathodeID1].second + shiftC2;
|
||||
|
||||
TVector3 n1 = (sx3Pos - a1).Unit();
|
||||
TVector3 n2 = (c1 - c2).Cross((sx3Pos - c2)).Unit();
|
||||
|
||||
// if the handiness of anode and cathode revered, it should be n2 cross n1
|
||||
trackVec = (n2.Cross(n1)).Unit();
|
||||
|
||||
if( verbose ) printf("Theta, Phi = %f, %f \n", trackVec.Theta() *TMath::RadToDeg(), trackVec.Phi()*TMath::RadToDeg());
|
||||
|
||||
}
|
||||
|
||||
inline double PC::GetZ0(){
|
||||
|
||||
double x = trackPos.X();
|
||||
double y = trackPos.Y();
|
||||
double rho = TMath::Sqrt(x*x + y*y);
|
||||
double theta = trackVec.Theta();
|
||||
|
||||
return trackPos.Z() - rho / TMath::Tan(theta);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
473
Armory/ClassPW.h
Normal file → Executable file
473
Armory/ClassPW.h
Normal file → Executable file
|
|
@ -2,6 +2,7 @@
|
|||
#define ClassPW_h
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <TMath.h>
|
||||
#include <TVector3.h>
|
||||
#include <TRandom.h>
|
||||
|
|
@ -61,6 +62,18 @@ public:
|
|||
double GetTrackPhi() const { return trackVec.Phi(); }
|
||||
double GetZ0();
|
||||
|
||||
Coord Crossover[24][24][2];
|
||||
|
||||
inline TVector3 getClosestWirePosAtWirePhi(std::pair<TVector3, TVector3>, double phi);
|
||||
inline TVector3 getClosestWirePosAtWirePhiOld(std::pair<TVector3, TVector3>, double phi);
|
||||
inline std::tuple<std::pair<TVector3, TVector3>, double, double, double> GetPseudoWire(const std::vector<std::tuple<int, double, double>> &cluster, std::string type);
|
||||
|
||||
inline std::tuple<TVector3, double, double, double, double, double, double, double>
|
||||
FindCrossoverProperties(const std::vector<std::tuple<int, double, double>> &a_cluster, const std::vector<std::tuple<int, double, double>> &c_cluster);
|
||||
|
||||
inline std::vector<std::vector<std::tuple<int, double, double>>>
|
||||
Make_Clusters(const std::unordered_map<int, std::tuple<int, double, double>> &wireEvents);
|
||||
|
||||
int GetNumWire() const { return nWire; }
|
||||
double GetDeltaAngle() const { return dAngle; }
|
||||
double GetAnodeLength() const { return anodeLength; }
|
||||
|
|
@ -74,7 +87,7 @@ public:
|
|||
double GetAnodeTheta(short id) const { return (An[id].first - An[id].second).Theta(); }
|
||||
double GetAnodePhi(short id) const { return (An[id].first - An[id].second).Phi(); }
|
||||
|
||||
TVector3 GetCathodneMid(short id) const { return (Ca[id].first + Ca[id].second) * 0.5; }
|
||||
TVector3 GetCathodeMid(short id) const { return (Ca[id].first + Ca[id].second) * 0.5; }
|
||||
double GetCathodeTheta(short id) const { return (Ca[id].first - Ca[id].second).Theta(); }
|
||||
double GetCathodePhi(short id) const { return (Ca[id].first - Ca[id].second).Phi(); }
|
||||
|
||||
|
|
@ -82,7 +95,7 @@ public:
|
|||
void ConstructGeo();
|
||||
void FindWireID(TVector3 pos, TVector3 direction, bool verbose = false);
|
||||
void CalTrack(TVector3 sx3Pos, int anodeID, int cathodeID, bool verbose = false);
|
||||
void CalTrack2(TVector3 sx3Pos, PWHitInfo hitInfo, double sigmaA = 0, double sigmaC = 0, bool verbose = false);
|
||||
void CalTrack2(TVector3 sx3Pos, TVector3 anodeInt, bool verbose = false);
|
||||
|
||||
void Print()
|
||||
{
|
||||
|
|
@ -104,10 +117,12 @@ private:
|
|||
TVector3 trackVec;
|
||||
|
||||
const int nWire = 24;
|
||||
const int wireShift = 3;
|
||||
const float zLen = 380; // mm
|
||||
const int wireShift = 4;
|
||||
// const float zLen = 380; // mm
|
||||
// const float zLen = 348.6; // mm
|
||||
const float zLen = 174.3 * 2; // mm
|
||||
const float radiusA = 37;
|
||||
const float radiusC = 43;
|
||||
const float radiusC = 42;
|
||||
|
||||
double dAngle;
|
||||
double anodeLength;
|
||||
|
|
@ -139,34 +154,318 @@ inline void PW::ConstructGeo()
|
|||
std::pair<TVector3, TVector3> p1; // anode
|
||||
std::pair<TVector3, TVector3> q1; // cathode
|
||||
|
||||
// anode and cathode start at pos-Y axis and count in right-Hand
|
||||
// anode wire shift is right-hand.
|
||||
// cathode wire shift is left-hand.
|
||||
double k = TMath::TwoPi() / 24.; // 48 solder thru holes, wires in every other one
|
||||
double offset_a1 = -6 * k - 4 * k; // -6 to go from 0,0 to 90degree up, and 4 for the wire offset //old version -5 * k - 5 * k;
|
||||
double offset_c1 = -6 * k + k / 2.0; // correct for a half-turn
|
||||
// std::cerr << "Here!" << std::endl;
|
||||
// #include "../scratch/testing.h"
|
||||
double offset_a2 = offset_a1 + wireShift * k;
|
||||
double offset_c2 = offset_c1 - wireShift * k;
|
||||
|
||||
for (int i = 0; i < nWire; i++)
|
||||
{
|
||||
// Anode rotate right-hand
|
||||
p1.first.SetXYZ(radiusA * TMath::Cos(TMath::TwoPi() / nWire * (i) + TMath::PiOver2()),
|
||||
radiusA * TMath::Sin(TMath::TwoPi() / nWire * (i) + TMath::PiOver2()),
|
||||
zLen / 2);
|
||||
p1.second.SetXYZ(radiusA * TMath::Cos(TMath::TwoPi() / nWire * (i + wireShift) + TMath::PiOver2()),
|
||||
radiusA * TMath::Sin(TMath::TwoPi() / nWire * (i + wireShift) + TMath::PiOver2()),
|
||||
-zLen / 2);
|
||||
An.push_back(p1);
|
||||
// Anode rotate right-hand coming in towards +z riding with the beam. In this frame, +x is to the right, and +y down
|
||||
// updated Feb 2026, Sudarsan B. Photographs indicate that anode wires twist right handed, as one moves from -z to +z with the convention above
|
||||
// wire indices increase leftward as one moves to +z (hence -k factor), but wires themselves twist rightward - as indicated by offset_a2 being more +ve w.r.t offset_a1
|
||||
//'First' is -z locus, 'second' is +z locus
|
||||
p1.first.SetXYZ(radiusA * TMath::Cos(-k * i + offset_a1),
|
||||
radiusA * TMath::Sin(-k * i + offset_a1),
|
||||
-zLen / 2);
|
||||
p1.second.SetXYZ(radiusA * TMath::Cos(-k * i + offset_a2),
|
||||
radiusA * TMath::Sin(-k * i + offset_a2),
|
||||
+zLen / 2);
|
||||
|
||||
// Cathod rotate left-hand
|
||||
q1.first.SetXYZ(radiusC * TMath::Cos(TMath::TwoPi() / nWire * (i) + TMath::PiOver2()),
|
||||
radiusC * TMath::Sin(TMath::TwoPi() / nWire * (i) + TMath::PiOver2()),
|
||||
zLen / 2);
|
||||
q1.second.SetXYZ(radiusC * TMath::Cos(TMath::TwoPi() / nWire * (i - wireShift) + TMath::PiOver2()),
|
||||
radiusC * TMath::Sin(TMath::TwoPi() / nWire * (i - wireShift) + TMath::PiOver2()),
|
||||
-zLen / 2);
|
||||
// Cathodes twist left-hand as indicated by offset_c2 being more negative than offset_c1, under the same system, while wires increase rightward (hence +k factor)
|
||||
q1.first.SetXYZ(radiusC * TMath::Cos(k * i + offset_c1),
|
||||
radiusC * TMath::Sin(k * i + offset_c1),
|
||||
-zLen / 2);
|
||||
q1.second.SetXYZ(radiusC * TMath::Cos(k * i + offset_c2),
|
||||
radiusC * TMath::Sin(k * i + offset_c2),
|
||||
zLen / 2);
|
||||
An.push_back(p1);
|
||||
Ca.push_back(q1);
|
||||
}
|
||||
|
||||
// Calculate Crossover Geometry ONCE
|
||||
TVector3 a, c, diff;
|
||||
double a2, ac, c2, adiff, cdiff, denom, alpha;
|
||||
|
||||
for (size_t i = 0; i < An.size(); i++)
|
||||
{
|
||||
// a = An[i].first - An[i].second;
|
||||
a = An[i].second - An[i].first;
|
||||
for (size_t j = 0; j < Ca.size(); j++)
|
||||
{
|
||||
c = Ca[j].second - Ca[j].first;
|
||||
diff = An[i].second - Ca[j].second;
|
||||
a2 = a.Dot(a);
|
||||
c2 = c.Dot(c);
|
||||
ac = a.Dot(c);
|
||||
adiff = a.Dot(diff);
|
||||
cdiff = c.Dot(diff);
|
||||
denom = a2 * c2 - ac * ac;
|
||||
alpha = (ac * cdiff - c2 * adiff) / denom;
|
||||
|
||||
Crossover[i][j][0].x = An[i].second.X() + alpha * a.X();
|
||||
Crossover[i][j][0].y = An[i].second.Y() + alpha * a.Y();
|
||||
Crossover[i][j][0].z = An[i].second.Z() + alpha * a.Z();
|
||||
|
||||
if (Crossover[i][j][0].z < -190 || Crossover[i][j][0].z > 190)
|
||||
{
|
||||
// std::cout << "Weird crossover but ok" << std::endl;
|
||||
}
|
||||
if ((i + j) % 24 == 12 || Crossover[i][j][0].z < -190 || Crossover[i][j][0].z > 190)
|
||||
{
|
||||
Crossover[i][j][0].z = 9999999;
|
||||
// std::cout << "Weird crossover" << std::endl;
|
||||
}
|
||||
|
||||
Crossover[i][j][1].x = alpha;
|
||||
Crossover[i][j][1].y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
dAngle = wireShift * TMath::TwoPi() / nWire;
|
||||
anodeLength = TMath::Sqrt(zLen * zLen + TMath::Power(2 * radiusA * TMath::Sin(dAngle / 2), 2));
|
||||
cathodeLength = TMath::Sqrt(zLen * zLen + TMath::Power(2 * radiusC * TMath::Sin(dAngle / 2), 2));
|
||||
cathodeLength = TMath::Sqrt(zLen * zLen + TMath::Power(2 * radiusC * TMath::Sin(dAngle / 2), 2)); // chord length subtending an angle alpha is 2rsin(alpha/2)
|
||||
}
|
||||
|
||||
inline TVector3 PW::getClosestWirePosAtWirePhiOld(std::pair<TVector3, TVector3> awire, double sx3phi_radian)
|
||||
{
|
||||
// 1. Get wire geometry
|
||||
TVector3 a1 = awire.first; // Top of the wire
|
||||
TVector3 a2 = awire.second; // Bottom of the wire
|
||||
TVector3 wireVec = a2 - a1; // Vector pointing down the wire
|
||||
|
||||
// Variables to track our minimums during the scan
|
||||
double min_delta_phi = 9999.0;
|
||||
double best_t = -1.0;
|
||||
TVector3 best_pcz_intersect;
|
||||
|
||||
// 2. THE SCAN: Walk down the wire in 1000 tiny steps
|
||||
// (For a 380mm wire, this is checking every 0.38 mm)
|
||||
int num_steps = 1000;
|
||||
for (int i = 0; i <= num_steps; ++i)
|
||||
{
|
||||
double t_test = (double)i / num_steps; // Ranges from 0.0 to 1.0
|
||||
TVector3 test_pt = a1 + t_test * wireVec; // The 3D point at this step
|
||||
|
||||
// Calculate absolute Delta Phi between Si hit and this specific point on the wire
|
||||
if (TMath::IsNaN(sx3phi_radian - test_pt.Phi()))
|
||||
continue;
|
||||
double dPhi = TMath::Abs(TVector2::Phi_mpi_pi(sx3phi_radian - test_pt.Phi())); // Phi_mpi_pi just puts the angle in the range -180 to 180
|
||||
|
||||
// If this is the smallest Delta Phi we've seen so far, save it!
|
||||
if (dPhi < min_delta_phi)
|
||||
{
|
||||
min_delta_phi = dPhi;
|
||||
best_t = t_test;
|
||||
best_pcz_intersect = test_pt;
|
||||
}
|
||||
}
|
||||
return best_pcz_intersect;
|
||||
}
|
||||
|
||||
inline TVector3 PW::getClosestWirePosAtWirePhi(std::pair<TVector3, TVector3> awire, double phi)
|
||||
{
|
||||
const TVector3 &a1 = awire.first;
|
||||
const TVector3 &a2 = awire.second;
|
||||
const double s = TMath::Sin(phi), c = TMath::Cos(phi);
|
||||
const double dx = a2.X() - a1.X(), dy = a2.Y() - a1.Y();
|
||||
const double t = (a1.Y() * c - a1.X() * s) / (dx * s - dy * c);
|
||||
|
||||
auto nearerEndpoint = [&]() -> TVector3
|
||||
{
|
||||
auto dphi = [&](const TVector3 &p)
|
||||
{
|
||||
return TMath::Abs(TVector2::Phi_mpi_pi(phi - p.Phi()));
|
||||
};
|
||||
return dphi(a1) <= dphi(a2) ? a1 : a2;
|
||||
};
|
||||
|
||||
if (t < 0.0 || t > 1.0)
|
||||
return nearerEndpoint();
|
||||
|
||||
const TVector3 hit = a1 + t * (a2 - a1);
|
||||
if (hit.X() * c + hit.Y() * s <= 0.0) // wrong half-plane (anti-phi side)
|
||||
return nearerEndpoint();
|
||||
|
||||
return hit;
|
||||
}
|
||||
|
||||
inline std::vector<std::vector<std::tuple<int, double, double>>>
|
||||
|
||||
PW::Make_Clusters(const std::unordered_map<int, std::tuple<int, double, double>> &wireEvents)
|
||||
{
|
||||
std::vector<std::vector<std::tuple<int, double, double>>> wireClusters;
|
||||
std::vector<std::tuple<int, double, double>> wireCluster;
|
||||
// TODO: Write a macro once, call it twice
|
||||
int wirecount = 0;
|
||||
while (wirecount < 24)
|
||||
{
|
||||
if (wireEvents.find(wirecount) == wireEvents.end())
|
||||
{
|
||||
wirecount++;
|
||||
continue;
|
||||
}
|
||||
wireCluster.clear();
|
||||
int ctr2 = wirecount;
|
||||
do
|
||||
{
|
||||
wireCluster.emplace_back(wireEvents.at(ctr2));
|
||||
ctr2 += 1;
|
||||
if (ctr2 == 24 || ctr2 - wirecount == 7)
|
||||
break; // loose logic, needs to be looked at.
|
||||
} while (wireEvents.find(ctr2) != wireEvents.end());
|
||||
wireClusters.push_back(std::move(wireCluster));
|
||||
wirecount = ctr2; // we already dealt with wires until the last value of ctr2
|
||||
}
|
||||
|
||||
if (wireClusters.size() > 1)
|
||||
{
|
||||
auto &first_cluster = wireClusters.front();
|
||||
auto &last_cluster = wireClusters.back();
|
||||
if (std::get<0>(last_cluster.back()) == 23 && std::get<0>(first_cluster.front()) == 0)
|
||||
{
|
||||
last_cluster.insert(last_cluster.end(), first_cluster.begin(), first_cluster.end());
|
||||
wireClusters.erase(wireClusters.begin()); // invalidates the references above; not used after this
|
||||
}
|
||||
// TODO: Can also deal with 'gaps' of missing wires similarly. end of one segment and beginning of another segment will be separated by missing wire --> combine the two
|
||||
// TODO: Also needs some development regarding the time-correlation. Don't put wires in the same cluster if they aren't time coincident
|
||||
}
|
||||
return wireClusters;
|
||||
|
||||
/*if(aClusters.size()>1 || cClusters.size() > 1) {
|
||||
std::cout << " ============== " << std::endl;
|
||||
}
|
||||
if(aClusters.size()>1 && cClusters.size() >=1) {
|
||||
std::cout << aClusters.size() << " new anode clusters ----> " << std::endl;
|
||||
int cc=1;
|
||||
for(auto ac : aClusters) {
|
||||
std::cout << " Cluster " << cc << std::endl;
|
||||
double first_ts = std::get<2>(ac.at(0));
|
||||
for(auto item : ac) {
|
||||
std::cout << " \t" << std::get<0>(item) << " " << std::get<1>(item) << " " << std::get<2>(item)-first_ts << std::endl;
|
||||
}
|
||||
std::cout << " ------" << std::endl;
|
||||
cc++;
|
||||
}
|
||||
}
|
||||
|
||||
if(cClusters.size()>=1 ) {
|
||||
std::cout << cClusters.size() << " new cathode clusters ----> " << std::endl;
|
||||
int cc=1;
|
||||
for(auto ac : cClusters) {
|
||||
std::cout << " Cluster " << cc << std::endl;
|
||||
double first_ts = std::get<2>(ac.at(0));
|
||||
for(auto item : ac) {
|
||||
std::cout << " \t" << std::get<0>(item) << " " << std::get<1>(item) << " " << std::get<2>(item)-first_ts << std::endl;
|
||||
}
|
||||
std::cout << " ------" << std::endl;
|
||||
cc++;
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
inline std::tuple<std::pair<TVector3, TVector3>, double, double, double>
|
||||
PW::GetPseudoWire(const std::vector<std::tuple<int, double, double>> &cluster, std::string type)
|
||||
{
|
||||
std::pair<TVector3, TVector3> avgvec = std::pair(TVector3(0, 0, 0), TVector3(0, 0, 0));
|
||||
double sumEnergy = 0;
|
||||
double maxEnergy = 0;
|
||||
double tsMaxEnergy = 0;
|
||||
if (type == "ANODE")
|
||||
{
|
||||
// if(cluster.size()>1) std::cout << " -------anodes" << std::endl;
|
||||
for (auto wire : cluster)
|
||||
{
|
||||
avgvec.first += std::get<1>(wire) * TVector3(An.at(std::get<0>(wire)).first.X(), An.at(std::get<0>(wire)).first.Y(), 0);
|
||||
avgvec.second += std::get<1>(wire) * TVector3(An.at(std::get<0>(wire)).second.X(), An.at(std::get<0>(wire)).second.Y(), 0);
|
||||
sumEnergy += std::get<1>(wire);
|
||||
if (std::get<1>(wire) > maxEnergy)
|
||||
{
|
||||
maxEnergy = std::get<1>(wire);
|
||||
tsMaxEnergy = std::get<2>(wire);
|
||||
}
|
||||
/*if(cluster.size()>1) {
|
||||
std::cout << "\t\t ch:" << std::get<0>(wire) << " " << std::get<1>(wire) << " " << std::get<2>(wire) << std::endl;
|
||||
std::cout << "\t\t w1(r,phi,z):" << An.at(std::get<0>(wire)).first.Perp() << " " << An.at(std::get<0>(wire)).first.Phi()*180/M_PI << " " << An.at(std::get<0>(wire)).first.Z() << std::endl;
|
||||
std::cout << "\t\t w2(r,phi,z):" << An.at(std::get<0>(wire)).second.Perp() << " " << An.at(std::get<0>(wire)).second.Phi()*180/M_PI << " " << An.at(std::get<0>(wire)).second.Z() << std::endl;
|
||||
}*/
|
||||
}
|
||||
avgvec.first = avgvec.first * (1.0 / sumEnergy);
|
||||
avgvec.second = avgvec.second * (1.0 / sumEnergy);
|
||||
double phi1 = avgvec.first.Phi();
|
||||
double phi2 = avgvec.second.Phi();
|
||||
avgvec.first.SetXYZ(radiusA * TMath::Cos(phi1), radiusA * TMath::Sin(phi1), -zLen / 2);
|
||||
avgvec.second.SetXYZ(radiusA * TMath::Cos(phi2), radiusA * TMath::Sin(phi2), zLen / 2);
|
||||
/*if(cluster.size()>1) {
|
||||
std::cout << "\t\t avg1(r,phi,z):" << avgvec.first.Perp() << " " << avgvec.first.Phi()*180/M_PI << " " << avgvec.first.Z() << std::endl;
|
||||
std::cout << "\t\t avg2(r,phi,z):" << avgvec.second.Perp() << " " << avgvec.second.Phi()*180/M_PI << " " << avgvec.second.Z() << std::endl;
|
||||
}*/
|
||||
}
|
||||
else if (type == "CATHODE")
|
||||
{
|
||||
for (auto wire : cluster)
|
||||
{
|
||||
avgvec.first += std::get<1>(wire) * TVector3(Ca.at(std::get<0>(wire)).first.X(), Ca.at(std::get<0>(wire)).first.Y(), 0);
|
||||
avgvec.second += std::get<1>(wire) * TVector3(Ca.at(std::get<0>(wire)).second.X(), Ca.at(std::get<0>(wire)).second.Y(), 0);
|
||||
sumEnergy += std::get<1>(wire);
|
||||
if (std::get<1>(wire) > maxEnergy)
|
||||
{
|
||||
maxEnergy = std::get<1>(wire);
|
||||
tsMaxEnergy = std::get<2>(wire);
|
||||
}
|
||||
}
|
||||
avgvec.first = avgvec.first * (1.0 / sumEnergy);
|
||||
avgvec.second = avgvec.second * (1.0 / sumEnergy);
|
||||
double phi1 = avgvec.first.Phi();
|
||||
double phi2 = avgvec.second.Phi();
|
||||
avgvec.first.SetXYZ(radiusC * TMath::Cos(phi1), radiusC * TMath::Sin(phi1), -zLen / 2);
|
||||
avgvec.second.SetXYZ(radiusC * TMath::Cos(phi2), radiusC * TMath::Sin(phi2), zLen / 2);
|
||||
}
|
||||
return std::tuple(avgvec, sumEnergy, maxEnergy, tsMaxEnergy);
|
||||
}
|
||||
|
||||
inline std::tuple<TVector3, double, double, double, double, double, double, double> PW::FindCrossoverProperties(const std::vector<std::tuple<int, double, double>> &a_cluster,
|
||||
const std::vector<std::tuple<int, double, double>> &c_cluster)
|
||||
{
|
||||
// std::pair<TVector3, TVector3> apwire = GetPseudoWire(a_cluster,"ANODE",anodeSumE);
|
||||
// std::pair<TVector3, TVector3> cpwire = GetPseudoWire(c_cluster,"CATHODE",cathodeSumE);
|
||||
auto [apwire, apSumE, apMaxE, apTSMaxE] = GetPseudoWire(a_cluster, "ANODE");
|
||||
auto [cpwire, cpSumE, cpMaxE, cpTSMaxE] = GetPseudoWire(c_cluster, "CATHODE");
|
||||
|
||||
TVector3 crossover;
|
||||
crossover.Clear();
|
||||
TVector3 a, c, diff;
|
||||
double a2, ac, c2, adiff, cdiff, denom, alpha = 0;
|
||||
|
||||
if (apSumE && cpSumE)
|
||||
{
|
||||
a = apwire.first - apwire.second;
|
||||
c = cpwire.first - cpwire.second;
|
||||
diff = apwire.first - cpwire.first;
|
||||
a2 = a.Dot(a);
|
||||
c2 = c.Dot(c);
|
||||
ac = a.Dot(c);
|
||||
adiff = a.Dot(diff);
|
||||
cdiff = c.Dot(diff);
|
||||
denom = a2 * c2 - ac * ac;
|
||||
alpha = (ac * cdiff - c2 * adiff) / denom;
|
||||
crossover = apwire.first + alpha * a;
|
||||
if (crossover.z() < -190 || crossover.Z() > 190)
|
||||
{
|
||||
alpha = 9999999;
|
||||
apSumE = -1;
|
||||
cpSumE = -1;
|
||||
apMaxE = -1;
|
||||
cpMaxE = -1;
|
||||
apTSMaxE = -1;
|
||||
cpTSMaxE = -1;
|
||||
}
|
||||
}
|
||||
// std::cout << apSumE << " " << cpSumE << " " << " " << crossover.Perp() << std::endl;
|
||||
return std::tuple(crossover, alpha, apSumE, cpSumE, apMaxE, cpMaxE, apTSMaxE, cpTSMaxE);
|
||||
}
|
||||
|
||||
inline void PW::FindWireID(TVector3 pos, TVector3 direction, bool verbose)
|
||||
|
|
@ -278,43 +577,35 @@ inline void PW::CalTrack(TVector3 sx3Pos, int anodeID, int cathodeID, bool verbo
|
|||
printf("Theta, Phi = %f, %f \n", trackVec.Theta() * TMath::RadToDeg(), trackVec.Phi() * TMath::RadToDeg());
|
||||
}
|
||||
|
||||
inline void PW::CalTrack2(TVector3 sx3Pos, PWHitInfo hitInfo, double sigmaA, double sigmaC, bool verbose)
|
||||
inline void PW::CalTrack2(TVector3 siPos, TVector3 anodeInt, bool verbose)
|
||||
{
|
||||
|
||||
trackPos = sx3Pos;
|
||||
|
||||
double p1 = TMath::Abs(hitInfo.nearestDist.first + gRandom->Gaus(0, sigmaA));
|
||||
double p2 = TMath::Abs(hitInfo.nextNearestDist.first + gRandom->Gaus(0, sigmaA));
|
||||
double fracA = p1 / (p1 + p2);
|
||||
short anodeID1 = hitInfo.nearestWire.first;
|
||||
short anodeID2 = hitInfo.nextNearestWire.first;
|
||||
TVector3 shiftA1 = (An[anodeID2].first - An[anodeID1].first) * fracA;
|
||||
TVector3 shiftA2 = (An[anodeID2].second - An[anodeID1].second) * fracA;
|
||||
|
||||
double q1 = TMath::Abs(hitInfo.nearestDist.second + gRandom->Gaus(0, sigmaC));
|
||||
double q2 = TMath::Abs(hitInfo.nextNearestDist.second + gRandom->Gaus(0, sigmaC));
|
||||
double fracC = q1 / (q1 + q2);
|
||||
short cathodeID1 = hitInfo.nearestWire.second;
|
||||
short cathodeID2 = hitInfo.nextNearestWire.second;
|
||||
TVector3 shiftC1 = (Ca[cathodeID2].first - Ca[cathodeID1].first) * fracC;
|
||||
TVector3 shiftC2 = (Ca[cathodeID2].second - Ca[cathodeID1].second) * fracC;
|
||||
|
||||
TVector3 a1 = An[anodeID1].first + shiftA1;
|
||||
TVector3 a2 = An[anodeID1].second + shiftA2;
|
||||
|
||||
TVector3 c1 = Ca[cathodeID1].first + shiftC1;
|
||||
TVector3 c2 = Ca[cathodeID1].second + shiftC2;
|
||||
|
||||
TVector3 n1 = (a1 - a2).Cross((sx3Pos - a2)).Unit();
|
||||
TVector3 n2 = (c1 - c2).Cross((sx3Pos - c2)).Unit();
|
||||
|
||||
// if the handiness of anode and cathode revered, it should be n2 cross n1
|
||||
trackVec = (n2.Cross(n1)).Unit();
|
||||
double mx, my;
|
||||
double z;
|
||||
mx = siPos.X() / (siPos.X() - anodeInt.X());
|
||||
my = siPos.Y() / (siPos.Y() - anodeInt.Y());
|
||||
z = siPos.Z() + mx * (anodeInt.Z() - siPos.Z());
|
||||
// if (mx == my)
|
||||
{
|
||||
trackVec = TVector3(0, 0, z);
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
printf("Theta, Phi = %f, %f \n", trackVec.Theta() * TMath::RadToDeg(), trackVec.Phi() * TMath::RadToDeg());
|
||||
printf("X slope = %f and Y slope = %f \n", mx, my);
|
||||
}
|
||||
|
||||
/*inline TVector3 PW::CalTrack3(TVector3 siPos, TVector3 anodeInt, bool verbose)
|
||||
{
|
||||
|
||||
TVector3 v = anodeInt-siPos;
|
||||
double t_minimum = -1.0*(siPos.X()*v.X()+siPos.Y()*v.Y())/(v.X()*v.X()+v.Y()*v.Y());
|
||||
TVector3 vector_closest_to_z = siPos + t_minimum*v;
|
||||
|
||||
return vector_closest_to_z;
|
||||
if (verbose)
|
||||
printf("X slope = %f and Y slope = %f \n", mx, my);
|
||||
}*/
|
||||
|
||||
inline double PW::GetZ0()
|
||||
{
|
||||
|
||||
|
|
@ -323,7 +614,75 @@ inline double PW::GetZ0()
|
|||
double rho = TMath::Sqrt(x * x + y * y);
|
||||
double theta = trackVec.Theta();
|
||||
|
||||
return trackPos.Z() - rho / TMath::Tan(theta);
|
||||
return trackVec.Z();
|
||||
}
|
||||
// Each wire family is strung as straight skew chords between two rings, so it sweeps a
|
||||
// one-sheet hyperboloid (x*x+y*y)/(a*a) - (z*z)/(c*c) = 1 whose waist 'a' is smaller than
|
||||
// the ring radius. The anode waist was fit to the measured anode crossover points; the
|
||||
// other families share the flare 'c' and scale their waist with the ring radius.
|
||||
//
|
||||
// Radial ordering, inner to outer: guard (ring 32) < anode (ring 37) < cathode (ring 42).
|
||||
// Charge collection spans guard -> cathode. The anodes in the middle are the readout, the
|
||||
// cathodes see the induced/mirror charge, and the guard wires are field-shaping, not read out.
|
||||
const double kPCHyperbC = 301.895;
|
||||
const double kPCAnodeWaist = 32.0429; // ring radius 37 mm -- readout
|
||||
const double kPCCathodeWaist = 32.0429 * 42.0 / 37.0; // ring radius 42 mm -- induced charge
|
||||
const double kPCGuardWaist = 32.0429 * 32.0 / 37.0; // ring radius 32 mm -- not read out
|
||||
|
||||
// Intersection of the segment x1 -> x1+dx with the one-sheet hyperboloid of waist a, flare c.
|
||||
// Returns TVector3(0, 0, 54321) when the segment does not cross the surface.
|
||||
inline TVector3 pc_hyperboloid_intersect(const TVector3 &x1, const TVector3 &dx, double a, double c)
|
||||
{
|
||||
double t2 = 1.0; // value of 't' at the destination point, by definition t=(z(t)-z0)/dz
|
||||
auto A = pow(dx.Perp(), 2) / (a * a) - pow(dx.Z(), 2) / (c * c);
|
||||
auto B = 2 * (dx.X() * x1.X() + dx.Y() * x1.Y()) / (a * a) - 2 * (dx.Z() * x1.Z()) / (c * c);
|
||||
auto C = pow(x1.Perp(), 2) / (a * a) - pow(x1.Z(), 2) / (c * c) - 1.0;
|
||||
double disc = B * B - 4 * A * C;
|
||||
if (disc < 0)
|
||||
return TVector3(0, 0, 54321);
|
||||
double tsol1 = (-B + TMath::Sqrt(disc)) / (2 * A);
|
||||
double tsol2 = (-B - TMath::Sqrt(disc)) / (2 * A);
|
||||
if (tsol1 >= 0 && tsol1 <= t2)
|
||||
return x1 + tsol1 * dx;
|
||||
else if (tsol2 >= 0 && tsol2 <= t2)
|
||||
return x1 + tsol2 * dx;
|
||||
else
|
||||
return TVector3(0, 0, 54321);
|
||||
}
|
||||
|
||||
#endif
|
||||
inline std::tuple<TVector3, TVector3, double> find_PC_PathLength(const TVector3 &x1, const TVector3 &x2)
|
||||
{
|
||||
/*
|
||||
Anode -> cathode segment for a particle moving from x1 to x2.
|
||||
* Typical arguments here will be x1=r_rhoMin, x2=qqqevent.pos or sx3event.pos
|
||||
* Returns {cathode_intersect, anode_intersect, gap_in_cm}; gap == 54321 sentinel on failure.
|
||||
* NOTE: this is the anode->cathode gap, NOT the full charge-collection region.
|
||||
For the anode energy calibration use find_PC_CollectionPath() instead.
|
||||
*/
|
||||
TVector3 dx = x2 - x1; // direction vector
|
||||
TVector3 anode_intersect = pc_hyperboloid_intersect(x1, dx, kPCAnodeWaist, kPCHyperbC);
|
||||
TVector3 cathode_intersect = pc_hyperboloid_intersect(x1, dx, kPCCathodeWaist, kPCHyperbC);
|
||||
if (anode_intersect.Z() != 54321 && cathode_intersect.Z() != 54321)
|
||||
return std::tuple(cathode_intersect, anode_intersect, (cathode_intersect - anode_intersect).Mag() * 0.1);
|
||||
else
|
||||
return std::tuple(TVector3(0, 0, 0), TVector3(0, 0, 0), 54321);
|
||||
}
|
||||
|
||||
inline std::tuple<TVector3, TVector3, double> find_PC_CollectionPath(const TVector3 &x1, const TVector3 &x2)
|
||||
{
|
||||
/*
|
||||
Charge-collection region for a particle moving from x1 to x2: it begins where the track
|
||||
passes the guard wires and ends at the cathode. This is the segment whose energy deposit
|
||||
the anodes actually collect, so it is the dE target for the anode energy calibration.
|
||||
* Returns {guard_intersect, cathode_intersect, thickness_in_cm}; 54321 sentinel on failure.
|
||||
*/
|
||||
TVector3 dx = x2 - x1;
|
||||
TVector3 guard_intersect = pc_hyperboloid_intersect(x1, dx, kPCGuardWaist, kPCHyperbC);
|
||||
TVector3 cathode_intersect = pc_hyperboloid_intersect(x1, dx, kPCCathodeWaist, kPCHyperbC);
|
||||
if (guard_intersect.Z() != 54321 && cathode_intersect.Z() != 54321)
|
||||
return std::tuple(guard_intersect, cathode_intersect, (cathode_intersect - guard_intersect).Mag() * 0.1);
|
||||
else
|
||||
return std::tuple(TVector3(0, 0, 0), TVector3(0, 0, 0), 54321);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
355
Armory/EventBuilder.cpp
Normal file
355
Armory/EventBuilder.cpp
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
#include "fsuReader.h"
|
||||
|
||||
#include "TROOT.h"
|
||||
#include "TSystem.h"
|
||||
#include "TClonesArray.h"
|
||||
#include "TGraph.h"
|
||||
#include "TFile.h"
|
||||
#include "TTree.h"
|
||||
#include "TMacro.h"
|
||||
|
||||
|
||||
#define MAX_MULTI 2000
|
||||
|
||||
struct FileInfo{
|
||||
|
||||
std::string fileName;
|
||||
int fileID;
|
||||
unsigned long hitCount;
|
||||
|
||||
};
|
||||
|
||||
//^#############################################################
|
||||
//^#############################################################
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
printf("=========================================\n");
|
||||
printf("=== *.fsu Events Builder ===\n");
|
||||
printf("=========================================\n");
|
||||
if (argc < 6) {
|
||||
printf("Incorrect number of arguments:\n");
|
||||
printf("%s [timeWindow] [withTrace] [verbose] [batchSize] [inFile1] [inFile2] .... \n", argv[0]);
|
||||
printf(" timeWindow : in ns, -1 = no event building \n");
|
||||
printf(" withTrace : 0 for no trace, 1 for trace \n");
|
||||
printf(" verbose : > 0 for debug \n");
|
||||
printf(" batchSize : the size of hit in a batch \n");
|
||||
printf(" Output file name is contructed from inFile1 \n");
|
||||
printf("\n");
|
||||
printf(" Example: %s 0 0 0 10000 '\\ls -1 *001*.fsu'\n", argv[0]);
|
||||
printf("\n\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
uInt runStartTime = getTime_us();
|
||||
|
||||
///============= read input
|
||||
long timeWindow = atoi(argv[1]);
|
||||
bool traceOn = atoi(argv[2]);
|
||||
unsigned int debug = atoi(argv[3]);
|
||||
unsigned int batchSize = atoi(argv[4]);
|
||||
int nFile = argc - 5;
|
||||
TString inFileName[nFile];
|
||||
for( int i = 0 ; i < nFile ; i++){ inFileName[i] = argv[i+5];}
|
||||
|
||||
/// Form outFileName;
|
||||
TString outFileName = inFileName[0];
|
||||
int pos = outFileName.Last('/');
|
||||
pos = outFileName.Index("_", pos+1); // find next "_"
|
||||
pos = outFileName.Index("_", pos+1); // find next "_"
|
||||
if( nFile == 1 ) pos = outFileName.Index("_", pos+1); // find next "_", S/N
|
||||
outFileName.Remove(pos); // remove the rest
|
||||
outFileName += "_" + std::to_string(timeWindow);
|
||||
outFileName += ".root";
|
||||
printf("-------> Out file name : %s \n", outFileName.Data());
|
||||
printf("========================================= Number of Files : %d \n", nFile);
|
||||
for( int i = 0; i < nFile; i++) printf("%2d | %s \n", i, inFileName[i].Data());
|
||||
printf("=========================================\n");
|
||||
printf(" Time Window = %ld ns = %.1f us\n", timeWindow, timeWindow/1000.);
|
||||
printf(" Include Trace = %s\n", traceOn ? "Yes" : "No");
|
||||
printf(" Debug level = %d\n", debug);
|
||||
printf(" Batch size = %d events/file\n", batchSize);
|
||||
printf(" Max multiplity = %d hits/event (hard coded)\n", MAX_MULTI);
|
||||
printf("========================================= Grouping files\n");
|
||||
|
||||
std::vector<std::vector<FileInfo>> fileGroupList; // fileName and ID = SN * 1000 + index
|
||||
std::vector<FileInfo> fileList;
|
||||
|
||||
unsigned long long int totalHitCount = 0;
|
||||
|
||||
FSUReader * readerA = new FSUReader(inFileName[0].Data(), 1, 1);
|
||||
readerA->ScanNumBlock(0,0);
|
||||
FileInfo fileInfo = {inFileName[0].Data(), readerA->GetSN() * 1000 + readerA->GetFileOrder(), readerA->GetHitCount()};
|
||||
fileList.push_back(fileInfo);
|
||||
totalHitCount += readerA->GetHitCount();
|
||||
|
||||
for( int i = 1; i < nFile; i++){
|
||||
FSUReader * readerB = new FSUReader(inFileName[i].Data(), 1, 0);
|
||||
readerB->ScanNumBlock(0,0);
|
||||
totalHitCount += readerB->GetHitCount();
|
||||
fileInfo = {inFileName[i].Data(), readerB->GetSN() * 1000 + readerB->GetFileOrder(), readerB->GetHitCount()};
|
||||
|
||||
if( readerA->GetSN() == readerB->GetSN() ){
|
||||
fileList.push_back(fileInfo);
|
||||
}else{
|
||||
fileGroupList.push_back(fileList);
|
||||
fileList.clear();
|
||||
fileList.push_back(fileInfo);
|
||||
}
|
||||
|
||||
delete readerA;
|
||||
readerA = readerB;
|
||||
}
|
||||
fileGroupList.push_back(fileList);
|
||||
delete readerA;
|
||||
|
||||
printf("======================= total Hit Count : %llu\n", totalHitCount);
|
||||
|
||||
for( size_t i = 0; i < fileGroupList.size(); i++){
|
||||
printf("group ----- %ld \n", i);
|
||||
|
||||
//sort by ID
|
||||
std::sort(fileGroupList[i].begin(), fileGroupList[i].end(), [](const FileInfo & a, const FileInfo & b) {
|
||||
return a.fileID < b.fileID;
|
||||
});
|
||||
|
||||
for( size_t j = 0; j < fileGroupList[i].size(); j++){
|
||||
printf("%3ld | %8d | %9lu| %s \n", j, fileGroupList[i][j].fileID, fileGroupList[i][j].hitCount, fileGroupList[i][j].fileName.c_str() );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// //*====================================== create tree
|
||||
TFile * outRootFile = new TFile(outFileName, "recreate");
|
||||
TTree * tree = new TTree("tree", outFileName);
|
||||
|
||||
unsigned long long evID = 0;
|
||||
unsigned int multi = 0;
|
||||
unsigned short sn[MAX_MULTI] = {0}; /// board SN
|
||||
unsigned short ch[MAX_MULTI] = {0}; /// chID
|
||||
unsigned short e[MAX_MULTI] = {0}; /// 15 bit
|
||||
unsigned short e2[MAX_MULTI] = {0}; /// 15 bit
|
||||
unsigned long long e_t[MAX_MULTI] = {0}; /// timestamp 47 bit
|
||||
unsigned short e_f[MAX_MULTI] = {0}; /// fine time 10 bit
|
||||
unsigned short traceLength[MAX_MULTI];
|
||||
|
||||
tree->Branch("evID", &evID, "event_ID/l");
|
||||
tree->Branch("multi", &multi, "multi/i");
|
||||
tree->Branch("sn", sn, "sn[multi]/s");
|
||||
tree->Branch("ch", ch, "ch[multi]/s");
|
||||
tree->Branch("e", e, "e[multi]/s");
|
||||
tree->Branch("e2", e2, "e2[multi]/s");
|
||||
tree->Branch("e_t", e_t, "e_timestamp[multi]/l");
|
||||
tree->Branch("e_f", e_f, "e_fineTime[multi]/s");
|
||||
tree->Branch("traceLength", traceLength, "traceLength[multi]/s");
|
||||
|
||||
TClonesArray * arrayTrace = nullptr;
|
||||
TGraph * trace = nullptr;
|
||||
|
||||
if( traceOn ) {
|
||||
arrayTrace = new TClonesArray("TGraph");
|
||||
tree->Branch("trace", arrayTrace, 2560000);
|
||||
arrayTrace->BypassStreamer();
|
||||
}
|
||||
|
||||
//*======================================= Open files
|
||||
printf("========================================= Open files & Build Events.\n");
|
||||
|
||||
const short nGroup = fileGroupList.size();
|
||||
std::vector<Hit> hitList[nGroup];
|
||||
|
||||
FSUReader ** reader = new FSUReader * [nGroup];
|
||||
ulong ID[nGroup];
|
||||
for( short i = 0; i < nGroup; i++){
|
||||
std::vector<std::string> fList;
|
||||
for( size_t j = 0; j < fileGroupList[i].size(); j++){
|
||||
fList.push_back( fileGroupList[i][j].fileName );
|
||||
}
|
||||
reader[i] = new FSUReader(fList, 600, debug);
|
||||
hitList[i] = reader[i]->ReadBatch(batchSize, debug );
|
||||
reader[i]->PrintHitListInfo(&hitList[i], "hitList-" + std::to_string(reader[i]->GetSN()));
|
||||
ID[i] = 0;
|
||||
if( debug ) {
|
||||
|
||||
for( size_t p = 0; p < 10; p ++ ){
|
||||
if( hitList[i].size() <= p ) break;
|
||||
hitList[i][p].Print();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long long tStart = 0;
|
||||
unsigned long long tEnd = 0;
|
||||
|
||||
//find earliest time group;
|
||||
unsigned long long t0 = -1;
|
||||
short g0 = 0 ;
|
||||
for( short i = 0; i < nGroup; i++){
|
||||
if( hitList[i].size() == 0 ) continue;
|
||||
if( hitList[i][0].timestamp < t0 ) {
|
||||
t0 = hitList[i][0].timestamp;
|
||||
g0 = i;
|
||||
}
|
||||
}
|
||||
tStart = t0;
|
||||
if( debug ) printf("First timestamp is %llu, group : %u\n", t0, g0);
|
||||
|
||||
int nFileFinished = 0;
|
||||
multi = 0;
|
||||
evID = 0;
|
||||
std::vector<Hit> events;
|
||||
|
||||
unsigned long long hitProcessed = 0;
|
||||
|
||||
do{
|
||||
|
||||
//*============= Build events from hitList[i]
|
||||
if( debug ) printf("################################ ev build %llu \n", evID);
|
||||
events.clear();
|
||||
|
||||
for( short i = 0; i < nGroup; i++){
|
||||
short ig = (i + g0 ) % nGroup;
|
||||
|
||||
if( hitList[ig].size() == 0 ) continue;
|
||||
|
||||
//chekc if reached the end of hitList
|
||||
if( ID[ig] >= hitList[ig].size() ) {
|
||||
hitList[ig] = reader[ig]->ReadBatch(batchSize, debug + 1);
|
||||
if( debug ) reader[ig]->PrintHitListInfo( &hitList[ig], "hitList-" + std::to_string(ig));
|
||||
ID[ig] = 0;
|
||||
if( hitList[ig].size() == 0 ) continue;
|
||||
}
|
||||
|
||||
if( timeWindow >= 0 ){
|
||||
|
||||
do{
|
||||
|
||||
if( (long int)(hitList[ig].at(ID[ig]).timestamp - t0) <= timeWindow ){
|
||||
events.push_back(hitList[ig].at(ID[ig]));
|
||||
ID[ig] ++;
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
|
||||
//check if reached the end of hitList
|
||||
if( ID[ig] >= hitList[ig].size() ) {
|
||||
hitList[ig] = reader[ig]->ReadBatch(batchSize, debug);
|
||||
if( debug ) reader[ig]->PrintHitListInfo( &hitList[ig], "hitList-" + std::to_string(ig));
|
||||
ID[ig] = 0;
|
||||
if( hitList[ig].size() == 0 ) break;
|
||||
}
|
||||
|
||||
}while(ID[ig] < hitList[ig].size());
|
||||
|
||||
}else{
|
||||
events.push_back(hitList[ig].at(ID[ig]));
|
||||
ID[ig] ++;
|
||||
}
|
||||
|
||||
if( timeWindow < 0) break;
|
||||
|
||||
}
|
||||
|
||||
if( events.size() > 1 ){
|
||||
std::sort(events.begin(), events.end(), [](const Hit& a, const Hit& b) {
|
||||
return a.timestamp < b.timestamp;
|
||||
});
|
||||
}
|
||||
|
||||
tEnd = events.back().timestamp;
|
||||
|
||||
hitProcessed += events.size();
|
||||
if( hitProcessed % 10000 == 0 ) printf("hit Porcessed %llu/%llu hit....%.2f%%\n\033[A\r", hitProcessed, totalHitCount, hitProcessed*100./totalHitCount);
|
||||
|
||||
|
||||
multi = events.size() ;
|
||||
if( events.size() >= MAX_MULTI ) {
|
||||
printf("event %lld has size = %d > MAX_MULTI = %d\n", evID, multi, MAX_MULTI);
|
||||
multi = MAX_MULTI;
|
||||
}
|
||||
if( debug ) printf("=================================== filling data | %u \n", multi);
|
||||
for( size_t p = 0; p < multi ; p ++ ) {
|
||||
if( debug ) {printf("%4zu | ", p); events[p].Print();}
|
||||
|
||||
sn[p] = events[p].sn;
|
||||
ch[p] = events[p].ch;
|
||||
e[p] = events[p].energy;
|
||||
e2[p] = events[p].energy2;
|
||||
e_t[p] = events[p].timestamp;
|
||||
e_f[p] = events[p].fineTime;
|
||||
|
||||
traceLength[p] = events[p].traceLength;
|
||||
if( traceOn ){
|
||||
trace = (TGraph *) arrayTrace->ConstructedAt(multi, "C");
|
||||
trace->Clear();
|
||||
for( int hh = 0; hh < traceLength[multi]; hh++){
|
||||
trace->SetPoint(hh, hh, events[p].trace[hh]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outRootFile->cd();
|
||||
tree->Fill();
|
||||
// tree->Write();
|
||||
multi = 0;
|
||||
evID ++;
|
||||
|
||||
///===================== find the next first timestamp
|
||||
t0 = -1;
|
||||
g0 = -1;
|
||||
|
||||
for( int i = 0; i < nGroup; i++) {
|
||||
if( hitList[i].size() == 0 ) continue;
|
||||
if( hitList[i][ID[i]].timestamp < t0 ) {
|
||||
t0 = hitList[i][ID[i]].timestamp;
|
||||
g0 = i;
|
||||
}
|
||||
}
|
||||
if( debug ) printf("Next First timestamp is %llu, group : %u\n", t0, g0);
|
||||
|
||||
//*=============
|
||||
nFileFinished = 0;
|
||||
for( int i = 0 ; i < nGroup; i++) {
|
||||
if( hitList[i].size() == 0 ) {
|
||||
nFileFinished ++;
|
||||
continue;
|
||||
}else{
|
||||
if( ID[i] >= hitList[i].size( )) {
|
||||
hitList[i] = reader[i]->ReadBatch(batchSize, debug);
|
||||
ID[i] = 0;
|
||||
if( hitList[i].size() == 0 ) nFileFinished ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( debug > 1 ) printf("========== nFileFinished : %d\n", nFileFinished);
|
||||
|
||||
}while( nFileFinished < nGroup);
|
||||
|
||||
tree->Write();
|
||||
|
||||
uInt runEndTime = getTime_us();
|
||||
double runTime = (runEndTime - runStartTime) * 1e-6;
|
||||
printf("========================================= finished.\n");
|
||||
printf(" event building time = %.2f sec = %.2f min\n", runTime, runTime/60.);
|
||||
printf(" total events built = %llu by event builder (%llu in tree)\n", evID, tree->GetEntriesFast());
|
||||
double tDuration_sec = (tEnd - tStart) * 1e-9;
|
||||
printf(" first timestamp = %20llu ns\n", tStart);
|
||||
printf(" last timestamp = %20llu ns\n", tEnd);
|
||||
printf(" total data duration = %.2f sec = %.2f min\n", tDuration_sec, tDuration_sec/60.);
|
||||
printf("==============> saved to %s \n", outFileName.Data());
|
||||
|
||||
TMacro info;
|
||||
info.AddLine(Form("tStart= %20llu ns",tStart));
|
||||
info.AddLine(Form(" tEnd= %20llu ns",tEnd));
|
||||
info.Write("info");
|
||||
|
||||
outRootFile->Close();
|
||||
|
||||
for( int i = 0; i < nGroup; i++) delete reader[i];
|
||||
delete [] reader;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
491
Armory/HistPlotter.h
Normal file
491
Armory/HistPlotter.h
Normal file
|
|
@ -0,0 +1,491 @@
|
|||
#ifndef HISTPLOTTER_H
|
||||
#define HISTPLOTTER_H
|
||||
#include <TCanvas.h>
|
||||
#include <TROOT.h>
|
||||
#include <TSystem.h>
|
||||
#include <TStyle.h>
|
||||
#include <iostream>
|
||||
#include <TFile.h>
|
||||
#include <TMemFile.h>
|
||||
#include <TH1.h>
|
||||
#include <TH2.h>
|
||||
#include <TCutG.h>
|
||||
#include <signal.h>
|
||||
#include <cstdlib>
|
||||
#include <utility>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
#include <TGraphErrors.h>
|
||||
|
||||
class HistPlotter
|
||||
{
|
||||
private:
|
||||
long long barrier_count, barrier_limit; // meant to keep track of how often to call FillN() on histograms
|
||||
enum
|
||||
{
|
||||
TFILE,
|
||||
TMEMFILE
|
||||
} filetype;
|
||||
std::map<std::string, TObject *> oMap; //!< Maps std::string to all TH1, TH2 objects in the class
|
||||
std::unordered_map<std::string, TObject *> cutsMap; //!< Maps std::string to TCutG objects held by the class
|
||||
std::set<std::string> folderList; //!< List of all folder names used to nest objects
|
||||
std::unordered_map<TObject *, std::string> foldersForObjects; //!< Map that returns the folder corresponding to the object whose pointer is specified
|
||||
TFile *ofile = nullptr; //!< TFile pointer for the output file
|
||||
TMemFile *omfile = nullptr; //!< TFile pointer for the output memfile
|
||||
|
||||
// Caches to permit FillN() calls
|
||||
std::map<std::string, std::vector<double>> onedimcache;
|
||||
std::map<std::string, std::pair<std::vector<double>, std::vector<double>>> twodimcache;
|
||||
inline void FillN_All_Histograms();
|
||||
|
||||
public:
|
||||
HistPlotter(std::string outfile, std::string type);
|
||||
inline void FlushToDisk(int integral); //!< Writes all objects to file before closing, nesting objects in folders as is found necessary
|
||||
inline void PrintObjects(); //!< Dump objects to std::cout for inspection
|
||||
inline void ReadCuts(std::string);
|
||||
inline TCutG *FindCut(std::string cut)
|
||||
{
|
||||
return static_cast<TCutG *>(cutsMap.at(cut));
|
||||
}
|
||||
inline void set_barrier_limit(long long limit) { barrier_limit = limit; }
|
||||
inline void barrier_increment()
|
||||
{
|
||||
barrier_count++;
|
||||
if (barrier_count == barrier_limit)
|
||||
{
|
||||
FillN_All_Histograms();
|
||||
barrier_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline void force_flush_caches()
|
||||
{
|
||||
FillN_All_Histograms();
|
||||
barrier_count = 0;
|
||||
}
|
||||
/*! \fn void FindCut()
|
||||
\brief
|
||||
- Searches for a cut by name 'cut' in the internal list of cuts 'cutsMap'. Ugly fails (via unresolved at()) if such a cut isn't found.
|
||||
\param filename - name of the plainxtext file containing the cut file locations and identifiers
|
||||
\return Pointer to the TCutG object that matches the name. Very useful to use this as plotter.FindCut("protonbarrelpid")->IsInside(deltaE, E) for instance.
|
||||
*/
|
||||
|
||||
inline void SetNewTitle(std::string name, std::string title)
|
||||
{
|
||||
auto result = oMap.find(name); // result is an iterator
|
||||
if (result == oMap.end())
|
||||
return; // no warnings, could be changed in future
|
||||
else
|
||||
static_cast<TNamed *>(oMap.at(name))->SetTitle(title.c_str()); // set new title
|
||||
}
|
||||
|
||||
// Smart functions that create a new histogram if it doesn't exist.
|
||||
inline void FillGraph(const std::string &name, float valuex, float valuey, float errx = 0, float erry = 0);
|
||||
inline void Fill1D(const std::string &name, int nbinsx, float xlow, float xhigh, float value);
|
||||
inline void Fill2D(const std::string &name, int nbinsx, float xlow, float xhigh, int nbinsy, float ylow, float yhigh, float valuex, float valuey);
|
||||
inline void Fill1D(const std::string &name, int nbinsx, float xlow, float xhigh, float value, const std::string &folder);
|
||||
inline void Fill2D(const std::string &name, int nbinsx, float xlow, float xhigh, int nbinsy, float ylow, float yhigh, float valuex, float valuey, const std::string &folder);
|
||||
// TObject* findObject(std::string key);
|
||||
};
|
||||
|
||||
HistPlotter::HistPlotter(std::string outfile, std::string type = "")
|
||||
{
|
||||
/*!
|
||||
\brief Constructor. Opens a TFile instance with the specified filename
|
||||
\param outfile : std::string that holds the desired output ROOT filename
|
||||
\return None
|
||||
*/
|
||||
if (type == "" || type == "TFILE")
|
||||
{
|
||||
ofile = new TFile(outfile.c_str(), "recreate");
|
||||
filetype = TFILE;
|
||||
}
|
||||
else if (type == "TMEMFILE")
|
||||
{
|
||||
omfile = new TMemFile(outfile.c_str(), "recreate");
|
||||
filetype = TMEMFILE;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Unknown type " << type << " specified for HistPlotter (use \"TFILE\" or \"TMEMFILE\"), using default \"TFILE\" " << std::endl;
|
||||
ofile = new TFile(outfile.c_str(), "recreate");
|
||||
filetype = TFILE;
|
||||
}
|
||||
barrier_count = 0;
|
||||
barrier_limit = 1000;
|
||||
}
|
||||
|
||||
void HistPlotter::FillN_All_Histograms()
|
||||
{
|
||||
for (auto it = oMap.begin(); it != oMap.end(); it++)
|
||||
{
|
||||
// it->first is std::string 'name', it->second is the TObject
|
||||
if (it->second->InheritsFrom("TH1F"))
|
||||
{
|
||||
// FillN(size, array-of-doubles, array-of-weights); //we set array-of-weights to (1,1,1,.. (size)
|
||||
static_cast<TH1F *>(it->second)->FillN(onedimcache[it->first].size(), // size
|
||||
onedimcache[it->first].data(), // array
|
||||
std::vector<double>(onedimcache[it->first].size(), 1.0).data()); // weight of ones
|
||||
onedimcache[it->first].clear();
|
||||
}
|
||||
else if (it->second->InheritsFrom("TH2F"))
|
||||
{
|
||||
// FillN(size, array-of-doubles, array-of-weights); //we set array-of-weights to (1,1,1,.. (size))
|
||||
static_cast<TH2F *>(it->second)->FillN(twodimcache[it->first].first.size(), // size
|
||||
twodimcache[it->first].first.data(), // x array
|
||||
twodimcache[it->first].second.data(), // y array
|
||||
std::vector<double>(twodimcache[it->first].first.size(), 1.0).data()); // weight of ones
|
||||
twodimcache[it->first].first.clear();
|
||||
twodimcache[it->first].second.clear();
|
||||
}
|
||||
}
|
||||
std::cout << "." << std::endl;
|
||||
}
|
||||
|
||||
void HistPlotter::FlushToDisk(int min_integral = 0)
|
||||
{
|
||||
/*! \fn void FlushToDisk()
|
||||
\brief Function that can be used at any point to exit smoothly by saving all ROOT objects in memory
|
||||
to the output file before closing it. Obeys the binding of histograms to separate folders, if so specified.
|
||||
\return No return -- void
|
||||
*/
|
||||
if (filetype == TMEMFILE && omfile)
|
||||
{
|
||||
std::cout << "Not flushing a TMemfile .. exiting .." << std::endl;
|
||||
delete omfile;
|
||||
return;
|
||||
}
|
||||
if (ofile->IsZombie() || !ofile)
|
||||
{
|
||||
std::cerr << "Output file is zombie, finishing up without writing to disk!" << std::endl;
|
||||
return;
|
||||
}
|
||||
FillN_All_Histograms();
|
||||
for (auto it = oMap.begin(); it != oMap.end(); it++)
|
||||
{
|
||||
// omap maps: name(first) to object address(second).
|
||||
// foldersForObjects maps: object address(first) to foldername(second)
|
||||
auto result = foldersForObjects.find(it->second); // returns <TObject* histogram,std::string foldername> pair if found
|
||||
if (result != foldersForObjects.end())
|
||||
{ // we try to create folder if needed and cd to it
|
||||
ofile->mkdir(result->second.c_str(), "", kTRUE); // args: name, title, returnExistingDirectory
|
||||
ofile->cd(result->second.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
ofile->cd(); // toplevel for all default histograms. Default setting
|
||||
}
|
||||
if (((TH1F *)it->second)->Integral() > min_integral)
|
||||
it->second->Write();
|
||||
}
|
||||
|
||||
// Create a directory for all cuts, and save all cuts in them
|
||||
ofile->mkdir("gCUTS", "", kTRUE);
|
||||
ofile->cd("gCUTS");
|
||||
for (auto it = cutsMap.begin(); it != cutsMap.end(); it++)
|
||||
{
|
||||
(static_cast<TNamed *>(it->second))->SetName(it->first.c_str());
|
||||
it->second->Write();
|
||||
}
|
||||
ofile->Close();
|
||||
std::cout << "Wrote " << oMap.size() << " histograms to TFile " << std::string(ofile->GetName()) << std::endl;
|
||||
}
|
||||
|
||||
void HistPlotter::FillGraph(const std::string &name, float valuex, float valuey, float errx, float erry)
|
||||
{
|
||||
/*! \fn void FillGraph()
|
||||
\brief
|
||||
- Creates a TGraphError in memory with name 'name' if it doesn't exist, and fills it with valuex, valuey
|
||||
- Writes present state to disk and fails with return value -1 if the name clashes with another object that's not of type TGraph*
|
||||
|
||||
\param name name of the TGraph
|
||||
\param valuex The xvalue
|
||||
\param valuey The yvalue
|
||||
\param errx The x error
|
||||
\param erry The y error
|
||||
\return No return void
|
||||
*/
|
||||
auto result = oMap.find(name);
|
||||
if (result == oMap.end())
|
||||
{
|
||||
TGraphErrors *tempG = new TGraphErrors();
|
||||
tempG->SetName(name.c_str());
|
||||
oMap.insert(std::make_pair(name, static_cast<TObject *>(tempG)));
|
||||
}
|
||||
if (!oMap.at(name)->InheritsFrom("TGraphErrors"))
|
||||
{
|
||||
std::cerr << "Object " << name << " refers to something other than a TGraph*, not filling it hence!" << std::endl;
|
||||
std::cerr << "Abort.." << std::endl;
|
||||
FlushToDisk();
|
||||
exit(-1);
|
||||
}
|
||||
// static_cast<TGraphErrors*>(oMap.at(name))->AddPointError(valuex,valuey,errx,erry);
|
||||
}
|
||||
|
||||
void HistPlotter::Fill1D(const std::string &name, int nbinsx, float xlow, float xhigh, float value)
|
||||
{
|
||||
/*! \fn void Fill1D()
|
||||
\brief
|
||||
- Creates a TH1F in memory with name 'name' if it doesn't exist, and fills it with valuex, valuey
|
||||
- Writes present state to disk and fails with return value -1 if the name clashes with another object that's not of type TH1*
|
||||
|
||||
\param name name of the TH1F histogram
|
||||
\param nbinsx Number of bins in the histogram
|
||||
\param xlow Lower limit on x-axis
|
||||
\param xhigh Upper limit on x-axis
|
||||
\param value The bin corresponding to value in (nbinsx, xlow, xhigh) is incremented by 1
|
||||
\return No return void
|
||||
*/
|
||||
auto result = oMap.find(name); // result is an iterator
|
||||
if (result == oMap.end())
|
||||
{
|
||||
TH1F *temp1D = new TH1F(name.c_str(), name.c_str(), nbinsx, xlow, xhigh);
|
||||
oMap.insert(std::make_pair(name, static_cast<TObject *>(temp1D)));
|
||||
onedimcache.insert(std::make_pair(name, std::vector<double>()));
|
||||
onedimcache[name].reserve(16384);
|
||||
}
|
||||
else if (foldersForObjects.find(oMap.at(name)) != foldersForObjects.end())
|
||||
{ // shouldn't have a folder associated with it
|
||||
std::cerr << "Object " << name << " already registered at " << foldersForObjects[oMap[name]] << ", choose a different name for the histogram to be stored in toplevel .." << std::endl;
|
||||
}
|
||||
|
||||
// Check if the string 'name' maps to a 1D hist. If there's any other object by this name raise issue
|
||||
if (!oMap.at(name)->InheritsFrom("TH1F"))
|
||||
{
|
||||
std::cerr << "Object " << name << " refers to something other than a TH1*, not filling it hence!" << std::endl;
|
||||
std::cerr << "Abort.." << std::endl;
|
||||
FlushToDisk();
|
||||
exit(-1);
|
||||
}
|
||||
onedimcache[name].emplace_back(value);
|
||||
// static_cast<TH1F*>(oMap.at(name))->Fill(value);
|
||||
}
|
||||
|
||||
void HistPlotter::Fill1D(const std::string &name, int nbinsx, float xlow, float xhigh, float value, const std::string &foldername)
|
||||
{
|
||||
/*! \fn void Fill1D()
|
||||
\brief
|
||||
- Creates a TH1F in memory with name 'name' if it doesn't exist, and fills it with valuex, valuey
|
||||
- Writes present state to disk and fails with return value -1 if the name clashes with another object that's not of type TH1*
|
||||
- Remembers the foldername this particular histogram maps to, if provided. If not, defaults to toplevel.
|
||||
|
||||
\param name name of the TH1F histogram
|
||||
\param nbinsx Number of bins in the histogram
|
||||
\param xlow Lower limit on x-axis
|
||||
\param xhigh Upper limit on x-axis
|
||||
\param value The bin corresponding to value in (nbinsx, xlow, xhigh) is incremented by 1
|
||||
\param foldername Name of the folder to put this histogram into. Defaults to toplevel if left empty
|
||||
\return No return -- void
|
||||
*/
|
||||
|
||||
auto result = oMap.find(name); // result is an iterator
|
||||
if (result == oMap.end())
|
||||
{
|
||||
TH1F *temp1D = new TH1F(name.c_str(), name.c_str(), nbinsx, xlow, xhigh);
|
||||
oMap.insert(std::make_pair(name, static_cast<TObject *>(temp1D)));
|
||||
onedimcache.insert(std::make_pair(name, std::vector<double>()));
|
||||
onedimcache[name].reserve(16384);
|
||||
if (foldername != "")
|
||||
{
|
||||
if (folderList.find(foldername) == folderList.end())
|
||||
{
|
||||
folderList.insert(foldername);
|
||||
}
|
||||
foldersForObjects.insert(std::make_pair(static_cast<TObject *>(temp1D), foldername));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// object is present in map, but we enforce unique names
|
||||
// it must already have a folder attached to it
|
||||
if (foldersForObjects.find(oMap.at(name)) == foldersForObjects.end())
|
||||
{
|
||||
std::cerr << "Object " << name << " already registered at toplevel, choose a different name for the histogram to be stored in " << foldername << " folder .." << std::endl;
|
||||
}
|
||||
else if (foldersForObjects[oMap[name]] != foldername)
|
||||
{
|
||||
std::cerr << "Object " << name << " already registered at " << foldersForObjects[oMap[name]] << ", choose a different name for the histogram to be stored in " << foldername << " folder .." << std::endl;
|
||||
}
|
||||
}
|
||||
// Check if the string 'name' maps to a 1D hist. If there's any other object by this name raise issue
|
||||
if (!oMap.at(name)->InheritsFrom("TH1F"))
|
||||
{
|
||||
std::cerr << "Object " << name << " refers to something other than a TH1*, not filling it hence!" << std::endl;
|
||||
std::cerr << "Abort.." << std::endl;
|
||||
FlushToDisk();
|
||||
exit(-1);
|
||||
}
|
||||
onedimcache[name].emplace_back(value);
|
||||
// static_cast<TH1F*>(oMap.at(name))->Fill(value);
|
||||
}
|
||||
|
||||
void HistPlotter::Fill2D(const std::string &name, int nbinsx, float xlow, float xhigh, int nbinsy, float ylow, float yhigh, float valuex, float valuey)
|
||||
{
|
||||
/*! \fn void Fill2D()
|
||||
\brief
|
||||
- Creates a TH2F in memory with name 'name' if it doesn't exist, and fills it with valuex, valuey
|
||||
- Writes present state to disk and fails with return value -1 if the name clashes with another object that's not of type TH2*
|
||||
\param name name of the TH1F histogram
|
||||
\param nbinsx Number of xbins in the histogram
|
||||
\param xlow Lower limit on x-axis
|
||||
\param xhigh Upper limit on x-axis
|
||||
\param nbinsy Number of ybins in the histogram
|
||||
\param ylow Lower limit on y-axis
|
||||
\param yhigh Upper limit on y-axis
|
||||
\param valuex
|
||||
\param valuey The bin corresponding to (valuex, valuey) in (nbinsx, xlow, xhigh, ybinsx, ylow, yhigh) is incremented by 1
|
||||
\return No return -- void
|
||||
*/
|
||||
|
||||
auto result = oMap.find(name); // result is an iterator
|
||||
if (result == oMap.end())
|
||||
{
|
||||
TH2F *temp2D = new TH2F(name.c_str(), name.c_str(), nbinsx, xlow, xhigh, nbinsy, ylow, yhigh);
|
||||
oMap.insert(std::make_pair(name, static_cast<TObject *>(temp2D)));
|
||||
twodimcache.insert(std::make_pair(name, std::make_pair(std::vector<double>(), std::vector<double>())));
|
||||
twodimcache[name].first.reserve(16384);
|
||||
twodimcache[name].second.reserve(16384);
|
||||
}
|
||||
else if (foldersForObjects.find(oMap.at(name)) != foldersForObjects.end())
|
||||
{ // shouldn't have a folder associated with it
|
||||
std::cerr << "Object " << name << " already registered at " << foldersForObjects[oMap[name]] << ", choose a different name for the histogram to be stored in toplevel .." << std::endl;
|
||||
}
|
||||
|
||||
// Check if the string 'name' maps to a 1D hist. If there's any other object by this name raise issue
|
||||
if (!oMap.at(name)->InheritsFrom("TH2F"))
|
||||
{
|
||||
std::cerr << "Object " << name << " refers to something other than a TH2*, not filling it hence!" << std::endl;
|
||||
std::cerr << "Abort.." << std::endl;
|
||||
FlushToDisk();
|
||||
exit(-1);
|
||||
}
|
||||
twodimcache[name].first.emplace_back(valuex);
|
||||
twodimcache[name].second.emplace_back(valuey);
|
||||
// static_cast<TH2F*>(oMap.at(name))->Fill(valuex,valuey);
|
||||
}
|
||||
|
||||
void HistPlotter::Fill2D(const std::string &name, int nbinsx, float xlow, float xhigh, int nbinsy, float ylow, float yhigh, float valuex, float valuey, const std::string &foldername)
|
||||
{
|
||||
/*! \fn void Fill2D()
|
||||
\brief
|
||||
- Creates a TH2F in memory with name 'name' if it doesn't exist, and fills it with valuex, valuey
|
||||
- Writes present state to disk and fails with return value -1 if the name clashes with another object that's not of type TH2*
|
||||
- Remembers the foldername this particular histogram maps to, if provided. If not defaults to toplevel
|
||||
|
||||
\param name name of the TH1F histogram
|
||||
\param nbinsx Number of xbins in the histogram
|
||||
\param xlow Lower limit on x-axis
|
||||
\param xhigh Upper limit on x-axis
|
||||
\param nbinsy Number of ybins in the histogram
|
||||
\param ylow Lower limit on y-axis
|
||||
\param yhigh Upper limit on y-axis
|
||||
\param valuex
|
||||
\param valuey The bin corresponding to (valuex, valuey) in (nbinsx, xlow, xhigh, ybinsx, ylow, yhigh) is incremented by 1
|
||||
\param foldername Name of the folder to put this histogram into. Defaults to toplevel if left empty
|
||||
\return No return -- void
|
||||
*/
|
||||
|
||||
auto result = oMap.find(name); // result is an iterator
|
||||
if (result == oMap.end())
|
||||
{
|
||||
TH2F *temp2D = new TH2F(name.c_str(), name.c_str(), nbinsx, xlow, xhigh, nbinsy, ylow, yhigh);
|
||||
oMap.insert(std::make_pair(name, static_cast<TObject *>(temp2D)));
|
||||
twodimcache.insert(std::make_pair(name, std::make_pair(std::vector<double>(), std::vector<double>())));
|
||||
twodimcache[name].first.reserve(16384);
|
||||
twodimcache[name].second.reserve(16384);
|
||||
if (foldername != "")
|
||||
{
|
||||
if (folderList.find(foldername) == folderList.end())
|
||||
{
|
||||
folderList.insert(foldername);
|
||||
}
|
||||
foldersForObjects.insert(std::make_pair(static_cast<TObject *>(temp2D), foldername));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// object is present in map, but we enforce unique names
|
||||
// it must already have a folder attached to it
|
||||
if (foldersForObjects.find(oMap.at(name)) == foldersForObjects.end())
|
||||
{
|
||||
std::cerr << "Object " << name << " already registered at toplevel, choose a different name for the histogram to be stored in " << foldername << " folder .." << std::endl;
|
||||
}
|
||||
else if (foldersForObjects[oMap.at(name)] != foldername)
|
||||
{
|
||||
std::cerr << "Object " << name << " already registered at " << foldersForObjects[oMap[name]] << ", choose a different name for the histogram to be stored in " << foldername << " folder .." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the string 'name' maps to a 1D hist. If there's any other object by this name raise issue
|
||||
if (!oMap.at(name)->InheritsFrom("TH2F"))
|
||||
{
|
||||
std::cerr << "Object " << name << " refers to something other than a TH2*, not filling it hence!" << std::endl;
|
||||
std::cerr << "Abort.." << std::endl;
|
||||
FlushToDisk();
|
||||
exit(-1);
|
||||
}
|
||||
twodimcache[name].first.emplace_back(valuex);
|
||||
twodimcache[name].second.emplace_back(valuey);
|
||||
// static_cast<TH2F*>(oMap.at(name))->Fill(valuex,valuey);
|
||||
}
|
||||
|
||||
void HistPlotter::ReadCuts(std::string filename)
|
||||
{
|
||||
/*! \fn void ReadCuts()
|
||||
\brief Reads a list of cuts from a file. The file must have the format below, two columns
|
||||
- Column#1 - path to a file that contains a single TCutG object named "CUTG", the default name in ROOT.
|
||||
- Column#2 - The identifier name you plan to use in the code, like 'protonbarrelpid' or something, that will be searched by FindCut()
|
||||
\param filename name of the plainxtext file containing the cut file locations and identifiers
|
||||
\return No return -- void
|
||||
*/
|
||||
|
||||
std::ifstream infile;
|
||||
infile.open(filename);
|
||||
std::string cutfilename, cutname;
|
||||
for (std::string line; std::getline(infile, line);)
|
||||
{
|
||||
if (line.size() != 0 && line[0] == '#')
|
||||
; // don't do anything with '#' lines
|
||||
else
|
||||
{
|
||||
std::stringstream ss(line);
|
||||
ss >> cutfilename >> cutname;
|
||||
|
||||
TFile f(cutfilename.c_str());
|
||||
if (f.IsZombie())
|
||||
{
|
||||
std::cerr << "Cannot open cutfile " << cutfilename << " .. skipping.." << std::endl;
|
||||
continue;
|
||||
}
|
||||
TCutG *cut = (TCutG *)(f.Get("CUTG"));
|
||||
cutsMap.insert(std::make_pair(cutname, static_cast<TObject *>(cut)));
|
||||
f.Close();
|
||||
} // else
|
||||
} // for loop
|
||||
infile.close();
|
||||
}
|
||||
|
||||
void HistPlotter::PrintObjects()
|
||||
{
|
||||
/*
|
||||
void PrintObjects()
|
||||
Prints the contents of the unordered_maps oMap and cutsMap to facilitate debugging
|
||||
|
||||
*/
|
||||
std::cout << "Type | Name " << std::endl;
|
||||
std::cout << "---- | --------------------- " << std::endl;
|
||||
for (auto it = oMap.begin(); it != oMap.end(); it++)
|
||||
{
|
||||
std::cout << it->second->ClassName() << " | " << it->first << std::endl;
|
||||
}
|
||||
for (auto it = cutsMap.begin(); it != cutsMap.end(); it++)
|
||||
{
|
||||
std::cout << it->second->ClassName() << " | " << it->first << std::endl;
|
||||
}
|
||||
std::cout << "---- | --------------------- " << std::endl;
|
||||
}
|
||||
|
||||
#endif
|
||||
52
Armory/Hit.h
Normal file
52
Armory/Hit.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#ifndef Hit_H
|
||||
#define Hit_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
class Hit{
|
||||
public:
|
||||
unsigned short sn;
|
||||
uint8_t ch;
|
||||
unsigned short energy;
|
||||
unsigned short energy2;
|
||||
unsigned long long timestamp;
|
||||
unsigned short fineTime;
|
||||
bool pileUp;
|
||||
|
||||
unsigned short traceLength;
|
||||
std::vector<short> trace;
|
||||
|
||||
Hit(){
|
||||
Clear();
|
||||
}
|
||||
|
||||
void Clear(){
|
||||
sn = 0;
|
||||
ch = 0;
|
||||
energy = 0;
|
||||
energy2 = 0;
|
||||
timestamp = 0;
|
||||
fineTime = 0;
|
||||
traceLength = 0;
|
||||
pileUp = false;
|
||||
trace.clear();
|
||||
}
|
||||
|
||||
void Print(){
|
||||
printf("(%5d, %2d) %6d %16llu, %6d, %d, %5ld\n", sn, ch, energy, timestamp, fineTime, pileUp, trace.size());
|
||||
}
|
||||
|
||||
void PrintTrace(){
|
||||
for( unsigned short i = 0; i < traceLength; i++){
|
||||
printf("%3u | %6d \n", i, trace[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Define operator< for sorting
|
||||
bool operator<(const Hit& other) const {
|
||||
return timestamp < other.timestamp;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
111
Armory/Kinematics.cpp
Normal file
111
Armory/Kinematics.cpp
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
#include "Kinematics.h"
|
||||
//#include "elastcaller.h"
|
||||
//double Kinematics::getQval(double m1, double m2, double m3, double t1, double t3, double angle3)
|
||||
double Kinematics::getExc(double t3, double angle3)
|
||||
/*
|
||||
\brief Follows convention in Marion, 2013: (1 - beam, 2- target, 3-ejectile, 4-recoil)
|
||||
|
||||
m1 is beam, (typically heavy nucleus)
|
||||
m2 is 'd', (light target)
|
||||
m3 is 'p', (light ejectile mass)
|
||||
t1 is beam kinetic energy,
|
||||
\param t3 lab-kinetic-energy in MeV
|
||||
\param angle3 lab-angle in deg of detected proton (if d,p) or other charged particle
|
||||
\return Excitation energy of the heavy-recoil nucleus in MeV
|
||||
*/
|
||||
{
|
||||
double m1 = m_A;
|
||||
double m2 = m_d;
|
||||
double m3 = m_p;
|
||||
double m4 = m_B;
|
||||
double t1 = E_beam;
|
||||
//t1 = slowitdown("75Ga",t1,"1(12C)2(2H)",1.2);
|
||||
|
||||
m1 *= u_MeV;
|
||||
m2 *= u_MeV;
|
||||
m3 *= u_MeV;
|
||||
m4 *= u_MeV;
|
||||
|
||||
double e1 = m1 + t1;
|
||||
double e3 = m3 + t3;
|
||||
ET = t1 + m1 + m2;
|
||||
double p1 = TMath::Sqrt(t1*t1 + 2*m1*t1);
|
||||
double p3 = TMath::Sqrt(t3*t3 + 2*m3*t3);
|
||||
double cosTheta = TMath::Cos(angle3*TMath::Pi()/180.);
|
||||
// return m1+m2-m3-TMath::Sqrt(m1*m1 + m2*m2 + m3*m3 + 2.*m2*e1 - 2.*e3*(e1+m2)+ 2.*p1*p3*cosTheta);
|
||||
double Q = m1+m2-m3-TMath::Sqrt(m1*m1 + m2*m2 + m3*m3 + 2.*m2*e1 - 2.*e3*(e1+m2)+ 2.*p1*p3*cosTheta);
|
||||
|
||||
Qx = Q;
|
||||
T4 = ET - e3 - (m1+m2-m3-Q);
|
||||
//T4 = slowitdown("75Ga",T4,"1(12C)2(2H)",1.2);
|
||||
P4 = TMath::Sqrt(T4*T4 + 2*m4*T4);
|
||||
|
||||
//this angle will not be affected by eloss
|
||||
theta4 = (180./M_PI)*TMath::ASin((p3/P4)*TMath::Sin(angle3*M_PI/180.));
|
||||
|
||||
// T4-=16.5; //eloss in about 1.4 mg CD2
|
||||
T4-=15.5; //eloss in 1.35 mg CD2 //TODO: actually degrade recoil/ejectiles in target
|
||||
// T4-=14.1; //eloss in about 1.2 mg CD2
|
||||
// T4-=31.7; //eloss in 2.7 mg CD2
|
||||
|
||||
//recalculate everything other than angle with lowered Kinetic energy
|
||||
P4 = TMath::Sqrt(T4*T4 + 2*m4*T4);
|
||||
gamma4 = T4/m4+1.;
|
||||
beta4 = TMath::Sqrt(1. - 1./(gamma4*gamma4));
|
||||
//beta4 = TMath::Sqrt((P4*P4)/(P4*P4 + m4*m4));
|
||||
theta4 = (180./M_PI)*TMath::ASin((p3/P4)*TMath::Sin(angle3*M_PI/180.));
|
||||
|
||||
return Q0 - Q;//Q0 = Q + Exc
|
||||
|
||||
}
|
||||
|
||||
double Kinematics::getBeta4(double t3, double angle3)
|
||||
/*
|
||||
\brief Follows convention in Marion, 2013: (1 - beam, 2- target, 3-ejectile, 4-recoil)
|
||||
|
||||
m1 is beam, (typically heavy nucleus)
|
||||
m2 is 'd', (light target)
|
||||
m3 is 'p', (light ejectile mass)
|
||||
t1 is beam kinetic energy,
|
||||
\param t3 lab-kinetic-energy in MeV
|
||||
\param angle3 lab-angle in deg of detected proton (if d,p) or other charged particle
|
||||
\return doppler-shift beta (=v/c) of the heavy-recoil nucleus, calls getExc() to fill the value
|
||||
*/
|
||||
{
|
||||
getExc(t3, angle3);
|
||||
return beta4;
|
||||
}
|
||||
|
||||
double Kinematics::getBrho(double t3, double angle3, double charge_state) {
|
||||
/*
|
||||
\brief Follows convention in Marion, 2013: (1 - beam, 2- target, 3-ejectile, 4-recoil)
|
||||
|
||||
m1 is beam, (typically heavy nucleus)
|
||||
m2 is 'd', (light target)
|
||||
m3 is 'p', (light ejectile mass)
|
||||
t1 is beam kinetic energy,
|
||||
\param t3 lab-kinetic-energy in MeV
|
||||
\param angle3 lab-angle in deg of detected proton (if d,p) or other charged particle
|
||||
\param charge_state charge state of the intended nucleus, in units of elementary charge (= +1 for H+, +2 for He2+ etc).
|
||||
\return b-rho value, generated from P4*3.3359e-3/charge_state where P4 is 4momentum of heavy-recoil calculated from orruba kinematics
|
||||
*/
|
||||
getExc(t3,angle3);
|
||||
return P4*3.3359e-3/charge_state;
|
||||
}
|
||||
|
||||
double Kinematics::getTheta4(double t3, double angle3) {
|
||||
/*
|
||||
\brief Follows convention in Marion, 2013: (1 - beam, 2- target, 3-ejectile, 4-recoil)
|
||||
|
||||
m1 is beam, (typically heavy nucleus)
|
||||
m2 is 'd', (light target)
|
||||
m3 is 'p', (light ejectile mass)
|
||||
t1 is beam kinetic energy,
|
||||
\param t3 lab-kinetic-energy in MeV
|
||||
\param angle3 lab-angle in deg of detected proton (if d,p) or other charged particle
|
||||
\param charge_state charge state of the intended nucleus, in units of elementary charge (= +1 for H+, +2 for He2+ etc).
|
||||
\return lab theta value of heavy recoil in degrees
|
||||
*/
|
||||
getExc(t3,angle3);
|
||||
return theta4;
|
||||
}
|
||||
207
Armory/Kinematics.h
Normal file
207
Armory/Kinematics.h
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
#ifndef KINEMATICS_H
|
||||
#define KINEMATICS_H
|
||||
#include <TMath.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <TVector3.h>
|
||||
const double u_MeV = 931.49410372; //u in MeV
|
||||
|
||||
class Kinematics {
|
||||
public:
|
||||
/*
|
||||
A(d,p)B is used as template, with A being beam, and p being ejectile.
|
||||
Always, make m3 the thing you detect, and m1 the beam
|
||||
*/
|
||||
double m_A, m_d, m_p, m_B;
|
||||
double E_beam;
|
||||
|
||||
/**
|
||||
* @{ \name List of funny impossible default values
|
||||
*/
|
||||
/**
|
||||
* \brief Default values used for all the physics values
|
||||
*/
|
||||
double Q0=-9999, Qx=-9999;
|
||||
double P4=-9999, E4=-9999, T4=-9999; //heavy recoil momentum, totE, KE
|
||||
double P3=-9999, E3=-9999, T3=-9999; //light recoil
|
||||
double ET=-9999;
|
||||
double gamma4=-9999, beta4=-9999, theta4=-9999; //theta=heavy-recoil lab angle
|
||||
double brho=-9999;
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
Kinematics(double m1, double m2, double m3, double m4, double ebeam) {
|
||||
/*
|
||||
A(d,p)B is used as template, with A being beam, and p being ejectile.
|
||||
Always, make m3 the thing you detect, m2 the target, and m1 the beam
|
||||
|
||||
ebeam is in MeV/u, all others are in amu
|
||||
*/
|
||||
m_A = m1;
|
||||
m_d = m2;
|
||||
m_p = m3, m_B = m4, E_beam = ebeam*m_A;
|
||||
Q0 = (- m_B - m_p + m_d + m_A)*u_MeV;
|
||||
}
|
||||
Kinematics() {}
|
||||
|
||||
void setValues(double m1, double m2, double m3, double m4, double ebeam) {
|
||||
/*
|
||||
Can be used to 'live update' say the beam energy in the case of active target detectors.
|
||||
*/
|
||||
|
||||
m_A = m1;
|
||||
m_d = m2;
|
||||
m_p = m3, m_B = m4, E_beam = ebeam*m_A;
|
||||
Q0 = (- m_B - m_p + m_d + m_A)*u_MeV;
|
||||
//std::cout << "Q0 MeV: " << Q0 << std::endl;
|
||||
}
|
||||
|
||||
void setEBeam(double Ebeam) {E_beam = Ebeam;}
|
||||
|
||||
double getBeta4(double t3, double angle3);
|
||||
double getTheta4(double t3, double angle3);
|
||||
double getBrho(double t3, double angle3, double charge_state);
|
||||
double getExc(double t3, double angle3); //t3 is proton energy detected in ORRUBA, angle3 is proton angle in degrees
|
||||
|
||||
double getBeta4_fromvec(double t3, const TVector3 &pos, const TVector3 &origin) {
|
||||
TVector3 local = pos - origin; //position w.r.t origin
|
||||
float angle = local.Theta()*180./M_PI;
|
||||
return getBeta4(t3, angle);
|
||||
}
|
||||
|
||||
double getExc_fromvec(double t3, const TVector3 &pos, const TVector3 &origin) {
|
||||
TVector3 local = pos - origin; //position w.r.t origin
|
||||
float angle = local.Theta()*180./M_PI;
|
||||
return getExc(t3, angle);
|
||||
}
|
||||
|
||||
void setValuesFromFile(const std::string& filename) {
|
||||
(void) filename;
|
||||
/*std::ifstream in;
|
||||
in.open(filename);
|
||||
if(!in) {
|
||||
std::cerr<< "File not open at " << filename << std::endl;
|
||||
return;
|
||||
}
|
||||
for(std::string line; std::getline(in, line); ) {
|
||||
if(line.size()!=0 && line[0]=='#')
|
||||
; //don't do anything with '#' lines
|
||||
else {
|
||||
std::stringstream ss(line);
|
||||
ss>>m_A>>m_d>>m_p>>m_B>>E_beam;
|
||||
}
|
||||
}
|
||||
in.close();*/
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
//double Kinematics::getQval(double m1, double m2, double m3, double t1, double t3, double angle3)
|
||||
double Kinematics::getExc(double t3, double angle3)
|
||||
/*
|
||||
\brief Follows convention in Marion, 2013: (1 - beam, 2- target, 3-ejectile, 4-recoil)
|
||||
|
||||
m1 is beam, (typically heavy nucleus)
|
||||
m2 is 'd', (light target)
|
||||
m3 is 'p', (light ejectile mass)
|
||||
t1 is beam kinetic energy,
|
||||
|
||||
All calculations are done here, and other wrapper functions written make derived quantities from stuff calculated here.
|
||||
|
||||
\param t3 lab-kinetic-energy in MeV
|
||||
\param angle3 lab-angle in deg of detected proton (if d,p) or other charged particle
|
||||
\return Excitation energy of the heavy-recoil nucleus in MeV
|
||||
*/
|
||||
{
|
||||
double m1 = m_A;
|
||||
double m2 = m_d;
|
||||
double m3 = m_p;
|
||||
double m4 = m_B;
|
||||
double t1 = E_beam;
|
||||
|
||||
m1 *= u_MeV;
|
||||
m2 *= u_MeV;
|
||||
m3 *= u_MeV;
|
||||
m4 *= u_MeV;
|
||||
|
||||
double e1 = m1 + t1;
|
||||
double e3 = m3 + t3;
|
||||
ET = t1 + m1 + m2;
|
||||
double p1 = TMath::Sqrt(t1*t1 + 2*m1*t1);
|
||||
double p3 = TMath::Sqrt(t3*t3 + 2*m3*t3);
|
||||
double cosTheta = TMath::Cos(angle3*TMath::Pi()/180.);
|
||||
// return m1+m2-m3-TMath::Sqrt(m1*m1 + m2*m2 + m3*m3 + 2.*m2*e1 - 2.*e3*(e1+m2)+ 2.*p1*p3*cosTheta);
|
||||
double Q = m1+m2-m3-TMath::Sqrt(m1*m1 + m2*m2 + m3*m3 + 2.*m2*e1 - 2.*e3*(e1+m2)+ 2.*p1*p3*cosTheta);
|
||||
|
||||
Qx = Q;
|
||||
//Recoil properties just in case it's useful
|
||||
T4 = ET - e3 - (m1+m2-m3-Q);
|
||||
P4 = TMath::Sqrt(T4*T4 + 2*m4*T4);
|
||||
|
||||
//this angle will not be affected by eloss
|
||||
theta4 = (180./M_PI)*TMath::ASin((p3/P4)*TMath::Sin(angle3*M_PI/180.));
|
||||
|
||||
//recalculate everything other than angle with lowered Kinetic energy
|
||||
P4 = TMath::Sqrt(T4*T4 + 2*m4*T4);
|
||||
gamma4 = T4/m4+1.;
|
||||
beta4 = TMath::Sqrt(1. - 1./(gamma4*gamma4));
|
||||
theta4 = (180./M_PI)*TMath::ASin((p3/P4)*TMath::Sin(angle3*M_PI/180.));
|
||||
|
||||
return Q0 - Q;//Q0 = Q + Exc
|
||||
|
||||
}
|
||||
|
||||
double Kinematics::getBeta4(double t3, double angle3)
|
||||
/*
|
||||
\brief Follows convention in Marion, 2013: (1 - beam, 2- target, 3-ejectile, 4-recoil)
|
||||
|
||||
m1 is beam, (typically heavy nucleus)
|
||||
m2 is 'd', (light target)
|
||||
m3 is 'p', (light ejectile mass)
|
||||
t1 is beam kinetic energy,
|
||||
\param t3 lab-kinetic-energy in MeV
|
||||
\param angle3 lab-angle in deg of detected proton (if d,p) or other charged particle
|
||||
\return doppler-shift beta (=v/c) of the heavy-recoil nucleus, calls getExc() to fill the value
|
||||
*/
|
||||
{
|
||||
getExc(t3, angle3);
|
||||
return beta4;
|
||||
}
|
||||
|
||||
double Kinematics::getBrho(double t3, double angle3, double charge_state) {
|
||||
/*
|
||||
\brief Follows convention in Marion, 2013: (1 - beam, 2- target, 3-ejectile, 4-recoil)
|
||||
|
||||
m1 is beam, (typically heavy nucleus)
|
||||
m2 is 'd', (light target)
|
||||
m3 is 'p', (light ejectile mass)
|
||||
t1 is beam kinetic energy,
|
||||
\param t3 lab-kinetic-energy in MeV
|
||||
\param angle3 lab-angle in deg of detected proton (if d,p) or other charged particle
|
||||
\param charge_state charge state of the intended nucleus, in units of elementary charge (= +1 for H+, +2 for He2+ etc).
|
||||
\return b-rho value, generated from P4*3.3359e-3/charge_state where P4 is 4momentum of heavy-recoil calculated from orruba kinematics
|
||||
*/
|
||||
getExc(t3,angle3);
|
||||
return P4*3.3359e-3/charge_state;
|
||||
}
|
||||
|
||||
double Kinematics::getTheta4(double t3, double angle3) {
|
||||
/*
|
||||
\brief Follows convention in Marion, 2013: (1 - beam, 2- target, 3-ejectile, 4-recoil)
|
||||
|
||||
m1 is beam, (typically heavy nucleus)
|
||||
m2 is 'd', (light target)
|
||||
m3 is 'p', (light ejectile mass)
|
||||
t1 is beam kinetic energy,
|
||||
\param t3 lab-kinetic-energy in MeV
|
||||
\param angle3 lab-angle in deg of detected proton (if d,p) or other charged particle
|
||||
\param charge_state charge state of the intended nucleus, in units of elementary charge (= +1 for H+, +2 for He2+ etc).
|
||||
\return lab theta value of heavy recoil in degrees
|
||||
*/
|
||||
getExc(t3,angle3);
|
||||
return theta4;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
674
Armory/LICENSE
Normal file
674
Armory/LICENSE
Normal file
|
|
@ -0,0 +1,674 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
|
|
@ -10,7 +10,7 @@ COPTS = -fPIC -DLINUX -g -O0 -Wall -std=c++17 -lpthread
|
|||
|
||||
ROOTLIBS = `root-config --cflags --glibs`
|
||||
|
||||
ALL = Mapper AnasenMS
|
||||
ALL = Mapper EventBuilder#AnasenMS
|
||||
|
||||
#########################################################################
|
||||
|
||||
|
|
@ -23,6 +23,10 @@ Mapper : Mapper.cpp ../mapping.h ClassDet.h
|
|||
@echo "--------- making Mapper"
|
||||
$(CC) $(COPTS) -o Mapper Mapper.cpp $(ROOTLIBS)
|
||||
|
||||
AnasenMS : constant.h Isotope.h ClassTransfer.h ClassSX3.h ClassPW.h ClassAnasen.h anasenMS.cpp
|
||||
@echo "--------- making ANASEN Monte Carlo"
|
||||
$(CC) $(COPTS) -o AnasenMS anasenMS.cpp $(ROOTLIBS)
|
||||
# AnasenMS : constant.h Isotope.h ClassTransfer.h ClassSX3.h ClassPW.h ClassAnasen.h anasenMS.cpp
|
||||
# @echo "--------- making ANASEN Monte Carlo"
|
||||
# $(CC) $(COPTS) -o AnasenMS anasenMS.cpp $(ROOTLIBS)
|
||||
|
||||
EventBuilder : EventBuilder.cpp ClassData.h fsuReader.h Hit.h
|
||||
@echo "--------- making EventBuilder"
|
||||
$(CC) $(COPTS) -o EventBuilder EventBuilder.cpp $(ROOTLIBS)
|
||||
|
|
|
|||
|
|
@ -11,12 +11,14 @@
|
|||
#include "ClassDet.h"
|
||||
|
||||
//===============================
|
||||
int main(int argc, char **argv){
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
||||
printf("=========================================\n");
|
||||
printf("=== Mapper ===\n");
|
||||
printf("=========================================\n");
|
||||
if (argc != 2) {
|
||||
printf("=========================================\n");
|
||||
if (argc != 2)
|
||||
{
|
||||
printf("Incorrect number of arguments:\n");
|
||||
printf("%s [inFile]\n", argv[0]);
|
||||
printf("\n\n");
|
||||
|
|
@ -28,155 +30,188 @@ int main(int argc, char **argv){
|
|||
|
||||
PrintMapping();
|
||||
|
||||
TFile * inFile = new TFile(inFileName.c_str(), "READ");
|
||||
TTree * tree = (TTree*) inFile->Get("tree");
|
||||
TFile *inFile = new TFile(inFileName.c_str(), "READ");
|
||||
TTree *tree = (TTree *)inFile->Get("tree");
|
||||
unsigned long long totnumEntry = tree->GetEntries();
|
||||
|
||||
ULong64_t evID;
|
||||
UInt_t multi;
|
||||
UShort_t sn[MAXMULTI];
|
||||
UShort_t ch[MAXMULTI];
|
||||
UShort_t e[MAXMULTI];
|
||||
UShort_t e2[MAXMULTI];
|
||||
ULong64_t e_t[MAXMULTI];
|
||||
UShort_t e_f[MAXMULTI];
|
||||
ULong64_t evID;
|
||||
UInt_t multi;
|
||||
UShort_t sn[MAXMULTI];
|
||||
UShort_t ch[MAXMULTI];
|
||||
UShort_t e[MAXMULTI];
|
||||
UShort_t e2[MAXMULTI];
|
||||
ULong64_t e_t[MAXMULTI];
|
||||
UShort_t e_f[MAXMULTI];
|
||||
|
||||
tree->SetBranchAddress("evID", &evID);
|
||||
tree->SetBranchAddress("evID", &evID);
|
||||
tree->SetBranchAddress("multi", &multi);
|
||||
tree->SetBranchAddress("sn", sn);
|
||||
tree->SetBranchAddress("ch", ch);
|
||||
tree->SetBranchAddress("e", e);
|
||||
tree->SetBranchAddress("e2", e2);
|
||||
tree->SetBranchAddress("e_t", e_t);
|
||||
tree->SetBranchAddress("e_f", e_f);
|
||||
tree->SetBranchAddress("sn", sn);
|
||||
tree->SetBranchAddress("ch", ch);
|
||||
tree->SetBranchAddress("e", e);
|
||||
tree->SetBranchAddress("e2", e2);
|
||||
tree->SetBranchAddress("e_t", e_t);
|
||||
tree->SetBranchAddress("e_f", e_f);
|
||||
|
||||
///================== new tree
|
||||
TString outFileName = inFileName;
|
||||
TString runStr = outFileName;
|
||||
TString runStr = outFileName;
|
||||
int pos = outFileName.Last('/');
|
||||
pos = outFileName.Index("_", pos+1); // find next "_"
|
||||
runStr.Remove(0, pos+1);
|
||||
pos = outFileName.Index("_", pos + 1); // find next "_"
|
||||
runStr.Remove(0, pos + 1);
|
||||
runStr.Remove(3);
|
||||
pos = outFileName.Index("_", pos+1); // find next "_"
|
||||
outFileName.Remove(pos); // remove the rest
|
||||
pos = outFileName.Index("_", pos + 1); // find next "_"
|
||||
outFileName.Remove(pos); // remove the rest
|
||||
outFileName += "_mapped.root";
|
||||
|
||||
ULong_t eventID;
|
||||
UInt_t run = runStr.Atoi();
|
||||
|
||||
|
||||
Det sx3;
|
||||
Det qqq;
|
||||
Det pc ;
|
||||
Det pc;
|
||||
Det misc;
|
||||
|
||||
printf(" Raw root file : %s\n", inFileName.c_str());
|
||||
printf(" Run : %03d\n", run);
|
||||
printf(" total Entry : %lld \n", totnumEntry);
|
||||
printf(" Out file name : %s \n", outFileName.Data());
|
||||
|
||||
TFile * saveFile = new TFile( outFileName,"RECREATE");
|
||||
TTree * newTree = new TTree("tree","tree");
|
||||
|
||||
TFile *saveFile = new TFile(outFileName, "RECREATE");
|
||||
TTree *newTree = new TTree("tree", "tree");
|
||||
|
||||
newTree->Branch("evID", &eventID,"eventID/l");
|
||||
newTree->Branch("run", &run,"run/i");
|
||||
newTree->Branch("evID", &eventID, "eventID/l");
|
||||
newTree->Branch("run", &run, "run/i");
|
||||
|
||||
newTree->Branch("sx3Multi", &sx3.multi, "sx3Multi/s");
|
||||
newTree->Branch("sx3ID", &sx3.id, "sx3ID[sx3Multi]/s");
|
||||
newTree->Branch("sx3Ch", &sx3.ch, "sx3Ch[sx3Multi]/s");
|
||||
newTree->Branch("sx3E", &sx3.e, "sx3Energy[sx3Multi]/s");
|
||||
newTree->Branch("sx3T", &sx3.t, "sx3Time[sx3Multi]/l");
|
||||
newTree->Branch("sx3ID", &sx3.id, "sx3ID[sx3Multi]/s");
|
||||
newTree->Branch("sx3Ch", &sx3.ch, "sx3Ch[sx3Multi]/s");
|
||||
newTree->Branch("sx3E", &sx3.e, "sx3Energy[sx3Multi]/s");
|
||||
newTree->Branch("sx3T", &sx3.t, "sx3Time[sx3Multi]/l");
|
||||
|
||||
newTree->Branch("qqqMulti", &qqq.multi, "qqqMulti/s");
|
||||
newTree->Branch("qqqID", &qqq.id, "qqqID[qqqMulti]/s");
|
||||
newTree->Branch("qqqCh", &qqq.ch, "qqqCh[qqqMulti]/s");
|
||||
newTree->Branch("qqqE", &qqq.e, "qqqEnergy[qqqMulti]/s");
|
||||
newTree->Branch("qqqT", &qqq.t, "qqqTime[qqqMulti]/l");
|
||||
newTree->Branch("qqqSN", &qqq.sn, "qqqSN[qqqMulti]/s");
|
||||
newTree->Branch("qqqID", &qqq.id, "qqqID[qqqMulti]/s");
|
||||
newTree->Branch("qqqCh", &qqq.ch, "qqqCh[qqqMulti]/s");
|
||||
newTree->Branch("qqqE", &qqq.e, "qqqEnergy[qqqMulti]/s");
|
||||
newTree->Branch("qqqT", &qqq.t, "qqqTime[qqqMulti]/l");
|
||||
newTree->Branch("qqqSN", &qqq.sn, "qqqSN[qqqMulti]/s");
|
||||
|
||||
newTree->Branch("pcMulti", &pc.multi, "pcMulti/s");
|
||||
newTree->Branch("pcID", &pc.id, "pcID[pcMulti]/s");
|
||||
newTree->Branch("pcCh", &pc.ch, "pcCh[pcMulti]/s");
|
||||
newTree->Branch("pcE", &pc.e, "pcEnergy[pcMulti]/s");
|
||||
newTree->Branch("pcT", &pc.t, "pcTime[pcMulti]/l");
|
||||
newTree->Branch("pcID", &pc.id, "pcID[pcMulti]/s");
|
||||
newTree->Branch("pcCh", &pc.ch, "pcCh[pcMulti]/s");
|
||||
newTree->Branch("pcE", &pc.e, "pcEnergy[pcMulti]/s");
|
||||
newTree->Branch("pcT", &pc.t, "pcTime[pcMulti]/l");
|
||||
|
||||
newTree->Branch("miscMulti", &misc.multi, "miscMulti/s");
|
||||
newTree->Branch("miscID", &misc.id, "miscID[miscMulti]/s");
|
||||
newTree->Branch("miscCh", &misc.ch, "miscCh[miscMulti]/s");
|
||||
newTree->Branch("miscE", &misc.e, "miscEnergy[miscMulti]/s");
|
||||
newTree->Branch("miscT", &misc.t, "miscTime[miscMulti]/l");
|
||||
newTree->Branch("miscF", &misc.tf, "miscFineTime[miscMulti]/l");
|
||||
|
||||
///================== looping old tree and apply mapping
|
||||
|
||||
//clock
|
||||
// TBenchmark clock;
|
||||
// Bool_t shown;
|
||||
// clock
|
||||
// TBenchmark clock;
|
||||
// Bool_t shown;
|
||||
|
||||
for( unsigned long long ev = 0; ev < totnumEntry; ev++){
|
||||
for (unsigned long long ev = 0; ev < totnumEntry; ev++)
|
||||
{
|
||||
tree->GetEntry(ev);
|
||||
|
||||
eventID = evID;
|
||||
sx3.multi = 0;
|
||||
qqq.multi = 0;
|
||||
pc.multi = 0;
|
||||
misc.multi = 0;
|
||||
|
||||
sx3.Clear();
|
||||
sx3.Clear();
|
||||
qqq.Clear();
|
||||
pc.Clear();
|
||||
misc.Clear();
|
||||
pc.Clear();
|
||||
misc.Clear();
|
||||
|
||||
for( unsigned int i = 0; i < multi; i++){
|
||||
for (unsigned int i = 0; i < multi; i++)
|
||||
{
|
||||
|
||||
// printf("%10u/%10u| %5d, %2u, %6u, %14llu\n", i, multi, sn[i], ch[i], e[i], e_t[i] );
|
||||
|
||||
//globalCh = digi-ID * nCh(digi-iD) + ch
|
||||
|
||||
// globalCh = digi-ID * nCh(digi-iD) + ch
|
||||
int globalCh = -1;
|
||||
|
||||
for( int j = 0; j < nBd; j++){
|
||||
if( board.at(j) == sn[i]){
|
||||
globalCh = (sn[i] > 1000 ? j * 64 : 7*64 + (j-7) * 16) + ch[i]; //& = number V1740
|
||||
for (int j = 0; j < nBd; j++)
|
||||
{
|
||||
if (board.at(j) == sn[i])
|
||||
{
|
||||
globalCh = (sn[i] > 1000 ? j * 64 : 7 * 64 + (j - 7) * 16) + ch[i]; //& = number V1740
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( globalCh == -1) printf("ev %llu\n", ev);
|
||||
|
||||
if (globalCh == -1)
|
||||
printf("ev %llu\n", ev);
|
||||
|
||||
unsigned short ID = mapping[globalCh];
|
||||
|
||||
//=================================== sx3
|
||||
if( ID < 10000 ) {
|
||||
if (ID < 10000)
|
||||
{
|
||||
sx3.id[sx3.multi] = ID / 100;
|
||||
sx3.ch[sx3.multi] = ID % 100;
|
||||
sx3.e[sx3.multi] = e[i];
|
||||
sx3.t[sx3.multi] = e_t[i];
|
||||
sx3.multi ++;
|
||||
sx3.multi++;
|
||||
}
|
||||
|
||||
//=================================== qqq
|
||||
if( 10000 <= ID && ID < 20000 ) {
|
||||
if (10000 <= ID && ID < 20000)
|
||||
{
|
||||
qqq.id[qqq.multi] = (ID - 10000) / 100;
|
||||
qqq.ch[qqq.multi] = (ID - 10000) % 100;
|
||||
qqq.e[qqq.multi] = e[i];
|
||||
qqq.t[qqq.multi] = e_t[i];
|
||||
qqq.sn[qqq.multi] = sn[i];
|
||||
qqq.multi ++;
|
||||
qqq.multi++;
|
||||
}
|
||||
|
||||
//=================================== pc
|
||||
if( 20000 <= ID && ID < 30000 ) {
|
||||
if (20000 <= ID && ID < 30000)
|
||||
{
|
||||
pc.id[pc.multi] = (ID - 20000) / 100;
|
||||
pc.ch[pc.multi] = (ID - 20000) % 100;
|
||||
pc.e[pc.multi] = e[i];
|
||||
pc.t[pc.multi] = e_t[i];
|
||||
pc.multi ++;
|
||||
pc.multi++;
|
||||
}
|
||||
|
||||
//=================================== misc
|
||||
if (30000 <= ID && ID < 40000)
|
||||
{
|
||||
misc.id[misc.multi] = (ID - 30000) / 100;
|
||||
misc.ch[misc.multi] = (ID - 30000) % 100;
|
||||
misc.e[misc.multi] = e[i];
|
||||
misc.t[misc.multi] = e_t[i];
|
||||
misc.tf[misc.multi] = e_f[i];
|
||||
// if( ID == 30002 || ID == 30004 ) printf("sn : %d ch: %2d | gID %3d | ID %6d | e_f : %d\n", sn[i], ch[i], globalCh, ID, e_f[i]);
|
||||
misc.multi++;
|
||||
}
|
||||
}
|
||||
|
||||
saveFile->cd(); //set focus on this file
|
||||
newTree->Fill();
|
||||
|
||||
if( eventID % 100 == 0 ) printf("%6lu/%6llu [%2d%%]\n\033[A\r", eventID, totnumEntry, TMath::Nint((eventID+1)*100./totnumEntry));
|
||||
|
||||
saveFile->cd(); // set focus on this file
|
||||
newTree->Fill();
|
||||
|
||||
if (eventID % 100 == 0)
|
||||
printf("%6lu/%6llu [%2d%%]\n\033[A\r", eventID, totnumEntry, TMath::Nint((eventID + 1) * 100. / totnumEntry));
|
||||
}
|
||||
|
||||
inFile->Close();
|
||||
|
||||
saveFile->cd(); //set focus on this file
|
||||
newTree->Write();
|
||||
saveFile->cd(); // set focus on this file
|
||||
newTree->Write();
|
||||
UInt_t eventNumber = newTree->GetEntries();
|
||||
saveFile->Close();
|
||||
printf("-------------- done, %u\n", eventNumber);
|
||||
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
331
Armory/PCZRecon.h
Normal file
331
Armory/PCZRecon.h
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
#ifndef PCZRecon_h
|
||||
#define PCZRecon_h
|
||||
|
||||
// PC Z-position reconstruction, one section per anode/cathode topology:
|
||||
// A1C0 (single anode wire only), A2C0 (two-wire anode cluster, no cathode --
|
||||
// same math as A1C0, see that section), A1C1 (anode + single cathode, charge
|
||||
// division), A1C2 (anode + two cathodes, "step ladder" correction). Each topology gets one
|
||||
// well-defined entry point instead of the math being split across files by
|
||||
// historical accident (A1C0/A1C1 used to live in TrackRecon.C itself; A1C2's
|
||||
// underlying model lives in the separately-shared PC_StepLadder_Correction.h
|
||||
// -- see the A1C2 section below for why that one isn't just moved in).
|
||||
//
|
||||
// This header holds the reconstruction MATH only. The per-dataset tuning
|
||||
// constants it reads (cfrac fit parameters, dead-wire lists, Z calibration,
|
||||
// beam-axis origin) are still owned and set by TrackRecon.C's Begin() --
|
||||
// this header just declares them `extern` so the same single translation
|
||||
// unit (TrackRecon.C is compiled as one .C file via ACLiC) can see them.
|
||||
// Moving the constants themselves out is a separate, riskier change and is
|
||||
// deliberately NOT done here.
|
||||
//
|
||||
// Relocated verbatim from TrackRecon.C (no logic changes): A1C1CellSol,
|
||||
// A1C1Sol, solve_cell, a1c1_solve, SideChoice, a1c1_pick_side, a1c1_zcorr,
|
||||
// a1c0_hybrid_pcz (split into a1c0_wirePos + a1c0_hybrid_pcz).
|
||||
//
|
||||
// New in this header (see call-site migration notes where each is used):
|
||||
// a1c1_solve_pick, a1c1_cfrac_pcz, a1c2_zfix.
|
||||
|
||||
#include <TVector3.h>
|
||||
#include <TRandom3.h>
|
||||
#include <TMath.h>
|
||||
#include "ClassPW.h"
|
||||
|
||||
// --- Per-dataset tuning constants, defined and set in TrackRecon.C ---
|
||||
extern PW pwinstance;
|
||||
extern const double a1c1_zg[8];
|
||||
extern double a1c1_cfmin_cell[7];
|
||||
extern double a1c1_k_cell[7];
|
||||
extern double a1c1_cfmin2_cell[7];
|
||||
extern double a1c1_k2_cell[7];
|
||||
extern double a1c1_cfrac_split;
|
||||
extern double a1c1_lowband_rfactor;
|
||||
extern double a1c1_missing_fmax;
|
||||
extern double a1c1_z_scale_qqq;
|
||||
extern double a1c1_z_off_qqq;
|
||||
extern double a1c1_z_scale_sx3;
|
||||
extern double a1c1_z_off_sx3;
|
||||
bool a1c1_missing_neighbor(int awire, int cwire); // TrackRecon.C: dead-wire-adjacency check
|
||||
TVector3 beamVertex(const TVector3 &si, const TVector3 &dir);
|
||||
double beamPerp(const TVector3 &p);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// A1C0: single-wire (anode only) position reconstruction
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// Anode-wire Z, corrected by the a1c1-derived scale+offset (a1c1_zcorr),
|
||||
// same reference frame the a1c1 solve below reports in.
|
||||
inline double a1c1_zcorr(double z_a1c0, bool isQQQ)
|
||||
{
|
||||
double scale = isQQQ ? a1c1_z_scale_qqq : a1c1_z_scale_sx3;
|
||||
double off = isQQQ ? a1c1_z_off_qqq : a1c1_z_off_sx3;
|
||||
return z_a1c0 * (1.0 - scale) - off;
|
||||
}
|
||||
|
||||
// The raw (undithered) A1C0 wire position: nearest-wire XY at the given
|
||||
// track phi, Z corrected into the same frame a1c1 uses. This is what
|
||||
// _rawZ_a1c0 / phi cuts / _dPhi_a1c0-style diagnostics should read --
|
||||
// anywhere you want the true discretized wire position, not a smoothed one.
|
||||
inline TVector3 a1c0_wirePos(const std::pair<TVector3, TVector3> &apwire, double phi, bool isQQQ)
|
||||
{
|
||||
TVector3 pc = pwinstance.getClosestWirePosAtWirePhi(apwire, phi);
|
||||
pc.SetZ(a1c1_zcorr(pc.Z(), isQQQ));
|
||||
return pc;
|
||||
}
|
||||
|
||||
// a1c0_wirePos, then Gaussian-dithered in Z to hide wire-pitch quantization.
|
||||
// sigma is the caller's choice (dither_sigma or dither_sigma_c0/2.0 etc in
|
||||
// TrackRecon.C) -- this function doesn't know which convention is "correct"
|
||||
// for a given call site, only how to apply whichever sigma it's given.
|
||||
inline TVector3 a1c0_hybrid_pcz(const std::pair<TVector3, TVector3> &apwire, double phi,
|
||||
bool isQQQ, double sigma, TRandom3 &rand)
|
||||
{
|
||||
TVector3 pc = a1c0_wirePos(apwire, phi, isQQQ);
|
||||
pc.SetZ(rand.Gaus(pc.Z(), sigma));
|
||||
return pc;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// A2C0: two-wire anode-cluster (charge-shared), no-cathode position
|
||||
// reconstruction
|
||||
// ---------------------------------------------------------------------
|
||||
//
|
||||
// Same math as A1C0 above -- pseudowire + phi-minimization, no dither --
|
||||
// just handed a genuine 2-wire energy-weighted pseudowire (GetPseudoWire
|
||||
// over a 2-wire anode cluster) instead of a single real wire. a1c0_wirePos
|
||||
// already treats its `apwire` argument as an opaque pseudowire pair
|
||||
// regardless of how many physical wires went into it, so this is a
|
||||
// documented, named entry point rather than new math: call sites can say
|
||||
// what topology they mean instead of reusing a1c0_wirePos silently and
|
||||
// trusting a comment to explain why.
|
||||
//
|
||||
// Deliberately no dithered twin (no a2c0_hybrid_pcz): A2C0 is meant to feed
|
||||
// the reaction-analysis plots at its raw, undithered resolution, not stand
|
||||
// in for a1c0_hybrid_pcz's benchmark-truth-comparison role. If a "genuine
|
||||
// A2C0" BenchMark validation block is ever wanted (mirroring the existing
|
||||
// aClusters.size()==1 && cClusters.size()==0 A1C0 block in TrackRecon.C),
|
||||
// add one there rather than adding dithering here.
|
||||
inline TVector3 a2c0_wirePos(const std::pair<TVector3, TVector3> &apwire, double phi, bool isQQQ)
|
||||
{
|
||||
return a1c0_wirePos(apwire, phi, isQQQ);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// A1C1: single-anode + single-cathode charge-division position reconstruction
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
struct A1C1CellSol
|
||||
{
|
||||
int cell = -1;
|
||||
double pcz = -99999;
|
||||
double f = 0.0;
|
||||
double pitch = 0.0;
|
||||
bool inband = false;
|
||||
bool pitchok = false;
|
||||
};
|
||||
|
||||
struct A1C1Sol
|
||||
{
|
||||
int band;
|
||||
double cfrac_used;
|
||||
double pcz_lo;
|
||||
double pcz_hi;
|
||||
A1C1CellSol hi;
|
||||
A1C1CellSol lo;
|
||||
};
|
||||
|
||||
inline A1C1CellSol solve_cell(int cell, int wf, double zf, double cfrac,
|
||||
const double *cfmin, const double *kk, bool dead_neighbor)
|
||||
{
|
||||
A1C1CellSol s;
|
||||
s.cell = cell;
|
||||
s.pcz = zf; // safe sentinel: fired-wire position so edge-wire defaults don't read as z=0
|
||||
|
||||
if (cell < 0 || cell > 6)
|
||||
return s;
|
||||
|
||||
double zc = 0.5 * (a1c1_zg[cell] + a1c1_zg[cell + 1]); // cell centre
|
||||
double half = 0.5 * (a1c1_zg[cell] - a1c1_zg[cell + 1]); // half-cell width
|
||||
double pitch = a1c1_zg[cell] - a1c1_zg[cell + 1]; // full wire spacing
|
||||
|
||||
if (half <= 0.0 || kk[cell] <= 0.0)
|
||||
return s;
|
||||
|
||||
s.pitch = pitch;
|
||||
|
||||
// f = 0 -> cell centre, f = 1 -> fired wire. Outside [0,1] = outside the band.
|
||||
s.f = (cfrac - cfmin[cell]) / kk[cell];
|
||||
|
||||
// sign maps increasing f toward the fired cathode wire.
|
||||
double sgn = (a1c1_zg[wf] >= zc) ? +1.0 : -1.0;
|
||||
s.pcz = zc + sgn * s.f * half;
|
||||
|
||||
double fmax = dead_neighbor ? a1c1_missing_fmax : 1.0;
|
||||
s.inband = (s.f >= 0.0 && s.f <= fmax);
|
||||
|
||||
// Reconstructed position should remain within one cell pitch of the fired wire.
|
||||
s.pitchok = (TMath::Abs(s.pcz - zf) <= pitch);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
inline A1C1Sol a1c1_solve(double cfrac, double zf, int cwire = -1, double anodeE = -1, int awire = -1)
|
||||
{
|
||||
A1C1Sol s{0, cfrac, zf, zf, {}, {}};
|
||||
const double *cfmin = a1c1_cfmin_cell;
|
||||
const double *kk = a1c1_k_cell;
|
||||
if (a1c1_cfrac_split > 0.0 && cfrac >= 0.0 && cfrac < a1c1_cfrac_split)
|
||||
{
|
||||
s.band = 1;
|
||||
if (a1c1_lowband_rfactor > 0.0 && cfrac > 0.0 && cfrac < 1.0)
|
||||
{
|
||||
double r = cfrac / (1.0 - cfrac);
|
||||
r *= a1c1_lowband_rfactor;
|
||||
cfrac = r / (1.0 + r);
|
||||
}
|
||||
else
|
||||
{
|
||||
cfmin = a1c1_cfmin2_cell;
|
||||
kk = a1c1_k2_cell;
|
||||
}
|
||||
}
|
||||
s.cfrac_used = cfrac;
|
||||
|
||||
int wf = 0;
|
||||
for (int i = 1; i < 8; ++i)
|
||||
if (TMath::Abs(a1c1_zg[i] - zf) < TMath::Abs(a1c1_zg[wf] - zf))
|
||||
wf = i;
|
||||
|
||||
bool dead_neighbor = a1c1_missing_neighbor(awire, cwire); // same for both cells; hoist to avoid double scan
|
||||
s.hi = solve_cell(wf - 1, wf, zf, cfrac, cfmin, kk, dead_neighbor); // cell above (higher z)
|
||||
s.lo = solve_cell(wf, wf, zf, cfrac, cfmin, kk, dead_neighbor); // cell below (lower z)
|
||||
s.pcz_hi = s.hi.pcz;
|
||||
s.pcz_lo = s.lo.pcz;
|
||||
return s;
|
||||
}
|
||||
|
||||
// Which of the two candidate cells the beam-axis test selects.
|
||||
enum class SideChoice
|
||||
{
|
||||
High, // the cell ABOVE the fired wire (pcz_hi)
|
||||
Low // the cell BELOW the fired wire (pcz_lo)
|
||||
};
|
||||
|
||||
// Picks between the two candidate cells (lo/hi) a1c1_solve returns.
|
||||
//
|
||||
// This used to compare beamVertex(si, ...)'s beamPerp for each candidate, but
|
||||
// that check is a no-op: both candidates share the same (cx, cy), so
|
||||
// beamVertex's t-parameter -- computed purely from dir.X()/dir.Y() -- comes out
|
||||
// identical for both, and therefore so does the resulting vertex X/Y and
|
||||
// beamPerp. The two disambiguating branches below were consequently dead code;
|
||||
// the function always fell through to returning Low regardless of the actual
|
||||
// physics.
|
||||
//
|
||||
// inband/pitchok DO differ between the two cells (each depends on cfrac's
|
||||
// position within that cell's own band), so they're used here for status
|
||||
// instead. The final tie-break prefers whichever candidate sits closer to the
|
||||
// fired wire itself, rather than farther into the next cell over.
|
||||
inline SideChoice a1c1_pick_side(const A1C1CellSol &lo, const A1C1CellSol &hi, double zf, int &status)
|
||||
{
|
||||
bool okl = lo.inband && lo.pitchok;
|
||||
bool okh = hi.inband && hi.pitchok;
|
||||
status = (okl || okh) ? ((okl && okh) ? 1 : 0) : 2;
|
||||
if (okl && !okh)
|
||||
return SideChoice::Low;
|
||||
if (okh && !okl)
|
||||
return SideChoice::High;
|
||||
double dl = TMath::Abs(lo.pcz - zf);
|
||||
double dh = TMath::Abs(hi.pcz - zf);
|
||||
return (dl <= dh) ? SideChoice::Low : SideChoice::High;
|
||||
}
|
||||
|
||||
// a1c1_solve() + a1c1_pick_side() together, with the picked cell already
|
||||
// resolved. Every call site that needs more than just the final pcz (side
|
||||
// status, cell index, f, inband, pitchok -- e.g. for benchmark/diagnostic
|
||||
// histograms) was previously re-deriving this same 4-line pattern by hand;
|
||||
// this is that pattern, named once.
|
||||
struct A1C1PickedSol
|
||||
{
|
||||
A1C1Sol sol;
|
||||
SideChoice side = SideChoice::Low;
|
||||
int side_status = -1;
|
||||
|
||||
const A1C1CellSol &best() const { return (side == SideChoice::High) ? sol.hi : sol.lo; }
|
||||
};
|
||||
|
||||
inline A1C1PickedSol a1c1_solve_pick(double cfrac, double zf, const TVector3 &si, double cx, double cy,
|
||||
int cwire = -1, double anodeE = -1, int awire = -1)
|
||||
{
|
||||
// si/cx/cy are no longer used by the pick itself (see a1c1_pick_side above)
|
||||
// but are kept in the signature so the ~10 existing call sites throughout
|
||||
// TrackRecon.C don't need to change.
|
||||
A1C1PickedSol out;
|
||||
out.sol = a1c1_solve(cfrac, zf, cwire, anodeE, awire);
|
||||
out.side = a1c1_pick_side(out.sol.lo, out.sol.hi, zf, out.side_status);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Full pipeline: raw anode/cathode energies -> cfrac -> solve -> pick side ->
|
||||
// picked Z, plus whether the pick landed in-band. This is what most call
|
||||
// sites actually want (they don't need the intermediate A1C1Sol/side_status
|
||||
// unless they're doing benchmark diagnostics -- for that, call
|
||||
// a1c1_solve_pick directly instead).
|
||||
//
|
||||
// Takes primitives rather than TrackRecon.C's `Event` type so this header
|
||||
// has no dependency on TrackRecon.C's class definitions; see the thin
|
||||
// `Event`-taking overload kept in TrackRecon.C next to the `Event` class
|
||||
// for the short call-site spelling existing code uses.
|
||||
inline double a1c1_cfrac_pcz(double pcz_raw, double energyAnode, double energyCathode,
|
||||
double cx, double cy, int cathodeCh, int anodeCh,
|
||||
const TVector3 &si, bool &inband)
|
||||
{
|
||||
inband = false;
|
||||
double ac = energyAnode + energyCathode;
|
||||
double cfrac = (ac > 0.0) ? energyCathode / ac : -1.0;
|
||||
if (cfrac < 0.0)
|
||||
return pcz_raw;
|
||||
A1C1PickedSol picked = a1c1_solve_pick(cfrac, pcz_raw, si, cx, cy, cathodeCh, energyAnode, anodeCh);
|
||||
const A1C1CellSol &best = picked.best();
|
||||
inband = (best.inband && picked.side_status != 2);
|
||||
return best.pcz;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// A1C2: two-cathode "step ladder" Z reconstruction
|
||||
// ---------------------------------------------------------------------
|
||||
//
|
||||
// Same step-ladder, pivot-about-cell-midpoint model as Armory/
|
||||
// PC_StepLadder_Correction.h's model_invert. That file is shared with
|
||||
// MakeVertex.C (a separate, parallel branch of development, out of scope
|
||||
// here) and a few scratch macros, so rather than include it or edit it,
|
||||
// this is a standalone copy -- keeping every TrackRecon.C reconstruction
|
||||
// path (A1C0/A1C1/A1C2) self-contained in this one header rather than
|
||||
// reaching into a file with unrelated consumers. If the underlying model
|
||||
// ever changes, both copies need the same edit; there are exactly two.
|
||||
//
|
||||
// Rewritten as a plain scalar function rather than kept in model_invert's
|
||||
// original `double f(double *y, double *p)` shape -- that signature only
|
||||
// existed to match TF1's raw-function-pointer constructor. TrackRecon.C
|
||||
// used to go through a `TF1 pcfix_func` purely to get a callable out of it,
|
||||
// calling only pcfix_func.Eval(z) at every site (npar `p` was never used,
|
||||
// and nothing called .Draw()/.Integral()/anything else TF1-specific) --
|
||||
// so the TF1 wrapper bought nothing. a1c2_zfix has the same scalar
|
||||
// in/scalar out shape as a1c0_wirePos/a1c1_solve above instead.
|
||||
inline double a1c2_zfix(double z)
|
||||
{
|
||||
double result = z;
|
||||
double slope = 0.52;
|
||||
double z_grid[8] = {148.2677, 102.1111, 59.8527, 19.7248, -19.7248, -59.8527, -102.1111, -148.2677};
|
||||
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
if (z <= z_grid[i] && z > z_grid[i + 1])
|
||||
{
|
||||
double zavg = (z_grid[i] + z_grid[i + 1]) * 0.5; // midpoint about which we pivot
|
||||
result = (z - zavg) / slope + zavg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
82
Armory/PC_StepLadder_Correction.h
Normal file
82
Armory/PC_StepLadder_Correction.h
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
#include <TF1.h>
|
||||
|
||||
double model_invert(double *y, double *p)
|
||||
{
|
||||
double result = y[0];
|
||||
double slope = 0.52;
|
||||
double z_grid[8] = {147.998, 101.946, 59.7634, 19.6965, -19.6965, -59.7634, -101.946, -147.998};
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
if (y[0] <= z_grid[i] && y[0] > z_grid[i + 1])
|
||||
{
|
||||
double zavg = (z_grid[i] + z_grid[i + 1]) * 0.5; // midpoint about which we pivot
|
||||
result = (y[0] - zavg) / slope + zavg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// ---- Parabola variant (now takes optional slope; out-of-band -> Si, no clamp-snap) ----
|
||||
inline double model_invert_a1c1(double cfrac, double z_si, double z_pivot,
|
||||
double C, const double* c0_table, bool& ok,
|
||||
double slope = 1.0)
|
||||
{
|
||||
ok = false;
|
||||
static const double z_grid[8] =
|
||||
{147.998, 101.946, 59.7634, 19.6965, -19.6965, -59.7634, -101.946, -147.998};
|
||||
const double cfrac_hi = 0.90, cfrac_lo = 0.12;
|
||||
if (cfrac > cfrac_hi || cfrac < cfrac_lo) return z_si;
|
||||
|
||||
const double sgn = (z_si >= z_pivot) ? +1.0 : -1.0;
|
||||
int gnb = -1; double z_nb = (sgn > 0) ? +1.0e30 : -1.0e30;
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
if (sgn > 0 && z_grid[i] > z_pivot + 1e-6 && z_grid[i] < z_nb) { z_nb = z_grid[i]; gnb = i; }
|
||||
if (sgn < 0 && z_grid[i] < z_pivot - 1e-6 && z_grid[i] > z_nb) { z_nb = z_grid[i]; gnb = i; }
|
||||
}
|
||||
if (gnb < 0) return z_si;
|
||||
const double W = TMath::Abs(z_nb - z_pivot);
|
||||
if (W <= 0.0) return z_si;
|
||||
int cell = (sgn > 0) ? gnb : gnb - 1;
|
||||
if (cell < 0) cell = 0; if (cell > 6) cell = 6;
|
||||
|
||||
double s2 = (c0_table[cell] - cfrac) / C;
|
||||
if (s2 < 0.0 || s2 > 1.0) return z_si; // out-of-band -> Si (no boundary snap)
|
||||
double s = TMath::Sqrt(s2);
|
||||
double z_rec = z_pivot + sgn * s * W;
|
||||
if (slope > 0.0 && slope != 1.0) {
|
||||
const double zc = 0.5 * (z_grid[cell] + z_grid[cell + 1]);
|
||||
z_rec = (z_rec - zc) / slope + zc;
|
||||
}
|
||||
if (TMath::Abs(z_rec - z_si) > W) return z_si;
|
||||
ok = true;
|
||||
return z_rec;
|
||||
}
|
||||
|
||||
// ---- Linear centre-fold variant ----
|
||||
inline double model_invert_a1c1_linear(double cfrac, double z_si, double z_pivot,
|
||||
double k, const double* cfmin_table, bool& ok)
|
||||
{
|
||||
ok = false;
|
||||
static const double z_grid[8] =
|
||||
{147.998, 101.946, 59.7634, 19.6965, -19.6965, -59.7634, -101.946, -147.998};
|
||||
const double cfrac_hi = 0.90, cfrac_lo = 0.12;
|
||||
if (cfrac > cfrac_hi || cfrac < cfrac_lo) return z_si;
|
||||
|
||||
int cell = -1;
|
||||
for (int i = 0; i < 7; ++i)
|
||||
if (z_si <= z_grid[i] && z_si > z_grid[i + 1]) { cell = i; break; }
|
||||
if (cell < 0) return z_si;
|
||||
const double zc = 0.5 * (z_grid[cell] + z_grid[cell + 1]);
|
||||
const double half = 0.5 * (z_grid[cell] - z_grid[cell + 1]);
|
||||
if (half <= 0.0 || k <= 0.0) return z_si;
|
||||
|
||||
double f = (cfrac - cfmin_table[cell]) / k;
|
||||
if (f < 0.0) f = 0.0;
|
||||
if (f > 1.0) return z_si;
|
||||
const double d = f * half;
|
||||
const double sgn = (z_pivot >= zc) ? +1.0 : -1.0;
|
||||
const double z_rec = zc + sgn * d;
|
||||
if (TMath::Abs(z_rec - z_si) > half) return z_si;
|
||||
ok = true;
|
||||
return z_rec;
|
||||
}
|
||||
190
Armory/SX3Geom.h
Executable file
190
Armory/SX3Geom.h
Executable file
|
|
@ -0,0 +1,190 @@
|
|||
#ifndef SX3Geom_h
|
||||
#define SX3Geom_h
|
||||
#include <vector>
|
||||
|
||||
const double DEFAULT_NULL=-987654321.;
|
||||
|
||||
class sx3_geometry_scalefactors {
|
||||
public:
|
||||
//If sx3 has L, R being the left and right extremities, we choose add, stretch here such that
|
||||
// x_mm = (x_raw+add)*stretch; so add=abs(L), stretch=75/(abs(L)+R)
|
||||
float add[4];
|
||||
float stretch[4];
|
||||
};
|
||||
|
||||
class qqq5_finegains {
|
||||
public:
|
||||
std::array<std::pair<float,float>,32> front;
|
||||
//front.at(30).first = slope at clkpos 0, ring 30 for E front layer
|
||||
//front.at(30).second = intercept for the same as above
|
||||
std::array<std::pair<float,float>,4> back;
|
||||
};
|
||||
class sx3_fbgains {
|
||||
public:
|
||||
//Order of indices is [pad][strip]
|
||||
float padoffsets[4][4];
|
||||
float padgains[4][4];
|
||||
|
||||
float stripLoffsets[4][4];
|
||||
float stripLgains[4][4];
|
||||
|
||||
float stripRoffsets[4][4];
|
||||
float stripRgains[4][4];
|
||||
};
|
||||
|
||||
std::array<sx3_fbgains,24> sx3_xtalk_gains; //every sx3 needs to be gainmatched as a frontL-back, frontR-back pair (pad strip pair)
|
||||
std::array<sx3_geometry_scalefactors,24> sx3gs;
|
||||
|
||||
class sx3 {
|
||||
public:
|
||||
//TODO: Convert to std::array
|
||||
//Holds all information in an event, including ped subtraction+scaling. back[2].at(0) will have the largest energy seen in ch2, if any
|
||||
std::vector<float> back[4];
|
||||
std::vector<float> frontL[4];
|
||||
std::vector<float> frontR[4];
|
||||
|
||||
double ts = DEFAULT_NULL;
|
||||
//Easy lookup of final calibrated event. Only filled for valid cases, assumed for now to be 1L, 1R, 1B
|
||||
float frontX=DEFAULT_NULL;
|
||||
float frontXmm=DEFAULT_NULL;
|
||||
float frontE=DEFAULT_NULL;
|
||||
float backE=DEFAULT_NULL;
|
||||
int stripF=DEFAULT_NULL;
|
||||
int stripB=DEFAULT_NULL;
|
||||
float frontEL=DEFAULT_NULL;
|
||||
float frontER=DEFAULT_NULL;
|
||||
|
||||
float phi=DEFAULT_NULL; //
|
||||
|
||||
std::set<int> valid_front_chans;
|
||||
std::set<int> valid_back_chans;
|
||||
std::set<int> unmatched_front_chans; //every front channel is unmatched and invalid at first. when it gets matched, it gets removed and sent to valid
|
||||
|
||||
bool foundevent=false;
|
||||
bool valid=false;//valid will be set to false in all cases where we have ambiguity
|
||||
int flags=-1;//flags settable to different types of values to indicate different invalid situations
|
||||
|
||||
void fillevent(const std::string& position, const int subchannel, const float value); //make 'const' what functions don't need to change, helps with performance
|
||||
void validate(const sx3_fbgains&, const sx3_geometry_scalefactors&);
|
||||
void validate();
|
||||
};
|
||||
|
||||
|
||||
void sx3::fillevent(const std::string& positionstring, const int subchannel, const float value) {
|
||||
assert(subchannel>=0 && subchannel<4);
|
||||
foundevent=1;
|
||||
if(positionstring=="FRONT_L") {
|
||||
frontL[subchannel].push_back(value);
|
||||
unmatched_front_chans.insert(subchannel);
|
||||
} else if(positionstring=="FRONT_R") {
|
||||
frontR[subchannel].push_back(value);
|
||||
unmatched_front_chans.insert(subchannel);
|
||||
} else if(positionstring=="BACK") {
|
||||
back[subchannel].push_back(value);
|
||||
valid_back_chans.insert(subchannel);
|
||||
} else {
|
||||
std::cout << "Unknown string "+positionstring+" encountered in sx3::fillevent \n" << std::endl;
|
||||
}
|
||||
if(frontL[subchannel].size()!=0 && frontR[subchannel].size()!=0 ) {
|
||||
unmatched_front_chans.erase(subchannel);
|
||||
valid_front_chans.insert(subchannel); //std::set, so no duplication will happen
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//void sx3::validate(const sx3_fbgains& fbgains, const sx3_geometry_scalefactors& sx3gs) {
|
||||
void sx3::validate() {
|
||||
if(valid_front_chans.size()!=0 && valid_back_chans.size()!=0) {
|
||||
valid=true;
|
||||
float maxFE=0;
|
||||
float maxBE=0;
|
||||
//float zpos=0;
|
||||
int bchan=-1;
|
||||
int fchan=-1;
|
||||
/* for(auto cc: valid_front_chans) {
|
||||
std::cout << "fc" << cc << std::endl;// " " << frontL[cc].at(0) << " " << frontR[cc].at(0) << std::endl;
|
||||
}
|
||||
for(auto cc: valid_back_chans) {
|
||||
std::cout << "bc" << cc << std::endl; //" " << back[cc].at(0) << std::endl;
|
||||
}
|
||||
*/
|
||||
for(auto chan: valid_front_chans) {
|
||||
if(frontL[chan].size()>1) {
|
||||
printf("\nmultihit sx3 at Lsubchan:%d, ts:%1.13g\n",chan,ts);
|
||||
for(const auto& e: frontL[chan]) printf("e: %f\t",e);
|
||||
std::sort(frontL[chan].begin(), frontL[chan].end(), std::greater<float>());
|
||||
flags += (-1000);
|
||||
}
|
||||
if(frontR[chan].size()>1) {
|
||||
printf("\nmultihit sx3 at Rsubchan:%d, ts:%1.13g\n",chan,ts);
|
||||
for(const auto& e: frontR[chan]) printf("e: %f\t",e);
|
||||
std::sort(frontR[chan].begin(), frontR[chan].end(), std::greater<float>());
|
||||
flags += (-2000);
|
||||
}
|
||||
//assign position using max L+R value
|
||||
/*printf("chan:%d sizeL: %d sizeR: %d\n",chan, frontL[chan].size(), frontR[chan].size()); fflush(stdout);
|
||||
printf("foo\n");
|
||||
std::cout << "\nL:" << std::endl;
|
||||
for(auto thing: frontL[chan]) std::cout << thing << " " << std::flush;
|
||||
std::cout << "\nR:" << std::endl;
|
||||
for(auto thing: frontR[chan]) std::cout << thing << " " << std::flush;*/
|
||||
if(frontL[chan].at(0) + frontR[chan].at(0)>= maxFE) {
|
||||
maxFE = frontL[chan].at(0) + frontR[chan].at(0);
|
||||
//zpos = (frontL[chan].at(0)-frontR[chan].at(0))/maxFE;
|
||||
fchan = chan;
|
||||
}
|
||||
}
|
||||
for(auto chan: valid_back_chans) {
|
||||
if(back[chan].size()>1) {
|
||||
printf("\nmultihit sx3 at Bsubchan:%d, ts:%1.13g\n",chan,ts);
|
||||
for(const auto& e: back[chan]) printf("e: %f\t",e);
|
||||
std::sort(back[chan].begin(), back[chan].end(), std::greater<float>());
|
||||
flags += (-3000);
|
||||
}
|
||||
if(back[chan].size() ==0 ) {
|
||||
printf("foo\n");
|
||||
//continue;
|
||||
}
|
||||
if(back[chan].at(0) >= maxBE) {
|
||||
maxBE = back[chan].at(0);
|
||||
bchan = chan;
|
||||
}
|
||||
}
|
||||
/*
|
||||
Cross-talk corrections are important when evaluating 'energy' signals from strips/pads.
|
||||
They can cause unexpected behavior when used universally for all EL, ER cases, so we split scenarios in two.
|
||||
- Positions along each strip (frontX) *are not* corrected for crosstalk.
|
||||
- Total F and B energies (frontE, backE) *are*.
|
||||
Sudarsan B, 31 Oct 2024
|
||||
*/
|
||||
|
||||
if(fchan==-1 || bchan==-1) {
|
||||
std::cout << "how" << std::endl;
|
||||
std::cout << "fc " << std::flush; for(auto fc : valid_front_chans) std::cout << fc << " (" << frontL[fc].at(0) << "," << frontR[fc].at(0)<< ") "; std::cout << std::endl;
|
||||
std::cout << "bc " << std::flush; for(auto bc : valid_back_chans) std::cout << bc << " " << back[bc].at(0) << std::flush; std::cout << std::endl;
|
||||
}
|
||||
float Eleft = frontL[fchan].at(0);
|
||||
float Eright = frontR[fchan].at(0);
|
||||
frontEL = Eleft;
|
||||
frontER = Eright;
|
||||
frontX = (Eleft-Eright)/(Eleft+Eright);
|
||||
//frontXmm = (frontX+sx3gs.add[fchan])*sx3gs.stretch[fchan]; //convert to mm
|
||||
|
||||
//frontE = Eleft*fbgains.stripLgains[bchan][fchan] + fbgains.stripLoffsets[bchan][fchan]
|
||||
// + Eright*fbgains.stripRgains[bchan][fchan] + fbgains.stripRoffsets[bchan][fchan];
|
||||
//backE = back[bchan].at(0)*fbgains.padgains[bchan][fchan]+fbgains.padoffsets[bchan][fchan];
|
||||
frontE = Eleft+Eright;
|
||||
backE = maxBE;
|
||||
stripF=fchan;
|
||||
stripB=bchan;
|
||||
|
||||
flags = 0;
|
||||
} else if(valid_front_chans.size()!=0 && valid_back_chans.size()==0) {
|
||||
flags = -10;
|
||||
} else if(valid_front_chans.size()==0 && valid_back_chans.size()!=0) {
|
||||
flags = -20;
|
||||
}
|
||||
}
|
||||
|
||||
typedef sx3 sx3det;
|
||||
#endif
|
||||
795
Armory/fsuReader.h
Normal file
795
Armory/fsuReader.h
Normal file
|
|
@ -0,0 +1,795 @@
|
|||
#include "ClassData.h"
|
||||
#include "Hit.h"
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
|
||||
// #include "AggSeparator.h"
|
||||
|
||||
class FSUReader{
|
||||
|
||||
public:
|
||||
FSUReader();
|
||||
FSUReader(std::string fileName, uInt dataSize = 100, int verbose = 1);
|
||||
FSUReader(std::vector<std::string> fileList, uInt dataSize = 100, int verbose = 1);
|
||||
~FSUReader();
|
||||
|
||||
void OpenFile(std::string fileName, uInt dataSize, int verbose = 1);
|
||||
bool IsOpen() const{return inFile == nullptr ? false : true;}
|
||||
bool IsEndOfFile() const {
|
||||
// printf("%s : %d | %ld |%ld\n", __func__, feof(inFile), ftell(inFile), inFileSize);
|
||||
if(fileList.empty() ) {
|
||||
if( (uLong )ftell(inFile) >= inFileSize){
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}else{
|
||||
if( fileID + 1 == (int) fileList.size() && ((uLong)ftell(inFile) >= inFileSize) ) {
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScanNumBlock(int verbose = 1, uShort saveData = 0); // saveData = 0 (no save), 1 (no trace), 2 (with trace);
|
||||
int ReadNextBlock(bool traceON = false, int verbose = 0, uShort saveData = 0); // saveData = 0 (no save), 1 (no trace), 2 (with trace);
|
||||
int ReadBlock(unsigned int ID, int verbose = 0);
|
||||
|
||||
unsigned int GetFilePos() const {return filePos;}
|
||||
unsigned long GetTotNumBlock() const{ return totNumBlock;}
|
||||
std::vector<unsigned int> GetBlockTimestamp() const {return blockTimeStamp;}
|
||||
|
||||
Data * GetData() const{return data;}
|
||||
|
||||
std::string GetFileName() const{return fileName;}
|
||||
int GetDPPType() const{return DPPType;}
|
||||
int GetSN() const{return sn;}
|
||||
int GetTick2ns() const{return tick2ns;}
|
||||
int GetNumCh() const{return numCh;}
|
||||
int GetFileOrder() const{return order;}
|
||||
int GetChMask() const{return chMask;}
|
||||
unsigned long GetFileByteSize() const {return inFileSize;}
|
||||
|
||||
void ClearHitList() { hit.clear();}
|
||||
ulong GetHitListLength() const {return hit.size();}
|
||||
std::vector<Hit> GetHitVector() const {return hit;}
|
||||
void SortHit(int verbose = false);
|
||||
Hit GetHit(int id) const {
|
||||
if( id < 0 ) id = hit.size() + id;
|
||||
return hit[id];
|
||||
}
|
||||
|
||||
void ClearHitCount() {hitCount = 0;}
|
||||
ulong GetHitCount() const{return hitCount;}
|
||||
|
||||
std::vector<Hit> ReadBatch(unsigned int batchSize = 1000000, bool verbose = false); // output the sorted Hit
|
||||
|
||||
// std::string SaveHit(std::vector<Hit> hitList, bool isAppend = false);
|
||||
// std::string SaveHit2NewFile(std::string saveFolder = "./", std::string indexStr = "");
|
||||
// void SortAndSaveTS(unsigned int batchSize = 1000000, bool verbose = false);
|
||||
// off_t GetTSFileSize() const {return tsFileSize;}
|
||||
|
||||
//TODO
|
||||
//void SplitFile(unsigned long hitSizePreFile);
|
||||
|
||||
void PrintHit(ulong numHit = -1, ulong startIndex = 0) {
|
||||
for( ulong i = startIndex; i < std::min(numHit, hitCount); i++){
|
||||
printf("%10zu ", i); hit[i].Print();
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintHitListInfo(std::vector<Hit> * hitList, std::string name){
|
||||
size_t n = hitList->size();
|
||||
size_t s = sizeof(Hit);
|
||||
printf("============== %s, size : %zu | %.2f MByte\n", name.c_str(), n, n*s/1024./1024.);
|
||||
if( n > 0 ){
|
||||
printf("t0 : %15llu ns\n", hitList->front().timestamp);
|
||||
printf("t1 : %15llu ns\n", hitList->back().timestamp);
|
||||
printf("dt : %15.3f ms\n", (hitList->back().timestamp - hitList->front().timestamp)/1e6);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintHitListInfo(){
|
||||
size_t n = hit.size();
|
||||
size_t s = sizeof(Hit);
|
||||
printf("============== reader.hit, size : %zu | %.2f MByte\n", n, n*s/1024./1024.);
|
||||
if( n > 0 ){
|
||||
printf("t0 : %15llu ns\n", hit.at(0).timestamp);
|
||||
printf("t1 : %15llu ns\n", hit.back().timestamp);
|
||||
printf("dt : %15.3f ms\n", (hit.back().timestamp - hit.front().timestamp)/1e6);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//void SaveAsCAENCoMPASSFormat();
|
||||
|
||||
private:
|
||||
|
||||
FILE * inFile;
|
||||
Data * data;
|
||||
|
||||
std::string fileName;
|
||||
std::vector<std::string> fileList;
|
||||
short fileID;
|
||||
unsigned long inFileSize;
|
||||
unsigned int filePos;
|
||||
unsigned long totNumBlock;
|
||||
unsigned int blockID;
|
||||
|
||||
bool isDualBlock;
|
||||
|
||||
uShort sn;
|
||||
uShort DPPType;
|
||||
uShort tick2ns;
|
||||
uShort order;
|
||||
uShort chMask;
|
||||
uShort numCh;
|
||||
|
||||
std::vector<unsigned int> blockPos;
|
||||
std::vector<unsigned int > blockTimeStamp;
|
||||
|
||||
unsigned long hitCount;
|
||||
|
||||
std::vector<Hit> hit;
|
||||
|
||||
unsigned int word[1]; /// 4 byte
|
||||
size_t dummy;
|
||||
char * buffer;
|
||||
|
||||
off_t tsFileSize;
|
||||
|
||||
};
|
||||
|
||||
inline FSUReader::~FSUReader(){
|
||||
delete data;
|
||||
|
||||
if( inFile ) fclose(inFile);
|
||||
|
||||
}
|
||||
|
||||
inline FSUReader::FSUReader(){
|
||||
inFile = nullptr;
|
||||
data = nullptr;
|
||||
|
||||
blockPos.clear();
|
||||
blockTimeStamp.clear();
|
||||
hit.clear();
|
||||
|
||||
fileList.clear();
|
||||
fileID = -1;
|
||||
|
||||
}
|
||||
|
||||
inline FSUReader::FSUReader(std::string fileName, uInt dataSize, int verbose){
|
||||
inFile = nullptr;
|
||||
data = nullptr;
|
||||
|
||||
blockPos.clear();
|
||||
blockTimeStamp.clear();
|
||||
hit.clear();
|
||||
|
||||
fileList.clear();
|
||||
fileID = -1;
|
||||
OpenFile(fileName, dataSize, verbose);
|
||||
}
|
||||
|
||||
inline FSUReader::FSUReader(std::vector<std::string> fileList, uInt dataSize, int verbose){
|
||||
inFile = nullptr;
|
||||
data = nullptr;
|
||||
|
||||
blockPos.clear();
|
||||
blockTimeStamp.clear();
|
||||
hit.clear();
|
||||
//The files are the same DPPType and sn
|
||||
this->fileList = fileList;
|
||||
fileID = 0;
|
||||
OpenFile(fileList[fileID], dataSize, verbose);
|
||||
|
||||
}
|
||||
|
||||
inline void FSUReader::OpenFile(std::string fileName, uInt dataSize, int verbose){
|
||||
|
||||
/// File format must be YYY...Y_runXXX_AAA_BBB_TT_CCC.fsu
|
||||
/// YYY...Y = prefix
|
||||
/// XXX = runID, 3 digits
|
||||
/// AAA = board Serial Number, 3 digits
|
||||
/// BBB = DPPtype, 3 digits
|
||||
/// TT = tick2ns, any digits
|
||||
/// CCC = over size index, 3 digits
|
||||
|
||||
if( inFile != nullptr ) fclose(inFile);
|
||||
|
||||
inFile = fopen(fileName.c_str(), "r");
|
||||
|
||||
if( inFile == NULL ){
|
||||
printf("FSUReader::Cannot open file : %s \n", fileName.c_str());
|
||||
this->fileName = "";
|
||||
return;
|
||||
}
|
||||
|
||||
this->fileName = fileName;
|
||||
|
||||
fseek(inFile, 0L, SEEK_END);
|
||||
inFileSize = ftell(inFile);
|
||||
if(verbose) printf("%s | file size : %ld Byte = %.2f MB\n", fileName.c_str() , inFileSize, inFileSize/1024./1024.);
|
||||
fseek(inFile, 0L, SEEK_SET);
|
||||
filePos = 0;
|
||||
|
||||
if( fileID > 0 ) return;
|
||||
|
||||
totNumBlock = 0;
|
||||
blockID = 0;
|
||||
blockPos.clear();
|
||||
blockTimeStamp.clear();
|
||||
|
||||
hitCount = 0;
|
||||
hit.clear();
|
||||
|
||||
//check is the file is *.fsu or *.fsu.X
|
||||
size_t found = fileName.find_last_of('.');
|
||||
std::string ext = fileName.substr(found + 1);
|
||||
|
||||
if( ext.find("fsu") != std::string::npos ) {
|
||||
if(verbose > 1) printf("It is an raw data *.fsu format\n");
|
||||
isDualBlock = false;
|
||||
chMask = -1;
|
||||
}else{
|
||||
chMask = atoi(ext.c_str());
|
||||
isDualBlock = true;
|
||||
if(verbose > 1) printf("It is a splitted dual block data *.fsu.X format, dual channel mask : %d \n", chMask);
|
||||
}
|
||||
|
||||
std::string fileNameNoExt;
|
||||
found = fileName.find_last_of(".fsu");
|
||||
size_t found2 = fileName.find_last_of('/');
|
||||
if( found2 == std::string::npos ){
|
||||
fileNameNoExt = fileName.substr(0, found-4);
|
||||
}else{
|
||||
fileNameNoExt = fileName.substr(found2+1, found-4);
|
||||
}
|
||||
|
||||
// Split the string by underscores
|
||||
std::istringstream iss(fileNameNoExt);
|
||||
std::vector<std::string> tokens;
|
||||
std::string token;
|
||||
|
||||
while (std::getline(iss, token, '_')) { tokens.push_back(token); }
|
||||
sn = atoi(tokens[2].c_str());
|
||||
tick2ns = atoi(tokens[4].c_str());
|
||||
order = atoi(tokens[5].c_str());
|
||||
|
||||
DPPType = 0;
|
||||
if( fileName.find("PHA") != std::string::npos ) DPPType = DPPTypeCode::DPP_PHA_CODE;
|
||||
if( fileName.find("PSD") != std::string::npos ) DPPType = DPPTypeCode::DPP_PSD_CODE;
|
||||
if( fileName.find("QDC") != std::string::npos ) DPPType = DPPTypeCode::DPP_QDC_CODE;
|
||||
if( DPPType == 0 ){
|
||||
fclose(inFile);
|
||||
inFile = nullptr;
|
||||
printf("Cannot find DPPType in the filename. Abort.");
|
||||
return ;
|
||||
}
|
||||
|
||||
numCh = (DPPType == DPPTypeCode::DPP_QDC_CODE ? 64 : 16);
|
||||
|
||||
data = new Data(numCh, dataSize);
|
||||
data->tick2ns = tick2ns;
|
||||
data->boardSN = sn;
|
||||
data->DPPType = DPPType;
|
||||
|
||||
}
|
||||
|
||||
inline int FSUReader::ReadNextBlock(bool traceON, int verbose, uShort saveData){
|
||||
if( inFile == NULL ) return -1;
|
||||
if( feof(inFile) || filePos >= inFileSize) {
|
||||
if( fileID >= 0 && fileID + 1 < (short) fileList.size() ){
|
||||
printf("-------------- next file\n");
|
||||
fileID ++;
|
||||
OpenFile(fileList[fileID], data->GetDataSize(), 1 );
|
||||
}else{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
dummy = fread(word, 4, 1, inFile);
|
||||
fseek(inFile, -4, SEEK_CUR);
|
||||
|
||||
if( dummy != 1) {
|
||||
printf("fread error, should read 4 bytes, but read %ld x 4 byte, file pos: %ld / %ld byte\n",
|
||||
dummy, ftell(inFile), inFileSize);
|
||||
return -10;
|
||||
}
|
||||
short header = ((word[0] >> 28 ) & 0xF);
|
||||
|
||||
Hit temp;
|
||||
|
||||
if( header == 0xA ) { ///normal header
|
||||
|
||||
unsigned int aggSize = (word[0] & 0x0FFFFFFF) * 4; ///byte
|
||||
if( aggSize > inFileSize - ftell(inFile)) aggSize = inFileSize - ftell(inFile);
|
||||
buffer = new char[aggSize];
|
||||
dummy = fread(buffer, aggSize, 1, inFile);
|
||||
filePos = ftell(inFile);
|
||||
if( dummy != 1) {
|
||||
printf("fread error, should read %d bytes, but read %ld x %d byte, file pos: %ld / %ld byte \n",
|
||||
aggSize, dummy, aggSize, ftell(inFile), inFileSize);
|
||||
return -30;
|
||||
}
|
||||
|
||||
data->DecodeBuffer(buffer, aggSize, !traceON, verbose); // data will own the buffer
|
||||
//printf(" word Index = %u | filePos : %u | ", data->GetWordIndex(), filePos);
|
||||
|
||||
}else if( (header & 0xF ) == 0x8 ) { /// dual channel header
|
||||
|
||||
unsigned int dualSize = (word[0] & 0x7FFFFFFF) * 4; ///byte
|
||||
buffer = new char[dualSize];
|
||||
dummy = fread(buffer, dualSize, 1, inFile);
|
||||
filePos = ftell(inFile);
|
||||
|
||||
data->buffer = buffer;
|
||||
data->DecodeDualBlock(buffer, dualSize, DPPType, chMask, !traceON, verbose);
|
||||
|
||||
}else{
|
||||
printf("incorrect header.\n trminate.");
|
||||
return -20;
|
||||
}
|
||||
|
||||
unsigned int eventCout = 0;
|
||||
|
||||
for( int ch = 0; ch < data->GetNChannel(); ch++){
|
||||
if( data->NumEventsDecoded[ch] == 0 ) continue;
|
||||
|
||||
hitCount += data->NumEventsDecoded[ch];
|
||||
eventCout += data->NumEventsDecoded[ch];
|
||||
|
||||
if( saveData ){
|
||||
int start = data->GetDataIndex(ch) - data->NumEventsDecoded[ch] + 1;
|
||||
if( start < 0 ) start = start + data->GetDataSize();
|
||||
|
||||
for( int i = start; i < start + data->NumEventsDecoded[ch]; i++ ){
|
||||
int k = i % data->GetDataSize();
|
||||
|
||||
temp.sn = sn;
|
||||
temp.ch = ch;
|
||||
temp.energy = data->GetEnergy(ch, k);
|
||||
temp.energy2 = data->GetEnergy2(ch, k);
|
||||
temp.timestamp = data->GetTimestamp(ch, k);
|
||||
temp.fineTime = data->GetFineTime(ch, k);
|
||||
temp.pileUp = data->GetPileUp(ch, k);
|
||||
if( saveData > 1 ) {
|
||||
temp.traceLength = data->Waveform1[ch][k].size();
|
||||
temp.trace = data->Waveform1[ch][k];
|
||||
}else{
|
||||
temp.traceLength = 0;
|
||||
if( temp.trace.size() > 0 ) temp.trace.clear();
|
||||
}
|
||||
|
||||
hit.push_back(temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data->ClearTriggerRate();
|
||||
data->ClearNumEventsDecoded();
|
||||
data->ClearBuffer(); // this will clear the buffer.
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline int FSUReader::ReadBlock(unsigned int ID, int verbose){
|
||||
if( totNumBlock == 0 )return -1;
|
||||
if( ID >= totNumBlock )return -1;
|
||||
|
||||
data->ClearData();
|
||||
|
||||
fseek( inFile, 0L, SEEK_SET);
|
||||
|
||||
if( verbose ) printf("Block index: %u, File Pos: %u byte\n", ID, blockPos[ID]);
|
||||
|
||||
fseek(inFile, blockPos[ID], SEEK_CUR);
|
||||
filePos = blockPos[ID];
|
||||
blockID = ID;
|
||||
return ReadNextBlock(false, verbose, false);
|
||||
|
||||
}
|
||||
|
||||
inline void FSUReader::SortHit(int verbose){
|
||||
if( verbose) printf("\nQuick Sort hit array according to time...");
|
||||
std::sort(hit.begin(), hit.end(), [](const Hit& a, const Hit& b) {
|
||||
return a.timestamp < b.timestamp;
|
||||
});
|
||||
if( verbose) printf(".......done.\n");
|
||||
}
|
||||
|
||||
inline void FSUReader::ScanNumBlock(int verbose, uShort saveData){
|
||||
if( inFile == nullptr ) return;
|
||||
if( feof(inFile) ) return;
|
||||
|
||||
blockID = 0;
|
||||
blockPos.push_back(0);
|
||||
|
||||
data->ClearData();
|
||||
rewind(inFile);
|
||||
filePos = 0;
|
||||
|
||||
bool isTraceOn = saveData < 2 ? false : true;
|
||||
|
||||
while( ReadNextBlock(isTraceOn, verbose - 1, saveData) == 0 ){
|
||||
blockPos.push_back(filePos);
|
||||
blockTimeStamp.push_back(data->aggTime);
|
||||
blockID ++;
|
||||
if(verbose && blockID % 10000 == 0) printf("%u, %.2f%% %u/%lu\n\033[A\r", blockID, filePos*100./inFileSize, filePos, inFileSize);
|
||||
}
|
||||
|
||||
totNumBlock = blockID;
|
||||
if(verbose) {
|
||||
printf("\nScan complete: number of data Block : %lu\n", totNumBlock);
|
||||
printf( " number of hit : %lu", hitCount);
|
||||
if( hitCount > 1e6 ) printf(" = %.3f million", hitCount/1e6);
|
||||
printf("\n");
|
||||
if( saveData )printf( " size of the hit array : %lu\n", hit.size());
|
||||
|
||||
if( saveData ){
|
||||
size_t sizeT = sizeof(hit[0]) * hit.size();
|
||||
printf("size of hit array : %lu byte = %.2f kByte, = %.2f MByte\n", sizeT, sizeT/1024., sizeT/1024./1024.);
|
||||
}
|
||||
}
|
||||
|
||||
if( fileList.size() > 0 ){
|
||||
fileID = 0;
|
||||
OpenFile(fileList[fileID], data->GetDataSize(), 0);
|
||||
}
|
||||
|
||||
rewind(inFile);
|
||||
blockID = 0;
|
||||
filePos = 0;
|
||||
|
||||
//check is the hitCount == hit.size();
|
||||
if( saveData ){
|
||||
if( hitCount != hit.size()){
|
||||
printf("!!!!!! the Data::dataSize is not big enough. !!!!!!!!!!!!!!!\n");
|
||||
}else{
|
||||
SortHit(verbose+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline std::vector<Hit> FSUReader::ReadBatch(unsigned int batchSize, bool verbose){
|
||||
|
||||
// printf("%s sn:%d. filePos : %lu\n", __func__, sn, ftell(inFile));
|
||||
|
||||
std::vector<Hit> hitList_A;
|
||||
if( IsEndOfFile() ) {
|
||||
hitList_A = hit;
|
||||
hit.clear();
|
||||
return hitList_A;
|
||||
}
|
||||
|
||||
if( hit.size() == 0 ){
|
||||
int res = 0;
|
||||
do{
|
||||
res = ReadNextBlock(true, 0, 3);
|
||||
}while ( hit.size() < batchSize && res == 0);
|
||||
SortHit();
|
||||
uLong t0_B = hit.at(0).timestamp;
|
||||
uLong t1_B = hit.back().timestamp;
|
||||
if( verbose ) {
|
||||
printf(" hit in memeory : %7zu | %u | %lu \n", hit.size(), filePos, inFileSize);
|
||||
printf("t0 : %15lu ns\n", t0_B);
|
||||
printf("t1 : %15lu ns\n", t1_B);
|
||||
printf("dt : %15.3f ms\n", (t1_B - t0_B)/1e6);
|
||||
}
|
||||
|
||||
hitList_A = hit;
|
||||
hit.clear();
|
||||
|
||||
}else{
|
||||
|
||||
hitList_A = hit;
|
||||
hit.clear();
|
||||
|
||||
}
|
||||
|
||||
if( IsEndOfFile() ) return hitList_A; // when file finished for 1st batch read
|
||||
|
||||
int res = 0;
|
||||
do{
|
||||
res = ReadNextBlock(true, 0, 3);
|
||||
}while ( hit.size() < batchSize && res == 0);
|
||||
SortHit();
|
||||
uLong t0_B = hit.at(0).timestamp;
|
||||
uLong t1_B = hit.back().timestamp;
|
||||
|
||||
if( verbose ) {
|
||||
printf(" hit in memeory : %7zu | %u | %lu \n", hit.size(), filePos, inFileSize);
|
||||
printf("t0 : %15lu\n", t0_B);
|
||||
printf("t1 : %15lu\n", t1_B);
|
||||
printf("dt : %15.3f ms\n", (t1_B - t0_B)/1e6);
|
||||
}
|
||||
|
||||
uLong t0_A = hitList_A.at(0).timestamp;
|
||||
uLong t1_A = hitList_A.back().timestamp;
|
||||
ulong ID_A = 0;
|
||||
ulong ID_B = 0;
|
||||
|
||||
if( t0_A >= t0_B) {
|
||||
printf("\033[0;31m!!!!!!!!!!!!!!!!! %s | Need to increase the batch size. \033[0m\n", __func__);
|
||||
return std::vector<Hit> ();
|
||||
}
|
||||
|
||||
if( t1_A > t0_B) { // need to sort between two hitList
|
||||
|
||||
if( verbose ) {
|
||||
printf("############# need to sort \n");
|
||||
printf("=========== sume of A + B : %zu \n", hitList_A.size() + hit.size());
|
||||
}
|
||||
|
||||
std::vector<Hit> hitTemp;
|
||||
|
||||
// find the hit that is >= t0_B, save them to hitTemp
|
||||
for( size_t j = 0; j < hitList_A.size() ; j++){
|
||||
if( hitList_A[j].timestamp < t0_B ) continue;;
|
||||
if( ID_A == 0 ) ID_A = j;
|
||||
hitTemp.push_back(hitList_A[j]);
|
||||
}
|
||||
|
||||
// remove hitList_A element that is >= t0_B
|
||||
hitList_A.erase(hitList_A.begin() + ID_A, hitList_A.end() );
|
||||
|
||||
// find the hit that is <= t1_A, save them to hitTemp
|
||||
for( size_t j = 0; j < hit.size(); j++){
|
||||
if( hit[j].timestamp > t1_A ) {
|
||||
break;
|
||||
}
|
||||
hitTemp.push_back(hit[j]);
|
||||
ID_B = j + 1;
|
||||
}
|
||||
|
||||
// remove hit elements that is <= t1_A
|
||||
hit.erase(hit.begin(), hit.begin() + ID_B );
|
||||
|
||||
// sort hitTemp
|
||||
std::sort(hitTemp.begin(), hitTemp.end(), [](const Hit& a, const Hit& b) {
|
||||
return a.timestamp < b.timestamp;
|
||||
});
|
||||
|
||||
|
||||
if( verbose ) {
|
||||
printf("----------------- ID_A : %lu, Drop\n", ID_A);
|
||||
printf("----------------- ID_B : %lu, Drop\n", ID_B);
|
||||
PrintHitListInfo(&hitList_A, "hitList_A");
|
||||
PrintHitListInfo(&hitTemp, "hitTemp");
|
||||
PrintHitListInfo();
|
||||
printf("=========== sume of A + B + Temp : %zu \n", hitList_A.size() + hit.size() + hitTemp.size());
|
||||
printf("----------------- refill hitList_A \n");
|
||||
}
|
||||
|
||||
for( size_t j = 0; j < hitTemp.size(); j++){
|
||||
hitList_A.push_back(hitTemp[j]);
|
||||
}
|
||||
hitTemp.clear();
|
||||
|
||||
if( verbose ) {
|
||||
PrintHitListInfo(&hitList_A, "hitList_A");
|
||||
PrintHitListInfo();
|
||||
printf("=========== sume of A + B : %zu \n", hitList_A.size() + hit.size());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return hitList_A;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
inline void FSUReader::SortAndSaveTS(unsigned int batchSize, bool verbose){
|
||||
|
||||
int count = 0;
|
||||
std::vector<Hit> hitList_A ;
|
||||
|
||||
do{
|
||||
|
||||
if( verbose ) printf("***************************************************\n");
|
||||
|
||||
int res = 0;
|
||||
do{
|
||||
res = ReadNextBlock(true, 0, 3);
|
||||
}while ( hit.size() < batchSize && res == 0);
|
||||
|
||||
SortHit();
|
||||
uLong t0_B = hit.at(0).timestamp;
|
||||
uLong t1_B = hit.back().timestamp;
|
||||
|
||||
if( verbose ) {
|
||||
printf(" hit in memeory : %7zu | %u | %lu \n", hit.size(), filePos, inFileSize);
|
||||
printf("t0 : %15lu\n", t0_B);
|
||||
printf("t1 : %15lu\n", t1_B);
|
||||
}
|
||||
|
||||
if( count == 0 ) {
|
||||
hitList_A = hit; // copy hit
|
||||
}else{
|
||||
|
||||
uLong t0_A = hitList_A.at(0).timestamp;
|
||||
uLong t1_A = hitList_A.back().timestamp;
|
||||
ulong ID_A = 0;
|
||||
ulong ID_B = 0;
|
||||
|
||||
if( t0_A > t0_B) {
|
||||
printf("Need to increase the batch size. \n");
|
||||
return;
|
||||
}
|
||||
|
||||
if( t1_A > t0_B) { // need to sort between two hitList
|
||||
|
||||
if( verbose ) {
|
||||
printf("############# need to sort \n");
|
||||
printf("=========== sume of A + B : %zu \n", hitList_A.size() + hit.size());
|
||||
}
|
||||
|
||||
std::vector<Hit> hitTemp;
|
||||
|
||||
for( size_t j = 0; j < hitList_A.size() ; j++){
|
||||
if( hitList_A[j].timestamp < t0_B ) continue;
|
||||
if( ID_A == 0 ) ID_A = j;
|
||||
hitTemp.push_back(hitList_A[j]);
|
||||
}
|
||||
|
||||
hitList_A.erase(hitList_A.begin() + ID_A, hitList_A.end() );
|
||||
if( verbose ) {
|
||||
printf("----------------- ID_A : %lu, Drop\n", ID_A);
|
||||
PrintHitListInfo(hitList_A, "hitList_A");
|
||||
}
|
||||
|
||||
|
||||
for( size_t j = 0; j < hit.size(); j++){
|
||||
if( hit[j].timestamp > t1_A ) {
|
||||
ID_B = j;
|
||||
break;
|
||||
}
|
||||
hitTemp.push_back(hit[j]);
|
||||
}
|
||||
|
||||
std::sort(hitTemp.begin(), hitTemp.end(), [](const Hit& a, const Hit& b) {
|
||||
return a.timestamp < b.timestamp;
|
||||
});
|
||||
|
||||
hit.erase(hit.begin(), hit.begin() + ID_B );
|
||||
|
||||
if( verbose ) {
|
||||
PrintHitListInfo(hitTemp, "hitTemp");
|
||||
printf("----------------- ID_B : %lu, Drop\n", ID_B);
|
||||
PrintHitListInfo(hit, "hit");
|
||||
printf("=========== sume of A + B + Temp : %zu \n", hitList_A.size() + hit.size() + hitTemp.size());
|
||||
printf("----------------- refill hitList_A \n");
|
||||
}
|
||||
ulong ID_Temp = 0;
|
||||
for( size_t j = 0; j < hitTemp.size(); j++){
|
||||
hitList_A.push_back(hitTemp[j]);
|
||||
if( hitList_A.size() >= batchSize ) {
|
||||
ID_Temp = j+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hitTemp.erase(hitTemp.begin(), hitTemp.begin() + ID_Temp );
|
||||
for( size_t j = 0 ; j < hit.size(); j ++){
|
||||
hitTemp.push_back(hit[j]);
|
||||
}
|
||||
SaveHit(hitList_A, count <= 1 ? false : true);
|
||||
|
||||
if( verbose ) {
|
||||
PrintHitListInfo(hitList_A, "hitList_A");
|
||||
PrintHitListInfo(hitTemp, "hitTemp");
|
||||
printf("----------------- replace hitList_A by hitTemp \n");
|
||||
}
|
||||
|
||||
hitList_A.clear();
|
||||
hitList_A = hitTemp;
|
||||
hit.clear();
|
||||
|
||||
if( verbose ) {
|
||||
PrintHitListInfo(hitList_A, "hitList_A");
|
||||
printf("===========================================\n");
|
||||
}
|
||||
|
||||
}else{ // save hitList_A, replace hitList_A
|
||||
|
||||
SaveHit(hitList_A, count <= 1? false : true);
|
||||
hitList_A.clear();
|
||||
hitList_A = hit;
|
||||
if( verbose ) PrintHitListInfo(hitList_A, "hitList_A");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ClearHitList();
|
||||
count ++;
|
||||
}while(filePos < inFileSize);
|
||||
|
||||
SaveHit(hitList_A, count <= 1 ? false : true);
|
||||
|
||||
printf("================= finished.\n");
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
inline std::string FSUReader::SaveHit(std::vector<Hit> hitList, bool isAppend){
|
||||
|
||||
std::string outFileName;
|
||||
if( fileList.empty() ) {
|
||||
outFileName = fileName + ".ts" ;
|
||||
}else{
|
||||
outFileName = fileList[0] + ".ts" ;
|
||||
}
|
||||
uint64_t hitSize = hitList.size();
|
||||
|
||||
FILE * outFile ;
|
||||
if( isAppend ) {
|
||||
outFile = fopen(outFileName.c_str(), "rb+"); //read/write bineary
|
||||
|
||||
rewind(outFile);
|
||||
fseek( outFile, 4, SEEK_CUR);
|
||||
uint64_t org_hitSize;
|
||||
fread(&org_hitSize, 8, 1, outFile);
|
||||
|
||||
rewind(outFile);
|
||||
fseek( outFile, 4, SEEK_CUR);
|
||||
|
||||
org_hitSize += hitSize;
|
||||
|
||||
fwrite(&org_hitSize, 8, 1, outFile);
|
||||
fseek(outFile, 0, SEEK_END);
|
||||
|
||||
}else{
|
||||
outFile = fopen(outFileName.c_str(), "wb"); //overwrite binary
|
||||
uint32_t header = 0xAA000000;
|
||||
header += sn;
|
||||
fwrite( &header, 4, 1, outFile );
|
||||
fwrite( &hitSize, 8, 1, outFile);
|
||||
}
|
||||
|
||||
|
||||
for( ulong i = 0; i < hitSize; i++){
|
||||
|
||||
if( i% 10000 == 0 ) printf("Saving %lu/%lu Hit (%.2f%%)\n\033[A\r", i, hitSize, i*100./hitSize);
|
||||
|
||||
uint16_t flag = hitList[i].ch + (hitList[i].pileUp << 8) ;
|
||||
|
||||
if( DPPType == DPPTypeCode::DPP_PSD_CODE ) flag += ( 1 << 15);
|
||||
if( hitList[i].traceLength > 0 ) flag += (1 << 14);
|
||||
|
||||
// fwrite( &(hit[i].ch), 1, 1, outFile);
|
||||
fwrite( &flag, 2, 1, outFile);
|
||||
fwrite( &(hitList[i].energy), 2, 1, outFile);
|
||||
if( DPPType == DPPTypeCode::DPP_PSD_CODE ) fwrite( &(hitList[i].energy2), 2, 1, outFile);
|
||||
fwrite( &(hitList[i].timestamp), 6, 1, outFile);
|
||||
fwrite( &(hitList[i].fineTime), 2, 1, outFile);
|
||||
if( hitList[i].traceLength > 0 ) fwrite( &(hitList[i].traceLength), 2, 1, outFile);
|
||||
|
||||
for( uShort j = 0; j < hitList[i].traceLength; j++){
|
||||
fwrite( &(hitList[i].trace[j]), 2, 1, outFile);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
off_t tsFileSize = ftello(outFile); // unsigned int = Max ~4GB
|
||||
fclose(outFile);
|
||||
|
||||
printf("Saved to %s, size: ", outFileName.c_str());
|
||||
if( tsFileSize < 1024 ) {
|
||||
printf(" %ld Byte", tsFileSize);
|
||||
}else if( tsFileSize < 1024*1024 ) {
|
||||
printf(" %.2f kB", tsFileSize/1024.);
|
||||
}else if( tsFileSize < 1024*1024*1024){
|
||||
printf(" %.2f MB", tsFileSize/1024./1024.);
|
||||
}else{
|
||||
printf(" %.2f GB", tsFileSize/1024./1024./1024.);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
return outFileName;
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
56
Armory/macro.h
Normal file
56
Armory/macro.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#ifndef MACRO_H
|
||||
#define MACRO_H
|
||||
|
||||
#define MaxNPorts 4 //for optical link
|
||||
#define MaxNBoards 4 //for both optical link and usb
|
||||
|
||||
#define MaxNDigitizer MaxNPorts * MaxNBoards
|
||||
|
||||
#define MaxRegChannel 16
|
||||
#define MaxNChannels 64
|
||||
#define MaxRecordLength 0x3fff * 8
|
||||
#define MaxSaveFileSize 1024 * 1024 * 1024 * 2
|
||||
|
||||
#define MaxDisplayTraceTimeLength 20000 //ns
|
||||
#define ScopeUpdateMiliSec 200 // msec
|
||||
#define MaxNumberOfTrace 5 // in an event
|
||||
|
||||
#define SETTINGSIZE 2048
|
||||
|
||||
#define DAQLockFile "DAQLock.dat"
|
||||
#define PIDFile "pid.dat"
|
||||
|
||||
#include <sys/time.h> /** struct timeval, select() */
|
||||
|
||||
inline unsigned int getTime_us(){
|
||||
unsigned int time_us;
|
||||
struct timeval t1;
|
||||
struct timezone tz;
|
||||
gettimeofday(&t1, &tz);
|
||||
time_us = (t1.tv_sec) * 1000 * 1000 + t1.tv_usec;
|
||||
return time_us;
|
||||
}
|
||||
|
||||
#include <chrono>
|
||||
inline unsigned long long getTime_ns(){
|
||||
std::chrono::high_resolution_clock::time_point currentTime = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::nanoseconds nanoseconds = std::chrono::duration_cast<std::chrono::nanoseconds>(currentTime.time_since_epoch());
|
||||
return nanoseconds.count();
|
||||
}
|
||||
|
||||
typedef unsigned short uShort;
|
||||
typedef unsigned int uInt;
|
||||
typedef unsigned long uLong;
|
||||
typedef unsigned long long ullong;
|
||||
|
||||
#define DebugMode 0 //process check, when 1, print out all function call
|
||||
|
||||
// if DebugMode is 1, define DebugPrint() to be printf(), else, DebugPrint() define nothing
|
||||
#if DebugMode
|
||||
#define DebugPrint(fmt, ...) printf(fmt "::%s\n",##__VA_ARGS__, __func__);
|
||||
#else
|
||||
#define DebugPrint(fmt, ...)
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
3594
Armory/mass20.txt
3594
Armory/mass20.txt
File diff suppressed because it is too large
Load Diff
6
BatchProcess.sh
Normal file
6
BatchProcess.sh
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#!/bin/bash
|
||||
#parallel -j 6 echo ./ProcessRun.sh {1} 2000 0 ::: {020..400}
|
||||
|
||||
parallel --jobs 1 --results log/log_{}.txt --memfree 1G --ctag -j 1 ./ProcessRun.sh {1} 2000 0 ::: {109..400} # for 17F
|
||||
|
||||
# parallel --results log/log_{}.txt --ctag -j 6 ./ProcessRun.sh {1} 4000 0 ::: {5..21}
|
||||
45
CLAUDE.md
Normal file
45
CLAUDE.md
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# ANASEN Analysis — working notes for Claude
|
||||
|
||||
## Agent behavior
|
||||
- ALWAYS output a brief, step-by-step plan before modifying files or running commands.
|
||||
- Commit only when explicitly asked. Do not push to remote on your own initiative; pushing is allowed only when the session's instructions name a branch to push to (e.g. a `claude/*` working branch), and never to `devel_vignesh` or any other shared branch.
|
||||
- **Targeted edits only.** Never regenerate a whole file to make a local change.
|
||||
- **Never read `MakeVertex.C`** (4,003 lines, 240 KB). Same rule as `TrackRecon.C` below if you must touch it: grep, then Read with offset/limit.
|
||||
- **Never read `TrackRecon.C` end to end** (4,268 lines, 218 KB; ~60k tokens, and it stays in the prefix for the rest of the session). Grep for the symbol, then Read with offset/limit around the hit. If a question genuinely needs the whole file, say so first. Do not explore the codebase open-endedly.
|
||||
- **Do not spawn subagents** for work that can be done inline — each starts cold and re-derives context already loaded. Worth it only for wide parallel searches where the conclusion is all that's needed.
|
||||
- **You cannot verify your own work here.** No test suite, no CI, no data in the repo; validation is visual, via ROOT `TBrowser`, on a machine that has the data. Say explicitly when a change is unverified — never report a physics change as working because it compiled. The dangerous bugs are silent: a flipped sign in a kinematics term, an inverted spline direction, a wrong argument order, an off-by-one in a wire mask. None of them throw. The run completes, the plot renders, and the spectrum is wrong.
|
||||
- **Review once per coherent change, not per edit.** Scale depth to blast radius: scratch macros, shell, and docs are shallow; `TrackRecon.C`, `MakeVertex.C`, `Armory/`, or any calibration fit is deep and should tolerate uncertain findings — silent failure is what's being hunted. Run `/security-review` only for file I/O or shell interpolation in the `run_*.sh` drivers; it is not a physics tool.
|
||||
|
||||
## Build & execution
|
||||
- ROOT macros (`.C`) compile via ACLiC inside ROOT. Do not link into binaries.
|
||||
- Standalone binaries (`EventBuilder`, `Mapper`): `cd Armory && make`
|
||||
- Pre-compile for parallel runs: `root -q -l -b -e '.L TrackRecon.C++O'`
|
||||
- Batch runs: `./run_17F.sh`, `./run_27Al.sh`, `./run_tr.sh`
|
||||
- `run_tr.sh` gates its stages with `if [[ 1 -eq 0 ]]` / `if [[ 1 -eq 1 ]]`. Flip the literal to enable a block.
|
||||
- Data is NOT in the repo. Mapped ROOT files live at `../ANASEN_analysis/data/${DATASET}_Data/${PREFIX}${run}_mapped.root`.
|
||||
|
||||
## Runtime configuration
|
||||
All config is `getenv`-driven; the run scripts `export` it. Defaults are in `TrackRecon.C::Begin()`.
|
||||
|
||||
Physics / results-affecting:
|
||||
`DATASET` `reactiondata` `CO2percent` `pressure_in_torr` `CATHODE_GAIN` `PC_ENERGY_CALIBRATION`
|
||||
`source_vertex` `CUTLIST` `DITHER_SIGMA` `RNG_SEED` `BEAM_AXIS_X` `BEAM_AXIS_Y`
|
||||
`timecut_low` `timecut_high` `DISABLE_BAD_ANODE_WIRES` `A1C1_LOWBAND_RFACTOR`
|
||||
`A1C1_Z_SCALE_QQQ` `A1C1_Z_SCALE_SX3` `A1C1_Z_OFF_QQQ` `A1C1_Z_OFF_SX3`
|
||||
|
||||
Plumbing: `OUT_DIR` `RUN_NUMBER` `FLUSH_BARRIER` `MAX_RSS_MB` `MEMCHECK_STRIDE`
|
||||
|
||||
**`DEDX_SCALE` is read by `eloss_calculations/Eloss.py`, NOT by `TrackRecon.C`.**
|
||||
It multiplies `catima.dedx` when the lookup tables are generated. Changing it invalidates every `.dat` table — re-run `Eloss.py` (the run scripts do this automatically before processing). Tables are keyed `eloss_calculations/<species>_lookup_<E>MeV_<P>torr_<CO2>pc.dat` (e.g. `alpha_lookup_50MeV_250torr_3pc.dat`).
|
||||
|
||||
## Footguns
|
||||
- **`HistPlotter` keys `oMap` by histogram NAME only.** The folder argument does not namespace. Two `Fill` calls with the same name and the *same* folder merge silently — no warning, no error, wrong plot. A same-name/different-folder (or toplevel-vs-folder) clash does print to `stderr` and then merges anyway, but the run scripts send stdout+stderr to `/dev/null`, so in a batch run it is invisible either way. Always make names unique.
|
||||
- **Si–PC coincidence sits at negative dt.** The band is centred well below zero, not near it. The one gate of record is `siPcCoincident()` in `TrackRecon.C` (`kSiPcDtMin = -500`, `kSiPcDtMax = 150` ns); anything else is a plotting range, not a cut. Do not "correct" a timing gate toward zero.
|
||||
- **RNG**: in `TrackRecon.C`, one shared `anasenRandom` (`TRandom3`, seed 4357, `RNG_SEED` to override), aliased by reference wherever a local `rnd`/`rand` appears — do not introduce fresh `TRandom3(0)` instances; it breaks reproducibility. `MakeVertex.C` predates this and still has ~12 independent default-seeded `TRandom3` objects; do not assume its output is reproducible against `TrackRecon.C`.
|
||||
- **Pass By Reference in Event Loops:** Always iterate via `const auto&` over cluster/hit vectors to prevent expensive struct copies in multi-million event runs.
|
||||
|
||||
## Invariants to check on any physics change
|
||||
Units (MeV vs keV, cm vs mm); sign conventions on `z` and beam direction; eloss spline domain and extrapolation; energy conservation in the kinematics; dead-wire / neighbour masking; RNG seeding. When reviewing, include the `.dat` and `run_*.sh` parameter deltas — the most consequential recent changes have been constants, not code.
|
||||
|
||||
## Docs
|
||||
`README.md` has the pipeline documentation and full file references.
|
||||
270
CalibrationQQQ.C
Normal file
270
CalibrationQQQ.C
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
|
||||
#define CalibrationQQQ_cxx
|
||||
|
||||
#include <TH2.h>
|
||||
#include <TF1.h>
|
||||
#include <TStyle.h>
|
||||
#include <TCanvas.h>
|
||||
#include <TMath.h>
|
||||
#include <TCutG.h>
|
||||
#include <fstream>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include "Armory/HistPlotter.h"
|
||||
#include "TVector3.h"
|
||||
#include "Calibration.h"
|
||||
|
||||
TH2F *hQQQFVB;
|
||||
HistPlotter *plotter;
|
||||
int padID = 0;
|
||||
|
||||
TCutG *cut;
|
||||
std::map<std::tuple<int, int, int>, std::vector<std::pair<double, double>>> dataPoints;
|
||||
|
||||
bool qqqEcut = false;
|
||||
|
||||
// Gain Arrays
|
||||
const int MAX_QQQ = 4;
|
||||
const int MAX_RING = 16;
|
||||
const int MAX_WEDGE = 16;
|
||||
double qqqwGain[MAX_QQQ][MAX_RING][MAX_WEDGE] = {{{0}}};
|
||||
// double qqqrGain[MAX_QQQ][MAX_RING][MAX_WEDGE] = {{{0}}};
|
||||
bool qqqwGainValid[MAX_QQQ][MAX_RING][MAX_WEDGE] = {{{false}}};
|
||||
// bool qqqrGainValid[MAX_QQQ][MAX_RING][MAX_WEDGE] = {{{false}}};
|
||||
|
||||
void CalibrationQQQ::Begin(TTree * /*tree*/)
|
||||
{
|
||||
plotter = new HistPlotter("Calib.root", "TFILE");
|
||||
// ----------------------- Load QQQ Gains
|
||||
{
|
||||
std::string filename = "qqq_GainMatch.dat";
|
||||
std::ifstream infile(filename);
|
||||
if (!infile.is_open())
|
||||
{
|
||||
std::cerr << "Error opening " << filename << "!" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
int det, ring, wedge;
|
||||
double gainw, gainr;
|
||||
while (infile >> det >> ring >> wedge >> gainw >> gainr)
|
||||
{
|
||||
qqqwGain[det][ring][wedge] = gainw;
|
||||
// qqqrGain[det][ring][wedge] = gainr;
|
||||
qqqwGainValid[det][ring][wedge] = (gainw > 0);
|
||||
// qqqrGainValid[det][ring][wedge] = (gainr > 0);
|
||||
}
|
||||
infile.close();
|
||||
std::cout << "Loaded QQQ gains from " << filename << std::endl;
|
||||
}
|
||||
}
|
||||
for (int det = 0; det < MAX_QQQ; det++)
|
||||
{
|
||||
for (int ring = 0; ring < MAX_RING; ring++)
|
||||
{
|
||||
for (int wedge = 0; wedge < MAX_WEDGE; wedge++)
|
||||
{
|
||||
TString hname = Form("hCal_qqq%d_ring%d_wedge%d", det, ring, wedge);
|
||||
TString htitle = Form("QQQ det%d ring%d wedge%d; Energy (arb); Counts", det, ring, wedge);
|
||||
// hQQQSpectra[det][ring][wedge] = new TH1F(hname, htitle, 4000, 0, 16000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bool_t CalibrationQQQ::Process(Long64_t entry)
|
||||
{
|
||||
b_qqqMulti->GetEntry(entry);
|
||||
b_qqqID->GetEntry(entry);
|
||||
b_qqqCh->GetEntry(entry);
|
||||
b_qqqE->GetEntry(entry);
|
||||
b_qqqT->GetEntry(entry);
|
||||
|
||||
qqq.CalIndex();
|
||||
|
||||
for (int i = 0; i < qqq.multi; i++)
|
||||
{
|
||||
for (int j = i + 1; j < qqq.multi; j++)
|
||||
{
|
||||
if (qqq.e[i] > 100)
|
||||
qqqEcut = true;
|
||||
if (qqq.id[i] == qqq.id[j])
|
||||
{
|
||||
int chWedge = -1;
|
||||
int chRing = -1;
|
||||
float eWedgeRaw = 0.0;
|
||||
float eWedge = 0.0;
|
||||
float eRingRaw = 0.0;
|
||||
float eRing = 0.0;
|
||||
if (qqq.ch[i] < 16 && qqq.ch[j] >= 16 && /*qqqrGainValid[qqq.id[i]][qqq.ch[i]][qqq.ch[j] - 16] &&*/ qqqwGainValid[qqq.id[i]][qqq.ch[i]][qqq.ch[j] - 16])
|
||||
{
|
||||
chWedge = qqq.ch[i];
|
||||
eWedgeRaw = qqq.e[i];
|
||||
eWedge = qqq.e[i] * qqqwGain[qqq.id[i]][qqq.ch[i]][qqq.ch[j] - 16];
|
||||
// printf("Wedge E: %.2f Gain: %.4f \n", eWedge, qqqGain[qqq.id[i]][qqq.ch[i]][qqq.ch[j] - 16]);
|
||||
chRing = qqq.ch[j] - 16;
|
||||
eRingRaw = qqq.e[j];
|
||||
eRing = qqq.e[j];// * qqqrGain[qqq.id[j]][qqq.ch[j]][qqq.ch[i] - 16];
|
||||
}
|
||||
else if (qqq.ch[j] < 16 && qqq.ch[i] >= 16 && /*qqqrGainValid[qqq.id[j]][qqq.ch[j]][qqq.ch[i] - 16] &&*/ qqqwGainValid[qqq.id[j]][qqq.ch[j]][qqq.ch[i] - 16])
|
||||
{
|
||||
chWedge = qqq.ch[j];
|
||||
eWedge = qqq.e[j] * qqqwGain[qqq.id[j]][qqq.ch[j]][qqq.ch[i] - 16];
|
||||
eWedgeRaw = qqq.e[j];
|
||||
|
||||
chRing = qqq.ch[i] - 16;
|
||||
eRing = qqq.e[i];// * qqqrGain[qqq.id[i]][qqq.ch[i]][qqq.ch[j] - 16];
|
||||
eRingRaw = qqq.e[i];
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
// hQQQFVB->Fill(eWedge, eRing);
|
||||
plotter->Fill2D(Form("hRaw_qqq%d_ring%d_wedge%d", qqq.id[i], chRing, chWedge), 400, 0, 16000, 400, 0, 16000, eWedgeRaw, eRingRaw, "ERaw");
|
||||
plotter->Fill2D(Form("hGM_qqq%d_ring%d_wedge%d", qqq.id[i], chRing, chWedge), 400, 0, 16000, 400, 0, 16000, eWedge, eRing, "EGM");
|
||||
plotter->Fill2D("hRawQQQ", 4000, 0, 16000, 4000, 0, 16000, eWedgeRaw, eRingRaw);
|
||||
plotter->Fill2D("hGMQQQ", 4000, 0, 16000, 4000, 0, 16000, eWedge, eRing);
|
||||
|
||||
TString histName = Form("hQQQFVB_id%d_r%d_w%d", qqq.id[i], chRing, chWedge);
|
||||
// TH2F *hist2d = (TH2F *)gDirectory->Get(histName);
|
||||
// if (!hist2d)
|
||||
// {
|
||||
// hist2d = new TH2F(histName, Form("QQQ Det%d R%d W%d;Wedge E;Ring E", qqq.id[i], chRing, chWedge), 400, 0, 16000, 400, 0, 16000);
|
||||
// }
|
||||
|
||||
// hist2d->Fill(eWedge, eRing);
|
||||
// if (cut && cut->IsInside(eWedge, eRing))
|
||||
const double MIN_ADC = 1500.0;
|
||||
const double MAX_ADC = 3000.0;
|
||||
|
||||
// if (eWedge >= MIN_ADC && eWedge <= MAX_ADC &&
|
||||
// eRing >= MIN_ADC && eRing <= MAX_ADC)
|
||||
double ratio = (eWedge > 0.0) ? (eRing / eWedge) : 0.0;
|
||||
|
||||
double maxslope = 1.5;
|
||||
|
||||
bool validPoint = false;
|
||||
if (ratio < maxslope && ratio > 1. / maxslope)
|
||||
{
|
||||
// Accumulate data for gain matching
|
||||
dataPoints[{qqq.id[i], chRing, chWedge}].emplace_back(eWedge, eRing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return kTRUE;
|
||||
}
|
||||
|
||||
void CalibrationQQQ::Terminate()
|
||||
{
|
||||
const double AM241_PEAK = 5485.56;
|
||||
const double P_PEAK = 7000; // keV
|
||||
|
||||
double calibArray[MAX_QQQ][MAX_RING][MAX_WEDGE] = {{{0}}};
|
||||
bool calibValid[MAX_QQQ][MAX_RING][MAX_WEDGE] = {{{false}}};
|
||||
|
||||
std::ofstream outFile("qqq_Calib.dat");
|
||||
if (!outFile.is_open())
|
||||
{
|
||||
std::cerr << "Error opening qqq_Calib.dat!" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// 1. Create per–channel 1D spectra in ADC from stored gain-matched data
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
std::map<std::tuple<int, int, int>, TH1F *> spectra;
|
||||
|
||||
for (auto &kv : dataPoints)
|
||||
{
|
||||
int det, ring, wedge;
|
||||
std::tie(det, ring, wedge) = kv.first;
|
||||
|
||||
TString hname = Form("hSpec_d%d_r%d_w%d", det, ring, wedge);
|
||||
TH1F *h = new TH1F(hname, hname, 4000, 0, 16000);
|
||||
|
||||
for (auto &p : kv.second)
|
||||
{
|
||||
double eWedge = p.first; // already gain-matched ADC
|
||||
double eRing = p.second;
|
||||
|
||||
// Use ring ADC for calibration (cleaner alpha peak)
|
||||
h->Fill(eRing);
|
||||
}
|
||||
|
||||
spectra[kv.first] = h;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// 2. Fit Am-241 peak and extract keV/ADC calibration slope
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
for (auto &kv : spectra)
|
||||
{
|
||||
int det, ring, wedge;
|
||||
std::tie(det, ring, wedge) = kv.first;
|
||||
TH1F *h = kv.second;
|
||||
|
||||
if (!h || h->GetEntries() < 50)
|
||||
continue;
|
||||
|
||||
int binMax = h->GetMaximumBin();
|
||||
double adcPeak = h->GetXaxis()->GetBinCenter(binMax);
|
||||
|
||||
if (adcPeak <= 0)
|
||||
continue;
|
||||
|
||||
double slope_keV = AM241_PEAK / adcPeak; // keV per ADC
|
||||
// double slope_keV = P_PEAK / adcPeak; // keV per ADC
|
||||
|
||||
calibArray[det][ring][wedge] = slope_keV;
|
||||
calibValid[det][ring][wedge] = true;
|
||||
|
||||
outFile << det << " " << wedge << " " << ring << " "
|
||||
<< slope_keV << "\n";
|
||||
|
||||
// printf("QQQ DET=%d R=%d W=%d ADCpeak=%.1f slope_keV=%.6f\n",det, ring, wedge, adcPeak, slope_keV);
|
||||
}
|
||||
|
||||
outFile.close();
|
||||
std::cout << "Wrote QQQ calibration file qqq_Calib.dat\n";
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// 3. Build fully calibrated 2D combined histogram
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
TH2F *hCal = new TH2F("hCal",
|
||||
"All QQQ Calibrated;Wedge Energy (keV);Ring Energy (keV)",
|
||||
800, 0, 7000,
|
||||
800, 0, 7000);
|
||||
|
||||
for (auto &kv : dataPoints)
|
||||
{
|
||||
int det, ring, wedge;
|
||||
std::tie(det, ring, wedge) = kv.first;
|
||||
|
||||
if (!calibValid[det][ring][wedge])
|
||||
continue;
|
||||
|
||||
double slope = calibArray[det][ring][wedge];
|
||||
|
||||
for (auto &p : kv.second)
|
||||
{
|
||||
double eWGM = p.first; // gain matched ADC
|
||||
double eRGM = p.second;
|
||||
|
||||
double eWkeV = eWGM * slope / 1000;
|
||||
double eRkeV = eRGM * slope / 1000;
|
||||
|
||||
hCal->Fill(eWkeV, eRkeV);
|
||||
plotter->Fill2D("hCalQQQ", 4000, 0, 10, 4000, 0, 10, eWkeV, eRkeV);
|
||||
plotter->Fill2D(Form("hRCal_qqq%d", det), 16, 0, 15, 400, 0, 24, ring, eRkeV, "RingCal");
|
||||
plotter->Fill2D(Form("hWCal_qqq%d", det), 16, 0, 15, 400, 0, 24, wedge, eWkeV, "WedgeCal");
|
||||
}
|
||||
}
|
||||
|
||||
plotter->FlushToDisk();
|
||||
std::cout << "Calibrated 2D QQQ histogram saved.\n";
|
||||
}
|
||||
|
|
@ -1,114 +1,114 @@
|
|||
#ifndef gainmatch_h
|
||||
#define gainmatch_h
|
||||
|
||||
#include <TROOT.h>
|
||||
#include <TChain.h>
|
||||
#include <TFile.h>
|
||||
#include <TSelector.h>
|
||||
|
||||
#include "Armory/ClassDet.h"
|
||||
|
||||
class gainmatch : public TSelector {
|
||||
public :
|
||||
TTree *fChain; //!pointer to the analyzed TTree or TChain
|
||||
|
||||
// Fixed size dimensions of array or collections stored in the TTree if any.
|
||||
|
||||
// Declaration of leaf types
|
||||
Det sx3;
|
||||
Det qqq;
|
||||
Det pc ;
|
||||
|
||||
ULong64_t evID;
|
||||
UInt_t run;
|
||||
|
||||
// List of branches
|
||||
TBranch *b_eventID; //!
|
||||
TBranch *b_run; //!
|
||||
TBranch *b_sx3Multi; //!
|
||||
TBranch *b_sx3ID; //!
|
||||
TBranch *b_sx3Ch; //!
|
||||
TBranch *b_sx3E; //!
|
||||
TBranch *b_sx3T; //!
|
||||
TBranch *b_qqqMulti; //!
|
||||
TBranch *b_qqqID; //!
|
||||
TBranch *b_qqqCh; //!
|
||||
TBranch *b_qqqE; //!
|
||||
TBranch *b_qqqT; //!
|
||||
TBranch *b_pcMulti; //!
|
||||
TBranch *b_pcID; //!
|
||||
TBranch *b_pcCh; //!
|
||||
TBranch *b_pcE; //!
|
||||
TBranch *b_pcT; //!
|
||||
|
||||
gainmatch(TTree * /*tree*/ =0) : fChain(0) { }
|
||||
virtual ~gainmatch() { }
|
||||
virtual Int_t Version() const { return 2; }
|
||||
virtual void Begin(TTree *tree);
|
||||
virtual void SlaveBegin(TTree *tree);
|
||||
virtual void Init(TTree *tree);
|
||||
virtual Bool_t Notify();
|
||||
virtual Bool_t Process(Long64_t entry);
|
||||
virtual Int_t GetEntry(Long64_t entry, Int_t getall = 0) { return fChain ? fChain->GetTree()->GetEntry(entry, getall) : 0; }
|
||||
virtual void SetOption(const char *option) { fOption = option; }
|
||||
virtual void SetObject(TObject *obj) { fObject = obj; }
|
||||
virtual void SetInputList(TList *input) { fInput = input; }
|
||||
virtual TList *GetOutputList() const { return fOutput; }
|
||||
virtual void SlaveTerminate();
|
||||
virtual void Terminate();
|
||||
|
||||
ClassDef(gainmatch,0);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef gainmatch_cxx
|
||||
void gainmatch::Init(TTree *tree){
|
||||
|
||||
// Set branch addresses and branch pointers
|
||||
if (!tree) return;
|
||||
fChain = tree;
|
||||
fChain->SetMakeClass(1);
|
||||
|
||||
fChain->SetBranchAddress("evID", &evID, &b_eventID);
|
||||
fChain->SetBranchAddress("run", &run, &b_run);
|
||||
|
||||
sx3.SetDetDimension(24,12);
|
||||
qqq.SetDetDimension(4,32);
|
||||
pc.SetDetDimension(2,24);
|
||||
|
||||
fChain->SetBranchAddress("sx3Multi", &sx3.multi, &b_sx3Multi);
|
||||
fChain->SetBranchAddress("sx3ID", &sx3.id, &b_sx3ID);
|
||||
fChain->SetBranchAddress("sx3Ch", &sx3.ch, &b_sx3Ch);
|
||||
fChain->SetBranchAddress("sx3E", &sx3.e, &b_sx3E);
|
||||
fChain->SetBranchAddress("sx3T", &sx3.t, &b_sx3T);
|
||||
fChain->SetBranchAddress("qqqMulti", &qqq.multi, &b_qqqMulti);
|
||||
fChain->SetBranchAddress("qqqID", &qqq.id, &b_qqqID);
|
||||
fChain->SetBranchAddress("qqqCh", &qqq.ch, &b_qqqCh);
|
||||
fChain->SetBranchAddress("qqqE", &qqq.e, &b_qqqE);
|
||||
fChain->SetBranchAddress("qqqT", &qqq.t, &b_qqqT);
|
||||
fChain->SetBranchAddress("pcMulti", &pc.multi, &b_pcMulti);
|
||||
fChain->SetBranchAddress("pcID", &pc.id, &b_pcID);
|
||||
fChain->SetBranchAddress("pcCh", &pc.ch, &b_pcCh);
|
||||
fChain->SetBranchAddress("pcE", &pc.e, &b_pcE);
|
||||
fChain->SetBranchAddress("pcT", &pc.t, &b_pcT);
|
||||
|
||||
}
|
||||
|
||||
Bool_t gainmatch::Notify(){
|
||||
|
||||
return kTRUE;
|
||||
}
|
||||
|
||||
void gainmatch::SlaveBegin(TTree * /*tree*/){
|
||||
|
||||
TString option = GetOption();
|
||||
|
||||
}
|
||||
|
||||
void gainmatch::SlaveTerminate(){
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // #ifdef gainmatch_cxx
|
||||
#ifndef CalibrationQQQ_h
|
||||
#define CalibrationQQQ_h
|
||||
|
||||
#include <TROOT.h>
|
||||
#include <TChain.h>
|
||||
#include <TFile.h>
|
||||
#include <TSelector.h>
|
||||
|
||||
#include "Armory/ClassDet.h"
|
||||
|
||||
class CalibrationQQQ : public TSelector {
|
||||
public :
|
||||
TTree *fChain; //!pointer to the analyzed TTree or TChain
|
||||
|
||||
// Fixed size dimensions of array or collections stored in the TTree if any.
|
||||
|
||||
// Declaration of leaf types
|
||||
Det sx3;
|
||||
Det qqq;
|
||||
Det pc ;
|
||||
|
||||
ULong64_t evID;
|
||||
UInt_t run;
|
||||
|
||||
// List of branches
|
||||
TBranch *b_eventID; //!
|
||||
TBranch *b_run; //!
|
||||
TBranch *b_sx3Multi; //!
|
||||
TBranch *b_sx3ID; //!
|
||||
TBranch *b_sx3Ch; //!
|
||||
TBranch *b_sx3E; //!
|
||||
TBranch *b_sx3T; //!
|
||||
TBranch *b_qqqMulti; //!
|
||||
TBranch *b_qqqID; //!
|
||||
TBranch *b_qqqCh; //!
|
||||
TBranch *b_qqqE; //!
|
||||
TBranch *b_qqqT; //!
|
||||
TBranch *b_pcMulti; //!
|
||||
TBranch *b_pcID; //!
|
||||
TBranch *b_pcCh; //!
|
||||
TBranch *b_pcE; //!
|
||||
TBranch *b_pcT; //!
|
||||
|
||||
CalibrationQQQ(TTree * /*tree*/ =0) : fChain(0) { }
|
||||
virtual ~CalibrationQQQ() { }
|
||||
virtual Int_t Version() const { return 2; }
|
||||
virtual void Begin(TTree *tree);
|
||||
virtual void SlaveBegin(TTree *tree);
|
||||
virtual void Init(TTree *tree);
|
||||
virtual Bool_t Notify();
|
||||
virtual Bool_t Process(Long64_t entry);
|
||||
virtual Int_t GetEntry(Long64_t entry, Int_t getall = 0) { return fChain ? fChain->GetTree()->GetEntry(entry, getall) : 0; }
|
||||
virtual void SetOption(const char *option) { fOption = option; }
|
||||
virtual void SetObject(TObject *obj) { fObject = obj; }
|
||||
virtual void SetInputList(TList *input) { fInput = input; }
|
||||
virtual TList *GetOutputList() const { return fOutput; }
|
||||
virtual void SlaveTerminate();
|
||||
virtual void Terminate();
|
||||
|
||||
ClassDef(CalibrationQQQ,0);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CalibrationQQQ_cxx
|
||||
void CalibrationQQQ::Init(TTree *tree){
|
||||
|
||||
// Set branch addresses and branch pointers
|
||||
if (!tree) return;
|
||||
fChain = tree;
|
||||
fChain->SetMakeClass(1);
|
||||
|
||||
fChain->SetBranchAddress("evID", &evID, &b_eventID);
|
||||
fChain->SetBranchAddress("run", &run, &b_run);
|
||||
|
||||
sx3.SetDetDimension(24,12);
|
||||
qqq.SetDetDimension(4,32);
|
||||
pc.SetDetDimension(2,24);
|
||||
|
||||
fChain->SetBranchAddress("sx3Multi", &sx3.multi, &b_sx3Multi);
|
||||
fChain->SetBranchAddress("sx3ID", &sx3.id, &b_sx3ID);
|
||||
fChain->SetBranchAddress("sx3Ch", &sx3.ch, &b_sx3Ch);
|
||||
fChain->SetBranchAddress("sx3E", &sx3.e, &b_sx3E);
|
||||
fChain->SetBranchAddress("sx3T", &sx3.t, &b_sx3T);
|
||||
fChain->SetBranchAddress("qqqMulti", &qqq.multi, &b_qqqMulti);
|
||||
fChain->SetBranchAddress("qqqID", &qqq.id, &b_qqqID);
|
||||
fChain->SetBranchAddress("qqqCh", &qqq.ch, &b_qqqCh);
|
||||
fChain->SetBranchAddress("qqqE", &qqq.e, &b_qqqE);
|
||||
fChain->SetBranchAddress("qqqT", &qqq.t, &b_qqqT);
|
||||
fChain->SetBranchAddress("pcMulti", &pc.multi, &b_pcMulti);
|
||||
fChain->SetBranchAddress("pcID", &pc.id, &b_pcID);
|
||||
fChain->SetBranchAddress("pcCh", &pc.ch, &b_pcCh);
|
||||
fChain->SetBranchAddress("pcE", &pc.e, &b_pcE);
|
||||
fChain->SetBranchAddress("pcT", &pc.t, &b_pcT);
|
||||
|
||||
}
|
||||
|
||||
Bool_t CalibrationQQQ::Notify(){
|
||||
|
||||
return kTRUE;
|
||||
}
|
||||
|
||||
void CalibrationQQQ::SlaveBegin(TTree * /*tree*/){
|
||||
|
||||
TString option = GetOption();
|
||||
|
||||
}
|
||||
|
||||
void CalibrationQQQ::SlaveTerminate(){
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // #ifdef CalibrationQQQ_cxx
|
||||
124
FitHistogramsWithTSpectrum_Sequential_Improved.C
Normal file
124
FitHistogramsWithTSpectrum_Sequential_Improved.C
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
#include <TFile.h>
|
||||
#include <TH1.h>
|
||||
#include <TSpectrum.h>
|
||||
#include <TF1.h>
|
||||
#include <TCanvas.h>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <TText.h>
|
||||
|
||||
void FitHistogramsWithTSpectrum_Sequential_Improved() {
|
||||
TFile *inputFile = new TFile("Histograms_anodes.root", "READ");
|
||||
if (!inputFile || inputFile->IsZombie()) {
|
||||
std::cerr << "Error opening the input file!" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
TCanvas *c1 = new TCanvas("c1", "Histogram Viewer", 800, 600);
|
||||
|
||||
// Open the output ASCII file to save the centroids
|
||||
std::ofstream outFile("centroids.txt");
|
||||
if (!outFile.is_open()) {
|
||||
std::cerr << "Error opening output file!" << std::endl;
|
||||
return;
|
||||
}
|
||||
outFile << "HistogramIndex\tPeakNumber\tCentroid\tAmplitude\tSigma" << std::endl;
|
||||
|
||||
for (int i = 0; i < 24; ++i) {
|
||||
TH1 *histogram = dynamic_cast<TH1*>(inputFile->Get(Form("hCathode_%d", i)));
|
||||
if (!histogram) {
|
||||
std::cerr << "Failed to retrieve histogram_" << i << " from the file." << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set range for peak search
|
||||
double minX = 700;
|
||||
double maxX = 25000;
|
||||
histogram->GetXaxis()->SetRangeUser(minX, maxX);
|
||||
|
||||
// Draw the histogram
|
||||
c1->cd();
|
||||
histogram->Draw();
|
||||
|
||||
// Peak search using TSpectrum
|
||||
const int maxPeaks = 5;
|
||||
TSpectrum spectrumFinder(maxPeaks);
|
||||
int nFound = spectrumFinder.Search(histogram, 2, "", 0.01);
|
||||
|
||||
if (nFound <= 0) {
|
||||
std::cerr << "No peaks found for histogram " << i << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
Double_t *xPositions = spectrumFinder.GetPositionX();
|
||||
Double_t *yPositions = spectrumFinder.GetPositionY();
|
||||
std::vector<std::pair<Double_t, Double_t>> peaks;
|
||||
|
||||
// Collect and sort peaks by X position
|
||||
for (int j = 0; j < nFound; ++j) {
|
||||
peaks.emplace_back(xPositions[j], yPositions[j]);
|
||||
}
|
||||
std::sort(peaks.begin(), peaks.end());
|
||||
|
||||
// Fit each peak with a Gaussian
|
||||
for (int j = 0; j < peaks.size(); ++j) {
|
||||
Double_t peakX = peaks[j].first;
|
||||
Double_t peakY = peaks[j].second;
|
||||
Double_t initialAmplitude = peakY; // Better initial guess
|
||||
Double_t initialCentroid = peakX; // Centroid based on peak position
|
||||
Double_t initialSigma = 60.0;
|
||||
// Define Gaussian with initial parameters
|
||||
TF1 *gaussFit = new TF1(Form("gauss_%d", j), "gaus", peakX - 200, peakX + 200);
|
||||
//gaussFit->SetParameters(peakY, peakX, 25.0); // Initial guesses for amplitude, mean, sigma
|
||||
gaussFit->SetParameters(initialAmplitude, initialCentroid, initialSigma);
|
||||
// Perform fit
|
||||
int fitStatus = histogram->Fit(gaussFit, "RQ+");
|
||||
if (fitStatus != 0) {
|
||||
std::cerr << "Fit failed for peak " << j + 1 << " in histogram " << i << std::endl;
|
||||
delete gaussFit;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Retrieve fit parameters
|
||||
double amplitude = gaussFit->GetParameter(0);
|
||||
double centroid = gaussFit->GetParameter(1);
|
||||
double sigma = gaussFit->GetParameter(2);
|
||||
double amplitudeError = gaussFit->GetParError(0);
|
||||
double centroidError = gaussFit->GetParError(1);
|
||||
double sigmaError = gaussFit->GetParError(2);
|
||||
|
||||
// Chi-squared value
|
||||
double chi2 = gaussFit->GetChisquare();
|
||||
int ndf = gaussFit->GetNDF();
|
||||
outFile << i << "\t" << j + 1 << "\t" << centroid << std::endl;
|
||||
gaussFit->SetLineColor(kRed);
|
||||
gaussFit->Draw("SAME");
|
||||
TText *text = new TText();
|
||||
text->SetNDC();
|
||||
text->SetTextSize(0.03);
|
||||
text->SetTextColor(kRed);
|
||||
//text->DrawText(0.15, 0.8 - j * 0.05, Form("Peak %d: Amp=%.2f, Mean=%.2f, Sigma=%.2f", j + 1, amplitude, centroid, sigma));
|
||||
text->DrawText(0.15, 0.8 - j * 0.05,
|
||||
Form("Peak %d: Amp=%.2f±%.2f, Mean=%.2f±%.2f, Sigma=%.2f±%.2f, Chi2/NDF=%.2f",
|
||||
j + 1, amplitude, amplitudeError, centroid, centroidError, sigma, sigmaError, chi2 / ndf));
|
||||
// Save results
|
||||
|
||||
|
||||
// Clean up
|
||||
delete gaussFit;
|
||||
}
|
||||
|
||||
// Update canvas for visualization
|
||||
c1->Update();
|
||||
std::cout << "Press Enter to view the next histogram..." << std::endl;
|
||||
c1->WaitPrimitive(); // Wait until Enter is pressed in the ROOT console
|
||||
}
|
||||
|
||||
// Close resources
|
||||
inputFile->Close();
|
||||
outFile.close();
|
||||
delete c1;
|
||||
}
|
||||
|
||||
309
GainMatchQQQ.C
Normal file
309
GainMatchQQQ.C
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
#define GainMatchQQQ_cxx
|
||||
|
||||
#include "GainMatchQQQ.h"
|
||||
#include <TH2.h>
|
||||
#include <TF1.h>
|
||||
#include <TStyle.h>
|
||||
#include <TCanvas.h>
|
||||
#include <TMath.h>
|
||||
#include <TCutG.h>
|
||||
#include <fstream>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <numeric>
|
||||
#include "Armory/HistPlotter.h"
|
||||
#include "TVector3.h"
|
||||
#include "TGraphErrors.h"
|
||||
#include "TF1.h"
|
||||
#include <cmath>
|
||||
|
||||
TH2F *hQQQFVB;
|
||||
HistPlotter *plotter;
|
||||
|
||||
int padID = 0;
|
||||
|
||||
TCutG *cut;
|
||||
std::map<std::tuple<int, int, int>, std::vector<std::pair<double, double>>> dataPoints;
|
||||
|
||||
void GainMatchQQQ::Begin(TTree * /*tree*/)
|
||||
{
|
||||
plotter = new HistPlotter("GainQQQ.root", "TFILE");
|
||||
TString option = GetOption();
|
||||
|
||||
hQQQFVB = new TH2F("hQQQFVB", "QQQ Front vs Back; Front E; Back E", 800, 0, 16000, 800, 0, 16000);
|
||||
|
||||
// Load the TCutG object
|
||||
TFile *cutFile = TFile::Open("qqqcorr.root");
|
||||
if (!cutFile || cutFile->IsZombie())
|
||||
{
|
||||
std::cerr << "Error: Could not open qqqcorr.root" << std::endl;
|
||||
return;
|
||||
}
|
||||
cut = dynamic_cast<TCutG *>(cutFile->Get("qqqcorr"));
|
||||
if (!cut)
|
||||
{
|
||||
std::cerr << "Error: Could not find TCutG named 'qqqcorr' in qqqcorr.root" << std::endl;
|
||||
return;
|
||||
}
|
||||
cut->SetName("qqqcorr"); // Ensure the cut has the correct name
|
||||
}
|
||||
|
||||
Bool_t GainMatchQQQ::Process(Long64_t entry)
|
||||
{
|
||||
|
||||
int ringMults[16] = {0};
|
||||
int wedgeMults[16] = {0};
|
||||
std::vector<std::tuple<int, int, int, double, double>> events;
|
||||
|
||||
b_qqqMulti->GetEntry(entry);
|
||||
b_qqqID->GetEntry(entry);
|
||||
b_qqqCh->GetEntry(entry);
|
||||
b_qqqE->GetEntry(entry);
|
||||
b_qqqT->GetEntry(entry);
|
||||
|
||||
qqq.CalIndex();
|
||||
|
||||
for (int i = 0; i < qqq.multi; i++)
|
||||
{
|
||||
for (int j = i + 1; j < qqq.multi; j++)
|
||||
{
|
||||
if (qqq.id[i] == qqq.id[j])
|
||||
{
|
||||
int chWedge = -1;
|
||||
int chRing = -1;
|
||||
float eWedge = 0.0;
|
||||
float eRing = 0.0;
|
||||
if (qqq.ch[i] < 16 && qqq.ch[j] >= 16)
|
||||
{
|
||||
chWedge = qqq.ch[i];
|
||||
eWedge = qqq.e[i];
|
||||
chRing = qqq.ch[j] - 16;
|
||||
eRing = qqq.e[j];
|
||||
}
|
||||
else if (qqq.ch[j] < 16 && qqq.ch[i] >= 16)
|
||||
{
|
||||
chWedge = qqq.ch[j];
|
||||
eWedge = qqq.e[j];
|
||||
chRing = qqq.ch[i] - 16;
|
||||
eRing = qqq.e[i];
|
||||
}
|
||||
else
|
||||
continue;
|
||||
ringMults[chRing]++;
|
||||
wedgeMults[chWedge]++;
|
||||
hQQQFVB->Fill(eWedge, eRing);
|
||||
events.emplace_back(qqq.id[i], chRing, chWedge, eRing, eWedge);
|
||||
plotter->Fill2D(Form("hRaw_qqq%d_ring%d_wedge%d", qqq.id[i], chRing, chWedge), 800, 0, 3000, 800, 0, 3000, eWedge, eRing, "hRawQQQ");
|
||||
// double ratio = (eWedge > 0.0) ? (eRing / eWedge) : 0.0;
|
||||
// double maxslope = 1.5;
|
||||
|
||||
// bool validPoint = false;
|
||||
// if (ratio < maxslope && ratio > 1. / maxslope)
|
||||
// {
|
||||
// // Accumulate data for gain matching
|
||||
// dataPoints[{qqq.id[i], chRing, chWedge}].emplace_back(eWedge, eRing);
|
||||
// plotter->Fill2D("hAll_in", 4000, 0, 16000, 4000, 0, 16000, eWedge, eRing);
|
||||
// validPoint = true;
|
||||
// }
|
||||
|
||||
// if (!validPoint)
|
||||
// {
|
||||
// plotter->Fill2D("hAll_out", 4000, 0, 16000, 4000, 0, 16000, eWedge, eRing);
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto tuple : events)
|
||||
{
|
||||
auto [id, chr, chw, er, ew] = tuple;
|
||||
if (ringMults[chr] > 1 || wedgeMults[chw] > 1)
|
||||
continue; // ignore multiplicity > 1 events
|
||||
double ratio = (ew > 0.0) ? (er / ew) : 0.0;
|
||||
double maxslope = 1.5;
|
||||
|
||||
bool validPoint = false;
|
||||
if (ratio < maxslope && ratio > 1. / maxslope)
|
||||
{
|
||||
// Accumulate data for gain matching
|
||||
dataPoints[{id, chr, chw}].emplace_back(ew, er);
|
||||
plotter->Fill2D("hAll_in", 4000, 0, 16000, 4000, 0, 16000, ew, er);
|
||||
validPoint = true;
|
||||
}
|
||||
if (!validPoint)
|
||||
{
|
||||
plotter->Fill2D("hAll_out", 4000, 0, 16000, 4000, 0, 16000, ew, er);
|
||||
}
|
||||
}
|
||||
|
||||
return kTRUE;
|
||||
}
|
||||
|
||||
|
||||
void GainMatchQQQ::Terminate()
|
||||
{
|
||||
const int MAX_DET = 4;
|
||||
const int MAX_RING = 16;
|
||||
const int MAX_WEDGE = 16;
|
||||
|
||||
// We store gains locally just for the "corrected" plot,
|
||||
// but the file will output Slopes for the global minimizer.
|
||||
double gainW[MAX_DET][MAX_RING][MAX_WEDGE] = {{{0}}};
|
||||
double gainR[MAX_DET][MAX_RING][MAX_WEDGE] = {{{0}}};
|
||||
bool gainValid[MAX_DET][MAX_RING][MAX_WEDGE] = {{{false}}};
|
||||
|
||||
// Output file for the Minimizer
|
||||
std::ofstream outFile("qqq_GainMatch.dat");
|
||||
|
||||
// Benchmark/Debug file
|
||||
std::ofstream benchFile("benchmark_diff.dat");
|
||||
benchFile << "ID Wedge Ring Chi2NDF Slope SlopeErr" << std::endl;
|
||||
|
||||
if (!outFile.is_open()) { std::cerr << "Error opening output file!" << std::endl; return; }
|
||||
|
||||
const int MIN_POINTS = 50;
|
||||
const int MAX_ITER = 3; // Outlier rejection passes
|
||||
const double CLIP_SIGMA = 2.5; // Sigma threshold for outliers
|
||||
|
||||
for (const auto &kv : dataPoints)
|
||||
{
|
||||
auto key = kv.first;
|
||||
auto [id, ring, wedge] = key;
|
||||
const auto &pts = kv.second;
|
||||
|
||||
if (pts.size() < (size_t)MIN_POINTS) continue;
|
||||
|
||||
std::vector<std::pair<double, double>> current_pts = pts;
|
||||
|
||||
double finalSlope = 0.0;
|
||||
double finalSlopeErr = 0.0;
|
||||
bool converged = false;
|
||||
|
||||
// --- Iterative Fitting ---
|
||||
for (int iter = 0; iter < MAX_ITER; ++iter)
|
||||
{
|
||||
if (current_pts.size() < (size_t)MIN_POINTS) break;
|
||||
|
||||
std::vector<double> x, y, ex, ey;
|
||||
|
||||
for (const auto &p : current_pts)
|
||||
{
|
||||
x.push_back(p.first); // Wedge E
|
||||
y.push_back(p.second); // Ring E
|
||||
ex.push_back(std::sqrt(std::abs(p.first))); // Error in X (Poisson)
|
||||
ey.push_back(std::sqrt(std::abs(p.second))); // Error in Y (Poisson)
|
||||
|
||||
// Sanity check to avoid 0 error
|
||||
if(ex.back() < 1.0) ex.back() = 1.0;
|
||||
if(ey.back() < 1.0) ey.back() = 1.0;
|
||||
}
|
||||
|
||||
// 2. Create Graph
|
||||
TGraphErrors *gr = new TGraphErrors(current_pts.size(), x.data(), y.data(), ex.data(), ey.data());
|
||||
|
||||
// 3. Fit Linear Function through Origin
|
||||
TF1 *f1= new TF1("calibFit", "[0]*x", 0, 16000);
|
||||
f1->SetParameter(0, 1.0);
|
||||
|
||||
// "Q"=Quiet, "N"=NoDraw, "S"=ResultPtr
|
||||
// We do NOT use "W" (Ignore weights), we want to use the errors we set.
|
||||
int fitStatus = gr->Fit(f1, "QNS");
|
||||
|
||||
if (fitStatus != 0) {
|
||||
delete gr; delete f1;
|
||||
break;
|
||||
}
|
||||
|
||||
finalSlope = f1->GetParameter(0);
|
||||
double chi2 = f1->GetChisquare();
|
||||
double ndf = f1->GetNDF();
|
||||
|
||||
// Get the statistical error on the slope
|
||||
double rawErr = f1->GetParError(0);
|
||||
|
||||
// SCALING ERROR:
|
||||
// If Chi2/NDF > 1, the data scatters more than Poisson stats predict.
|
||||
// // We inflate the error by sqrt(Chi2/NDF) to be conservative for the Minimizer.
|
||||
// double redChi2 = (ndf > 0) ? (chi2 / ndf) : 1.0;
|
||||
// double inflation = (redChi2 > 1.0) ? std::sqrt(redChi2) : 1.0;
|
||||
|
||||
// finalSlopeErr = rawErr * inflation;
|
||||
|
||||
// 4. Outlier Rejection
|
||||
if (iter == MAX_ITER - 1) {
|
||||
converged = true;
|
||||
delete gr; delete f1;
|
||||
break;
|
||||
}
|
||||
|
||||
// Calculate Residuals
|
||||
std::vector<double> residuals;
|
||||
double sumSqResid = 0.0;
|
||||
for(size_t k=0; k<current_pts.size(); ++k) {
|
||||
double val = f1->Eval(current_pts[k].first);
|
||||
double res = current_pts[k].second - val;
|
||||
residuals.push_back(res);
|
||||
sumSqResid += res*res;
|
||||
}
|
||||
// double sigma = std::sqrt(sumSqResid / current_pts.size());
|
||||
|
||||
// // Filter
|
||||
// std::vector<std::pair<double, double>> next_pts;
|
||||
// for(size_t k=0; k<current_pts.size(); ++k) {
|
||||
// if(std::abs(residuals[k]) < CLIP_SIGMA * sigma) {
|
||||
// next_pts.push_back(current_pts[k]);
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (next_pts.size() == current_pts.size()) {
|
||||
// converged = true;
|
||||
// delete gr; delete f1;
|
||||
// break;
|
||||
// }
|
||||
// current_pts = next_pts;
|
||||
// delete gr; delete f1;
|
||||
}
|
||||
|
||||
if (!converged || finalSlope <= 0) continue;
|
||||
|
||||
// --- Store/Output ---
|
||||
|
||||
// 1. Save locally for the verification plot (hAll)
|
||||
// Approximate local gain for plotting purposes only
|
||||
double gW_local = std::sqrt(finalSlope);
|
||||
double gR_local = 1.0 / gW_local;
|
||||
gainW[id][ring][wedge] = gW_local;
|
||||
gainR[id][ring][wedge] = gR_local;
|
||||
gainValid[id][ring][wedge] = true;
|
||||
|
||||
// 2. Write to File for Minimizer
|
||||
// Format: ID Wedge Ring Slope Error
|
||||
outFile << id << " " << wedge << " " << ring << " " << finalSlope << " " << finalSlopeErr << std::endl;
|
||||
|
||||
// 3. Benchmark Info
|
||||
benchFile << id << " " << wedge << " " << ring << " "
|
||||
<< finalSlope << " " << finalSlopeErr << std::endl;
|
||||
}
|
||||
|
||||
outFile.close();
|
||||
benchFile.close();
|
||||
std::cout << "Gain matching with Errors complete." << std::endl;
|
||||
|
||||
// Plotting the corrected data (Visual check using local approx gains)
|
||||
for (auto &kv : dataPoints)
|
||||
{
|
||||
int id, ring, wedge;
|
||||
std::tie(id, ring, wedge) = kv.first;
|
||||
if (!gainValid[id][ring][wedge]) continue;
|
||||
auto &pts = kv.second;
|
||||
for (auto &pr : pts)
|
||||
{
|
||||
double corrWedge = pr.first * gainW[id][ring][wedge];
|
||||
double corrRing = pr.second * gainR[id][ring][wedge];
|
||||
plotter->Fill2D("hAll", 4000, 0, 16000, 4000, 0, 16000, corrWedge, corrRing);
|
||||
}
|
||||
}
|
||||
|
||||
plotter->FlushToDisk();
|
||||
}
|
||||
114
GainMatchQQQ.h
Normal file
114
GainMatchQQQ.h
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
#ifndef GainMatchQQQ_h
|
||||
#define GainMatchQQQ_h
|
||||
|
||||
#include <TROOT.h>
|
||||
#include <TChain.h>
|
||||
#include <TFile.h>
|
||||
#include <TSelector.h>
|
||||
|
||||
#include "Armory/ClassDet.h"
|
||||
|
||||
class GainMatchQQQ : public TSelector {
|
||||
public :
|
||||
TTree *fChain; //!pointer to the analyzed TTree or TChain
|
||||
|
||||
// Fixed size dimensions of array or collections stored in the TTree if any.
|
||||
|
||||
// Declaration of leaf types
|
||||
Det sx3;
|
||||
Det qqq;
|
||||
Det pc ;
|
||||
|
||||
ULong64_t evID;
|
||||
UInt_t run;
|
||||
|
||||
// List of branches
|
||||
TBranch *b_eventID; //!
|
||||
TBranch *b_run; //!
|
||||
TBranch *b_sx3Multi; //!
|
||||
TBranch *b_sx3ID; //!
|
||||
TBranch *b_sx3Ch; //!
|
||||
TBranch *b_sx3E; //!
|
||||
TBranch *b_sx3T; //!
|
||||
TBranch *b_qqqMulti; //!
|
||||
TBranch *b_qqqID; //!
|
||||
TBranch *b_qqqCh; //!
|
||||
TBranch *b_qqqE; //!
|
||||
TBranch *b_qqqT; //!
|
||||
TBranch *b_pcMulti; //!
|
||||
TBranch *b_pcID; //!
|
||||
TBranch *b_pcCh; //!
|
||||
TBranch *b_pcE; //!
|
||||
TBranch *b_pcT; //!
|
||||
|
||||
GainMatchQQQ(TTree * /*tree*/ =0) : fChain(0) { }
|
||||
virtual ~GainMatchQQQ() { }
|
||||
virtual Int_t Version() const { return 2; }
|
||||
virtual void Begin(TTree *tree);
|
||||
virtual void SlaveBegin(TTree *tree);
|
||||
virtual void Init(TTree *tree);
|
||||
virtual Bool_t Notify();
|
||||
virtual Bool_t Process(Long64_t entry);
|
||||
virtual Int_t GetEntry(Long64_t entry, Int_t getall = 0) { return fChain ? fChain->GetTree()->GetEntry(entry, getall) : 0; }
|
||||
virtual void SetOption(const char *option) { fOption = option; }
|
||||
virtual void SetObject(TObject *obj) { fObject = obj; }
|
||||
virtual void SetInputList(TList *input) { fInput = input; }
|
||||
virtual TList *GetOutputList() const { return fOutput; }
|
||||
virtual void SlaveTerminate();
|
||||
virtual void Terminate();
|
||||
|
||||
ClassDef(GainMatchQQQ,0);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef GainMatchQQQ_cxx
|
||||
void GainMatchQQQ::Init(TTree *tree){
|
||||
|
||||
// Set branch addresses and branch pointers
|
||||
if (!tree) return;
|
||||
fChain = tree;
|
||||
fChain->SetMakeClass(1);
|
||||
|
||||
fChain->SetBranchAddress("evID", &evID, &b_eventID);
|
||||
fChain->SetBranchAddress("run", &run, &b_run);
|
||||
|
||||
sx3.SetDetDimension(24,12);
|
||||
qqq.SetDetDimension(4,32);
|
||||
pc.SetDetDimension(2,24);
|
||||
|
||||
fChain->SetBranchAddress("sx3Multi", &sx3.multi, &b_sx3Multi);
|
||||
fChain->SetBranchAddress("sx3ID", &sx3.id, &b_sx3ID);
|
||||
fChain->SetBranchAddress("sx3Ch", &sx3.ch, &b_sx3Ch);
|
||||
fChain->SetBranchAddress("sx3E", &sx3.e, &b_sx3E);
|
||||
fChain->SetBranchAddress("sx3T", &sx3.t, &b_sx3T);
|
||||
fChain->SetBranchAddress("qqqMulti", &qqq.multi, &b_qqqMulti);
|
||||
fChain->SetBranchAddress("qqqID", &qqq.id, &b_qqqID);
|
||||
fChain->SetBranchAddress("qqqCh", &qqq.ch, &b_qqqCh);
|
||||
fChain->SetBranchAddress("qqqE", &qqq.e, &b_qqqE);
|
||||
fChain->SetBranchAddress("qqqT", &qqq.t, &b_qqqT);
|
||||
fChain->SetBranchAddress("pcMulti", &pc.multi, &b_pcMulti);
|
||||
fChain->SetBranchAddress("pcID", &pc.id, &b_pcID);
|
||||
fChain->SetBranchAddress("pcCh", &pc.ch, &b_pcCh);
|
||||
fChain->SetBranchAddress("pcE", &pc.e, &b_pcE);
|
||||
fChain->SetBranchAddress("pcT", &pc.t, &b_pcT);
|
||||
|
||||
}
|
||||
|
||||
Bool_t GainMatchQQQ::Notify(){
|
||||
|
||||
return kTRUE;
|
||||
}
|
||||
|
||||
void GainMatchQQQ::SlaveBegin(TTree * /*tree*/){
|
||||
|
||||
TString option = GetOption();
|
||||
|
||||
}
|
||||
|
||||
void GainMatchQQQ::SlaveTerminate(){
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // #ifdef GainMatchQQQ_cxx
|
||||
4003
MakeVertex.C
Executable file
4003
MakeVertex.C
Executable file
File diff suppressed because it is too large
Load Diff
133
MakeVertex.h
Normal file
133
MakeVertex.h
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
#ifndef MakeVertex_h
|
||||
#define MakeVertex_h
|
||||
|
||||
#include <TROOT.h>
|
||||
#include <TChain.h>
|
||||
#include <TApplication.h>
|
||||
#include <TFile.h>
|
||||
#include <TSelector.h>
|
||||
#include <iomanip>
|
||||
#include <vector> // Required for vectors
|
||||
#include <utility> // Required for std::pair
|
||||
|
||||
#include "Armory/ClassDet.h"
|
||||
#include "Armory/ClassPW.h" // YOU ADDED THIS (Correct! Defines Coord)
|
||||
|
||||
class MakeVertex : public TSelector {
|
||||
public :
|
||||
TTree *fChain; //!pointer to the analyzed TTree or TChain
|
||||
|
||||
// Declaration of leaf types
|
||||
Det sx3;
|
||||
Det qqq;
|
||||
Det pc ;
|
||||
Det misc;
|
||||
|
||||
ULong64_t evID;
|
||||
UInt_t run;
|
||||
|
||||
// List of branches
|
||||
TBranch *b_eventID; //!
|
||||
TBranch *b_run; //!
|
||||
TBranch *b_sx3Multi; //!
|
||||
TBranch *b_sx3ID; //!
|
||||
TBranch *b_sx3Ch; //!
|
||||
TBranch *b_sx3E; //!
|
||||
TBranch *b_sx3T; //!
|
||||
TBranch *b_qqqMulti; //!
|
||||
TBranch *b_qqqID; //!
|
||||
TBranch *b_qqqCh; //!
|
||||
TBranch *b_qqqE; //!
|
||||
TBranch *b_qqqT; //!
|
||||
TBranch *b_pcMulti; //!
|
||||
TBranch *b_pcID; //!
|
||||
TBranch *b_pcCh; //!
|
||||
TBranch *b_pcE; //!
|
||||
TBranch *b_pcT; //!
|
||||
TBranch *b_miscMulti; //!
|
||||
TBranch *b_miscID; //!
|
||||
TBranch *b_miscCh; //!
|
||||
TBranch *b_miscE; //!
|
||||
TBranch *b_miscT; //!
|
||||
TBranch *b_miscTf; //!
|
||||
|
||||
// 1. Geometry Cache
|
||||
Coord Crossover[24][24][2];
|
||||
|
||||
// 2. Persistent Vectors (REQUIRED for the optimized .cxx to work)
|
||||
std::vector<std::pair<int, double>> anodeHits;
|
||||
std::vector<std::pair<int, double>> cathodeHits;
|
||||
std::vector<std::pair<int, double>> corrcatMax;
|
||||
std::vector<std::pair<int, double>> corranoMax;
|
||||
std::vector<double> cathodeTimes;
|
||||
std::vector<double> anodeTimes;
|
||||
|
||||
MakeVertex(TTree * /*tree*/ =0) : fChain(0) { }
|
||||
virtual ~MakeVertex() { }
|
||||
virtual Int_t Version() const { return 2; }
|
||||
virtual void Begin(TTree *tree);
|
||||
virtual void SlaveBegin(TTree *tree);
|
||||
virtual void Init(TTree *tree);
|
||||
virtual Bool_t Notify();
|
||||
virtual Bool_t Process(Long64_t entry);
|
||||
virtual Int_t GetEntry(Long64_t entry, Int_t getall = 0) { return fChain ? fChain->GetTree()->GetEntry(entry, getall) : 0; }
|
||||
virtual void SetOption(const char *option) { fOption = option; }
|
||||
virtual void SetObject(TObject *obj) { fObject = obj; }
|
||||
virtual void SetInputList(TList *input) { fInput = input; }
|
||||
virtual TList *GetOutputList() const { return fOutput; }
|
||||
virtual void SlaveTerminate();
|
||||
virtual void Terminate();
|
||||
|
||||
ClassDef(MakeVertex,0);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MakeVertex_cxx
|
||||
void MakeVertex::Init(TTree *tree){
|
||||
|
||||
if (!tree) return;
|
||||
fChain = tree;
|
||||
fChain->SetMakeClass(1);
|
||||
|
||||
fChain->SetBranchAddress("evID", &evID, &b_eventID);
|
||||
fChain->SetBranchAddress("run", &run, &b_run);
|
||||
|
||||
sx3.SetDetDimension(24,12);
|
||||
qqq.SetDetDimension(4,32);
|
||||
pc.SetDetDimension(2,24);
|
||||
|
||||
fChain->SetBranchAddress("sx3Multi", &sx3.multi, &b_sx3Multi);
|
||||
fChain->SetBranchAddress("sx3ID", &sx3.id, &b_sx3ID);
|
||||
fChain->SetBranchAddress("sx3Ch", &sx3.ch, &b_sx3Ch);
|
||||
fChain->SetBranchAddress("sx3E", &sx3.e, &b_sx3E);
|
||||
fChain->SetBranchAddress("sx3T", &sx3.t, &b_sx3T);
|
||||
fChain->SetBranchAddress("qqqMulti", &qqq.multi, &b_qqqMulti);
|
||||
fChain->SetBranchAddress("qqqID", &qqq.id, &b_qqqID);
|
||||
fChain->SetBranchAddress("qqqCh", &qqq.ch, &b_qqqCh);
|
||||
fChain->SetBranchAddress("qqqE", &qqq.e, &b_qqqE);
|
||||
fChain->SetBranchAddress("qqqT", &qqq.t, &b_qqqT);
|
||||
fChain->SetBranchAddress("pcMulti", &pc.multi, &b_pcMulti);
|
||||
fChain->SetBranchAddress("pcID", &pc.id, &b_pcID);
|
||||
fChain->SetBranchAddress("pcCh", &pc.ch, &b_pcCh);
|
||||
fChain->SetBranchAddress("pcE", &pc.e, &b_pcE);
|
||||
fChain->SetBranchAddress("pcT", &pc.t, &b_pcT);
|
||||
fChain->SetBranchAddress("miscMulti", &misc.multi, &b_miscMulti);
|
||||
fChain->SetBranchAddress("miscID", &misc.id, &b_miscID);
|
||||
fChain->SetBranchAddress("miscCh", &misc.ch, &b_miscCh);
|
||||
fChain->SetBranchAddress("miscE", &misc.e, &b_miscE);
|
||||
fChain->SetBranchAddress("miscT", &misc.t, &b_miscT);
|
||||
fChain->SetBranchAddress("miscf", &misc.tf, &b_miscTf);
|
||||
}
|
||||
|
||||
Bool_t MakeVertex::Notify(){
|
||||
return kTRUE;
|
||||
}
|
||||
|
||||
void MakeVertex::SlaveBegin(TTree * /*tree*/){
|
||||
// TString option = GetOption();
|
||||
}
|
||||
|
||||
void MakeVertex::SlaveTerminate(){
|
||||
}
|
||||
#endif // #ifdef MakeVertex_cxx
|
||||
139
MatchAndPlotCentroids.C
Normal file
139
MatchAndPlotCentroids.C
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include <TGraph.h>
|
||||
#include <TF1.h>
|
||||
#include <TCanvas.h>
|
||||
#include <TH1.h>
|
||||
|
||||
|
||||
|
||||
void MatchAndPlotCentroids() {
|
||||
// Open the centroid data file
|
||||
std::ifstream inputFile("centroids_27Al.txt");
|
||||
// std::ifstream inputFile("centroids_17F.txt");
|
||||
if (!inputFile.is_open()) {
|
||||
std::cerr << "Error: Could not open Centroids.txt" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Data structure to store centroids by histogram and peak number
|
||||
std::map<int, std::map<int, double>> centroidData;
|
||||
|
||||
// Read data from the file
|
||||
std::string line;
|
||||
while (std::getline(inputFile, line)) {
|
||||
std::istringstream iss(line);
|
||||
int histogramIndex, peakNumber;
|
||||
double centroid;
|
||||
if (iss >> histogramIndex >> peakNumber >> centroid) {
|
||||
centroidData[histogramIndex][peakNumber] = centroid;
|
||||
}
|
||||
}
|
||||
|
||||
inputFile.close();
|
||||
|
||||
// Ensure histogram 24 exists and has data
|
||||
if (centroidData.find(1) == centroidData.end()) {
|
||||
std::cerr << "Error: Histogram 0 not found in the data!" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Reference centroids from histogram 24
|
||||
const auto& referenceCentroids = centroidData[1];
|
||||
std::ofstream outputFile("slope_intercept_results.dat");
|
||||
if (!outputFile.is_open()) {
|
||||
std::cerr << "Error: Could not open the output file for writing!" << std::endl;
|
||||
return;
|
||||
}
|
||||
outputFile << "Histogram Number\tSlope\tIntercept\n";
|
||||
// Loop through histograms 25 to 47
|
||||
|
||||
// 1. Create a SINGLE canvas BEFORE the loop
|
||||
TCanvas *c1 = new TCanvas("c1", "Centroid Fit Viewer", 800, 600);
|
||||
|
||||
for (int targetHist = 0; targetHist <= 23; targetHist++) {
|
||||
// Ensure the target histogram exists and matches in peak numbers
|
||||
if (centroidData.find(targetHist) == centroidData.end() || centroidData[targetHist].size() != referenceCentroids.size()) {
|
||||
//4th cnetroid data point for 19 was generated using the 3 datqa points for the slope of wires 0 and 19
|
||||
std::cout << "Skipping Histogram " << targetHist << " due to mismatched or missing data." << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Prepare x and y values for TGraph
|
||||
std::vector<double> xValues, yValues;
|
||||
for (const auto& [peakNumber, refCentroid] : referenceCentroids) {
|
||||
if (centroidData[targetHist].find(peakNumber) != centroidData[targetHist].end()) {
|
||||
yValues.push_back(refCentroid);
|
||||
xValues.push_back(centroidData[targetHist][peakNumber]);
|
||||
} else {
|
||||
std::cerr << "Warning: Peak " << peakNumber << " missing in histogram " << targetHist << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (xValues.size() < 3) {
|
||||
std::cout << "Skipping Histogram " << targetHist << " as it has less than 3 matching centroids." << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create a TGraph
|
||||
TGraph *graph = new TGraph(xValues.size(), &xValues[0], &yValues[0]);
|
||||
graph->SetTitle(Form("Centroid of Histogram %d vs 1", targetHist));
|
||||
graph->GetYaxis()->SetTitle("Centroid of Histogram 1");
|
||||
graph->GetXaxis()->SetTitle(Form("Centroid of Histogram %d", targetHist));
|
||||
graph->SetMarkerStyle(20);
|
||||
graph->SetMarkerSize(1.0);
|
||||
graph->SetMarkerColor(kBlue);
|
||||
|
||||
graph->Draw("AP");
|
||||
// Draw the graph
|
||||
graph->Draw("AP");
|
||||
double minX = *std::min_element(xValues.begin(), xValues.end());
|
||||
double maxX = *std::max_element(xValues.begin(), xValues.end());
|
||||
// Fit the data with a linear function
|
||||
TF1 *fitLine = new TF1("fitLine", "pol1", minX, maxX); // Adjust range as needed
|
||||
fitLine->SetLineColor(kRed); // Set the line color to distinguish it
|
||||
fitLine->SetLineWidth(2); // Thicker line for visibility
|
||||
graph->Fit(fitLine, "M");
|
||||
fitLine->Draw("same");
|
||||
fitLine->SetParLimits(0, -10, 10); // Limit intercept between -10 and 10
|
||||
fitLine->SetParLimits(1, 0, 2);
|
||||
// Extract slope and intercept
|
||||
double slope = fitLine->GetParameter(1);
|
||||
double intercept = fitLine->GetParameter(0);
|
||||
outputFile << targetHist << "\t" << slope << "\t" << intercept << "\n";
|
||||
std::cout << "Histogram 24 vs " << targetHist << ": Slope = " << slope << ", Intercept = " << intercept << std::endl;
|
||||
std::vector<double> residuals;
|
||||
for (size_t i = 0; i < xValues.size(); ++i) {
|
||||
double fittedY = fitLine->Eval(xValues[i]); // Evaluate fitted function at x
|
||||
double residual = yValues[i] - fittedY; // Residual = observed - fitted
|
||||
residuals.push_back(residual);
|
||||
}
|
||||
|
||||
// Create a graph for the residuals
|
||||
/*TGraph *residualGraph = new TGraph(residuals.size(), &xValues[0], &residuals[0]);
|
||||
residualGraph->SetTitle(Form("Residuals for Histogram 24 vs %d", targetHist));
|
||||
residualGraph->GetYaxis()->SetTitle("Residuals");
|
||||
residualGraph->GetXaxis()->SetTitle(Form("Centroid of Histogram %d", targetHist));
|
||||
residualGraph->SetMarkerStyle(20);
|
||||
residualGraph->SetMarkerSize(1.0);
|
||||
residualGraph->SetMarkerColor(kGreen);
|
||||
|
||||
// Draw the residuals plot below the original plot (can be on a new canvas if preferred)
|
||||
TCanvas *c2 = new TCanvas(Form("c_residuals_24_vs_%d", targetHist), Form("Residuals for Centroid 24 vs %d", targetHist), 800, 400);
|
||||
residualGraph->Draw("AP");*/
|
||||
c1->Update();
|
||||
//c2->Update();
|
||||
std::cout << "Press Enter to continue..." << std::endl;
|
||||
|
||||
//std::cin.get();
|
||||
c1->WaitPrimitive();
|
||||
//c2->WaitPrimitive();
|
||||
//std::cin.get();
|
||||
//std::cin.get();
|
||||
}
|
||||
outputFile.close();
|
||||
std::cout << "Results written to slope_intercept_results.txt" << std::endl;
|
||||
}
|
||||
359
MiscEdE.C
Normal file
359
MiscEdE.C
Normal file
|
|
@ -0,0 +1,359 @@
|
|||
#include "TFile.h"
|
||||
#include "TTree.h"
|
||||
#include "TChain.h"
|
||||
#include "TH1D.h"
|
||||
#include "TH2D.h"
|
||||
#include "TCutG.h"
|
||||
#include "TKey.h"
|
||||
#include "TString.h"
|
||||
#include "TSystem.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr double kAdcMax = 16384; // 14-bit-ish ADC range for axes
|
||||
|
||||
struct HitData
|
||||
{
|
||||
double e;
|
||||
unsigned long long t;
|
||||
};
|
||||
}
|
||||
|
||||
void MiscEdE(const char *f0,
|
||||
const char *f1 = "", const char *f2 = "", const char *f3 = "",
|
||||
const char *f4 = "", const char *f5 = "", const char *f6 = "",
|
||||
const char *f7 = "", const char *f8 = "", const char *f9 = "")
|
||||
{
|
||||
std::vector<TString> inputs;
|
||||
for (const char *f : {f0, f1, f2, f3, f4, f5, f6, f7, f8, f9})
|
||||
if (f && f[0])
|
||||
inputs.emplace_back(f);
|
||||
if (inputs.empty())
|
||||
{
|
||||
printf("MiscEdE: no input files given.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
TChain *tree = new TChain("tree");
|
||||
for (auto &in : inputs)
|
||||
{
|
||||
if (gSystem->AccessPathName(in))
|
||||
{
|
||||
printf("MiscEdE: WARNING input not found, skipping: %s\n", in.Data());
|
||||
continue;
|
||||
}
|
||||
tree->Add(in);
|
||||
}
|
||||
if (tree->GetNtrees() == 0)
|
||||
{
|
||||
printf("MiscEdE: no readable inputs.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Bash Environment Variable Extraction
|
||||
// ---------------------------------------------------------
|
||||
int runNumber = -1;
|
||||
if (gSystem->Getenv("CURRENT_RUN")) {
|
||||
runNumber = std::atoi(gSystem->Getenv("CURRENT_RUN"));
|
||||
}
|
||||
printf("MiscEdE: Run Number loaded from Bash: %d\n", runNumber);
|
||||
|
||||
TString cutDir = "~/ANASEN_analysis"; // Fallback if not run via bash
|
||||
if (gSystem->Getenv("CUT_DIR")) {
|
||||
cutDir = gSystem->Getenv("CUT_DIR");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Run-Dependent Channel Routing
|
||||
// ---------------------------------------------------------
|
||||
// Defaults (Run <= 180)
|
||||
int snLolli = 405, chLolli = 9;
|
||||
int snSi = 405, chSi = 11;
|
||||
int snRF = 405, chRF = 15;
|
||||
int snMCP = 405, chMCP = 14;
|
||||
|
||||
if (runNumber > 180 && runNumber <= 282) {
|
||||
chRF = 10;
|
||||
}
|
||||
else if (runNumber > 282 && runNumber <= 322) {
|
||||
snRF = 89; chRF = 0;
|
||||
snMCP = 89; chMCP = 1;
|
||||
}
|
||||
else if (runNumber > 322) {
|
||||
snRF = 89; chRF = 0;
|
||||
snMCP = 89; chMCP = 2;
|
||||
}
|
||||
|
||||
// Composite Unique IDs (UID = Board * 100 + Channel) to prevent collisions
|
||||
int uidLollipop = snLolli * 100 + chLolli;
|
||||
int uidSiMon = snSi * 100 + chSi;
|
||||
int uidRF = snRF * 100 + chRF;
|
||||
int uidMCP = snMCP * 100 + chMCP;
|
||||
|
||||
printf(" -> Routed Lollipop : Board %d, Ch %d (UID: %d)\n", snLolli, chLolli, uidLollipop);
|
||||
printf(" -> Routed Si Monitor: Board %d, Ch %d (UID: %d)\n", snSi, chSi, uidSiMon);
|
||||
printf(" -> Routed RF : Board %d, Ch %d (UID: %d)\n", snRF, chRF, uidRF);
|
||||
printf(" -> Routed MCP : Board %d, Ch %d (UID: %d)\n", snMCP, chMCP, uidMCP);
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Exact schema matching EventBuilder.cpp
|
||||
// ---------------------------------------------------------
|
||||
const int MAX_MULTI = 2000;
|
||||
unsigned int multi = 0;
|
||||
unsigned short sn[MAX_MULTI];
|
||||
unsigned short ch[MAX_MULTI];
|
||||
unsigned short e[MAX_MULTI];
|
||||
unsigned long long e_t[MAX_MULTI]; // Timestamp branch is "e_t"
|
||||
|
||||
tree->SetBranchAddress("multi", &multi);
|
||||
tree->SetBranchAddress("sn", sn);
|
||||
tree->SetBranchAddress("ch", ch);
|
||||
tree->SetBranchAddress("e", e);
|
||||
tree->SetBranchAddress("e_t", e_t);
|
||||
|
||||
TString stem = gSystem->BaseName(inputs[0].Data());
|
||||
if (stem.EndsWith(".root"))
|
||||
stem.Remove(stem.Length() - 5);
|
||||
TString outName = "MiscEdE_" + stem + ".root";
|
||||
TFile *out = new TFile(outName, "recreate");
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Load the 17F and 16O TCutGs from file
|
||||
// ---------------------------------------------------------
|
||||
TCutG *cut17F = nullptr;
|
||||
TString path17F = gSystem->ExpandPathName(Form("%s/17FCut.root", cutDir.Data()));
|
||||
if (!gSystem->AccessPathName(path17F))
|
||||
{
|
||||
TFile *fCut = TFile::Open(path17F);
|
||||
if (fCut && !fCut->IsZombie())
|
||||
{
|
||||
for (auto keyObj : *fCut->GetListOfKeys())
|
||||
{
|
||||
TKey *key = (TKey *)keyObj;
|
||||
if (TString(key->GetClassName()) == "TCutG")
|
||||
{
|
||||
cut17F = (TCutG *)key->ReadObj()->Clone("cut17F");
|
||||
break;
|
||||
}
|
||||
}
|
||||
fCut->Close();
|
||||
}
|
||||
}
|
||||
|
||||
TCutG *cut16O = nullptr;
|
||||
TString path16O = gSystem->ExpandPathName(Form("%s/16OCut.root", cutDir.Data()));
|
||||
if (!gSystem->AccessPathName(path16O))
|
||||
{
|
||||
TFile *fCut = TFile::Open(path16O);
|
||||
if (fCut && !fCut->IsZombie())
|
||||
{
|
||||
for (auto keyObj : *fCut->GetListOfKeys())
|
||||
{
|
||||
TKey *key = (TKey *)keyObj;
|
||||
if (TString(key->GetClassName()) == "TCutG")
|
||||
{
|
||||
cut16O = (TCutG *)key->ReadObj()->Clone("cut16O");
|
||||
break;
|
||||
}
|
||||
}
|
||||
fCut->Close();
|
||||
}
|
||||
}
|
||||
|
||||
out->cd();
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Prepare Histograms
|
||||
// ---------------------------------------------------------
|
||||
std::map<int, TH2D*> hEvsChMap;
|
||||
auto getEvsCh = [&](int boardSN) -> TH2D* {
|
||||
if (hEvsChMap.count(boardSN)) return hEvsChMap[boardSN];
|
||||
TH2D *h = new TH2D(Form("h2_E_vs_ch_bd%d", boardSN), Form("Board %d: E vs channel;board channel;E [ADC]", boardSN), 16, -0.5, 15.5, 800, 0, kAdcMax);
|
||||
hEvsChMap[boardSN] = h;
|
||||
return h;
|
||||
};
|
||||
|
||||
TH2D *h2_EvsdT_LolliSi = new TH2D("EvsdT_Lollipop_Si", "Energy Si vs dT (Lollipop - Si);dT [ticks];Si E [ADC]", 500, 0, 2000, 400, 0, kAdcMax);
|
||||
TH2D *h2_TOF_SiRF = new TH2D("TOF_Si_RF", "Time of Flight: Energy Si vs (T_Si - T_RF);T_Si - T_RF [ticks];Si E [ADC]", 500, 0, 2000, 400, 0, kAdcMax);
|
||||
|
||||
// Dynamic Pairwise E-dE mapped by UID
|
||||
std::map<std::pair<int, int>, TH2D *> hEdE;
|
||||
auto getEdE = [&](int uidA, int uidB) -> TH2D * {
|
||||
if (uidA > uidB) std::swap(uidA, uidB);
|
||||
auto key = std::make_pair(uidA, uidB);
|
||||
auto it = hEdE.find(key);
|
||||
if (it != hEdE.end()) return it->second;
|
||||
|
||||
TString name = Form("h2_ede_bd%d_ch%d_vs_bd%d_ch%d", uidA/100, uidA%100, uidB/100, uidB%100);
|
||||
TString title = Form("E-dE: Bd %d Ch %d (x) vs Bd %d Ch %d (y);X E [ADC];Y E [ADC]", uidA/100, uidA%100, uidB/100, uidB%100);
|
||||
|
||||
TH2D *h = new TH2D(name, title, 400, 0, kAdcMax, 400, 0, kAdcMax);
|
||||
hEdE[key] = h;
|
||||
return h;
|
||||
};
|
||||
|
||||
// 17F Gated Plots
|
||||
TH2D *hEdE_17F = nullptr;
|
||||
TH2D *hEdE_inv = nullptr;
|
||||
TH2D *h2_EvsdT_LolliSi_17F = nullptr;
|
||||
TH2D *h2_TOF_SiRF_17F = nullptr;
|
||||
TH1D *h1_dT_17F = nullptr;
|
||||
|
||||
if (cut17F)
|
||||
{
|
||||
hEdE_17F = new TH2D("EdE_lollipopIC_vs_SiMonitor_17FCut", "lollipop IC vs Si monitor (17F Gated);Si monitor E [ADC];lollipop IC E [ADC]", 400, 0, kAdcMax, 400, 0, kAdcMax);
|
||||
hEdE_inv = new TH2D("EdE_lollipopIC_vs_SiMonitor_17FCut_Inverse", "lollipop IC vs Si monitor (Inverse 17F Gated);Si monitor E [ADC];lollipop IC E [ADC]", 400, 0, kAdcMax, 400, 0, kAdcMax);
|
||||
h2_EvsdT_LolliSi_17F = new TH2D("EvsdT_Lollipop_Si_17FCut", "Energy Si vs dT (17F Gated);dT (Lollipop - Si) [ticks];Si E [ADC]", 500, 0, 2000, 400, 0, kAdcMax);
|
||||
h2_TOF_SiRF_17F = new TH2D("TOF_Si_RF_17FCut", "Si TOF vs Energy (17F Gated);T_Si - T_RF [ticks];Si E [ADC]", 500, 0, 2000, 400, 0, kAdcMax);
|
||||
h1_dT_17F = new TH1D("dT_Lollipop_Si_17FCut", "dT Lollipop-Si (17F Gated);T_Lollipop - T_Si [ticks]", 500, 0, 2000);
|
||||
}
|
||||
|
||||
// 16O Gated Plots
|
||||
TH2D *hEdE_16O = nullptr;
|
||||
TH2D *h2_EvsdT_LolliSi_16O = nullptr;
|
||||
TH2D *h2_TOF_SiRF_16O = nullptr;
|
||||
TH1D *h1_dT_16O = nullptr;
|
||||
|
||||
if (cut16O)
|
||||
{
|
||||
hEdE_16O = new TH2D("EdE_lollipopIC_vs_SiMonitor_16OCut", "lollipop IC vs Si monitor (16O Gated);Si monitor E [ADC];lollipop IC E [ADC]", 400, 0, kAdcMax, 400, 0, kAdcMax);
|
||||
h2_EvsdT_LolliSi_16O = new TH2D("EvsdT_Lollipop_Si_16OCut", "Energy Si vs dT (16O Gated);dT (Lollipop - Si) [ticks];Si E [ADC]", 500, 0, 2000, 400, 0, kAdcMax);
|
||||
h2_TOF_SiRF_16O = new TH2D("TOF_Si_RF_16OCut", "Si TOF vs Energy (16O Gated);T_Si - T_RF [ticks];Si E [ADC]", 500, 0, 2000, 800, 0, kAdcMax);
|
||||
h1_dT_16O = new TH1D("dT_Lollipop_Si_16OCut", "dT Lollipop-Si (16O Gated);T_Lollipop - T_Si [ticks]", 500, 0, 2000);
|
||||
}
|
||||
|
||||
Long64_t nEnt = tree->GetEntries();
|
||||
printf("MiscEdE: %lld entries across %d file(s) -> %s\n", nEnt, tree->GetNtrees(), outName.Data());
|
||||
|
||||
Long64_t used = 0;
|
||||
for (Long64_t i = 0; i < nEnt; ++i)
|
||||
{
|
||||
tree->GetEntry(i);
|
||||
|
||||
// We map hits by UID instead of standard channel to prevent overlaps between Bd405 and Bd89
|
||||
std::map<int, HitData> hit;
|
||||
for (unsigned int j = 0; j < multi && j < (unsigned)MAX_MULTI; ++j)
|
||||
{
|
||||
// Only care about MISC boards 405 and 89
|
||||
if (sn[j] != 405 && sn[j] != 89)
|
||||
continue;
|
||||
|
||||
int c = ch[j];
|
||||
int uid = (sn[j] * 100) + ch[j];
|
||||
double en = e[j];
|
||||
unsigned long long ts = e_t[j];
|
||||
|
||||
auto it = hit.find(uid);
|
||||
if (it == hit.end() || en > it->second.e)
|
||||
hit[uid] = {en, ts};
|
||||
|
||||
getEvsCh(sn[j])->Fill(c, en);
|
||||
}
|
||||
|
||||
if (hit.empty())
|
||||
continue;
|
||||
++used;
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Generic Dynamic Pairwise E-dE Logic across all UIDs
|
||||
// ---------------------------------------------------------
|
||||
for (auto a = hit.begin(); a != hit.end(); ++a)
|
||||
{
|
||||
for (auto b = std::next(a); b != hit.end(); ++b)
|
||||
{
|
||||
if (a->second.e > 500 && b->second.e > 500)
|
||||
{
|
||||
getEdE(a->first, b->first)->Fill(a->second.e, b->second.e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Core Timing and PID Gating Logic (Lollipop-Si specific)
|
||||
// ---------------------------------------------------------
|
||||
if (hit.count(uidLollipop) && hit.count(uidSiMon))
|
||||
{
|
||||
double e_lol = hit[uidLollipop].e;
|
||||
double e_si = hit[uidSiMon].e;
|
||||
|
||||
// Calculate delta T safely across potential CAEN rollovers
|
||||
double dt_lol_si = static_cast<double>(hit[uidLollipop].t) - static_cast<double>(hit[uidSiMon].t);
|
||||
|
||||
if (e_lol > 500 && e_si > 500)
|
||||
{
|
||||
h2_EvsdT_LolliSi->Fill(dt_lol_si, e_si);
|
||||
|
||||
bool is17F = cut17F && cut17F->IsInside(e_si, e_lol);
|
||||
bool is16O = cut16O && cut16O->IsInside(e_si, e_lol);
|
||||
|
||||
if (is17F)
|
||||
{
|
||||
hEdE_17F->Fill(e_si, e_lol);
|
||||
h2_EvsdT_LolliSi_17F->Fill(dt_lol_si, e_si);
|
||||
h1_dT_17F->Fill(dt_lol_si);
|
||||
}
|
||||
else if (cut17F)
|
||||
{
|
||||
hEdE_inv->Fill(e_si, e_lol);
|
||||
}
|
||||
|
||||
if (is16O)
|
||||
{
|
||||
hEdE_16O->Fill(e_si, e_lol);
|
||||
h2_EvsdT_LolliSi_16O->Fill(dt_lol_si, e_si);
|
||||
h1_dT_16O->Fill(dt_lol_si);
|
||||
}
|
||||
|
||||
// Generate absolute TOF if the RF channel fired
|
||||
if (hit.count(uidRF))
|
||||
{
|
||||
double tof = static_cast<double>(hit[uidSiMon].t) - static_cast<double>(hit[uidRF].t);
|
||||
h2_TOF_SiRF->Fill(tof, e_si);
|
||||
|
||||
if (is17F)
|
||||
h2_TOF_SiRF_17F->Fill(tof, e_si);
|
||||
if (is16O)
|
||||
h2_TOF_SiRF_16O->Fill(tof, e_si);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Purge Sparse E-dE Histograms (< 500 counts)
|
||||
// ---------------------------------------------------------
|
||||
for (auto it = hEdE.begin(); it != hEdE.end();)
|
||||
{
|
||||
if (it->second->GetEntries() < 500)
|
||||
{
|
||||
delete it->second; // Free memory and remove from ROOT file
|
||||
it = hEdE.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience alias: Transpose generic plot to correctly place stopping E on X
|
||||
if (hEdE.count({uidLollipop, uidSiMon}))
|
||||
{
|
||||
TH2D *src = hEdE[{uidLollipop, uidSiMon}];
|
||||
TH2D *alias = new TH2D("EdE_lollipopIC_vs_SiMonitor_Ungated",
|
||||
Form("lollipop IC vs Si monitor (Ungated);Bd %d Ch %d (Si) E [ADC];Bd %d Ch %d (Lolli) E [ADC]", snSi, chSi, snLolli, chLolli),
|
||||
src->GetNbinsY(), src->GetYaxis()->GetXmin(), src->GetYaxis()->GetXmax(),
|
||||
src->GetNbinsX(), src->GetXaxis()->GetXmin(), src->GetXaxis()->GetXmax());
|
||||
for (int ix = 1; ix <= src->GetNbinsX(); ++ix)
|
||||
for (int iy = 1; iy <= src->GetNbinsY(); ++iy)
|
||||
alias->SetBinContent(iy, ix, src->GetBinContent(ix, iy)); // (x,y) -> (y,x)
|
||||
}
|
||||
|
||||
out->Write();
|
||||
out->Close();
|
||||
printf("MiscEdE: Processed %lld valid MISC events. Wrote %s\n", used, outName.Data());
|
||||
}
|
||||
454
PCGainMatch.C
Normal file
454
PCGainMatch.C
Normal file
|
|
@ -0,0 +1,454 @@
|
|||
#define PCGainMatch_cxx
|
||||
|
||||
#include "PCGainMatch.h"
|
||||
#include <TH2.h>
|
||||
#include <TStyle.h>
|
||||
#include <TCanvas.h>
|
||||
#include <TMath.h>
|
||||
#include <TCutG.h>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include "Armory/ClassSX3.h"
|
||||
#include "Armory/ClassPW.h"
|
||||
|
||||
#include "TVector3.h"
|
||||
|
||||
TH2F * hsx3IndexVE;
|
||||
TH2F * hqqqIndexVE;
|
||||
TH2F * hpcIndexVE;
|
||||
|
||||
TH2F * hsx3Coin;
|
||||
TH2F * hqqqCoin;
|
||||
TH2F * hpcCoin;
|
||||
|
||||
TH2F * hqqqPolar;
|
||||
TH2F * hsx3VpcIndex;
|
||||
TH2F * hqqqVpcIndex;
|
||||
TH2F * hqqqVpcE;
|
||||
TH2F * hsx3VpcE;
|
||||
TH2F * hanVScatsum;
|
||||
TH2F * hanVScatsum_a[24];
|
||||
TH2F * hanVScatsum_hcut;
|
||||
TH2F * hanVScatsum_lcut;
|
||||
TH2F * hAnodeHits;
|
||||
TH1F * hAnodeMultiplicity;
|
||||
|
||||
|
||||
int padID = 0;
|
||||
|
||||
SX3 sx3_contr;
|
||||
PW pw_contr;
|
||||
TVector3 hitPos;
|
||||
bool HitNonZero;
|
||||
|
||||
TH1F * hZProj;
|
||||
TCutG *AnCatSum_high;
|
||||
TCutG *AnCatSum_low;
|
||||
TCutG *PCCoinc_cut1;
|
||||
TCutG *PCCoinc_cut2;
|
||||
bool inCuth;
|
||||
bool inCutl;
|
||||
bool inPCCut;
|
||||
|
||||
void PCGainMatch::Begin(TTree * /*tree*/){
|
||||
TString option = GetOption();
|
||||
|
||||
hsx3IndexVE = new TH2F("hsx3IndexVE", "SX3 index vs Energy; sx3 index ; Energy", 24*12, 0, 24*12, 400, 0, 5000); hsx3IndexVE->SetNdivisions( -612, "x");
|
||||
hqqqIndexVE = new TH2F("hqqqIndexVE", "QQQ index vs Energy; QQQ index ; Energy", 4*2*16, 0, 4*2*16, 400, 0, 5000); hqqqIndexVE->SetNdivisions( -1204, "x");
|
||||
hpcIndexVE = new TH2F("hpcIndexVE", "PC index vs Energy; PC index ; Energy", 2*24, 0, 2*24, 800, 0, 16000); hpcIndexVE->SetNdivisions( -1204, "x");
|
||||
|
||||
|
||||
hsx3Coin = new TH2F("hsx3Coin", "SX3 Coincident", 24*12, 0, 24*12, 24*12, 0, 24*12);
|
||||
hqqqCoin = new TH2F("hqqqCoin", "QQQ Coincident", 4*2*16, 0, 4*2*16, 4*2*16, 0, 4*2*16);
|
||||
hpcCoin = new TH2F("hpcCoin", "PC Coincident", 2*24, 0, 2*24, 2*24, 0, 2*24);
|
||||
|
||||
hqqqPolar = new TH2F("hqqqPolar", "QQQ Polar ID", 16*4, -TMath::Pi(), TMath::Pi(),16, 10, 50);
|
||||
|
||||
hsx3VpcIndex = new TH2F("hsx3Vpcindex", "sx3 vs pc; sx3 index; pc index", 24*12, 0, 24*12, 48, 0, 48);
|
||||
hsx3VpcIndex->SetNdivisions( -612, "x");
|
||||
hsx3VpcIndex->SetNdivisions( -12, "y");
|
||||
hqqqVpcIndex = new TH2F("hqqqVpcindex", "qqq vs pc; qqq index; pc index", 4*2*16, 0, 4*2*16, 48, 0, 48);
|
||||
hqqqVpcIndex->SetNdivisions( -612, "x");
|
||||
hqqqVpcIndex->SetNdivisions( -12, "y");
|
||||
|
||||
hqqqVpcE = new TH2F("hqqqVpcEnergy", "qqq vs pc; qqq energy; pc energy", 400, 0, 5000, 400, 0, 5000);
|
||||
hqqqVpcE->SetNdivisions( -612, "x");
|
||||
hqqqVpcE->SetNdivisions( -12, "y");
|
||||
|
||||
hsx3VpcE = new TH2F("hsx3VpcEnergy", "sx3 vs pc; sx3 energy; pc energy", 400, 0, 5000, 400, 0, 5000);
|
||||
hsx3VpcE->SetNdivisions( -612, "x");
|
||||
hsx3VpcE->SetNdivisions( -12, "y");
|
||||
|
||||
hZProj = new TH1F("hZProj", "Nos of anodes", 20, 0, 19);
|
||||
hAnodeHits = new TH2F("hAnodeHits", "Anode vs Anode Energy, Anode ID; Anode E", 24,0 , 23, 400, 0 , 20000);
|
||||
hAnodeMultiplicity = new TH1F("hAnodeMultiplicity", "Number of Anodes/Event", 40, 0, 40);
|
||||
hanVScatsum = new TH2F("hanVScatsum", "Anode vs Cathode Sum; Anode E; Cathode E", 400,0 , 10000, 800, 0 , 16000);
|
||||
for (int i = 0; i < 24; i++) {
|
||||
TString histName = Form("hAnodeVsCathode_%d", i);
|
||||
TString histTitle = Form("Anode %d vs Cathode Sum; Anode E; Cathode Sum E", i);
|
||||
hanVScatsum_a[i] = new TH2F(histName, histTitle, 400, 0, 10000, 400, 0, 16000);
|
||||
}
|
||||
hanVScatsum_lcut = new TH2F("hanVScatsum_LCUT", "Anode vs Cathode Sum; Anode E; Cathode E", 400,0 , 16000, 400, 0 , 16000);
|
||||
hanVScatsum_hcut = new TH2F("hanVScatsum_HCUT", "Anode vs Cathode Sum; Anode E; Cathode E", 400,0 , 16000, 400, 0 , 16000);
|
||||
|
||||
sx3_contr.ConstructGeo();
|
||||
pw_contr.ConstructGeo();
|
||||
// TFile *f1 = new TFile("AnCatSum_high.root");
|
||||
// TFile *f2 = new TFile("AnCatSum_low.root");
|
||||
// TFile *f3 = new TFile("PCCoinc_cut1.root");
|
||||
// TFile *f4 = new TFile("PCCoinc_cut2.root");
|
||||
// AnCatSum_high= (TCutG*)f1->Get("AnCatSum_high");
|
||||
// AnCatSum_low= (TCutG*)f2->Get("AnCatSum_low");
|
||||
// PCCoinc_cut1= (TCutG*)f3->Get("PCCoinc_cut1");
|
||||
// PCCoinc_cut2= (TCutG*)f4->Get("PCCoinc_cut2");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Bool_t PCGainMatch::Process(Long64_t entry){
|
||||
// if (entry % 1000000 == 0) {
|
||||
// std::cout << "Processing entry: " << entry << std::endl;
|
||||
// }
|
||||
// if ( entry > 100 ) return kTRUE;
|
||||
|
||||
hitPos.Clear();
|
||||
HitNonZero = false;
|
||||
|
||||
// if( entry > 1) return kTRUE;
|
||||
// printf("################### ev : %llu \n", entry);
|
||||
|
||||
b_sx3Multi->GetEntry(entry);
|
||||
b_sx3ID->GetEntry(entry);
|
||||
b_sx3Ch->GetEntry(entry);
|
||||
b_sx3E->GetEntry(entry);
|
||||
b_sx3T->GetEntry(entry);
|
||||
b_qqqMulti->GetEntry(entry);
|
||||
b_qqqID->GetEntry(entry);
|
||||
b_qqqCh->GetEntry(entry);
|
||||
b_qqqE->GetEntry(entry);
|
||||
b_qqqT->GetEntry(entry);
|
||||
b_pcMulti->GetEntry(entry);
|
||||
b_pcID->GetEntry(entry);
|
||||
b_pcCh->GetEntry(entry);
|
||||
b_pcE->GetEntry(entry);
|
||||
b_pcT->GetEntry(entry);
|
||||
|
||||
sx3.CalIndex();
|
||||
qqq.CalIndex();
|
||||
pc.CalIndex();
|
||||
|
||||
// sx3.Print();
|
||||
|
||||
//########################################################### Raw data
|
||||
|
||||
// //======================= PC
|
||||
|
||||
std::vector<std::pair<int, double>> anodeHits={};
|
||||
std::vector<std::pair<int, double>> cathodeHits={};
|
||||
int aID = 0;
|
||||
int cID = 0;
|
||||
float aE = 0;
|
||||
float cE = 0;
|
||||
|
||||
// Define the excluded SX3 and QQQ channels
|
||||
// std::unordered_set<int> excludeSX3 = {34, 35, 36, 37, 61, 62, 67, 73, 74, 75, 76, 77, 78, 79, 80, 93, 97, 100, 103, 108, 109, 110, 111, 112};
|
||||
// std::unordered_set<int> excludeQQQ = {0, 17, 109, 110, 111, 112, 113, 119, 127, 128};
|
||||
// inCuth=false;
|
||||
// inCutl=false;
|
||||
// inPCCut=false;
|
||||
for( int i = 0; i < pc.multi; i ++){
|
||||
|
||||
if(pc.e[i]>50 && pc.multi<7){
|
||||
|
||||
float aESum = 0;
|
||||
float cESum = 0;
|
||||
if (pc.index[i] < 24 ) {
|
||||
anodeHits.push_back(std::pair<int, double>(pc.index[i], pc.e[i]));
|
||||
} else if (pc.index[i] >= 24) {
|
||||
cathodeHits.push_back(std::pair<int, double>(pc.index[i], pc.e[i]));
|
||||
}
|
||||
|
||||
for(int j=i+1;j<pc.multi;j++){
|
||||
// if(PCCoinc_cut1->IsInside(pc.index[i], pc.index[j]) || PCCoinc_cut2->IsInside(pc.index[i], pc.index[j])){
|
||||
// // hpcCoin->Fill(pc.index[i], pc.index[j]);
|
||||
// inPCCut = true;
|
||||
// }
|
||||
hpcCoin->Fill(pc.index[i], pc.index[j]);
|
||||
}
|
||||
if (anodeHits.size()==1 && cathodeHits.size() >= 1) {
|
||||
|
||||
for (const auto& anode : anodeHits) {
|
||||
// for(int l=0; l<sx3.multi; l++){
|
||||
// if (sx3.index[l]==80){
|
||||
|
||||
aID = anode.first;
|
||||
aE = anode.second;
|
||||
aESum += aE;
|
||||
// printf("aID : %d, aE : %f\n", aID, aE);
|
||||
}
|
||||
|
||||
// printf("aID : %d, aE : %f, cE : %f\n", aID, aE, cE);
|
||||
for (const auto& cathode : cathodeHits) {
|
||||
cID = cathode.first;
|
||||
cE = cathode.second;
|
||||
// if(cE>cEMax){
|
||||
// cEMax = cE;
|
||||
// cIDMax = cID;
|
||||
// }
|
||||
// if(cE>cEnextMax && cE<cEMax){
|
||||
// cEnextMax = cE;
|
||||
// cIDnextMax = cID;
|
||||
// }
|
||||
|
||||
cESum += cE;
|
||||
}
|
||||
// }
|
||||
|
||||
// inCuth = false;
|
||||
// inCutl = false;
|
||||
// inPCCut = false;
|
||||
// for(int j=i+1;j<pc.multi;j++){
|
||||
// if(PCCoinc_cut1->IsInside(pc.index[i], pc.index[j]) || PCCoinc_cut2->IsInside(pc.index[i], pc.index[j])){
|
||||
// // hpcCoin->Fill(pc.index[i], pc.index[j]);
|
||||
// inPCCut = true;
|
||||
// }
|
||||
// hpcCoin->Fill(pc.index[i], pc.index[j]);
|
||||
// }
|
||||
|
||||
// Check if the accumulated energies are within the defined ranges
|
||||
// if (AnCatSum_high && AnCatSum_high->IsInside(aESum, cESum)) {
|
||||
// inCuth = true;
|
||||
// }
|
||||
// if (AnCatSum_low && AnCatSum_low->IsInside(aESum, cESum)) {
|
||||
// inCutl = true;
|
||||
// }
|
||||
|
||||
// Fill histograms based on the cut conditions
|
||||
// if (inCuth && inPCCut) {
|
||||
// hanVScatsum_hcut->Fill(aESum, cESum);
|
||||
// }
|
||||
// if (inCutl && inPCCut) {
|
||||
// hanVScatsum_lcut->Fill(aESum, cESum);
|
||||
// }
|
||||
// for(auto anode : anodeHits){
|
||||
|
||||
// float aE = anode.second;
|
||||
// aESum += aE;
|
||||
// if(inPCCut){
|
||||
hanVScatsum->Fill(aESum, cESum);
|
||||
// }
|
||||
if (aID < 24 && aE > 50) {
|
||||
hanVScatsum_a[aID]->Fill(aE, cESum);
|
||||
}
|
||||
|
||||
|
||||
// }
|
||||
// Fill histograms for the `pc` data
|
||||
hpcIndexVE->Fill(pc.index[i], pc.e[i]);
|
||||
// if(inPCCut){
|
||||
hAnodeMultiplicity->Fill(anodeHits.size());
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// //======================= SX3
|
||||
|
||||
std::vector<std::pair<int, int>> ID; // first = id, 2nd = index
|
||||
for( int i = 0; i < sx3.multi; i ++){
|
||||
if(sx3.e[i]>50){
|
||||
ID.push_back(std::pair<int, int>(sx3.id[i], i));
|
||||
hsx3IndexVE->Fill( sx3.index[i], sx3.e[i] );
|
||||
|
||||
for( int j = i+1; j < sx3.multi; j++){
|
||||
hsx3Coin->Fill( sx3.index[i], sx3.index[j]);
|
||||
}
|
||||
|
||||
for( int j = 0; j < pc.multi; j++){
|
||||
hsx3VpcIndex->Fill( sx3.index[i], pc.index[j] );
|
||||
// if( sx3.ch[index] > 8 ){
|
||||
// hsx3VpcE->Fill( sx3.e[i], pc.e[j] );
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if( ID.size() > 0 ){
|
||||
std::sort(ID.begin(), ID.end(), [](const std::pair<int, int> & a, const std::pair<int, int> & b) {
|
||||
return a.first < b.first;
|
||||
} );
|
||||
// printf("##############################\n");
|
||||
// for( size_t i = 0; i < ID.size(); i++) printf("%zu | %d %d \n", i, ID[i].first, ID[i].second );
|
||||
|
||||
std::vector<std::pair<int, int>> sx3ID;
|
||||
sx3ID.push_back(ID[0]);
|
||||
bool found = false;
|
||||
for( size_t i = 1; i < ID.size(); i++){
|
||||
if( ID[i].first == sx3ID.back().first) {
|
||||
sx3ID.push_back(ID[i]);
|
||||
if( sx3ID.size() >= 3) {
|
||||
found = true;
|
||||
}
|
||||
}else{
|
||||
if( !found ){
|
||||
sx3ID.clear();
|
||||
sx3ID.push_back(ID[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// printf("---------- sx3ID Multi : %zu \n", sx3ID.size());
|
||||
|
||||
if( found ){
|
||||
int sx3ChUp, sx3ChDn, sx3ChBk;
|
||||
float sx3EUp, sx3EDn;
|
||||
// printf("------ sx3 ID : %d, multi: %zu\n", sx3ID[0].first, sx3ID.size());
|
||||
for( size_t i = 0; i < sx3ID.size(); i++ ){
|
||||
int index = sx3ID[i].second;
|
||||
// printf(" %zu | index %d | ch : %d, energy : %d \n", i, index, sx3.ch[index], sx3.e[index]);
|
||||
|
||||
|
||||
if( sx3.ch[index] < 8 ){
|
||||
if( sx3.ch[index] % 2 == 0) {
|
||||
sx3ChDn = sx3.ch[index];
|
||||
sx3EDn = sx3.e[index];
|
||||
}else{
|
||||
sx3ChUp = sx3.ch[index];
|
||||
sx3EUp = sx3.e[index];
|
||||
}
|
||||
}else{
|
||||
sx3ChBk = sx3.ch[index];
|
||||
}
|
||||
for( int j = 0; j < pc.multi; j++){
|
||||
|
||||
// hsx3VpcIndex->Fill( sx3.index[i], pc.index[j] );
|
||||
if( sx3.ch[index] > 8 && pc.index[j]<24 && pc.e[j]>50 ){
|
||||
hsx3VpcE->Fill( sx3.e[i], pc.e[j] );
|
||||
// hpcIndexVE->Fill( pc.index[i], pc.e[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sx3_contr.CalSX3Pos(sx3ID[0].first, sx3ChUp, sx3ChDn, sx3ChBk, sx3EUp, sx3EDn);
|
||||
hitPos = sx3_contr.GetHitPos();
|
||||
HitNonZero = true;
|
||||
// hitPos.Print();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// //======================= QQQ
|
||||
for( int i = 0; i < qqq.multi; i ++){
|
||||
|
||||
// for( int j = 0; j < pc.multi; j++){
|
||||
if(qqq.e[i]>50 ){
|
||||
hqqqIndexVE->Fill( qqq.index[i], qqq.e[i] );
|
||||
for( int j = 0; j < qqq.multi; j++){
|
||||
if ( j == i ) continue;
|
||||
hqqqCoin->Fill( qqq.index[i], qqq.index[j]);
|
||||
}
|
||||
|
||||
|
||||
for( int j = i + 1; j < qqq.multi; j++){
|
||||
for( int k = 0; k < pc.multi; k++){
|
||||
// if(qqq.e[i>50]){
|
||||
hqqqVpcE->Fill( qqq.e[i], pc.e[k] );
|
||||
hqqqVpcIndex->Fill( qqq.index[i], pc.index[j] );
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
// hanVScatsum->Fill(aE,cE);
|
||||
|
||||
|
||||
if( HitNonZero){
|
||||
pw_contr.CalTrack( hitPos, aID, cID);
|
||||
hZProj->Fill(pw_contr.GetZ0());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//########################################################### Track constrcution
|
||||
|
||||
|
||||
//############################## DO THE KINEMATICS
|
||||
|
||||
|
||||
return kTRUE;
|
||||
}
|
||||
|
||||
void PCGainMatch::Terminate(){
|
||||
|
||||
gStyle->SetOptStat("neiou");
|
||||
TCanvas * canvas = new TCanvas("cANASEN", "ANASEN", 2000, 2000);
|
||||
canvas->Divide(3,3);
|
||||
|
||||
//hsx3VpcIndex->Draw("colz");
|
||||
|
||||
//=============================================== pad-1
|
||||
padID ++; canvas->cd(padID); canvas->cd(padID)->SetGrid(1);
|
||||
|
||||
hsx3IndexVE->Draw("colz");
|
||||
|
||||
//=============================================== pad-2
|
||||
padID ++; canvas->cd(padID); canvas->cd(padID)->SetGrid(1);
|
||||
|
||||
hqqqIndexVE->Draw("colz");
|
||||
|
||||
//=============================================== pad-3
|
||||
padID ++; canvas->cd(padID); canvas->cd(padID)->SetGrid(1);
|
||||
|
||||
hpcIndexVE->Draw("colz");
|
||||
|
||||
//=============================================== pad-4
|
||||
padID ++; canvas->cd(padID); canvas->cd(padID)->SetGrid(1);
|
||||
|
||||
hsx3Coin->Draw("colz");
|
||||
|
||||
//=============================================== pad-5
|
||||
padID ++; canvas->cd(padID); canvas->cd(padID)->SetGrid(1);
|
||||
|
||||
canvas->cd(padID)->SetLogz(true);
|
||||
|
||||
hqqqCoin->Draw("colz");
|
||||
|
||||
//=============================================== pad-6
|
||||
padID ++; canvas->cd(padID); canvas->cd(padID)->SetGrid(1);
|
||||
|
||||
hpcCoin->Draw("colz");
|
||||
|
||||
//=============================================== pad-7
|
||||
padID ++; canvas->cd(padID); canvas->cd(padID)->SetGrid(1);
|
||||
|
||||
// hsx3VpcIndex ->Draw("colz");
|
||||
hsx3VpcE->Draw("colz") ;
|
||||
|
||||
//=============================================== pad-8
|
||||
padID ++; canvas->cd(padID); canvas->cd(padID)->SetGrid(1);
|
||||
|
||||
// hqqqVpcIndex ->Draw("colz");
|
||||
|
||||
hqqqVpcE ->Draw("colz");
|
||||
//=============================================== pad-9
|
||||
padID ++;
|
||||
|
||||
// canvas->cd(padID)->DrawFrame(-50, -50, 50, 50);
|
||||
// hqqqPolar->Draw("same colz pol");
|
||||
|
||||
canvas->cd(padID); canvas->cd(padID)->SetGrid(1);
|
||||
hanVScatsum->Draw("colz");
|
||||
// hAnodeHits->Draw("colz");
|
||||
// hAnodeMultiplicity->Draw();
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef Analyzer_h
|
||||
#define Analyzer_h
|
||||
#ifndef PCGainMatch_h
|
||||
#define PCGainMatch_h
|
||||
|
||||
#include <TROOT.h>
|
||||
#include <TChain.h>
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#include "Armory/ClassDet.h"
|
||||
|
||||
class Analyzer : public TSelector {
|
||||
class PCGainMatch : public TSelector {
|
||||
public :
|
||||
TTree *fChain; //!pointer to the analyzed TTree or TChain
|
||||
|
||||
|
|
@ -41,8 +41,8 @@ public :
|
|||
TBranch *b_pcE; //!
|
||||
TBranch *b_pcT; //!
|
||||
|
||||
Analyzer(TTree * /*tree*/ =0) : fChain(0) { }
|
||||
virtual ~Analyzer() { }
|
||||
PCGainMatch(TTree * /*tree*/ =0) : fChain(0) { }
|
||||
virtual ~PCGainMatch() { }
|
||||
virtual Int_t Version() const { return 2; }
|
||||
virtual void Begin(TTree *tree);
|
||||
virtual void SlaveBegin(TTree *tree);
|
||||
|
|
@ -57,13 +57,13 @@ public :
|
|||
virtual void SlaveTerminate();
|
||||
virtual void Terminate();
|
||||
|
||||
ClassDef(Analyzer,0);
|
||||
ClassDef(PCGainMatch,0);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef Analyzer_cxx
|
||||
void Analyzer::Init(TTree *tree){
|
||||
#ifdef PCGainMatch_cxx
|
||||
void PCGainMatch::Init(TTree *tree){
|
||||
|
||||
// Set branch addresses and branch pointers
|
||||
if (!tree) return;
|
||||
|
|
@ -95,18 +95,18 @@ void Analyzer::Init(TTree *tree){
|
|||
|
||||
}
|
||||
|
||||
Bool_t Analyzer::Notify(){
|
||||
Bool_t PCGainMatch::Notify(){
|
||||
|
||||
return kTRUE;
|
||||
}
|
||||
|
||||
void Analyzer::SlaveBegin(TTree * /*tree*/){
|
||||
void PCGainMatch::SlaveBegin(TTree * /*tree*/){
|
||||
|
||||
TString option = GetOption();
|
||||
|
||||
}
|
||||
|
||||
void Analyzer::SlaveTerminate(){
|
||||
void PCGainMatch::SlaveTerminate(){
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -1,29 +1,38 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ "$#" -ne 2 ]; then
|
||||
echo "Usage: $0 runID timeWindow_ns"
|
||||
if [ "$#" -ne 3 ]; then
|
||||
echo "Usage: $0 runID timeWindow_ns option"
|
||||
echo "option: 0 - process raw data, 1 - process mapped data"
|
||||
echo "Exiting..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
runID=$1
|
||||
runID=$(printf "%03d" $1)
|
||||
timeWindow=$2
|
||||
|
||||
rawFolder=/home/tandem/Desktop/analysis/data
|
||||
rootFolder=../root_data
|
||||
option=$3
|
||||
|
||||
# rawFolder=/home/tandem/data1/2024_09_17Fap/data
|
||||
# rawFolder=/mnt/d/17F
|
||||
# rootFolder=/mnt/d/Remapped_files/17F_data/root_data
|
||||
# rawFolder=/mnt/data1/vs19g/2024_06_27Al
|
||||
# rootFolder=/home/vs19g/Desktop/27Al_Data/root_data
|
||||
rawFolder=/mnt/data1/vs19g/2024_09_17Fap/data
|
||||
rootFolder=/home/vs19g/Desktop/17F_Data/root_data
|
||||
|
||||
if [ $option -eq 0 ]; then
|
||||
|
||||
rsync -auh --info=progress2 splitpole@128.186.111.223:/media/nvmeData/2024_09_17Fap/*.fsu /home/tandem/data1/2024_09_17Fap/data
|
||||
# rsync -auh --info=progress2 splitpole@128.186.111.223:/media/nvmeData/2024_09_17Fap/*.fsu /home/tandem/data1/2024_09_17Fap/data
|
||||
|
||||
fileList=`\ls -1 ${rawFolder}/*Run_${runID}_*.fsu`
|
||||
fileList=`\ls -1 ${rawFolder}/*Run*_${runID}_*.fsu`
|
||||
|
||||
./EventBuilder ${timeWindow} 0 0 100000000 ${fileList}
|
||||
time ./EventBuilder ${timeWindow} 0 0 100000000 ${fileList}
|
||||
|
||||
outFile=${rawFolder}/*${runID}*${timeWindow}.root
|
||||
|
||||
mv -vf ${outFile} ${rootFolder}/.
|
||||
|
||||
./Mapper ${rootFolder}/*${runID}*${timeWindow}.root
|
||||
time ./Mapper ${rootFolder}/*${runID}*${timeWindow}.root
|
||||
fi
|
||||
root "processRun.C(\"${rootFolder}/Run_${runID}_mapped.root\")"
|
||||
|
||||
# root "processRun.C(\"${rootFolder}/Run_${runID}_mapped.root\")"
|
||||
|
|
|
|||
167
QQQ_Calcheck.C
Normal file
167
QQQ_Calcheck.C
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
|
||||
#define QQQ_Calcheck_cxx
|
||||
|
||||
#include <TH2.h>
|
||||
#include <TF1.h>
|
||||
#include <TStyle.h>
|
||||
#include <TCanvas.h>
|
||||
#include <TMath.h>
|
||||
#include <TCutG.h>
|
||||
#include <fstream>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include "Armory/HistPlotter.h"
|
||||
#include "TVector3.h"
|
||||
#include "QQQ_Calcheck.h"
|
||||
|
||||
TH2F *hQQQFVB;
|
||||
HistPlotter *plotter;
|
||||
int padID = 0;
|
||||
|
||||
TCutG *cut;
|
||||
std::map<std::tuple<int, int, int>, std::vector<std::pair<double, double>>> dataPoints;
|
||||
|
||||
bool qqqEcut = false;
|
||||
|
||||
// Gain Arrays
|
||||
const int MAX_QQQ = 4;
|
||||
const int MAX_RING = 16;
|
||||
const int MAX_WEDGE = 16;
|
||||
double qqqwGain[MAX_QQQ][MAX_RING][MAX_WEDGE] = {{{0}}};
|
||||
double qqqrGain[MAX_QQQ][MAX_RING][MAX_WEDGE] = {{{0}}};
|
||||
bool qqqwGainValid[MAX_QQQ][MAX_RING][MAX_WEDGE] = {{{false}}};
|
||||
bool qqqrGainValid[MAX_QQQ][MAX_RING][MAX_WEDGE] = {{{false}}};
|
||||
double qqqCalib[MAX_QQQ][MAX_RING][MAX_WEDGE] = {{{0}}};
|
||||
bool qqqCalibValid[MAX_QQQ][MAX_RING][MAX_WEDGE] = {{{false}}};
|
||||
|
||||
void QQQ_Calcheck::Begin(TTree * /*tree*/)
|
||||
{
|
||||
plotter = new HistPlotter("Cal_checkQQQ.root", "TFILE");
|
||||
// ----------------------- Load QQQ Gains
|
||||
{
|
||||
std::string filename = "qqq_GainMatch.dat";
|
||||
std::ifstream infile(filename);
|
||||
if (!infile.is_open())
|
||||
{
|
||||
std::cerr << "Error opening " << filename << "!" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
int det, ring, wedge;
|
||||
double gainw,gainr;
|
||||
while (infile >> det >> ring >> wedge >> gainw>> gainr)
|
||||
{
|
||||
qqqwGain[det][ring][wedge] = gainw;
|
||||
// qqqrGain[det][ring][wedge] = gainr;
|
||||
qqqwGainValid[det][ring][wedge] = (gainw > 0);
|
||||
// qqqrGainValid[det][ring][wedge] = (gainr > 0);
|
||||
}
|
||||
infile.close();
|
||||
std::cout << "Loaded QQQ gains from " << filename << std::endl;
|
||||
}
|
||||
}
|
||||
// ----------------------- Load QQQ Calibrations
|
||||
{
|
||||
std::string filename = "qqq_Calib.dat";
|
||||
std::ifstream infile(filename);
|
||||
if (!infile.is_open())
|
||||
{
|
||||
std::cerr << "Error opening " << filename << "!" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
int det, ring, wedge;
|
||||
double slope;
|
||||
while (infile >> det >> ring >> wedge >> slope)
|
||||
{
|
||||
qqqCalib[det][ring][wedge] = slope;
|
||||
qqqCalibValid[det][ring][wedge] = (slope > 0);
|
||||
}
|
||||
infile.close();
|
||||
std::cout << "Loaded QQQ calibrations from " << filename << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bool_t QQQ_Calcheck::Process(Long64_t entry)
|
||||
{
|
||||
b_qqqMulti->GetEntry(entry);
|
||||
b_qqqID->GetEntry(entry);
|
||||
b_qqqCh->GetEntry(entry);
|
||||
b_qqqE->GetEntry(entry);
|
||||
b_qqqT->GetEntry(entry);
|
||||
|
||||
qqq.CalIndex();
|
||||
|
||||
for (int i = 0; i < qqq.multi; i++)
|
||||
{
|
||||
for (int j = i + 1; j < qqq.multi; j++)
|
||||
{
|
||||
if (qqq.e[i] > 100)
|
||||
qqqEcut = true;
|
||||
if (qqq.id[i] == qqq.id[j])
|
||||
{
|
||||
int chWedge = -1;
|
||||
int chRing = -1;
|
||||
float eWedgeRaw = 0.0;
|
||||
float eWedge = 0.0;
|
||||
float eWedgeMeV = 0.0;
|
||||
float eRingRaw = 0.0;
|
||||
float eRing = 0.0;
|
||||
float eRingMeV = 0.0;
|
||||
// plug in gains
|
||||
if (qqq.ch[i] < 16 && qqq.ch[j] >= 16 && /*qqqrGainValid[qqq.id[i]][qqq.ch[i]][qqq.ch[j] - 16] &&*/ qqqwGainValid[qqq.id[i]][qqq.ch[i]][qqq.ch[j] - 16])
|
||||
{
|
||||
chWedge = qqq.ch[i];
|
||||
eWedgeRaw = qqq.e[i];
|
||||
eWedge = qqq.e[i] * qqqwGain[qqq.id[i]][qqq.ch[i]][qqq.ch[j] - 16];
|
||||
// printf("Wedge E: %.2f Gain: %.4f \n", eWedge, qqqGain[qqq.id[i]][qqq.ch[i]][qqq.ch[j] - 16]);
|
||||
chRing = qqq.ch[j] - 16;
|
||||
eRingRaw = qqq.e[j];
|
||||
eRing = qqq.e[j];//* qqqrGain[qqq.id[j]][qqq.ch[j]][qqq.ch[i]-16];
|
||||
}
|
||||
else if (qqq.ch[j] < 16 && qqq.ch[i] >= 16/* && qqqrGainValid[qqq.id[j]][qqq.ch[j]][qqq.ch[i] - 16] */&& qqqwGainValid[qqq.id[j]][qqq.ch[j]][qqq.ch[i] - 16])
|
||||
{
|
||||
chWedge = qqq.ch[j];
|
||||
eWedge = qqq.e[j] * qqqwGain[qqq.id[j]][qqq.ch[j]][qqq.ch[i] - 16];
|
||||
eWedgeRaw = qqq.e[j];
|
||||
|
||||
chRing = qqq.ch[i] - 16;
|
||||
eRing = qqq.e[i];// * qqqrGain[qqq.id[i]][qqq.ch[i]][qqq.ch[j] - 16];
|
||||
eRingRaw = qqq.e[i];
|
||||
}
|
||||
else
|
||||
continue;
|
||||
// plug in calibrations
|
||||
if (qqqCalibValid[qqq.id[i]][chWedge][chRing])
|
||||
{
|
||||
eWedgeMeV = eWedge * qqqCalib[qqq.id[i]][chWedge][chRing] / 1000;
|
||||
eRingMeV = eRing * qqqCalib[qqq.id[i]][chWedge][chRing] / 1000;
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
// hQQQFVB->Fill(eWedge, eRing);
|
||||
plotter->Fill2D(Form("hRaw_qqq%d_ring%d_wedge%d", qqq.id[i], chRing, chWedge), 400, 0, 8000, 400, 0, 8000, eWedgeRaw, eRingRaw, "ERaw");
|
||||
plotter->Fill2D(Form("hGM_qqq%d_ring%d_wedge%d", qqq.id[i], chRing, chWedge), 400, 0, 16000, 400, 0, 16000, eWedge, eRing, "EGM");
|
||||
plotter->Fill2D(Form("hCal_qqq%d_ring%d_wedge%d", qqq.id[i], chRing, chWedge), 400, 0, 10, 400, 0, 10, eWedgeMeV, eRingMeV, "ECal");
|
||||
if(eWedgeRaw >1500 && eRingRaw>1500 )
|
||||
plotter->Fill2D(Form("hCal_cut_qqq%d_ring%d_wedge%d", qqq.id[i], chRing, chWedge), 400, 0, 10, 400, 0, 10, eWedgeMeV, eRingMeV, "ECal_cut");
|
||||
|
||||
plotter->Fill2D(Form("hRCal_qqq%d", qqq.id[i]), 16, 0, 15, 1000, 0, 30, chRing, eRingMeV, "RingCal");
|
||||
plotter->Fill2D(Form("hWCal_qqq%d", qqq.id[i]), 16, 0, 15, 1000, 0, 30, chWedge, eWedgeMeV, "WedgeCal");
|
||||
plotter->Fill2D("hRawQQQ", 4000, 0, 8000, 4000, 0, 8000, eWedgeRaw, eRingRaw);
|
||||
plotter->Fill2D("hGMQQQ", 4000, 0, 8000, 4000, 0, 8000, eWedge, eRing);
|
||||
plotter->Fill2D("hCalQQQ", 4000, 0, 10, 4000, 0, 10, eWedgeMeV, eRingMeV);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return kTRUE;
|
||||
}
|
||||
|
||||
void QQQ_Calcheck::Terminate()
|
||||
{
|
||||
plotter->FlushToDisk();
|
||||
std::cout << "Calibration check file for 2D QQQ histogram saved.\n";
|
||||
}
|
||||
114
QQQ_Calcheck.h
Normal file
114
QQQ_Calcheck.h
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
#ifndef QQQ_Calcheck_h
|
||||
#define QQQ_Calcheck_h
|
||||
|
||||
#include <TROOT.h>
|
||||
#include <TChain.h>
|
||||
#include <TFile.h>
|
||||
#include <TSelector.h>
|
||||
|
||||
#include "Armory/ClassDet.h"
|
||||
|
||||
class QQQ_Calcheck : public TSelector {
|
||||
public :
|
||||
TTree *fChain; //!pointer to the analyzed TTree or TChain
|
||||
|
||||
// Fixed size dimensions of array or collections stored in the TTree if any.
|
||||
|
||||
// Declaration of leaf types
|
||||
Det sx3;
|
||||
Det qqq;
|
||||
Det pc ;
|
||||
|
||||
ULong64_t evID;
|
||||
UInt_t run;
|
||||
|
||||
// List of branches
|
||||
TBranch *b_eventID; //!
|
||||
TBranch *b_run; //!
|
||||
TBranch *b_sx3Multi; //!
|
||||
TBranch *b_sx3ID; //!
|
||||
TBranch *b_sx3Ch; //!
|
||||
TBranch *b_sx3E; //!
|
||||
TBranch *b_sx3T; //!
|
||||
TBranch *b_qqqMulti; //!
|
||||
TBranch *b_qqqID; //!
|
||||
TBranch *b_qqqCh; //!
|
||||
TBranch *b_qqqE; //!
|
||||
TBranch *b_qqqT; //!
|
||||
TBranch *b_pcMulti; //!
|
||||
TBranch *b_pcID; //!
|
||||
TBranch *b_pcCh; //!
|
||||
TBranch *b_pcE; //!
|
||||
TBranch *b_pcT; //!
|
||||
|
||||
QQQ_Calcheck(TTree * /*tree*/ =0) : fChain(0) { }
|
||||
virtual ~QQQ_Calcheck() { }
|
||||
virtual Int_t Version() const { return 2; }
|
||||
virtual void Begin(TTree *tree);
|
||||
virtual void SlaveBegin(TTree *tree);
|
||||
virtual void Init(TTree *tree);
|
||||
virtual Bool_t Notify();
|
||||
virtual Bool_t Process(Long64_t entry);
|
||||
virtual Int_t GetEntry(Long64_t entry, Int_t getall = 0) { return fChain ? fChain->GetTree()->GetEntry(entry, getall) : 0; }
|
||||
virtual void SetOption(const char *option) { fOption = option; }
|
||||
virtual void SetObject(TObject *obj) { fObject = obj; }
|
||||
virtual void SetInputList(TList *input) { fInput = input; }
|
||||
virtual TList *GetOutputList() const { return fOutput; }
|
||||
virtual void SlaveTerminate();
|
||||
virtual void Terminate();
|
||||
|
||||
ClassDef(QQQ_Calcheck,0);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef QQQ_Calcheck_cxx
|
||||
void QQQ_Calcheck::Init(TTree *tree){
|
||||
|
||||
// Set branch addresses and branch pointers
|
||||
if (!tree) return;
|
||||
fChain = tree;
|
||||
fChain->SetMakeClass(1);
|
||||
|
||||
fChain->SetBranchAddress("evID", &evID, &b_eventID);
|
||||
fChain->SetBranchAddress("run", &run, &b_run);
|
||||
|
||||
sx3.SetDetDimension(24,12);
|
||||
qqq.SetDetDimension(4,32);
|
||||
pc.SetDetDimension(2,24);
|
||||
|
||||
fChain->SetBranchAddress("sx3Multi", &sx3.multi, &b_sx3Multi);
|
||||
fChain->SetBranchAddress("sx3ID", &sx3.id, &b_sx3ID);
|
||||
fChain->SetBranchAddress("sx3Ch", &sx3.ch, &b_sx3Ch);
|
||||
fChain->SetBranchAddress("sx3E", &sx3.e, &b_sx3E);
|
||||
fChain->SetBranchAddress("sx3T", &sx3.t, &b_sx3T);
|
||||
fChain->SetBranchAddress("qqqMulti", &qqq.multi, &b_qqqMulti);
|
||||
fChain->SetBranchAddress("qqqID", &qqq.id, &b_qqqID);
|
||||
fChain->SetBranchAddress("qqqCh", &qqq.ch, &b_qqqCh);
|
||||
fChain->SetBranchAddress("qqqE", &qqq.e, &b_qqqE);
|
||||
fChain->SetBranchAddress("qqqT", &qqq.t, &b_qqqT);
|
||||
fChain->SetBranchAddress("pcMulti", &pc.multi, &b_pcMulti);
|
||||
fChain->SetBranchAddress("pcID", &pc.id, &b_pcID);
|
||||
fChain->SetBranchAddress("pcCh", &pc.ch, &b_pcCh);
|
||||
fChain->SetBranchAddress("pcE", &pc.e, &b_pcE);
|
||||
fChain->SetBranchAddress("pcT", &pc.t, &b_pcT);
|
||||
|
||||
}
|
||||
|
||||
Bool_t QQQ_Calcheck::Notify(){
|
||||
|
||||
return kTRUE;
|
||||
}
|
||||
|
||||
void QQQ_Calcheck::SlaveBegin(TTree * /*tree*/){
|
||||
|
||||
TString option = GetOption();
|
||||
|
||||
}
|
||||
|
||||
void QQQ_Calcheck::SlaveTerminate(){
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // #ifdef QQQ_Calcheck_cxx
|
||||
238
README.md
Normal file
238
README.md
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
# ANASEN Analysis
|
||||
|
||||
Analysis code for the **Array for Nuclear Astrophysics and Structure with Exotic Nuclei (ANASEN)** detector at FSU. Processes raw .fsu data through event building, channel mapping, calibration, and physics-level vertex reconstruction for transfer reaction experiments.
|
||||
|
||||
---
|
||||
|
||||
## Detector Overview
|
||||
|
||||
| Detector | Type | Role |
|
||||
|----------|------|------|
|
||||
| **SX3** | Double-sided silicon strip (barrel, ~88 mm radius) | Energy and Z-position of recoils/ejectiles |
|
||||
| **QQQ** | Annular silicon (forward, z = 100 mm) | Energy and polar angle of forward-going particles |
|
||||
| **PC** | Helical proportional counter | dE and azimuthal/Z position via 24 anode + 24 cathode wires |
|
||||
|
||||
The PC uses 24 twisted anode wires and 24 cathode wires. Wire geometry, crossover positions, pseudo-wire interpolation, and track-plane intersection are all handled by `Armory/ClassPW.h`.
|
||||
|
||||
---
|
||||
|
||||
## Full Analysis Chain
|
||||
|
||||
```
|
||||
Raw .fsu files (FSU digitizer output)
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 1. EVENT BUILDING │
|
||||
│ Binary: EventBuilder (Armory/EventBuilder.cpp) │
|
||||
│ Script: buildEvents.sh or ProcessRun.sh <run> <tw> 0 │
|
||||
│ Input : *.fsu files │
|
||||
│ Output: Run_NNN_<timeWindow>ns.root (raw, unmapped TTree) │
|
||||
│ Hits within a configurable time window are grouped as events. │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 3. PRE-ANALYSIS CHECKS (optional) │
|
||||
│ Macro: PreAnalysis.C │
|
||||
│ Plots raw rates and energy spectra per channel from unmapped │
|
||||
│ data — useful for identifying dead/noisy channels before │
|
||||
│ mapping. │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 2. CHANNEL MAPPING │
|
||||
│ Binary: Mapper (Armory/Mapper.cpp) │
|
||||
│ Script: ProcessRun.sh <run> <tw> 0 (calls Mapper internally) │
|
||||
│ Config: mapping.h │
|
||||
│ Input : Eventbuilt ROOT tree │
|
||||
│ Output: Run_NNN_mapped.root │
|
||||
│ Translates hardware (digitizer SN, channel) to logical │
|
||||
│ detector identity (SX3/QQQ/PC, strip/wire number). │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 4. CALIBRATION │
|
||||
│ │
|
||||
| SX3 — two-pass procedure: │
|
||||
│ Pass 1 — Left/Right matching (sx3cal/LRFit.C) │
|
||||
│ │ Start with unity gains: │
|
||||
│ │ LRFit.C fits the left/right charge ratio │
|
||||
│ │ Collate per-detector results into a single rightgains.dat │
|
||||
| │ │
|
||||
│ Pass 2 — Back/Front gain matching (sx3cal/EXFit.C) │
|
||||
│ │ Run on data that is unity-gain sorted but L/R matched │
|
||||
│ │ EXFit.C : │
|
||||
│ │ 1) gain-matches the back strips (backgains.dat) │
|
||||
│ │ 2) corrects dynamic range non-linearity in the fronts │
|
||||
│ │ (frontgains.dat) │
|
||||
│ │ Run for every detector, collate into master backgains.dat │
|
||||
│ │ and frontgains.dat. │
|
||||
│ │ |
|
||||
│ ├── GainMatchQQQ.C │
|
||||
│ │ QQQ ring/wedge gain matching │
|
||||
│ │ Output: qqq_GainMatch.dat │
|
||||
│ ├── CalibrationQQQ.C │
|
||||
│ │ Final absolute energy calibration for all detectors │
|
||||
│ │ Output: qqq_Calib.dat │
|
||||
│ ├── PCGainMatch.C │
|
||||
│ │ PC anode and cathode gain matching │
|
||||
│ │ Output: slope_intercept_*.dat │
|
||||
│ ├── FitHistogramsWithTSpectrum_Sequential_Improved.C │
|
||||
│ │ Automated peak finding on PC wire spectra │
|
||||
│ │ done separately for anodes and cathodes then combined │
|
||||
│ │ (TSpectrum-based; writes centroids for gain match) │
|
||||
│ └──QQQ_Calcheck.C │
|
||||
│ Verifies QQQ calibration against known sources │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 5. VERTEX RECONSTRUCTION (MakeVertex) │
|
||||
│ Macro : MakeVertex.C (ROOT TSelector) │
|
||||
│ Batch : run_sx3.sh (choose dataset, wireflip and offset) │
|
||||
│ Input : Run_NNN_mapped.root │
|
||||
│ Output: result_runNNN.root (calibrated TTree │
|
||||
│ + diagnostic histograms) │
|
||||
│ Applies energy calibrations, reconstructs PC hit positions │
|
||||
│ (anode energy + cathode charge division), clusters wires, │
|
||||
│ and builds SX3/QQQ/PC event objects. │
|
||||
│ 3D track-reconstruction diagnostics using │
|
||||
│ PC wire geometry and Si hit positions. │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Channel Mapping (`mapping.h` / `mapping_alpha.h`)
|
||||
|
||||
The Mapper binary reads the raw TTree (digitizer serial number + channel) and rewrites it with logical detector labels. `mapping.h` is used for standard beam runs; `mapping_alpha.h` for source/alpha calibration runs where the electronics configuration differs. The active mapping is selected by editing the `#include` at the top of `Armory/Mapper.cpp` before building.
|
||||
|
||||
`PrintMapping()` (defined in the mapping header) prints the full channel table at runtime for verification.
|
||||
|
||||
---
|
||||
|
||||
## MakeVertex — Vertex Reconstruction
|
||||
|
||||
`MakeVertex.C` is a ROOT `TSelector` that loops over the analyzed event tree and fills diagnostic and physics histograms. It loads energy-loss lookup tables from `eloss_calculations/` and uses `Armory/Kinematics.h` for two-body reaction kinematics.
|
||||
|
||||
### Diagnostic Section Toggles
|
||||
|
||||
Comment out any `#define` at the top of `MakeVertex.C` to skip that block at compile time:
|
||||
|
||||
```cpp
|
||||
#define DIAG_WIREMULT // anode/cathode cluster multiplicity plots
|
||||
#define DIAG_1WIRE // raw per-wire dE vs Si (no PC required)
|
||||
#define DIAG_PC_SX3 // PC-SX3 coincidence analysis
|
||||
#define DIAG_1A0C_SX3 // 1A0C single-wire vertex with SX3
|
||||
#define DIAG_1A0C_QQQ // 1A0C single-wire vertex with QQQ
|
||||
#define DIAG_NA0C_SX3 // nA0C (n>=2) pseudo-wire vertex with SX3
|
||||
#define DIAG_NA0C_QQQ // nA0C (n>=2) pseudo-wire vertex with QQQ
|
||||
#define DIAG_PC_QQQ // PC-QQQ coincidence analysis
|
||||
```
|
||||
|
||||
### Histogram Output Folders
|
||||
|
||||
| ROOT Folder | Content |
|
||||
|-------------|---------|
|
||||
| `wiremult` | Anode/cathode cluster size and multiplicity matrices |
|
||||
| `1wire` | Raw single-wire dE vs SX3/QQQ energy and phi correlations |
|
||||
| `hTiming` | PC-Si timing spectra |
|
||||
| `hPCZSX3` | PC-Z reconstruction with SX3 coincidence |
|
||||
| `hPCzQQQ` | PC-Z projection with QQQ coincidence |
|
||||
| `1A0C`,`2A0C`, `3A0C`, … | n anodes (n≥1), 0 cathode: pseudo-wire vertex + excitation spectra |
|
||||
| `ainterp_noc` | Pseudo-wire interpolation diagnostics (no cathode gate) |
|
||||
| `phicut` | PC-QQQ with phi coincidence gate |
|
||||
|
||||
### Vertex Reconstruction Methods
|
||||
|
||||
**nA0C (n ≥ 1 anodes, `DIAG_NA0C_SX3/QQQ`):** All anode clusters are flattened and passed to `GetPseudoWire()` to produce an energy-weighted average wire. `getClosestWirePosAtWirePhi()` then finds the point on that pseudo-wire at the Si phi. Histograms land in a folder named `{n}A0C` (e.g. `2A0C`).
|
||||
|
||||
---
|
||||
|
||||
## Energy Loss (`eloss_calculations/`)
|
||||
|
||||
Lookup tables pre-computed with [pycatima](https://github.com/hrosiak/pycatima) for the isobutane/helium fill gas at 250 Torr. Tables cover protons, alphas, aluminum beam, fluorine, and oxygen. `Eloss.py` regenerates the tables; `make_eloss_table.C` is a ROOT-based alternative generator.
|
||||
|
||||
The `cm_to_MeV` / `MeV_to_cm` TGraph pairs loaded in `MakeVertex.C` provide fast range–energy conversion for correcting energy loss in the Si detectors.
|
||||
|
||||
---
|
||||
|
||||
## Geometry Utilities
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `grid_generate.py` | Generates `detector_geometry.dat` — calculates SX3, QQQ, and PC wire azimuthal angles from first principles given detector radii and strip positions |
|
||||
| `shadowplay.py` | Exports ANASEN geometry (wires, SX3, QQQ) as a VTK file + `anasen_labels.csv` for visualisation in ParaView |
|
||||
| `detector_geometry.dat` | Pre-generated geometry reference (SX3 strip angles, wire positions) |
|
||||
| `anasen_labels.csv` | Label table for ParaView geometry display |
|
||||
|
||||
---
|
||||
|
||||
## FEM Simulations (`anasen_fem/`)
|
||||
|
||||
Finite-element simulations of the PC electric field using gmsh (meshing), Elmer (FEM solver), and ParaView (visualisation). See `anasen_fem/README.md` for full setup and installation instructions.
|
||||
|
||||
```bash
|
||||
cd anasen_fem
|
||||
python3 run.py # mesh → FEM solve → field extraction → ParaView output
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Batch Processing Scripts
|
||||
|
||||
| Script | Purpose |
|
||||
|--------|---------|
|
||||
| `buildEvents.sh <run> <tw>` | Build events from raw .fsu for a single run |
|
||||
| `ProcessRun.sh <run> <tw> <opt>` | Full single-run pipeline: event build (opt=0) or analysis (opt=1) |
|
||||
| `BatchProcess.sh` | Parallel batch processing over a run range via GNU parallel |
|
||||
| `process_mapped_run.sh <start> <end>` | Run `TrackRecon.C` in parallel over a range of mapped files |
|
||||
| `run_sx3.sh/run_27Al.sh/run_17F.sh` | Run `VertexRecon.C` in parallel over a range of mapped files and hAdds them for analysis|
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
|
||||
- [ROOT](https://root.cern) ≥ 6.x (TSelector, TChain, TVector3, TF1, TGraph, TSpectrum)
|
||||
- [GNU parallel](https://www.gnu.org/software/parallel/) — for batch scripts
|
||||
- [pycatima](https://github.com/hrosiak/pycatima) — for regenerating energy-loss tables
|
||||
- gmsh + Elmer + ParaView — for FEM simulations only
|
||||
|
||||
---
|
||||
|
||||
## Key Files Reference
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `mapping.h` / `mapping_alpha.h` | Hardware→logical channel mapping (beam / alpha-source configs) |
|
||||
| `Armory/EventBuilder.cpp` | Time-window coincidence event builder (compiled binary) |
|
||||
| `Armory/Mapper.cpp` | Channel remapper: SN+ch → detector ID (compiled binary) |
|
||||
| `PreAnalysis.C / .h` | Raw rate and energy checks on mapped data |
|
||||
| `sx3cal/EXFit.C`, `EXFit2.C` | SX3 front/back gain coefficient extraction |
|
||||
| `sx3cal/LRFit.C` | SX3 left-right position calibration |
|
||||
| `sx3cal/{17F,27Al}/` | Per-experiment gain coefficient files (frontgains, backgains, rightgains) |
|
||||
| `GainMatchQQQ.C` | QQQ gain matching |
|
||||
| `Calibration.C / .h` | Absolute energy calibration |
|
||||
| `QQQ_Calcheck.C` | QQQ calibration verification |
|
||||
| `PCGainMatch.C` | PC anode/cathode gain matching |
|
||||
| `FitHistogramsWithTSpectrum_Sequential_Improved.C` | Automated PC anode peak fitting |
|
||||
| `Analyzer.C / .h` | Event-level clustering and PC hit reconstruction, (older in need of rehaul)|
|
||||
| `Analysis.C` | TChain wrapper to run Analyzer over a run range |
|
||||
| `processRun.C` | Single-file wrapper to run Analyzer on one ROOT file |
|
||||
| `MakeVertex.C / .h` | Vertex reconstruction and physics histogram production |
|
||||
| `TrackRecon.C / .h` | Alternate to VertexRecon, to track changes through code, eventaully plan to clean up one for actual data analysis |
|
||||
| `RunTimeSummary.C` | Run-by-run timing/rate summary across a run range |
|
||||
| `Armory/ClassPW.h` | PC wire geometry: crossovers, pseudo-wire, track-plane intersection |
|
||||
| `Armory/Kinematics.h/.cpp` | Two-body reaction kinematics (excitation energy, Q-value) |
|
||||
| `Armory/HistPlotter.h` | Histogram management with named ROOT folders |
|
||||
| `Armory/SX3Geom.h` | SX3 detector geometry |
|
||||
| `Armory/PC_StepLadder_Correction.h` | PC Z-position slope correction |
|
||||
| `eloss_calculations/Eloss.py` | Energy-loss table generator (pycatima) |
|
||||
| `grid_generate.py` | Generates `detector_geometry.dat` from first principles |
|
||||
| `shadowplay.py` | Exports ANASEN geometry to VTK + CSV for ParaView |
|
||||
| `detector_geometry.dat` | Pre-generated detector geometry reference |
|
||||
| `anasen_labels.csv` | Label table for ParaView geometry visualisation |
|
||||
| `anasen_fem/` | FEM electric field simulations for the PC |
|
||||
140
RunTimeSummary.C
Normal file
140
RunTimeSummary.C
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
#include <TFile.h>
|
||||
#include <TH1.h>
|
||||
#include <TH2.h>
|
||||
#include <TString.h>
|
||||
#include <TSystem.h>
|
||||
#include <TCanvas.h>
|
||||
#include <iostream>
|
||||
|
||||
void RunTimeSummary(int startRun, int endRun)
|
||||
{
|
||||
TString fileDir = "/mnt/d/Remapped_files/17F_data/root_data/";
|
||||
TString histName = "AnodeQQQ_Time";
|
||||
TString filePattern = "Run_%03d_mapped_histograms.root";
|
||||
TString filePatternAlt = "ProtonRun_%d_mapped_histograms.root";
|
||||
TString filePatternAlt2 = "Source_%d_mapped_histograms.root";
|
||||
|
||||
int nBinsTime = 0;
|
||||
double timeMin = 0, timeMax = 0;
|
||||
bool foundRef = false;
|
||||
|
||||
for (int r = startRun; r <= endRun; r++)
|
||||
{
|
||||
TString tempName;
|
||||
|
||||
// 1. Try Pattern 1: Run_XXX...
|
||||
tempName = fileDir + Form(filePattern, r);
|
||||
if (gSystem->AccessPathName(tempName))
|
||||
{ // Returns true if MISSING
|
||||
|
||||
// 2. Try Pattern 2: ProtonRun_X...
|
||||
tempName = fileDir + Form(filePatternAlt, r);
|
||||
if (gSystem->AccessPathName(tempName))
|
||||
{
|
||||
|
||||
// 3. Try Pattern 3: Source_X...
|
||||
tempName = fileDir + Form(filePatternAlt2, r);
|
||||
if (gSystem->AccessPathName(tempName))
|
||||
{
|
||||
// All 3 patterns failed. Skip this run.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here, 'tempName' holds the valid filename that was found
|
||||
TFile *fTemp = TFile::Open(tempName);
|
||||
if (!fTemp || fTemp->IsZombie())
|
||||
{
|
||||
if (fTemp)
|
||||
delete fTemp;
|
||||
continue;
|
||||
}
|
||||
|
||||
TH1F *hRef = (TH1F *)fTemp->Get(histName);
|
||||
if (hRef)
|
||||
{
|
||||
|
||||
nBinsTime = hRef->GetNbinsX();
|
||||
timeMin = hRef->GetXaxis()->GetXmin();
|
||||
timeMax = hRef->GetXaxis()->GetXmax();
|
||||
foundRef = true;
|
||||
|
||||
delete hRef;
|
||||
fTemp->Close();
|
||||
delete fTemp;
|
||||
printf("Reference found in Run %d: %d bins, Range [%.1f, %.1f]\n", r, nBinsTime, timeMin, timeMax);
|
||||
break;
|
||||
}
|
||||
fTemp->Close();
|
||||
delete fTemp;
|
||||
}
|
||||
|
||||
if (!foundRef)
|
||||
{
|
||||
printf("Error: No valid histograms found in the entire range. Exiting.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int nRuns = endRun - startRun + 1;
|
||||
TH2F *hSummary = new TH2F("hSummary",
|
||||
Form("Timing Summary (Runs %d-%d);Timing;Run Number", startRun, endRun),
|
||||
nBinsTime, timeMin, timeMax,
|
||||
nRuns, startRun, endRun + 1);
|
||||
|
||||
for (int run = startRun; run <= endRun; run++)
|
||||
{
|
||||
|
||||
TString filename = fileDir + Form(filePattern, run);
|
||||
|
||||
if (gSystem->AccessPathName(filename))
|
||||
continue;
|
||||
|
||||
TFile *fin = TFile::Open(filename);
|
||||
if (!fin || fin->IsZombie())
|
||||
{
|
||||
if (fin)
|
||||
delete fin;
|
||||
continue;
|
||||
}
|
||||
|
||||
TH1F *hin = (TH1F *)fin->Get(histName);
|
||||
|
||||
if (hin)
|
||||
{
|
||||
// Determine which ROW (Y-bin) corresponds to this Run
|
||||
// Note: ROOT bins start at 1.
|
||||
// If startRun=10 and run=10 -> binY=1.
|
||||
int binY_Run = run - startRun + 1;
|
||||
|
||||
// Loop through the Time bins (X-bins in the 1D hist)
|
||||
for (int binX_Time = 1; binX_Time <= hin->GetNbinsX(); binX_Time++)
|
||||
{
|
||||
|
||||
double content = hin->GetBinContent(binX_Time);
|
||||
|
||||
// Copy content to: (Time, Run)
|
||||
if (content > 0)
|
||||
{
|
||||
hSummary->SetBinContent(binX_Time, binY_Run, content);
|
||||
}
|
||||
}
|
||||
delete hin;
|
||||
}
|
||||
|
||||
fin->Close();
|
||||
delete fin;
|
||||
|
||||
if ((run - startRun) % 10 == 0)
|
||||
printf("Stitched Run %d...\n", run);
|
||||
}
|
||||
|
||||
TFile *fOut = new TFile("SummaryPlot.root", "RECREATE");
|
||||
hSummary->Write();
|
||||
|
||||
TCanvas *c1 = new TCanvas("c1", "Time Summary Plot", 1000, 800);
|
||||
hSummary->SetStats(0);
|
||||
hSummary->Draw("COLZ");
|
||||
|
||||
printf("Done! Saved to SummaryPlot.root\n");
|
||||
}
|
||||
5149
TrackRecon.C
5149
TrackRecon.C
File diff suppressed because it is too large
Load Diff
171
TrackRecon.h
171
TrackRecon.h
|
|
@ -5,110 +5,137 @@
|
|||
#include <TChain.h>
|
||||
#include <TFile.h>
|
||||
#include <TSelector.h>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <unordered_map>
|
||||
#include <tuple>
|
||||
|
||||
#include "Armory/ClassDet.h"
|
||||
#include "Armory/ClassPW.h"
|
||||
|
||||
class TrackRecon : public TSelector {
|
||||
public :
|
||||
TTree *fChain; //!pointer to the analyzed TTree or TChain
|
||||
|
||||
// Fixed size dimensions of array or collections stored in the TTree if any.
|
||||
class TrackRecon : public TSelector
|
||||
{
|
||||
public:
|
||||
TTree *fChain; //! pointer to the analyzed TTree or TChain
|
||||
|
||||
// Declaration of leaf types
|
||||
Det sx3;
|
||||
Det qqq;
|
||||
Det pc ;
|
||||
Det pc;
|
||||
Det misc;
|
||||
|
||||
ULong64_t evID;
|
||||
UInt_t run;
|
||||
ULong64_t evID;
|
||||
UInt_t run;
|
||||
|
||||
// List of branches
|
||||
TBranch *b_eventID; //!
|
||||
TBranch *b_run; //!
|
||||
TBranch *b_sx3Multi; //!
|
||||
TBranch *b_sx3ID; //!
|
||||
TBranch *b_sx3Ch; //!
|
||||
TBranch *b_sx3E; //!
|
||||
TBranch *b_sx3T; //!
|
||||
TBranch *b_qqqMulti; //!
|
||||
TBranch *b_qqqID; //!
|
||||
TBranch *b_qqqCh; //!
|
||||
TBranch *b_qqqE; //!
|
||||
TBranch *b_qqqT; //!
|
||||
TBranch *b_pcMulti; //!
|
||||
TBranch *b_pcID; //!
|
||||
TBranch *b_pcCh; //!
|
||||
TBranch *b_pcE; //!
|
||||
TBranch *b_pcT; //!
|
||||
TBranch *b_eventID; //!
|
||||
TBranch *b_run; //!
|
||||
TBranch *b_sx3Multi; //!
|
||||
TBranch *b_sx3ID; //!
|
||||
TBranch *b_sx3Ch; //!
|
||||
TBranch *b_sx3E; //!
|
||||
TBranch *b_sx3T; //!
|
||||
TBranch *b_qqqMulti; //!
|
||||
TBranch *b_qqqID; //!
|
||||
TBranch *b_qqqCh; //!
|
||||
TBranch *b_qqqE; //!
|
||||
TBranch *b_qqqT; //!
|
||||
TBranch *b_pcMulti; //!
|
||||
TBranch *b_pcID; //!
|
||||
TBranch *b_pcCh; //!
|
||||
TBranch *b_pcE; //!
|
||||
TBranch *b_pcT; //!
|
||||
TBranch *b_miscMulti; //!
|
||||
TBranch *b_miscID; //!
|
||||
TBranch *b_miscCh; //!
|
||||
TBranch *b_miscE; //!
|
||||
TBranch *b_miscT; //!
|
||||
TBranch *b_miscTf; //!
|
||||
|
||||
TrackRecon(TTree * /*tree*/ =0) : fChain(0) { }
|
||||
virtual ~TrackRecon() { }
|
||||
virtual Int_t Version() const { return 2; }
|
||||
virtual void Begin(TTree *tree);
|
||||
virtual void SlaveBegin(TTree *tree);
|
||||
virtual void Init(TTree *tree);
|
||||
virtual Bool_t Notify();
|
||||
virtual Bool_t Process(Long64_t entry);
|
||||
virtual Int_t GetEntry(Long64_t entry, Int_t getall = 0) { return fChain ? fChain->GetTree()->GetEntry(entry, getall) : 0; }
|
||||
virtual void SetOption(const char *option) { fOption = option; }
|
||||
virtual void SetObject(TObject *obj) { fObject = obj; }
|
||||
virtual void SetInputList(TList *input) { fInput = input; }
|
||||
virtual TList *GetOutputList() const { return fOutput; }
|
||||
virtual void SlaveTerminate();
|
||||
virtual void Terminate();
|
||||
// 1. Geometry Cache
|
||||
Coord Crossover[24][24][2];
|
||||
|
||||
ClassDef(TrackRecon,0);
|
||||
// 2. Persistent Vectors (REQUIRED for the optimized .cxx to work)
|
||||
std::vector<std::pair<int, double>> anodeHits;
|
||||
std::vector<std::pair<int, double>> cathodeHits;
|
||||
std::vector<std::pair<int, double>> corrcatMax;
|
||||
std::vector<std::pair<int, double>> corranoMax;
|
||||
std::vector<double> cathodeTimes;
|
||||
std::vector<double> anodeTimes;
|
||||
std::unordered_map<int, std::tuple<int, double, double>> aWireEvents, cWireEvents;
|
||||
|
||||
TrackRecon(TTree * /*tree*/ = 0) : fChain(0) {}
|
||||
virtual ~TrackRecon() {}
|
||||
virtual Int_t Version() const { return 2; }
|
||||
virtual void Begin(TTree *tree);
|
||||
virtual void SlaveBegin(TTree *tree);
|
||||
virtual void Init(TTree *tree);
|
||||
virtual Bool_t Notify();
|
||||
virtual Bool_t Process(Long64_t entry);
|
||||
virtual Int_t GetEntry(Long64_t entry, Int_t getall = 0) { return fChain ? fChain->GetTree()->GetEntry(entry, getall) : 0; }
|
||||
virtual void SetOption(const char *option) { fOption = option; }
|
||||
virtual void SetObject(TObject *obj) { fObject = obj; }
|
||||
virtual void SetInputList(TList *input) { fInput = input; }
|
||||
virtual TList *GetOutputList() const { return fOutput; }
|
||||
virtual void SlaveTerminate();
|
||||
virtual void Terminate();
|
||||
void OldAnalysis();
|
||||
|
||||
ClassDef(TrackRecon, 0);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef TrackRecon_cxx
|
||||
void TrackRecon::Init(TTree *tree){
|
||||
void TrackRecon::Init(TTree *tree)
|
||||
{
|
||||
|
||||
// Set branch addresses and branch pointers
|
||||
if (!tree) return;
|
||||
if (!tree)
|
||||
return;
|
||||
fChain = tree;
|
||||
fChain->SetMakeClass(1);
|
||||
|
||||
fChain->SetBranchAddress("evID", &evID, &b_eventID);
|
||||
fChain->SetBranchAddress("run", &run, &b_run);
|
||||
|
||||
sx3.SetDetDimension(24,12);
|
||||
qqq.SetDetDimension(4,32);
|
||||
pc.SetDetDimension(2,24);
|
||||
sx3.SetDetDimension(24, 12);
|
||||
qqq.SetDetDimension(4, 32);
|
||||
pc.SetDetDimension(2, 24);
|
||||
|
||||
fChain->SetBranchAddress("sx3Multi", &sx3.multi, &b_sx3Multi);
|
||||
fChain->SetBranchAddress("sx3ID", &sx3.id, &b_sx3ID);
|
||||
fChain->SetBranchAddress("sx3Ch", &sx3.ch, &b_sx3Ch);
|
||||
fChain->SetBranchAddress("sx3E", &sx3.e, &b_sx3E);
|
||||
fChain->SetBranchAddress("sx3T", &sx3.t, &b_sx3T);
|
||||
fChain->SetBranchAddress("sx3ID", &sx3.id, &b_sx3ID);
|
||||
fChain->SetBranchAddress("sx3Ch", &sx3.ch, &b_sx3Ch);
|
||||
fChain->SetBranchAddress("sx3E", &sx3.e, &b_sx3E);
|
||||
fChain->SetBranchAddress("sx3T", &sx3.t, &b_sx3T);
|
||||
fChain->SetBranchAddress("qqqMulti", &qqq.multi, &b_qqqMulti);
|
||||
fChain->SetBranchAddress("qqqID", &qqq.id, &b_qqqID);
|
||||
fChain->SetBranchAddress("qqqCh", &qqq.ch, &b_qqqCh);
|
||||
fChain->SetBranchAddress("qqqE", &qqq.e, &b_qqqE);
|
||||
fChain->SetBranchAddress("qqqT", &qqq.t, &b_qqqT);
|
||||
fChain->SetBranchAddress("pcMulti", &pc.multi, &b_pcMulti);
|
||||
fChain->SetBranchAddress("pcID", &pc.id, &b_pcID);
|
||||
fChain->SetBranchAddress("pcCh", &pc.ch, &b_pcCh);
|
||||
fChain->SetBranchAddress("pcE", &pc.e, &b_pcE);
|
||||
fChain->SetBranchAddress("pcT", &pc.t, &b_pcT);
|
||||
|
||||
fChain->SetBranchAddress("qqqID", &qqq.id, &b_qqqID);
|
||||
fChain->SetBranchAddress("qqqCh", &qqq.ch, &b_qqqCh);
|
||||
fChain->SetBranchAddress("qqqE", &qqq.e, &b_qqqE);
|
||||
fChain->SetBranchAddress("qqqT", &qqq.t, &b_qqqT);
|
||||
fChain->SetBranchAddress("pcMulti", &pc.multi, &b_pcMulti);
|
||||
fChain->SetBranchAddress("pcID", &pc.id, &b_pcID);
|
||||
fChain->SetBranchAddress("pcCh", &pc.ch, &b_pcCh);
|
||||
fChain->SetBranchAddress("pcE", &pc.e, &b_pcE);
|
||||
fChain->SetBranchAddress("pcT", &pc.t, &b_pcT);
|
||||
fChain->SetBranchAddress("miscMulti", &misc.multi, &b_miscMulti);
|
||||
fChain->SetBranchAddress("miscID", &misc.id, &b_miscID);
|
||||
fChain->SetBranchAddress("miscCh", &misc.ch, &b_miscCh);
|
||||
fChain->SetBranchAddress("miscE", &misc.e, &b_miscE);
|
||||
fChain->SetBranchAddress("miscT", &misc.t, &b_miscT);
|
||||
fChain->SetBranchAddress("miscF", &misc.tf, &b_miscTf);
|
||||
}
|
||||
|
||||
Bool_t TrackRecon::Notify(){
|
||||
|
||||
Bool_t TrackRecon::Notify()
|
||||
{
|
||||
return kTRUE;
|
||||
}
|
||||
|
||||
void TrackRecon::SlaveBegin(TTree * /*tree*/){
|
||||
|
||||
TString option = GetOption();
|
||||
|
||||
void TrackRecon::SlaveBegin(TTree * /*tree*/)
|
||||
{
|
||||
// TString option = GetOption();
|
||||
}
|
||||
|
||||
void TrackRecon::SlaveTerminate(){
|
||||
|
||||
void TrackRecon::SlaveTerminate()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#endif // #ifdef TrackRecon_cxx
|
||||
#endif // #ifdef TrackRecon_cxx
|
||||
187
anasen_fem/1dpotplot.py
Executable file
187
anasen_fem/1dpotplot.py
Executable file
|
|
@ -0,0 +1,187 @@
|
|||
#!/home/vsitaraman/ParaView-6.1.0-RC1-MPI-Linux-Python3.12-x86_64/bin/pvbatch
|
||||
|
||||
import sys
|
||||
import numpy as np
|
||||
from paraview.simple import *
|
||||
import paraview.servermanager as sm
|
||||
from paraview.vtk.numpy_interface import dataset_adapter as dsa
|
||||
|
||||
# ==========================================
|
||||
# COMMAND LINE ARGUMENTS
|
||||
# ==========================================
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: pvbatch 1dpotplot.py <z_locus in mm>")
|
||||
sys.exit(1)
|
||||
|
||||
z_loc = float(sys.argv[1])
|
||||
|
||||
INPUT_FILE = f"wires2d/elfield_anasen_t0001.vtu"
|
||||
OUTPUT_CSV = f"anode_to_cathode_1d_{z_loc}.csv"
|
||||
OUTPUT_IMAGE = f"1D_Variation_Plot_{z_loc}.png"
|
||||
|
||||
print(f"--- Starting 1D Potential Extraction for Z = {z_loc} mm ---")
|
||||
|
||||
# ==========================================
|
||||
# 1. EXACT GEOMETRIC CALCULATION
|
||||
# ==========================================
|
||||
print("Calculating theoretical wire coordinates...")
|
||||
|
||||
wireShift = 4.0
|
||||
k = 2 * np.pi / 24.0
|
||||
|
||||
offset_a1 = -10.0 * k
|
||||
offset_c1 = -5.5 * k
|
||||
offset_a2 = offset_a1 + (wireShift * k)
|
||||
offset_c2 = offset_c1 - (wireShift * k)
|
||||
|
||||
t = (z_loc + 174.3) / (2 * 174.3)
|
||||
|
||||
# Anode 0 theoretical position
|
||||
xa_loc = (37.0 * np.cos(offset_a1)) + t * (37.0 * np.cos(offset_a2) - 37.0 * np.cos(offset_a1))
|
||||
ya_loc = (37.0 * np.sin(offset_a1)) + t * (37.0 * np.sin(offset_a2) - 37.0 * np.sin(offset_a1))
|
||||
|
||||
# All Cathodes theoretical positions
|
||||
xc1_all = np.array([42.0 * np.cos(k * i + offset_c1) for i in range(24)])
|
||||
yc1_all = np.array([42.0 * np.sin(k * i + offset_c1) for i in range(24)])
|
||||
xc2_all = np.array([42.0 * np.cos(k * i + offset_c2) for i in range(24)])
|
||||
yc2_all = np.array([42.0 * np.sin(k * i + offset_c2) for i in range(24)])
|
||||
|
||||
xc_loc_all = xc1_all + t * (xc2_all - xc1_all)
|
||||
yc_loc_all = yc1_all + t * (yc2_all - yc1_all)
|
||||
|
||||
# Nearest Cathode theoretical position
|
||||
distances = np.sqrt((xc_loc_all - xa_loc)**2 + (yc_loc_all - ya_loc)**2)
|
||||
nearest_c_idx = np.argmin(distances)
|
||||
xc_loc = xc_loc_all[nearest_c_idx]
|
||||
yc_loc = yc_loc_all[nearest_c_idx]
|
||||
|
||||
# Theoretical targets in meters
|
||||
math_anode = np.array([xa_loc / 1000.0, ya_loc / 1000.0, 0.0])
|
||||
math_cathode = np.array([xc_loc / 1000.0, yc_loc / 1000.0, 0.0])
|
||||
# ==========================================
|
||||
# 2. MESH DATA SNAPPING (Electrode-Constrained)
|
||||
# ==========================================
|
||||
print(f"Loading VTU file: {INPUT_FILE} to snap to electrode nodes...")
|
||||
|
||||
reader = XMLUnstructuredGridReader(FileName=[INPUT_FILE])
|
||||
UpdatePipeline()
|
||||
|
||||
# Fetch data to Python
|
||||
raw_data = sm.Fetch(reader)
|
||||
wrapped_data = dsa.WrapDataObject(raw_data)
|
||||
|
||||
pts = wrapped_data.Points
|
||||
pot = wrapped_data.PointData['potential']
|
||||
|
||||
print("\nPotential range:")
|
||||
print(" min =", np.min(pot))
|
||||
print(" max =", np.max(pot))
|
||||
|
||||
# -------------------------------------------------
|
||||
# Find actual electrode nodes
|
||||
# -------------------------------------------------
|
||||
|
||||
anode_candidates = np.where(pot > 640.0)[0]
|
||||
|
||||
if len(anode_candidates) == 0:
|
||||
raise RuntimeError("No anode nodes found near 650 V")
|
||||
|
||||
cathode_candidates = np.where(np.abs(pot) < 1.0)[0]
|
||||
|
||||
if len(cathode_candidates) == 0:
|
||||
raise RuntimeError("No cathode nodes found near 0 V")
|
||||
|
||||
print(f"Found {len(anode_candidates)} anode nodes")
|
||||
print(f"Found {len(cathode_candidates)} cathode nodes")
|
||||
|
||||
# -------------------------------------------------
|
||||
# Snap theoretical anode location to nearest
|
||||
# actual anode node
|
||||
# -------------------------------------------------
|
||||
|
||||
anode_dist = np.linalg.norm(
|
||||
pts[anode_candidates] - math_anode,
|
||||
axis=1
|
||||
)
|
||||
|
||||
anode_idx = anode_candidates[np.argmin(anode_dist)]
|
||||
|
||||
true_anode_pos = pts[anode_idx]
|
||||
true_anode_pot = pot[anode_idx]
|
||||
|
||||
# -------------------------------------------------
|
||||
# Snap theoretical cathode location to nearest
|
||||
# actual cathode node
|
||||
# -------------------------------------------------
|
||||
|
||||
cathode_dist = np.linalg.norm(
|
||||
pts[cathode_candidates] - math_cathode,
|
||||
axis=1
|
||||
)
|
||||
|
||||
cathode_idx = cathode_candidates[np.argmin(cathode_dist)]
|
||||
|
||||
true_cathode_pos = pts[cathode_idx]
|
||||
true_cathode_pot = pot[cathode_idx]
|
||||
|
||||
# -------------------------------------------------
|
||||
# Diagnostics
|
||||
# -------------------------------------------------
|
||||
|
||||
anode_snap_error = np.linalg.norm(
|
||||
true_anode_pos - math_anode
|
||||
)
|
||||
|
||||
cathode_snap_error = np.linalg.norm(
|
||||
true_cathode_pos - math_cathode
|
||||
)
|
||||
|
||||
print("\nSelected electrode nodes:")
|
||||
print(f"Anode:")
|
||||
print(f" position = {true_anode_pos}")
|
||||
print(f" potential = {true_anode_pot:.3f} V")
|
||||
print(f" distance = {anode_snap_error*1e6:.1f} um")
|
||||
|
||||
print(f"\nCathode:")
|
||||
print(f" position = {true_cathode_pos}")
|
||||
print(f" potential = {true_cathode_pot:.3f} V")
|
||||
print(f" distance = {cathode_snap_error*1e6:.1f} um")
|
||||
# ==========================================
|
||||
# 3. PARAVIEW EXTRACTION & PLOTTING
|
||||
# ==========================================
|
||||
print("Extracting data along the snapped line...")
|
||||
plot_over_line = PlotOverLine(Input=reader)
|
||||
|
||||
# Apply the SNAPPED coordinates (converted to standard Python lists)
|
||||
plot_over_line.Point1 = true_anode_pos.tolist()
|
||||
plot_over_line.Point2 = true_cathode_pos.tolist()
|
||||
plot_over_line.Resolution = 1000
|
||||
|
||||
SaveData(OUTPUT_CSV, proxy=plot_over_line)
|
||||
|
||||
print("Rendering plot...")
|
||||
line_chart_view = CreateView('XYChartView')
|
||||
line_chart_view.ViewSize = [1000, 600]
|
||||
line_chart_view.ChartTitle = f'Potential & E-Field: Anode to Cathode (Z = {z_loc} mm)'
|
||||
line_chart_view.LeftAxisTitle = 'Potential (V)'
|
||||
line_chart_view.BottomAxisTitle = 'Distance from Anode (m)'
|
||||
|
||||
line_display = Show(plot_over_line, line_chart_view)
|
||||
|
||||
# Tell ParaView to draw BOTH variables
|
||||
# line_display.SeriesVisibility = ['potential', 'electric field_Magnitude']
|
||||
line_display.SeriesVisibility = ['potential']
|
||||
|
||||
# Style Potential (Red)
|
||||
line_display.SeriesColor = ['potential', '1.0', '0.0', '0.0']
|
||||
line_display.SeriesLineThickness = ['potential', '3']
|
||||
|
||||
# # Style Electric Field (Blue, Right Axis)
|
||||
# line_display.SeriesColor = ['electric field_Magnitude', '0.0', '0.5', '1.0']
|
||||
# line_display.SeriesLineThickness = ['electric field_Magnitude', '3']
|
||||
# line_display.SeriesPlotCorner = ['electric field_Magnitude', '1']
|
||||
# line_chart_view.RightAxisTitle = 'Electric Field (V/m)'
|
||||
|
||||
SaveScreenshot(OUTPUT_IMAGE, view=line_chart_view)
|
||||
print(f"Saved plot screenshot to: {OUTPUT_IMAGE}")
|
||||
print("--- Extraction Complete ---")
|
||||
46
anasen_fem/README.md
Executable file
46
anasen_fem/README.md
Executable file
|
|
@ -0,0 +1,46 @@
|
|||
### README for ANASEN fem simulations:
|
||||
|
||||
* There are a few iterations of these simulations that already exist. Be sure to also locate and refer to them if necessary.
|
||||
* Install gmsh and its python api by running (Ubuntu 22.04 LTS)
|
||||
|
||||
```
|
||||
sudo apt install gmsh python3-gmsh
|
||||
```
|
||||
|
||||
* Gmsh gives us the tools to create a meshgrid that samples the 2d space appropriately to plot the field/equipotential lines.
|
||||
* The output file typically has the .msh extension. This is read as input to Elmer, which is the FEM differential-equation solver.
|
||||
|
||||
* Install Elmer via the following steps:
|
||||
|
||||
```
|
||||
sudo add-apt-repository ppa:elmer-csc-ubuntu/elmer-csc-ppa
|
||||
sudo apt install elmerfem-csc-eg
|
||||
```
|
||||
|
||||
* Install ParaView for visualizations by downloading from the Linux .tar.gz link at https://www.paraview.org/download/
|
||||
- The current version is tested to work on Paraview 6.1.0. The default version in Ubuntu 22.04 repositories has some trouble with scripting
|
||||
* v0.0.1, March 10 2026
|
||||
- 2d simulations of fields only. gmsh for meshing, elmer for fem, paraview to plot
|
||||
- Before running, open `paraview_plotter.py` to make the bash shebang (#!) point to the location of `pvpython` or `pvbatch`
|
||||
- `python3 run.py` should run everything in order, and is hopefully all the files are self-documenting
|
||||
* v0.0.2, September 2026
|
||||
- Adds the Ramo weighting field and the `E . E_w` dot product for one cathode wire.
|
||||
- `wires_gmsh2d_bc.py` now cuts the wires into the gas disk with `occ.fragment` instead of `mesh.embed`, so each wire boundary is a real edge of the domain. One cathode (optional 2nd argument, default 1) gets its own physical group, **tag 40**; the other 23 stay in tag 30.
|
||||
- Two solves, same mesh, identical apart from the potentials:
|
||||
|
||||
| file | tag 40 | output |
|
||||
|---|---|---|
|
||||
| `wires2d.sif` | 0 V, like every other cathode | `wires2d/elfield_anasen_t0001.vtu` |
|
||||
| `wires2d_weight.sif` | 1 V, all else 0 V | `wires2d/elfield_weight_t0001.vtu` |
|
||||
|
||||
- `dotproduct.py` reads both and writes `wires2d/dotproduct.vtu` with `DotProduct = E . E_w` (V/m^2). It aborts if the node sets differ or if either field is all zeros — both are silent failures otherwise.
|
||||
- `paraview_dotproduct.py` renders it. `DOT_MIN`/`DOT_MAX` set the colour range and decide whether the picture shows anything.
|
||||
- `paraview_plotter.py` gains two views: the equipotentials over one quadrant, and streamlines over the same quadrant. Streamlines are drift paths up to diffusion, so they show which wire collects charge from where; `SEED_POTENTIAL` and `SEED_STRIDE` control them.
|
||||
- Six PNGs per z-locus, archived as before (`<Stem>_z_<count>_<z>[_quarter].png`). `Field_ouput` keeps its typo so the new files sort with the dozen already in `png/`.
|
||||
- `STAGE` in `run.py`: `1` = mesh + physical solve, `2` = weighting solve + dot product reusing stage 1's mesh, `0` = both. Stage 2 never re-meshes, so it applies to whichever z stage 1 ran last.
|
||||
- **Do not pass `-autoclean` to ElmerGrid.** It renumbers the physical groups (13/10/20/30/40 become 1/4/5/6/7), so every `Target Bodies`/`Target Boundaries` in the sifs matches nothing, Elmer solves nothing, and the potential comes out identically zero with no error.
|
||||
- `mesh.recombine()` and `mesh.refine()` are both off: recombination ran over half an hour on the barrel surface without finishing, and refine took the mesh to ~16M nodes.
|
||||
- Note on the solver stack: both sifs are the same file bar the potentials, so whatever `FluxSolver` does to `Electric Field` it does equally to both and the dot product stays consistent.
|
||||
* v0.0.3, planned TODO
|
||||
- Garfield to take Elmer results and perform charge-transport
|
||||
- Sweep the weighting solve over all 24 cathodes rather than one at a time
|
||||
42
anasen_fem/cfrac_pred_2026-10-28/README.md
Normal file
42
anasen_fem/cfrac_pred_2026-10-28/README.md
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Charge-fraction prediction, 2026-10-28
|
||||
|
||||
Four slices solved for the defense figure `defense/figures/cfrac_pred.png`,
|
||||
archived here beside the solves that were already in `wires2d/vtu_files`.
|
||||
|
||||
| index | z (mm) | phase in the cell | relative azimuth | anode-cathode gap |
|
||||
|---|---|---|---|---|
|
||||
| 10 | 0.0000 | midpoint | 7.500 deg | 6.220 mm |
|
||||
| 10 | 19.6900 | crossover | 0.037 deg | 4.339 mm |
|
||||
| 11 | 19.7940 | crossover | 0.003 deg | 4.339 mm |
|
||||
| 12 | 29.8500 | quarter | 3.794 deg | 4.908 mm |
|
||||
| 13 | 39.9200 | midpoint, next cell | 7.435 deg | 6.247 mm |
|
||||
| 14 | 44.7820 | three eighths | 5.625 deg | 5.535 mm |
|
||||
| 15 | 49.9800 | three quarters | 3.699 deg | 4.925 mm |
|
||||
|
||||
Indices 10 and 11 were already here; 12 to 15 were added by this study.
|
||||
Each index has an `elfield_anasen_*` (physical solve, anodes at 660 V) and an
|
||||
`elfield_weight_*` (Ramo weighting solve, cathode 1 at 1 V) on the same mesh.
|
||||
The archived 10 and 11 ran at 650 V, the new ones at 660; only ratios are
|
||||
used, and the extraction reads the electrode potential off the wire interior
|
||||
rather than assuming it.
|
||||
|
||||
`dotproduct.py` was not run on the new slices: it writes another ~1 GB per
|
||||
slice and only the anode surfaces are needed. `probe_fields.py` in the code
|
||||
below computes the same product straight from the two field VTUs.
|
||||
|
||||
## Do not take E.E_w from the nodal "electric field" array
|
||||
|
||||
`FluxSolver` recovers the gradient element by element and a degenerate element
|
||||
puts a spike on a node. The meshes for 12 to 15 carry ~960 nodes above
|
||||
1e6 V/m where meshes 10 and 11 have none, so the extremum over a wire's
|
||||
surface nodes lands on a spike: a re-solve of z = 19.794 returned -1.526e12
|
||||
where the archived value is -2.569e8. Take it from the potential instead, as
|
||||
`surface_field.py` does. That is stable to five figures across meshes.
|
||||
|
||||
## The code
|
||||
|
||||
`scratch/cfrac_fem_pred_2026-10-28/` in the analysis repository
|
||||
(github.com/vs19g/ANASEN-Analysis_private, branch devel_vignesh). `run_all.sh`
|
||||
reruns everything; `collect_slices.py` and `map_function.py` read the VTUs
|
||||
here. The CSVs in this directory are their output, so the numbers can be read
|
||||
without re-solving.
|
||||
28
anasen_fem/cfrac_pred_2026-10-28/band_centre.csv
Executable file
28
anasen_fem/cfrac_pred_2026-10-28/band_centre.csv
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
# Benchmark_SX3_ref/Benchmark_SX3_A1C1_cfrac_vs_sx3pczguess, alpha-source runs 12 and 18-21, 9850 entries
|
||||
# main band only: c/(a+c) > 0.25 ; slice width 4.0 mm
|
||||
z_mm,counts,median_cfrac,c_over_a
|
||||
-42.00,271,0.41183,0.70018
|
||||
-38.00,407,0.42125,0.72786
|
||||
-34.00,415,0.44240,0.79339
|
||||
-30.00,281,0.47118,0.89102
|
||||
-26.00,94,0.50500,1.02020
|
||||
-22.00,24,0.52333,1.09790
|
||||
-14.00,263,0.50393,1.01584
|
||||
-10.00,234,0.48917,0.95759
|
||||
-6.00,263,0.42490,0.73883
|
||||
-2.00,220,0.41763,0.71713
|
||||
2.00,121,0.43272,0.76279
|
||||
6.00,191,0.45483,0.83430
|
||||
10.00,627,0.45713,0.84207
|
||||
14.00,494,0.47207,0.89417
|
||||
18.00,417,0.47070,0.88930
|
||||
22.00,358,0.47882,0.91874
|
||||
26.00,322,0.44903,0.81499
|
||||
30.00,411,0.43383,0.76625
|
||||
34.00,676,0.43226,0.76138
|
||||
38.00,665,0.41607,0.71252
|
||||
42.00,549,0.40439,0.67895
|
||||
46.00,473,0.41838,0.71933
|
||||
50.00,232,0.43873,0.78166
|
||||
54.00,128,0.45327,0.82905
|
||||
58.00,100,0.45320,0.82882
|
||||
|
228
anasen_fem/cfrac_pred_2026-10-28/cfrac_pred_method.md
Executable file
228
anasen_fem/cfrac_pred_2026-10-28/cfrac_pred_method.md
Executable file
|
|
@ -0,0 +1,228 @@
|
|||
# How the predicted points on `cfrac_pred.png` are generated
|
||||
|
||||
2026-10-28. `defense/figures/cfrac_pred.png` is `cfrac.png` with open red
|
||||
markers added: the single-cathode fraction predicted from the weighting-field
|
||||
solves, over the measured A1C1 charge fraction (alpha-source runs 12 and
|
||||
18--21, 9850 events). `cfrac.png` itself is unchanged.
|
||||
|
||||
## 1. The quantity
|
||||
|
||||
Shockley--Ramo gives the current induced on an electrode by an ion drifting at
|
||||
$v = \mu E$ as $i = -q\mu\,(E\cdot E_w)$. The 3 us shaping and the 650 ns
|
||||
coincidence keep only the first part of the ion drift, so what the readout
|
||||
sees is set by $E\cdot E_w$ where the ions start, on the surface of the anode
|
||||
that avalanched. This is the quantity on slide 21.
|
||||
|
||||
$E$ comes from the physical solve, anodes biased and everything else grounded.
|
||||
$E_w$ comes from the Ramo weighting solve, one cathode at 1 V and everything
|
||||
else grounded, on the same mesh. Both are the Gmsh/Elmer solves in
|
||||
`anasen_fem`.
|
||||
|
||||
## 2. The slices
|
||||
|
||||
The wires are straight chords between the two end planes, so the lattice is a
|
||||
hyperboloid: the relative azimuth of the anode and cathode lattices is not
|
||||
linear in z, and the model's crossovers sit at z = 19.787 and 60.051 mm with
|
||||
the cell midpoint at 39.92 mm. Seven slices were used, two already archived
|
||||
and five solved for this:
|
||||
|
||||
| z (mm) | phase | relative azimuth | gap (mm) | $\lvert E\cdot E_w\rvert$ (V$^2$/m$^2$) |
|
||||
|---|---|---|---|---|
|
||||
| 19.794 | crossover | 0.003 deg | 4.339 | 2.9308e8 |
|
||||
| 19.690 | crossover, a second mesh | 0.037 deg | 4.339 | 2.9308e8 |
|
||||
| 29.850 | quarter | 3.794 deg | 4.908 | 2.5092e8 |
|
||||
| 49.980 | three quarters | 3.699 deg | 4.925 | 2.5041e8 |
|
||||
| 44.782 | three eighths | 5.625 deg | 5.535 | 2.1167e8 |
|
||||
| 39.920 | midpoint, next cell | 7.500 deg | 6.247 | 1.7489e8 |
|
||||
| 0.000 | midpoint | 7.500 deg | 6.220 | 1.7375e8 |
|
||||
|
||||
The two crossover solves are independent meshes at different z and agree to
|
||||
five figures. A solve costs about an hour, mostly meshing 10.3 M nodes.
|
||||
|
||||
## 3. Reading the field off the surface
|
||||
|
||||
**Not** from Elmer's nodal `electric field` array. `FluxSolver` recovers the
|
||||
gradient element by element, and one degenerate element puts a spike on a
|
||||
node. Taking the extremum over a wire's surface nodes reproduces slide 21
|
||||
exactly on the archived meshes, which happen to be clean, but it does not
|
||||
survive a re-mesh: the new meshes carry around 960 nodes above $10^6$ V/m
|
||||
where the archived ones have none, and a re-solve of z = 19.794 returned
|
||||
$-1.5\times10^{12}$ for a configuration whose archived value is
|
||||
$-2.57\times10^8$.
|
||||
|
||||
The potential is unaffected. In the gas annulus outside a wire it is harmonic
|
||||
and the wire surface is an equipotential at $V_0$, so
|
||||
|
||||
$$V(r,\theta) = V_0 + b\ln(r/R) + \sum_m \left(r^m - R^{2m}r^{-m}\right)
|
||||
\left(c_m\cos m\theta + d_m\sin m\theta\right)$$
|
||||
|
||||
is exact, and a least-squares fit of it over the few hundred to few thousand
|
||||
nodes within $R$ to $R+0.3$ mm gives the normal field at the surface
|
||||
analytically. Both solves hold the anode at a fixed potential, so at its
|
||||
surface $E$ and $E_w$ are both normal to it and $E\cdot E_w$ is the product of
|
||||
the two normal components; the value taken is its extremum over $\theta$,
|
||||
which falls within 0.1 deg of the direction to the driven cathode.
|
||||
|
||||
$V_0$ is read off the wire interior rather than assumed. The interior is
|
||||
meshed as gas with the wire boundary as a Dirichlet condition, so it solves to
|
||||
exactly the electrode potential, and the two campaigns differ there: the
|
||||
archived solves ran at 650 V and the current sifs read 660. Getting $V_0$
|
||||
wrong by those 10 V leaves an offset the basis cannot represent, since every
|
||||
basis function vanishes at $r=R$, and the fit absorbs it into the gradient.
|
||||
|
||||
With $V_0$ read from the interior the fit residual is 0.003 V and the answer
|
||||
is stable to five figures over annulus thicknesses of 0.10 to 0.50 mm and over
|
||||
2 to 12 azimuthal orders. It is also stable across meshes, which the nodal
|
||||
recipe is not.
|
||||
|
||||
The two recipes agree on every ratio, which is all that enters the figure:
|
||||
crossover/midpoint 1.687 against 1.711, and 4.68 against 4.67 for the
|
||||
neighbouring anodes at $\pm15$ deg. Slide 21's 1.7x stands either way.
|
||||
|
||||
## 4. What function it follows
|
||||
|
||||
Each slice has 24 anodes at 15 deg intervals around the driven cathode, so one
|
||||
solve samples the falloff at 12 separations at once, from 4.3 mm out to the
|
||||
68 mm of the far side of the barrel. Seven slices give one curve: compared at
|
||||
the azimuths they share, two meshes agree to five figures at the crossover and
|
||||
at 15 deg, the quarter and three-quarter agree to 0.2%, and the two midpoints
|
||||
to 0.05%. $E\cdot E_w$ is a function of the anode--cathode geometry alone,
|
||||
which is what lets one table of azimuth serve every cell.
|
||||
|
||||
The curve is a **Lorentzian in the azimuthal arc**. The wires are infinite, so
|
||||
this is a two-dimensional problem, and in 2D a line charge at height $h$ above
|
||||
a grounded plane induces $\sigma(x) = (q/\pi)\,h/(h^2+x^2)$. Fitted over the
|
||||
cell,
|
||||
|
||||
$$\lvert E\cdot E_w\rvert = \frac{D_0}{1 + (s/h)^2},
|
||||
\qquad s = r_a\lvert\Delta\phi\rvert, \qquad h = 5.102\ \mathrm{mm},$$
|
||||
|
||||
with an rms of 0.49% over the nine points inside the cell. The fitted width is
|
||||
16% above the geometric anode--cathode gap of 4.33 to 4.39 mm, which is what
|
||||
the discreteness of the cathodes and the curvature of the barrel cost.
|
||||
|
||||
The three-eighths slice is a check of this rather than an input to it. Fitted
|
||||
on the crossover, quarter and midpoint alone the Lorentzian gave $h = 5.100$
|
||||
mm and put that slice at $2.117\times10^8$; solving it returned
|
||||
$2.1167\times10^8$, a difference of 0.015%. Normalised at the crossover, over
|
||||
one half cell:
|
||||
|
||||
| relative azimuth | fit | solved |
|
||||
|---|---|---|
|
||||
| 0.000 deg | 1.0000 | 1.0000 |
|
||||
| 1.875 deg | 0.9591 | -- |
|
||||
| 3.750 deg | 0.8542 | 0.8553 |
|
||||
| 5.625 deg | 0.7226 | 0.7222 |
|
||||
| 7.500 deg | 0.5944 | 0.5948 |
|
||||
|
||||
The Mathieson form, the standard one for wire chambers, is the
|
||||
three-dimensional case: a point avalanche on a cathode *plane*. It is not this
|
||||
geometry. Over one cell it fits as well, because the range is too narrow to
|
||||
separate the two forms, but its parameters mean nothing here.
|
||||
|
||||
Away from the cell the Lorentzian is only local. It holds to 3% out to 11 deg,
|
||||
and then the real falloff is faster -- 79% of it at 15 deg, 60% at 19 deg,
|
||||
about a third beyond 30 deg -- because the cathodes are discrete wires that
|
||||
screen each other rather than a continuous plane.
|
||||
|
||||
## 5. Placing the points on the figure
|
||||
|
||||
The model is
|
||||
|
||||
$$c/a = \lambda\,D(z),\qquad
|
||||
D(z) = \lvert E\cdot E_w\rvert \ \text{on the fired anode's surface},$$
|
||||
|
||||
with one free scale $\lambda$. $D$ is tabulated against relative azimuth and
|
||||
mapped back onto the settled crossover grid ($\pm19.7248$, $\pm59.8527$ mm),
|
||||
which is what the red dashed lines in the figure mark; the azimuth runs
|
||||
linearly from 0 at a crossover to 7.5 deg at a cell midpoint. The figure's
|
||||
observable is $c/(a+c)$.
|
||||
|
||||
The markers are the solved phases. The curve drawn through them is the fitted
|
||||
Lorentzian of section 4 rather than interpolation between those phases; it
|
||||
passes through them to better than 0.5% in $c/a$, under a pixel at this scale.
|
||||
|
||||
$\lambda$ is fitted at **one point**, the z = +19.7248 mm crossover, which is
|
||||
the best populated of them with 600 main-band events within $\pm3$ mm. The
|
||||
overlay therefore tests the shape and the depth of the modulation, not its
|
||||
level.
|
||||
|
||||
The band centre of the data is the median of $c/(a+c)$ over the main band,
|
||||
$c/(a+c) > 0.25$, in 4 mm slices of z. The low band near 0.1 is a separate
|
||||
population and is excluded.
|
||||
|
||||
## 6. What the comparison says
|
||||
|
||||
The predicted crossover/midpoint ratio in $c/a$ is **1.68**. Measured from the
|
||||
band centre of the figure it is **1.22** against the z = 0 midpoint and
|
||||
**1.32** against +39.79, and 1.19 to 1.34 over 24 variants of the band cut,
|
||||
the window width and the statistic. The predicted modulation is about half
|
||||
again as deep as the measured one.
|
||||
|
||||
Two things account for most of that, and both are about the figure's x axis
|
||||
and its observable rather than about the fields.
|
||||
|
||||
**The x axis is not a measured z.** It is the SX3 track angle projected onto
|
||||
the 37 mm anode radius, so every event is entered at a smeared z, and smearing
|
||||
damps a modulation whose period is the cell. Fitting the first harmonic of
|
||||
both curves gives an amplitude of 0.0342 in the data against 0.0525 predicted,
|
||||
a ratio of 0.65; a Gaussian of $\sigma = 5.9$ mm would account for all of it.
|
||||
|
||||
**$c/(a+c)$ is not what the model predicts.** The model predicts $c/a$, and
|
||||
converting assumes the anode signal does not vary along the cell. It does. On
|
||||
the A1C2 benchmark sample, where the position comes from the A1C2
|
||||
reconstruction rather than the projection, the raw anode median runs from
|
||||
8798 at the cell midpoint to 7350 at the crossover, a spread of 18%. Against
|
||||
that sharper position the three observables compare as follows, each scaled by
|
||||
a single factor at the crossover:
|
||||
|
||||
| observable | rms against the model | measured midpoint-to-crossover |
|
||||
|---|---|---|
|
||||
| raw cathode | 15.1% | 1.13 |
|
||||
| $c/a$ | 6.7% | 1.37 |
|
||||
| $c/a$ from $c/(a+c)$ | 6.9% | 1.38 |
|
||||
| model | -- | 1.63 over the same range |
|
||||
|
||||
$c/a$ is the better observable and the one the model actually predicts: the
|
||||
avalanche gain cancels in the ratio, where it does not in the raw cathode. The
|
||||
raw cathode varies by only 1.13 against a predicted 1.63, and most of what
|
||||
modulation $c/a$ does show at the raw level is carried by the anode falling
|
||||
towards the crossover rather than by the cathode rising.
|
||||
|
||||
With the sharper position the measured ratio rises from 1.22--1.32 to 1.37,
|
||||
against 1.63 for the model folded the same way. The remainder is not accounted
|
||||
for here. Candidates that have not been separated: the residual 2.57 mm
|
||||
resolution of the A1C2 position, misassignment of events to the wrong cell
|
||||
(the output carries a `cellsel_misclass` histogram for exactly this), the
|
||||
cathode threshold, and the fact that the benchmark sample requires two
|
||||
cathodes above threshold and so is not the same selection as a true A1C1
|
||||
event.
|
||||
|
||||
## 7. The same thing in $c/a$
|
||||
|
||||
`cfrac_pred_ca.png` is the same data and the same model with $c/a$ on the y
|
||||
axis instead of $c/(a+c)$. It is the more direct comparison: $c/a$ is what the
|
||||
weighting field predicts, so the curve there is the fitted Lorentzian itself,
|
||||
undistorted, and nothing has to be assumed about the anode signal. The model
|
||||
runs from 0.9063 at a crossover to 0.5387 at a midpoint.
|
||||
|
||||
That figure is redrawn rather than composited, because $y o y/(1-y)$ is not
|
||||
linear and the markers could not simply be moved. The margins and font sizes
|
||||
are the ones measured off `cfrac.png`, which are also those of the macro that
|
||||
made it, so the two match. The source bins are 0.005 wide in $c/(a+c)$ and
|
||||
$0.005/(1-y)^2$ wide in $c/a$, so each is spread over the target bins its
|
||||
edges span in proportion to the overlap; filling at the bin centre instead
|
||||
leaves gaps that show as horizontal stripes.
|
||||
|
||||
## 8. Where everything lives
|
||||
|
||||
The solves are archived beside the earlier ones in
|
||||
`anasen_fem/wires2d/vtu_files` as indices 12 to 15, with the derived tables and
|
||||
a README in `anasen_fem/cfrac_pred_2026-10-28`.
|
||||
|
||||
The code is `scratch/cfrac_fem_pred_2026-10-28/` in the analysis repository.
|
||||
`run_all.sh` reruns the whole chain. The figure is composited onto a copy of
|
||||
`cfrac.png` rather than onto a regenerated plot, because no script for that
|
||||
figure exists in either repository and redrawing it would have changed the
|
||||
picture under the markers; the axis transform is read back out of the image
|
||||
itself, the four red dashed verticals fixing the x scale to 0.02 px.
|
||||
5
anasen_fem/cfrac_pred_2026-10-28/check_extrapolation.log
Executable file
5
anasen_fem/cfrac_pred_2026-10-28/check_extrapolation.log
Executable file
|
|
@ -0,0 +1,5 @@
|
|||
configuration new z repeats z new used diff
|
||||
midpoint 39.920 0.000 1.7489e+08 1.7375e+08 +0.66%
|
||||
quarter / three-quarter 49.980 29.850 2.5041e+08 2.5092e+08 -0.20%
|
||||
|
||||
largest disagreement 0.66%; the figure's modulation is 69%, so this does not bear on it
|
||||
8
anasen_fem/cfrac_pred_2026-10-28/collect_slices.log
Executable file
8
anasen_fem/cfrac_pred_2026-10-28/collect_slices.log
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
crossover (archived) z= 19.7940 |d_az|= 0.003 anodes [23] -2.9308e+08 -2.9308e+08
|
||||
crossover, 2nd mesh (archived) z= 19.6900 |d_az|= 0.037 anodes [23] -2.9308e+08 -2.9308e+08
|
||||
midpoint (archived) z= 0.0000 |d_az|= 7.500 anodes [23, 22] -1.7375e+08 -1.7375e+08 -1.7375e+08
|
||||
quarter (new) z= 29.8500 |d_az|= 3.794 anodes [23] -2.5092e+08 -2.5092e+08
|
||||
midpoint, next cell (new) z= 39.9200 |d_az|= 7.500 anodes [0, 23] -1.7489e+08 -1.7612e+08 -1.7366e+08
|
||||
three quarters (new) z= 49.9800 |d_az|= 3.699 anodes [0] -2.5041e+08 -2.5041e+08
|
||||
three eighths (new) z= 44.7820 |d_az|= 5.625 anodes [0] -2.1167e+08 -2.1167e+08
|
||||
wrote slices_fit.csv
|
||||
110
anasen_fem/cfrac_pred_2026-10-28/fit_function.log
Executable file
110
anasen_fem/cfrac_pred_2026-10-28/fit_function.log
Executable file
|
|
@ -0,0 +1,110 @@
|
|||
168 rows, 7 slices
|
||||
|
||||
collapse: slices compared at the azimuths they share
|
||||
|d_az| crossover crossover_2nd midpoint midpoint_next quarter three_eighths three_quarter
|
||||
0.0 2.9308e+08 2.9308e+08 - - - - -
|
||||
4.0 - - - - 2.5092e+08 - 2.5041e+08
|
||||
6.0 - - - - - 2.1167e+08 -
|
||||
7.0 - - - 1.7612e+08 - - -
|
||||
8.0 - - 1.7375e+08 1.7366e+08 - - -
|
||||
9.0 - - - - - 1.4115e+08 -
|
||||
11.0 - - - - 1.1293e+08 - 1.1060e+08
|
||||
|
||||
fitted over the cell (|d_az| <= 7.6 deg, 9 points):
|
||||
Lorentzian 1/(1+(arc/h)^2) : h = 5.102 mm, rms 0.0049 (0.49%)
|
||||
Mathieson (h fixed at the gap) : K3 = 1.930, rms 0.0056 (0.56%)
|
||||
the geometric anode-cathode gap is 4.330 to 4.389 mm
|
||||
|
||||
extrapolation: anodes the cell fit never saw
|
||||
|d_az| arc[mm] measured Lorentzian ratio
|
||||
8.0 4.219 1.7372e+08 1.7422e+08 0.997
|
||||
9.0 5.300 1.4115e+08 1.4108e+08 1.000
|
||||
11.0 6.352 1.1176e+08 1.1504e+08 0.972
|
||||
15.0 8.407 6.2616e+07 7.8964e+07 0.793
|
||||
19.0 10.581 3.3326e+07 5.5340e+07 0.602
|
||||
21.0 11.661 2.5520e+07 4.7134e+07 0.541
|
||||
22.0 12.607 2.0513e+07 4.1281e+07 0.497
|
||||
23.0 12.729 2.0475e+07 4.0602e+07 0.504
|
||||
24.0 13.781 1.7098e+07 3.5361e+07 0.484
|
||||
26.0 14.818 1.4266e+07 3.1090e+07 0.459
|
||||
30.0 16.814 9.3546e+06 2.4734e+07 0.378
|
||||
34.0 19.047 6.5436e+06 1.9639e+07 0.333
|
||||
36.0 20.142 5.8540e+06 1.7688e+07 0.331
|
||||
37.0 21.118 5.3724e+06 1.6178e+07 0.332
|
||||
38.0 21.045 5.3095e+06 1.6284e+07 0.326
|
||||
39.0 22.262 4.8845e+06 1.4639e+07 0.334
|
||||
41.0 23.285 4.3965e+06 1.3439e+07 0.327
|
||||
45.0 25.220 3.3175e+06 1.1533e+07 0.288
|
||||
49.0 27.514 2.7932e+06 9.7520e+06 0.286
|
||||
51.0 28.622 2.6716e+06 9.0341e+06 0.296
|
||||
52.0 29.434 2.5395e+06 8.5570e+06 0.297
|
||||
53.0 29.653 2.5524e+06 8.4345e+06 0.303
|
||||
54.0 30.742 2.4121e+06 7.8632e+06 0.307
|
||||
56.0 31.751 2.2352e+06 7.3839e+06 0.303
|
||||
60.0 33.627 1.7979e+06 6.6010e+06 0.272
|
||||
64.0 35.980 1.6430e+06 5.7823e+06 0.284
|
||||
66.0 37.103 1.6105e+06 5.4441e+06 0.296
|
||||
67.0 38.042 1.5707e+06 5.1834e+06 0.303
|
||||
68.0 37.871 1.5565e+06 5.2292e+06 0.298
|
||||
69.0 39.223 1.5011e+06 4.8810e+06 0.308
|
||||
71.0 40.217 1.4101e+06 4.6463e+06 0.303
|
||||
75.0 42.034 1.1748e+06 4.2592e+06 0.276
|
||||
79.0 44.446 1.1222e+06 3.8152e+06 0.294
|
||||
81.0 45.583 1.1143e+06 3.6296e+06 0.307
|
||||
82.0 46.260 1.0882e+06 3.5254e+06 0.309
|
||||
83.0 46.577 1.0947e+06 3.4782e+06 0.315
|
||||
84.0 47.703 1.0574e+06 3.3177e+06 0.319
|
||||
86.0 48.684 1.0016e+06 3.1868e+06 0.314
|
||||
90.0 50.440 8.5453e+05 2.9709e+06 0.288
|
||||
94.0 52.913 8.4006e+05 2.7023e+06 0.311
|
||||
96.0 54.064 8.4082e+05 2.5894e+06 0.325
|
||||
97.0 54.965 8.3265e+05 2.5059e+06 0.332
|
||||
98.0 54.698 8.2620e+05 2.5303e+06 0.327
|
||||
99.0 56.184 8.0766e+05 2.3993e+06 0.337
|
||||
101.0 57.150 7.6964e+05 2.3195e+06 0.332
|
||||
105.0 58.847 6.6855e+05 2.1886e+06 0.305
|
||||
109.0 61.379 6.7132e+05 2.0130e+06 0.333
|
||||
111.0 62.544 6.7610e+05 1.9392e+06 0.349
|
||||
112.0 63.087 6.6789e+05 1.9062e+06 0.350
|
||||
113.0 63.501 6.7212e+05 1.8816e+06 0.357
|
||||
114.0 64.665 6.5545e+05 1.8149e+06 0.361
|
||||
116.0 65.616 6.2759e+05 1.7629e+06 0.356
|
||||
120.0 67.254 5.5350e+05 1.6786e+06 0.330
|
||||
124.0 69.845 5.6538e+05 1.5570e+06 0.363
|
||||
126.0 71.025 5.7228e+05 1.5060e+06 0.380
|
||||
127.0 71.889 5.7164e+05 1.4702e+06 0.389
|
||||
128.0 71.524 5.6744e+05 1.4851e+06 0.382
|
||||
129.0 73.145 5.5911e+05 1.4203e+06 0.394
|
||||
131.0 74.083 5.3764e+05 1.3848e+06 0.388
|
||||
135.0 75.661 4.8056e+05 1.3279e+06 0.362
|
||||
139.0 78.312 4.9815e+05 1.2399e+06 0.402
|
||||
141.0 79.506 5.0640e+05 1.2031e+06 0.421
|
||||
142.0 79.913 5.0405e+05 1.1909e+06 0.423
|
||||
143.0 80.424 5.0740e+05 1.1759e+06 0.432
|
||||
144.0 81.626 4.9829e+05 1.1416e+06 0.436
|
||||
146.0 82.549 4.8099e+05 1.1163e+06 0.431
|
||||
150.0 84.067 4.3539e+05 1.0765e+06 0.404
|
||||
154.0 86.778 4.5748e+05 1.0105e+06 0.453
|
||||
156.0 87.986 4.6690e+05 9.8307e+05 0.475
|
||||
157.0 88.813 4.6955e+05 9.6492e+05 0.487
|
||||
158.0 88.350 4.6625e+05 9.7501e+05 0.478
|
||||
159.0 90.106 4.6241e+05 9.3749e+05 0.493
|
||||
161.0 91.016 4.4806e+05 9.1892e+05 0.488
|
||||
165.0 92.474 4.1067e+05 8.9025e+05 0.461
|
||||
169.0 95.245 4.3703e+05 8.3936e+05 0.521
|
||||
171.0 96.467 4.4772e+05 8.1828e+05 0.547
|
||||
172.0 96.739 4.4858e+05 8.1369e+05 0.551
|
||||
173.0 97.348 4.5166e+05 8.0358e+05 0.562
|
||||
174.0 98.587 4.4633e+05 7.8356e+05 0.570
|
||||
176.0 99.482 4.3405e+05 7.6956e+05 0.564
|
||||
180.0 100.870 4.0281e+05 7.4858e+05 0.538
|
||||
|
||||
the half cell, normalised at the crossover:
|
||||
d_az[deg] arc[mm] fit solved
|
||||
0.000 0.000 1.0000 1.0000
|
||||
1.875 1.054 0.9591 -
|
||||
3.750 2.107 0.8542 0.8553
|
||||
5.625 3.161 0.7226 0.7222
|
||||
7.500 4.215 0.5944 0.5948
|
||||
|
||||
wrote function_map.png
|
||||
171
anasen_fem/cfrac_pred_2026-10-28/function_map.csv
Executable file
171
anasen_fem/cfrac_pred_2026-10-28/function_map.csv
Executable file
|
|
@ -0,0 +1,171 @@
|
|||
# |E.E_w| at each anode surface, potential-fit recipe, annulus 0.30 mm, 6 orders
|
||||
# arc_mm = r_anode * |d_az| ; gap_mm = r_cathode - r_anode
|
||||
label,z_mm,anode,d_az_deg,sep_mm,arc_mm,r_a_mm,r_c_mm,gap_mm,dot_surf
|
||||
crossover,19.7940,0,-14.9975,9.9284,8.4054,32.1117,36.4512,4.3394,-6.264028e+07
|
||||
crossover,19.7940,1,-29.9975,18.2323,16.8123,32.1117,36.4512,4.3394,-9.357083e+06
|
||||
crossover,19.7940,2,-44.9975,26.5410,25.2191,32.1117,36.4512,4.3394,-3.318036e+06
|
||||
crossover,19.7940,3,-59.9975,34.4855,33.6259,32.1117,36.4512,4.3394,-1.798141e+06
|
||||
crossover,19.7940,4,-74.9975,41.8790,42.0328,32.1117,36.4512,4.3394,-1.174901e+06
|
||||
crossover,19.7940,5,-89.9975,48.5772,50.4396,32.1117,36.4512,4.3394,-8.545572e+05
|
||||
crossover,19.7940,6,-104.9975,54.4578,58.8464,32.1117,36.4512,4.3394,-6.685716e+05
|
||||
crossover,19.7940,7,-119.9975,59.4161,67.2533,32.1117,36.4512,4.3394,-5.535249e+05
|
||||
crossover,19.7940,8,-134.9975,63.3651,75.6601,32.1117,36.4512,4.3394,-4.805962e+05
|
||||
crossover,19.7940,9,-149.9975,66.2358,84.0669,32.1117,36.4512,4.3394,-4.354049e+05
|
||||
crossover,19.7940,10,-164.9975,67.9785,92.4738,32.1117,36.4512,4.3394,-4.106824e+05
|
||||
crossover,19.7940,11,-179.9975,68.5629,100.8806,32.1117,36.4512,4.3394,-4.028100e+05
|
||||
crossover,19.7940,12,165.0025,67.9789,92.4766,32.1117,36.4512,4.3394,-4.106489e+05
|
||||
crossover,19.7940,13,150.0025,66.2366,84.0697,32.1117,36.4512,4.3394,-4.353595e+05
|
||||
crossover,19.7940,14,135.0025,63.3662,75.6629,32.1117,36.4512,4.3394,-4.805101e+05
|
||||
crossover,19.7940,15,120.0025,59.4176,67.2561,32.1117,36.4512,4.3394,-5.534480e+05
|
||||
crossover,19.7940,16,105.0025,54.4596,58.8492,32.1117,36.4512,4.3394,-6.685026e+05
|
||||
crossover,19.7940,17,90.0025,48.5794,50.4424,32.1117,36.4512,4.3394,-8.544877e+05
|
||||
crossover,19.7940,18,75.0025,41.8814,42.0356,32.1117,36.4512,4.3394,-1.174686e+06
|
||||
crossover,19.7940,19,60.0025,34.4881,33.6287,32.1117,36.4512,4.3394,-1.797689e+06
|
||||
crossover,19.7940,20,45.0025,26.5438,25.2219,32.1117,36.4512,4.3394,-3.316963e+06
|
||||
crossover,19.7940,21,30.0025,18.2351,16.8151,32.1117,36.4512,4.3394,-9.351989e+06
|
||||
crossover,19.7940,22,15.0025,9.9310,8.4082,32.1117,36.4512,4.3394,-6.258937e+07
|
||||
crossover,19.7940,23,0.0025,4.3394,0.0014,32.1117,36.4512,4.3394,-2.930785e+08
|
||||
crossover_2nd_mesh,19.6900,0,-15.0368,9.9491,8.4273,32.1110,36.4503,4.3393,-6.222104e+07
|
||||
crossover_2nd_mesh,19.6900,1,-30.0368,18.2539,16.8339,32.1110,36.4503,4.3393,-9.314435e+06
|
||||
crossover_2nd_mesh,19.6900,2,-45.0368,26.5618,25.2406,32.1110,36.4503,4.3393,-3.308520e+06
|
||||
crossover_2nd_mesh,19.6900,3,-60.0368,34.5049,33.6472,32.1110,36.4503,4.3393,-1.794435e+06
|
||||
crossover_2nd_mesh,19.6900,4,-75.0368,41.8966,42.0538,32.1110,36.4503,4.3393,-1.173097e+06
|
||||
crossover_2nd_mesh,19.6900,5,-90.0368,48.5927,50.4605,32.1110,36.4503,4.3393,-8.535860e+05
|
||||
crossover_2nd_mesh,19.6900,6,-105.0368,54.4708,58.8671,32.1110,36.4503,4.3393,-6.679819e+05
|
||||
crossover_2nd_mesh,19.6900,7,-120.0368,59.4265,67.2738,32.1110,36.4503,4.3393,-5.531363e+05
|
||||
crossover_2nd_mesh,19.6900,8,-135.0368,63.3726,75.6804,32.1110,36.4503,4.3393,-4.803347e+05
|
||||
crossover_2nd_mesh,19.6900,9,-150.0368,66.2404,84.0871,32.1110,36.4503,4.3393,-4.352689e+05
|
||||
crossover_2nd_mesh,19.6900,10,-165.0368,67.9800,92.4937,32.1110,36.4503,4.3393,-4.106183e+05
|
||||
crossover_2nd_mesh,19.6900,11,179.9632,68.5614,100.8591,32.1110,36.4503,4.3393,-4.028197e+05
|
||||
crossover_2nd_mesh,19.6900,12,164.9632,67.9743,92.4525,32.1110,36.4503,4.3393,-4.107492e+05
|
||||
crossover_2nd_mesh,19.6900,13,149.9632,66.2290,84.0458,32.1110,36.4503,4.3393,-4.355214e+05
|
||||
crossover_2nd_mesh,19.6900,14,134.9632,63.3558,75.6392,32.1110,36.4503,4.3393,-4.808003e+05
|
||||
crossover_2nd_mesh,19.6900,15,119.9632,59.4046,67.2325,32.1110,36.4503,4.3393,-5.538755e+05
|
||||
crossover_2nd_mesh,19.6900,16,104.9632,54.4442,58.8259,32.1110,36.4503,4.3393,-6.691537e+05
|
||||
crossover_2nd_mesh,19.6900,17,89.9632,48.5617,50.4193,32.1110,36.4503,4.3393,-8.554925e+05
|
||||
crossover_2nd_mesh,19.6900,18,74.9632,41.8619,42.0126,32.1110,36.4503,4.3393,-1.176564e+06
|
||||
crossover_2nd_mesh,19.6900,19,59.9632,34.4672,33.6060,32.1110,36.4503,4.3393,-1.801464e+06
|
||||
crossover_2nd_mesh,19.6900,20,44.9632,26.5218,25.1993,32.1110,36.4503,4.3393,-3.326643e+06
|
||||
crossover_2nd_mesh,19.6900,21,29.9632,18.2127,16.7927,32.1110,36.4503,4.3393,-9.395021e+06
|
||||
crossover_2nd_mesh,19.6900,22,14.9632,9.9099,8.3860,32.1110,36.4503,4.3393,-6.301273e+07
|
||||
crossover_2nd_mesh,19.6900,23,-0.0368,4.3394,0.0206,32.1110,36.4503,4.3393,-2.930799e+08
|
||||
midpoint,0.0000,0,-22.5000,14.0067,12.5832,32.0429,36.3731,4.3301,-2.039204e+07
|
||||
midpoint,0.0000,1,-37.5000,22.3706,20.9721,32.0429,36.3731,4.3301,-5.294365e+06
|
||||
midpoint,0.0000,2,-52.5000,30.5078,29.3609,32.0429,36.3731,4.3301,-2.528524e+06
|
||||
midpoint,0.0000,3,-67.5000,38.1800,37.7497,32.0429,36.3731,4.3301,-1.551434e+06
|
||||
midpoint,0.0000,4,-82.5000,45.2271,46.1385,32.0429,36.3731,4.3301,-1.083803e+06
|
||||
midpoint,0.0000,5,-97.5000,51.5171,54.5273,32.0429,36.3731,4.3301,-8.233796e+05
|
||||
midpoint,0.0000,6,-112.5000,56.9367,62.9162,32.0429,36.3731,4.3301,-6.653680e+05
|
||||
midpoint,0.0000,7,-127.5000,61.3903,71.3050,32.0429,36.3731,4.3301,-5.654790e+05
|
||||
midpoint,0.0000,8,-142.5000,64.8001,79.6938,32.0429,36.3731,4.3301,-5.023224e+05
|
||||
midpoint,0.0000,9,-157.5000,67.1067,88.0826,32.0429,36.3731,4.3301,-4.647409e+05
|
||||
midpoint,0.0000,10,-172.5000,68.2701,96.4715,32.0429,36.3731,4.3301,-4.470796e+05
|
||||
midpoint,0.0000,11,172.5000,68.2701,96.4715,32.0429,36.3731,4.3301,-4.470037e+05
|
||||
midpoint,0.0000,12,157.5000,67.1067,88.0826,32.0429,36.3731,4.3301,-4.645535e+05
|
||||
midpoint,0.0000,13,142.5000,64.8001,79.6938,32.0429,36.3731,4.3301,-5.021926e+05
|
||||
midpoint,0.0000,14,127.5000,61.3903,71.3050,32.0429,36.3731,4.3301,-5.655687e+05
|
||||
midpoint,0.0000,15,112.5000,56.9367,62.9162,32.0429,36.3731,4.3301,-6.655079e+05
|
||||
midpoint,0.0000,16,97.5000,51.5171,54.5273,32.0429,36.3731,4.3301,-8.236472e+05
|
||||
midpoint,0.0000,17,82.5000,45.2271,46.1385,32.0429,36.3731,4.3301,-1.083890e+06
|
||||
midpoint,0.0000,18,67.5000,38.1800,37.7497,32.0429,36.3731,4.3301,-1.551420e+06
|
||||
midpoint,0.0000,19,52.5000,30.5078,29.3609,32.0429,36.3731,4.3301,-2.528568e+06
|
||||
midpoint,0.0000,20,37.5000,22.3706,20.9721,32.0429,36.3731,4.3301,-5.294461e+06
|
||||
midpoint,0.0000,21,22.5000,14.0067,12.5832,32.0429,36.3731,4.3301,-2.039219e+07
|
||||
midpoint,0.0000,22,7.5000,6.2203,4.1944,32.0429,36.3731,4.3301,-1.737511e+08
|
||||
midpoint,0.0000,23,-7.5000,6.2203,4.1944,32.0429,36.3731,4.3301,-1.737511e+08
|
||||
quarter,29.8500,0,-11.2065,7.9883,6.2978,32.1992,36.5504,4.3512,-1.129326e+08
|
||||
quarter,29.8500,1,-26.2065,16.1519,14.7276,32.1992,36.5504,4.3512,-1.436402e+07
|
||||
quarter,29.8500,2,-41.2065,24.5331,23.1573,32.1992,36.5504,4.3512,-4.418369e+06
|
||||
quarter,29.8500,3,-56.2065,32.6120,31.5870,32.1992,36.5504,4.3512,-2.244959e+06
|
||||
quarter,29.8500,4,-71.2065,40.1799,40.0167,32.1992,36.5504,4.3512,-1.415750e+06
|
||||
quarter,29.8500,5,-86.2065,47.0849,48.4465,32.1992,36.5504,4.3512,-1.005442e+06
|
||||
quarter,29.8500,6,-101.2065,53.1993,56.8762,32.1992,36.5504,4.3512,-7.725405e+05
|
||||
quarter,29.8500,7,-116.2065,58.4138,65.3059,32.1992,36.5504,4.3512,-6.298993e+05
|
||||
quarter,29.8500,8,-131.2065,62.6366,73.7357,32.1992,36.5504,4.3512,-5.396007e+05
|
||||
quarter,29.8500,9,-146.2065,65.7939,82.1654,32.1992,36.5504,4.3512,-4.827200e+05
|
||||
quarter,29.8500,10,-161.2065,67.8308,90.5951,32.1992,36.5504,4.3512,-4.496201e+05
|
||||
quarter,29.8500,11,-176.2065,68.7121,99.0248,32.1992,36.5504,4.3512,-4.355789e+05
|
||||
quarter,29.8500,12,168.7935,68.4224,94.8589,32.1992,36.5504,4.3512,-4.384851e+05
|
||||
quarter,29.8500,13,153.7935,66.9669,86.4292,32.1992,36.5504,4.3512,-4.589077e+05
|
||||
quarter,29.8500,14,138.7935,64.3706,77.9994,32.1992,36.5504,4.3512,-4.996246e+05
|
||||
quarter,29.8500,15,123.7935,60.6787,69.5697,32.1992,36.5504,4.3512,-5.669569e+05
|
||||
quarter,29.8500,16,108.7935,55.9555,61.1400,32.1992,36.5504,4.3512,-6.730520e+05
|
||||
quarter,29.8500,17,93.7935,50.2837,52.7103,32.1992,36.5504,4.3512,-8.419619e+05
|
||||
quarter,29.8500,18,78.7935,43.7638,44.2805,32.1992,36.5504,4.3512,-1.124446e+06
|
||||
quarter,29.8500,19,63.7935,36.5140,35.8508,32.1992,36.5504,4.3512,-1.645427e+06
|
||||
quarter,29.8500,20,48.7935,28.6724,27.4211,32.1992,36.5504,4.3512,-2.794903e+06
|
||||
quarter,29.8500,21,33.7935,20.4111,18.9914,32.1992,36.5504,4.3512,-6.532951e+06
|
||||
quarter,29.8500,22,18.7935,12.0177,10.5616,32.1992,36.5504,4.3512,-3.317603e+07
|
||||
quarter,29.8500,23,3.7935,4.9082,2.1319,32.1992,36.5504,4.3512,-2.509238e+08
|
||||
midpoint_next_cell,39.9200,0,-7.4349,6.2465,4.1942,32.3219,36.6897,4.3678,-1.761237e+08
|
||||
midpoint_next_cell,39.9200,1,-22.4349,14.0921,12.6560,32.3219,36.6897,4.3678,-2.075531e+07
|
||||
midpoint_next_cell,39.9200,2,-37.4349,22.5289,21.1179,32.3219,36.6897,4.3678,-5.372362e+06
|
||||
midpoint_next_cell,39.9200,3,-52.4349,30.7386,29.5797,32.3219,36.6897,4.3678,-2.561314e+06
|
||||
midpoint_next_cell,39.9200,4,-67.4349,38.4800,38.0416,32.3219,36.6897,4.3678,-1.570653e+06
|
||||
midpoint_next_cell,39.9200,5,-82.4349,45.5915,46.5034,32.3219,36.6897,4.3678,-1.096806e+06
|
||||
midpoint_next_cell,39.9200,6,-97.4349,51.9398,54.9652,32.3219,36.6897,4.3678,-8.326541e+05
|
||||
midpoint_next_cell,39.9200,7,-112.4349,57.4106,63.4271,32.3219,36.6897,4.3678,-6.727963e+05
|
||||
midpoint_next_cell,39.9200,8,-127.4349,61.9074,71.8889,32.3219,36.6897,4.3678,-5.716440e+05
|
||||
midpoint_next_cell,39.9200,9,-142.4349,65.3516,80.3508,32.3219,36.6897,4.3678,-5.076333e+05
|
||||
midpoint_next_cell,39.9200,10,-157.4349,67.6832,88.8126,32.3219,36.6897,4.3678,-4.695465e+05
|
||||
midpoint_next_cell,39.9200,11,-172.4349,68.8618,97.2745,32.3219,36.6897,4.3678,-4.516568e+05
|
||||
midpoint_next_cell,39.9200,12,172.5651,68.8669,97.3479,32.3219,36.6897,4.3678,-4.516637e+05
|
||||
midpoint_next_cell,39.9200,13,157.5651,67.6985,88.8861,32.3219,36.6897,4.3678,-4.694576e+05
|
||||
midpoint_next_cell,39.9200,14,142.5651,65.3767,80.4243,32.3219,36.6897,4.3678,-5.074022e+05
|
||||
midpoint_next_cell,39.9200,15,127.5651,61.9420,71.9624,32.3219,36.6897,4.3678,-5.712809e+05
|
||||
midpoint_next_cell,39.9200,16,112.5651,57.4540,63.5006,32.3219,36.6897,4.3678,-6.721183e+05
|
||||
midpoint_next_cell,39.9200,17,97.5651,51.9912,55.0387,32.3219,36.6897,4.3678,-8.315842e+05
|
||||
midpoint_next_cell,39.9200,18,82.5651,45.6501,46.5769,32.3219,36.6897,4.3678,-1.094733e+06
|
||||
midpoint_next_cell,39.9200,19,67.5651,38.5447,38.1150,32.3219,36.6897,4.3678,-1.566789e+06
|
||||
midpoint_next_cell,39.9200,20,52.5651,30.8081,29.6532,32.3219,36.6897,4.3678,-2.552420e+06
|
||||
midpoint_next_cell,39.9200,21,37.5651,22.6017,21.1914,32.3219,36.6897,4.3678,-5.339551e+06
|
||||
midpoint_next_cell,39.9200,22,22.5651,14.1651,12.7295,32.3219,36.6897,4.3678,-2.047473e+07
|
||||
midpoint_next_cell,39.9200,23,7.5651,6.3025,4.2677,32.3219,36.6897,4.3678,-1.736599e+08
|
||||
three_quarter,49.9800,0,-3.6995,4.9249,2.0971,32.4791,36.8682,4.3891,-2.504102e+08
|
||||
three_quarter,49.9800,1,-18.6995,12.0699,10.6001,32.4791,36.8682,4.3891,-3.347552e+07
|
||||
three_quarter,49.9800,2,-33.6995,20.5354,19.1031,32.4791,36.8682,4.3891,-6.554253e+06
|
||||
three_quarter,49.9800,3,-48.6995,28.8705,27.6061,32.4791,36.8682,4.3891,-2.791526e+06
|
||||
three_quarter,49.9800,4,-63.6995,36.7835,36.1091,32.4791,36.8682,4.3891,-1.640473e+06
|
||||
three_quarter,49.9800,5,-78.6995,44.1006,44.6121,32.4791,36.8682,4.3891,-1.120026e+06
|
||||
three_quarter,49.9800,6,-93.6995,50.6821,53.1152,32.4791,36.8682,4.3891,-8.381522e+05
|
||||
three_quarter,49.9800,7,-108.6995,56.4089,61.6182,32.4791,36.8682,4.3891,-6.695898e+05
|
||||
three_quarter,49.9800,8,-123.6995,61.1794,70.1212,32.4791,36.8682,4.3891,-5.637983e+05
|
||||
three_quarter,49.9800,9,-138.6995,64.9102,78.6242,32.4791,36.8682,4.3891,-4.966657e+05
|
||||
three_quarter,49.9800,10,-153.6995,67.5361,87.1272,32.4791,36.8682,4.3891,-4.560533e+05
|
||||
three_quarter,49.9800,11,-168.6995,69.0117,95.6302,32.4791,36.8682,4.3891,-4.355764e+05
|
||||
three_quarter,49.9800,12,176.3005,69.3112,99.9389,32.4791,36.8682,4.3891,-4.325198e+05
|
||||
three_quarter,49.9800,13,161.3005,68.4297,91.4359,32.4791,36.8682,4.3891,-4.464923e+05
|
||||
three_quarter,49.9800,14,146.3005,66.3822,82.9329,32.4791,36.8682,4.3891,-4.792532e+05
|
||||
three_quarter,49.9800,15,131.3005,63.2044,74.4299,32.4791,36.8682,4.3891,-5.356818e+05
|
||||
three_quarter,49.9800,16,116.3005,58.9515,65.9269,32.4791,36.8682,4.3891,-6.252740e+05
|
||||
three_quarter,49.9800,17,101.3005,53.6977,57.4239,32.4791,36.8682,4.3891,-7.667390e+05
|
||||
three_quarter,49.9800,18,86.3005,47.5355,48.9209,32.4791,36.8682,4.3891,-9.977283e+05
|
||||
three_quarter,49.9800,19,71.3005,40.5751,40.4179,32.4791,36.8682,4.3891,-1.404378e+06
|
||||
three_quarter,49.9800,20,56.3005,32.9451,31.9149,32.4791,36.8682,4.3891,-2.225433e+06
|
||||
three_quarter,49.9800,21,41.3005,24.7986,23.4119,32.4791,36.8682,4.3891,-4.374588e+06
|
||||
three_quarter,49.9800,22,26.3005,16.3455,14.9089,32.4791,36.8682,4.3891,-1.416708e+07
|
||||
three_quarter,49.9800,23,11.3005,8.1052,6.4059,32.4791,36.8682,4.3891,-1.105954e+08
|
||||
three_eighths,44.7820,0,-5.6250,5.5348,3.1803,32.3935,36.7711,4.3775,-2.116655e+08
|
||||
three_eighths,44.7820,1,-20.6250,13.1093,11.6609,32.3935,36.7711,4.3775,-2.552036e+07
|
||||
three_eighths,44.7820,2,-35.6250,21.5643,20.1415,32.3935,36.7711,4.3775,-5.854026e+06
|
||||
three_eighths,44.7820,3,-50.6250,29.8353,28.6221,32.3935,36.7711,4.3775,-2.671645e+06
|
||||
three_eighths,44.7820,4,-65.6250,37.6599,37.1027,32.3935,36.7711,4.3775,-1.610535e+06
|
||||
three_eighths,44.7820,5,-80.6250,44.8708,45.5833,32.3935,36.7711,4.3775,-1.114330e+06
|
||||
three_eighths,44.7820,6,-95.6250,51.3319,54.0639,32.3935,36.7711,4.3775,-8.408221e+05
|
||||
three_eighths,44.7820,7,-110.6250,56.9264,62.5445,32.3935,36.7711,4.3775,-6.761017e+05
|
||||
three_eighths,44.7820,8,-125.6250,61.5555,71.0251,32.3935,36.7711,4.3775,-5.722770e+05
|
||||
three_eighths,44.7820,9,-140.6250,65.1382,79.5058,32.3935,36.7711,4.3775,-5.064035e+05
|
||||
three_eighths,44.7820,10,-155.6250,67.6121,87.9864,32.3935,36.7711,4.3775,-4.669031e+05
|
||||
three_eighths,44.7820,11,-170.6250,68.9342,96.4670,32.3935,36.7711,4.3775,-4.477174e+05
|
||||
three_eighths,44.7820,12,174.3750,69.0816,98.5871,32.3935,36.7711,4.3775,-4.463304e+05
|
||||
three_eighths,44.7820,13,159.3750,68.0518,90.1065,32.3935,36.7711,4.3775,-4.624125e+05
|
||||
three_eighths,44.7820,14,144.3750,65.8626,81.6259,32.3935,36.7711,4.3775,-4.982920e+05
|
||||
three_eighths,44.7820,15,129.3750,62.5521,73.1453,32.3935,36.7711,4.3775,-5.591098e+05
|
||||
three_eighths,44.7820,16,114.3750,58.1776,64.6646,32.3935,36.7711,4.3775,-6.554538e+05
|
||||
three_eighths,44.7820,17,99.3750,52.8159,56.1840,32.3935,36.7711,4.3775,-8.076560e+05
|
||||
three_eighths,44.7820,18,84.3750,46.5612,47.7034,32.3935,36.7711,4.3775,-1.057369e+06
|
||||
three_eighths,44.7820,19,69.3750,39.5258,39.2228,32.3935,36.7711,4.3775,-1.501138e+06
|
||||
three_eighths,44.7820,20,54.3750,31.8405,30.7422,32.3935,36.7711,4.3775,-2.412077e+06
|
||||
three_eighths,44.7820,21,39.3750,23.6626,22.2616,32.3935,36.7711,4.3775,-4.884531e+06
|
||||
three_eighths,44.7820,22,24.3750,15.2155,13.7810,32.3935,36.7711,4.3775,-1.709812e+07
|
||||
three_eighths,44.7820,23,9.3750,7.1402,5.3004,32.3935,36.7711,4.3775,-1.411534e+08
|
||||
|
31
anasen_fem/cfrac_pred_2026-10-28/make_cfrac_pred.log
Executable file
31
anasen_fem/cfrac_pred_2026-10-28/make_cfrac_pred.log
Executable file
|
|
@ -0,0 +1,31 @@
|
|||
solved slices: azimuth[deg] -> |E.E_w| [V^2/m^2]
|
||||
0.020 2.9308e+08
|
||||
3.700 2.5041e+08
|
||||
3.793 2.5092e+08
|
||||
5.625 2.1167e+08
|
||||
7.500 1.7432e+08
|
||||
|
||||
crossover / midpoint, predicted c/a ratio : 1.681
|
||||
scale fitted at z = +19.7248 mm : s = 3.0923e-09 (c/a = 0.9063 there)
|
||||
|
||||
predicted points
|
||||
z[mm] |d_az| c/a c/(a+c)
|
||||
-39.79 7.50 0.539 0.3503
|
||||
-29.76 3.75 0.775 0.4367
|
||||
-19.72 0.00 0.906 0.4754
|
||||
-9.86 3.75 0.775 0.4367
|
||||
0.00 7.50 0.539 0.3503
|
||||
9.86 3.75 0.775 0.4367
|
||||
19.72 0.00 0.906 0.4754
|
||||
29.76 3.75 0.775 0.4367
|
||||
39.79 7.50 0.539 0.3503
|
||||
49.82 3.75 0.775 0.4367
|
||||
59.85 0.00 0.906 0.4754
|
||||
image : /home/vsitaraman/Dissertation_ch5/defense/figures/cfrac.png 1800x1238
|
||||
red verticals : [ 390. 719.5 1043.5 1373. ]
|
||||
x fit : px = 8.21194 * z + 881.500 (residuals [ 0.01 -0.02 0.02 -0.01] px)
|
||||
frame rows : top 36 (y=0.80), bottom 1070 (y=0.00)
|
||||
x range of frame : -107.34 .. 111.85 mm
|
||||
y-axis title: set to 'cathode charge fraction', 55x589 px at x=40, centred on y=553
|
||||
x-axis title: set to 'PC z [mm]', 273x55 px, centred on x=936.5 at y=1153
|
||||
wrote /home/vsitaraman/Dissertation_ch5/defense/figures/cfrac_pred.png (1800x1238)
|
||||
8
anasen_fem/cfrac_pred_2026-10-28/map_function.log
Executable file
8
anasen_fem/cfrac_pred_2026-10-28/map_function.log
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
crossover z= 19.7940 nearest anode 0.003 deg, |E.E_w| 2.9308e+08
|
||||
crossover_2nd_mesh z= 19.6900 nearest anode 0.037 deg, |E.E_w| 2.9308e+08
|
||||
midpoint z= 0.0000 nearest anode 7.500 deg, |E.E_w| 1.7375e+08
|
||||
quarter z= 29.8500 nearest anode 3.794 deg, |E.E_w| 2.5092e+08
|
||||
midpoint_next_cell z= 39.9200 nearest anode 7.435 deg, |E.E_w| 1.7612e+08
|
||||
three_quarter z= 49.9800 nearest anode 3.699 deg, |E.E_w| 2.5041e+08
|
||||
three_eighths z= 44.7820 nearest anode 5.625 deg, |E.E_w| 2.1167e+08
|
||||
wrote function_map.csv, 168 rows
|
||||
9
anasen_fem/cfrac_pred_2026-10-28/slices_fit.csv
Executable file
9
anasen_fem/cfrac_pred_2026-10-28/slices_fit.csv
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
# potential-fit recipe, annulus 0.30 mm, 6 azimuthal orders
|
||||
z_mm,d_az_deg,dot_surf,label
|
||||
19.7940,0.0025,-2.930785e+08,crossover (archived)
|
||||
19.6900,0.0368,-2.930799e+08,crossover, 2nd mesh (archived)
|
||||
0.0000,7.5000,-1.737511e+08,midpoint (archived)
|
||||
29.8500,3.7935,-2.509238e+08,quarter (new)
|
||||
39.9200,7.5000,-1.748918e+08,midpoint, next cell (new)
|
||||
49.9800,3.6995,-2.504102e+08,three quarters (new)
|
||||
44.7820,5.6250,-2.116655e+08,three eighths (new)
|
||||
|
77
anasen_fem/cfrac_pred_2026-10-28/solve_z44.7820/README.md
Normal file
77
anasen_fem/cfrac_pred_2026-10-28/solve_z44.7820/README.md
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
# Raw Elmer solution kept for z = 44.7820 mm
|
||||
|
||||
Moved here on 2026-10-28 from the working directory `~/anasen_fem_cfrac_2026-10-28_B`,
|
||||
which was then deleted. This is the one solve whose **raw solution** was kept, so that a
|
||||
quantity the cfrac study did not export can still be recovered without re-solving.
|
||||
|
||||
Re-meshing and re-solving this configuration costs about an hour, almost all of it meshing
|
||||
10.8 M nodes. Re-running a postprocessor on the solution kept here costs minutes.
|
||||
|
||||
## What this slice is
|
||||
|
||||
z = 44.7820 mm, the **three-eighths point** of the cell. It was not an input to the
|
||||
Lorentzian fit: the fit was made on the crossover, quarter and midpoint alone, predicted
|
||||
2.117e8 for this slice, and solving it returned 2.1167e8, a difference of 0.015%. It is the
|
||||
blind test of the functional form in `../cfrac_pred_method.md`, section 4.
|
||||
|
||||
Relative azimuth 5.625 deg, anode-cathode gap 5.535 mm, |E.E_w| = 2.1167e8 V^2/m^2 at the
|
||||
fired anode. `anode_dot.csv` holds all 24 anodes for this slice.
|
||||
|
||||
## Files
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| `wires2d/elstatics.result` | the physical solve, anodes biased, everything else grounded |
|
||||
| `wires2d/elstatics_weight.result` | the Ramo weighting solve, one cathode at 1 V |
|
||||
| `wires2d/mesh.*`, `wires2d/entities.sif` | the ElmerGrid mesh these were solved on |
|
||||
| `wires2d.sif`, `wires2d_weight.sif` | the two solver input files |
|
||||
| `wires_gmsh2d_bc.py` | regenerates `wires2d.msh` for this slice |
|
||||
| `geometry.py`, `probe_fields.py` | the wire placement and the node-recipe probe |
|
||||
| `elmer*.log`, `elmergrid.log` | what the run did |
|
||||
| `anode_dot.csv` | the probe output, 24 anodes |
|
||||
|
||||
A `.result` is only nodal values; it is unreadable without the mesh it was solved on, which
|
||||
is why `mesh.*` is here and must stay with it.
|
||||
|
||||
**Not kept.** The two `elstatics*.ep` (3.4 GB) are the same solutions in the legacy
|
||||
ElmerPost format, and `wires2d.msh` (805 MB) is the Gmsh source that `mesh.*` was built
|
||||
from and that `wires_gmsh2d_bc.py` regenerates.
|
||||
|
||||
**The derived VTUs are elsewhere and are what the analysis actually reads:**
|
||||
`../../wires2d/vtu_files/elfield_{anasen,weight}_14_44.7820.vtu`.
|
||||
|
||||
## Reusing it
|
||||
|
||||
To run another postprocessor without re-solving, point a sif at this mesh and restart from
|
||||
the stored solution, roughly:
|
||||
|
||||
```
|
||||
Header
|
||||
Mesh DB "." "wires2d"
|
||||
End
|
||||
Simulation
|
||||
Restart File = "elstatics.result"
|
||||
Restart Position = 0
|
||||
End
|
||||
```
|
||||
|
||||
then add the solver you want. **Untested** -- nothing has been restarted from these files
|
||||
yet, so treat the restart block as the documented shape rather than a verified recipe, and
|
||||
check the first field it gives you against `anode_dot.csv` or the archived VTU before
|
||||
trusting a new quantity.
|
||||
|
||||
One caveat carried over from the method doc: do **not** take the surface field from Elmer's
|
||||
nodal `electric field` array. `FluxSolver` recovers the gradient element by element and
|
||||
spikes on degenerate elements -- on these newer meshes about 960 nodes exceed 1e6 V/m where
|
||||
the archived meshes have none. The potential is unaffected, and `surface_field.py` in the
|
||||
analysis repo fits it harmonically instead.
|
||||
|
||||
## The two solves not kept
|
||||
|
||||
`~/anasen_fem_cfrac_2026-10-28` held z = 19.7940 (the crossover re-solve, the one whose
|
||||
nodal recipe returned -1.5e12 against an archived -2.57e8) and
|
||||
`~/anasen_fem_cfrac_2026-10-28_A` held z = 29.8500 (the quarter point). Both were deleted
|
||||
with their folders; their VTUs are archived as indices 11 and 12.
|
||||
|
||||
The code that reads any of this is `scratch/cfrac_fem_pred_2026-10-28/` in the analysis
|
||||
repository.
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
z_mm,anode,d_az_deg,nodes,dot_min,dot_mean,dist_mm
|
||||
44.7820,0,-5.6250,130,-6.412815e+11,-5.725599e+09,5.5348
|
||||
44.7820,23,9.3750,141,-5.458907e+09,-1.617053e+08,7.1402
|
||||
44.7820,1,-20.6250,132,-1.758049e+11,-1.426033e+09,13.1093
|
||||
44.7820,22,24.3750,143,-1.436395e+10,-1.844563e+08,15.2155
|
||||
44.7820,2,-35.6250,131,-1.158139e+08,-5.139516e+06,21.5643
|
||||
44.7820,21,39.3750,133,-1.207014e+09,-1.980558e+07,23.6626
|
||||
44.7820,3,-50.6250,132,-9.650451e+07,-4.160549e+06,29.8353
|
||||
44.7820,20,54.3750,133,-4.780039e+07,-2.048480e+06,31.8405
|
||||
44.7820,4,-65.6250,139,-1.584056e+09,-2.129574e+07,37.6599
|
||||
44.7820,19,69.3750,132,-8.705723e+10,-7.365464e+08,39.5258
|
||||
44.7820,5,-80.6250,140,-3.382874e+07,-1.308908e+06,44.8708
|
||||
44.7820,18,84.3750,130,-3.284999e+08,-3.913803e+06,46.5612
|
||||
44.7820,6,-95.6250,132,-4.616585e+08,-4.135442e+06,51.3319
|
||||
44.7820,17,99.3750,141,-2.609670e+07,-8.311030e+05,52.8159
|
||||
44.7820,7,-110.6250,134,-4.229769e+10,-3.193509e+08,56.9264
|
||||
44.7820,16,114.3750,139,-8.731587e+08,-6.853938e+06,58.1776
|
||||
44.7820,8,-125.6250,129,-1.169752e+07,-4.738091e+05,61.5555
|
||||
44.7820,15,129.3750,130,-2.620801e+09,-2.464152e+07,62.5521
|
||||
44.7820,9,-140.6250,130,-2.939267e+10,-2.300810e+08,65.1382
|
||||
44.7820,14,144.3750,133,-1.080835e+07,-4.263347e+05,65.8626
|
||||
44.7820,10,-155.6250,134,-4.783587e+08,-3.926604e+06,67.6121
|
||||
44.7820,13,159.3750,132,-5.782822e+08,-5.730639e+06,68.0518
|
||||
44.7820,11,-170.6250,140,-2.061392e+07,-5.902745e+05,68.9342
|
||||
44.7820,12,174.3750,130,-1.633216e+08,-2.588149e+06,69.0816
|
||||
|
882
anasen_fem/cfrac_pred_2026-10-28/solve_z44.7820/elmer_phys.log
Normal file
882
anasen_fem/cfrac_pred_2026-10-28/solve_z44.7820/elmer_phys.log
Normal file
|
|
@ -0,0 +1,882 @@
|
|||
ELMER SOLVER (v 26.2) STARTED AT: 2026/10/28 09:45:04
|
||||
ParCommInit: Initialize #PEs: 1
|
||||
MAIN: OMP_NUM_THREADS not set. Using only 1 thread per task.
|
||||
MAIN:
|
||||
MAIN: =============================================================
|
||||
MAIN: ElmerSolver finite element software, Welcome!
|
||||
MAIN: This program is free software licensed under (L)GPL
|
||||
MAIN: Copyright April 1st 1995 -> , CSC - IT Center for Science Ltd.
|
||||
MAIN: Repository https://www.github.org/elmerCSC/elmerfem
|
||||
MAIN: Homepage https://www.elmerfem.org/, Email elmeradm@csc.fi
|
||||
MAIN: Version: 26.2-unknown (Rev: unknown, Compiled: 2026-10-16)
|
||||
MAIN: Running one task without MPI parallelization.
|
||||
MAIN: Running with just one thread per task.
|
||||
MAIN: HYPRE library linked in.
|
||||
MAIN: MUMPS library linked in.
|
||||
MAIN: Lua interpreter linked in.
|
||||
MAIN: =============================================================
|
||||
MAIN:
|
||||
MAIN:
|
||||
MAIN: -------------------------------------
|
||||
MAIN: Reading Model: wires2d.sif
|
||||
LoadInputFile: Scanning input file: wires2d.sif
|
||||
LoadInputFile: Scanning only size info
|
||||
LoadInputFile: First time visiting
|
||||
LoadInputFile: Reading base load of sif file
|
||||
LoadInputFile: Loading input file: wires2d.sif
|
||||
LoadInputFile: Reading base load of sif file
|
||||
CheckKeyword: Unlisted keyword: [calculate vectors] in section: [solver 1]
|
||||
CheckKeyword: Unlisted keyword: [calculate vectors] in section: [solver 2]
|
||||
LoadInputFile: Number of BCs: 7
|
||||
LoadInputFile: Number of Body Forces: 0
|
||||
LoadInputFile: Number of Initial Conditions: 0
|
||||
LoadInputFile: Number of Materials: 1
|
||||
LoadInputFile: Number of Equations: 1
|
||||
LoadInputFile: Number of Solvers: 4
|
||||
LoadInputFile: Number of Bodies: 1
|
||||
Loading user function library: [StatElecSolve]...[StatElecSolver_Init0]
|
||||
Loading user function library: [FluxSolver]...[FluxSolver_Init0]
|
||||
Loading user function library: [FluxSolver]...[FluxSolver_Init0]
|
||||
Loading user function library: [ResultOutputSolve]...[ResultOutputSolver_Init0]
|
||||
Loading user function library: [ResultOutputSolve]...[ResultOutputSolver_Init0]
|
||||
Loading user function library: [SaveData]...[SaveScalars_Init0]
|
||||
Loading user function library: [SaveData]...[SaveScalars_Init0]
|
||||
LoadMesh: Loading serial mesh!
|
||||
ElmerMeshReader: Performing step: 1
|
||||
ElmerMeshReader: Base mesh name: ./wires2d
|
||||
ReadHeaderFile: Reading header info from file: ./wires2d/mesh.header
|
||||
InitializeMesh: Number of nodes in mesh: 10765324
|
||||
InitializeMesh: Number of bulk elements in mesh: 5382347
|
||||
InitializeMesh: Number of boundary elements in mesh: 4640
|
||||
InitializeMesh: Initial number of max element nodes: 6
|
||||
ElmerMeshReader: Performing step: 2
|
||||
ReadNodesFile: Reading nodes from file: ./wires2d/mesh.nodes
|
||||
SetMeshDimension: Dimension of mesh is: 2
|
||||
SetMeshDimension: Max dimension of mesh is: 2
|
||||
MapCoordinates: Scaling coordinates: 1.000E-03 1.000E-03 1.000E+00
|
||||
ElmerMeshReader: Performing step: 3
|
||||
ReadElementsFile: Reading bulk elements from file: ./wires2d/mesh.elements
|
||||
ElmerMeshReader: Performing step: 4
|
||||
ReadBoundaryFile: Reading boundary elements from file: ./wires2d/mesh.boundary
|
||||
PermuteNodeNumbering: Performing node mapping
|
||||
MapBodiesAndBCs: Remapping bodies
|
||||
MapBodiesAndBCs: Minimum initial body index: 13
|
||||
MapBodiesAndBCs: Maximum initial body index: 13
|
||||
MapBodiesAndBCs: Remapping boundaries
|
||||
MapBodiesAndBCs: Minimum initial boundary index: 1
|
||||
MapBodiesAndBCs: Maximum initial boundary index: 40
|
||||
ElmerMeshReader: Performing step: 5
|
||||
ElmerMeshReader: Performing step: 6
|
||||
LoadMesh: Loading mesh done
|
||||
PrepareMesh: Preparing mesh for the future
|
||||
LoadMesh: Preparing mesh done
|
||||
LoadMesh: Elapsed REAL time: 14.8571 (s)
|
||||
MeshStabParams: Computing stabilization parameters
|
||||
MeshStabParams: Elapsed REAL time: 2.2454 (s)
|
||||
MAIN: -------------------------------------
|
||||
AddMeshCoordinates: Setting mesh coordinates and time
|
||||
AddSolvers: Setting up 4 solvers
|
||||
AddSolvers: Setting up solver 1: electrostatics
|
||||
AddEquationBasics: Using procedure: StatElecSolve StatElecSolver
|
||||
AddEquationBasics: Setting up solver: electrostatics
|
||||
Loading user function library: [StatElecSolve]...[StatElecSolver_Init]
|
||||
Loading user function library: [StatElecSolve]...[StatElecSolver_bulk]
|
||||
Loading user function library: [StatElecSolve]...[StatElecSolver_bulk]
|
||||
Loading user function library: [StatElecSolve]...[StatElecSolver]
|
||||
AddEquationBasics: Creating standard variable: potential
|
||||
OptimizeBandwidth: ---------------------------------------------------------
|
||||
OptimizeBandwidth: Computing matrix structure for: electrostatics
|
||||
OptimizeBandwidth: Initial bandwidth for electrostatics: 10752353
|
||||
OptimizeBandwidth: Optimized bandwidth for electrostatics: 20840
|
||||
OptimizeBandwidth: ---------------------------------------------------------
|
||||
AddSolvers: Setting up solver 2: electric field
|
||||
AddEquationBasics: Using procedure: FluxSolver FluxSolver
|
||||
AddEquationBasics: Setting up solver: electric field
|
||||
Loading user function library: [FluxSolver]...[FluxSolver_Init]
|
||||
WARNING:: FluxSolver_init: No field computation requested, setting > Calculate Flux = True <
|
||||
FluxSolver_init: Saving flux to: electric field Flux
|
||||
Loading user function library: [FluxSolver]...[FluxSolver_bulk]
|
||||
Loading user function library: [FluxSolver]...[FluxSolver_bulk]
|
||||
Loading user function library: [FluxSolver]...[FluxSolver]
|
||||
AddEquationBasics: Creating standard variable: electric field_temp
|
||||
OptimizeBandwidth: ---------------------------------------------------------
|
||||
OptimizeBandwidth: Computing matrix structure for: electric field
|
||||
OptimizeBandwidth: Initial bandwidth for electric field: 10752353
|
||||
OptimizeBandwidth: Optimized bandwidth for electric field: 20840
|
||||
OptimizeBandwidth: ---------------------------------------------------------
|
||||
AddSolvers: Setting up solver 3: result output
|
||||
AddEquationBasics: Using procedure: ResultOutputSolve ResultOutputSolver
|
||||
AddEquationBasics: Setting up solver: result output
|
||||
Loading user function library: [ResultOutputSolve]...[ResultOutputSolver_Init]
|
||||
Loading user function library: [ResultOutputSolve]...[ResultOutputSolver_Init]
|
||||
Loading user function library: [ResultOutputSolve]...[ResultOutputSolver_bulk]
|
||||
Loading user function library: [ResultOutputSolve]...[ResultOutputSolver_bulk]
|
||||
Loading user function library: [ResultOutputSolve]...[ResultOutputSolver]
|
||||
AddSolvers: Setting up solver 4: savescalars
|
||||
AddEquationBasics: Using procedure: SaveData SaveScalars
|
||||
AddEquationBasics: Setting up solver: savescalars
|
||||
Loading user function library: [SaveData]...[SaveScalars_Init]
|
||||
Loading user function library: [SaveData]...[SaveScalars_bulk]
|
||||
Loading user function library: [SaveData]...[SaveScalars_bulk]
|
||||
Loading user function library: [SaveData]...[SaveScalars]
|
||||
AddTimeEtc: Setting time and other global variables
|
||||
MAIN: Random seed initialized to: 314159265
|
||||
SetInitialConditions: Setting up initial conditions (if any)
|
||||
MAIN: Number of timesteps to be saved: 1
|
||||
MAIN:
|
||||
MAIN: -------------------------------------
|
||||
MAIN: Steady state iteration: 1
|
||||
MAIN: -------------------------------------
|
||||
MAIN:
|
||||
SingleSolver: Attempting to call solver: 1
|
||||
SingleSolver: Solver Equation string is: electrostatics
|
||||
StatElecSolver: ------------------------------------------------
|
||||
StatElecSolver: Solving static electric field for insulators
|
||||
StatElecSolver: Permittivity Of Vacuum = 8.854200E-12 (set explicitly in Constants)
|
||||
DefaultStart: Starting solver: electrostatics
|
||||
StatElecSolver: Performing non-vectorized bulk element assembly
|
||||
StatElecSolverBulkAssembly: Elapsed REAL time: 36.5859 (s)
|
||||
StatElecSolver: Assembly of boundary colour: 1
|
||||
StatElecSolverBCAssembly: Elapsed REAL time: 0.0105 (s)
|
||||
DefUtils::DefaultDirichletBCs: Setting Dirichlet boundary conditions
|
||||
EnforceDirichletConditions: Enforcing total of 9280 Dirichlet conditions.
|
||||
EnforceDirichletConditions: Applying Dirichlet conditions using scaled diagonal
|
||||
ScaleLinearSystem: Creating > DiagScaling < vector of size 10765324
|
||||
EnforceDirichletConditions: Dirichlet conditions enforced for dofs: 9280
|
||||
DefaultSolve: Solving linear system with default routines
|
||||
SolveSystem: Solving linear system
|
||||
SolveLinearSystem: Assuming real valued linear system
|
||||
SolveLinearSystem: Assuming serial linear system
|
||||
ScaleLinearSystem: Creating > DiagScaling < vector of size 10765324
|
||||
SolveLinearSystem: Linear System Preconditioning: ilu1
|
||||
Prec0-ilu1: Elapsed REAL time: 0.0000 (s)
|
||||
SolveLinearSystem: Serial linear System Solver: iterative
|
||||
IterSolver: Using iterative method: cg
|
||||
CRS_IncompleteLU: ILU(1) (Real), Performing Factorization:
|
||||
CRS_IncompleteLU: Allocated LU matrix of size: 166400948
|
||||
CRS_IncompleteLU: ILU(1) (Real), NOF nonzeros: 166400949
|
||||
CRS_IncompleteLU: ILU(1) (Real), filling (%) : 134
|
||||
CRS_IncompleteLU: ILU(1) (Real), Factorization time (s): 138.74
|
||||
Prec-ilu1: Elapsed REAL time: 140.1287 (s)
|
||||
1 0.5172E-01
|
||||
2 0.3192E-01
|
||||
3 0.1702E-01
|
||||
4 0.1569E-01
|
||||
5 0.1354E-01
|
||||
6 0.9474E-02
|
||||
7 0.8213E-02
|
||||
8 0.8654E-02
|
||||
9 0.6712E-02
|
||||
10 0.6050E-02
|
||||
11 0.6148E-02
|
||||
12 0.5694E-02
|
||||
13 0.5034E-02
|
||||
14 0.5168E-02
|
||||
15 0.4545E-02
|
||||
16 0.4294E-02
|
||||
17 0.4379E-02
|
||||
18 0.3858E-02
|
||||
19 0.3862E-02
|
||||
20 0.3584E-02
|
||||
21 0.3621E-02
|
||||
22 0.3427E-02
|
||||
23 0.3170E-02
|
||||
24 0.3229E-02
|
||||
25 0.3071E-02
|
||||
26 0.2964E-02
|
||||
27 0.2841E-02
|
||||
28 0.2858E-02
|
||||
29 0.2645E-02
|
||||
30 0.2672E-02
|
||||
31 0.2653E-02
|
||||
32 0.2487E-02
|
||||
33 0.2752E-02
|
||||
34 0.2822E-02
|
||||
35 0.2758E-02
|
||||
36 0.2776E-02
|
||||
37 0.2805E-02
|
||||
38 0.2816E-02
|
||||
39 0.2790E-02
|
||||
40 0.2836E-02
|
||||
41 0.2657E-02
|
||||
42 0.2651E-02
|
||||
43 0.2584E-02
|
||||
44 0.2344E-02
|
||||
45 0.2349E-02
|
||||
46 0.2205E-02
|
||||
47 0.2061E-02
|
||||
48 0.1952E-02
|
||||
49 0.1829E-02
|
||||
50 0.1658E-02
|
||||
51 0.1527E-02
|
||||
52 0.1386E-02
|
||||
53 0.1277E-02
|
||||
54 0.1204E-02
|
||||
55 0.1155E-02
|
||||
56 0.1113E-02
|
||||
57 0.1054E-02
|
||||
58 0.1042E-02
|
||||
59 0.9941E-03
|
||||
60 0.9639E-03
|
||||
61 0.9270E-03
|
||||
62 0.8842E-03
|
||||
63 0.8413E-03
|
||||
64 0.7933E-03
|
||||
65 0.7752E-03
|
||||
66 0.7361E-03
|
||||
67 0.7361E-03
|
||||
68 0.7591E-03
|
||||
69 0.7607E-03
|
||||
70 0.7468E-03
|
||||
71 0.7440E-03
|
||||
72 0.7360E-03
|
||||
73 0.7291E-03
|
||||
74 0.7464E-03
|
||||
75 0.7439E-03
|
||||
76 0.7427E-03
|
||||
77 0.7624E-03
|
||||
78 0.7009E-03
|
||||
79 0.6600E-03
|
||||
80 0.6430E-03
|
||||
81 0.6288E-03
|
||||
82 0.6356E-03
|
||||
83 0.6202E-03
|
||||
84 0.6244E-03
|
||||
85 0.5867E-03
|
||||
86 0.5818E-03
|
||||
87 0.5810E-03
|
||||
88 0.5603E-03
|
||||
89 0.5571E-03
|
||||
90 0.5426E-03
|
||||
91 0.5299E-03
|
||||
92 0.5102E-03
|
||||
93 0.4973E-03
|
||||
94 0.4622E-03
|
||||
95 0.4439E-03
|
||||
96 0.4280E-03
|
||||
97 0.4018E-03
|
||||
98 0.3901E-03
|
||||
99 0.3892E-03
|
||||
100 0.3840E-03
|
||||
101 0.3793E-03
|
||||
102 0.3835E-03
|
||||
103 0.3772E-03
|
||||
104 0.3647E-03
|
||||
105 0.3575E-03
|
||||
106 0.3587E-03
|
||||
107 0.3451E-03
|
||||
108 0.3367E-03
|
||||
109 0.3212E-03
|
||||
110 0.3115E-03
|
||||
111 0.3188E-03
|
||||
112 0.3121E-03
|
||||
113 0.3195E-03
|
||||
114 0.3252E-03
|
||||
115 0.3149E-03
|
||||
116 0.3174E-03
|
||||
117 0.3179E-03
|
||||
118 0.3160E-03
|
||||
119 0.3191E-03
|
||||
120 0.3149E-03
|
||||
121 0.3124E-03
|
||||
122 0.3108E-03
|
||||
123 0.3016E-03
|
||||
124 0.2954E-03
|
||||
125 0.2855E-03
|
||||
126 0.2828E-03
|
||||
127 0.2789E-03
|
||||
128 0.2746E-03
|
||||
129 0.2694E-03
|
||||
130 0.2649E-03
|
||||
131 0.2661E-03
|
||||
132 0.2657E-03
|
||||
133 0.2620E-03
|
||||
134 0.2647E-03
|
||||
135 0.2522E-03
|
||||
136 0.2455E-03
|
||||
137 0.2455E-03
|
||||
138 0.2382E-03
|
||||
139 0.2432E-03
|
||||
140 0.2435E-03
|
||||
141 0.2373E-03
|
||||
142 0.2273E-03
|
||||
143 0.2231E-03
|
||||
144 0.2193E-03
|
||||
145 0.2112E-03
|
||||
146 0.2098E-03
|
||||
147 0.2005E-03
|
||||
148 0.2007E-03
|
||||
149 0.2054E-03
|
||||
150 0.2037E-03
|
||||
151 0.2056E-03
|
||||
152 0.2072E-03
|
||||
153 0.2042E-03
|
||||
154 0.2045E-03
|
||||
155 0.2005E-03
|
||||
156 0.1972E-03
|
||||
157 0.1976E-03
|
||||
158 0.2005E-03
|
||||
159 0.2008E-03
|
||||
160 0.1999E-03
|
||||
161 0.2004E-03
|
||||
162 0.1953E-03
|
||||
163 0.1994E-03
|
||||
164 0.2010E-03
|
||||
165 0.2006E-03
|
||||
166 0.2048E-03
|
||||
167 0.2037E-03
|
||||
168 0.2013E-03
|
||||
169 0.2080E-03
|
||||
170 0.2157E-03
|
||||
171 0.2211E-03
|
||||
172 0.2350E-03
|
||||
173 0.2415E-03
|
||||
174 0.2441E-03
|
||||
175 0.2537E-03
|
||||
176 0.2495E-03
|
||||
177 0.2456E-03
|
||||
178 0.2418E-03
|
||||
179 0.2357E-03
|
||||
180 0.2435E-03
|
||||
181 0.2473E-03
|
||||
182 0.2469E-03
|
||||
183 0.2528E-03
|
||||
184 0.2624E-03
|
||||
185 0.2689E-03
|
||||
186 0.2826E-03
|
||||
187 0.2889E-03
|
||||
188 0.2885E-03
|
||||
189 0.2801E-03
|
||||
190 0.2710E-03
|
||||
191 0.2572E-03
|
||||
192 0.2426E-03
|
||||
193 0.2335E-03
|
||||
194 0.2160E-03
|
||||
195 0.2092E-03
|
||||
196 0.2009E-03
|
||||
197 0.1903E-03
|
||||
198 0.1828E-03
|
||||
199 0.1794E-03
|
||||
200 0.1756E-03
|
||||
201 0.1720E-03
|
||||
202 0.1695E-03
|
||||
203 0.1654E-03
|
||||
204 0.1661E-03
|
||||
205 0.1603E-03
|
||||
206 0.1628E-03
|
||||
207 0.1611E-03
|
||||
208 0.1549E-03
|
||||
209 0.1565E-03
|
||||
210 0.1486E-03
|
||||
211 0.1435E-03
|
||||
212 0.1398E-03
|
||||
213 0.1396E-03
|
||||
214 0.1366E-03
|
||||
215 0.1335E-03
|
||||
216 0.1321E-03
|
||||
217 0.1246E-03
|
||||
218 0.1198E-03
|
||||
219 0.1204E-03
|
||||
220 0.1138E-03
|
||||
221 0.1090E-03
|
||||
222 0.1034E-03
|
||||
223 0.9468E-04
|
||||
224 0.9266E-04
|
||||
225 0.8576E-04
|
||||
226 0.7893E-04
|
||||
227 0.7359E-04
|
||||
228 0.6808E-04
|
||||
229 0.6388E-04
|
||||
230 0.6011E-04
|
||||
231 0.5877E-04
|
||||
232 0.5606E-04
|
||||
233 0.5455E-04
|
||||
234 0.5174E-04
|
||||
235 0.4767E-04
|
||||
236 0.4525E-04
|
||||
237 0.4171E-04
|
||||
238 0.3901E-04
|
||||
239 0.3639E-04
|
||||
240 0.3337E-04
|
||||
241 0.3038E-04
|
||||
242 0.2799E-04
|
||||
243 0.2596E-04
|
||||
244 0.2393E-04
|
||||
245 0.2196E-04
|
||||
246 0.2113E-04
|
||||
247 0.1927E-04
|
||||
248 0.1864E-04
|
||||
249 0.1777E-04
|
||||
250 0.1691E-04
|
||||
251 0.1672E-04
|
||||
252 0.1594E-04
|
||||
253 0.1642E-04
|
||||
254 0.1632E-04
|
||||
255 0.1653E-04
|
||||
256 0.1694E-04
|
||||
257 0.1664E-04
|
||||
258 0.1700E-04
|
||||
259 0.1735E-04
|
||||
260 0.1815E-04
|
||||
261 0.1854E-04
|
||||
262 0.1923E-04
|
||||
263 0.1992E-04
|
||||
264 0.1991E-04
|
||||
265 0.2005E-04
|
||||
266 0.1997E-04
|
||||
267 0.1951E-04
|
||||
268 0.2002E-04
|
||||
269 0.1962E-04
|
||||
270 0.1926E-04
|
||||
271 0.1930E-04
|
||||
272 0.1905E-04
|
||||
273 0.1829E-04
|
||||
274 0.1788E-04
|
||||
275 0.1756E-04
|
||||
276 0.1620E-04
|
||||
277 0.1593E-04
|
||||
278 0.1522E-04
|
||||
279 0.1436E-04
|
||||
280 0.1377E-04
|
||||
281 0.1306E-04
|
||||
282 0.1223E-04
|
||||
283 0.1160E-04
|
||||
284 0.1139E-04
|
||||
285 0.1078E-04
|
||||
286 0.1045E-04
|
||||
287 0.1013E-04
|
||||
288 0.9520E-05
|
||||
289 0.9212E-05
|
||||
290 0.8741E-05
|
||||
291 0.8203E-05
|
||||
292 0.8022E-05
|
||||
293 0.7667E-05
|
||||
294 0.7470E-05
|
||||
295 0.7248E-05
|
||||
296 0.7022E-05
|
||||
297 0.6742E-05
|
||||
298 0.6720E-05
|
||||
299 0.6815E-05
|
||||
300 0.6660E-05
|
||||
301 0.6836E-05
|
||||
302 0.6908E-05
|
||||
303 0.6974E-05
|
||||
304 0.7180E-05
|
||||
305 0.7073E-05
|
||||
306 0.6915E-05
|
||||
307 0.6740E-05
|
||||
308 0.6415E-05
|
||||
309 0.6102E-05
|
||||
310 0.5770E-05
|
||||
311 0.5686E-05
|
||||
312 0.5387E-05
|
||||
313 0.5211E-05
|
||||
314 0.5043E-05
|
||||
315 0.4706E-05
|
||||
316 0.4623E-05
|
||||
317 0.4378E-05
|
||||
318 0.4283E-05
|
||||
319 0.4133E-05
|
||||
320 0.4085E-05
|
||||
321 0.4008E-05
|
||||
322 0.3902E-05
|
||||
323 0.3891E-05
|
||||
324 0.3748E-05
|
||||
325 0.3646E-05
|
||||
326 0.3551E-05
|
||||
327 0.3417E-05
|
||||
328 0.3325E-05
|
||||
329 0.3249E-05
|
||||
330 0.3169E-05
|
||||
331 0.3204E-05
|
||||
332 0.3166E-05
|
||||
333 0.3261E-05
|
||||
334 0.3211E-05
|
||||
335 0.3182E-05
|
||||
336 0.3155E-05
|
||||
337 0.3024E-05
|
||||
338 0.3023E-05
|
||||
339 0.3015E-05
|
||||
340 0.3075E-05
|
||||
341 0.3042E-05
|
||||
342 0.3035E-05
|
||||
343 0.2991E-05
|
||||
344 0.2849E-05
|
||||
345 0.2812E-05
|
||||
346 0.2779E-05
|
||||
347 0.2680E-05
|
||||
348 0.2695E-05
|
||||
349 0.2582E-05
|
||||
350 0.2558E-05
|
||||
351 0.2516E-05
|
||||
352 0.2553E-05
|
||||
353 0.2623E-05
|
||||
354 0.2665E-05
|
||||
355 0.2810E-05
|
||||
356 0.2823E-05
|
||||
357 0.2862E-05
|
||||
358 0.2853E-05
|
||||
359 0.2832E-05
|
||||
360 0.2807E-05
|
||||
361 0.2802E-05
|
||||
362 0.2856E-05
|
||||
363 0.2891E-05
|
||||
364 0.2961E-05
|
||||
365 0.3042E-05
|
||||
366 0.3028E-05
|
||||
367 0.3007E-05
|
||||
368 0.3066E-05
|
||||
369 0.3060E-05
|
||||
370 0.3090E-05
|
||||
371 0.3190E-05
|
||||
372 0.3258E-05
|
||||
373 0.3281E-05
|
||||
374 0.3376E-05
|
||||
375 0.3417E-05
|
||||
376 0.3450E-05
|
||||
377 0.3519E-05
|
||||
378 0.3671E-05
|
||||
379 0.3709E-05
|
||||
380 0.3866E-05
|
||||
381 0.4009E-05
|
||||
382 0.3943E-05
|
||||
383 0.4004E-05
|
||||
384 0.3990E-05
|
||||
385 0.3910E-05
|
||||
386 0.3802E-05
|
||||
387 0.3890E-05
|
||||
388 0.3881E-05
|
||||
389 0.3836E-05
|
||||
390 0.3940E-05
|
||||
391 0.3852E-05
|
||||
392 0.3780E-05
|
||||
393 0.3753E-05
|
||||
394 0.3604E-05
|
||||
395 0.3480E-05
|
||||
396 0.3413E-05
|
||||
397 0.3324E-05
|
||||
398 0.3141E-05
|
||||
399 0.3027E-05
|
||||
400 0.2903E-05
|
||||
401 0.2676E-05
|
||||
402 0.2616E-05
|
||||
403 0.2466E-05
|
||||
404 0.2343E-05
|
||||
405 0.2266E-05
|
||||
406 0.2139E-05
|
||||
407 0.2114E-05
|
||||
408 0.2030E-05
|
||||
409 0.1966E-05
|
||||
410 0.1930E-05
|
||||
411 0.1874E-05
|
||||
412 0.1855E-05
|
||||
413 0.1816E-05
|
||||
414 0.1788E-05
|
||||
415 0.1745E-05
|
||||
416 0.1711E-05
|
||||
417 0.1658E-05
|
||||
418 0.1582E-05
|
||||
419 0.1508E-05
|
||||
420 0.1452E-05
|
||||
421 0.1362E-05
|
||||
422 0.1315E-05
|
||||
423 0.1245E-05
|
||||
424 0.1160E-05
|
||||
425 0.1117E-05
|
||||
426 0.1079E-05
|
||||
427 0.1025E-05
|
||||
428 0.1001E-05
|
||||
429 0.9631E-06
|
||||
430 0.9010E-06
|
||||
431 0.8597E-06
|
||||
432 0.8010E-06
|
||||
433 0.7747E-06
|
||||
434 0.7154E-06
|
||||
435 0.6968E-06
|
||||
436 0.6646E-06
|
||||
437 0.6298E-06
|
||||
438 0.6218E-06
|
||||
439 0.5918E-06
|
||||
440 0.5737E-06
|
||||
441 0.5500E-06
|
||||
442 0.5251E-06
|
||||
443 0.4873E-06
|
||||
444 0.4520E-06
|
||||
445 0.4233E-06
|
||||
446 0.3871E-06
|
||||
447 0.3631E-06
|
||||
448 0.3453E-06
|
||||
449 0.3301E-06
|
||||
450 0.3120E-06
|
||||
451 0.3080E-06
|
||||
452 0.2969E-06
|
||||
453 0.2803E-06
|
||||
454 0.2861E-06
|
||||
455 0.2749E-06
|
||||
456 0.2684E-06
|
||||
457 0.2676E-06
|
||||
458 0.2568E-06
|
||||
459 0.2429E-06
|
||||
460 0.2292E-06
|
||||
461 0.2185E-06
|
||||
462 0.1988E-06
|
||||
463 0.1884E-06
|
||||
464 0.1804E-06
|
||||
465 0.1670E-06
|
||||
466 0.1619E-06
|
||||
467 0.1591E-06
|
||||
468 0.1488E-06
|
||||
469 0.1475E-06
|
||||
470 0.1429E-06
|
||||
471 0.1349E-06
|
||||
472 0.1291E-06
|
||||
473 0.1242E-06
|
||||
474 0.1158E-06
|
||||
475 0.1104E-06
|
||||
476 0.1085E-06
|
||||
477 0.9961E-07
|
||||
478 0.9807E-07
|
||||
479 0.9746E-07
|
||||
480 0.9440E-07
|
||||
481 0.9385E-07
|
||||
482 0.9377E-07
|
||||
483 0.9279E-07
|
||||
484 0.9273E-07
|
||||
485 0.9531E-07
|
||||
486 0.9486E-07
|
||||
487 0.9594E-07
|
||||
488 0.9592E-07
|
||||
489 0.9332E-07
|
||||
490 0.9167E-07
|
||||
491 0.8922E-07
|
||||
492 0.8511E-07
|
||||
493 0.8275E-07
|
||||
494 0.7953E-07
|
||||
495 0.7692E-07
|
||||
496 0.7188E-07
|
||||
497 0.6766E-07
|
||||
498 0.6320E-07
|
||||
499 0.5887E-07
|
||||
500 0.5709E-07
|
||||
501 0.5463E-07
|
||||
502 0.5333E-07
|
||||
503 0.5198E-07
|
||||
504 0.5050E-07
|
||||
505 0.4883E-07
|
||||
506 0.4720E-07
|
||||
507 0.4553E-07
|
||||
508 0.4394E-07
|
||||
509 0.4276E-07
|
||||
510 0.4206E-07
|
||||
511 0.4127E-07
|
||||
512 0.4067E-07
|
||||
513 0.4105E-07
|
||||
514 0.4071E-07
|
||||
515 0.4055E-07
|
||||
516 0.4066E-07
|
||||
517 0.4049E-07
|
||||
518 0.3995E-07
|
||||
519 0.3898E-07
|
||||
520 0.3834E-07
|
||||
521 0.3729E-07
|
||||
522 0.3670E-07
|
||||
523 0.3605E-07
|
||||
524 0.3475E-07
|
||||
525 0.3418E-07
|
||||
526 0.3314E-07
|
||||
527 0.3292E-07
|
||||
528 0.3201E-07
|
||||
529 0.3158E-07
|
||||
530 0.3051E-07
|
||||
531 0.2995E-07
|
||||
532 0.3013E-07
|
||||
533 0.2955E-07
|
||||
534 0.2995E-07
|
||||
535 0.2998E-07
|
||||
536 0.2908E-07
|
||||
537 0.2865E-07
|
||||
538 0.2778E-07
|
||||
539 0.2681E-07
|
||||
540 0.2558E-07
|
||||
541 0.2462E-07
|
||||
542 0.2356E-07
|
||||
543 0.2240E-07
|
||||
544 0.2211E-07
|
||||
545 0.2119E-07
|
||||
546 0.2052E-07
|
||||
547 0.1985E-07
|
||||
548 0.1906E-07
|
||||
549 0.1837E-07
|
||||
550 0.1782E-07
|
||||
551 0.1767E-07
|
||||
552 0.1713E-07
|
||||
553 0.1701E-07
|
||||
554 0.1664E-07
|
||||
555 0.1636E-07
|
||||
556 0.1649E-07
|
||||
557 0.1621E-07
|
||||
558 0.1605E-07
|
||||
559 0.1615E-07
|
||||
560 0.1583E-07
|
||||
561 0.1570E-07
|
||||
562 0.1555E-07
|
||||
563 0.1550E-07
|
||||
564 0.1497E-07
|
||||
565 0.1463E-07
|
||||
566 0.1436E-07
|
||||
567 0.1379E-07
|
||||
568 0.1379E-07
|
||||
569 0.1358E-07
|
||||
570 0.1338E-07
|
||||
571 0.1323E-07
|
||||
572 0.1312E-07
|
||||
573 0.1275E-07
|
||||
574 0.1290E-07
|
||||
575 0.1265E-07
|
||||
576 0.1231E-07
|
||||
577 0.1212E-07
|
||||
578 0.1192E-07
|
||||
579 0.1169E-07
|
||||
580 0.1120E-07
|
||||
581 0.1095E-07
|
||||
582 0.1039E-07
|
||||
583 0.9830E-08
|
||||
583 0.9830E-08
|
||||
ComputeNorm: Computing norm of solution
|
||||
ComputeChange: NS (ITER=1) (NRM,RELC): ( 188.93505 2.0000000 ) :: electrostatics
|
||||
DefaultFinish: Finished solver: electrostatics
|
||||
Loading user function library: [StatElecSolve]...[StatElecSolver_post]
|
||||
SingleSolver: Calling solver for postprocessing
|
||||
StatElecSolver_post: ------------------------------------------------
|
||||
StatElecSolver_post: Calculating postprocessing fields
|
||||
StatElecSolver_post: Number of 1 postprocessing fields
|
||||
StatElecSolver_post: Creating a weighting for scaling purposes from 7
|
||||
StatElecSolver_post: Total Electric Energy: 8.120E-05
|
||||
StatElecSolver_post: Effective Capacitance: 2.196E-10
|
||||
ComputeNorm: Computing norm of solution
|
||||
ComputeChange: SS (ITER=1) (NRM,RELC): ( 188.93505 2.0000000 ) :: electrostatics
|
||||
SingleSolver: Attempting to call solver: 2
|
||||
SingleSolver: Solver Equation string is: electric field
|
||||
FluxSolver: -------------------------------------
|
||||
FluxSolver: Computing the flux and/or gradient
|
||||
FluxSolver: -------------------------------------
|
||||
FluxSolver: Assembly Time: 55.003733873367310
|
||||
DefaultSolve: Solving linear system with default routines
|
||||
SolveSystem: Solving linear system
|
||||
SolveLinearSystem: Assuming real valued linear system
|
||||
SolveLinearSystem: Assuming serial linear system
|
||||
ScaleLinearSystem: Creating > DiagScaling < vector of size 10765324
|
||||
SolveLinearSystem: Linear System Preconditioning: ilu0
|
||||
Prec0-ilu0: Elapsed REAL time: 0.0001 (s)
|
||||
SolveLinearSystem: Serial linear System Solver: iterative
|
||||
IterSolver: Using iterative method: cg
|
||||
CRS_IncompleteLU: ILU(0) (Real), Performing Factorization:
|
||||
CRS_IncompleteLU: Allocated LU matrix of size: 123796498
|
||||
CRS_IncompleteLU: ILU(0) (Real), NOF nonzeros: 123796499
|
||||
CRS_IncompleteLU: ILU(0) (Real), filling (%) : 100
|
||||
CRS_IncompleteLU: ILU(0) (Real), Factorization time (s): 2.91
|
||||
Prec-ilu0: Elapsed REAL time: 5.4674 (s)
|
||||
10 0.9294E-10
|
||||
10 0.9294E-10
|
||||
DefaultSolve: Solving linear system with default routines
|
||||
SolveSystem: Solving linear system
|
||||
SolveLinearSystem: Assuming real valued linear system
|
||||
SolveLinearSystem: Assuming serial linear system
|
||||
ScaleLinearSystem: Creating > DiagScaling < vector of size 10765324
|
||||
SolveLinearSystem: Linear System Preconditioning: ilu0
|
||||
Prec0-ilu0: Elapsed REAL time: 0.0000 (s)
|
||||
SolveLinearSystem: Serial linear System Solver: iterative
|
||||
IterSolver: Using iterative method: cg
|
||||
CRS_IncompleteLU: ILU(0) (Real), Performing Factorization:
|
||||
CRS_IncompleteLU: ILU(0) (Real), NOF nonzeros: 123796499
|
||||
CRS_IncompleteLU: ILU(0) (Real), filling (%) : 100
|
||||
CRS_IncompleteLU: ILU(0) (Real), Factorization time (s): 1.85
|
||||
Prec-ilu0: Elapsed REAL time: 1.9819 (s)
|
||||
10 0.1357E-09
|
||||
11 0.1565E-10
|
||||
ComputeNorm: Computing norm of solution
|
||||
ComputeChange: NS (ITER=2) (NRM,RELC): ( 0.97469715E+10 2.0000000 ) :: electric field
|
||||
FluxSolver: Solution Time: 28.676481008529663
|
||||
FluxSolver: Result Norm: 9746971516.0560455
|
||||
FluxSolver: All done
|
||||
FluxSolver: -------------------------------------
|
||||
ComputeNorm: Computing norm of solution
|
||||
ComputeChange: SS (ITER=1) (NRM,RELC): ( 0.97469715E+10 2.0000000 ) :: electric field
|
||||
SingleSolver: Attempting to call solver: 3
|
||||
SingleSolver: Solver Equation string is: result output
|
||||
ResultOutputSolver: -------------------------------------
|
||||
ResultOutputSolver: Saving with prefix: elfield_anasen
|
||||
ResultOutputSolver: Dimension of mesh is: 2
|
||||
ResultOutputSolver: Working on mesh 1: wires2d with 10765324 nodes
|
||||
ResultOutputSolver: Creating list for saving - if not present
|
||||
CreateListForSaving: Scalar Field 1 exists, creating no list!
|
||||
ResultOutputSolver: Saving in unstructured VTK XML (.vtu) format
|
||||
VtuOutputSolver: Saving results in VTK XML format with prefix: elfield_anasen
|
||||
VtuOutputSolver: Saving number of partitions: 1
|
||||
SolverOutputDirectory: Creating directory: wires2d
|
||||
VtuOutputSolver: Full filename base is: wires2d/elfield_anasen
|
||||
GenerateSaveMask: Number of active elements 5386987 out of 5386987
|
||||
GenerateSaveMask: Number of geometry nodes 10765324 out of 10765324
|
||||
VtuOutputSolver: Total number of elements to save: 5386987
|
||||
VtuOutputSolver: Total number of geometry nodes to save: 10765324
|
||||
VtuOutputSolver: Total number of dof nodes to save: 10765324
|
||||
VtuOutputSolver: Writing the vtu file: wires2d/elfield_anasen_t0001.vtu
|
||||
AscBinWriteInit: Initializing buffered ascii/binary writing
|
||||
AscBinWriteInit: Writing in binary
|
||||
AscBinWriteInit: Writing in double precision
|
||||
AscBinWriteInit: Writing to unit number: 58
|
||||
AscBinWriteInit: Size of buffer is: 10765324
|
||||
WriteVtuFile: Writing nodal fields
|
||||
WriteVtuFile: Saving 1-component variable: potential
|
||||
WriteVtuFile: Saving 2-component variable: electric field
|
||||
WriteVtuFile: Number of nodal fields written: 2
|
||||
WriteVtuFile: Writing elemental fields
|
||||
WriteVtuFile: Number of elemental fields written: 0
|
||||
WriteVtuFile: Writing coordinates for each used node
|
||||
WriteVtuFile: Writing the elemental connectivity data
|
||||
WriteVtuFile: Writing nodal fields
|
||||
WriteVtuFile: Saving 1-component variable: potential
|
||||
WriteVtuFile: Saving 2-component variable: electric field
|
||||
WriteVtuFile: Writing elemental fields
|
||||
AscBinWriteFree: Terminating buffered ascii/binary writing
|
||||
VtuOutputSolver: All done for now
|
||||
ResultOutputSolver: -------------------------------------
|
||||
WritePostFile: Saving results in ElmerPost format to file wires2d/elstatics.ep
|
||||
SaveResult: -----------------------------------------
|
||||
SaveResult: Saving results to file: wires2d/elstatics.result
|
||||
SaveResult: Done writing results file
|
||||
SaveResult: -----------------------------------------
|
||||
SingleSolver: Attempting to call solver: 4
|
||||
SingleSolver: Solver Equation string is: savescalars
|
||||
SaveScalars: -----------------------------------------
|
||||
SaveScalars: Saving scalar values of various kinds
|
||||
SaveScalars: Performed 0 reduction operations
|
||||
SaveScalars: Found 3 result scalars in simulation section
|
||||
SaveScalars: Found 3 values to save in total
|
||||
SaveScalars: Saving names of values to file: ./scalars.dat.names
|
||||
SaveScalars: Saving values to file: ./scalars.dat
|
||||
SaveScalars: -----------------------------------------
|
||||
ReloadInputFile: Reloading input file
|
||||
LoadInputFile: Loading input file:
|
||||
MAIN: Reporting unused list entries for sif improvement!
|
||||
MAIN: If you do not want these lines undefine > DEVEL_LISTUSAGE < !
|
||||
Unused keywords:
|
||||
Boundary Condition 1 calculate electric force
|
||||
Boundary Condition 2 calculate electric force
|
||||
Boundary Condition 3 calculate electric force
|
||||
Boundary Condition 5 calculate electric force
|
||||
Solver 1 calculate vectors
|
||||
Solver 2 target variable
|
||||
Solver 2 calculate vectors
|
||||
MAIN: *** Elmer Solver: ALL DONE ***
|
||||
Loading user function library: [StatElecSolve]...[StatElecSolver_Finalize]
|
||||
Loading user function library: [StatElecSolve]...[StatElecSolver_Finalize]
|
||||
Loading user function library: [FluxSolver]...[FluxSolver_Finalize]
|
||||
Loading user function library: [FluxSolver]...[FluxSolver_Finalize]
|
||||
Loading user function library: [ResultOutputSolve]...[ResultOutputSolver_Finalize]
|
||||
Loading user function library: [ResultOutputSolve]...[ResultOutputSolver_Finalize]
|
||||
Loading user function library: [SaveData]...[SaveScalars_Finalize]
|
||||
Loading user function library: [SaveData]...[SaveScalars_Finalize]
|
||||
MAIN: The end
|
||||
SOLVER TOTAL TIME(CPU,REAL): 1293.09 1396.16
|
||||
ELMER SOLVER FINISHED AT: 2026/10/28 10:08:20
|
||||
897
anasen_fem/cfrac_pred_2026-10-28/solve_z44.7820/elmer_weight.log
Normal file
897
anasen_fem/cfrac_pred_2026-10-28/solve_z44.7820/elmer_weight.log
Normal file
|
|
@ -0,0 +1,897 @@
|
|||
ELMER SOLVER (v 26.2) STARTED AT: 2026/10/28 10:08:23
|
||||
ParCommInit: Initialize #PEs: 1
|
||||
MAIN: OMP_NUM_THREADS not set. Using only 1 thread per task.
|
||||
MAIN:
|
||||
MAIN: =============================================================
|
||||
MAIN: ElmerSolver finite element software, Welcome!
|
||||
MAIN: This program is free software licensed under (L)GPL
|
||||
MAIN: Copyright April 1st 1995 -> , CSC - IT Center for Science Ltd.
|
||||
MAIN: Repository https://www.github.org/elmerCSC/elmerfem
|
||||
MAIN: Homepage https://www.elmerfem.org/, Email elmeradm@csc.fi
|
||||
MAIN: Version: 26.2-unknown (Rev: unknown, Compiled: 2026-10-16)
|
||||
MAIN: Running one task without MPI parallelization.
|
||||
MAIN: Running with just one thread per task.
|
||||
MAIN: HYPRE library linked in.
|
||||
MAIN: MUMPS library linked in.
|
||||
MAIN: Lua interpreter linked in.
|
||||
MAIN: =============================================================
|
||||
MAIN:
|
||||
MAIN:
|
||||
MAIN: -------------------------------------
|
||||
MAIN: Reading Model: wires2d_weight.sif
|
||||
LoadInputFile: Scanning input file: wires2d_weight.sif
|
||||
LoadInputFile: Scanning only size info
|
||||
LoadInputFile: First time visiting
|
||||
LoadInputFile: Reading base load of sif file
|
||||
LoadInputFile: Loading input file: wires2d_weight.sif
|
||||
LoadInputFile: Reading base load of sif file
|
||||
CheckKeyword: Unlisted keyword: [calculate vectors] in section: [solver 1]
|
||||
CheckKeyword: Unlisted keyword: [calculate vectors] in section: [solver 2]
|
||||
LoadInputFile: Number of BCs: 7
|
||||
LoadInputFile: Number of Body Forces: 0
|
||||
LoadInputFile: Number of Initial Conditions: 0
|
||||
LoadInputFile: Number of Materials: 1
|
||||
LoadInputFile: Number of Equations: 1
|
||||
LoadInputFile: Number of Solvers: 4
|
||||
LoadInputFile: Number of Bodies: 1
|
||||
Loading user function library: [StatElecSolve]...[StatElecSolver_Init0]
|
||||
Loading user function library: [FluxSolver]...[FluxSolver_Init0]
|
||||
Loading user function library: [FluxSolver]...[FluxSolver_Init0]
|
||||
Loading user function library: [ResultOutputSolve]...[ResultOutputSolver_Init0]
|
||||
Loading user function library: [ResultOutputSolve]...[ResultOutputSolver_Init0]
|
||||
Loading user function library: [SaveData]...[SaveScalars_Init0]
|
||||
Loading user function library: [SaveData]...[SaveScalars_Init0]
|
||||
LoadMesh: Loading serial mesh!
|
||||
ElmerMeshReader: Performing step: 1
|
||||
ElmerMeshReader: Base mesh name: ./wires2d
|
||||
ReadHeaderFile: Reading header info from file: ./wires2d/mesh.header
|
||||
InitializeMesh: Number of nodes in mesh: 10765324
|
||||
InitializeMesh: Number of bulk elements in mesh: 5382347
|
||||
InitializeMesh: Number of boundary elements in mesh: 4640
|
||||
InitializeMesh: Initial number of max element nodes: 6
|
||||
ElmerMeshReader: Performing step: 2
|
||||
ReadNodesFile: Reading nodes from file: ./wires2d/mesh.nodes
|
||||
SetMeshDimension: Dimension of mesh is: 2
|
||||
SetMeshDimension: Max dimension of mesh is: 2
|
||||
MapCoordinates: Scaling coordinates: 1.000E-03 1.000E-03 1.000E+00
|
||||
ElmerMeshReader: Performing step: 3
|
||||
ReadElementsFile: Reading bulk elements from file: ./wires2d/mesh.elements
|
||||
ElmerMeshReader: Performing step: 4
|
||||
ReadBoundaryFile: Reading boundary elements from file: ./wires2d/mesh.boundary
|
||||
PermuteNodeNumbering: Performing node mapping
|
||||
MapBodiesAndBCs: Remapping bodies
|
||||
MapBodiesAndBCs: Minimum initial body index: 13
|
||||
MapBodiesAndBCs: Maximum initial body index: 13
|
||||
MapBodiesAndBCs: Remapping boundaries
|
||||
MapBodiesAndBCs: Minimum initial boundary index: 1
|
||||
MapBodiesAndBCs: Maximum initial boundary index: 40
|
||||
ElmerMeshReader: Performing step: 5
|
||||
ElmerMeshReader: Performing step: 6
|
||||
LoadMesh: Loading mesh done
|
||||
PrepareMesh: Preparing mesh for the future
|
||||
LoadMesh: Preparing mesh done
|
||||
LoadMesh: Elapsed REAL time: 77.1926 (s)
|
||||
MeshStabParams: Computing stabilization parameters
|
||||
MeshStabParams: Elapsed REAL time: 4.1362 (s)
|
||||
MAIN: -------------------------------------
|
||||
AddMeshCoordinates: Setting mesh coordinates and time
|
||||
AddSolvers: Setting up 4 solvers
|
||||
AddSolvers: Setting up solver 1: electrostatics
|
||||
AddEquationBasics: Using procedure: StatElecSolve StatElecSolver
|
||||
AddEquationBasics: Setting up solver: electrostatics
|
||||
Loading user function library: [StatElecSolve]...[StatElecSolver_Init]
|
||||
Loading user function library: [StatElecSolve]...[StatElecSolver_bulk]
|
||||
Loading user function library: [StatElecSolve]...[StatElecSolver_bulk]
|
||||
Loading user function library: [StatElecSolve]...[StatElecSolver]
|
||||
AddEquationBasics: Creating standard variable: potential
|
||||
OptimizeBandwidth: ---------------------------------------------------------
|
||||
OptimizeBandwidth: Computing matrix structure for: electrostatics
|
||||
OptimizeBandwidth: Initial bandwidth for electrostatics: 10752353
|
||||
OptimizeBandwidth: Optimized bandwidth for electrostatics: 20840
|
||||
OptimizeBandwidth: ---------------------------------------------------------
|
||||
AddSolvers: Setting up solver 2: electric field
|
||||
AddEquationBasics: Using procedure: FluxSolver FluxSolver
|
||||
AddEquationBasics: Setting up solver: electric field
|
||||
Loading user function library: [FluxSolver]...[FluxSolver_Init]
|
||||
WARNING:: FluxSolver_init: No field computation requested, setting > Calculate Flux = True <
|
||||
FluxSolver_init: Saving flux to: electric field Flux
|
||||
Loading user function library: [FluxSolver]...[FluxSolver_bulk]
|
||||
Loading user function library: [FluxSolver]...[FluxSolver_bulk]
|
||||
Loading user function library: [FluxSolver]...[FluxSolver]
|
||||
AddEquationBasics: Creating standard variable: electric field_temp
|
||||
OptimizeBandwidth: ---------------------------------------------------------
|
||||
OptimizeBandwidth: Computing matrix structure for: electric field
|
||||
OptimizeBandwidth: Initial bandwidth for electric field: 10752353
|
||||
OptimizeBandwidth: Optimized bandwidth for electric field: 20840
|
||||
OptimizeBandwidth: ---------------------------------------------------------
|
||||
AddSolvers: Setting up solver 3: result output
|
||||
AddEquationBasics: Using procedure: ResultOutputSolve ResultOutputSolver
|
||||
AddEquationBasics: Setting up solver: result output
|
||||
Loading user function library: [ResultOutputSolve]...[ResultOutputSolver_Init]
|
||||
Loading user function library: [ResultOutputSolve]...[ResultOutputSolver_Init]
|
||||
Loading user function library: [ResultOutputSolve]...[ResultOutputSolver_bulk]
|
||||
Loading user function library: [ResultOutputSolve]...[ResultOutputSolver_bulk]
|
||||
Loading user function library: [ResultOutputSolve]...[ResultOutputSolver]
|
||||
AddSolvers: Setting up solver 4: savescalars
|
||||
AddEquationBasics: Using procedure: SaveData SaveScalars
|
||||
AddEquationBasics: Setting up solver: savescalars
|
||||
Loading user function library: [SaveData]...[SaveScalars_Init]
|
||||
Loading user function library: [SaveData]...[SaveScalars_bulk]
|
||||
Loading user function library: [SaveData]...[SaveScalars_bulk]
|
||||
Loading user function library: [SaveData]...[SaveScalars]
|
||||
AddTimeEtc: Setting time and other global variables
|
||||
MAIN: Random seed initialized to: 314159265
|
||||
SetInitialConditions: Setting up initial conditions (if any)
|
||||
MAIN: Number of timesteps to be saved: 1
|
||||
MAIN:
|
||||
MAIN: -------------------------------------
|
||||
MAIN: Steady state iteration: 1
|
||||
MAIN: -------------------------------------
|
||||
MAIN:
|
||||
SingleSolver: Attempting to call solver: 1
|
||||
SingleSolver: Solver Equation string is: electrostatics
|
||||
StatElecSolver: ------------------------------------------------
|
||||
StatElecSolver: Solving static electric field for insulators
|
||||
StatElecSolver: Permittivity Of Vacuum = 8.854200E-12 (set explicitly in Constants)
|
||||
DefaultStart: Starting solver: electrostatics
|
||||
StatElecSolver: Performing non-vectorized bulk element assembly
|
||||
StatElecSolverBulkAssembly: Elapsed REAL time: 51.8520 (s)
|
||||
StatElecSolver: Assembly of boundary colour: 1
|
||||
StatElecSolverBCAssembly: Elapsed REAL time: 0.0198 (s)
|
||||
DefUtils::DefaultDirichletBCs: Setting Dirichlet boundary conditions
|
||||
EnforceDirichletConditions: Enforcing total of 9280 Dirichlet conditions.
|
||||
EnforceDirichletConditions: Applying Dirichlet conditions using scaled diagonal
|
||||
ScaleLinearSystem: Creating > DiagScaling < vector of size 10765324
|
||||
EnforceDirichletConditions: Dirichlet conditions enforced for dofs: 9280
|
||||
DefaultSolve: Solving linear system with default routines
|
||||
SolveSystem: Solving linear system
|
||||
SolveLinearSystem: Assuming real valued linear system
|
||||
SolveLinearSystem: Assuming serial linear system
|
||||
ScaleLinearSystem: Creating > DiagScaling < vector of size 10765324
|
||||
SolveLinearSystem: Linear System Preconditioning: ilu1
|
||||
Prec0-ilu1: Elapsed REAL time: 0.0000 (s)
|
||||
SolveLinearSystem: Serial linear System Solver: iterative
|
||||
IterSolver: Using iterative method: cg
|
||||
CRS_IncompleteLU: ILU(1) (Real), Performing Factorization:
|
||||
CRS_IncompleteLU: Allocated LU matrix of size: 166400948
|
||||
CRS_IncompleteLU: ILU(1) (Real), NOF nonzeros: 166400949
|
||||
CRS_IncompleteLU: ILU(1) (Real), filling (%) : 134
|
||||
CRS_IncompleteLU: ILU(1) (Real), Factorization time (s): 144.70
|
||||
Prec-ilu1: Elapsed REAL time: 144.2477 (s)
|
||||
1 0.5347E-01
|
||||
2 0.3328E-01
|
||||
3 0.1924E-01
|
||||
4 0.1481E-01
|
||||
5 0.1439E-01
|
||||
6 0.1095E-01
|
||||
7 0.8128E-02
|
||||
8 0.8861E-02
|
||||
9 0.7366E-02
|
||||
10 0.5646E-02
|
||||
11 0.6125E-02
|
||||
12 0.5680E-02
|
||||
13 0.4608E-02
|
||||
14 0.5164E-02
|
||||
15 0.4180E-02
|
||||
16 0.4191E-02
|
||||
17 0.4135E-02
|
||||
18 0.3766E-02
|
||||
19 0.3636E-02
|
||||
20 0.3383E-02
|
||||
21 0.3640E-02
|
||||
22 0.2877E-02
|
||||
23 0.3296E-02
|
||||
24 0.3014E-02
|
||||
25 0.2586E-02
|
||||
26 0.3012E-02
|
||||
27 0.2732E-02
|
||||
28 0.2580E-02
|
||||
29 0.2592E-02
|
||||
30 0.2634E-02
|
||||
31 0.2402E-02
|
||||
32 0.2337E-02
|
||||
33 0.2333E-02
|
||||
34 0.2288E-02
|
||||
35 0.2159E-02
|
||||
36 0.2161E-02
|
||||
37 0.2118E-02
|
||||
38 0.1982E-02
|
||||
39 0.2037E-02
|
||||
40 0.1904E-02
|
||||
41 0.1910E-02
|
||||
42 0.1837E-02
|
||||
43 0.1789E-02
|
||||
44 0.1713E-02
|
||||
45 0.1642E-02
|
||||
46 0.1752E-02
|
||||
47 0.1474E-02
|
||||
48 0.1612E-02
|
||||
49 0.1601E-02
|
||||
50 0.1360E-02
|
||||
51 0.1403E-02
|
||||
52 0.1318E-02
|
||||
53 0.1258E-02
|
||||
54 0.1253E-02
|
||||
55 0.1211E-02
|
||||
56 0.1154E-02
|
||||
57 0.1126E-02
|
||||
58 0.1050E-02
|
||||
59 0.1024E-02
|
||||
60 0.9542E-03
|
||||
61 0.9713E-03
|
||||
62 0.9079E-03
|
||||
63 0.8562E-03
|
||||
64 0.9112E-03
|
||||
65 0.8001E-03
|
||||
66 0.7960E-03
|
||||
67 0.7864E-03
|
||||
68 0.7477E-03
|
||||
69 0.6913E-03
|
||||
70 0.6955E-03
|
||||
71 0.6548E-03
|
||||
72 0.6144E-03
|
||||
73 0.6377E-03
|
||||
74 0.5784E-03
|
||||
75 0.5479E-03
|
||||
76 0.5419E-03
|
||||
77 0.5080E-03
|
||||
78 0.4891E-03
|
||||
79 0.4966E-03
|
||||
80 0.4592E-03
|
||||
81 0.4495E-03
|
||||
82 0.4433E-03
|
||||
83 0.4142E-03
|
||||
84 0.4067E-03
|
||||
85 0.4097E-03
|
||||
86 0.4061E-03
|
||||
87 0.3725E-03
|
||||
88 0.3917E-03
|
||||
89 0.3857E-03
|
||||
90 0.3596E-03
|
||||
91 0.3725E-03
|
||||
92 0.3573E-03
|
||||
93 0.3453E-03
|
||||
94 0.3398E-03
|
||||
95 0.3423E-03
|
||||
96 0.3117E-03
|
||||
97 0.2955E-03
|
||||
98 0.2988E-03
|
||||
99 0.2779E-03
|
||||
100 0.2713E-03
|
||||
101 0.2686E-03
|
||||
102 0.2528E-03
|
||||
103 0.2623E-03
|
||||
104 0.2608E-03
|
||||
105 0.2365E-03
|
||||
106 0.2498E-03
|
||||
107 0.2369E-03
|
||||
108 0.2279E-03
|
||||
109 0.2193E-03
|
||||
110 0.2139E-03
|
||||
111 0.2121E-03
|
||||
112 0.2028E-03
|
||||
113 0.2030E-03
|
||||
114 0.1943E-03
|
||||
115 0.1868E-03
|
||||
116 0.1858E-03
|
||||
117 0.1810E-03
|
||||
118 0.1724E-03
|
||||
119 0.1669E-03
|
||||
120 0.1707E-03
|
||||
121 0.1616E-03
|
||||
122 0.1630E-03
|
||||
123 0.1641E-03
|
||||
124 0.1615E-03
|
||||
125 0.1531E-03
|
||||
126 0.1580E-03
|
||||
127 0.1535E-03
|
||||
128 0.1505E-03
|
||||
129 0.1457E-03
|
||||
130 0.1459E-03
|
||||
131 0.1503E-03
|
||||
132 0.1429E-03
|
||||
133 0.1424E-03
|
||||
134 0.1387E-03
|
||||
135 0.1378E-03
|
||||
136 0.1336E-03
|
||||
137 0.1281E-03
|
||||
138 0.1307E-03
|
||||
139 0.1291E-03
|
||||
140 0.1213E-03
|
||||
141 0.1235E-03
|
||||
142 0.1262E-03
|
||||
143 0.1202E-03
|
||||
144 0.1200E-03
|
||||
145 0.1216E-03
|
||||
146 0.1148E-03
|
||||
147 0.1112E-03
|
||||
148 0.1130E-03
|
||||
149 0.1102E-03
|
||||
150 0.1052E-03
|
||||
151 0.1057E-03
|
||||
152 0.1017E-03
|
||||
153 0.1061E-03
|
||||
154 0.1021E-03
|
||||
155 0.9918E-04
|
||||
156 0.1041E-03
|
||||
157 0.9991E-04
|
||||
158 0.9982E-04
|
||||
159 0.9954E-04
|
||||
160 0.1023E-03
|
||||
161 0.1002E-03
|
||||
162 0.9694E-04
|
||||
163 0.1000E-03
|
||||
164 0.9766E-04
|
||||
165 0.9679E-04
|
||||
166 0.9694E-04
|
||||
167 0.9222E-04
|
||||
168 0.9434E-04
|
||||
169 0.9274E-04
|
||||
170 0.8896E-04
|
||||
171 0.9257E-04
|
||||
172 0.9057E-04
|
||||
173 0.8984E-04
|
||||
174 0.9194E-04
|
||||
175 0.9215E-04
|
||||
176 0.9206E-04
|
||||
177 0.9011E-04
|
||||
178 0.8920E-04
|
||||
179 0.8822E-04
|
||||
180 0.8819E-04
|
||||
181 0.8434E-04
|
||||
182 0.8724E-04
|
||||
183 0.8690E-04
|
||||
184 0.8368E-04
|
||||
185 0.8410E-04
|
||||
186 0.8546E-04
|
||||
187 0.8308E-04
|
||||
188 0.8511E-04
|
||||
189 0.8511E-04
|
||||
190 0.8252E-04
|
||||
191 0.8361E-04
|
||||
192 0.8306E-04
|
||||
193 0.8293E-04
|
||||
194 0.8164E-04
|
||||
195 0.8151E-04
|
||||
196 0.7998E-04
|
||||
197 0.7898E-04
|
||||
198 0.8071E-04
|
||||
199 0.7827E-04
|
||||
200 0.7854E-04
|
||||
201 0.7897E-04
|
||||
202 0.8053E-04
|
||||
203 0.7724E-04
|
||||
204 0.7795E-04
|
||||
205 0.7749E-04
|
||||
206 0.7667E-04
|
||||
207 0.8003E-04
|
||||
208 0.7836E-04
|
||||
209 0.7533E-04
|
||||
210 0.7639E-04
|
||||
211 0.7463E-04
|
||||
212 0.7297E-04
|
||||
213 0.7311E-04
|
||||
214 0.7148E-04
|
||||
215 0.7277E-04
|
||||
216 0.7063E-04
|
||||
217 0.7210E-04
|
||||
218 0.7044E-04
|
||||
219 0.7116E-04
|
||||
220 0.7107E-04
|
||||
221 0.6990E-04
|
||||
222 0.7070E-04
|
||||
223 0.6931E-04
|
||||
224 0.6757E-04
|
||||
225 0.6630E-04
|
||||
226 0.6642E-04
|
||||
227 0.6550E-04
|
||||
228 0.6404E-04
|
||||
229 0.6327E-04
|
||||
230 0.6178E-04
|
||||
231 0.6057E-04
|
||||
232 0.5921E-04
|
||||
233 0.5852E-04
|
||||
234 0.5681E-04
|
||||
235 0.5642E-04
|
||||
236 0.5626E-04
|
||||
237 0.5609E-04
|
||||
238 0.5491E-04
|
||||
239 0.5562E-04
|
||||
240 0.5611E-04
|
||||
241 0.5551E-04
|
||||
242 0.5410E-04
|
||||
243 0.5453E-04
|
||||
244 0.5310E-04
|
||||
245 0.5163E-04
|
||||
246 0.5115E-04
|
||||
247 0.5003E-04
|
||||
248 0.5043E-04
|
||||
249 0.4970E-04
|
||||
250 0.4901E-04
|
||||
251 0.5027E-04
|
||||
252 0.4822E-04
|
||||
253 0.4767E-04
|
||||
254 0.4822E-04
|
||||
255 0.4624E-04
|
||||
256 0.4534E-04
|
||||
257 0.4563E-04
|
||||
258 0.4418E-04
|
||||
259 0.4335E-04
|
||||
260 0.4312E-04
|
||||
261 0.4176E-04
|
||||
262 0.4157E-04
|
||||
263 0.3985E-04
|
||||
264 0.3950E-04
|
||||
265 0.3906E-04
|
||||
266 0.3707E-04
|
||||
267 0.3820E-04
|
||||
268 0.3702E-04
|
||||
269 0.3608E-04
|
||||
270 0.3567E-04
|
||||
271 0.3487E-04
|
||||
272 0.3369E-04
|
||||
273 0.3254E-04
|
||||
274 0.3249E-04
|
||||
275 0.3156E-04
|
||||
276 0.3085E-04
|
||||
277 0.2980E-04
|
||||
278 0.2851E-04
|
||||
279 0.2829E-04
|
||||
280 0.2816E-04
|
||||
281 0.2714E-04
|
||||
282 0.2687E-04
|
||||
283 0.2588E-04
|
||||
284 0.2489E-04
|
||||
285 0.2407E-04
|
||||
286 0.2359E-04
|
||||
287 0.2282E-04
|
||||
288 0.2222E-04
|
||||
289 0.2190E-04
|
||||
290 0.2161E-04
|
||||
291 0.2101E-04
|
||||
292 0.2062E-04
|
||||
293 0.2042E-04
|
||||
294 0.1961E-04
|
||||
295 0.1907E-04
|
||||
296 0.1875E-04
|
||||
297 0.1833E-04
|
||||
298 0.1762E-04
|
||||
299 0.1778E-04
|
||||
300 0.1717E-04
|
||||
301 0.1655E-04
|
||||
302 0.1629E-04
|
||||
303 0.1610E-04
|
||||
304 0.1531E-04
|
||||
305 0.1522E-04
|
||||
306 0.1482E-04
|
||||
307 0.1430E-04
|
||||
308 0.1423E-04
|
||||
309 0.1382E-04
|
||||
310 0.1336E-04
|
||||
311 0.1285E-04
|
||||
312 0.1299E-04
|
||||
313 0.1239E-04
|
||||
314 0.1181E-04
|
||||
315 0.1160E-04
|
||||
316 0.1147E-04
|
||||
317 0.1104E-04
|
||||
318 0.1088E-04
|
||||
319 0.1084E-04
|
||||
320 0.1021E-04
|
||||
321 0.1027E-04
|
||||
322 0.1001E-04
|
||||
323 0.9569E-05
|
||||
324 0.9237E-05
|
||||
325 0.9143E-05
|
||||
326 0.8905E-05
|
||||
327 0.8415E-05
|
||||
328 0.8132E-05
|
||||
329 0.8103E-05
|
||||
330 0.7573E-05
|
||||
331 0.7570E-05
|
||||
332 0.7289E-05
|
||||
333 0.6822E-05
|
||||
334 0.6800E-05
|
||||
335 0.6537E-05
|
||||
336 0.6328E-05
|
||||
337 0.6221E-05
|
||||
338 0.6078E-05
|
||||
339 0.5812E-05
|
||||
340 0.5670E-05
|
||||
341 0.5561E-05
|
||||
342 0.5374E-05
|
||||
343 0.5297E-05
|
||||
344 0.5221E-05
|
||||
345 0.4998E-05
|
||||
346 0.4893E-05
|
||||
347 0.4758E-05
|
||||
348 0.4603E-05
|
||||
349 0.4331E-05
|
||||
350 0.4298E-05
|
||||
351 0.4205E-05
|
||||
352 0.3932E-05
|
||||
353 0.3970E-05
|
||||
354 0.3879E-05
|
||||
355 0.3700E-05
|
||||
356 0.3648E-05
|
||||
357 0.3590E-05
|
||||
358 0.3417E-05
|
||||
359 0.3353E-05
|
||||
360 0.3349E-05
|
||||
361 0.3132E-05
|
||||
362 0.3060E-05
|
||||
363 0.2992E-05
|
||||
364 0.2878E-05
|
||||
365 0.2777E-05
|
||||
366 0.2726E-05
|
||||
367 0.2680E-05
|
||||
368 0.2561E-05
|
||||
369 0.2499E-05
|
||||
370 0.2503E-05
|
||||
371 0.2350E-05
|
||||
372 0.2291E-05
|
||||
373 0.2291E-05
|
||||
374 0.2196E-05
|
||||
375 0.2124E-05
|
||||
376 0.2069E-05
|
||||
377 0.1943E-05
|
||||
378 0.1900E-05
|
||||
379 0.1825E-05
|
||||
380 0.1762E-05
|
||||
381 0.1756E-05
|
||||
382 0.1682E-05
|
||||
383 0.1636E-05
|
||||
384 0.1583E-05
|
||||
385 0.1562E-05
|
||||
386 0.1482E-05
|
||||
387 0.1454E-05
|
||||
388 0.1433E-05
|
||||
389 0.1362E-05
|
||||
390 0.1341E-05
|
||||
391 0.1310E-05
|
||||
392 0.1240E-05
|
||||
393 0.1232E-05
|
||||
394 0.1146E-05
|
||||
395 0.1134E-05
|
||||
396 0.1104E-05
|
||||
397 0.1038E-05
|
||||
398 0.1010E-05
|
||||
399 0.9848E-06
|
||||
400 0.9695E-06
|
||||
401 0.9204E-06
|
||||
402 0.8811E-06
|
||||
403 0.8680E-06
|
||||
404 0.8370E-06
|
||||
405 0.8015E-06
|
||||
406 0.7960E-06
|
||||
407 0.7700E-06
|
||||
408 0.7217E-06
|
||||
409 0.7124E-06
|
||||
410 0.7122E-06
|
||||
411 0.6730E-06
|
||||
412 0.6387E-06
|
||||
413 0.6421E-06
|
||||
414 0.6217E-06
|
||||
415 0.5826E-06
|
||||
416 0.5622E-06
|
||||
417 0.5619E-06
|
||||
418 0.5238E-06
|
||||
419 0.5128E-06
|
||||
420 0.5005E-06
|
||||
421 0.4782E-06
|
||||
422 0.4584E-06
|
||||
423 0.4437E-06
|
||||
424 0.4296E-06
|
||||
425 0.4155E-06
|
||||
426 0.4046E-06
|
||||
427 0.3897E-06
|
||||
428 0.3829E-06
|
||||
429 0.3635E-06
|
||||
430 0.3528E-06
|
||||
431 0.3411E-06
|
||||
432 0.3293E-06
|
||||
433 0.3161E-06
|
||||
434 0.3003E-06
|
||||
435 0.2931E-06
|
||||
436 0.2790E-06
|
||||
437 0.2727E-06
|
||||
438 0.2657E-06
|
||||
439 0.2595E-06
|
||||
440 0.2537E-06
|
||||
441 0.2409E-06
|
||||
442 0.2420E-06
|
||||
443 0.2365E-06
|
||||
444 0.2253E-06
|
||||
445 0.2279E-06
|
||||
446 0.2187E-06
|
||||
447 0.2137E-06
|
||||
448 0.2069E-06
|
||||
449 0.1991E-06
|
||||
450 0.1951E-06
|
||||
451 0.1842E-06
|
||||
452 0.1770E-06
|
||||
453 0.1703E-06
|
||||
454 0.1636E-06
|
||||
455 0.1573E-06
|
||||
456 0.1513E-06
|
||||
457 0.1410E-06
|
||||
458 0.1384E-06
|
||||
459 0.1329E-06
|
||||
460 0.1291E-06
|
||||
461 0.1232E-06
|
||||
462 0.1203E-06
|
||||
463 0.1166E-06
|
||||
464 0.1130E-06
|
||||
465 0.1101E-06
|
||||
466 0.1066E-06
|
||||
467 0.1039E-06
|
||||
468 0.1017E-06
|
||||
469 0.1001E-06
|
||||
470 0.9576E-07
|
||||
471 0.9171E-07
|
||||
472 0.8996E-07
|
||||
473 0.8820E-07
|
||||
474 0.8378E-07
|
||||
475 0.8248E-07
|
||||
476 0.8146E-07
|
||||
477 0.7693E-07
|
||||
478 0.7579E-07
|
||||
479 0.7377E-07
|
||||
480 0.7089E-07
|
||||
481 0.7071E-07
|
||||
482 0.6895E-07
|
||||
483 0.6634E-07
|
||||
484 0.6513E-07
|
||||
485 0.6406E-07
|
||||
486 0.6326E-07
|
||||
487 0.6113E-07
|
||||
488 0.5976E-07
|
||||
489 0.5801E-07
|
||||
490 0.5689E-07
|
||||
491 0.5503E-07
|
||||
492 0.5377E-07
|
||||
493 0.5211E-07
|
||||
494 0.5148E-07
|
||||
495 0.4939E-07
|
||||
496 0.4864E-07
|
||||
497 0.4811E-07
|
||||
498 0.4785E-07
|
||||
499 0.4624E-07
|
||||
500 0.4535E-07
|
||||
501 0.4584E-07
|
||||
502 0.4342E-07
|
||||
503 0.4297E-07
|
||||
504 0.4229E-07
|
||||
505 0.4091E-07
|
||||
506 0.4088E-07
|
||||
507 0.4060E-07
|
||||
508 0.3892E-07
|
||||
509 0.3850E-07
|
||||
510 0.3815E-07
|
||||
511 0.3699E-07
|
||||
512 0.3530E-07
|
||||
513 0.3515E-07
|
||||
514 0.3497E-07
|
||||
515 0.3305E-07
|
||||
516 0.3345E-07
|
||||
517 0.3404E-07
|
||||
518 0.3306E-07
|
||||
519 0.3356E-07
|
||||
520 0.3382E-07
|
||||
521 0.3314E-07
|
||||
522 0.3261E-07
|
||||
523 0.3305E-07
|
||||
524 0.3246E-07
|
||||
525 0.3201E-07
|
||||
526 0.3222E-07
|
||||
527 0.3211E-07
|
||||
528 0.3162E-07
|
||||
529 0.3162E-07
|
||||
530 0.3235E-07
|
||||
531 0.3170E-07
|
||||
532 0.3129E-07
|
||||
533 0.3237E-07
|
||||
534 0.3117E-07
|
||||
535 0.3098E-07
|
||||
536 0.3116E-07
|
||||
537 0.3058E-07
|
||||
538 0.3030E-07
|
||||
539 0.3048E-07
|
||||
540 0.3023E-07
|
||||
541 0.2970E-07
|
||||
542 0.3004E-07
|
||||
543 0.2973E-07
|
||||
544 0.2952E-07
|
||||
545 0.2971E-07
|
||||
546 0.2997E-07
|
||||
547 0.2920E-07
|
||||
548 0.2955E-07
|
||||
549 0.2905E-07
|
||||
550 0.2834E-07
|
||||
551 0.2822E-07
|
||||
552 0.2813E-07
|
||||
553 0.2747E-07
|
||||
554 0.2726E-07
|
||||
555 0.2719E-07
|
||||
556 0.2665E-07
|
||||
557 0.2645E-07
|
||||
558 0.2649E-07
|
||||
559 0.2609E-07
|
||||
560 0.2574E-07
|
||||
561 0.2585E-07
|
||||
562 0.2479E-07
|
||||
563 0.2519E-07
|
||||
564 0.2474E-07
|
||||
565 0.2390E-07
|
||||
566 0.2377E-07
|
||||
567 0.2350E-07
|
||||
568 0.2286E-07
|
||||
569 0.2249E-07
|
||||
570 0.2208E-07
|
||||
571 0.2142E-07
|
||||
572 0.2064E-07
|
||||
573 0.2004E-07
|
||||
574 0.1932E-07
|
||||
575 0.1882E-07
|
||||
576 0.1813E-07
|
||||
577 0.1769E-07
|
||||
578 0.1713E-07
|
||||
579 0.1644E-07
|
||||
580 0.1624E-07
|
||||
581 0.1563E-07
|
||||
582 0.1508E-07
|
||||
583 0.1492E-07
|
||||
584 0.1422E-07
|
||||
585 0.1380E-07
|
||||
586 0.1336E-07
|
||||
587 0.1304E-07
|
||||
588 0.1245E-07
|
||||
589 0.1242E-07
|
||||
590 0.1211E-07
|
||||
591 0.1184E-07
|
||||
592 0.1147E-07
|
||||
593 0.1119E-07
|
||||
594 0.1099E-07
|
||||
595 0.1068E-07
|
||||
596 0.1040E-07
|
||||
597 0.1019E-07
|
||||
598 0.9852E-08
|
||||
598 0.9852E-08
|
||||
ComputeNorm: Computing norm of solution
|
||||
ComputeChange: NS (ITER=1) (NRM,RELC): ( 0.41800370E-01 2.0000000 ) :: electrostatics
|
||||
DefaultFinish: Finished solver: electrostatics
|
||||
Loading user function library: [StatElecSolve]...[StatElecSolver_post]
|
||||
SingleSolver: Calling solver for postprocessing
|
||||
StatElecSolver_post: ------------------------------------------------
|
||||
StatElecSolver_post: Calculating postprocessing fields
|
||||
StatElecSolver_post: Number of 1 postprocessing fields
|
||||
StatElecSolver_post: Creating a weighting for scaling purposes from 7
|
||||
StatElecSolver_post: Total Electric Energy: 7.297E-12
|
||||
StatElecSolver_post: Effective Capacitance: 1.459E-11
|
||||
ComputeNorm: Computing norm of solution
|
||||
ComputeChange: SS (ITER=1) (NRM,RELC): ( 0.41800370E-01 2.0000000 ) :: electrostatics
|
||||
SingleSolver: Attempting to call solver: 2
|
||||
SingleSolver: Solver Equation string is: electric field
|
||||
FluxSolver: -------------------------------------
|
||||
FluxSolver: Computing the flux and/or gradient
|
||||
FluxSolver: -------------------------------------
|
||||
FluxSolver: Assembly Time: 34.690865039825439
|
||||
DefaultSolve: Solving linear system with default routines
|
||||
SolveSystem: Solving linear system
|
||||
SolveLinearSystem: Assuming real valued linear system
|
||||
SolveLinearSystem: Assuming serial linear system
|
||||
ScaleLinearSystem: Creating > DiagScaling < vector of size 10765324
|
||||
SolveLinearSystem: Linear System Preconditioning: ilu0
|
||||
Prec0-ilu0: Elapsed REAL time: 0.0000 (s)
|
||||
SolveLinearSystem: Serial linear System Solver: iterative
|
||||
IterSolver: Using iterative method: cg
|
||||
CRS_IncompleteLU: ILU(0) (Real), Performing Factorization:
|
||||
CRS_IncompleteLU: Allocated LU matrix of size: 123796498
|
||||
CRS_IncompleteLU: ILU(0) (Real), NOF nonzeros: 123796499
|
||||
CRS_IncompleteLU: ILU(0) (Real), filling (%) : 100
|
||||
CRS_IncompleteLU: ILU(0) (Real), Factorization time (s): 1.74
|
||||
Prec-ilu0: Elapsed REAL time: 8.0068 (s)
|
||||
10 0.1203E-09
|
||||
11 0.1404E-10
|
||||
DefaultSolve: Solving linear system with default routines
|
||||
SolveSystem: Solving linear system
|
||||
SolveLinearSystem: Assuming real valued linear system
|
||||
SolveLinearSystem: Assuming serial linear system
|
||||
ScaleLinearSystem: Creating > DiagScaling < vector of size 10765324
|
||||
SolveLinearSystem: Linear System Preconditioning: ilu0
|
||||
Prec0-ilu0: Elapsed REAL time: 0.0000 (s)
|
||||
SolveLinearSystem: Serial linear System Solver: iterative
|
||||
IterSolver: Using iterative method: cg
|
||||
CRS_IncompleteLU: ILU(0) (Real), Performing Factorization:
|
||||
CRS_IncompleteLU: ILU(0) (Real), NOF nonzeros: 123796499
|
||||
CRS_IncompleteLU: ILU(0) (Real), filling (%) : 100
|
||||
CRS_IncompleteLU: ILU(0) (Real), Factorization time (s): 1.76
|
||||
Prec-ilu0: Elapsed REAL time: 1.8808 (s)
|
||||
10 0.1758E-09
|
||||
11 0.2016E-10
|
||||
ComputeNorm: Computing norm of solution
|
||||
ComputeChange: NS (ITER=2) (NRM,RELC): ( 21621.538 2.0000000 ) :: electric field
|
||||
FluxSolver: Solution Time: 27.188190937042236
|
||||
FluxSolver: Result Norm: 21621.538030194177
|
||||
FluxSolver: All done
|
||||
FluxSolver: -------------------------------------
|
||||
ComputeNorm: Computing norm of solution
|
||||
ComputeChange: SS (ITER=1) (NRM,RELC): ( 21621.538 2.0000000 ) :: electric field
|
||||
SingleSolver: Attempting to call solver: 3
|
||||
SingleSolver: Solver Equation string is: result output
|
||||
ResultOutputSolver: -------------------------------------
|
||||
ResultOutputSolver: Saving with prefix: elfield_weight
|
||||
ResultOutputSolver: Dimension of mesh is: 2
|
||||
ResultOutputSolver: Working on mesh 1: wires2d with 10765324 nodes
|
||||
ResultOutputSolver: Creating list for saving - if not present
|
||||
CreateListForSaving: Scalar Field 1 exists, creating no list!
|
||||
ResultOutputSolver: Saving in unstructured VTK XML (.vtu) format
|
||||
VtuOutputSolver: Saving results in VTK XML format with prefix: elfield_weight
|
||||
VtuOutputSolver: Saving number of partitions: 1
|
||||
SolverOutputDirectory: Creating directory: wires2d
|
||||
VtuOutputSolver: Full filename base is: wires2d/elfield_weight
|
||||
GenerateSaveMask: Number of active elements 5386987 out of 5386987
|
||||
GenerateSaveMask: Number of geometry nodes 10765324 out of 10765324
|
||||
VtuOutputSolver: Total number of elements to save: 5386987
|
||||
VtuOutputSolver: Total number of geometry nodes to save: 10765324
|
||||
VtuOutputSolver: Total number of dof nodes to save: 10765324
|
||||
VtuOutputSolver: Writing the vtu file: wires2d/elfield_weight_t0001.vtu
|
||||
AscBinWriteInit: Initializing buffered ascii/binary writing
|
||||
AscBinWriteInit: Writing in binary
|
||||
AscBinWriteInit: Writing in double precision
|
||||
AscBinWriteInit: Writing to unit number: 58
|
||||
AscBinWriteInit: Size of buffer is: 10765324
|
||||
WriteVtuFile: Writing nodal fields
|
||||
WriteVtuFile: Saving 1-component variable: potential
|
||||
WriteVtuFile: Saving 2-component variable: electric field
|
||||
WriteVtuFile: Number of nodal fields written: 2
|
||||
WriteVtuFile: Writing elemental fields
|
||||
WriteVtuFile: Number of elemental fields written: 0
|
||||
WriteVtuFile: Writing coordinates for each used node
|
||||
WriteVtuFile: Writing the elemental connectivity data
|
||||
WriteVtuFile: Writing nodal fields
|
||||
WriteVtuFile: Saving 1-component variable: potential
|
||||
WriteVtuFile: Saving 2-component variable: electric field
|
||||
WriteVtuFile: Writing elemental fields
|
||||
AscBinWriteFree: Terminating buffered ascii/binary writing
|
||||
VtuOutputSolver: All done for now
|
||||
ResultOutputSolver: -------------------------------------
|
||||
WritePostFile: Saving results in ElmerPost format to file wires2d/elstatics_weight.ep
|
||||
SaveResult: -----------------------------------------
|
||||
SaveResult: Saving results to file: wires2d/elstatics_weight.result
|
||||
SaveResult: Done writing results file
|
||||
SaveResult: -----------------------------------------
|
||||
SingleSolver: Attempting to call solver: 4
|
||||
SingleSolver: Solver Equation string is: savescalars
|
||||
SaveScalars: -----------------------------------------
|
||||
SaveScalars: Saving scalar values of various kinds
|
||||
SaveScalars: Performed 0 reduction operations
|
||||
SaveScalars: Found 3 result scalars in simulation section
|
||||
SaveScalars: Found 3 values to save in total
|
||||
SaveScalars: Saving names of values to file: ./scalars_weight.dat.names
|
||||
SaveScalars: Saving values to file: ./scalars_weight.dat
|
||||
SaveScalars: -----------------------------------------
|
||||
ReloadInputFile: Reloading input file
|
||||
LoadInputFile: Loading input file:
|
||||
MAIN: Reporting unused list entries for sif improvement!
|
||||
MAIN: If you do not want these lines undefine > DEVEL_LISTUSAGE < !
|
||||
Unused keywords:
|
||||
Boundary Condition 1 calculate electric force
|
||||
Boundary Condition 2 calculate electric force
|
||||
Boundary Condition 3 calculate electric force
|
||||
Boundary Condition 5 calculate electric force
|
||||
Solver 1 calculate vectors
|
||||
Solver 2 target variable
|
||||
Solver 2 calculate vectors
|
||||
MAIN: *** Elmer Solver: ALL DONE ***
|
||||
Loading user function library: [StatElecSolve]...[StatElecSolver_Finalize]
|
||||
Loading user function library: [StatElecSolve]...[StatElecSolver_Finalize]
|
||||
Loading user function library: [FluxSolver]...[FluxSolver_Finalize]
|
||||
Loading user function library: [FluxSolver]...[FluxSolver_Finalize]
|
||||
Loading user function library: [ResultOutputSolve]...[ResultOutputSolver_Finalize]
|
||||
Loading user function library: [ResultOutputSolve]...[ResultOutputSolver_Finalize]
|
||||
Loading user function library: [SaveData]...[SaveScalars_Finalize]
|
||||
Loading user function library: [SaveData]...[SaveScalars_Finalize]
|
||||
MAIN: The end
|
||||
SOLVER TOTAL TIME(CPU,REAL): 1148.85 1276.22
|
||||
ELMER SOLVER FINISHED AT: 2026/10/28 10:29:40
|
||||
376
anasen_fem/cfrac_pred_2026-10-28/solve_z44.7820/elmergrid.log
Normal file
376
anasen_fem/cfrac_pred_2026-10-28/solve_z44.7820/elmergrid.log
Normal file
|
|
@ -0,0 +1,376 @@
|
|||
==================================================================
|
||||
ElmerGrid mesh conversion and manipulation utility, Welcome!
|
||||
Version: 26.2 (Rev: NA, Compiled: NA)
|
||||
This program is free software licensed under GPL
|
||||
==================================================================
|
||||
Elmergrid reading in-line arguments
|
||||
Output will be saved to file wires2d.
|
||||
|
||||
Elmergrid loading data:
|
||||
-----------------------
|
||||
Format chosen using the first line: $MeshFormat
|
||||
Gmsh input file format is type 4.1 in ASCII.
|
||||
Loading mesh in Gmsh format 4.1 from file wires2d.msh
|
||||
Reading 146 entities in 0D
|
||||
Reading 146 entities in 1D
|
||||
Reading 146 entities in 2D
|
||||
Allocating for 10765324 knots and 5386987 elements.
|
||||
Leading element dimension is 2
|
||||
BC name for physical group 1 is: hot_needle
|
||||
BC name for physical group 2 is: ic_wire_1
|
||||
BC name for physical group 3 is: ic_wire_2
|
||||
BC name for physical group 10 is: guard_wires
|
||||
BC name for physical group 20 is: anode_wires
|
||||
BC name for physical group 30 is: cathode_wires
|
||||
BC name for physical group 40 is: cathode_wire_selected
|
||||
Body name for physical group 13 is: gas
|
||||
Allocating lookup table for tags of size 146
|
||||
Defined 146 0DIM entities with geometric tag range [1 146]
|
||||
Defined 146 1DIM entities with geometric tag range [1 146]
|
||||
Physical given tag range is [1 40]
|
||||
Defined 146 2DIM entities with geometric tag range [1 146]
|
||||
Physical given tag range is [13 13]
|
||||
Physical tag offset for 0D is 40
|
||||
Geometric tag offset for 0D is 186
|
||||
Reading 10765324 nodes in 438 blocks.
|
||||
Reading 5386987 elements in 291 blocks.
|
||||
Mapping mesh tag 2 to physical tag 30 in 1DIM
|
||||
Mapping mesh tag 3 to physical tag 30 in 1DIM
|
||||
Mapping mesh tag 4 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 5 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 6 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 7 to physical tag 40 in 1DIM
|
||||
Mapping mesh tag 8 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 9 to physical tag 30 in 1DIM
|
||||
Mapping mesh tag 10 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 11 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 12 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 13 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 14 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 15 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 16 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 17 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 18 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 19 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 20 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 21 to physical tag 30 in 1DIM
|
||||
Mapping mesh tag 22 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 23 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 24 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 25 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 26 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 27 to physical tag 30 in 1DIM
|
||||
Mapping mesh tag 28 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 29 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 30 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 31 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 32 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 33 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 34 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 35 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 36 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 37 to physical tag 30 in 1DIM
|
||||
Mapping mesh tag 38 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 39 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 40 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 41 to physical tag 30 in 1DIM
|
||||
Mapping mesh tag 42 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 43 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 44 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 45 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 46 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 47 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 48 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 49 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 50 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 51 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 52 to physical tag 30 in 1DIM
|
||||
Mapping mesh tag 53 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 54 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 55 to physical tag 30 in 1DIM
|
||||
Mapping mesh tag 56 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 57 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 58 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 59 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 60 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 61 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 62 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 63 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 64 to physical tag 30 in 1DIM
|
||||
Mapping mesh tag 65 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 66 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 67 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 68 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 69 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 70 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 71 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 72 to physical tag 30 in 1DIM
|
||||
Mapping mesh tag 73 to physical tag 30 in 1DIM
|
||||
Mapping mesh tag 74 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 75 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 76 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 77 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 78 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 79 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 80 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 81 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 82 to physical tag 30 in 1DIM
|
||||
Mapping mesh tag 83 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 84 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 85 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 86 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 87 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 88 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 89 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 90 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 91 to physical tag 30 in 1DIM
|
||||
Mapping mesh tag 92 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 93 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 94 to physical tag 30 in 1DIM
|
||||
Mapping mesh tag 95 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 96 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 97 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 98 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 99 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 100 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 101 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 102 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 103 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 104 to physical tag 30 in 1DIM
|
||||
Mapping mesh tag 105 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 106 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 107 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 108 to physical tag 30 in 1DIM
|
||||
Mapping mesh tag 109 to physical tag 1 in 1DIM
|
||||
Mapping mesh tag 110 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 111 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 112 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 113 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 114 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 115 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 116 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 117 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 118 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 119 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 120 to physical tag 30 in 1DIM
|
||||
Mapping mesh tag 121 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 122 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 123 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 124 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 125 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 126 to physical tag 30 in 1DIM
|
||||
Mapping mesh tag 127 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 128 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 129 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 130 to physical tag 3 in 1DIM
|
||||
Mapping mesh tag 131 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 132 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 133 to physical tag 2 in 1DIM
|
||||
Mapping mesh tag 134 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 135 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 136 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 137 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 138 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 139 to physical tag 30 in 1DIM
|
||||
Mapping mesh tag 140 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 141 to physical tag 30 in 1DIM
|
||||
Mapping mesh tag 142 to physical tag 10 in 1DIM
|
||||
Mapping mesh tag 143 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 144 to physical tag 20 in 1DIM
|
||||
Mapping mesh tag 145 to physical tag 30 in 1DIM
|
||||
Mapping mesh tag 146 to physical tag 30 in 1DIM
|
||||
Mapping mesh tag 1 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 2 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 3 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 4 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 5 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 6 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 7 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 8 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 9 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 10 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 11 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 12 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 13 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 14 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 15 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 16 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 17 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 18 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 19 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 20 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 21 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 22 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 23 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 24 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 25 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 26 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 27 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 28 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 29 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 30 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 31 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 32 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 33 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 34 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 35 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 36 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 37 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 38 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 39 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 40 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 41 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 42 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 43 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 44 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 45 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 46 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 47 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 48 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 49 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 50 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 51 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 52 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 53 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 54 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 55 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 56 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 57 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 58 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 59 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 60 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 61 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 62 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 63 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 64 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 65 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 66 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 67 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 68 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 69 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 70 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 71 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 72 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 73 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 74 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 75 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 76 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 77 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 78 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 79 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 80 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 81 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 82 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 83 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 84 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 85 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 86 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 87 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 88 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 89 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 90 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 91 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 92 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 93 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 94 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 95 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 96 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 97 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 98 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 99 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 100 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 101 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 102 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 103 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 104 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 105 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 106 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 107 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 108 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 109 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 110 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 111 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 112 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 113 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 114 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 115 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 116 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 117 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 118 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 119 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 120 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 121 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 122 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 123 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 124 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 125 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 126 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 127 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 128 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 129 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 130 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 131 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 132 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 133 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 134 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 135 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 136 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 137 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 138 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 139 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 140 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 141 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 142 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 143 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 144 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 145 to physical tag 13 in 2DIM
|
||||
Mapping mesh tag 146 to physical tag 13 in 2DIM
|
||||
Moving bulk elements to boundary elements
|
||||
Assuming that boundaries names are already Ok!
|
||||
Leading bulk elementtype is 306
|
||||
Trailing bulk elementtype is 203
|
||||
There are 4640 (out of 5386987) lower dimensional elements.
|
||||
Node 2373 belongs to maximum of 10 elements
|
||||
Found 4640 side elements that have two parents.
|
||||
Found correctly 4640 side elements.
|
||||
Parent elements were reordered up to index 5382347.
|
||||
Moved 5382347 elements (out of 5386987) to new positions
|
||||
Successfully read the mesh from the Gmsh input file wires2d.msh
|
||||
Using physical numbering of entities
|
||||
|
||||
Elmergrid creating and manipulating meshes:
|
||||
-------------------------------------------
|
||||
Coordinates defined in 2 dimensions
|
||||
Bounding box of all nodes in mesh:
|
||||
X:[-500,500] Y:[-499.998,499.998] Z:[0,0]
|
||||
|
||||
Notice: the longest bounding box side length of [1000] is greater than 50.
|
||||
ElmerGUI includes a library of material properties, defined in SI units. If using ElmerGUI,
|
||||
then the geometry is expected to have meters as length. Geometry that exceeds 50 meters
|
||||
in length or width or height may not be intended. Many Geometry generators assume
|
||||
millimeters as the basic unit of length. Scaling the geometry from millimeters to meters
|
||||
may be the desired action. For more help, search the Elmer users forum for posts
|
||||
about SI units, or for posts about Coordinate Scaling.
|
||||
Scaling can be accomplished in at least three ways, as follows:
|
||||
1. Define the original geometry in meters, not millimeters.
|
||||
2. Call ElmerGrid with -scale 0.001 0.001 0.001 as an option.
|
||||
3. Add Coordinate Scaling = 0.001 to the simulation section of the sif file.
|
||||
If using Elmer to analyze large geometry, such as a glacier, then ignore this notice.
|
||||
|
||||
Calculate Mesh Pieces in mesh:
|
||||
There is a single piece in the mesh, so the mesh is conforming.
|
||||
|
||||
Elmergrid saving data with method 2:
|
||||
-------------------------------------
|
||||
Saving mesh in ElmerSolver format to directory wires2d.
|
||||
Reusing an existing directory
|
||||
Saving 10765324 coordinates to mesh.nodes.
|
||||
Saving 5382347 element topologies to mesh.elements.
|
||||
Saving boundary elements to mesh.boundary.
|
||||
Saving header info to mesh.header.
|
||||
Saving names info to mesh.names.
|
||||
Saving entities info to entities.sif.
|
||||
|
||||
Thank you for using ElmerGrid!
|
||||
Repository: https://github.com/ElmerCSC/elmerfem
|
||||
Contact: elmeradm@csc.fi
|
||||
61
anasen_fem/cfrac_pred_2026-10-28/solve_z44.7820/geometry.py
Normal file
61
anasen_fem/cfrac_pred_2026-10-28/solve_z44.7820/geometry.py
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
"""Relative anode-cathode azimuth against z in the anasen_fem 2D model.
|
||||
|
||||
The mesher (anasen_fem/wires_gmsh2d_bc.py) places every wire by linear
|
||||
interpolation in x and y between its plane-1 and plane-2 end points, so a wire
|
||||
is a straight chord and the lattice is a hyperboloid: the radius shrinks to the
|
||||
waist at z = 0 and the azimuth is NOT linear in z. This reproduces that
|
||||
placement exactly and reports, for each z, the azimuth of the anode and cathode
|
||||
lattices, their relative offset within one 15 deg cathode pitch, and the
|
||||
smallest anode-cathode centre distance.
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
k = 2 * np.pi / 24.0
|
||||
offset_a1 = -6 * k - 4 * k
|
||||
offset_c1 = -6 * k + k / 2.0
|
||||
wireShift = 4.0
|
||||
offset_a2 = offset_a1 + wireShift * k
|
||||
offset_c2 = offset_c1 - wireShift * k
|
||||
|
||||
i = np.arange(24)
|
||||
xa1, ya1 = 37 * np.cos(-k * i + offset_a1), 37 * np.sin(-k * i + offset_a1)
|
||||
xa2, ya2 = 37 * np.cos(-k * i + offset_a2), 37 * np.sin(-k * i + offset_a2)
|
||||
xc1, yc1 = 42 * np.cos(k * i + offset_c1), 42 * np.sin(k * i + offset_c1)
|
||||
xc2, yc2 = 42 * np.cos(k * i + offset_c2), 42 * np.sin(k * i + offset_c2)
|
||||
|
||||
|
||||
def wires(z):
|
||||
t = (z + 174.3) / (2 * 174.3)
|
||||
xa, ya = xa1 + t * (xa2 - xa1), ya1 + t * (ya2 - ya1)
|
||||
xc, yc = xc1 + t * (xc2 - xc1), yc1 + t * (yc2 - yc1)
|
||||
return (xa, ya), (xc, yc)
|
||||
|
||||
|
||||
def rel_azimuth_deg(z, cath=1):
|
||||
"""Signed azimuth of the nearest anode from cathode `cath`, in degrees."""
|
||||
(xa, ya), (xc, yc) = wires(z)
|
||||
phi_c = np.arctan2(yc[cath], xc[cath])
|
||||
d = np.degrees(np.arctan2(ya, xa) - phi_c)
|
||||
d = (d + 180.0) % 360.0 - 180.0
|
||||
j = np.argmin(np.abs(d))
|
||||
return d[j], j
|
||||
|
||||
|
||||
def min_distance(z, cath=1):
|
||||
(xa, ya), (xc, yc) = wires(z)
|
||||
r = np.hypot(xa - xc[cath], ya - yc[cath])
|
||||
return r.min()
|
||||
|
||||
|
||||
def radii(z):
|
||||
(xa, ya), (xc, yc) = wires(z)
|
||||
return np.hypot(xa, ya).mean(), np.hypot(xc, yc).mean()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(" z[mm] rel_az[deg] anode r_a[mm] r_c[mm] min|a-c|[mm]")
|
||||
for z in np.arange(-5, 70.001, 1.0):
|
||||
d, j = rel_azimuth_deg(z)
|
||||
ra, rc = radii(z)
|
||||
print("%7.3f %9.4f %4d %7.3f %7.3f %8.4f"
|
||||
% (z, d, j, ra, rc, min_distance(z)))
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
"""E.E_w on every anode wire surface, read straight from the Elmer VTUs.
|
||||
|
||||
Same quantity as anasen_fem/dotproduct.py, but by default it never writes the
|
||||
~1 GB dotproduct VTU: only the anode surfaces are needed here, and a sweep over
|
||||
z would otherwise cost 1 GB per slice. The node-set and all-zeros checks that
|
||||
dotproduct.py makes are repeated, because both failures are silent. Pass
|
||||
--dotvtu to read one of the archived dotproduct VTUs instead.
|
||||
|
||||
The value reported per anode is the extremum of E.E_w over the nodes lying on
|
||||
that wire's surface. That is the recipe behind the numbers on slide 21 of the
|
||||
defense: it returns -2.569e8 for the aligned anode at the z = 19.794 crossover
|
||||
and -1.517e8 / -1.486e8 for the two anodes at the z = 0 midpoint, against the
|
||||
-2.57e8, -1.50e8 and 1.7x quoted there.
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
import meshio
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from geometry import wires
|
||||
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("z", type=float)
|
||||
ap.add_argument("--phys", default="wires2d/elfield_anasen_t0001.vtu")
|
||||
ap.add_argument("--weight", default="wires2d/elfield_weight_t0001.vtu")
|
||||
ap.add_argument("--dotvtu", default=None,
|
||||
help="an archived dotproduct VTU, read instead of the two "
|
||||
"field VTUs")
|
||||
ap.add_argument("--cath", type=int, default=1)
|
||||
ap.add_argument("--wire-radius", type=float, default=0.254)
|
||||
ap.add_argument("--band", type=float, default=0.02)
|
||||
ap.add_argument("--csv", default=None)
|
||||
a = ap.parse_args()
|
||||
|
||||
|
||||
def array(m, wanted, path):
|
||||
key = [k for k in m.point_data
|
||||
if "".join(k.lower().split()) == "".join(wanted.lower().split())]
|
||||
if not key:
|
||||
sys.exit("%s: no %r array; has %s" % (path, wanted, sorted(m.point_data)))
|
||||
v = np.asarray(m.point_data[key[0]], dtype=float)
|
||||
if not np.any(v):
|
||||
sys.exit("%s: %s is identically zero -- Elmer applied no boundary "
|
||||
"condition (check the physical-group tags)." % (path, wanted))
|
||||
return v
|
||||
|
||||
|
||||
if a.dotvtu:
|
||||
m = meshio.read(a.dotvtu)
|
||||
pts3, dot = m.points, array(m, "DotProduct", a.dotvtu)
|
||||
else:
|
||||
mp = meshio.read(a.phys)
|
||||
mw = meshio.read(a.weight)
|
||||
if mp.points.shape != mw.points.shape or not np.allclose(
|
||||
mp.points, mw.points, rtol=0.0, atol=1e-12):
|
||||
sys.exit("the two solves were run on different meshes")
|
||||
E = array(mp, "electric field", a.phys)
|
||||
Ew = array(mw, "electric field", a.weight)
|
||||
ncomp = min(E.shape[1], Ew.shape[1])
|
||||
dot = np.einsum("ij,ij->i", E[:, :ncomp], Ew[:, :ncomp])
|
||||
pts3 = mp.points
|
||||
|
||||
pts = pts3[:, :2] * 1000.0 # Elmer works in m; wires are in mm
|
||||
|
||||
(xa, ya), (xc, yc) = wires(a.z)
|
||||
phi_c = np.degrees(np.arctan2(yc[a.cath], xc[a.cath]))
|
||||
|
||||
rows = []
|
||||
for j in range(24):
|
||||
r = np.hypot(pts[:, 0] - xa[j], pts[:, 1] - ya[j])
|
||||
on = np.abs(r - a.wire_radius) < a.band
|
||||
if not np.any(on):
|
||||
continue
|
||||
d = dot[on]
|
||||
daz = (np.degrees(np.arctan2(ya[j], xa[j])) - phi_c + 180.0) % 360.0 - 180.0
|
||||
rows.append((j, daz, int(on.sum()), float(d.min()), float(d.mean()),
|
||||
float(np.hypot(xa[j] - xc[a.cath], ya[j] - yc[a.cath]))))
|
||||
|
||||
rows.sort(key=lambda r: abs(r[1]))
|
||||
print("# z = %.4f mm, %d nodes" % (a.z, len(dot)))
|
||||
print("anode d_az[deg] nodes min mean dist[mm]")
|
||||
for j, daz, n, mn, mean, dist in rows[:7]:
|
||||
print("%4d %9.3f %6d %10.3e %10.3e %8.3f" % (j, daz, n, mn, mean, dist))
|
||||
|
||||
if a.csv:
|
||||
new = not os.path.exists(a.csv)
|
||||
with open(a.csv, "a") as f:
|
||||
if new:
|
||||
f.write("z_mm,anode,d_az_deg,nodes,dot_min,dot_mean,dist_mm\n")
|
||||
for j, daz, n, mn, mean, dist in rows:
|
||||
f.write("%.4f,%d,%.4f,%d,%.6e,%.6e,%.4f\n"
|
||||
% (a.z, j, daz, n, mn, mean, dist))
|
||||
print("appended %d rows to %s" % (len(rows), a.csv))
|
||||
|
|
@ -0,0 +1 @@
|
|||
8.119747066140E-005 8.600000000000E+002 2.195713106041E-010
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
Metadata for SaveScalars file: ./scalars.dat
|
||||
Elmer version: 26.2
|
||||
Elmer compilation date: 2026-10-16
|
||||
Solver input file: wires2d.sif
|
||||
File started at: 2026/10/28 10:08:02
|
||||
|
||||
Variables in columns of matrix:
|
||||
1: res: electric energy
|
||||
2: res: potential difference
|
||||
3: res: effective capacitance
|
||||
|
|
@ -0,0 +1 @@
|
|||
7.297444630138E-012 1.000000000000E+000 1.459488926028E-011
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
Metadata for SaveScalars file: ./scalars_weight.dat
|
||||
Elmer version: 26.2
|
||||
Elmer compilation date: 2026-10-16
|
||||
Solver input file: wires2d_weight.sif
|
||||
File started at: 2026/10/28 10:29:31
|
||||
|
||||
Variables in columns of matrix:
|
||||
1: res: electric energy
|
||||
2: res: potential difference
|
||||
3: res: effective capacitance
|
||||
127
anasen_fem/cfrac_pred_2026-10-28/solve_z44.7820/wires2d.sif
Normal file
127
anasen_fem/cfrac_pred_2026-10-28/solve_z44.7820/wires2d.sif
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
Check Keywords Warn
|
||||
|
||||
Header
|
||||
Mesh DB "." "wires2d"
|
||||
End
|
||||
|
||||
Simulation
|
||||
Coordinate System = Cartesian 2D
|
||||
Simulation Type = Steady State
|
||||
Steady State Max Iterations = 1
|
||||
Output File = "elstatics.result"
|
||||
Post File = "elstatics.ep"
|
||||
Coordinate Scaling = 0.001 ! Converts mm from Gmsh to meters for Elmer
|
||||
End
|
||||
|
||||
Constants
|
||||
Permittivity Of Vacuum = 8.8542e-12
|
||||
End
|
||||
|
||||
|
||||
Body 1
|
||||
Target Bodies(1) = 13
|
||||
Equation = 1
|
||||
Material = 1
|
||||
End
|
||||
|
||||
|
||||
Equation 1
|
||||
Active Solvers(2) = 1 2
|
||||
End
|
||||
|
||||
|
||||
Material 1
|
||||
Relative Permittivity = 1
|
||||
End
|
||||
|
||||
|
||||
Solver 1
|
||||
Equation = Electrostatics
|
||||
Procedure = "StatElecSolve" "StatElecSolver"
|
||||
|
||||
Variable = Potential
|
||||
Variable DOFs = 1
|
||||
|
||||
Calculate Electric Field = True
|
||||
Calculate Electric Flux = False
|
||||
|
||||
Linear System Solver = Iterative
|
||||
Linear System Iterative Method = CG
|
||||
Linear System Max Iterations = 5000
|
||||
Linear System Convergence Tolerance = 1.0e-8
|
||||
Linear System Preconditioning = ILU1
|
||||
Calculate Vectors = Logical True
|
||||
End
|
||||
|
||||
Solver 2
|
||||
Equation = "Electric Field"
|
||||
Procedure = "FluxSolver" "FluxSolver"
|
||||
|
||||
! Calculate from the potential
|
||||
Target Variable = "Potential"
|
||||
! Name of the output vector field in VTU
|
||||
Flux Variable = String "Electric Field"
|
||||
|
||||
! Use 2D components (x, y)
|
||||
Flux Coefficient = String "Permittivity"
|
||||
|
||||
Calculate Vectors = Logical True
|
||||
End
|
||||
|
||||
Solver 3
|
||||
Equation = Result Output
|
||||
Procedure = "ResultOutputSolve" "ResultOutputSolver"
|
||||
Output File Name = elfield_anasen ! Sets prefix for output files
|
||||
Output Format = Vtu
|
||||
! Optional: Select specific variables to save
|
||||
Scalar Field 1 = Potential
|
||||
Vector Field 1 = Electric Field
|
||||
End
|
||||
|
||||
Solver 4
|
||||
Exec Solver = After All
|
||||
Equation = SaveScalars
|
||||
Procedure = "SaveData" "SaveScalars"
|
||||
Filename = "scalars.dat"
|
||||
End
|
||||
|
||||
Boundary Condition 1
|
||||
Target Boundaries = 1
|
||||
Potential = -200
|
||||
Calculate Electric Force = True
|
||||
End
|
||||
|
||||
Boundary Condition 2
|
||||
Target Boundaries = 2
|
||||
Potential = 150
|
||||
Calculate Electric Force = True
|
||||
End
|
||||
|
||||
Boundary Condition 3
|
||||
Target Boundaries = 3
|
||||
Potential = 250
|
||||
Calculate Electric Force = True
|
||||
End
|
||||
|
||||
Boundary Condition 4
|
||||
Target Boundaries = 10
|
||||
Potential = 0
|
||||
End
|
||||
|
||||
Boundary Condition 5
|
||||
Target Boundaries = 20
|
||||
Potential = 660
|
||||
Calculate Electric Force = True
|
||||
End
|
||||
|
||||
Boundary Condition 6
|
||||
Target Boundaries = 30
|
||||
Potential = 0
|
||||
End
|
||||
|
||||
! The one cathode singled out in wires_gmsh2d_bc.py. Grounded here, exactly
|
||||
! like the other 23; it is only separate so wires2d_weight.sif can drive it.
|
||||
Boundary Condition 7
|
||||
Target Boundaries = 40
|
||||
Potential = 0
|
||||
End
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
Check Keywords Warn
|
||||
|
||||
! Ramo weighting potential for the single cathode at tag 40: that electrode
|
||||
! sits at 1 V and every other one at 0 V. Identical to wires2d.sif apart from
|
||||
! the potentials and the output names, so both solves discretise the same way
|
||||
! and E . E_w is meaningful node by node.
|
||||
|
||||
Header
|
||||
Mesh DB "." "wires2d"
|
||||
End
|
||||
|
||||
Simulation
|
||||
Coordinate System = Cartesian 2D
|
||||
Simulation Type = Steady State
|
||||
Steady State Max Iterations = 1
|
||||
Output File = "elstatics_weight.result"
|
||||
Post File = "elstatics_weight.ep"
|
||||
Coordinate Scaling = 0.001 ! Converts mm from Gmsh to meters for Elmer
|
||||
End
|
||||
|
||||
Constants
|
||||
Permittivity Of Vacuum = 8.8542e-12
|
||||
End
|
||||
|
||||
|
||||
Body 1
|
||||
Target Bodies(1) = 13
|
||||
Equation = 1
|
||||
Material = 1
|
||||
End
|
||||
|
||||
|
||||
Equation 1
|
||||
Active Solvers(2) = 1 2
|
||||
End
|
||||
|
||||
|
||||
Material 1
|
||||
Relative Permittivity = 1
|
||||
End
|
||||
|
||||
|
||||
Solver 1
|
||||
Equation = Electrostatics
|
||||
Procedure = "StatElecSolve" "StatElecSolver"
|
||||
|
||||
Variable = Potential
|
||||
Variable DOFs = 1
|
||||
|
||||
Calculate Electric Field = True
|
||||
Calculate Electric Flux = False
|
||||
|
||||
Linear System Solver = Iterative
|
||||
Linear System Iterative Method = CG
|
||||
Linear System Max Iterations = 5000
|
||||
Linear System Convergence Tolerance = 1.0e-8
|
||||
Linear System Preconditioning = ILU1
|
||||
Calculate Vectors = Logical True
|
||||
End
|
||||
|
||||
Solver 2
|
||||
Equation = "Electric Field"
|
||||
Procedure = "FluxSolver" "FluxSolver"
|
||||
|
||||
! Calculate from the potential
|
||||
Target Variable = "Potential"
|
||||
! Name of the output vector field in VTU
|
||||
Flux Variable = String "Electric Field"
|
||||
|
||||
! Use 2D components (x, y)
|
||||
Flux Coefficient = String "Permittivity"
|
||||
|
||||
Calculate Vectors = Logical True
|
||||
End
|
||||
|
||||
Solver 3
|
||||
Equation = Result Output
|
||||
Procedure = "ResultOutputSolve" "ResultOutputSolver"
|
||||
Output File Name = elfield_weight ! Sets prefix for output files
|
||||
Output Format = Vtu
|
||||
! Optional: Select specific variables to save
|
||||
Scalar Field 1 = Potential
|
||||
Vector Field 1 = Electric Field
|
||||
End
|
||||
|
||||
Solver 4
|
||||
Exec Solver = After All
|
||||
Equation = SaveScalars
|
||||
Procedure = "SaveData" "SaveScalars"
|
||||
Filename = "scalars_weight.dat"
|
||||
End
|
||||
|
||||
Boundary Condition 1
|
||||
Target Boundaries = 1
|
||||
Potential = 0
|
||||
Calculate Electric Force = True
|
||||
End
|
||||
|
||||
Boundary Condition 2
|
||||
Target Boundaries = 2
|
||||
Potential = 0
|
||||
Calculate Electric Force = True
|
||||
End
|
||||
|
||||
Boundary Condition 3
|
||||
Target Boundaries = 3
|
||||
Potential = 0
|
||||
Calculate Electric Force = True
|
||||
End
|
||||
|
||||
Boundary Condition 4
|
||||
Target Boundaries = 10
|
||||
Potential = 0
|
||||
End
|
||||
|
||||
Boundary Condition 5
|
||||
Target Boundaries = 20
|
||||
Potential = 0
|
||||
Calculate Electric Force = True
|
||||
End
|
||||
|
||||
Boundary Condition 6
|
||||
Target Boundaries = 30
|
||||
Potential = 0
|
||||
End
|
||||
|
||||
! The one cathode singled out in wires_gmsh2d_bc.py. Grounded here, exactly
|
||||
! like the other 23; it is only separate so wires2d_weight.sif can drive it.
|
||||
Boundary Condition 7
|
||||
Target Boundaries = 40
|
||||
Potential = 1
|
||||
End
|
||||
|
|
@ -0,0 +1,295 @@
|
|||
# This script generates a 2D Gmsh geometry for the ANASEN detector's wire planes, including an optional hot needle and IC wires. The geometry is designed for adaptive meshing, with finer mesh near the wires and coarser mesh in empty space. The script takes a z-locus as a command-line argument to determine the interpolation between the two wire planes.
|
||||
import numpy as np
|
||||
import gmsh, sys
|
||||
|
||||
# --- Configuration Flags ---
|
||||
include_ic_wires = True
|
||||
include_needle = True
|
||||
|
||||
gmsh.initialize()
|
||||
gmsh.model.add("adaptive_mesh")
|
||||
gmsh.option.setNumber("General.NumThreads", 10)
|
||||
# gmsh.option.setNumber("Mesh.Adapt.MaxNumberOfElements", 200000)
|
||||
# gmsh.option.setNumber("Mesh.Adapt.MaxNumberOfNodes", 200000)
|
||||
# gmsh.option.setNumber("Mesh.Adapt.MaxIter",5)
|
||||
# gmsh.option.setNumber("Mesh.MeshSizeMin", 5e-3)
|
||||
# gmsh.option.setNumber("Mesh.MeshSizeMax", 10.0)
|
||||
gmsh.option.setNumber("Geometry.Tolerance", 4e-2)
|
||||
# gmsh.option.setNumber("Mesh.MeshSizeExtendFromBoundary", 0)
|
||||
# Only needed alongside mesh.recombine(), which is off. See the note there.
|
||||
# gmsh.option.setNumber("Mesh.RecombinationAlgorithm", 2)
|
||||
# gmsh.option.setNumber("Mesh.SubdivisionAlgorithm", 1)
|
||||
gmsh.option.setNumber("Mesh.SecondOrderIncomplete", 1)
|
||||
|
||||
lc = 0.04
|
||||
# z_loc = -174.3
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python3 wires_gmsh2d_bc.py <z_locus in mm> [selected_cathode_index]")
|
||||
quit()
|
||||
|
||||
z_loc = float(sys.argv[1])
|
||||
|
||||
# Cathode wire (0..23) given its own physical group, tag 40, so that
|
||||
# wires2d_weight.sif can hold it at 1 V while everything else stays grounded.
|
||||
selected_cathode = int(sys.argv[2]) if len(sys.argv) > 2 else 1
|
||||
|
||||
wireShift = 4.0
|
||||
k = 2 * np.pi / 24.0
|
||||
kg = k/2.0
|
||||
|
||||
# Plane 1 Offsets (-zmax/2)
|
||||
# Anodes: -6*k (base) - 4*k (correction) = -10*k
|
||||
offset_a1 = -6 * k - 4 * k
|
||||
# Cathodes: -6*k (base) + 0.5*k (half-placement)
|
||||
offset_c1 = -6 * k + (k / 2.0)
|
||||
# Guard wires: aligned with cathodes
|
||||
offset_g1 = offset_c1
|
||||
|
||||
# Plane 2 Offsets (+zmax/2) with Twist
|
||||
offset_a2 = offset_a1 + (wireShift * k)
|
||||
offset_c2 = offset_c1 - (wireShift * k)
|
||||
offset_g2 = offset_c2
|
||||
|
||||
# 1 needle, 24 ic1, 24 ic2, 48 guard wires, 24 anodes, 24 cathodes
|
||||
|
||||
# needle at plane 1 at -zmax/2 no rotation
|
||||
xarr_needle = np.array([0])
|
||||
yarr_needle = np.array([0])
|
||||
|
||||
# ic1 wires, plane 1 at -zmax/2 no rotation
|
||||
xarr_i11 = np.array([23 * np.cos(k * i) for i in range(24)])
|
||||
yarr_i11 = np.array([23 * np.sin(k * i) for i in range(24)])
|
||||
|
||||
# ic1 wires, plane 1 at -zmax/2 no rotation
|
||||
xarr_i21 = np.array([23 * np.cos(k * i + k / 2.0) for i in range(24)])
|
||||
yarr_i21 = np.array([23 * np.sin(k * i + k / 2.0) for i in range(24)])
|
||||
|
||||
# --- Coordinate Arrays (Plane 1) ---
|
||||
# Anodes: -k*i (Left-handed twist direction)
|
||||
xarra_1 = np.array([37 * np.cos(-k * i + offset_a1) for i in range(24)])
|
||||
yarra_1 = np.array([37 * np.sin(-k * i + offset_a1) for i in range(24)])
|
||||
|
||||
# Cathodes: +k*i (Right-handed twist direction)
|
||||
xarrc_1 = np.array([42 * np.cos(k * i + offset_c1) for i in range(24)])
|
||||
yarrc_1 = np.array([42 * np.sin(k * i + offset_c1) for i in range(24)])
|
||||
|
||||
# Guard Wires (48 wires, use kg spacing)
|
||||
xarrg_1 = np.array([32 * np.cos(kg * i + offset_g1) for i in range(48)])
|
||||
yarrg_1 = np.array([32 * np.sin(kg * i + offset_g1) for i in range(48)])
|
||||
|
||||
# needle at plane 2 at zmax/2
|
||||
xarr_needle_2 = np.array([0])
|
||||
yarr_needle_2 = np.array([0])
|
||||
|
||||
# #ic1 wires, plane 2 at zmax/2
|
||||
xarr_i12 = np.array([23 * np.cos(k * i) for i in range(24)])
|
||||
yarr_i12 = np.array([23 * np.sin(k * i) for i in range(24)])
|
||||
|
||||
# #ic2 wires, plane 2 at zmax/2
|
||||
xarr_i22 = np.array([23 * np.cos(k * i + k / 2.0) for i in range(24)])
|
||||
yarr_i22 = np.array([23 * np.sin(k * i + k / 2.0) for i in range(24)])
|
||||
|
||||
# --- Coordinate Arrays (Plane 2) ---
|
||||
xarra_2 = np.array([37 * np.cos(-k * i + offset_a2) for i in range(24)])
|
||||
yarra_2 = np.array([37 * np.sin(-k * i + offset_a2) for i in range(24)])
|
||||
|
||||
xarrc_2 = np.array([42 * np.cos(k * i + offset_c2) for i in range(24)])
|
||||
yarrc_2 = np.array([42 * np.sin(k * i + offset_c2) for i in range(24)])
|
||||
|
||||
xarrg_2 = np.array([32 * np.cos(kg * i + offset_g2) for i in range(48)])
|
||||
yarrg_2 = np.array([32 * np.sin(kg * i + offset_g2) for i in range(48)])
|
||||
|
||||
direction_needle_x = xarr_needle_2 - xarr_needle
|
||||
direction_needle_y = yarr_needle_2 - yarr_needle
|
||||
|
||||
direction_ic1_x = xarr_i12 - xarr_i11
|
||||
direction_ic1_y = yarr_i12 - yarr_i11
|
||||
|
||||
direction_ic2_x = xarr_i22 - xarr_i21
|
||||
direction_ic2_y = yarr_i22 - yarr_i21
|
||||
|
||||
direction_guard_x = xarrg_2 - xarrg_1
|
||||
direction_guard_y = yarrg_2 - yarrg_1
|
||||
|
||||
direction_anodes_x = xarra_2 - xarra_1
|
||||
direction_anodes_y = yarra_2 - yarra_1
|
||||
|
||||
direction_cathodes_x = xarrc_2 - xarrc_1
|
||||
direction_cathodes_y = yarrc_2 - yarrc_1
|
||||
|
||||
t = (z_loc + 174.3) / (2 * 174.3) # z=-174.3 is 0, z=+174.3 is 1
|
||||
|
||||
xloc_needle = xarr_needle + t * direction_needle_x
|
||||
yloc_needle = yarr_needle + t * direction_needle_y
|
||||
xloc_i1 = xarr_i11 + t * direction_ic1_x
|
||||
yloc_i1 = yarr_i11 + t * direction_ic1_y
|
||||
xloc_i2 = xarr_i21 + t * direction_ic2_x
|
||||
yloc_i2 = yarr_i21 + t * direction_ic2_y
|
||||
xloc_g = xarrg_1 + t * direction_guard_x
|
||||
yloc_g = yarrg_1 + t * direction_guard_y
|
||||
xloc_a = xarra_1 + t * direction_anodes_x
|
||||
yloc_a = yarra_1 + t * direction_anodes_y
|
||||
xloc_c = xarrc_1 + t * direction_cathodes_x
|
||||
yloc_c = yarrc_1 + t * direction_cathodes_y
|
||||
|
||||
# wire_radius_a = 0.018 #mm
|
||||
# wire_radius_c = 0.0762 #mm
|
||||
# wire_radius_g = 0.0762 #mm
|
||||
wire_radius = 0.254 # mm
|
||||
needle = []
|
||||
ic1_wires = []
|
||||
ic2_wires = []
|
||||
guard_wires = []
|
||||
anode_wires = []
|
||||
cathode_wires = []
|
||||
iw1_tags = [(3, i) for i in range(24)]
|
||||
iw2_tags = [(3, i + 24) for i in range(24)]
|
||||
gw_tags = [(3, i + 48) for i in range(48)]
|
||||
aw_tags = [(3, i) for i in range(24)]
|
||||
cw_tags = [(3, i + 24) for i in range(24)]
|
||||
|
||||
# for i,[xa,ya,xc,yc] in enumerate(zip(xarra_1,yarra_1,xarrc_1,yarrc_1)):
|
||||
# create Hot Needle (1 total)
|
||||
for i, (xn, yn) in enumerate(zip(xloc_needle, yloc_needle)):
|
||||
if include_needle:
|
||||
ndisk = gmsh.model.occ.addDisk(xn, yn, 0, wire_radius, wire_radius)
|
||||
needle.append(ndisk)
|
||||
|
||||
# create Guard Wires (48 total)
|
||||
for xg, yg in zip(xloc_g, yloc_g):
|
||||
gdisk = gmsh.model.occ.addDisk(xg, yg, 0, wire_radius, wire_radius)
|
||||
guard_wires.append(gdisk)
|
||||
|
||||
# create Cathode Wires (24 total)
|
||||
for xc, yc in zip(xloc_c, yloc_c):
|
||||
cdisk = gmsh.model.occ.addDisk(xc, yc, 0, wire_radius, wire_radius)
|
||||
cathode_wires.append(cdisk)
|
||||
|
||||
# create IC Anode and Cathode Wires (24 total each)
|
||||
for i, (xa, ya) in enumerate(zip(xloc_a, yloc_a)):
|
||||
adisk = gmsh.model.occ.addDisk(xa, ya, 0, wire_radius, wire_radius)
|
||||
anode_wires.append(adisk)
|
||||
|
||||
# Place IC wires only if flag is True
|
||||
if include_ic_wires:
|
||||
i1disk = gmsh.model.occ.addDisk(
|
||||
xloc_i1[i], yloc_i1[i], 0, wire_radius, wire_radius
|
||||
)
|
||||
i2disk = gmsh.model.occ.addDisk(
|
||||
xloc_i2[i], yloc_i2[i], 0, wire_radius, wire_radius
|
||||
)
|
||||
ic1_wires.append(i1disk)
|
||||
ic2_wires.append(i2disk)
|
||||
|
||||
anasen_barrel = gmsh.model.occ.addDisk(0, 0, 0, 500, 500)
|
||||
# gmsh.model.occ.synchronize()
|
||||
# gmsh.model.mesh.embed(1,anode_wires+cathode_wires,2,anasen_barrel)
|
||||
|
||||
gmsh.option.setNumber("Geometry.Tolerance", 1e-6)
|
||||
gmsh.option.setNumber("Geometry.OCCFixDegenerated", 1)
|
||||
gmsh.model.occ.synchronize()
|
||||
|
||||
|
||||
# --- Surface Extraction ---
|
||||
def get_surfs(disks):
|
||||
surfs = []
|
||||
for d in disks:
|
||||
surfs += [
|
||||
s[1] for s in gmsh.model.getBoundary([(2, d)], oriented=False) if s[0] == 1
|
||||
]
|
||||
return surfs
|
||||
|
||||
|
||||
if not 0 <= selected_cathode < len(cathode_wires):
|
||||
print("selected_cathode must be 0..%d" % (len(cathode_wires) - 1))
|
||||
quit()
|
||||
|
||||
cathode_wires_other = [
|
||||
disk for i, disk in enumerate(cathode_wires) if i != selected_cathode
|
||||
]
|
||||
|
||||
# Cut the wire disks into the gas disk, replacing the old mesh.embed() call.
|
||||
# embed only makes the mesher honour the curves; fragment splits the barrel
|
||||
# into conforming surfaces, which is what lets one wire carry its own BC.
|
||||
gmsh.model.occ.synchronize()
|
||||
|
||||
all_wire_disks = needle + guard_wires + cathode_wires + anode_wires
|
||||
if include_ic_wires:
|
||||
all_wire_disks += ic1_wires + ic2_wires
|
||||
|
||||
gmsh.model.occ.fragment([(2, anasen_barrel)], [(2, d) for d in all_wire_disks])
|
||||
gmsh.model.occ.synchronize()
|
||||
|
||||
# fragment re-derives the bounding curves, so re-extract them.
|
||||
needle_surfs = get_surfs(needle) if include_needle else []
|
||||
gwire_surfs = get_surfs(guard_wires)
|
||||
awire_surfs = get_surfs(anode_wires)
|
||||
cwire_surfs = get_surfs(cathode_wires_other)
|
||||
cwire_sel_surfs = get_surfs([cathode_wires[selected_cathode]])
|
||||
i1wire_surfs = get_surfs(ic1_wires) if include_ic_wires else []
|
||||
i2wire_surfs = get_surfs(ic2_wires) if include_ic_wires else []
|
||||
|
||||
all_active_wire_surfs = (
|
||||
needle_surfs
|
||||
+ gwire_surfs
|
||||
+ awire_surfs
|
||||
+ cwire_surfs
|
||||
+ cwire_sel_surfs
|
||||
+ i1wire_surfs
|
||||
+ i2wire_surfs
|
||||
)
|
||||
|
||||
f1 = gmsh.model.mesh.field.add("Distance")
|
||||
gmsh.model.mesh.field.setNumbers(f1, "CurvesList", all_active_wire_surfs)
|
||||
|
||||
f2 = gmsh.model.mesh.field.add("Threshold")
|
||||
gmsh.model.mesh.field.setNumber(f2, "InField", f1)
|
||||
gmsh.model.mesh.field.setNumber(f2, "SizeMin", 0.05) # Fine mesh near wires
|
||||
gmsh.model.mesh.field.setNumber(f2, "SizeMax", 5.0) # Large mesh in empty space
|
||||
gmsh.model.mesh.field.setNumber(f2, "DistMin", 0.5) # Apply SizeMin within 1mm
|
||||
gmsh.model.mesh.field.setNumber(f2, "DistMax", 15.0) # Transition to SizeMax by 20mm
|
||||
|
||||
gmsh.model.mesh.field.setAsBackgroundMesh(f2)
|
||||
|
||||
|
||||
# --- Physical Groups ---
|
||||
# These tags are the Target Bodies / Target Boundaries numbers in the .sif
|
||||
# files. Do not renumber them without changing both sifs -- and note that
|
||||
# ElmerGrid's -autoclean renumbers them for you, which silently breaks every
|
||||
# boundary condition.
|
||||
# Needle
|
||||
if include_needle:
|
||||
gmsh.model.addPhysicalGroup(1, needle_surfs, tag=1, name="hot_needle")
|
||||
|
||||
# IC Wires
|
||||
if include_ic_wires:
|
||||
gmsh.model.addPhysicalGroup(1, i1wire_surfs, tag=2, name="ic_wire_1")
|
||||
gmsh.model.addPhysicalGroup(1, i2wire_surfs, tag=3, name="ic_wire_2")
|
||||
|
||||
# Proportional Counter Wires
|
||||
gmsh.model.addPhysicalGroup(1, gwire_surfs, tag=10, name="guard_wires")
|
||||
gmsh.model.addPhysicalGroup(1, awire_surfs, tag=20, name="anode_wires")
|
||||
gmsh.model.addPhysicalGroup(1, cwire_surfs, tag=30, name="cathode_wires")
|
||||
|
||||
# The one cathode the weighting potential is solved for, kept out of tag 30 so
|
||||
# wires2d_weight.sif can drive it independently.
|
||||
gmsh.model.addPhysicalGroup(1, cwire_sel_surfs, tag=40, name="cathode_wire_selected")
|
||||
|
||||
# Gas Volume (2D). fragment split the barrel into many surfaces, so collect
|
||||
# them all; naming the original disk alone would solve one sliver of the domain.
|
||||
all_surfaces_2d = [s[1] for s in gmsh.model.getEntities(dim=2)]
|
||||
gmsh.model.addPhysicalGroup(2, all_surfaces_2d, tag=13, name="gas")
|
||||
|
||||
gmsh.option.setNumber("Mesh.Algorithm", 5)
|
||||
|
||||
gmsh.model.mesh.generate(2)
|
||||
gmsh.model.mesh.setOrder(2)
|
||||
# recombine() ran for over half an hour on the barrel surface without
|
||||
# finishing, and refine() took the mesh to ~16M nodes. Neither is needed: the
|
||||
# Threshold field above already gives 0.05 mm elements on 0.254 mm wires.
|
||||
# gmsh.model.mesh.recombine()
|
||||
# gmsh.model.mesh.refine()
|
||||
gmsh.write("wires2d.msh")
|
||||
# gmsh.fltk.run()
|
||||
gmsh.finalize()
|
||||
2
anasen_fem/clean.sh
Executable file
2
anasen_fem/clean.sh
Executable file
|
|
@ -0,0 +1,2 @@
|
|||
rm -f wires2d.msh
|
||||
rm -rf wires2d/*
|
||||
2
anasen_fem/dielectrics.dat
Normal file
2
anasen_fem/dielectrics.dat
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
1
|
||||
13 1.0
|
||||
148
anasen_fem/dotproduct.py
Normal file
148
anasen_fem/dotproduct.py
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Node-by-node dot product of the physical field with a Ramo weighting field.
|
||||
|
||||
Reads the two Elmer VTUs from wires2d.sif and wires2d_weight.sif and writes one
|
||||
carrying DotProduct = E . E_w. E is in V/m and E_w in 1/m (its electrode is
|
||||
driven to 1 V), so DotProduct is in V/m^2.
|
||||
|
||||
By Shockley-Ramo a charge q drifting at v induces i = -q v . E_w, and for
|
||||
v = mu E that is -q mu (E . E_w). So this map is the induced-current density up
|
||||
to the constant -q*mu; --scale folds it in. The signs of q and mu differ
|
||||
between the electron and ion components, so they are left to the caller.
|
||||
|
||||
python3 dotproduct.py
|
||||
python3 dotproduct.py --scale -1.0e-4
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
try:
|
||||
import meshio
|
||||
except ImportError:
|
||||
sys.exit("meshio is required: pip install meshio")
|
||||
|
||||
|
||||
def find_array(point_data, wanted, label):
|
||||
"""Look up a point-data array, ignoring case and spacing.
|
||||
|
||||
Elmer lowercases variable names into the VTU and the spelling has varied
|
||||
between versions, so match loosely and list what is there on failure.
|
||||
"""
|
||||
def norm(s):
|
||||
return "".join(s.lower().split())
|
||||
|
||||
target = norm(wanted)
|
||||
for key in point_data:
|
||||
if norm(key) == target:
|
||||
return point_data[key]
|
||||
sys.exit(
|
||||
"%s: no point-data array matching %r.\nAvailable arrays: %s"
|
||||
% (label, wanted, sorted(point_data))
|
||||
)
|
||||
|
||||
|
||||
def as_vectors(arr, label):
|
||||
"""Return an (N, ncomp) float array, tolerating flat or 2-component data."""
|
||||
a = np.asarray(arr, dtype=float)
|
||||
if a.ndim == 1:
|
||||
sys.exit("%s: expected a vector field, got a scalar array" % label)
|
||||
if a.ndim != 2:
|
||||
sys.exit("%s: expected a 2D array, got shape %s" % (label, a.shape))
|
||||
return a
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description=__doc__,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
ap.add_argument("--phys", default="wires2d/elfield_anasen_t0001.vtu",
|
||||
help="VTU from the physical solve (wires2d.sif)")
|
||||
ap.add_argument("--weight", default="wires2d/elfield_weight_t0001.vtu",
|
||||
help="VTU from the weighting solve (wires2d_weight.sif)")
|
||||
ap.add_argument("--out", default="wires2d/dotproduct.vtu",
|
||||
help="output VTU")
|
||||
ap.add_argument("--field", default="electric field",
|
||||
help="name of the electric-field array in both inputs")
|
||||
ap.add_argument("--scale", type=float, default=None,
|
||||
help="constant (e.g. -q*mu) multiplying the dot product; "
|
||||
"writes an extra ScaledDotProduct array")
|
||||
ap.add_argument("--rtol", type=float, default=0.0,
|
||||
help="relative tolerance for the node-position check")
|
||||
ap.add_argument("--atol", type=float, default=1e-12,
|
||||
help="absolute tolerance (m) for the node-position check")
|
||||
args = ap.parse_args()
|
||||
|
||||
phys = meshio.read(args.phys)
|
||||
weight = meshio.read(args.weight)
|
||||
|
||||
# Taken node by node, so both solves must have produced the same node list.
|
||||
# If they did not, every value below is meaningless and nothing else checks.
|
||||
if phys.points.shape != weight.points.shape:
|
||||
sys.exit(
|
||||
"Node counts differ: %s has %d nodes, %s has %d. Both solves must "
|
||||
"read the same mesh (same wires2d/ directory)."
|
||||
% (args.phys, len(phys.points), args.weight, len(weight.points))
|
||||
)
|
||||
if not np.allclose(phys.points, weight.points, rtol=args.rtol, atol=args.atol):
|
||||
worst = np.max(np.linalg.norm(phys.points - weight.points, axis=1))
|
||||
sys.exit(
|
||||
"Node positions do not match (largest discrepancy %.3e m). The two "
|
||||
"solves were run on different meshes." % worst
|
||||
)
|
||||
|
||||
E = as_vectors(find_array(phys.point_data, args.field, args.phys), args.phys)
|
||||
Ew = as_vectors(find_array(weight.point_data, args.field, args.weight), args.weight)
|
||||
|
||||
# A zero field means the solve never ran: Laplace with no Dirichlet
|
||||
# condition applied has the trivial solution and Elmer reports no error.
|
||||
# Without this the chain completes and writes zeros, and the only symptom
|
||||
# is a blank plot.
|
||||
for label, arr, path in (
|
||||
("physical", E, args.phys),
|
||||
("weighting", Ew, args.weight),
|
||||
):
|
||||
if not np.any(arr):
|
||||
sys.exit(
|
||||
"the %s field in %s is identically zero.\n"
|
||||
"Elmer returned the trivial solution, which means no boundary "
|
||||
"condition was applied. Compare the Target Bodies and Target "
|
||||
"Boundaries numbers in the .sif against wires2d/mesh.names -- "
|
||||
"ElmerGrid's -autoclean renumbers them." % (label, path)
|
||||
)
|
||||
|
||||
# Elmer may pad 2D vectors to three components; compare only the
|
||||
# components both arrays actually carry.
|
||||
ncomp = min(E.shape[1], Ew.shape[1])
|
||||
if E.shape[1] != Ew.shape[1]:
|
||||
print("note: component counts differ (%d vs %d), using the first %d"
|
||||
% (E.shape[1], Ew.shape[1], ncomp))
|
||||
dot = np.einsum("ij,ij->i", E[:, :ncomp], Ew[:, :ncomp])
|
||||
|
||||
out = meshio.Mesh(
|
||||
points=phys.points,
|
||||
cells=phys.cells,
|
||||
point_data=dict(phys.point_data),
|
||||
cell_data=dict(phys.cell_data),
|
||||
)
|
||||
out.point_data["DotProduct"] = dot
|
||||
out.point_data["WeightingField"] = Ew
|
||||
if args.scale is not None:
|
||||
out.point_data["ScaledDotProduct"] = args.scale * dot
|
||||
|
||||
meshio.write(args.out, out)
|
||||
|
||||
finite = dot[np.isfinite(dot)]
|
||||
print("nodes : %d" % len(dot))
|
||||
print("components used : %d" % ncomp)
|
||||
print("DotProduct min : %.6e V/m^2" % finite.min())
|
||||
print("DotProduct max : %.6e V/m^2" % finite.max())
|
||||
print("DotProduct mean : %.6e V/m^2" % finite.mean())
|
||||
if len(finite) != len(dot):
|
||||
print("WARNING: %d non-finite nodes" % (len(dot) - len(finite)))
|
||||
print("wrote %s" % args.out)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
107
anasen_fem/garfield_sim.py
Normal file
107
anasen_fem/garfield_sim.py
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
import ROOT
|
||||
import os
|
||||
import sys
|
||||
|
||||
# 1. FIX: Manually load the Garfield library if it's not in the ROOT namespace
|
||||
# Update this path to your actual installation location
|
||||
garfield_lib_path = "/home/vs19g/garfieldpp/install/lib/libGarfield.so" #penguin path
|
||||
# garfield_lib_path = "/home/vsitaraman/garfieldpp/install/lib/libGarfield.so" #laptop path
|
||||
|
||||
if os.path.exists(garfield_lib_path):
|
||||
ROOT.gSystem.Load(garfield_lib_path)
|
||||
else:
|
||||
print(f"CRITICAL ERROR: {garfield_lib_path} not found.")
|
||||
sys.exit(1)
|
||||
|
||||
# Verify access
|
||||
try:
|
||||
test_gas = ROOT.Garfield.MediumMagboltz()
|
||||
except AttributeError:
|
||||
print("ERROR: Garfield shared library loaded, but 'Garfield' namespace not found.")
|
||||
sys.exit(1)
|
||||
|
||||
# --- 2. GAS SETUP (96% He, 4% CO2) ---
|
||||
gas = ROOT.Garfield.MediumMagboltz()
|
||||
gas_file = "He96_CO2_4_260Torr.gas"
|
||||
|
||||
if not os.path.exists(gas_file):
|
||||
print("Generating new Magboltz gas table (260 Torr)...")
|
||||
# --- Optimized Magboltz Settings ---
|
||||
gas.SetComposition("he", 96.0, "co2", 4.0)
|
||||
gas.SetTemperature(293.15)
|
||||
gas.SetPressure(260.0)
|
||||
|
||||
# 1. Limit the Field Grid:
|
||||
gas.SetFieldGrid(10., 80000., 20, True)
|
||||
|
||||
# 2. Reduce the precision slightly for the first run:
|
||||
gas.GenerateGasTable(8)
|
||||
gas.WriteGasFile(gas_file)
|
||||
else:
|
||||
print(f"Loading existing gas table: {gas_file}")
|
||||
gas.LoadGasFile(gas_file)
|
||||
|
||||
# --- 3. FIELD MAP SETUP ---
|
||||
fm = ROOT.Garfield.ComponentElmer2d()
|
||||
|
||||
|
||||
fm.Initialise("wires2d/mesh.header",
|
||||
"wires2d/mesh.elements",
|
||||
"wires2d/mesh.nodes",
|
||||
"dielectrics.dat",
|
||||
"wires2d/elstatics.result",
|
||||
"mm")
|
||||
|
||||
# Set the medium (Body 13 from your Gmsh script)
|
||||
fm.SetMedium(0, gas)
|
||||
|
||||
# --- 4. SENSOR AND DRIFT SETUP ---
|
||||
sensor = ROOT.Garfield.Sensor()
|
||||
sensor.AddComponent(fm)
|
||||
sensor.SetArea(-50.0, -50.0, -5.0, 50.0, 50.0, 5.0) #hardcoding the sesnsor area to define a psuedo 3d geometry
|
||||
|
||||
# Heavy Ion Drift (RKF) - Best for the general track
|
||||
drift = ROOT.Garfield.DriftLineRKF()
|
||||
drift.SetSensor(sensor)
|
||||
|
||||
# Electron Avalanche (Microscopic) - Best for high-field gain
|
||||
aval = ROOT.Garfield.AvalancheMicroscopic()
|
||||
aval.SetSensor(sensor)
|
||||
|
||||
# --- 5. EXECUTION ---
|
||||
# Starting position (e.g., near the IC wires at r=23mm or closer to Anodes)
|
||||
x0, y0, z0, t0 = 3.50, 0.0, 0.0, 0.0
|
||||
|
||||
print(f"Simulating heavy ion drift from r={x0}...")
|
||||
drift.DriftIon(x0, y0, z0, t0)
|
||||
|
||||
# Create a file to store the heavy ion track
|
||||
with open("heavy_ion_track.csv", "w") as f:
|
||||
f.write("x,y,z,t\n")
|
||||
|
||||
# After running drift.DriftIon(x0, y0, z0, t0):
|
||||
n_points = drift.GetNumberOfDriftLinePoints()
|
||||
for i in range(n_points):
|
||||
xi, yi, zi, ti = ROOT.double(0), ROOT.double(0), ROOT.double(0), ROOT.double(0)
|
||||
drift.GetDriftLinePoint(i, xi, yi, zi, ti)
|
||||
f.write(f"{xi},{yi},{zi},{ti}\n")
|
||||
|
||||
print(f"Simulating electron avalanche from r={x0}...")
|
||||
# AvalancheElectron(x, y, z, t, energy, dx, dy, dz)
|
||||
aval.AvalancheElectron(x0, y0, z0, t0, 0.1, 0.0, 0.0, 0.0)
|
||||
|
||||
with open("avalanche_endpoints.csv", "w") as f:
|
||||
f.write("x,y,z,t\n")
|
||||
|
||||
# After aval.AvalancheElectron(...)
|
||||
n_endpoints = aval.GetNumberOfEndpoints()
|
||||
for i in range(n_endpoints):
|
||||
# Get start and end points of each electron in the avalanche
|
||||
x1, y1, z1, t1, e1 = ROOT.double(0), ROOT.double(0), ROOT.double(0), ROOT.double(0), ROOT.double(0)
|
||||
x2, y2, z2, t2, e2, status = ROOT.double(0), ROOT.double(0), ROOT.double(0), ROOT.double(0), ROOT.double(0), ROOT.int(0)
|
||||
|
||||
aval.GetEndpoint(i, x1, y1, z1, t1, e1, x2, y2, z2, t2, e2, status)
|
||||
# We save the endpoint (x2, y2, z2) where the electron was collected or attached
|
||||
f.write(f"{x2},{y2},{z2},{t2}\n")
|
||||
|
||||
print("Simulation complete.")
|
||||
114
anasen_fem/junk/wires.py
Executable file
114
anasen_fem/junk/wires.py
Executable file
|
|
@ -0,0 +1,114 @@
|
|||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from mpl_toolkits.mplot3d import Axes3D
|
||||
|
||||
acolors = plt.get_cmap('tab20',24)
|
||||
ccolors = plt.get_cmap('tab20',24)
|
||||
|
||||
k=-2*np.pi/24.
|
||||
offset = 6*k + 3*k #-pi/2
|
||||
xarra_1 = np.array([37*np.cos(k*i+offset) for i in np.arange(0,24)])
|
||||
yarra_1 = np.array([37*np.sin(k*i+offset) for i in np.arange(0,24)])
|
||||
labelsa_1 = np.array([i for i in np.arange(0,24)])
|
||||
|
||||
fig,ax = plt.subplots(figsize=(10,10))
|
||||
ax.invert_yaxis()
|
||||
ax.plot(xarra_1,yarra_1,"x",label="anode, z=-L/2")
|
||||
|
||||
for x,y,label in zip(xarra_1,yarra_1,labelsa_1):
|
||||
ax.text(x,y,label)
|
||||
|
||||
kc=2*np.pi/24.
|
||||
offsetc = -4*kc + 2*kc - np.pi/24 #-pi/4
|
||||
xarrc_1 = np.array([42*np.cos(kc*i+offsetc) for i in np.arange(0,24)])
|
||||
yarrc_1 = np.array([42*np.sin(kc*i+offsetc) for i in np.arange(0,24)])
|
||||
labelsc_1 = np.array([i for i in np.arange(0,24)])
|
||||
|
||||
ax.plot(xarrc_1,yarrc_1,"o",label="cathode, z=-L/2, where they are picked up")
|
||||
|
||||
for x,y,label in zip(xarrc_1,yarrc_1,labelsc_1):
|
||||
ax.text(x,y,label)
|
||||
plt.title("z=-L/2 plane, beam going into the plane along +z, (+x right, +y down)")
|
||||
plt.grid()
|
||||
plt.legend()
|
||||
plt.savefig("plane1.png")
|
||||
plt.show()
|
||||
|
||||
fig,ax = plt.subplots(figsize=(10,10))
|
||||
ax.invert_yaxis()
|
||||
|
||||
offset = offset-3*k
|
||||
xarra_2 = np.array([37*np.cos(k*i+offset) for i in np.arange(0,24)])
|
||||
yarra_2 = np.array([37*np.sin(k*i+offset) for i in np.arange(0,24)])
|
||||
labelsa_2 = np.array([i for i in np.arange(0,24)])
|
||||
|
||||
ax.plot(xarra_2,yarra_2,"x",label="anode, z=+L/2, where they are picked up")
|
||||
|
||||
for x,y,label in zip(xarra_2,yarra_2,labelsa_2):
|
||||
ax.text(x,y,label)
|
||||
|
||||
offsetc = offsetc-3*kc
|
||||
xarrc_2 = np.array([42*np.cos(kc*i+offsetc) for i in np.arange(0,24)])
|
||||
yarrc_2 = np.array([42*np.sin(kc*i+offsetc) for i in np.arange(0,24)])
|
||||
labelsc_2 = np.array([i for i in np.arange(0,24)])
|
||||
|
||||
ax.plot(xarrc_2,yarrc_2,"o",label="cathode, z=+L/2")
|
||||
|
||||
for x,y,label in zip(xarrc_2,yarrc_2,labelsc_2):
|
||||
ax.text(x,y,label)
|
||||
plt.title("z=+L/2 plane, beam going into the plane along +z, (+x right, +y down)")
|
||||
plt.grid()
|
||||
plt.legend()
|
||||
plt.savefig("plane2.png")
|
||||
plt.show()
|
||||
|
||||
fig = plt.figure(figsize=(10,10))
|
||||
ax = fig.add_subplot(111,projection='3d')
|
||||
ax.set_xlabel("x")
|
||||
ax.set_ylabel("y")
|
||||
ax.set_zlabel("z")
|
||||
|
||||
xx_a = np.array([[x1,x2] for x1,x2 in zip(xarra_1,xarra_2)])
|
||||
yy_a = np.array([[y1,y2] for y1,y2 in zip(yarra_1,yarra_2)])
|
||||
zz_a = np.array([[-173.5,173.5] for x1,x2 in zip(xarra_1,xarra_2)])
|
||||
for i,[xx,yy,zz] in enumerate(zip(xx_a,yy_a,zz_a)):
|
||||
ax.plot(xx,yy,zz,'-',color=acolors(i/24))
|
||||
|
||||
for i,[x,y,label] in enumerate(zip(xarra_1,yarra_1,labelsa_1)):
|
||||
ax.text(x,y,-173,"a"+str(label),color=acolors(i/24))
|
||||
for i,[x,y,label] in enumerate(zip(xarra_2,yarra_2,labelsa_2)):
|
||||
ax.text(x,y,+173,"a"+str(label),color=acolors(i/24))
|
||||
|
||||
xx_c = np.array([[x1,x2] for x1,x2 in zip(xarrc_1,xarrc_2)])
|
||||
yy_c = np.array([[y1,y2] for y1,y2 in zip(yarrc_1,yarrc_2)])
|
||||
zz_c = np.array([[-173.5,173.5] for x1,x2 in zip(xarrc_1,xarrc_2)])
|
||||
for i,[xx,yy,zz] in enumerate(zip(xx_c,yy_c,zz_c)):
|
||||
ax.plot(xx,yy,zz,'--',color=ccolors(((47-i)%24)/24))
|
||||
|
||||
for i,[x,y,label] in enumerate(zip(xarrc_1,yarrc_1,labelsc_1)):
|
||||
ax.text(x,y,-173,"c"+str(label),color=ccolors(((25-i)%24)/24))
|
||||
for i,[x,y,label] in enumerate(zip(xarrc_2,yarrc_2,labelsc_2)):
|
||||
ax.text(x,y,+173,"c"+str(label),color=ccolors(((47-i)%24)/24))
|
||||
ax.view_init(elev=-53, azim=-106, roll=18)
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
phi_qqq = np.array([[2*np.pi*(-i*16+j+0.5)/(16*4) for i in range(4)] for j in range(16)])
|
||||
print(phi_qqq)
|
||||
#'''
|
||||
for i,[phi1,phi2,phi3,phi4] in enumerate(phi_qqq):
|
||||
ax.plot([50*np.cos(phi1),100*np.cos(phi1)],[50*np.sin(phi1),100*np.sin(phi1)],[100,100],'-',color='red')
|
||||
ax.text(104*np.cos(phi1),104*np.sin(phi1),100,"0_%d"%(i),color="red")
|
||||
|
||||
ax.plot([50*np.cos(phi2),100*np.cos(phi2)],[50*np.sin(phi2),100*np.sin(phi2)],[100,100],'-',color='green')
|
||||
ax.text(104*np.cos(phi2),104*np.sin(phi2),100,"1_%d"%(i),color="green")
|
||||
|
||||
ax.plot([50*np.cos(phi3),100*np.cos(phi3)],[50*np.sin(phi3),100*np.sin(phi3)],[100,100],'-',color='blue')
|
||||
ax.text(104*np.cos(phi3),104*np.sin(phi3),100,"2_%d"%(i),color="blue")
|
||||
|
||||
ax.plot([50*np.cos(phi4),100*np.cos(phi4)],[50*np.sin(phi4),100*np.sin(phi4)],[100,100],'-',color='brown')
|
||||
ax.text(104*np.cos(phi4),104*np.sin(phi4),100,"3_%d"%(i),color="brown")
|
||||
#'''
|
||||
#coords_qqq = np.array([[50*np.cos(phi),100*np.sin(phi),100] for phi in phi_qqq]).T
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
|
||||
71
anasen_fem/junk/wires2d_test.sif
Executable file
71
anasen_fem/junk/wires2d_test.sif
Executable file
|
|
@ -0,0 +1,71 @@
|
|||
Check Keywords Warn
|
||||
|
||||
Header
|
||||
Mesh DB "." "wires2d"
|
||||
End
|
||||
|
||||
Simulation
|
||||
Coordinate System = Cartesian 2D
|
||||
Simulation Type = Steady State
|
||||
Steady State Max Iterations = 1
|
||||
Output File = "elstatics.result"
|
||||
Post File = "elstatics.ep"
|
||||
End
|
||||
|
||||
Constants
|
||||
Permittivity Of Vacuum = 8.8542e-12
|
||||
End
|
||||
|
||||
|
||||
Body 1
|
||||
Target Bodies(1) = 13
|
||||
Equation = 1
|
||||
Material = 1
|
||||
End
|
||||
|
||||
|
||||
Equation 1
|
||||
Active Solvers(2) = 1 2
|
||||
End
|
||||
|
||||
|
||||
Material 1
|
||||
Relative Permittivity = 1
|
||||
End
|
||||
|
||||
|
||||
Solver 1
|
||||
Equation = Electrostatics
|
||||
Procedure = "StatElecSolve" "StatElecSolver"
|
||||
|
||||
Variable = Potential
|
||||
Variable DOFs = 1
|
||||
|
||||
Calculate Electric Field = True
|
||||
Calculate Electric Flux = False
|
||||
|
||||
Linear System Solver = Iterative
|
||||
Linear System Iterative Method = CG
|
||||
Linear System Max Iterations = 500
|
||||
Linear System Convergence Tolerance = 1.0e-8
|
||||
Linear System Preconditioning = ILU1
|
||||
End
|
||||
|
||||
|
||||
Solver 2
|
||||
Equation = Electric Force
|
||||
Procedure = "ElectricForce" "StatElecForce"
|
||||
End
|
||||
|
||||
|
||||
Boundary Condition 1
|
||||
Target Boundaries = 10
|
||||
Potential = 650
|
||||
Calculate Electric Force = True
|
||||
End
|
||||
|
||||
|
||||
Boundary Condition 2
|
||||
Target Boundaries = 20
|
||||
Potential = 0
|
||||
End
|
||||
91
anasen_fem/junk/wires_gmsh.py
Executable file
91
anasen_fem/junk/wires_gmsh.py
Executable file
|
|
@ -0,0 +1,91 @@
|
|||
import numpy as np
|
||||
import gmsh
|
||||
|
||||
gmsh.initialize()
|
||||
gmsh.model.add("adaptive_mesh")
|
||||
gmsh.option.setNumber('General.NumThreads', 4)
|
||||
#gmsh.option.setNumber("Mesh.Adapt.MaxNumberOfElements", 200000)
|
||||
#gmsh.option.setNumber("Mesh.Adapt.MaxNumberOfNodes", 200000)
|
||||
#gmsh.option.setNumber("Mesh.Adapt.MaxIter",5)
|
||||
#gmsh.option.setNumber("Mesh.MeshSizeMin", 5e-3)
|
||||
#gmsh.option.setNumber("Mesh.MeshSizeMax", 10.0)
|
||||
#gmsh.option.setNumber("Mesh.CharacteristicLengthFromCurvature", 0)
|
||||
|
||||
lc = 0.01
|
||||
|
||||
#anodes, plane 1 at -zmax/2
|
||||
k=-2*np.pi/24.
|
||||
offset = 6*k + 3*k #-pi/2
|
||||
xarra_1 = np.array([37*np.cos(k*i+offset) for i in np.arange(0,24)])
|
||||
yarra_1 = np.array([37*np.sin(k*i+offset) for i in np.arange(0,24)])
|
||||
|
||||
#cathodes, plane 1 at -zmax/2
|
||||
kc=2*np.pi/24.
|
||||
offsetc = -4*kc + 2*kc - np.pi/24 #-pi/4
|
||||
xarrc_1 = np.array([42*np.cos(kc*i+offsetc) for i in np.arange(0,24)])
|
||||
yarrc_1 = np.array([42*np.sin(kc*i+offsetc) for i in np.arange(0,24)])
|
||||
|
||||
#anodes, plane 2 at +zmax/2
|
||||
offset = offset-3*k
|
||||
xarra_2 = np.array([37*np.cos(k*i+offset) for i in np.arange(0,24)])
|
||||
yarra_2 = np.array([37*np.sin(k*i+offset) for i in np.arange(0,24)])
|
||||
|
||||
#cathodes, plane2 at +zmax/2
|
||||
offsetc = offsetc-3*kc
|
||||
xarrc_2 = np.array([42*np.cos(kc*i+offsetc) for i in np.arange(0,24)])
|
||||
yarrc_2 = np.array([42*np.sin(kc*i+offsetc) for i in np.arange(0,24)])
|
||||
|
||||
pa1 = []
|
||||
pa2 = []
|
||||
pc1 = []
|
||||
pc2 = []
|
||||
|
||||
wire_radius = 0.254 #mm
|
||||
anode_wires = []
|
||||
cathode_wires = []
|
||||
aw_tags = [(3,i) for i in range(24)]
|
||||
cw_tags = [(3,i+24) for i in range(24)]
|
||||
|
||||
for i,[xa,ya,xc,yc,xa2,ya2,xc2,yc2] in enumerate(zip(xarra_1,yarra_1,xarrc_1,yarrc_1,xarra_2,yarra_2,xarrc_2,yarrc_2)):
|
||||
print(i,xa,ya,-178.3,xc,yc,-178.3,xa2,ya2,178.3,xc2,yc2,178.3)
|
||||
anode_wires.append(gmsh.model.occ.addCylinder(xa,ya,-178.3,(xa2-xa),(ya2-ya),178.3*2,wire_radius,i)) #x,y,z of first face center, dx,dy,dz of the axis, then the wire radius
|
||||
cathode_wires.append(gmsh.model.occ.addCylinder(xc,yc,-178.3,(xc2-xc),(yc2-yc),178.3*2,wire_radius,i+24)) #cathode tags 24-47, anode 0-23
|
||||
|
||||
|
||||
anasen_barrel = gmsh.model.occ.addCylinder(0,0,-500,0,0,500+605,300,1234) #tag 1234
|
||||
#anasen_barrel = gmsh.model.occ.addCylinder(0,0,-500,0,0,500+605,300,1234) #tag 1234
|
||||
|
||||
gmsh.model.occ.synchronize()
|
||||
|
||||
all_wires = aw_tags+cw_tags
|
||||
gmsh.model.occ.fragment([(3,1234)],all_wires)
|
||||
gmsh.model.occ.removeAllDuplicates()
|
||||
gmsh.model.occ.synchronize()
|
||||
gmsh.option.setNumber("Geometry.Tolerance", 1e-6)
|
||||
gmsh.option.setNumber("Geometry.OCCFixDegenerated", 1)
|
||||
gmsh.option.setNumber("Geometry.OCCFixSmallEdges", 1)
|
||||
gmsh.option.setNumber("Geometry.OCCFixSmallFaces", 1)
|
||||
|
||||
wire_surfs = []
|
||||
for w in anode_wires + cathode_wires:
|
||||
wire_surfs += [s[1] for s in gmsh.model.getBoundary([(3,w)], oriented=False) if s[0] == 2]
|
||||
#'''
|
||||
f1 = gmsh.model.mesh.field.add("Distance")
|
||||
gmsh.model.mesh.field.setNumbers(f1, "FacesList", wire_surfs) # Example curves
|
||||
f2 = gmsh.model.mesh.field.add("Threshold")
|
||||
gmsh.model.mesh.field.setNumber(f2, "InField", f1)
|
||||
gmsh.model.mesh.field.setNumber(f2, "SizeMin", 0.05)
|
||||
gmsh.model.mesh.field.setNumber(f2, "SizeMax", 5.)
|
||||
gmsh.model.mesh.field.setNumber(f2, "DistMin", 1.)
|
||||
gmsh.model.mesh.field.setNumber(f2, "DistMax", 20.)
|
||||
gmsh.model.mesh.field.setAsBackgroundMesh(f2)
|
||||
#'''
|
||||
gmsh.option.setNumber("Mesh.Algorithm", 2)
|
||||
gmsh.option.setNumber("Mesh.Algorithm3D", 1) # For 3D meshes
|
||||
|
||||
gmsh.model.mesh.generate(dim=3)
|
||||
#gmsh.model.mesh.refine()
|
||||
#gmsh.model.mesh.refine()
|
||||
#gmsh.model.mesh.refine()
|
||||
gmsh.fltk.run()
|
||||
gmsh.finalize()
|
||||
87
anasen_fem/junk/wires_gmsh_bc.py
Executable file
87
anasen_fem/junk/wires_gmsh_bc.py
Executable file
|
|
@ -0,0 +1,87 @@
|
|||
import numpy as np
|
||||
import gmsh
|
||||
|
||||
gmsh.initialize()
|
||||
#gmsh.model.add("adaptive_mesh")
|
||||
gmsh.option.setNumber('General.NumThreads', 4)
|
||||
#gmsh.option.setNumber("Mesh.Adapt.MaxNumberOfElements", 200000)
|
||||
#gmsh.option.setNumber("Mesh.Adapt.MaxNumberOfNodes", 200000)
|
||||
#gmsh.option.setNumber("Mesh.Adapt.MaxIter",5)
|
||||
#gmsh.option.setNumber("Mesh.MeshSizeMin", 5e-3)
|
||||
#gmsh.option.setNumber("Mesh.MeshSizeMax", 10.0)
|
||||
gmsh.option.setNumber("Geometry.Tolerance", 1e-2)
|
||||
#gmsh.option.setNumber("Mesh.CharacteristicLengthFromCurvature", 0)
|
||||
|
||||
lc = 0.04
|
||||
#anodes, plane 1 at -zmax/2
|
||||
k=-2*np.pi/24.
|
||||
offset = 6*k + 3*k #-pi/2
|
||||
xarra_1 = np.array([37*np.cos(k*i+offset) for i in np.arange(0,24)])
|
||||
yarra_1 = np.array([37*np.sin(k*i+offset) for i in np.arange(0,24)])
|
||||
|
||||
#cathodes, plane 1 at -zmax/2
|
||||
kc=2*np.pi/24.
|
||||
offsetc = -4*kc + 2*kc - np.pi/24 #-pi/4
|
||||
xarrc_1 = np.array([42*np.cos(kc*i+offsetc) for i in np.arange(0,24)])
|
||||
yarrc_1 = np.array([42*np.sin(kc*i+offsetc) for i in np.arange(0,24)])
|
||||
|
||||
#anodes, plane 2 at +zmax/2
|
||||
offset = offset-3*k
|
||||
xarra_2 = np.array([37*np.cos(k*i+offset) for i in np.arange(0,24)])
|
||||
yarra_2 = np.array([37*np.sin(k*i+offset) for i in np.arange(0,24)])
|
||||
|
||||
#cathodes, plane2 at +zmax/2
|
||||
offsetc = offsetc-3*kc
|
||||
xarrc_2 = np.array([42*np.cos(kc*i+offsetc) for i in np.arange(0,24)])
|
||||
yarrc_2 = np.array([42*np.sin(kc*i+offsetc) for i in np.arange(0,24)])
|
||||
|
||||
wire_radius = 0.254 #mm
|
||||
anode_wires = []
|
||||
cathode_wires = []
|
||||
aw_tags = [(3,i) for i in range(24)]
|
||||
cw_tags = [(3,i+24) for i in range(24)]
|
||||
|
||||
for i,[xa,ya,xc,yc,xa2,ya2,xc2,yc2] in enumerate(zip(xarra_1,yarra_1,xarrc_1,yarrc_1,xarra_2,yarra_2,xarrc_2,yarrc_2)):
|
||||
print(i,xa,ya,-178.3,xc,yc,-178.3,xa2,ya2,178.3,xc2,yc2,178.3)
|
||||
pa1 = gmsh.model.occ.addPoint(xa,ya,-178.3,lc)
|
||||
pa2 = gmsh.model.occ.addPoint(xa2,ya2,178.3,lc)
|
||||
pc1 = gmsh.model.occ.addPoint(xc,yc,-178.3,lc)
|
||||
pc2 = gmsh.model.occ.addPoint(xc2,yc2,178.3,lc)
|
||||
linea = gmsh.model.occ.addLine(pa1,pa2)
|
||||
linec = gmsh.model.occ.addLine(pc1,pc2)
|
||||
anode_wires.append(linea)
|
||||
cathode_wires.append(linec)
|
||||
|
||||
#anasen_barrel = gmsh.model.occ.addCylinder(0,0,-500,0,0,500+605,300,1234) #tag 1234
|
||||
anasen_barrel = gmsh.model.occ.addCylinder(0,0,-200, 0,0,400, 300) #tag 1234
|
||||
|
||||
gmsh.model.occ.synchronize()
|
||||
gmsh.model.mesh.embed(1,anode_wires+cathode_wires,3,anasen_barrel)
|
||||
|
||||
f1 = gmsh.model.mesh.field.add("Distance")
|
||||
gmsh.model.mesh.field.setNumbers(f1,"CurvesList",anode_wires+cathode_wires)
|
||||
|
||||
f2 = gmsh.model.mesh.field.add("Threshold")
|
||||
gmsh.model.mesh.field.setNumber(f2,"InField",f1)
|
||||
gmsh.model.mesh.field.setNumber(f2,"SizeMin",0.08)
|
||||
gmsh.model.mesh.field.setNumber(f2,"SizeMax",5)
|
||||
gmsh.model.mesh.field.setNumber(f2,"DistMin",1)
|
||||
gmsh.model.mesh.field.setNumber(f2,"DistMax",20)
|
||||
|
||||
gmsh.model.mesh.field.setAsBackgroundMesh(f2)
|
||||
|
||||
gmsh.model.addPhysicalGroup(1, anode_wires, tag=10)
|
||||
gmsh.model.setPhysicalName(1,10,"anode_wires")
|
||||
|
||||
gmsh.model.addPhysicalGroup(1, cathode_wires, tag=20)
|
||||
gmsh.model.setPhysicalName(1,20,"cathode_wires")
|
||||
|
||||
gmsh.option.setNumber("Mesh.Algorithm",6)
|
||||
gmsh.option.setNumber("Mesh.Algorithm3D", 10) # For 3D meshes
|
||||
|
||||
gmsh.model.mesh.generate(dim=3)
|
||||
gmsh.model.mesh.refine()
|
||||
#gmsh.model.mesh.refine()
|
||||
#gmsh.model.mesh.refine()
|
||||
gmsh.fltk.run()
|
||||
gmsh.finalize()
|
||||
279
anasen_fem/paraview_dotproduct.py
Executable file
279
anasen_fem/paraview_dotproduct.py
Executable file
|
|
@ -0,0 +1,279 @@
|
|||
#!/home/vsitaraman/ParaView-6.1.0-RC1-MPI-Linux-Python3.12-x86_64/bin/pvbatch
|
||||
# Point the shebang above at your own pvbatch/pvpython, as for paraview_plotter.py.
|
||||
#
|
||||
# Renders the E . E_w map written by dotproduct.py.
|
||||
#
|
||||
# By Shockley-Ramo the dot product describes the current induced on ONE
|
||||
# electrode: the cathode that wires2d_weight.sif held at 1 V. Everywhere else
|
||||
# the number is just the tail of that electrode's weighting field, so the view
|
||||
# is centred on that wire and nothing else. Its position is read out of the
|
||||
# weighting solve rather than recomputed from the geometry -- the driven
|
||||
# electrode is wherever the weighting potential reaches 1.
|
||||
#
|
||||
# Usage (from anasen_fem/):
|
||||
# ./paraview_dotproduct.py [dotproduct.vtu] [output.png] [weight.vtu]
|
||||
#
|
||||
# Re-plotting an archived z needs its archived weighting solve as well, since
|
||||
# that is where the driven cathode's position is read from:
|
||||
# ./paraview_dotproduct.py wires2d/vtu_files/dotproduct_10_0.0000.vtu \
|
||||
# png/DotProduct_output_z_10_0.0000.png \
|
||||
# wires2d/vtu_files/elfield_weight_10_0.0000.vtu
|
||||
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
from paraview.simple import *
|
||||
|
||||
INPUT_VTU = sys.argv[1] if len(sys.argv) > 1 else "wires2d/dotproduct.vtu"
|
||||
OUTPUT_IMAGE = sys.argv[2] if len(sys.argv) > 2 else "DotProduct_output.png"
|
||||
|
||||
# Weighting solve, used only to locate the driven cathode. Must be the one that
|
||||
# belongs to this dot product -- the wire moves with z.
|
||||
WEIGHT_VTU = sys.argv[3] if len(sys.argv) > 3 else "wires2d/elfield_weight_t0001.vtu"
|
||||
|
||||
# Half-height of the view in metres. Cathodes sit every 15 degrees at a radius
|
||||
# of about 36.4 mm, so the neighbours either side are 9.5 mm away; 14 mm takes
|
||||
# them both in with a margin.
|
||||
# Image size. The structure round the driven cathode is much wider than it is
|
||||
# tall, so a square frame spends most of its area on empty gas.
|
||||
VIEW_SIZE = [2000, 1200]
|
||||
|
||||
# CameraParallelScale, which is the half-HEIGHT of the view in metres. The
|
||||
# width follows from the aspect: half-height * (VIEW_SIZE[0] / VIEW_SIZE[1]).
|
||||
# So widening the frame alone does not crop anything, it just shows more
|
||||
# sideways; the scale has to come down with it. 0.0084 at 2000x1200 keeps the
|
||||
# same 28 mm width as the old square 0.014 and trims the height to 16.8 mm.
|
||||
ZOOM_SCALE = 0.0084
|
||||
|
||||
# The driven cathode swings round the axis as z changes, so with a fixed "up"
|
||||
# every z comes out rotated by a different amount and the shots cannot be laid
|
||||
# side by side. Aligning up with the outward radial direction through the
|
||||
# cathode puts the detector axis below and the barrel wall above in every
|
||||
# frame, whatever z. Set False for plain screen-aligned axes.
|
||||
ALIGN_RADIAL = True
|
||||
|
||||
# Shift of the view centre along the outward radial, in metres. Up in the frame
|
||||
# is the outward radial, so a NEGATIVE value walks the camera in towards the
|
||||
# detector axis and the cathode rides higher in the picture. Positive drops it.
|
||||
# Purely framing; it does not move the wire.
|
||||
CENTER_SHIFT = -0.0036
|
||||
|
||||
# Second, wider shot over the same quadrant paraview_plotter.py frames, so the
|
||||
# dot product can be laid beside the field and contour views of that z. Centre
|
||||
# is at x < 0, y > 0; negate the y for the lower-left quadrant instead.
|
||||
|
||||
# Colour range for DotProduct. Both ends negative: E and E_w point in broadly
|
||||
# opposite senses over most of the drift volume.
|
||||
# Most of the painted patch lies between -2e7 and -2e6. Against a range
|
||||
# reaching -3.1e8 that is the top few percent of the scale, so it all came out
|
||||
# at the white end of Greens. Narrowing the range spreads those values over the
|
||||
# green; anything stronger simply clamps to the darkest end, which only affects
|
||||
# the last fraction of a millimetre at the wire surface.
|
||||
DOT_MIN, DOT_MAX = -8.0e7, -3.0e6
|
||||
|
||||
# Opacity of the painted map. Anything at least as strong as OPAQUE_BELOW is
|
||||
# drawn solid; from there it ramps down to transparent at FADE_TO, so the grey
|
||||
# shows through with a soft round edge rather than the ragged one a hard cut
|
||||
# leaves.
|
||||
#
|
||||
# The plateau matters more than either endpoint. Ramping all the way from
|
||||
# DOT_MIN (-3.1e8) meant the opacity at -5e7 was only about 0.13, so the whole
|
||||
# patch read as washed out however the fade point was set.
|
||||
OPAQUE_BELOW = -2.0e7
|
||||
FADE_TO = -2.0e6
|
||||
|
||||
# Coarse cut, only to keep the far field out of the render. Must be nearer zero
|
||||
# than FADE_TO so its hard edge lands where the surface is already fully
|
||||
# transparent and cannot be seen.
|
||||
PAINT_CUT = -5.0e5
|
||||
|
||||
# "signed-log" maps sign and magnitude together through a signed logarithm and
|
||||
# a diverging colour map; "linear" is the earlier one-sided Greens scale, which
|
||||
# the constants above still drive.
|
||||
COLOR_MODE = "signed-log"
|
||||
# X0 sets where the mapping turns from linear to logarithmic, and so how much
|
||||
# of the weak far field survives at all: at 1e6 the data map to -2.51 .. 1.62,
|
||||
# against -3.51 .. 2.61 at 1e5. Raise it to paint less.
|
||||
SYMLOG_X0 = 1.0e6
|
||||
SYMLOG_LIMIT = 2.5 # symmetric colour limit; match it to the mapped range
|
||||
SYMLOG_OPAQUE = 0.6 # |value| at which the map is fully opaque
|
||||
SYMLOG_FADE = 0.3 # |value| below which it is transparent
|
||||
|
||||
# Shape of the two opacity ramps, as opposed to where they sit.
|
||||
#
|
||||
# A piecewise function's midpoint describes the segment running RIGHTWARD from
|
||||
# each node, and the two ramps run in opposite senses -- falling on the
|
||||
# negative side, rising on the positive. So a single midpoint below 0.5 applied
|
||||
# to both makes the negative lobe fade out sooner while the positive lobe goes
|
||||
# opaque sooner, rendering the two polarities at different effective
|
||||
# thresholds. Mirroring it keeps them symmetric: below 0.5 both sides go opaque
|
||||
# sooner, so more is painted, equally on each.
|
||||
#
|
||||
# Sharpness is the independent dial for how abrupt the edge is: 0 smooth, 1 a
|
||||
# hard step. 0.5 is already close to a step.
|
||||
SYMLOG_MIDPOINT = 0.3
|
||||
SYMLOG_SHARPNESS = 0.0
|
||||
DIVERGING_PRESET = "PRGn" # purple / white / green; zero sits at white
|
||||
|
||||
BACKGROUND = [0.35, 0.34, 0.33] # grey
|
||||
|
||||
# Equipotential lines over the whole frame, painted gas or not. Off by default:
|
||||
# the map is the subject here, and the lines are already in the contour plots.
|
||||
SHOW_CONTOURS = False
|
||||
CONTOUR_BY = "potential"
|
||||
CONTOUR_LEVELS = list(np.arange(0, 660, 660 / 40.0))
|
||||
|
||||
|
||||
def threshold_between(source, array, low, high):
|
||||
"""Threshold filter, tolerating the pre-5.10 property names."""
|
||||
t = Threshold(Input=source, Scalars=["POINTS", array])
|
||||
try:
|
||||
t.LowerThreshold = low
|
||||
t.UpperThreshold = high
|
||||
t.ThresholdMethod = "Between"
|
||||
except Exception:
|
||||
t.ThresholdRange = [low, high]
|
||||
return t
|
||||
|
||||
|
||||
# --- locate the driven cathode: the weighting potential is 1 on it ---
|
||||
weight_reader = XMLUnstructuredGridReader(FileName=[WEIGHT_VTU])
|
||||
driven = threshold_between(weight_reader, "potential", 0.99, 1.01)
|
||||
driven.UpdatePipeline()
|
||||
|
||||
info = driven.GetDataInformation()
|
||||
if info.GetNumberOfPoints() == 0:
|
||||
sys.exit(
|
||||
"no nodes with weighting potential ~1 in %s -- the weighting solve did\n"
|
||||
"not drive any electrode, so there is nothing to centre on." % WEIGHT_VTU
|
||||
)
|
||||
b = info.GetBounds()
|
||||
CATHODE = [0.5 * (b[0] + b[1]), 0.5 * (b[2] + b[3]), 0.0]
|
||||
print("driven cathode at x = %.5f m, y = %.5f m" % (CATHODE[0], CATHODE[1]))
|
||||
|
||||
radius = (CATHODE[0] ** 2 + CATHODE[1] ** 2) ** 0.5
|
||||
outward = [CATHODE[0] / radius, CATHODE[1] / radius, 0.0] if radius > 0 else [0.0, 1.0, 0.0]
|
||||
|
||||
# Frame centre, offset from the wire along that radial.
|
||||
CENTER = [
|
||||
CATHODE[0] + CENTER_SHIFT * outward[0],
|
||||
CATHODE[1] + CENTER_SHIFT * outward[1],
|
||||
0.0,
|
||||
]
|
||||
|
||||
Delete(driven)
|
||||
Delete(weight_reader)
|
||||
|
||||
# --- the dot product map ---
|
||||
reader = XMLUnstructuredGridReader(FileName=[INPUT_VTU])
|
||||
|
||||
renderView = GetActiveViewOrCreate("RenderView")
|
||||
renderView.ViewSize = VIEW_SIZE
|
||||
renderView.OrientationAxesVisibility = 1
|
||||
renderView.UseColorPaletteForBackground = 0
|
||||
renderView.Background = BACKGROUND
|
||||
renderView.MultiSamples = 8
|
||||
|
||||
if COLOR_MODE == "signed-log":
|
||||
# E . E_w spans about 3.5 decades negative and 2.6 positive, and 38% of the
|
||||
# nodes are positive -- charge drifting there induces current of the
|
||||
# opposite polarity on the cathode. A linear scale over one sign shows
|
||||
# neither the decades nor the sign, so map through a signed log:
|
||||
#
|
||||
# sign(d) * log10(1 + |d| / SYMLOG_X0)
|
||||
#
|
||||
# sign() is not in the calculator namespace, hence d/(|d|+1); the +1 is
|
||||
# negligible beside the 1e5 scale and the expression tends to 0 anyway
|
||||
# where d does.
|
||||
calc = PythonCalculator(Input=reader)
|
||||
calc.ArrayName = "SignedLogDot"
|
||||
calc.ArrayAssociation = "Point Data"
|
||||
calc.Expression = (
|
||||
"(DotProduct/(abs(DotProduct)+1.0))*log10(1.0+abs(DotProduct)/%g)"
|
||||
% SYMLOG_X0
|
||||
)
|
||||
painted = calc
|
||||
array = "SignedLogDot"
|
||||
colour_range = (-SYMLOG_LIMIT, SYMLOG_LIMIT) # symmetric, so zero is neutral
|
||||
preset = DIVERGING_PRESET
|
||||
# Transparent either side of zero, opaque past SYMLOG_OPAQUE, both signs.
|
||||
_m, _s = SYMLOG_MIDPOINT, SYMLOG_SHARPNESS
|
||||
opacity_points = [
|
||||
-SYMLOG_LIMIT, 1.0, 0.5, _s,
|
||||
-SYMLOG_OPAQUE, 1.0, 1.0 - _m, _s, # mirrored: this ramp falls
|
||||
-SYMLOG_FADE, 0.0, 0.5, _s,
|
||||
SYMLOG_FADE, 0.0, _m, _s, # this one rises
|
||||
SYMLOG_OPAQUE, 1.0, 0.5, _s,
|
||||
SYMLOG_LIMIT, 1.0, 0.5, _s,
|
||||
]
|
||||
bar_title = "sign(E.Ew) log10(1+|E.Ew|/%g)" % SYMLOG_X0
|
||||
else:
|
||||
painted = threshold_between(reader, "DotProduct", -1.0e30, PAINT_CUT)
|
||||
array = "DotProduct"
|
||||
colour_range = (DOT_MIN, DOT_MAX)
|
||||
preset = "Greens"
|
||||
opacity_points = [
|
||||
DOT_MIN, 1.0, 0.5, 0.0,
|
||||
OPAQUE_BELOW, 1.0, 0.5, 0.0,
|
||||
FADE_TO, 0.0, 0.5, 0.0,
|
||||
]
|
||||
bar_title = "E . E_w (V/m^2)"
|
||||
|
||||
surface_display = Show(painted, renderView)
|
||||
surface_display.Representation = "Surface"
|
||||
ColorBy(surface_display, ("POINTS", array))
|
||||
|
||||
dotLUT = GetColorTransferFunction(array)
|
||||
dotLUT.ApplyPreset(preset, True)
|
||||
dotLUT.RescaleTransferFunction(colour_range[0], colour_range[1])
|
||||
|
||||
# Points go (value, opacity, midpoint, sharpness) in ascending value.
|
||||
dotPWF = GetOpacityTransferFunction(array)
|
||||
dotPWF.Points = opacity_points
|
||||
dotLUT.ScalarOpacityFunction = dotPWF
|
||||
dotLUT.EnableOpacityMapping = 1
|
||||
|
||||
surface_display.SetScalarBarVisibility(renderView, True)
|
||||
try:
|
||||
GetScalarBar(dotLUT, renderView).Title = bar_title
|
||||
except Exception as exc:
|
||||
print("note: could not set the scalar bar title (%s)" % exc)
|
||||
|
||||
# --- contour lines over the whole field, including the unpainted gas ---
|
||||
if SHOW_CONTOURS:
|
||||
contour_filter = Contour(Input=reader, ContourBy=["POINTS", CONTOUR_BY])
|
||||
contour_filter.Isosurfaces = CONTOUR_LEVELS
|
||||
|
||||
contour_display = Show(contour_filter, renderView)
|
||||
contour_display.LineWidth = 1.0
|
||||
contour_display.DiffuseColor = [1.0, 1.0, 1.0]
|
||||
# ColorBy(rep, None) is rejected by ParaView 6.1 -- it forwards the
|
||||
# association as the string 'NONE' and raises -- so set the property.
|
||||
contour_display.ColorArrayName = [None, ""]
|
||||
contour_display.SetScalarBarVisibility(renderView, False)
|
||||
|
||||
# --- camera: parallel projection, centred on the driven cathode ---
|
||||
# ParaView resets the camera on a view's FIRST render, throwing away anything
|
||||
# set beforehand, so burn that render here and set the camera after it.
|
||||
# Measured: scale 0.008 set before the first Render comes back as 0.021; set
|
||||
# again afterwards it holds.
|
||||
Render()
|
||||
|
||||
view = GetActiveView()
|
||||
view.CameraParallelScale = ZOOM_SCALE
|
||||
view.CenterOfRotation = CENTER
|
||||
view.CameraPosition = [CENTER[0], CENTER[1], 1.0]
|
||||
view.CameraFocalPoint = CENTER
|
||||
if ALIGN_RADIAL:
|
||||
up = outward
|
||||
print("view rotated %.1f deg so the outward radial is up"
|
||||
% np.degrees(np.arctan2(up[0], up[1])))
|
||||
else:
|
||||
up = [0.0, 1.0, 0.0]
|
||||
view.CameraViewUp = up
|
||||
view.CameraParallelProjection = 1
|
||||
|
||||
Render()
|
||||
SaveScreenshot(OUTPUT_IMAGE)
|
||||
print("Saved %s" % OUTPUT_IMAGE)
|
||||
|
||||
268
anasen_fem/paraview_plotter.py
Executable file
268
anasen_fem/paraview_plotter.py
Executable file
|
|
@ -0,0 +1,268 @@
|
|||
#!/home/vsitaraman/ParaView-6.1.0-RC1-MPI-Linux-Python3.12-x86_64/bin/pvbatch
|
||||
# #######!/home/vsitaraman/ParaView-6.1.0-MPI-Linux-Python3.12-x86_64/bin/pvbatch
|
||||
import numpy as np
|
||||
import sys
|
||||
from paraview.simple import *
|
||||
|
||||
# Optional argument so an archived VTU can be re-plotted without re-solving:
|
||||
# ./paraview_plotter.py wires2d/vtu_files/elfield_anasen_10_0.0000.vtu
|
||||
INPUT_VTU = sys.argv[1] if len(sys.argv) > 1 else "wires2d/elfield_anasen_t0001.vtu"
|
||||
|
||||
# --- contour settings ---
|
||||
# The whole-PC view keeps the original sparse, evenly spaced levels; at that
|
||||
# scale more lines just fill in solid. The quadrant gets the denser, biased
|
||||
# set, since that is where the structure between wires is actually readable.
|
||||
POT_MIN, POT_MAX = 0.0, 660.0
|
||||
N_CONTOURS_FULL, CONTOUR_BIAS_FULL = 40, 1.0
|
||||
N_CONTOURS_QUARTER, CONTOUR_BIAS_QUARTER = 80, 1.0
|
||||
|
||||
# Line widths. Thin lines wash out their own colour, so the quadrant views draw
|
||||
# heavier than the full-PC one; tubes keep them smooth at 2000 px.
|
||||
LINE_WIDTH_FULL = 3.0
|
||||
LINE_WIDTH_QUARTER = 2.5
|
||||
STREAM_LINE_WIDTH = 2.5
|
||||
|
||||
# Arrows are glyphed onto the contour points, so doubling the quadrant contour
|
||||
# density doubled the arrow count too. This offsets that.
|
||||
GLYPH_STRIDE = 96
|
||||
|
||||
# --- quadrant view ---
|
||||
# Centre sits at radius 35.4 mm, near the outer edge of the wire band, which
|
||||
# pushes the wires to one side and leaves the far corner empty. Pulled in to
|
||||
# 31.8 mm so the band sits more centrally.
|
||||
ZOOM_CENTER = [-0.0225, 0.0225, 0.0]
|
||||
ZOOM_SCALE = 0.022 # half-height of the view in metres; larger = more in frame
|
||||
|
||||
|
||||
def contour_levels(n, bias):
|
||||
"""Isosurface values.
|
||||
|
||||
phi ~ ln(r) around a wire, so evenly spaced levels crowd onto the wire
|
||||
surfaces and thin out in the gas. Raising a symmetric parameter to `bias`
|
||||
pulls levels towards mid-range, where the saddles between wires are,
|
||||
without adding more of them. 1.0 is plain linear spacing; much above 1.5
|
||||
and the middle levels start landing on top of each other.
|
||||
"""
|
||||
u = np.linspace(-1.0, 1.0, n)
|
||||
u = np.sign(u) * np.abs(u) ** bias
|
||||
mid = 0.5 * (POT_MIN + POT_MAX)
|
||||
return list(mid + 0.5 * (POT_MAX - POT_MIN) * u)
|
||||
|
||||
|
||||
reader = XMLUnstructuredGridReader(FileName=[INPUT_VTU])
|
||||
|
||||
contour_filter = Contour(Input=reader,ContourBy = 'potential')
|
||||
contour_filter.Isosurfaces = contour_levels(N_CONTOURS_FULL, CONTOUR_BIAS_FULL)
|
||||
|
||||
renderView = GetActiveViewOrCreate('RenderView')
|
||||
renderView.ViewSize = [2000,2000]
|
||||
renderView.OrientationAxesVisibility = 0 # Hide axis
|
||||
renderView.UseColorPaletteForBackground=0
|
||||
renderView.Background = [0.1, 0.1, 0.1] # Set background to dark gray (RGB 0-1)
|
||||
|
||||
renderView.MultiSamples = 8 # 0 disables it, 4-8 is usually sufficient
|
||||
|
||||
ResetCamera()
|
||||
|
||||
contour_display = Show(contour_filter, renderView)
|
||||
contour_display.LineWidth = LINE_WIDTH_FULL
|
||||
contour_display.RenderLinesAsTubes = 1 # Makes lines look smoother at high res
|
||||
#colorbar
|
||||
contour_display_potentialLUT = GetColorTransferFunction('potential', contour_display, separate=True)
|
||||
contour_display_potentialLUT.ApplyPreset('Cool to Warm', True)
|
||||
contour_display.SetScalarBarVisibility(renderView, True)
|
||||
|
||||
#axesGrid = renderView.AxesGridrfcxgdtv
|
||||
#axesGrid.Visibility = 1
|
||||
#axesGrid.XTitle = "x (mm)"
|
||||
#axesGrid.YTitle = "y (mm)"
|
||||
|
||||
# 1. Get the active view
|
||||
view = GetActiveView()
|
||||
|
||||
# 2. Define your desired coordinate ranges (x_min, x_max, y_min, y_max, z_min, z_max)
|
||||
x_min, x_max = -0.05, 0.05
|
||||
y_min, y_max = -0.05, 0.05
|
||||
z_min, z_max = -0.05, 0.05
|
||||
|
||||
# 3. Calculate Center, Position, and Parallel Scale
|
||||
center = [(x_min + x_max) / 2.0, (y_min + y_max) / 2.0, (z_min + z_max) / 2.0]
|
||||
# Position the camera far away along Z to look at the center
|
||||
position = [center[0], center[1], 1.0]
|
||||
# Parallel scale defines how much of the scene is visible.
|
||||
# It is usually half the height of the viewed area.
|
||||
view.CameraParallelScale = max((x_max - x_min), (y_max - y_min))/1.6
|
||||
|
||||
# 4. Apply settings
|
||||
view.CenterOfRotation = center
|
||||
view.CameraPosition = position
|
||||
view.CameraFocalPoint = center
|
||||
view.CameraViewUp = [0.0, 1.0, 0.0] # Y-axis is up
|
||||
|
||||
# 5. Enable Parallel Projection (optional, often better for exact mapping)
|
||||
view.CameraParallelProjection = 1
|
||||
|
||||
#ResetCamera()
|
||||
Render()
|
||||
|
||||
SaveScreenshot("contour_output.png")
|
||||
|
||||
# Everything from here on is a quadrant view: denser levels, heavier lines.
|
||||
contour_filter.Isosurfaces = contour_levels(N_CONTOURS_QUARTER, CONTOUR_BIAS_QUARTER)
|
||||
contour_display.LineWidth = LINE_WIDTH_QUARTER
|
||||
contour_display.RenderLinesAsTubes = 1
|
||||
|
||||
# 1. Get the active view
|
||||
view = GetActiveView()
|
||||
|
||||
# 1. Set the Focal Point to the middle of the quadrant in metres
|
||||
zoom_center = ZOOM_CENTER
|
||||
|
||||
# 2. Tighten the Parallel Scale
|
||||
view.CameraParallelScale = ZOOM_SCALE
|
||||
|
||||
# 3. Position the Camera (0.5m away is fine)
|
||||
view.CameraPosition = [zoom_center[0], zoom_center[1], 0.5]
|
||||
view.CameraFocalPoint = zoom_center
|
||||
view.CameraViewUp = [0.0, 1.0, 0.0]
|
||||
|
||||
# Equipotentials over one quadrant, saved before the arrows go on so this and
|
||||
# Field_output.png below share a camera and overlay exactly.
|
||||
Render()
|
||||
SaveScreenshot("contour_quarter_output.png")
|
||||
|
||||
# pot_threshold = Threshold(Input=reader)
|
||||
# pot_threshold.Scalars = ['POINTS', 'potential']
|
||||
# pot_threshold.ThresholdMethod = 'Above Upper Threshold'
|
||||
# pot_threshold.UpperThreshold = 100.0
|
||||
|
||||
# --- 2. Create the Glyph Filter (The Arrows) ---
|
||||
# IMPORTANT: Use 'pot_threshold' as the Input, not the 'reader'
|
||||
glyph = Glyph(Input=contour_filter, GlyphType='Arrow') #
|
||||
# glyph = Glyph(Input=reader, GlyphType='Arrow') #this uses all field line snot just the ones from the equipotential lines shown
|
||||
|
||||
# Orientation Array: Use the 'electric field' vector from Elmer
|
||||
glyph.OrientationArray = ['POINTS', 'electric field']
|
||||
glyph.ScaleArray = ['POINTS', 'No scale array']
|
||||
glyph.ScaleFactor = 0.001
|
||||
|
||||
glyph.GlyphMode = 'Every Nth Point'
|
||||
glyph.Stride = GLYPH_STRIDE
|
||||
|
||||
# --- 3. Display the Glyphs ---
|
||||
glyph_display = Show(glyph, renderView)
|
||||
|
||||
# Set the representation to Surface so we see the full arrow colors
|
||||
glyph_display.Representation = 'Surface'
|
||||
|
||||
|
||||
# This is the critical line: Color the arrows by the 'potential' scalar
|
||||
ColorBy(glyph_display, ('POINTS', 'potential'))
|
||||
glyph_display.LookupTable = contour_display_potentialLUT
|
||||
contour_display_potentialLUT.RescaleTransferFunction(POT_MIN, POT_MAX)
|
||||
|
||||
# Optional: Disable the scalar bar for the arrows to avoid cluttering
|
||||
# the existing 'potential' scalar bar.
|
||||
glyph_display.SetScalarBarVisibility(renderView, False)
|
||||
|
||||
# --- 4. Final Render ---
|
||||
Render()
|
||||
SaveScreenshot("Field_output.png")
|
||||
|
||||
|
||||
# --- Streamlines over the same quadrant ---
|
||||
# Field lines rather than arrows: with no B field the drift velocity is
|
||||
# parallel to E, so these are drift paths up to diffusion, i.e. a map of which
|
||||
# wire collects charge from where. The glyphs above are fixed length, so they
|
||||
# show direction only. Quadrant only; over the whole PC it is an unreadable mat.
|
||||
# Equipotentials the streamlines start from. One level near the anodes seeds
|
||||
# lines only in a ring around each anode, so with the lines truncated short the
|
||||
# rest of the frame comes out empty. Spanning the range instead puts seeds
|
||||
# right across the gas: every equipotential is a closed curve winding between
|
||||
# the wires, so together they cover the volume.
|
||||
SEED_POTENTIALS = [660.0, 600.0, 550.0, 500.0, 450.0, 400.0,350.0, 300.0,250.0, 250.0, 200.0, 150.0, 100.0, 50.0]
|
||||
|
||||
# Keep 1 seed point in N along the seed contours. Measured point counts per
|
||||
# level, for working out the total:
|
||||
#
|
||||
# 600 V 4970 300 V 29971
|
||||
# 500 V 8861 200 V 51558
|
||||
# 400 V 16331 100 V 51645
|
||||
# 50 V 27259
|
||||
#
|
||||
# 250 V and 150 V are not measured; from the trend they are of order 40000 and
|
||||
# 50000, putting the nine levels near 280000 points. Stride 3 is then ~94000
|
||||
# seeds, of which roughly an eighth land in the rendered quadrant. That is
|
||||
# three times the previous seed count, but each line is now 4 mm instead of
|
||||
# 20 mm, so the integration work is comparable.
|
||||
SEED_STRIDE = 3
|
||||
# Arc length each line is allowed to run, in metres. Field lines terminate on
|
||||
# electrodes, so any line long enough to reach one will converge with every
|
||||
# other line reaching the same wire -- that convergence is the field, not a
|
||||
# plotting artefact, and the only way to avoid drawing it is to stop the lines
|
||||
# short. The anode-cathode gap runs 4.3-6.2 mm depending on z, so 3 mm keeps
|
||||
# every line clear of the cathodes. Raise it towards 0.005 for longer lines,
|
||||
# and they will start meeting again at the wires.
|
||||
# Short lines, and the gaps between them closed by seeding more planes rather
|
||||
# than by making each line longer. 4 mm is just under the 4.3 mm closest
|
||||
# anode-cathode approach, so no line quite reaches a wire and they stay
|
||||
# separate instead of converging.
|
||||
STREAM_MAX_LENGTH = 0.0005
|
||||
|
||||
# StreamTracer counts steps as well as length, and its step sizes are in
|
||||
# cell-length units rather than metres: 0.2 of a cell by default, and cells are
|
||||
# 0.05 mm at the wires. 2000 steps therefore buys about 20 mm at best and less
|
||||
# wherever the mesh is fine, so the default caps the lines before
|
||||
# STREAM_MAX_LENGTH ever does. Raise it well past what the length needs.
|
||||
MAX_STEPS = 20000
|
||||
STREAM_DIRECTION = 'BOTH' # 'BOTH' also draws the half that runs into the anode
|
||||
STREAM_RANGE = [-200.0, 650.0] # full electrode span, needle to anode
|
||||
|
||||
# Clear the quadrant view of everything else so only the field lines show.
|
||||
Hide(glyph, renderView)
|
||||
Hide(contour_filter, renderView)
|
||||
|
||||
seed_contour = Contour(Input=reader, ContourBy=['POINTS', 'potential'])
|
||||
seed_contour.Isosurfaces = SEED_POTENTIALS
|
||||
|
||||
# StreamTracer's own Point Cloud and Line seeds are 3D and this mesh is a
|
||||
# single plane at z = 0, so they would mostly land outside it. Seeding from a
|
||||
# contour of the mesh keeps every seed in the plane by construction.
|
||||
seed_points = MaskPoints(Input=seed_contour)
|
||||
seed_points.OnRatio = SEED_STRIDE
|
||||
seed_points.RandomSampling = 0
|
||||
|
||||
stream = StreamTracerWithCustomSource(Input=reader, SeedSource=seed_points)
|
||||
stream.Vectors = ['POINTS', 'electric field']
|
||||
# FORWARD follows E, which runs from the anode outwards, so each line leaves
|
||||
# its seed and heads for a cathode. With BOTH, the backward half of every line
|
||||
# also climbs to the anode the seed ring encircles, and all of them meet there
|
||||
# -- that is the joining. BOTH is still the honest full field line if you want
|
||||
# it; this only changes what gets drawn.
|
||||
stream.IntegrationDirection = STREAM_DIRECTION
|
||||
stream.MaximumStreamlineLength = STREAM_MAX_LENGTH
|
||||
stream.MaximumSteps = MAX_STEPS
|
||||
|
||||
# Keep integration in the z = 0 plane. Newer ParaView only, so tolerate it
|
||||
# being absent.
|
||||
try:
|
||||
stream.SurfaceStreamlines = 1
|
||||
except Exception as exc:
|
||||
print("note: SurfaceStreamlines unavailable (%s)" % exc)
|
||||
|
||||
stream_display = Show(stream, renderView)
|
||||
stream_display.Representation = 'Surface'
|
||||
stream_display.LineWidth = STREAM_LINE_WIDTH
|
||||
stream_display.RenderLinesAsTubes = 1
|
||||
|
||||
ColorBy(stream_display, ('POINTS', 'potential'))
|
||||
streamLUT = GetColorTransferFunction('potential', stream_display, separate=True)
|
||||
streamLUT.ApplyPreset('Cool to Warm', True)
|
||||
streamLUT.RescaleTransferFunction(STREAM_RANGE[0], STREAM_RANGE[1])
|
||||
stream_display.LookupTable = streamLUT
|
||||
stream_display.SetScalarBarVisibility(renderView, True)
|
||||
|
||||
renderView.Background = [0.0, 0.0, 0.0]
|
||||
|
||||
Render()
|
||||
SaveScreenshot("Streamlines_quarter_output.png")
|
||||
113
anasen_fem/run.py
Executable file
113
anasen_fem/run.py
Executable file
|
|
@ -0,0 +1,113 @@
|
|||
import os
|
||||
import sys
|
||||
|
||||
# Cathode wire (0..23) that wires2d_weight.sif drives to 1 V. Passed to the
|
||||
# mesher, which puts that one wire in its own physical group, tag 40.
|
||||
SELECTED_CATHODE = 1
|
||||
|
||||
# 0 = everything in one pass
|
||||
# 1 = mesh and the physical field only
|
||||
# 2 = weighting field and dot product only, reusing stage 1's mesh
|
||||
STAGE = 0
|
||||
|
||||
# gmsh comes from apt and lives in the system python3; meshio is pip-installed
|
||||
# into the venv, which cannot see apt packages. Neither interpreter has both,
|
||||
# so name them explicitly rather than relying on whichever python3 is on PATH.
|
||||
MESH_PY = "/usr/bin/python3"
|
||||
DOT_PY = os.path.expanduser("~/myenv/bin/python3")
|
||||
|
||||
|
||||
def run(cmd):
|
||||
"""os.system that stops on failure.
|
||||
|
||||
Worth the four lines: a solve that quietly did nothing once produced a
|
||||
field of zeros, and every later step ran happily on it.
|
||||
"""
|
||||
print(cmd)
|
||||
if os.system(cmd) != 0:
|
||||
sys.exit("failed: " + cmd)
|
||||
|
||||
|
||||
# z-loci to run, in mm.
|
||||
#
|
||||
# The wires twist: over z = 0 -> 174.3 the anode lattice turns 60 deg against
|
||||
# the cathode lattice, which is exactly 4 cathode pitches of 15 deg. So the
|
||||
# field pattern goes through the same cycle four times across that range, while
|
||||
# the wire radii drift slowly and monotonically (anodes 32.0 -> 37.0 mm).
|
||||
#
|
||||
# A uniform 17.43 step samples the fast cycle 2.75 times per period, which
|
||||
# aliases it -- consecutive slices land at relative azimuths 7.5, 14.1, 5.7,
|
||||
# 12.2, 3.5 deg, jumping around rather than sweeping. Sample the two variations
|
||||
# separately instead:
|
||||
# - 8 slices across one pitch, which walks the azimuth in even ~2 deg steps
|
||||
# at essentially fixed radius
|
||||
# - 5 slices one whole pitch apart, which holds the azimuth near-fixed and
|
||||
# walks the radius over its full range
|
||||
# 12 z-loci in total, against 11 for the uniform sweep.
|
||||
PITCH_Z = 174.3 / 4.0
|
||||
|
||||
Z_VALUES = sorted(set(
|
||||
[round(i * PITCH_Z / 8.0, 4) for i in range(8)]
|
||||
+ [round(i * PITCH_Z, 4) for i in range(5)]
|
||||
))
|
||||
|
||||
# Running only two loci for now; delete this line for the full sweep.
|
||||
# 19.794 is the crossover: the relative azimuth wraps through zero there, so an
|
||||
# anode sits directly radially outside a cathode, and the closest anode-cathode
|
||||
# approach over the whole range (4.339 mm) is at 19.707. It is deliberately off
|
||||
# the grid above, whose nearest point is 21.7875.
|
||||
# Z_VALUES = [19.690]
|
||||
|
||||
# Stage 2 reuses whatever single mesh is sitting in wires2d/, so it cannot span
|
||||
# several z. Without this it would happily solve the weighting field on one z's
|
||||
# mesh and archive the result under another z's name -- and dotproduct.py could
|
||||
# not catch it, since the node sets would agree perfectly.
|
||||
if STAGE == 2 and len(Z_VALUES) > 1:
|
||||
sys.exit(
|
||||
"STAGE 2 applies to the one mesh in wires2d/, which is whichever z\n"
|
||||
"stage 1 ran last. Set Z_VALUES to that single z."
|
||||
)
|
||||
|
||||
for count, val in enumerate(Z_VALUES, start=10):
|
||||
print(val)
|
||||
os.system("mkdir -p wires2d/mesh_files wires2d/sif_files wires2d/vtu_files png")
|
||||
|
||||
# Each stage archives its own output before the next one starts, so a
|
||||
# failure in stage 2 does not throw away what stage 1 already produced.
|
||||
if STAGE in (0, 1):
|
||||
run("%s wires_gmsh2d_bc.py %s %d" % (MESH_PY, val, SELECTED_CATHODE))
|
||||
run("ElmerGrid 14 2 wires2d.msh -2d")
|
||||
run("ElmerSolver wires2d.sif")
|
||||
run("./paraview_plotter.py")
|
||||
|
||||
os.system("cp wires2d.msh wires2d/mesh_files/wires2d%02d_%1.4f.msh"%(count,val))
|
||||
os.system("cp wires2d.sif wires2d/sif_files/wires2d_%02d_%1.4f.sif"%(count,val))
|
||||
os.system("cp wires2d/elfield_anasen_t0001.vtu wires2d/vtu_files/elfield_anasen_%02d_%1.4f.vtu"%(count,val))
|
||||
os.system("cp contour_output.png png/Contour_output_z_%02d_%1.4f.png"%(count,val))
|
||||
os.system("cp contour_quarter_output.png png/Contour_output_z_%02d_%1.4f_quarter.png"%(count,val))
|
||||
os.system("cp Field_output.png png/Field_ouput_z_%02d_%1.4f.png"%(count,val))
|
||||
os.system("cp Streamlines_quarter_output.png png/Streamlines_output_z_%02d_%1.4f_quarter.png"%(count,val))
|
||||
|
||||
# Stage 2 does not re-mesh: it reuses wires2d/ as stage 1 left it, which is
|
||||
# what makes the node-by-node dot product valid.
|
||||
if STAGE in (0, 2):
|
||||
run("ElmerSolver wires2d_weight.sif")
|
||||
run("%s dotproduct.py" % DOT_PY)
|
||||
run("./paraview_dotproduct.py")
|
||||
|
||||
os.system("cp wires2d_weight.sif wires2d/sif_files/wires2d_weight_%02d_%1.4f.sif"%(count,val))
|
||||
os.system("cp wires2d/elfield_weight_t0001.vtu wires2d/vtu_files/elfield_weight_%02d_%1.4f.vtu"%(count,val))
|
||||
os.system("cp wires2d/dotproduct.vtu wires2d/vtu_files/dotproduct_%02d_%1.4f.vtu"%(count,val))
|
||||
os.system("cp DotProduct_output.png png/DotProduct_output_z_%02d_%1.4f.png"%(count,val))
|
||||
os.system("cp DotProduct_quarter_output.png png/DotProduct_output_z_%02d_%1.4f_quarter.png"%(count,val))
|
||||
|
||||
# os.system("python3 garfield_sim.py")
|
||||
|
||||
# (a bare "break" here runs only the first z, as the original did)
|
||||
|
||||
# os.system("tar -cvzf wiress2d/mesh.tar.gz wires2d/mesh_files")
|
||||
# os.system("rm -rf wires2d/mesh_files/*")
|
||||
# os.system("tar -cvzf wires2d/sif.tar.gz wires2d/sif_files")
|
||||
# os.system("rm -rf wires2d/sif_files/*")
|
||||
# os.system("tar -cvzf wires2d/vtu.tar.gz wires2d/vtu_files")
|
||||
# os.system("rm -rf wires2d/vtu_files/*")
|
||||
1
anasen_fem/scalars.dat
Executable file
1
anasen_fem/scalars.dat
Executable file
|
|
@ -0,0 +1 @@
|
|||
8.500000000000E+002
|
||||
8
anasen_fem/scalars.dat.names
Executable file
8
anasen_fem/scalars.dat.names
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
Metadata for SaveScalars file: ./scalars.dat
|
||||
Elmer version: 26.2
|
||||
Elmer compilation date: 2026-06-10
|
||||
Solver input file: wires2d.sif
|
||||
File started at: 2026/09/23 14:27:47
|
||||
|
||||
Variables in columns of matrix:
|
||||
1: res: potential difference
|
||||
1
anasen_fem/scalars_weight.dat
Normal file
1
anasen_fem/scalars_weight.dat
Normal file
|
|
@ -0,0 +1 @@
|
|||
1.000000000000E+000
|
||||
8
anasen_fem/scalars_weight.dat.names
Normal file
8
anasen_fem/scalars_weight.dat.names
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
Metadata for SaveScalars file: ./scalars_weight.dat
|
||||
Elmer version: 26.2
|
||||
Elmer compilation date: 2026-06-10
|
||||
Solver input file: wires2d_weight.sif
|
||||
File started at: 2026/09/23 14:50:46
|
||||
|
||||
Variables in columns of matrix:
|
||||
1: res: potential difference
|
||||
127
anasen_fem/wires2d.sif
Executable file
127
anasen_fem/wires2d.sif
Executable file
|
|
@ -0,0 +1,127 @@
|
|||
Check Keywords Warn
|
||||
|
||||
Header
|
||||
Mesh DB "." "wires2d"
|
||||
End
|
||||
|
||||
Simulation
|
||||
Coordinate System = Cartesian 2D
|
||||
Simulation Type = Steady State
|
||||
Steady State Max Iterations = 1
|
||||
Output File = "elstatics.result"
|
||||
Post File = "elstatics.ep"
|
||||
Coordinate Scaling = 0.001 ! Converts mm from Gmsh to meters for Elmer
|
||||
End
|
||||
|
||||
Constants
|
||||
Permittivity Of Vacuum = 8.8542e-12
|
||||
End
|
||||
|
||||
|
||||
Body 1
|
||||
Target Bodies(1) = 13
|
||||
Equation = 1
|
||||
Material = 1
|
||||
End
|
||||
|
||||
|
||||
Equation 1
|
||||
Active Solvers(2) = 1 2
|
||||
End
|
||||
|
||||
|
||||
Material 1
|
||||
Relative Permittivity = 1
|
||||
End
|
||||
|
||||
|
||||
Solver 1
|
||||
Equation = Electrostatics
|
||||
Procedure = "StatElecSolve" "StatElecSolver"
|
||||
|
||||
Variable = Potential
|
||||
Variable DOFs = 1
|
||||
|
||||
Calculate Electric Field = True
|
||||
Calculate Electric Flux = False
|
||||
|
||||
Linear System Solver = Iterative
|
||||
Linear System Iterative Method = CG
|
||||
Linear System Max Iterations = 5000
|
||||
Linear System Convergence Tolerance = 1.0e-8
|
||||
Linear System Preconditioning = ILU1
|
||||
Calculate Vectors = Logical True
|
||||
End
|
||||
|
||||
Solver 2
|
||||
Equation = "Electric Field"
|
||||
Procedure = "FluxSolver" "FluxSolver"
|
||||
|
||||
! Calculate from the potential
|
||||
Target Variable = "Potential"
|
||||
! Name of the output vector field in VTU
|
||||
Flux Variable = String "Electric Field"
|
||||
|
||||
! Use 2D components (x, y)
|
||||
Flux Coefficient = String "Permittivity"
|
||||
|
||||
Calculate Vectors = Logical True
|
||||
End
|
||||
|
||||
Solver 3
|
||||
Equation = Result Output
|
||||
Procedure = "ResultOutputSolve" "ResultOutputSolver"
|
||||
Output File Name = elfield_anasen ! Sets prefix for output files
|
||||
Output Format = Vtu
|
||||
! Optional: Select specific variables to save
|
||||
Scalar Field 1 = Potential
|
||||
Vector Field 1 = Electric Field
|
||||
End
|
||||
|
||||
Solver 4
|
||||
Exec Solver = After All
|
||||
Equation = SaveScalars
|
||||
Procedure = "SaveData" "SaveScalars"
|
||||
Filename = "scalars.dat"
|
||||
End
|
||||
|
||||
Boundary Condition 1
|
||||
Target Boundaries = 1
|
||||
Potential = -200
|
||||
Calculate Electric Force = True
|
||||
End
|
||||
|
||||
Boundary Condition 2
|
||||
Target Boundaries = 2
|
||||
Potential = 150
|
||||
Calculate Electric Force = True
|
||||
End
|
||||
|
||||
Boundary Condition 3
|
||||
Target Boundaries = 3
|
||||
Potential = 250
|
||||
Calculate Electric Force = True
|
||||
End
|
||||
|
||||
Boundary Condition 4
|
||||
Target Boundaries = 10
|
||||
Potential = 0
|
||||
End
|
||||
|
||||
Boundary Condition 5
|
||||
Target Boundaries = 20
|
||||
Potential = 660
|
||||
Calculate Electric Force = True
|
||||
End
|
||||
|
||||
Boundary Condition 6
|
||||
Target Boundaries = 30
|
||||
Potential = 0
|
||||
End
|
||||
|
||||
! The one cathode singled out in wires_gmsh2d_bc.py. Grounded here, exactly
|
||||
! like the other 23; it is only separate so wires2d_weight.sif can drive it.
|
||||
Boundary Condition 7
|
||||
Target Boundaries = 40
|
||||
Potential = 0
|
||||
End
|
||||
132
anasen_fem/wires2d_weight.sif
Normal file
132
anasen_fem/wires2d_weight.sif
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
Check Keywords Warn
|
||||
|
||||
! Ramo weighting potential for the single cathode at tag 40: that electrode
|
||||
! sits at 1 V and every other one at 0 V. Identical to wires2d.sif apart from
|
||||
! the potentials and the output names, so both solves discretise the same way
|
||||
! and E . E_w is meaningful node by node.
|
||||
|
||||
Header
|
||||
Mesh DB "." "wires2d"
|
||||
End
|
||||
|
||||
Simulation
|
||||
Coordinate System = Cartesian 2D
|
||||
Simulation Type = Steady State
|
||||
Steady State Max Iterations = 1
|
||||
Output File = "elstatics_weight.result"
|
||||
Post File = "elstatics_weight.ep"
|
||||
Coordinate Scaling = 0.001 ! Converts mm from Gmsh to meters for Elmer
|
||||
End
|
||||
|
||||
Constants
|
||||
Permittivity Of Vacuum = 8.8542e-12
|
||||
End
|
||||
|
||||
|
||||
Body 1
|
||||
Target Bodies(1) = 13
|
||||
Equation = 1
|
||||
Material = 1
|
||||
End
|
||||
|
||||
|
||||
Equation 1
|
||||
Active Solvers(2) = 1 2
|
||||
End
|
||||
|
||||
|
||||
Material 1
|
||||
Relative Permittivity = 1
|
||||
End
|
||||
|
||||
|
||||
Solver 1
|
||||
Equation = Electrostatics
|
||||
Procedure = "StatElecSolve" "StatElecSolver"
|
||||
|
||||
Variable = Potential
|
||||
Variable DOFs = 1
|
||||
|
||||
Calculate Electric Field = True
|
||||
Calculate Electric Flux = False
|
||||
|
||||
Linear System Solver = Iterative
|
||||
Linear System Iterative Method = CG
|
||||
Linear System Max Iterations = 5000
|
||||
Linear System Convergence Tolerance = 1.0e-8
|
||||
Linear System Preconditioning = ILU1
|
||||
Calculate Vectors = Logical True
|
||||
End
|
||||
|
||||
Solver 2
|
||||
Equation = "Electric Field"
|
||||
Procedure = "FluxSolver" "FluxSolver"
|
||||
|
||||
! Calculate from the potential
|
||||
Target Variable = "Potential"
|
||||
! Name of the output vector field in VTU
|
||||
Flux Variable = String "Electric Field"
|
||||
|
||||
! Use 2D components (x, y)
|
||||
Flux Coefficient = String "Permittivity"
|
||||
|
||||
Calculate Vectors = Logical True
|
||||
End
|
||||
|
||||
Solver 3
|
||||
Equation = Result Output
|
||||
Procedure = "ResultOutputSolve" "ResultOutputSolver"
|
||||
Output File Name = elfield_weight ! Sets prefix for output files
|
||||
Output Format = Vtu
|
||||
! Optional: Select specific variables to save
|
||||
Scalar Field 1 = Potential
|
||||
Vector Field 1 = Electric Field
|
||||
End
|
||||
|
||||
Solver 4
|
||||
Exec Solver = After All
|
||||
Equation = SaveScalars
|
||||
Procedure = "SaveData" "SaveScalars"
|
||||
Filename = "scalars_weight.dat"
|
||||
End
|
||||
|
||||
Boundary Condition 1
|
||||
Target Boundaries = 1
|
||||
Potential = 0
|
||||
Calculate Electric Force = True
|
||||
End
|
||||
|
||||
Boundary Condition 2
|
||||
Target Boundaries = 2
|
||||
Potential = 0
|
||||
Calculate Electric Force = True
|
||||
End
|
||||
|
||||
Boundary Condition 3
|
||||
Target Boundaries = 3
|
||||
Potential = 0
|
||||
Calculate Electric Force = True
|
||||
End
|
||||
|
||||
Boundary Condition 4
|
||||
Target Boundaries = 10
|
||||
Potential = 0
|
||||
End
|
||||
|
||||
Boundary Condition 5
|
||||
Target Boundaries = 20
|
||||
Potential = 0
|
||||
Calculate Electric Force = True
|
||||
End
|
||||
|
||||
Boundary Condition 6
|
||||
Target Boundaries = 30
|
||||
Potential = 0
|
||||
End
|
||||
|
||||
! The one cathode singled out in wires_gmsh2d_bc.py. Grounded here, exactly
|
||||
! like the other 23; it is only separate so wires2d_weight.sif can drive it.
|
||||
Boundary Condition 7
|
||||
Target Boundaries = 40
|
||||
Potential = 1
|
||||
End
|
||||
295
anasen_fem/wires_gmsh2d_bc.py
Executable file
295
anasen_fem/wires_gmsh2d_bc.py
Executable file
|
|
@ -0,0 +1,295 @@
|
|||
# This script generates a 2D Gmsh geometry for the ANASEN detector's wire planes, including an optional hot needle and IC wires. The geometry is designed for adaptive meshing, with finer mesh near the wires and coarser mesh in empty space. The script takes a z-locus as a command-line argument to determine the interpolation between the two wire planes.
|
||||
import numpy as np
|
||||
import gmsh, sys
|
||||
|
||||
# --- Configuration Flags ---
|
||||
include_ic_wires = True
|
||||
include_needle = True
|
||||
|
||||
gmsh.initialize()
|
||||
gmsh.model.add("adaptive_mesh")
|
||||
gmsh.option.setNumber("General.NumThreads", 10)
|
||||
# gmsh.option.setNumber("Mesh.Adapt.MaxNumberOfElements", 200000)
|
||||
# gmsh.option.setNumber("Mesh.Adapt.MaxNumberOfNodes", 200000)
|
||||
# gmsh.option.setNumber("Mesh.Adapt.MaxIter",5)
|
||||
# gmsh.option.setNumber("Mesh.MeshSizeMin", 5e-3)
|
||||
# gmsh.option.setNumber("Mesh.MeshSizeMax", 10.0)
|
||||
gmsh.option.setNumber("Geometry.Tolerance", 4e-2)
|
||||
# gmsh.option.setNumber("Mesh.MeshSizeExtendFromBoundary", 0)
|
||||
# Only needed alongside mesh.recombine(), which is off. See the note there.
|
||||
# gmsh.option.setNumber("Mesh.RecombinationAlgorithm", 2)
|
||||
# gmsh.option.setNumber("Mesh.SubdivisionAlgorithm", 1)
|
||||
gmsh.option.setNumber("Mesh.SecondOrderIncomplete", 1)
|
||||
|
||||
lc = 0.04
|
||||
# z_loc = -174.3
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python3 wires_gmsh2d_bc.py <z_locus in mm> [selected_cathode_index]")
|
||||
quit()
|
||||
|
||||
z_loc = float(sys.argv[1])
|
||||
|
||||
# Cathode wire (0..23) given its own physical group, tag 40, so that
|
||||
# wires2d_weight.sif can hold it at 1 V while everything else stays grounded.
|
||||
selected_cathode = int(sys.argv[2]) if len(sys.argv) > 2 else 1
|
||||
|
||||
wireShift = 4.0
|
||||
k = 2 * np.pi / 24.0
|
||||
kg = k/2.0
|
||||
|
||||
# Plane 1 Offsets (-zmax/2)
|
||||
# Anodes: -6*k (base) - 4*k (correction) = -10*k
|
||||
offset_a1 = -6 * k - 4 * k
|
||||
# Cathodes: -6*k (base) + 0.5*k (half-placement)
|
||||
offset_c1 = -6 * k + (k / 2.0)
|
||||
# Guard wires: aligned with cathodes
|
||||
offset_g1 = offset_c1
|
||||
|
||||
# Plane 2 Offsets (+zmax/2) with Twist
|
||||
offset_a2 = offset_a1 + (wireShift * k)
|
||||
offset_c2 = offset_c1 - (wireShift * k)
|
||||
offset_g2 = offset_c2
|
||||
|
||||
# 1 needle, 24 ic1, 24 ic2, 48 guard wires, 24 anodes, 24 cathodes
|
||||
|
||||
# needle at plane 1 at -zmax/2 no rotation
|
||||
xarr_needle = np.array([0])
|
||||
yarr_needle = np.array([0])
|
||||
|
||||
# ic1 wires, plane 1 at -zmax/2 no rotation
|
||||
xarr_i11 = np.array([23 * np.cos(k * i) for i in range(24)])
|
||||
yarr_i11 = np.array([23 * np.sin(k * i) for i in range(24)])
|
||||
|
||||
# ic1 wires, plane 1 at -zmax/2 no rotation
|
||||
xarr_i21 = np.array([23 * np.cos(k * i + k / 2.0) for i in range(24)])
|
||||
yarr_i21 = np.array([23 * np.sin(k * i + k / 2.0) for i in range(24)])
|
||||
|
||||
# --- Coordinate Arrays (Plane 1) ---
|
||||
# Anodes: -k*i (Left-handed twist direction)
|
||||
xarra_1 = np.array([37 * np.cos(-k * i + offset_a1) for i in range(24)])
|
||||
yarra_1 = np.array([37 * np.sin(-k * i + offset_a1) for i in range(24)])
|
||||
|
||||
# Cathodes: +k*i (Right-handed twist direction)
|
||||
xarrc_1 = np.array([42 * np.cos(k * i + offset_c1) for i in range(24)])
|
||||
yarrc_1 = np.array([42 * np.sin(k * i + offset_c1) for i in range(24)])
|
||||
|
||||
# Guard Wires (48 wires, use kg spacing)
|
||||
xarrg_1 = np.array([32 * np.cos(kg * i + offset_g1) for i in range(48)])
|
||||
yarrg_1 = np.array([32 * np.sin(kg * i + offset_g1) for i in range(48)])
|
||||
|
||||
# needle at plane 2 at zmax/2
|
||||
xarr_needle_2 = np.array([0])
|
||||
yarr_needle_2 = np.array([0])
|
||||
|
||||
# #ic1 wires, plane 2 at zmax/2
|
||||
xarr_i12 = np.array([23 * np.cos(k * i) for i in range(24)])
|
||||
yarr_i12 = np.array([23 * np.sin(k * i) for i in range(24)])
|
||||
|
||||
# #ic2 wires, plane 2 at zmax/2
|
||||
xarr_i22 = np.array([23 * np.cos(k * i + k / 2.0) for i in range(24)])
|
||||
yarr_i22 = np.array([23 * np.sin(k * i + k / 2.0) for i in range(24)])
|
||||
|
||||
# --- Coordinate Arrays (Plane 2) ---
|
||||
xarra_2 = np.array([37 * np.cos(-k * i + offset_a2) for i in range(24)])
|
||||
yarra_2 = np.array([37 * np.sin(-k * i + offset_a2) for i in range(24)])
|
||||
|
||||
xarrc_2 = np.array([42 * np.cos(k * i + offset_c2) for i in range(24)])
|
||||
yarrc_2 = np.array([42 * np.sin(k * i + offset_c2) for i in range(24)])
|
||||
|
||||
xarrg_2 = np.array([32 * np.cos(kg * i + offset_g2) for i in range(48)])
|
||||
yarrg_2 = np.array([32 * np.sin(kg * i + offset_g2) for i in range(48)])
|
||||
|
||||
direction_needle_x = xarr_needle_2 - xarr_needle
|
||||
direction_needle_y = yarr_needle_2 - yarr_needle
|
||||
|
||||
direction_ic1_x = xarr_i12 - xarr_i11
|
||||
direction_ic1_y = yarr_i12 - yarr_i11
|
||||
|
||||
direction_ic2_x = xarr_i22 - xarr_i21
|
||||
direction_ic2_y = yarr_i22 - yarr_i21
|
||||
|
||||
direction_guard_x = xarrg_2 - xarrg_1
|
||||
direction_guard_y = yarrg_2 - yarrg_1
|
||||
|
||||
direction_anodes_x = xarra_2 - xarra_1
|
||||
direction_anodes_y = yarra_2 - yarra_1
|
||||
|
||||
direction_cathodes_x = xarrc_2 - xarrc_1
|
||||
direction_cathodes_y = yarrc_2 - yarrc_1
|
||||
|
||||
t = (z_loc + 174.3) / (2 * 174.3) # z=-174.3 is 0, z=+174.3 is 1
|
||||
|
||||
xloc_needle = xarr_needle + t * direction_needle_x
|
||||
yloc_needle = yarr_needle + t * direction_needle_y
|
||||
xloc_i1 = xarr_i11 + t * direction_ic1_x
|
||||
yloc_i1 = yarr_i11 + t * direction_ic1_y
|
||||
xloc_i2 = xarr_i21 + t * direction_ic2_x
|
||||
yloc_i2 = yarr_i21 + t * direction_ic2_y
|
||||
xloc_g = xarrg_1 + t * direction_guard_x
|
||||
yloc_g = yarrg_1 + t * direction_guard_y
|
||||
xloc_a = xarra_1 + t * direction_anodes_x
|
||||
yloc_a = yarra_1 + t * direction_anodes_y
|
||||
xloc_c = xarrc_1 + t * direction_cathodes_x
|
||||
yloc_c = yarrc_1 + t * direction_cathodes_y
|
||||
|
||||
# wire_radius_a = 0.018 #mm
|
||||
# wire_radius_c = 0.0762 #mm
|
||||
# wire_radius_g = 0.0762 #mm
|
||||
wire_radius = 0.254 # mm
|
||||
needle = []
|
||||
ic1_wires = []
|
||||
ic2_wires = []
|
||||
guard_wires = []
|
||||
anode_wires = []
|
||||
cathode_wires = []
|
||||
iw1_tags = [(3, i) for i in range(24)]
|
||||
iw2_tags = [(3, i + 24) for i in range(24)]
|
||||
gw_tags = [(3, i + 48) for i in range(48)]
|
||||
aw_tags = [(3, i) for i in range(24)]
|
||||
cw_tags = [(3, i + 24) for i in range(24)]
|
||||
|
||||
# for i,[xa,ya,xc,yc] in enumerate(zip(xarra_1,yarra_1,xarrc_1,yarrc_1)):
|
||||
# create Hot Needle (1 total)
|
||||
for i, (xn, yn) in enumerate(zip(xloc_needle, yloc_needle)):
|
||||
if include_needle:
|
||||
ndisk = gmsh.model.occ.addDisk(xn, yn, 0, wire_radius, wire_radius)
|
||||
needle.append(ndisk)
|
||||
|
||||
# create Guard Wires (48 total)
|
||||
for xg, yg in zip(xloc_g, yloc_g):
|
||||
gdisk = gmsh.model.occ.addDisk(xg, yg, 0, wire_radius, wire_radius)
|
||||
guard_wires.append(gdisk)
|
||||
|
||||
# create Cathode Wires (24 total)
|
||||
for xc, yc in zip(xloc_c, yloc_c):
|
||||
cdisk = gmsh.model.occ.addDisk(xc, yc, 0, wire_radius, wire_radius)
|
||||
cathode_wires.append(cdisk)
|
||||
|
||||
# create IC Anode and Cathode Wires (24 total each)
|
||||
for i, (xa, ya) in enumerate(zip(xloc_a, yloc_a)):
|
||||
adisk = gmsh.model.occ.addDisk(xa, ya, 0, wire_radius, wire_radius)
|
||||
anode_wires.append(adisk)
|
||||
|
||||
# Place IC wires only if flag is True
|
||||
if include_ic_wires:
|
||||
i1disk = gmsh.model.occ.addDisk(
|
||||
xloc_i1[i], yloc_i1[i], 0, wire_radius, wire_radius
|
||||
)
|
||||
i2disk = gmsh.model.occ.addDisk(
|
||||
xloc_i2[i], yloc_i2[i], 0, wire_radius, wire_radius
|
||||
)
|
||||
ic1_wires.append(i1disk)
|
||||
ic2_wires.append(i2disk)
|
||||
|
||||
anasen_barrel = gmsh.model.occ.addDisk(0, 0, 0, 500, 500)
|
||||
# gmsh.model.occ.synchronize()
|
||||
# gmsh.model.mesh.embed(1,anode_wires+cathode_wires,2,anasen_barrel)
|
||||
|
||||
gmsh.option.setNumber("Geometry.Tolerance", 1e-6)
|
||||
gmsh.option.setNumber("Geometry.OCCFixDegenerated", 1)
|
||||
gmsh.model.occ.synchronize()
|
||||
|
||||
|
||||
# --- Surface Extraction ---
|
||||
def get_surfs(disks):
|
||||
surfs = []
|
||||
for d in disks:
|
||||
surfs += [
|
||||
s[1] for s in gmsh.model.getBoundary([(2, d)], oriented=False) if s[0] == 1
|
||||
]
|
||||
return surfs
|
||||
|
||||
|
||||
if not 0 <= selected_cathode < len(cathode_wires):
|
||||
print("selected_cathode must be 0..%d" % (len(cathode_wires) - 1))
|
||||
quit()
|
||||
|
||||
cathode_wires_other = [
|
||||
disk for i, disk in enumerate(cathode_wires) if i != selected_cathode
|
||||
]
|
||||
|
||||
# Cut the wire disks into the gas disk, replacing the old mesh.embed() call.
|
||||
# embed only makes the mesher honour the curves; fragment splits the barrel
|
||||
# into conforming surfaces, which is what lets one wire carry its own BC.
|
||||
gmsh.model.occ.synchronize()
|
||||
|
||||
all_wire_disks = needle + guard_wires + cathode_wires + anode_wires
|
||||
if include_ic_wires:
|
||||
all_wire_disks += ic1_wires + ic2_wires
|
||||
|
||||
gmsh.model.occ.fragment([(2, anasen_barrel)], [(2, d) for d in all_wire_disks])
|
||||
gmsh.model.occ.synchronize()
|
||||
|
||||
# fragment re-derives the bounding curves, so re-extract them.
|
||||
needle_surfs = get_surfs(needle) if include_needle else []
|
||||
gwire_surfs = get_surfs(guard_wires)
|
||||
awire_surfs = get_surfs(anode_wires)
|
||||
cwire_surfs = get_surfs(cathode_wires_other)
|
||||
cwire_sel_surfs = get_surfs([cathode_wires[selected_cathode]])
|
||||
i1wire_surfs = get_surfs(ic1_wires) if include_ic_wires else []
|
||||
i2wire_surfs = get_surfs(ic2_wires) if include_ic_wires else []
|
||||
|
||||
all_active_wire_surfs = (
|
||||
needle_surfs
|
||||
+ gwire_surfs
|
||||
+ awire_surfs
|
||||
+ cwire_surfs
|
||||
+ cwire_sel_surfs
|
||||
+ i1wire_surfs
|
||||
+ i2wire_surfs
|
||||
)
|
||||
|
||||
f1 = gmsh.model.mesh.field.add("Distance")
|
||||
gmsh.model.mesh.field.setNumbers(f1, "CurvesList", all_active_wire_surfs)
|
||||
|
||||
f2 = gmsh.model.mesh.field.add("Threshold")
|
||||
gmsh.model.mesh.field.setNumber(f2, "InField", f1)
|
||||
gmsh.model.mesh.field.setNumber(f2, "SizeMin", 0.05) # Fine mesh near wires
|
||||
gmsh.model.mesh.field.setNumber(f2, "SizeMax", 5.0) # Large mesh in empty space
|
||||
gmsh.model.mesh.field.setNumber(f2, "DistMin", 0.5) # Apply SizeMin within 1mm
|
||||
gmsh.model.mesh.field.setNumber(f2, "DistMax", 15.0) # Transition to SizeMax by 20mm
|
||||
|
||||
gmsh.model.mesh.field.setAsBackgroundMesh(f2)
|
||||
|
||||
|
||||
# --- Physical Groups ---
|
||||
# These tags are the Target Bodies / Target Boundaries numbers in the .sif
|
||||
# files. Do not renumber them without changing both sifs -- and note that
|
||||
# ElmerGrid's -autoclean renumbers them for you, which silently breaks every
|
||||
# boundary condition.
|
||||
# Needle
|
||||
if include_needle:
|
||||
gmsh.model.addPhysicalGroup(1, needle_surfs, tag=1, name="hot_needle")
|
||||
|
||||
# IC Wires
|
||||
if include_ic_wires:
|
||||
gmsh.model.addPhysicalGroup(1, i1wire_surfs, tag=2, name="ic_wire_1")
|
||||
gmsh.model.addPhysicalGroup(1, i2wire_surfs, tag=3, name="ic_wire_2")
|
||||
|
||||
# Proportional Counter Wires
|
||||
gmsh.model.addPhysicalGroup(1, gwire_surfs, tag=10, name="guard_wires")
|
||||
gmsh.model.addPhysicalGroup(1, awire_surfs, tag=20, name="anode_wires")
|
||||
gmsh.model.addPhysicalGroup(1, cwire_surfs, tag=30, name="cathode_wires")
|
||||
|
||||
# The one cathode the weighting potential is solved for, kept out of tag 30 so
|
||||
# wires2d_weight.sif can drive it independently.
|
||||
gmsh.model.addPhysicalGroup(1, cwire_sel_surfs, tag=40, name="cathode_wire_selected")
|
||||
|
||||
# Gas Volume (2D). fragment split the barrel into many surfaces, so collect
|
||||
# them all; naming the original disk alone would solve one sliver of the domain.
|
||||
all_surfaces_2d = [s[1] for s in gmsh.model.getEntities(dim=2)]
|
||||
gmsh.model.addPhysicalGroup(2, all_surfaces_2d, tag=13, name="gas")
|
||||
|
||||
gmsh.option.setNumber("Mesh.Algorithm", 5)
|
||||
|
||||
gmsh.model.mesh.generate(2)
|
||||
gmsh.model.mesh.setOrder(2)
|
||||
# recombine() ran for over half an hour on the barrel surface without
|
||||
# finishing, and refine() took the mesh to ~16M nodes. Neither is needed: the
|
||||
# Threshold field above already gives 0.05 mm elements on 0.254 mm wires.
|
||||
# gmsh.model.mesh.recombine()
|
||||
# gmsh.model.mesh.refine()
|
||||
gmsh.write("wires2d.msh")
|
||||
# gmsh.fltk.run()
|
||||
gmsh.finalize()
|
||||
89
centroids_edited.txt
Normal file
89
centroids_edited.txt
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
HistogramIndex PeakNumber Centroid Amplitude Sigma
|
||||
1 1 922.213
|
||||
1 2 1885.55
|
||||
1 3 2845.53
|
||||
1 4 3810.32
|
||||
2 1 955.591
|
||||
2 2 1953.17
|
||||
2 3 2949.37
|
||||
2 4 3950.79
|
||||
3 1 995.787
|
||||
3 2 2036.58
|
||||
3 3 3076.91
|
||||
3 4 4112.05
|
||||
4 1 1017.48
|
||||
4 2 2080.19
|
||||
4 3 3142.24
|
||||
4 4 4206.1
|
||||
5 1 1022.78
|
||||
5 2 2091.21
|
||||
5 3 3158.28
|
||||
5 4 4226.97
|
||||
6 1 1076.22
|
||||
6 2 2203.37
|
||||
6 3 3329.53
|
||||
6 4 4457.69
|
||||
7 1 977.46
|
||||
7 2 1998.02
|
||||
7 3 3017.36
|
||||
7 4 4040.47
|
||||
8 1 1049.74
|
||||
8 2 2144.38
|
||||
8 3 3238.2
|
||||
8 4 4335.25
|
||||
9 1 1000.59
|
||||
9 2 2046.42
|
||||
9 3 3090.29
|
||||
9 4 4129.63
|
||||
10 1 1014.92
|
||||
10 2 2076.16
|
||||
10 3 3134.59
|
||||
10 4 4213.42
|
||||
11 1 1004.85
|
||||
11 2 2052.88
|
||||
11 3 3100.3
|
||||
11 4 4164.75
|
||||
12 1 945.861
|
||||
12 2 1932.49
|
||||
12 3 2917.95
|
||||
12 4 3955.15
|
||||
13 1 998.307
|
||||
13 2 2040.38
|
||||
13 3 3078.76
|
||||
13 4 4135.51
|
||||
14 1 966.429
|
||||
14 2 1972.15
|
||||
14 3 2974.84
|
||||
14 4 4056.41
|
||||
15 1 958.352
|
||||
15 2 1958.64
|
||||
15 3 2957.7
|
||||
15 4 3970.41
|
||||
16 1 970.732
|
||||
16 2 1977.63
|
||||
16 3 2984.97
|
||||
16 4 4002.56
|
||||
17 1 1013.65
|
||||
17 2 2064.9
|
||||
17 3 3114.19
|
||||
17 4 4190.98
|
||||
18 1 975.538
|
||||
18 2 1990.64
|
||||
18 3 3005.46
|
||||
18 4 4048.99
|
||||
20 1 912.778
|
||||
20 2 1866.83
|
||||
20 3 2819.21
|
||||
20 4 3781.63
|
||||
21 1 1002.36
|
||||
21 2 1989.95
|
||||
21 3 2975.53
|
||||
21 4 3986.71
|
||||
22 1 1075.38
|
||||
22 2 2144.25
|
||||
22 3 3210.17
|
||||
22 4 4312.84
|
||||
23 1 988.828
|
||||
23 2 2016.35
|
||||
23 3 3044.19
|
||||
23 4 4082.41
|
||||
197
cross_section/README.md
Executable file
197
cross_section/README.md
Executable file
|
|
@ -0,0 +1,197 @@
|
|||
# Cross-section pipeline (ANASEN active target)
|
||||
|
||||
This folder turns data into a cross section for any reaction in the ANASEN active target. There are three steps,
|
||||
one script each, and one config file per study shared by all three:
|
||||
|
||||
```
|
||||
root -l -b -q 'extract_yields.C+("examples/27Al_ap_runA.toml")' # 1. ROOT -> yields_<det>.csv
|
||||
~/myenv/bin/python -B geometric_efficiency.py examples/27Al_ap_runA.toml # 2. detectors.py -> eps_<det>.csv ~10 s
|
||||
~/myenv/bin/python -B cross_section.py examples/27Al_ap_runA.toml # 3. -> xs_<det>.csv, xs.png ~3 s
|
||||
```
|
||||
|
||||
Run them in WSL from this folder (`~/ANASEN_analysis/cross_section`). Step 1 needs ROOT; steps 2 and 3 need
|
||||
`~/myenv/bin/python`, not ROOT.
|
||||
|
||||
| file | what it does |
|
||||
|---|---|
|
||||
| `extract_yields.C` | **Step 1, the only step that reads the data.** It reads a TH1/TH2, or fills a histogram from a TTree with a cut, and writes a plain CSV histogram. |
|
||||
| `geometric_efficiency.py` | Step 2: geometric efficiency per bin, a toy Monte Carlo of the reaction, with every hit rule taken from `detectors.py`. |
|
||||
| `detectors.py` | **The detectors as they are now** (Run A): QQQ3, the four analysed SX3s, the counter, the beam tube and the PCB hole, plus the hit rules. To change the geometry, edit this file. |
|
||||
| `cross_section.py` | Step 3: reads only CSVs (the yields and an efficiency) and computes σ(E_cm) or dσ/dΩ(θ_cm). |
|
||||
| `common.py` | What the three scripts share: the config, the beam track E(z), the bins. |
|
||||
| `examples/` | One commented config per study: `27Al_ap_runA.toml` and `paap_ta_runs_hist.toml`. |
|
||||
| `examples/reference/` | Barnard 1964 (EXFOR A1113) for p + ⁴He. |
|
||||
| `inputs/` | The beam track (`beam_tracks.csv`) and the TALYS states (`talys_27Al_ap.csv`). The p + ⁴He track is read from `../eloss_calculations/`. |
|
||||
| `out/<study>/` | Each study's outputs: `yields_<det>.csv`, `eps_<det>.csv`, `xs_<det>.csv`, `xs.png`. |
|
||||
|
||||
## Starting a new study
|
||||
|
||||
1. Copy the closest example in `examples/` (excitation function: `27Al_ap_runA.toml`; angular distribution:
|
||||
`paap_ta_runs_hist.toml`) and give it its own `out_dir`.
|
||||
2. Fill in `[extract]` for your histogram or tree, then run step 1 and look at `yields_<det>.csv`.
|
||||
3. Fill in `[reaction]`, `[beam]` and `[bins]`, then run step 2. Check that `eps_<det>.csv` is non-zero where you
|
||||
expect events.
|
||||
4. Fill in `[target]`, `[acceptance]` and any `[[reference]]`, then run step 3.
|
||||
|
||||
If the detectors have changed, edit `detectors.py`, not the configs.
|
||||
|
||||

|
||||
|
||||
## The formula
|
||||
|
||||
| mode | result | formula |
|
||||
|---|---|---|
|
||||
| `excitation` | σ(E_cm), mb | Y / (N_b · n · dz · ε) |
|
||||
| `angular` | dσ/dΩ(θ_cm) in one E_cm (or z) window, mb/sr | Y / (N_b · n · dz · ε · ΔΩ) |
|
||||
|
||||
- **Y:** the yield in the bin.
|
||||
- **N_b:** the number of beam particles.
|
||||
- **n:** target atoms per cm² per mm of path.
|
||||
- **dz:** the length of beam path over which the beam's E_cm lies in the bin, read off the beam track E(z).
|
||||
- **ε:** the efficiency of the bin.
|
||||
|
||||
## Step 1: `extract_yields.C`, data to CSV
|
||||
|
||||
`[extract] source` says where the events are:
|
||||
- `"hist"`: a TH1 or TH2 in a ROOT file. A TH2 is projected on the axis that holds `variable`, or kept 2D with
|
||||
`angle_axis` for the angular mode.
|
||||
- `"tree"`: a ROOT TTree (`file` may be a glob or a list of runs), filled into `bins` after `cut`.
|
||||
|
||||
Cuts use `and` / `or` (the macro turns them into `&&` / `||`) and ROOT's TTree::Draw syntax.
|
||||
|
||||
**Histogram paths** are `"folder/name"`.
|
||||
- **They are case-sensitive.** Production files of different vintages spell the detector differently: the 27Al
|
||||
outputs use `_m27Alax+misc_qqq/...`, the proton runs' `Output_p/Al_protons.root` uses `elastic+misc_QQQ_a/...`.
|
||||
Put the spelling in `[det_vars]`:
|
||||
```toml
|
||||
dets = ["qqq", "sx3"] # the names detectors.py knows
|
||||
[det_vars.qqq]
|
||||
DET = "QQQ"
|
||||
[det_vars.sx3]
|
||||
DET = "SX3"
|
||||
[extract]
|
||||
hist = "elastic+misc_{DET}_a/elastic_VertexReconZ_a_{DET}"
|
||||
```
|
||||
- **A name that itself contains `/`** (e.g. `dE3_E_AnodeQQQR_A1C2_(vertex_fix_z/100)=0.000000`) is found too.
|
||||
- **If the path doesn't exist,** the macro stops and names the closest histogram paths in the file.
|
||||
|
||||
The CSV it writes, `<out_dir>/yields_<det>.csv`, is the hand-off between the steps. `cross_section.py` reads nothing
|
||||
else, so a yield made any other way works too as long as it is in this format:
|
||||
|
||||
```
|
||||
1D: <var>_lo, <var>_hi, counts, variance
|
||||
2D: <var>_lo, <var>_hi, theta_cm_lo, theta_cm_hi, counts, variance (angular mode)
|
||||
```
|
||||
|
||||
`<var>` is `z` (vertex z, mm), `e_beam` (MeV) or `ecm` (MeV). The bins may be finer than the analysis bins and need
|
||||
not line up with them. Step 3 shares each one out by overlap.
|
||||
|
||||
It is run like this:
|
||||
|
||||
```
|
||||
root -l -b -q 'extract_yields.C+("examples/27Al_ap_runA.toml")'
|
||||
root -l -b -q 'extract_yields.C+("examples/27Al_ap_runA.toml", "out/elsewhere")' # optional output folder
|
||||
```
|
||||
|
||||
- **Trees:** it fills the histogram with `TTree::Draw` on a `TChain`, so `file` may be a glob or a list of runs.
|
||||
- **Cuts:** write them with `and` / `or` (e.g. `"det == 1 and e_si > 0.5"`). The macro turns them into `&&` / `||`.
|
||||
- **TOML:** the macro has a small reader that handles only what these configs use: strings, numbers, one-line lists
|
||||
and `{start, stop, step}` tables. It stops with an error on anything else.
|
||||
- **Errors:** a missing file or tree, or a tree with no entries, stops it with exit code 1 rather than writing zeros.
|
||||
- **One edge case:** an entry exactly on a tree histogram's last upper edge goes to ROOT's overflow.
|
||||
|
||||
## Step 2: `geometric_efficiency.py`, ε from the detector geometry
|
||||
|
||||
The toy runs once per bin:
|
||||
- vertices uniform over the bin's path (angular mode: over the window);
|
||||
- beam energy at each vertex from the nominal track;
|
||||
- the residual state drawn from fixed weights, or from TALYS state cross sections at the bin's E_cm;
|
||||
- isotropic CM emission (angular mode: inside the θ_cm bin);
|
||||
- relativistic two-body kinematics and straight tracks.
|
||||
|
||||
The particle the silicon sees can be the ejectile or the recoil (`detect`). θ_cm is always the ejectile's, so with
|
||||
`detect = "recoil"` the bins are still in the ejectile's CM angle. For p + ⁴He that is the proton's angle, Barnard's
|
||||
convention, which TrackRecon's `elastic_VertexZ_vs_ThetaCM` histograms also use. The output has one column per
|
||||
condition, each times the azimuthal coverage:
|
||||
|
||||
| column | the particle... |
|
||||
|---|---|
|
||||
| `eps_si` | reaches the analysed silicon (solid angle) |
|
||||
| `eps_si_thr` | ...and has a vertex energy above the Si threshold. This is the ch5 "solid angle" convention: a kinematic cut, not geometry. |
|
||||
| `eps_counter` | `eps_si`, and it crosses the guard, anode and cathode radii inside the counter |
|
||||
| **`eps_geometric`** | `eps_counter`, and it clears the beam tube and the PCB hole. **This is the geometric efficiency.** |
|
||||
|
||||
Each column has a Monte Carlo error, `<column>_rel_err`. Gas energy loss, Si thresholds after the gas and the counter's
|
||||
deposit threshold are **not** included, because they are not geometry. That is why the adopted `eps_tube` of
|
||||
`../scratch/xs_27Al_lowE_2026-10-15` is lower than `eps_geometric`; to use it instead, see the commented
|
||||
`[acceptance]` block in the example. The angular distribution is isotropic.
|
||||
|
||||
## Step 3: `cross_section.py`
|
||||
|
||||
`[acceptance] source` picks ε:
|
||||
- `"geometric"`: step 2's file; `column` chooses which efficiency.
|
||||
- `"csv"`: any file, matched on a bin edge or interpolated.
|
||||
- `"value"`: one number for every bin.
|
||||
- `"none"`: ε = 1, which gives σ_app.
|
||||
|
||||
`[[acceptance.extra]]` multiplies in further factors, such as an intrinsic efficiency. The output `xs_<det>.csv` keeps
|
||||
every intermediate number: the bins, z ranges, dz, counts, σ_app, ε, σ and its errors (stat, ε, total), and the
|
||||
references. The header lines record every input. The N_b and n uncertainties are a common scale, so the header gives
|
||||
them as a percentage and they stay out of the error columns.
|
||||
|
||||
## The config (TOML)
|
||||
|
||||
Paths are relative to the config file, and `~` is the home folder. `{det}` is replaced by each entry of `dets`, and
|
||||
so is every key of `[det_vars.<det>]`. Commented templates in `examples/`:
|
||||
- `27Al_ap_runA.toml`: a TH2 from the merged Run A production file, all three steps.
|
||||
- `paap_ta_runs_hist.toml`: p + ⁴He elastic **straight from a TrackRecon.C production file**, as for 27Al. It reads
|
||||
the TH2 `elastic_VertexZ_vs_ThetaCM_cath_exgate_a_<DET>` (vertex z × proton θ_cm). `protonAlphaElastic_core` fills
|
||||
that histogram since 2026-10-16 in every species folder, in four variants: all, `_cath`, `_exgate`, `_cath_exgate`.
|
||||
- Input: `~/ANASEN_analysis/Output_p/results_run022.root` (run 22 alone, run_tr.sh block 5 with CUTLIST),
|
||||
N_b = 1.35 pA × 672.65 s = 5.67 × 10⁹, vertex window z = −170 to −53 mm.
|
||||
- On 2026-10-16 the merged runs 18–22 agreed with a per-hit route to 2–4% in QQQ3 counts and about 3% in dσ/dΩ.
|
||||
|
||||
| section | used by | keys |
|
||||
|---|---|---|
|
||||
| top | all | `title`, `mode` (`excitation` / `angular`), `dets` (names in `detectors.py`: `qqq`, `sx3`), `out_dir` |
|
||||
| `[extract]` histogram | 1 | `source = "hist"`, `file`, `hist`, `variable`, `axis` (the axis holding `variable`), `angle_axis` (keep it 2D), `other_range` |
|
||||
| `[extract]` tree | 1 | `source = "tree"`, `file` (glob or list), `tree`, `variable`, `branch`, `bins`, `cut`, `weight_branch`, `angle_branch` + `angle_bins` |
|
||||
| `[reaction]` | 2, 3 | `beam_mass_u`, `target_mass_u`; for step 2 also `ejectile_mass_u`, `recoil_mass_u`, `detect`, and `states` *or* `states_csv`; optional `ecm_per_ebeam` |
|
||||
| `[beam]` | 2, 3 | track: `track_csv` + `track_e_col` (+ `nominal_e_col`, `track_z_col`), *or* `eloss_table` + `e_entrance_mev` + `z_entrance_mm`. N_b: `n_beam` *or* `charge_c` *or* `current_a` + `time_s`; `charge_state`, `n_beam_rel_err`. For Run A runs, `../runlog/beam_exposure.py` gives `n_beam` for any set of runs from the elog's cup readings |
|
||||
| `[bins]` | 2, 3 | `ecm_edges` (a list or `{start, stop, step}`); angular: `theta_edges_deg` and `window_ecm` *or* `window_z_mm` |
|
||||
| `[efficiency]` | 2 | `n_per_bin` (default 400 000), `seed` |
|
||||
| `[target]` | 3 | `pressure_torr`, `temperature_k`, `mole_fraction`, `atoms_per_molecule`, `rel_err`; *or* `n_per_cm2_per_mm` |
|
||||
| `[yield]` | 3 | `csv`: only to read yields from somewhere other than step 1's output |
|
||||
| `[acceptance]` | 3 | `source` and its keys (see above), `[[acceptance.extra]]` |
|
||||
| `[[reference]]` | 3 | `csv`, `x_col`, `y_col`, `label`, `scale`, `dets` |
|
||||
| `[plot]` | 3 | `yscale`, `xlim`, `ylim`, `xlabel`, `ylabel` |
|
||||
|
||||
## Example: p + ⁴He elastic scattering, Ta run 22 (`examples/paap_ta_runs_hist.toml`)
|
||||
|
||||
```
|
||||
root -l -b -q 'extract_yields.C+("examples/paap_ta_runs_hist.toml")'
|
||||
~/myenv/bin/python -B geometric_efficiency.py examples/paap_ta_runs_hist.toml
|
||||
~/myenv/bin/python -B cross_section.py examples/paap_ta_runs_hist.toml
|
||||
```
|
||||
|
||||
dσ/dΩ(θ_cm) from the α recoils (`detect = "recoil"`, bins in the proton's θ_cm, Barnard's convention), against
|
||||
Barnard, Jones & Weil (1964). Histogram: `elastic_VertexZ_vs_ThetaCM_cath_exgate_a_<DET>` (cathode topologies,
|
||||
graphical α cut, |E_x| < 1 MeV). The merged runs 18–22 gave QQQ3 0.44–0.54 × Barnard at 132–156°; SX3 is at 1–3%.
|
||||
Not in `detectors.py`: the Ta holder rod (~28% of the azimuth downstream of each foil) and Si thresholds after the gas.
|
||||
|
||||
## Checks (run in the Windows repo, `/mnt/c/Users/sengi/ANASEN_analysis/cross_section`; the scripts are not in this copy)
|
||||
|
||||
- 27Al(α,p), Run A: yields, dz and nominal E_cm equal the earlier study's in every bin; ε_si_thr agrees with its
|
||||
independent toy within the MC errors.
|
||||
- Synthetic events with a known σ, ε and dσ/dΩ are returned with unit-Gaussian pulls.
|
||||
- `extract_yields.C` and the Python version gave byte-identical yields on every example.
|
||||
|
||||
**Not verified:** the physics inputs are whatever the config and `detectors.py` say. The checks show the arithmetic and
|
||||
the bookkeeping are right, and that `detectors.py` reproduces the geometry of `anasen_detectability.py`. They cannot
|
||||
show that the geometry, a track or an N_b is right. In particular:
|
||||
- The dE/dx scale of a `.dat` table is not written in the file. The live 250 Torr / 3% tables were regenerated on
|
||||
2026-10-16 and match the 0.962 track to 3 keV.
|
||||
- `detectors.py` gives QQQ3 0.967 of the azimuth, but TrackRecon drops QQQ pixels without a calibration, which leaves
|
||||
about 0.675. QQQ3 ε is therefore about 1.4× too high, and its cross sections about 1.4× too low.
|
||||
- Where only a sliver of solid angle is open, ε is tiny and σ explodes. Example: QQQ3 at E_cm 5.4 MeV, ε = 1.5 × 10⁻⁵,
|
||||
behind the beam tube. The error bar shows it; no ε cut is applied.
|
||||
183
cross_section/common.py
Executable file
183
cross_section/common.py
Executable file
|
|
@ -0,0 +1,183 @@
|
|||
"""Shared by extract_yields.py, geometric_efficiency.py and cross_section.py: the config, the beam track, the bins.
|
||||
|
||||
The cross section in this active target:
|
||||
sigma(E_cm) = Y / (N_b * n * dz * eps) mode = "excitation"
|
||||
dsigma/dOmega(th) = Y / (N_b * n * dz * eps * dOmega) mode = "angular"
|
||||
The beam loses energy along z, so a vertex at z means the reaction happened at the beam energy E(z) there.
|
||||
An E_cm bin is the stretch of beam path dz over which the beam's E_cm lies in the bin.
|
||||
"""
|
||||
import os
|
||||
import tomllib
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
|
||||
# ============================================================ config
|
||||
|
||||
def read_config(config_file):
|
||||
"""Read the TOML config. Returns the config and a function that turns paths in it into absolute paths
|
||||
(relative paths are relative to the config file's folder)."""
|
||||
config_file = Path(config_file).resolve()
|
||||
with open(config_file, "rb") as f:
|
||||
config = tomllib.load(f)
|
||||
|
||||
def full_path(p):
|
||||
p = os.path.expanduser(str(p)) # "~/..." is the home folder, as in a shell
|
||||
if p.startswith("/") or Path(p).is_absolute():
|
||||
return p
|
||||
return str((config_file.parent / p).resolve())
|
||||
|
||||
return config, full_path
|
||||
|
||||
|
||||
def config_for_detector(config, det):
|
||||
"""Copy of the config with "{det}" (and any key of [det_vars.<det>]) replaced in every string.
|
||||
This lets one config serve several detectors, e.g. hist = "..._{det}"."""
|
||||
replacements = {"det": det}
|
||||
replacements.update(config.get("det_vars", {}).get(det, {}))
|
||||
|
||||
def replace(value):
|
||||
if isinstance(value, str):
|
||||
for key, new in replacements.items():
|
||||
value = value.replace("{" + key + "}", str(new))
|
||||
return value
|
||||
if isinstance(value, dict):
|
||||
return {k: replace(v) for k, v in value.items()}
|
||||
if isinstance(value, list):
|
||||
return [replace(v) for v in value]
|
||||
return value
|
||||
|
||||
return replace(config)
|
||||
|
||||
|
||||
def output_file(config, full_path, kind, det):
|
||||
"""Where each step writes for one detector: <out_dir>/<kind>_<det>.csv, kind = yields, eps or xs."""
|
||||
out_dir = Path(full_path(config.get("out_dir", "out")))
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
return out_dir / (f"{kind}_{det}.csv" if det else f"{kind}.csv")
|
||||
|
||||
|
||||
def make_edges(spec):
|
||||
"""Bin edges from a list [e0, e1, ...] or from {start = .., stop = .., step = ..} (stop included)."""
|
||||
if isinstance(spec, dict):
|
||||
n_bins = int(round((spec["stop"] - spec["start"]) / spec["step"]))
|
||||
return spec["start"] + spec["step"] * np.arange(n_bins + 1)
|
||||
return np.array(spec, dtype=float)
|
||||
|
||||
|
||||
def write_csv(table, header_lines, out_file):
|
||||
with open(out_file, "w", newline="\n") as f:
|
||||
for line in header_lines:
|
||||
f.write(f"# {line}\n")
|
||||
table.to_csv(f, index=False, float_format="%.6g")
|
||||
|
||||
|
||||
# ============================================================ beam track
|
||||
|
||||
class BeamTrack:
|
||||
"""Beam energy (MeV, lab) along z (mm). The energy falls with z and is 0 past the stopping point.
|
||||
|
||||
`energy` is the track used to define the bins (the one the data were reconstructed with). `nominal` is an
|
||||
optional second track (e.g. another stopping-power scale): it gives each bin's nominal E_cm and the beam
|
||||
energy in the efficiency toy. It defaults to the same track."""
|
||||
|
||||
def __init__(self, z, energy, nominal=None, description=""):
|
||||
self.z = np.asarray(z, dtype=float)
|
||||
self.energy = np.asarray(energy, dtype=float)
|
||||
self.nominal = self.energy if nominal is None else np.asarray(nominal, dtype=float)
|
||||
self.description = description
|
||||
|
||||
def energy_at(self, z, nominal=False):
|
||||
return np.interp(z, self.z, self.nominal if nominal else self.energy)
|
||||
|
||||
def z_where_energy_is(self, e):
|
||||
"""The z where the beam has energy e.
|
||||
e <= 0 gives the stopping point; e above the entrance energy gives the entrance z."""
|
||||
if e <= 0:
|
||||
stopped = np.flatnonzero(self.energy <= 0)
|
||||
return float(self.z[stopped[0]]) if len(stopped) else float(self.z[-1])
|
||||
# np.interp needs increasing x: energy decreases along z, so interpolate in -energy
|
||||
return float(np.interp(-e, -self.energy, self.z))
|
||||
|
||||
|
||||
def load_beam_track(beam, full_path):
|
||||
"""Either a CSV with columns z and E (e.g. beam_tracks.csv), or an energy-loss table from
|
||||
eloss_calculations/ plus the beam energy at the entrance."""
|
||||
if "track_csv" in beam:
|
||||
table = pd.read_csv(full_path(beam["track_csv"]), comment="#")
|
||||
z = table[beam.get("track_z_col", "z_mm")]
|
||||
energy = table[beam["track_e_col"]]
|
||||
nominal = table[beam["nominal_e_col"]] if "nominal_e_col" in beam else None
|
||||
text = f"{beam['track_csv']}, column {beam['track_e_col']}"
|
||||
if nominal is not None:
|
||||
text += f" (nominal E_cm from {beam['nominal_e_col']})"
|
||||
return BeamTrack(z, energy, nominal, text)
|
||||
|
||||
if "eloss_table" in beam:
|
||||
# Table columns: energy (MeV), thickness (mg/cm2), distance along the path (cm)
|
||||
table = np.loadtxt(full_path(beam["eloss_table"]), comments="#")
|
||||
table_energy, table_cm = table[:, 0], table[:, 2]
|
||||
e_entrance, z_entrance = float(beam["e_entrance_mev"]), float(beam["z_entrance_mm"])
|
||||
if e_entrance > table_energy[0]:
|
||||
raise ValueError(f"e_entrance_mev = {e_entrance} is above the table's start energy {table_energy[0]}")
|
||||
# Where in the table the beam starts, then walk along it in 0.1 mm steps
|
||||
start_cm = np.interp(-e_entrance, -table_energy, table_cm)
|
||||
z = np.arange(z_entrance, beam.get("z_max_mm", 400.0) + 0.05, 0.1)
|
||||
path_cm = start_cm + (z - z_entrance) / 10.0
|
||||
energy = np.interp(path_cm, table_cm, table_energy, right=0.0)
|
||||
return BeamTrack(z, energy, None, f"{beam['eloss_table']}, {e_entrance} MeV at z = {z_entrance} mm")
|
||||
|
||||
raise KeyError("[beam] needs either track_csv or eloss_table")
|
||||
|
||||
|
||||
def ecm_per_beam_energy(reaction):
|
||||
"""E_cm = E_beam x m_target / (m_beam + m_target) (non-relativistic; or give ecm_per_ebeam directly)."""
|
||||
if "ecm_per_ebeam" in reaction:
|
||||
return float(reaction["ecm_per_ebeam"])
|
||||
return reaction["target_mass_u"] / (reaction["beam_mass_u"] + reaction["target_mass_u"])
|
||||
|
||||
|
||||
# ============================================================ bins
|
||||
|
||||
def energy_bins(bins, track, k):
|
||||
"""E_cm bins and, for each, the stretch of beam path [z_up, z_down] where the beam's E_cm is inside it.
|
||||
The higher energy is upstream, so z_up belongs to the bin's top edge."""
|
||||
ecm_edges = make_edges(bins["ecm_edges"])
|
||||
t = pd.DataFrame({"ecm_lo_mev": ecm_edges[:-1], "ecm_hi_mev": ecm_edges[1:]})
|
||||
t["ecm_c_mev"] = 0.5 * (t["ecm_lo_mev"] + t["ecm_hi_mev"])
|
||||
t["z_up_mm"] = [track.z_where_energy_is(e / k) for e in t["ecm_hi_mev"]]
|
||||
t["z_c_mm"] = [track.z_where_energy_is(e / k) for e in t["ecm_c_mev"]]
|
||||
t["z_down_mm"] = [track.z_where_energy_is(e / k) for e in t["ecm_lo_mev"]]
|
||||
t["dz_mm"] = t["z_down_mm"] - t["z_up_mm"]
|
||||
t["ecm_nominal_mev"] = k * track.energy_at(t["z_c_mm"], nominal=True)
|
||||
# True if the beam enters the chamber already inside this bin (dz then starts at the entrance)
|
||||
t["partial_top"] = t["ecm_hi_mev"] / k > track.energy[0]
|
||||
t["d_omega_sr"] = 1.0 # not used in excitation mode
|
||||
return t
|
||||
|
||||
|
||||
def angle_bins(bins, track, k):
|
||||
"""theta_cm bins inside one window, given in E_cm (window_ecm) or in vertex z (window_z_mm).
|
||||
Returns the bins and the window as (ecm_lo, ecm_hi, z_up, z_down)."""
|
||||
if "window_ecm" in bins:
|
||||
ecm_lo, ecm_hi = bins["window_ecm"]
|
||||
z_up, z_down = track.z_where_energy_is(ecm_hi / k), track.z_where_energy_is(ecm_lo / k)
|
||||
else:
|
||||
z_up, z_down = bins["window_z_mm"]
|
||||
ecm_lo, ecm_hi = k * track.energy_at(z_down), k * track.energy_at(z_up)
|
||||
theta_edges = make_edges(bins["theta_edges_deg"])
|
||||
t = pd.DataFrame({"theta_lo_deg": theta_edges[:-1], "theta_hi_deg": theta_edges[1:]})
|
||||
t["theta_c_deg"] = 0.5 * (t["theta_lo_deg"] + t["theta_hi_deg"])
|
||||
t["d_omega_sr"] = 2 * np.pi * (np.cos(np.radians(t["theta_lo_deg"])) - np.cos(np.radians(t["theta_hi_deg"])))
|
||||
t["z_up_mm"], t["z_down_mm"] = z_up, z_down
|
||||
t["dz_mm"] = z_down - z_up
|
||||
return t, (ecm_lo, ecm_hi, z_up, z_down)
|
||||
|
||||
|
||||
def make_bins(config, track, k):
|
||||
"""The bins of the config's mode. Returns (table, window or None)."""
|
||||
if config.get("mode", "excitation") == "excitation":
|
||||
return energy_bins(config["bins"], track, k), None
|
||||
return angle_bins(config["bins"], track, k)
|
||||
332
cross_section/cross_section.py
Executable file
332
cross_section/cross_section.py
Executable file
|
|
@ -0,0 +1,332 @@
|
|||
"""Step 3: the cross section, from the yields CSV (step 1) and an efficiency (step 2, or any other).
|
||||
|
||||
~/myenv/bin/python -B cross_section.py examples/27Al_ap_runA.toml # no ROOT needed; any python with pandas
|
||||
|
||||
sigma(E_cm) = Y / (N_b * n * dz * eps) mode = "excitation"
|
||||
dsigma/dOmega(th) = Y / (N_b * n * dz * eps * dOmega) mode = "angular"
|
||||
|
||||
Y yield in the bin, from <out_dir>/yields_<det>.csv (or [yield] csv); see extract_yields.py for the format
|
||||
N_b number of beam particles [beam]
|
||||
n target atoms per cm^2 per mm of beam path [target]
|
||||
dz length of beam path (mm) over which the beam's E_cm lies in the bin, from the beam track E(z)
|
||||
eps efficiency of the bin [acceptance]
|
||||
dOmega solid angle of the theta_cm bin (angular mode)
|
||||
|
||||
Writes <out_dir>/xs_<det>.csv (every intermediate number; the header records every input) and <out_dir>/xs.png.
|
||||
|
||||
WHAT IT DOES, STEP BY STEP (see analyse_detector())
|
||||
1. beam track: beam energy E at every z
|
||||
2. normalisation: N_b and n
|
||||
3. bins: E_cm bins -> their z ranges and dz (or theta_cm bins inside one energy window)
|
||||
4. yields: share the yield histogram's bins into those bins
|
||||
5. efficiency: eps for each bin
|
||||
6. cross section: the formula, with statistical and efficiency errors
|
||||
"""
|
||||
import argparse
|
||||
import datetime
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
import common
|
||||
|
||||
BOLTZMANN = 1.380649e-23 # J/K
|
||||
PASCAL_PER_TORR = 133.322368
|
||||
ELEMENTARY_CHARGE = 1.602176634e-19 # C
|
||||
CM2_PER_MB = 1e-27
|
||||
YIELD_VARIABLES = ("z", "e_beam", "ecm")
|
||||
|
||||
|
||||
# ============================================================ normalisation
|
||||
|
||||
def number_of_beam_particles(beam):
|
||||
"""N_b from: n_beam directly, or integrated charge, or current x time. Returns (N_b, relative error, text)."""
|
||||
charge_state = beam.get("charge_state", 1)
|
||||
if "n_beam" in beam:
|
||||
n_b, text = beam["n_beam"], "given"
|
||||
elif "charge_c" in beam:
|
||||
n_b = beam["charge_c"] / (charge_state * ELEMENTARY_CHARGE)
|
||||
text = f"{beam['charge_c']} C / ({charge_state} e)"
|
||||
elif "current_a" in beam:
|
||||
n_b = beam["current_a"] * beam["time_s"] / (charge_state * ELEMENTARY_CHARGE)
|
||||
text = f"{beam['current_a']} A x {beam['time_s']} s / ({charge_state} e)"
|
||||
else:
|
||||
raise KeyError("[beam] needs n_beam, charge_c, or current_a and time_s")
|
||||
return float(n_b), float(beam.get("n_beam_rel_err", 0.0)), text
|
||||
|
||||
|
||||
def target_atoms_per_cm2_per_mm(target):
|
||||
"""n = (gas molecules per cm^3) x (mole fraction of the target molecule) x (target atoms per molecule) x 0.1 cm/mm.
|
||||
E.g. He in 250 Torr He + 3% CO2 at 293.15 K: mole_fraction 0.97, atoms_per_molecule 1 -> 7.988e17.
|
||||
Returns (n, relative error, text)."""
|
||||
if "n_per_cm2_per_mm" in target:
|
||||
return float(target["n_per_cm2_per_mm"]), float(target.get("rel_err", 0.0)), "given"
|
||||
pressure_pa = target["pressure_torr"] * PASCAL_PER_TORR
|
||||
molecules_per_cm3 = pressure_pa / (BOLTZMANN * target["temperature_k"]) * 1e-6
|
||||
fraction = target.get("mole_fraction", 1.0)
|
||||
atoms = target.get("atoms_per_molecule", 1)
|
||||
n = molecules_per_cm3 * fraction * atoms * 0.1
|
||||
text = f"{target['pressure_torr']} Torr, {target['temperature_k']} K, mole fraction {fraction}, {atoms} atom(s)/molecule"
|
||||
return n, float(target.get("rel_err", 0.0)), text
|
||||
|
||||
|
||||
# ============================================================ yields
|
||||
|
||||
def read_yields(file):
|
||||
"""The yields CSV of extract_yields.py. Returns (table, variable), variable = z, e_beam or ecm."""
|
||||
table = pd.read_csv(file, comment="#")
|
||||
for variable in YIELD_VARIABLES:
|
||||
if f"{variable}_lo" in table.columns:
|
||||
return table, variable
|
||||
raise ValueError(f"{file}: no column z_lo, e_beam_lo or ecm_lo (see extract_yields.py for the format)")
|
||||
|
||||
|
||||
def in_yield_units(variable, k, ecm_range, z_range):
|
||||
"""Convert a bin, given both as an E_cm range and a z range, into the units of the yield's variable."""
|
||||
if variable == "ecm":
|
||||
return ecm_range
|
||||
if variable == "e_beam":
|
||||
return ecm_range[0] / k, ecm_range[1] / k
|
||||
return z_range
|
||||
|
||||
|
||||
def fraction_inside(src_lo, src_hi, lo, hi):
|
||||
"""Fraction of each yield bin [src_lo, src_hi) that lies inside [lo, hi). Used to share the yield's bins
|
||||
between the analysis bins when their edges do not line up."""
|
||||
return np.clip(np.minimum(src_hi, hi) - np.maximum(src_lo, lo), 0, None) / (src_hi - src_lo)
|
||||
|
||||
|
||||
def yields_excitation(table, variable, t, k):
|
||||
"""Yield and its error in each E_cm bin."""
|
||||
if "theta_cm_lo" in table.columns: # a 2D yield: add up the theta bins first
|
||||
table = table.groupby([f"{variable}_lo", f"{variable}_hi"], as_index=False)[["counts", "variance"]].sum()
|
||||
src_lo, src_hi = table[f"{variable}_lo"].to_numpy(), table[f"{variable}_hi"].to_numpy()
|
||||
lo, hi = in_yield_units(variable, k, (t["ecm_lo_mev"], t["ecm_hi_mev"]), (t["z_up_mm"], t["z_down_mm"]))
|
||||
counts, errors = [], []
|
||||
for a, b in zip(lo, hi):
|
||||
f = fraction_inside(src_lo, src_hi, a, b)
|
||||
counts.append(np.sum(f * table["counts"]))
|
||||
errors.append(np.sqrt(np.sum(f ** 2 * table["variance"])))
|
||||
return np.array(counts), np.array(errors)
|
||||
|
||||
|
||||
def yields_angular(table, variable, t, window, k):
|
||||
"""Yield and its error in each theta_cm bin, for the part of the yield inside the energy (or z) window."""
|
||||
if "theta_cm_lo" not in table.columns:
|
||||
raise ValueError("angular mode needs a 2D yield (theta_cm_lo/hi columns): set angle_axis or angle_branch in [extract]")
|
||||
ecm_lo, ecm_hi, z_up, z_down = window
|
||||
w_lo, w_hi = in_yield_units(variable, k, (ecm_lo, ecm_hi), (z_up, z_down))
|
||||
in_window = fraction_inside(table[f"{variable}_lo"].to_numpy(), table[f"{variable}_hi"].to_numpy(), w_lo, w_hi)
|
||||
th_lo, th_hi = table["theta_cm_lo"].to_numpy(), table["theta_cm_hi"].to_numpy()
|
||||
counts, errors = [], []
|
||||
for a, b in zip(t["theta_lo_deg"], t["theta_hi_deg"]):
|
||||
f = in_window * fraction_inside(th_lo, th_hi, a, b)
|
||||
counts.append(np.sum(f * table["counts"]))
|
||||
errors.append(np.sqrt(np.sum(f ** 2 * table["variance"])))
|
||||
return np.array(counts), np.array(errors)
|
||||
|
||||
|
||||
# ============================================================ efficiency
|
||||
|
||||
def efficiency(config, det, t, full_path):
|
||||
"""eps and its relative error for every bin. [acceptance] source =
|
||||
"geometric": <out_dir>/eps_<det>.csv from geometric_efficiency.py; `column` picks the efficiency
|
||||
(default eps_geometric), its Monte Carlo error comes with it
|
||||
"csv": any file: csv, x_col, eps_col, match = "bin" (same bin edge: our column `key` = their x_col)
|
||||
or "interp" (interpolate at our column `key`; NaN outside the file's range), rel_err_col or rel_err
|
||||
"value": one number for every bin (value, rel_err)
|
||||
"none": eps = 1, the apparent cross section (the default)
|
||||
[[acceptance.extra]] name, value, rel_err: further factors, multiplied in (e.g. an intrinsic efficiency)."""
|
||||
eff = config.get("acceptance", {})
|
||||
mode = config.get("mode", "excitation")
|
||||
source = eff.get("source", "none")
|
||||
n_bins = len(t)
|
||||
eps, rel_err = np.ones(n_bins), np.zeros(n_bins)
|
||||
text = "none (eps = 1: apparent cross section)"
|
||||
|
||||
if source == "geometric":
|
||||
column = eff.get("column", "eps_geometric")
|
||||
file = common.output_file(config, full_path, "eps", det)
|
||||
eff = {"csv": str(file), "match": "bin", "eps_col": column, "rel_err_col": f"{column}_rel_err",
|
||||
"x_col": "ecm_lo_mev" if mode == "excitation" else "theta_lo_deg", "extra": eff.get("extra", [])}
|
||||
source = "csv"
|
||||
if source == "csv":
|
||||
table = pd.read_csv(full_path(eff["csv"]), comment="#")
|
||||
match = eff.get("match", "interp")
|
||||
default_key = {("excitation", "bin"): "ecm_lo_mev", ("excitation", "interp"): "ecm_nominal_mev",
|
||||
("angular", "bin"): "theta_lo_deg", ("angular", "interp"): "theta_c_deg"}[(mode, match)]
|
||||
key = eff.get("key", default_key)
|
||||
their_x = table[eff["x_col"]].to_numpy(float)
|
||||
their_eps = table[eff["eps_col"]].to_numpy(float)
|
||||
if "rel_err_col" in eff:
|
||||
their_err = table[eff["rel_err_col"]].to_numpy(float)
|
||||
else:
|
||||
their_err = np.full(len(table), eff.get("rel_err", 0.0))
|
||||
|
||||
ours = t[key].to_numpy(float)
|
||||
if match == "bin":
|
||||
for i, x in enumerate(ours):
|
||||
same = np.flatnonzero(np.isclose(their_x, x, rtol=1e-6, atol=1e-9))
|
||||
eps[i] = their_eps[same[0]] if len(same) else np.nan
|
||||
rel_err[i] = their_err[same[0]] if len(same) else np.nan
|
||||
else:
|
||||
order = np.argsort(their_x)
|
||||
eps = np.interp(ours, their_x[order], their_eps[order], left=np.nan, right=np.nan)
|
||||
rel_err = np.interp(ours, their_x[order], their_err[order], left=np.nan, right=np.nan)
|
||||
text = f"{eff['csv']}, column {eff['eps_col']} ({match} on {eff['x_col']} = our {key})"
|
||||
elif source == "value":
|
||||
eps = np.full(n_bins, float(eff["value"]))
|
||||
rel_err = np.full(n_bins, float(eff.get("rel_err", 0.0)))
|
||||
text = f"constant {eff['value']}"
|
||||
elif source != "none":
|
||||
raise ValueError(f"[acceptance] source must be geometric, csv, value or none, not {source!r}")
|
||||
|
||||
for extra in eff.get("extra", []):
|
||||
eps = eps * extra["value"]
|
||||
rel_err = np.hypot(rel_err, extra.get("rel_err", 0.0))
|
||||
text += f" x {extra.get('name', 'extra factor')} {extra['value']}"
|
||||
return eps, rel_err, text
|
||||
|
||||
|
||||
# ============================================================ cross section
|
||||
|
||||
def cross_section(t, n_b, n, eps, eps_rel_err, name):
|
||||
"""The formula. `name` is "sigma_mb" (excitation) or "dsdo_mb_sr" (angular). Adds columns to t:
|
||||
<name>_app, <name>_app_err apparent cross section (eps = 1), statistical error
|
||||
<name>, <name>_err_stat divided by eps
|
||||
<name>_err_eps, <name>_err error from eps; total (stat and eps in quadrature)
|
||||
The N_b and n uncertainties are a common scale for all bins and are NOT in these columns."""
|
||||
exposure = n_b * n * t["dz_mm"] * t["d_omega_sr"] * CM2_PER_MB # events per mb (per mb/sr)
|
||||
exposure = exposure.where(exposure > 0) # dz = 0 (past the stopping point) -> NaN
|
||||
eps = pd.Series(eps, index=t.index).where(lambda e: e > 0) # eps = 0 -> NaN
|
||||
t[f"{name}_app"] = t["counts"] / exposure
|
||||
t[f"{name}_app_err"] = t["stat_err"] / exposure
|
||||
t["eps"] = eps
|
||||
t["eps_rel_err"] = eps_rel_err
|
||||
t[name] = t[f"{name}_app"] / eps
|
||||
t[f"{name}_err_stat"] = t[f"{name}_app_err"] / eps
|
||||
t[f"{name}_err_eps"] = t[name] * t["eps_rel_err"]
|
||||
t[f"{name}_err"] = np.hypot(t[f"{name}_err_stat"], t[f"{name}_err_eps"])
|
||||
return t
|
||||
|
||||
|
||||
def add_references(t, references, det, x_col, full_path):
|
||||
"""Reference curves ([[reference]]: csv, x_col, y_col, label, optional scale and dets) interpolated at our x
|
||||
(NaN outside their range). Returns the curves for the plot."""
|
||||
curves = []
|
||||
for ref in references or []:
|
||||
if "dets" in ref and det not in ref["dets"]:
|
||||
continue
|
||||
table = pd.read_csv(full_path(ref["csv"]), comment="#").sort_values(ref["x_col"])
|
||||
x = table[ref["x_col"]].to_numpy(float)
|
||||
y = ref.get("scale", 1.0) * table[ref["y_col"]].to_numpy(float)
|
||||
label = ref.get("label", Path(ref["csv"]).stem)
|
||||
column = "ref_" + re.sub(r"\W+", "_", label).strip("_").lower()
|
||||
t[column] = np.interp(t[x_col], x, y, left=np.nan, right=np.nan)
|
||||
curves.append((label, x, y))
|
||||
return curves
|
||||
|
||||
|
||||
def analyse_detector(config, det, full_path):
|
||||
"""Steps 1-6 for one detector. Returns the result table, its header lines, the x column, the cross-section
|
||||
column name and the reference curves."""
|
||||
mode = config.get("mode", "excitation")
|
||||
k = common.ecm_per_beam_energy(config["reaction"])
|
||||
|
||||
# 1. beam track
|
||||
track = common.load_beam_track(config["beam"], full_path)
|
||||
|
||||
# 2. normalisation
|
||||
n_b, n_b_rel_err, n_b_text = number_of_beam_particles(config["beam"])
|
||||
n, n_rel_err, n_text = target_atoms_per_cm2_per_mm(config["target"])
|
||||
|
||||
# 3. bins and 4. yields
|
||||
yield_file = full_path(config["yield"]["csv"]) if "csv" in config.get("yield", {}) \
|
||||
else common.output_file(config, full_path, "yields", det)
|
||||
yields, variable = read_yields(yield_file)
|
||||
t, window = common.make_bins(config, track, k)
|
||||
if mode == "excitation":
|
||||
t["counts"], t["stat_err"] = yields_excitation(yields, variable, t, k)
|
||||
name, x_col, window_text = "sigma_mb", "ecm_nominal_mev", ""
|
||||
else:
|
||||
t["counts"], t["stat_err"] = yields_angular(yields, variable, t, window, k)
|
||||
name, x_col = "dsdo_mb_sr", "theta_c_deg"
|
||||
ecm_lo, ecm_hi, z_up, z_down = window
|
||||
window_text = f"; window E_cm {ecm_lo:.4g} to {ecm_hi:.4g} MeV, z {z_up:.2f} to {z_down:.2f} mm"
|
||||
|
||||
# 5. efficiency and 6. cross section
|
||||
eps, eps_rel_err, eps_text = efficiency(config, det, t, full_path)
|
||||
t = cross_section(t, n_b, n, eps, eps_rel_err, name)
|
||||
curves = add_references(t, config.get("reference"), det, x_col, full_path)
|
||||
|
||||
header = [
|
||||
f"{config.get('title', '')} {det.upper()}: {mode} mode{window_text}",
|
||||
f"yield: {yield_file} (binned in {variable})",
|
||||
f"beam track: {track.description}; E_cm = {k:.6g} x E_beam",
|
||||
f"N_b = {n_b:.6g} ({n_b_text}); n = {n:.6g} target atoms per cm2 per mm ({n_text})",
|
||||
f"eps: {eps_text}",
|
||||
f"common scale uncertainty from N_b and n: {100 * np.hypot(n_b_rel_err, n_rel_err):.3g} % (not in the error columns)",
|
||||
f"written by cross_section.py on {datetime.date.today().isoformat()}",
|
||||
]
|
||||
return t, header, x_col, name, curves
|
||||
|
||||
|
||||
def make_plot(results, config, out_file):
|
||||
import matplotlib
|
||||
matplotlib.use("Agg")
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
opts = config.get("plot", {})
|
||||
excitation = config.get("mode", "excitation") == "excitation"
|
||||
fig, ax = plt.subplots(figsize=opts.get("figsize", (9, 6)))
|
||||
plotted_refs = set()
|
||||
for det, (t, x_col, name, curves) in results.items():
|
||||
ok = np.isfinite(t[name]) & (t[name] > 0)
|
||||
ax.errorbar(t.loc[ok, x_col], t.loc[ok, name], yerr=t.loc[ok, f"{name}_err"],
|
||||
fmt="o", ms=4, capsize=2, label=det.upper() or "data")
|
||||
for label, x, y in curves:
|
||||
if label not in plotted_refs:
|
||||
ax.plot(x, y, "-", lw=1.2, label=label)
|
||||
plotted_refs.add(label)
|
||||
ax.set_xlabel(opts.get("xlabel", "E$_\\mathrm{cm}$ (MeV)" if excitation else "$\\theta_\\mathrm{cm}$ (deg)"))
|
||||
ax.set_ylabel(opts.get("ylabel", "$\\sigma$ (mb)" if excitation else "d$\\sigma$/d$\\Omega$ (mb/sr)"))
|
||||
ax.set_yscale(opts.get("yscale", "log"))
|
||||
if "xlim" in opts:
|
||||
ax.set_xlim(*opts["xlim"])
|
||||
if "ylim" in opts:
|
||||
ax.set_ylim(*opts["ylim"])
|
||||
ax.set_title(config.get("title", ""))
|
||||
ax.legend(fontsize=8)
|
||||
fig.tight_layout()
|
||||
fig.savefig(out_file, dpi=110)
|
||||
plt.close(fig)
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
parser = argparse.ArgumentParser(description="Step 3: cross section from the yields CSV and an efficiency.")
|
||||
parser.add_argument("config", help="TOML config file")
|
||||
args = parser.parse_args(argv)
|
||||
config, full_path = common.read_config(args.config)
|
||||
|
||||
results = {}
|
||||
for det in config.get("dets", [""]):
|
||||
print(f"--- {det or 'data'}")
|
||||
det_config = common.config_for_detector(config, det)
|
||||
t, header, x_col, name, curves = analyse_detector(det_config, det, full_path)
|
||||
common.write_csv(t, header, common.output_file(config, full_path, "xs", det))
|
||||
results[det] = (t, x_col, name, curves)
|
||||
print("\n".join(header[:-1]))
|
||||
show = [x_col, "dz_mm" if name == "sigma_mb" else "d_omega_sr", "counts", f"{name}_app", "eps", name,
|
||||
f"{name}_err"] + [c for c in t.columns if c.startswith("ref_")]
|
||||
print(t[show].to_string(index=False, float_format=lambda v: f"{v:.4g}"), "\n")
|
||||
|
||||
out_png = common.output_file(config, full_path, "xs", "").with_suffix(".png")
|
||||
make_plot(results, config, out_png)
|
||||
print(f"written to {out_png.parent}")
|
||||
return results
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
80
cross_section/detectors.py
Executable file
80
cross_section/detectors.py
Executable file
|
|
@ -0,0 +1,80 @@
|
|||
"""The ANASEN detectors as they are now (Run A, 27Al campaign), for the geometric efficiency.
|
||||
|
||||
geometric_efficiency.py reads everything about the detectors from here. Change a number here and every efficiency
|
||||
computed afterwards follows it.
|
||||
|
||||
Sources:
|
||||
- Si and counter: anasen_detectability.py of ~/TALYS/al027_a_scripts_stoichgas_dedx0962_2026-10-08
|
||||
("detector geometry as used by TrackRecon.C"; counter radii from ClassPW.h)
|
||||
- beam tube and PCB hole: the author, 2026-10-15 (../scratch/reaction_efficiency_tube_2026-10-15)
|
||||
Geometry only: straight tracks from the vertex, no energy loss.
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
# ---------------------------------------------------------------- silicon
|
||||
# QQQ3: the downstream annular detector
|
||||
QQQ_Z_MM = 105.0 # TrackRecon.C qqq_z
|
||||
QQQ_R_MM = (50.0, 100.0) # inner and outer active radius (16 rings: rho = 50 + 50/16 (ring + 0.5))
|
||||
QQQ_PHI_FRACTION = 4 * 87.0 / 360.0 # 4 quadrants x 87 deg of wedges
|
||||
|
||||
# SX3: the barrel; only the four detectors TrackRecon.C builds events for, downstream half only
|
||||
SX3_R_MM = 88.0
|
||||
SX3_Z_MM = (-3.0, 72.0) # z_local + 75/2 - 3
|
||||
SX3_IDS = (1, 3, 7, 9)
|
||||
SX3_HALF_ANGLE = np.arctan(4 * 40.30 / (8.0 * 88.0 * np.cos(np.radians(15.0)))) # outer strip edge, each side
|
||||
SX3_PHI_FRACTION = len(SX3_IDS) * 2 * SX3_HALF_ANGLE / (2 * np.pi)
|
||||
|
||||
PHI_FRACTION = {"qqq": QQQ_PHI_FRACTION, "sx3": SX3_PHI_FRACTION}
|
||||
|
||||
# Si energy thresholds (MeV), TrackRecon.C reaction_ax_core si_ecut. Used only for the eps_si_thr column.
|
||||
SI_THRESHOLD_MEV = {"qqq": 0.45, "sx3": 0.6}
|
||||
|
||||
# ---------------------------------------------------------------- proportional counter
|
||||
COUNTER_HALF_LENGTH_MM = 174.3
|
||||
COUNTER_RADII_MM = (32.0, 37.0, 42.0) # guard, anode, cathode (ClassPW.h)
|
||||
|
||||
# ---------------------------------------------------------------- upstream apertures
|
||||
# (name, radius mm, z mm of the face that binds). A track from an on-axis vertex only moves away from the axis,
|
||||
# so an opening binds at its downstream end.
|
||||
APERTURES = (("beam tube", 16.0, -189.0),
|
||||
("PCB hole", 18.0, -174.3))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- hit rules
|
||||
# z_vertex in mm, theta = lab polar angle of the particle (radians). All return one True/False per track.
|
||||
|
||||
def hits_silicon(det, z_vertex, theta):
|
||||
"""Does the straight track from z_vertex at angle theta reach the active silicon of `det` ("qqq" or "sx3")?
|
||||
Polar angle only: multiply by PHI_FRACTION[det] for the azimuthal coverage.
|
||||
A track that reaches the SX3 barrel's z range is counted as SX3, not QQQ."""
|
||||
s, c = np.sin(theta), np.cos(theta)
|
||||
with np.errstate(divide="ignore", invalid="ignore"):
|
||||
z_at_sx3 = z_vertex + SX3_R_MM * c / s # z where the track reaches the barrel radius
|
||||
on_sx3 = (z_at_sx3 >= SX3_Z_MM[0]) & (z_at_sx3 <= SX3_Z_MM[1])
|
||||
if det == "sx3":
|
||||
return on_sx3
|
||||
r_at_qqq = np.where(c > 0, (QQQ_Z_MM - z_vertex) * s / c, -1.0) # radius where it reaches the QQQ plane
|
||||
if det == "qqq":
|
||||
return (QQQ_Z_MM > z_vertex) & (r_at_qqq >= QQQ_R_MM[0]) & (r_at_qqq <= QQQ_R_MM[1]) & ~on_sx3
|
||||
raise ValueError(f"unknown detector {det!r}: detectors.py defines 'qqq' and 'sx3'")
|
||||
|
||||
|
||||
def crosses_counter(z_vertex, theta):
|
||||
"""Does the track cross the guard, anode and cathode radii inside the counter's length?"""
|
||||
with np.errstate(divide="ignore", invalid="ignore"):
|
||||
cot = np.cos(theta) / np.sin(theta)
|
||||
ok = np.ones(np.shape(z_vertex), bool)
|
||||
for r in COUNTER_RADII_MM:
|
||||
ok &= np.abs(z_vertex + r * cot) <= COUNTER_HALF_LENGTH_MM
|
||||
return ok
|
||||
|
||||
|
||||
def clears_apertures(z_vertex, theta):
|
||||
"""Does the track pass the beam tube and the PCB hole? Only vertices upstream of an aperture can be blocked
|
||||
by it: blocked when the track is farther from the axis than the opening's radius at the opening's z."""
|
||||
s, c = np.sin(theta), np.cos(theta)
|
||||
tan_theta = s / np.where(c > 0, c, 1e-9) # backward tracks from upstream never reach the detectors
|
||||
ok = np.ones(np.shape(z_vertex), bool)
|
||||
for _name, radius, z_face in APERTURES:
|
||||
ok &= ~((z_vertex < z_face) & ((z_face - z_vertex) * tan_theta > radius))
|
||||
return ok
|
||||
88
cross_section/examples/27Al_ap_runA.toml
Executable file
88
cross_section/examples/27Al_ap_runA.toml
Executable file
|
|
@ -0,0 +1,88 @@
|
|||
# 27Al(a,p)30Si excitation function, Run A, QQQ3 and SX3. Same inputs as ../../scratch/xs_27Al_lowE_2026-10-15.
|
||||
#
|
||||
# ~/myenv/bin/python -B extract_yields.py examples/27Al_ap_runA.toml # 1. ROOT -> out/27Al_ap_runA/yields_<det>.csv
|
||||
# ~/myenv/bin/python -B geometric_efficiency.py examples/27Al_ap_runA.toml # 2. detectors.py -> eps_<det>.csv
|
||||
# ~/myenv/bin/python -B cross_section.py examples/27Al_ap_runA.toml # 3. -> xs_<det>.csv, xs.png
|
||||
#
|
||||
# Each script reads the sections it needs. Paths are relative to this file's folder.
|
||||
# "{det}" is replaced by each entry of `dets`.
|
||||
|
||||
title = "27Al(a,p)30Si, Run A"
|
||||
mode = "excitation" # "excitation": sigma(E_cm); "angular": dsigma/dOmega(theta_cm)
|
||||
dets = ["sx3", "qqq"] # names known to detectors.py
|
||||
out_dir = "../out/27Al_ap"
|
||||
|
||||
# ------------------------------------------------------------- step 1: extract_yields.py
|
||||
[extract]
|
||||
# One fill per candidate: x = vertex z, y = tracked beam energy. Projected on y (all z, including overflow).
|
||||
source = "hist"
|
||||
file = "~/ANASEN_analysis/Output_27Al_X0_Y0/Output_27Al.root"
|
||||
hist = "_m27Alax+misc_{det}/m27Alax_BeamEnergy_vs_VertexZ_{det}"
|
||||
axis = "y"
|
||||
variable = "e_beam"
|
||||
|
||||
# ------------------------------------------------------------- the reaction (steps 2 and 3)
|
||||
[reaction]
|
||||
beam_mass_u = 26.981538 # 27Al (atomic masses, as TrackRecon.C)
|
||||
target_mass_u = 4.002603254 # 4He
|
||||
ejectile_mass_u = 1.007825032 # p -> Q = 2.372 MeV for the ground state
|
||||
recoil_mass_u = 29.973770 # 30Si
|
||||
detect = "ejectile"
|
||||
ecm_per_ebeam = 0.12918 # the rounded factor of the established analysis (the masses give 0.129182)
|
||||
# 30Si states weighted by the TALYS (a,p) state cross sections at each bin's E_cm
|
||||
states_csv = "../inputs/talys_27Al_ap.csv"
|
||||
# or fixed weights: states = [{ex_mev = 0.0, weight = 1.0}, {ex_mev = 2.235, weight = 0.5}]
|
||||
|
||||
[beam]
|
||||
# Beam energy along z;
|
||||
# the nominal E_cm of each bin and the beam energy in the efficiency toy come from the 0.962 track.
|
||||
track_csv = "../inputs/beam_tracks.csv"
|
||||
track_z_col = "z_mm"
|
||||
track_e_col = "e_beam_mev_0962"
|
||||
# Alternative: build the track from an energy-loss table (check which dE/dx scale the table was made with):
|
||||
# eloss_table = "../../eloss_calculations/aluminum_lookup_80MeV_250torr_3pc.dat"
|
||||
# e_entrance_mev = 56.16
|
||||
# z_entrance_mm = -454.0
|
||||
n_beam = 1.4405e12 # Faraday cup, ch5 ssec:beam_exposure: the 45 runs (2.2096e12)
|
||||
# For another set of runs: ../../runlog/beam_exposure.py "<runs>" --data-dir ../../Output_27Al_fb
|
||||
n_beam_rel_err = 0.5
|
||||
|
||||
[bins]
|
||||
ecm_edges = { start = 0.0, stop = 7.25, step = 0.25 }
|
||||
|
||||
# ------------------------------------------------------------- step 2: geometric_efficiency.py
|
||||
[efficiency]
|
||||
n_per_bin = 400000
|
||||
seed = 20261016
|
||||
|
||||
# ------------------------------------------------------------- step 3: cross_section.py
|
||||
[target]
|
||||
# 4He in 250 Torr He + 3% CO2 at 20 C -> 7.988e17 He atoms per cm2 per mm
|
||||
pressure_torr = 250.0
|
||||
temperature_k = 293.15
|
||||
mole_fraction = 0.97
|
||||
atoms_per_molecule = 1
|
||||
|
||||
# [yield] csv = "..." # only to read yields from somewhere other than <out_dir>/yields_<det>.csv
|
||||
|
||||
[acceptance]
|
||||
source = "geometric" # <out_dir>/eps_<det>.csv from step 2
|
||||
column = "eps_geometric" # or eps_si, eps_si_thr, eps_counter
|
||||
# To use the adopted efficiency of ../../scratch/xs_27Al_lowE_2026-10-15 instead (includes gas loss and thresholds):
|
||||
# source = "csv"
|
||||
# csv = "../../scratch/xs_27Al_lowE_2026-10-15/out/xs_{det}.csv"
|
||||
# match = "bin"
|
||||
# x_col = "ecm_lo_mev"
|
||||
# eps_col = "eps_tube_adopted"
|
||||
# rel_err_col = "eps_tube_rel_err"
|
||||
|
||||
[[reference]]
|
||||
csv = "../inputs/talys_27Al_ap.csv"
|
||||
x_col = "ecm_mev"
|
||||
y_col = "sigma_total_mb"
|
||||
label = "TALYS (a,p), discrete states"
|
||||
|
||||
[plot]
|
||||
yscale = "log"
|
||||
xlim = [0, 7.4]
|
||||
ylim = [1e-3, 3e4]
|
||||
87
cross_section/examples/paap_ta_runs.toml
Executable file
87
cross_section/examples/paap_ta_runs.toml
Executable file
|
|
@ -0,0 +1,87 @@
|
|||
# p + 4He elastic scattering, dsigma/dOmega(theta_cm), from the alpha recoils of the Run A Ta-foil runs 18-22, straight
|
||||
# from a TrackRecon.C production file -- the same route as 27Al_ap_runA.toml. The histogram is
|
||||
# elastic_VertexZ_vs_ThetaCM_<variant>_a_<DET>, filled by protonAlphaElastic_core since 2026-10-16.
|
||||
# paap_ta_runs.toml is the same measurement from the p-alpha study's per-hit list.
|
||||
#
|
||||
# (once) process runs 18-22 with CUTLIST=pid_cuts/pid_cutlist.txt and merge them, e.g.
|
||||
# bash scratch/paap_hist_2026-10-16/reprocess.sh (or run_tr.sh block 5, then hadd runs 18-22 only)
|
||||
# ~/myenv/bin/python -B extract_yields.py examples/paap_ta_runs_hist.toml # or, the same in ROOT:
|
||||
# root -l -b -q 'extract_yields.C+("examples/paap_ta_runs_hist.toml")'
|
||||
# ~/myenv/bin/python -B geometric_efficiency.py examples/paap_ta_runs_hist.toml
|
||||
# ~/myenv/bin/python -B cross_section.py examples/paap_ta_runs_hist.toml
|
||||
#
|
||||
# Paths are relative to this file's folder.
|
||||
|
||||
title = "p + 4He elastic, alpha recoils, Ta runs 18-22 (TrackRecon histograms)"
|
||||
mode = "angular"
|
||||
dets = ["qqq", "sx3"]
|
||||
out_dir = "../out/paap_ta"
|
||||
|
||||
[det_vars.qqq] # TrackRecon.C spells the detector in upper case in these folders
|
||||
DET = "QQQ"
|
||||
[det_vars.sx3]
|
||||
DET = "SX3"
|
||||
|
||||
# ------------------------------------------------------------- step 1: extract_yields.py / extract_yields.C
|
||||
[extract]
|
||||
# The merged output of runs 18-22 (not 17: its foil, at z = -135.7 mm, sits inside the vertex window below).
|
||||
# Point this at your own merged file to use your own processing.
|
||||
source = "hist"
|
||||
file = "~/ANASEN_analysis/Output_p/Al_protons.root"
|
||||
# Folder _a: hits the p/alpha cut (pid_alpha_<det>, CUTLIST) calls alpha. Variant _cath_exgate: cathode topologies
|
||||
# (a1c1, a1c2, a2c1, a2c2) and |Ex| < 1 MeV under the alpha hypothesis, i.e. elastic. Others: "" (all topologies,
|
||||
# no Ex gate), "_cath", "_exgate".
|
||||
hist = "elastic+misc_{DET}_a/elastic_VertexZ_vs_ThetaCM_cath_exgate_a_{DET}"
|
||||
axis = "x" # x = vertex z (mm)
|
||||
angle_axis = "y" # y = proton theta_cm (deg)
|
||||
variable = "z"
|
||||
|
||||
# ------------------------------------------------------------- the reaction (steps 2 and 3)
|
||||
[reaction]
|
||||
beam_mass_u = 1.007825032 # p
|
||||
target_mass_u = 4.002603254 # 4He
|
||||
ejectile_mass_u = 1.007825032 # p
|
||||
recoil_mass_u = 4.002603254 # 4He
|
||||
detect = "recoil" # the silicon sees the alpha; theta_cm is the proton's
|
||||
states = [{ ex_mev = 0.0, weight = 1.0 }]
|
||||
|
||||
[beam]
|
||||
# As paap_ta_runs.toml: 6.89 MeV protons into the gas; N_b = run 17's Ta-foil Rutherford current (1.35 pA) times
|
||||
# the wall time of runs 18-22. The run log's 13 pA would give 2.6378e11 (../../runlog/beam_exposure.py "18-22").
|
||||
eloss_table = "../../eloss_calculations/proton_lookup_30MeV_250torr_3pc.dat"
|
||||
e_entrance_mev = 6.89
|
||||
z_entrance_mm = -454.0
|
||||
current_a = 1.35e-12
|
||||
time_s = 3232.95
|
||||
charge_state = 1
|
||||
|
||||
[bins]
|
||||
window_z_mm = [-170.0, -53.0] # upstream of every run's foil by 25 mm for runs 18-22, inside the counter
|
||||
theta_edges_deg = { start = 88.0, stop = 168.0, step = 8.0 }
|
||||
|
||||
# ------------------------------------------------------------- step 2: geometric_efficiency.py
|
||||
[efficiency]
|
||||
n_per_bin = 400000
|
||||
seed = 20261016
|
||||
|
||||
# ------------------------------------------------------------- step 3: cross_section.py
|
||||
[target]
|
||||
pressure_torr = 250.0
|
||||
temperature_k = 293.15
|
||||
mole_fraction = 0.97
|
||||
atoms_per_molecule = 1
|
||||
|
||||
[acceptance]
|
||||
source = "geometric"
|
||||
column = "eps_geometric"
|
||||
|
||||
[[reference]]
|
||||
csv = "reference/barnard1964_A1113003.csv"
|
||||
x_col = "theta_cm_deg"
|
||||
y_col = "dsdo_window_mb_sr"
|
||||
label = "Barnard 1964, at the window's E_p"
|
||||
|
||||
[plot]
|
||||
yscale = "linear"
|
||||
xlim = [80, 175]
|
||||
ylim = [0, 130]
|
||||
144
cross_section/examples/reference/barnard1964_A1113003.csv
Executable file
144
cross_section/examples/reference/barnard1964_A1113003.csv
Executable file
|
|
@ -0,0 +1,144 @@
|
|||
# Barnard, Jones & Weil, NP 50, 604 (1964), EXFOR A1113003: p + 4He elastic, dsigma/dOmega_cm (mb/sr)
|
||||
# Legendre order 6 fits of the 6.016 and 6.977 MeV distributions; dsdo_window: log-linear in E_p at 6.7437 MeV,
|
||||
# the beam energy at the centre of the vertex window -170.0 to -53.0 mm of paap_ta_runs.toml
|
||||
# written by make_barnard_reference.py
|
||||
theta_cm_deg,dsdo_6p016_mb_sr,dsdo_6p977_mb_sr,dsdo_window_mb_sr
|
||||
26,514.406,470.065,480.465
|
||||
27,504.693,461.948,471.98
|
||||
28,494.922,453.754,463.423
|
||||
29,485.115,445.501,454.81
|
||||
30,475.295,437.203,446.161
|
||||
31,465.481,428.878,437.491
|
||||
32,455.692,420.54,428.816
|
||||
33,445.946,412.203,420.153
|
||||
34,436.261,403.88,411.513
|
||||
35,426.651,395.584,402.912
|
||||
36,417.132,387.326,394.36
|
||||
37,407.715,379.117,385.87
|
||||
38,398.413,370.966,377.45
|
||||
39,389.235,362.882,369.111
|
||||
40,380.191,354.874,360.861
|
||||
41,371.288,346.947,352.705
|
||||
42,362.532,339.108,344.652
|
||||
43,353.928,331.362,336.705
|
||||
44,345.481,323.714,328.869
|
||||
45,337.192,316.166,321.147
|
||||
46,329.064,308.722,313.542
|
||||
47,321.097,301.384,306.056
|
||||
48,313.29,294.152,298.688
|
||||
49,305.642,287.028,291.44
|
||||
50,298.152,280.012,284.312
|
||||
51,290.816,273.103,277.302
|
||||
52,283.631,266.301,270.408
|
||||
53,276.592,259.604,263.63
|
||||
54,269.697,253.011,256.964
|
||||
55,262.94,246.52,250.409
|
||||
56,256.315,240.129,243.962
|
||||
57,249.819,233.836,237.62
|
||||
58,243.445,227.639,231.379
|
||||
59,237.189,221.535,225.238
|
||||
60,231.045,215.523,219.192
|
||||
61,225.008,209.599,213.24
|
||||
62,219.074,203.763,207.379
|
||||
63,213.237,198.013,201.606
|
||||
64,207.495,192.345,195.918
|
||||
65,201.842,186.761,190.315
|
||||
66,196.276,181.257,184.794
|
||||
67,190.793,175.833,179.353
|
||||
68,185.391,170.488,173.993
|
||||
69,180.069,165.223,168.711
|
||||
70,174.824,160.037,163.508
|
||||
71,169.656,154.931,158.384
|
||||
72,164.564,149.905,153.339
|
||||
73,159.549,144.961,148.375
|
||||
74,154.61,140.099,143.492
|
||||
75,149.75,135.322,138.691
|
||||
76,144.969,130.63,133.975
|
||||
77,140.27,126.028,129.346
|
||||
78,135.655,121.516,124.807
|
||||
79,131.127,117.097,120.359
|
||||
80,126.688,112.775,116.005
|
||||
81,122.342,108.552,111.75
|
||||
82,118.093,104.431,107.595
|
||||
83,113.945,100.416,103.545
|
||||
84,109.9,96.5094,99.6024
|
||||
85,105.964,92.7147,95.7706
|
||||
86,102.14,89.0351,92.0532
|
||||
87,98.4316,85.4736,88.4535
|
||||
88,94.8431,82.0333,84.9746
|
||||
89,91.378,78.7172,81.6197
|
||||
90,88.0401,75.528,78.3918
|
||||
91,84.8326,72.4683,75.2935
|
||||
92,81.7586,69.5403,72.3274
|
||||
93,78.8209,66.7463,69.4959
|
||||
94,76.0221,64.0881,66.8009
|
||||
95,73.3644,61.5673,64.2442
|
||||
96,70.8494,59.1852,61.8272
|
||||
97,68.4787,56.9427,59.5509
|
||||
98,66.2533,54.8405,57.4162
|
||||
99,64.1738,52.8789,55.4235
|
||||
100,62.2404,51.058,53.5729
|
||||
101,60.4529,49.3774,51.864
|
||||
102,58.8107,47.8365,50.2963
|
||||
103,57.3127,46.4343,48.8688
|
||||
104,55.9576,45.1696,47.5803
|
||||
105,54.7435,44.0406,46.4292
|
||||
106,53.6684,43.0456,45.4134
|
||||
107,52.7296,42.1824,44.5309
|
||||
108,51.9245,41.4485,43.7792
|
||||
109,51.25,40.8414,43.1556
|
||||
110,50.7026,40.3582,42.657
|
||||
111,50.279,39.9957,42.2804
|
||||
112,49.9752,39.7509,42.0224
|
||||
113,49.7873,39.6202,41.8794
|
||||
114,49.7114,39.6002,41.8479
|
||||
115,49.7433,39.6873,41.9241
|
||||
116,49.8787,39.8777,42.1041
|
||||
117,50.1134,40.1678,42.3841
|
||||
118,50.4431,40.5537,42.7601
|
||||
119,50.8637,41.0316,43.2282
|
||||
120,51.3709,41.5977,43.7844
|
||||
121,51.9605,42.2482,44.4248
|
||||
122,52.6287,42.9794,45.1456
|
||||
123,53.3713,43.7876,45.9431
|
||||
124,54.1848,44.6692,46.8133
|
||||
125,55.0653,45.6205,47.7529
|
||||
126,56.0094,46.6381,48.7581
|
||||
127,57.0138,47.7186,49.8256
|
||||
128,58.0753,48.8588,50.9522
|
||||
129,59.1907,50.0552,52.1344
|
||||
130,60.3572,51.305,53.3694
|
||||
131,61.5722,52.6049,54.654
|
||||
132,62.8329,53.9521,55.9855
|
||||
133,64.137,55.3437,57.3609
|
||||
134,65.4821,56.7769,58.7776
|
||||
135,66.8659,58.2489,60.2329
|
||||
136,68.2862,59.757,61.7243
|
||||
137,69.741,61.2987,63.2493
|
||||
138,71.2282,62.8712,64.8053
|
||||
139,72.7458,64.472,66.3899
|
||||
140,74.2917,66.0985,68.0005
|
||||
141,75.864,67.7481,69.6349
|
||||
142,77.4604,69.4181,71.2903
|
||||
143,79.079,71.1058,72.9644
|
||||
144,80.7174,72.8086,74.6544
|
||||
145,82.3734,74.5237,76.3578
|
||||
146,84.0445,76.2483,78.0719
|
||||
147,85.7282,77.9794,79.7937
|
||||
148,87.4218,79.7141,81.5205
|
||||
149,89.1225,81.4492,83.2491
|
||||
150,90.8273,83.1817,84.9765
|
||||
151,92.533,84.9082,86.6995
|
||||
152,94.2363,86.6253,88.4146
|
||||
153,95.9337,88.3298,90.1185
|
||||
154,97.6217,90.0179,91.8076
|
||||
155,99.2963,91.686,93.4782
|
||||
156,100.954,93.3306,95.1267
|
||||
157,102.59,94.9478,96.7491
|
||||
158,104.2,96.5338,98.3415
|
||||
159,105.781,98.0847,99.9001
|
||||
160,107.328,99.5967,101.421
|
||||
161,108.836,101.066,102.9
|
||||
162,110.3,102.488,104.333
|
||||
163,111.718,103.86,105.716
|
||||
164,113.083,105.178,107.045
|
||||
|
378
cross_section/extract_yields.C
Executable file
378
cross_section/extract_yields.C
Executable file
|
|
@ -0,0 +1,378 @@
|
|||
// Step 1 in ROOT: yields out of ROOT, into a plain CSV histogram. The ROOT twin of extract_yields.py:
|
||||
// same config, same output (<out_dir>/yields_<det>.csv), same format. Use whichever is handier.
|
||||
//
|
||||
// root -l -b -q 'extract_yields.C+("examples/27Al_ap_runA.toml")'
|
||||
// root -l -b -q 'extract_yields.C+("examples/27Al_ap_runA.toml", "out/elsewhere")' // another output folder
|
||||
// // (relative to where root runs)
|
||||
// THE CSV FORMAT (cross_section.py reads only this)
|
||||
// 1D: <var>_lo, <var>_hi, counts, variance
|
||||
// 2D: <var>_lo, <var>_hi, theta_cm_lo, theta_cm_hi, counts, variance (angular mode)
|
||||
// <var> is z (mm), e_beam (MeV) or ecm (MeV). One row per bin; under/overflow are not written.
|
||||
//
|
||||
// CONFIG: the [extract] section, `dets`, `out_dir`, `title` and [det_vars.<det>] of the study's TOML, exactly as for
|
||||
// extract_yields.py (keys listed there). Two differences:
|
||||
// - `cut` is handed to TTree::Draw. Write it with and / or (e.g. "det == 1 and e_si > 0.5"): they become && / ||
|
||||
// here and stay valid pandas syntax for the Python version.
|
||||
// - A tree histogram puts an entry exactly on the last upper edge in the overflow (numpy puts it in the last bin).
|
||||
// The TOML reader below handles only what these configs use: "strings", numbers, one-line [lists] and
|
||||
// { start = .., stop = .., step = .. } tables. Anything else stops with an error.
|
||||
|
||||
#include <TChain.h>
|
||||
#include <TDirectory.h>
|
||||
#include <TFile.h>
|
||||
#include <TKey.h>
|
||||
#include <TH1.h>
|
||||
#include <TH2.h>
|
||||
#include <TROOT.h>
|
||||
#include <TString.h>
|
||||
#include <TSystem.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace xs {
|
||||
|
||||
using Section = std::map<std::string, std::string>; // key -> raw value text
|
||||
using Config = std::map<std::string, Section>; // section name ("" = top level) -> keys
|
||||
|
||||
// ============================================================ minimal TOML reading
|
||||
|
||||
std::string Trim(const std::string& s)
|
||||
{
|
||||
const auto a = s.find_first_not_of(" \t\r\n");
|
||||
if (a == std::string::npos) return "";
|
||||
const auto b = s.find_last_not_of(" \t\r\n");
|
||||
return s.substr(a, b - a + 1);
|
||||
}
|
||||
|
||||
std::string StripComment(const std::string& line)
|
||||
{
|
||||
bool inString = false;
|
||||
for (size_t i = 0; i < line.size(); ++i) {
|
||||
if (line[i] == '"') inString = !inString;
|
||||
if (line[i] == '#' && !inString) return line.substr(0, i);
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
Config ReadToml(const std::string& file)
|
||||
{
|
||||
std::ifstream in(file);
|
||||
if (!in) throw std::runtime_error("cannot open config " + file);
|
||||
Config config;
|
||||
std::string section, line;
|
||||
while (std::getline(in, line)) {
|
||||
line = Trim(StripComment(line));
|
||||
if (line.empty()) continue;
|
||||
if (line.rfind("[[", 0) == 0) { section = line; continue; } // array tables (e.g. [[reference]]): not used here
|
||||
if (line[0] == '[') { section = Trim(line.substr(1, line.size() - 2)); continue; }
|
||||
const auto eq = line.find('=');
|
||||
if (eq == std::string::npos) throw std::runtime_error("cannot read config line: " + line);
|
||||
const std::string value = Trim(line.substr(eq + 1));
|
||||
if ((value[0] == '[' && value.back() != ']') || (value[0] == '{' && value.back() != '}'))
|
||||
throw std::runtime_error("lists and tables must be on one line: " + line);
|
||||
config[section][Trim(line.substr(0, eq))] = value;
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
std::string Unquote(const std::string& v)
|
||||
{
|
||||
if (v.size() >= 2 && v.front() == '"' && v.back() == '"') return v.substr(1, v.size() - 2);
|
||||
return v;
|
||||
}
|
||||
|
||||
// "[a, b]" or "{ k = v, ... }" -> its items, split on commas outside quotes
|
||||
std::vector<std::string> SplitItems(const std::string& v)
|
||||
{
|
||||
std::vector<std::string> items;
|
||||
std::string inner = Trim(v.substr(1, v.size() - 2)), item;
|
||||
bool inString = false;
|
||||
for (char c : inner) {
|
||||
if (c == '"') inString = !inString;
|
||||
if (c == ',' && !inString) { items.push_back(Trim(item)); item.clear(); }
|
||||
else item += c;
|
||||
}
|
||||
if (!Trim(item).empty()) items.push_back(Trim(item));
|
||||
return items;
|
||||
}
|
||||
|
||||
// Bin edges from [e0, e1, ...] or { start = .., stop = .., step = .. } (stop included), computed as numpy does
|
||||
std::vector<double> Edges(const std::string& v)
|
||||
{
|
||||
std::vector<double> edges;
|
||||
if (v.front() == '[') {
|
||||
for (const auto& item : SplitItems(v)) edges.push_back(std::stod(item));
|
||||
return edges;
|
||||
}
|
||||
std::map<std::string, double> table;
|
||||
for (const auto& item : SplitItems(v)) {
|
||||
const auto eq = item.find('=');
|
||||
table[Trim(item.substr(0, eq))] = std::stod(Trim(item.substr(eq + 1)));
|
||||
}
|
||||
const double start = table.at("start"), stop = table.at("stop"), step = table.at("step");
|
||||
const long nBins = std::lround((stop - start) / step);
|
||||
for (long i = 0; i <= nBins; ++i) edges.push_back(start + step * i);
|
||||
return edges;
|
||||
}
|
||||
|
||||
// The [extract] section of one detector: every string with {det} and the [det_vars.<det>] keys replaced
|
||||
Section ExtractSection(const Config& config, const std::string& det)
|
||||
{
|
||||
std::map<std::string, std::string> replacements = {{"det", det}};
|
||||
const auto vars = config.find("det_vars." + det);
|
||||
if (vars != config.end())
|
||||
for (const auto& kv : vars->second) replacements[kv.first] = Unquote(kv.second);
|
||||
const auto ex = config.find("extract");
|
||||
if (ex == config.end()) throw std::runtime_error("the config has no [extract] section");
|
||||
Section out;
|
||||
for (const auto& kv : ex->second) {
|
||||
TString value = kv.second.c_str();
|
||||
for (const auto& r : replacements) value.ReplaceAll(("{" + r.first + "}").c_str(), r.second.c_str());
|
||||
out[kv.first] = value.Data();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string Get(const Section& s, const std::string& key, const std::string& fallback = "\x01")
|
||||
{
|
||||
const auto it = s.find(key);
|
||||
if (it != s.end()) return Unquote(it->second);
|
||||
if (fallback == "\x01") throw std::runtime_error("[extract] needs the key " + key);
|
||||
return fallback;
|
||||
}
|
||||
|
||||
// ============================================================ the yields table
|
||||
|
||||
struct Row {
|
||||
double lo, hi, thetaLo, thetaHi, counts, variance;
|
||||
};
|
||||
|
||||
// Sum of weights squared of a bin (global index); a histogram without Sumw2 counts each entry once
|
||||
double Variance(const TH1* h, int bin)
|
||||
{
|
||||
return h->GetSumw2N() > 0 ? h->GetSumw2()->At(bin) : h->GetBinContent(bin);
|
||||
}
|
||||
|
||||
// The histogram at "folder/name". A name that itself contains '/' (e.g. "dE_(z/100)=1") is not reachable with Get(),
|
||||
// so each split into an existing folder + a key of that folder is also tried. Not found: the error names the
|
||||
// histograms whose path matches when case is ignored (e.g. "elastic+misc_qqq_a" for "elastic+misc_QQQ_a").
|
||||
TH1* FindHistogram(TFile* file, const std::string& path)
|
||||
{
|
||||
if (auto* h = dynamic_cast<TH1*>(file->Get(path.c_str()))) return h;
|
||||
// every split into folder + name: at each '/', from the last one back, and finally no folder at all
|
||||
std::vector<std::pair<std::string, std::string>> splits;
|
||||
for (size_t i = path.size(); i-- > 0;)
|
||||
if (path[i] == '/') splits.emplace_back(path.substr(0, i), path.substr(i + 1));
|
||||
splits.emplace_back("", path);
|
||||
for (const auto& [folder, name] : splits) {
|
||||
TDirectory* dir = folder.empty() ? static_cast<TDirectory*>(file) : file->GetDirectory(folder.c_str());
|
||||
if (!dir) continue;
|
||||
if (TKey* key = dir->GetKey(name.c_str()))
|
||||
if (auto* h = dynamic_cast<TH1*>(key->ReadObj())) return h;
|
||||
}
|
||||
std::string close;
|
||||
TString lower = path.c_str();
|
||||
lower.ToLower();
|
||||
std::function<void(TDirectory*, const std::string&)> walk = [&](TDirectory* dir, const std::string& prefix) {
|
||||
for (auto* obj : *dir->GetListOfKeys()) {
|
||||
auto* key = static_cast<TKey*>(obj);
|
||||
const std::string full = prefix + key->GetName();
|
||||
if (TString(key->GetClassName()).BeginsWith("TDirectory"))
|
||||
walk(dir->GetDirectory(key->GetName()), full + "/");
|
||||
else if (TString(full.c_str()).EqualTo(lower, TString::kIgnoreCase))
|
||||
close += " '" + full + "'";
|
||||
}
|
||||
};
|
||||
walk(file, "");
|
||||
throw std::runtime_error("no histogram " + path + " in " + file->GetName() + (close.empty() ? "" : "; same path ignoring case:" + close) +
|
||||
". Paths are case-sensitive; for upper-case names per detector use [det_vars], e.g. DET = \"QQQ\"");
|
||||
}
|
||||
|
||||
std::vector<Row> FromHistogram(const Section& ex, const std::string& path, bool& twoD)
|
||||
{
|
||||
TFile* file = TFile::Open(path.c_str());
|
||||
if (!file || file->IsZombie()) throw std::runtime_error("cannot open " + path);
|
||||
const std::string name = Get(ex, "hist");
|
||||
TH1* h = FindHistogram(file, name);
|
||||
|
||||
std::vector<Row> rows;
|
||||
twoD = false;
|
||||
if (h->GetDimension() == 1) {
|
||||
for (int i = 1; i <= h->GetNbinsX(); ++i)
|
||||
rows.push_back({h->GetXaxis()->GetBinLowEdge(i), h->GetXaxis()->GetBinUpEdge(i), 0, 0,
|
||||
h->GetBinContent(i), Variance(h, i)});
|
||||
file->Close();
|
||||
return rows;
|
||||
}
|
||||
|
||||
// TH2: `axis` holds the variable; the other axis is theta_cm (angle_axis given) or is summed
|
||||
const bool varOnX = Get(ex, "axis", "x") == "x";
|
||||
TH2* h2 = static_cast<TH2*>(h);
|
||||
TAxis* varAxis = varOnX ? h2->GetXaxis() : h2->GetYaxis();
|
||||
TAxis* otherAxis = varOnX ? h2->GetYaxis() : h2->GetXaxis();
|
||||
const int nVar = varAxis->GetNbins(), nOther = otherAxis->GetNbins();
|
||||
auto global = [&](int iVar, int iOther) { return varOnX ? h2->GetBin(iVar, iOther) : h2->GetBin(iOther, iVar); };
|
||||
|
||||
if (ex.count("angle_axis")) { // keep it 2D: one row per (variable bin, theta bin)
|
||||
twoD = true;
|
||||
for (int i = 1; i <= nVar; ++i)
|
||||
for (int j = 1; j <= nOther; ++j)
|
||||
rows.push_back({varAxis->GetBinLowEdge(i), varAxis->GetBinUpEdge(i), otherAxis->GetBinLowEdge(j),
|
||||
otherAxis->GetBinUpEdge(j), h2->GetBinContent(global(i, j)), Variance(h2, global(i, j))});
|
||||
file->Close();
|
||||
return rows;
|
||||
}
|
||||
|
||||
// project on the variable: the other axis summed whole (with under/overflow), or the fraction inside other_range
|
||||
std::vector<double> weight(nOther + 2, 1.0);
|
||||
if (ex.count("other_range")) {
|
||||
const auto range = Edges(ex.at("other_range"));
|
||||
weight.front() = weight.back() = 0.0;
|
||||
for (int j = 1; j <= nOther; ++j) {
|
||||
const double lo = otherAxis->GetBinLowEdge(j), hi = otherAxis->GetBinUpEdge(j);
|
||||
weight[j] = std::max(0.0, std::min(hi, range[1]) - std::max(lo, range[0])) / (hi - lo);
|
||||
}
|
||||
}
|
||||
for (int i = 1; i <= nVar; ++i) {
|
||||
double counts = 0, variance = 0;
|
||||
for (int j = 0; j <= nOther + 1; ++j) {
|
||||
counts += weight[j] * h2->GetBinContent(global(i, j));
|
||||
variance += weight[j] * weight[j] * Variance(h2, global(i, j));
|
||||
}
|
||||
rows.push_back({varAxis->GetBinLowEdge(i), varAxis->GetBinUpEdge(i), 0, 0, counts, variance});
|
||||
}
|
||||
file->Close();
|
||||
return rows;
|
||||
}
|
||||
|
||||
std::vector<Row> FromTree(const Section& ex, const std::vector<std::string>& files, bool& twoD)
|
||||
{
|
||||
TChain chain(Get(ex, "tree").c_str());
|
||||
for (const auto& f : files) chain.Add(f.c_str()); // TChain::Add takes wildcards in the file name
|
||||
if (chain.GetEntries() <= 0) // a missing tree or file must not become a table of zeros
|
||||
throw std::runtime_error("no entries in tree " + Get(ex, "tree") + " of " + files.front() + (files.size() > 1 ? " ..." : ""));
|
||||
|
||||
// The cut, with and / or as && / ||; a weight branch multiplies it
|
||||
TString cut = Get(ex, "cut", "").c_str();
|
||||
cut.ReplaceAll(" and ", " && ").ReplaceAll(" or ", " || ");
|
||||
if (cut.IsNull()) cut = "1";
|
||||
TString selection = ex.count("weight_branch") ? TString::Format("(%s)*(%s)", Get(ex, "weight_branch").c_str(), cut.Data())
|
||||
: cut;
|
||||
|
||||
const auto edges = Edges(ex.at("bins"));
|
||||
const TString branch = Get(ex, "branch").c_str();
|
||||
gROOT->cd();
|
||||
std::vector<Row> rows;
|
||||
twoD = ex.count("angle_branch") > 0;
|
||||
if (!twoD) {
|
||||
TH1D h("h_extract_yields", "", edges.size() - 1, edges.data());
|
||||
h.Sumw2();
|
||||
const Long64_t n = chain.Draw(branch + ">>h_extract_yields", selection, "goff");
|
||||
printf(" %d file(s); %lld entries after the cut\n", chain.GetListOfFiles()->GetEntries(), n);
|
||||
for (int i = 1; i <= h.GetNbinsX(); ++i)
|
||||
rows.push_back({edges[i - 1], edges[i], 0, 0, h.GetBinContent(i), Variance(&h, i)});
|
||||
return rows;
|
||||
}
|
||||
const auto angleEdges = Edges(ex.at("angle_bins"));
|
||||
TH2D h("h_extract_yields2", "", edges.size() - 1, edges.data(), angleEdges.size() - 1, angleEdges.data());
|
||||
h.Sumw2();
|
||||
const Long64_t n = chain.Draw(TString(Get(ex, "angle_branch").c_str()) + ":" + branch + ">>h_extract_yields2",
|
||||
selection, "goff"); // "y:x" in TTree::Draw
|
||||
printf(" %d file(s); %lld entries after the cut\n", chain.GetListOfFiles()->GetEntries(), n);
|
||||
for (size_t i = 1; i < edges.size(); ++i)
|
||||
for (size_t j = 1; j < angleEdges.size(); ++j)
|
||||
rows.push_back({edges[i - 1], edges[i], angleEdges[j - 1], angleEdges[j], h.GetBinContent(h.GetBin(i, j)),
|
||||
Variance(&h, h.GetBin(i, j))});
|
||||
return rows;
|
||||
}
|
||||
|
||||
void WriteCsv(const std::vector<Row>& rows, bool twoD, const std::string& var, const std::vector<std::string>& header,
|
||||
const std::string& outFile)
|
||||
{
|
||||
FILE* f = fopen(outFile.c_str(), "w");
|
||||
if (!f) throw std::runtime_error("cannot write " + outFile);
|
||||
for (const auto& line : header) fprintf(f, "# %s\n", line.c_str());
|
||||
fprintf(f, twoD ? "%s_lo,%s_hi,theta_cm_lo,theta_cm_hi,counts,variance\n" : "%s_lo,%s_hi,counts,variance\n",
|
||||
var.c_str(), var.c_str());
|
||||
for (const auto& r : rows) {
|
||||
if (twoD) fprintf(f, "%.6g,%.6g,%.6g,%.6g,%.6g,%.6g\n", r.lo, r.hi, r.thetaLo, r.thetaHi, r.counts, r.variance);
|
||||
else fprintf(f, "%.6g,%.6g,%.6g,%.6g\n", r.lo, r.hi, r.counts, r.variance);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
} // namespace xs
|
||||
|
||||
void extract_yields(const char* configFile, const char* outDirOverride = "")
|
||||
{
|
||||
using namespace xs;
|
||||
try {
|
||||
const Config config = ReadToml(configFile);
|
||||
const std::string configDir = gSystem->GetDirName(configFile).Data();
|
||||
auto fullPath = [&](const std::string& p) -> std::string {
|
||||
if (p.rfind("~/", 0) == 0 && getenv("HOME")) return std::string(getenv("HOME")) + p.substr(1); // "~/..." = home
|
||||
return p.empty() || p[0] == '/' ? p : configDir + "/" + p;
|
||||
};
|
||||
|
||||
const Section& top = config.count("") ? config.at("") : Section();
|
||||
std::vector<std::string> dets = {""};
|
||||
if (top.count("dets")) {
|
||||
dets.clear();
|
||||
for (const auto& d : SplitItems(top.at("dets"))) dets.push_back(Unquote(d));
|
||||
}
|
||||
const std::string outDir = *outDirOverride ? std::string(outDirOverride)
|
||||
: fullPath(top.count("out_dir") ? Unquote(top.at("out_dir")) : "out");
|
||||
gSystem->mkdir(outDir.c_str(), kTRUE);
|
||||
const std::string title = top.count("title") ? Unquote(top.at("title")) : "";
|
||||
|
||||
for (const auto& det : dets) {
|
||||
const Section ex = ExtractSection(config, det);
|
||||
const std::string source = Get(ex, "source"), var = Get(ex, "variable");
|
||||
if (source != "hist" && source != "tree")
|
||||
throw std::runtime_error("[extract] source must be \"hist\" or \"tree\", not " + source +
|
||||
(source == "csv" ? " (event-list CSVs: use extract_yields.py)" : ""));
|
||||
std::vector<std::string> files; // `file` may be one path or a list
|
||||
if (ex.at("file").front() == '[') for (const auto& f : SplitItems(ex.at("file"))) files.push_back(fullPath(Unquote(f)));
|
||||
else files.push_back(fullPath(Get(ex, "file")));
|
||||
const std::string fileText = ex.at("file").front() == '[' ? ex.at("file") : Get(ex, "file");
|
||||
printf("--- %s: %s : %s\n", det.empty() ? "data" : det.c_str(), fileText.c_str(),
|
||||
Get(ex, source == "hist" ? "hist" : "tree").c_str());
|
||||
|
||||
bool twoD = false;
|
||||
std::vector<Row> rows;
|
||||
std::string from;
|
||||
if (source == "hist") {
|
||||
rows = FromHistogram(ex, files.front(), twoD);
|
||||
from = "histogram " + Get(ex, "hist");
|
||||
} else if (source == "tree") {
|
||||
rows = FromTree(ex, files, twoD);
|
||||
from = "tree " + Get(ex, "tree") + ", branch " + Get(ex, "branch");
|
||||
if (!Get(ex, "cut", "").empty()) from += ", cut: " + Get(ex, "cut");
|
||||
} else {
|
||||
throw std::runtime_error("[extract] source must be \"hist\" or \"tree\", not " + source +
|
||||
(source == "csv" ? " (event-list CSVs: use extract_yields.py)" : ""));
|
||||
}
|
||||
|
||||
TString detUpper = det.c_str();
|
||||
detUpper.ToUpper();
|
||||
const std::string outFile = outDir + "/" + (det.empty() ? "yields.csv" : "yields_" + det + ".csv");
|
||||
WriteCsv(rows, twoD, var,
|
||||
{title + " " + detUpper.Data() + ": yields, binned in " + var, "from " + fileText + " : " + from,
|
||||
"written by extract_yields.C"},
|
||||
outFile);
|
||||
double total = 0;
|
||||
for (const auto& r : rows) total += r.counts;
|
||||
printf(" %.0f counts in %zu bins -> %s\n", total, rows.size(), outFile.c_str());
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
fprintf(stderr, "extract_yields.C: %s\n", e.what());
|
||||
gSystem->Exit(1); // a failed extraction must not look like a finished one
|
||||
}
|
||||
}
|
||||
164
cross_section/geometric_efficiency.py
Executable file
164
cross_section/geometric_efficiency.py
Executable file
|
|
@ -0,0 +1,164 @@
|
|||
"""Step 2: geometric efficiency of every bin, for the detectors as they are now (detectors.py).
|
||||
|
||||
~/myenv/bin/python -B geometric_efficiency.py examples/27Al_ap_runA.toml # ~1 min; numpy and pandas only
|
||||
|
||||
A toy Monte Carlo per bin (same bins as the cross section):
|
||||
- vertices uniform over the bin's stretch of beam path (angular mode: over the window);
|
||||
- beam energy at each vertex from the nominal beam track;
|
||||
- the residual nucleus' state drawn from [reaction] states: fixed weights, or state cross sections
|
||||
from a table (e.g. TALYS) at the bin's nominal E_cm;
|
||||
- emission isotropic in the CM (angular mode: cos theta_cm uniform inside the theta bin, theta_cm of the ejectile);
|
||||
- relativistic two-body kinematics, straight tracks from the vertex.
|
||||
|
||||
Writes <out_dir>/eps_<det>.csv, one row per bin, with these efficiencies (each times the azimuthal coverage):
|
||||
eps_si the particle reaches the analysed silicon (solid angle)
|
||||
eps_si_thr eps_si, and its energy at the vertex is above the Si threshold (the ch5 "solid angle" convention;
|
||||
a kinematic cut, not geometry)
|
||||
eps_counter eps_si, and it crosses the guard, anode and cathode radii inside the counter
|
||||
eps_geometric eps_counter, and it clears the beam tube and the PCB hole <- THE GEOMETRIC EFFICIENCY
|
||||
and the Monte Carlo relative error of each (<name>_rel_err).
|
||||
Not included, because they are not geometry: energy loss in the gas, the Si threshold after the gas, the counter's
|
||||
deposit threshold, intrinsic efficiencies. They are why the adopted eps_tube of ../scratch/xs_27Al_lowE_2026-10-15 is lower.
|
||||
|
||||
CONFIG
|
||||
[reaction] beam_mass_u, target_mass_u, ejectile_mass_u, recoil_mass_u (atomic masses; Q from these)
|
||||
detect = "ejectile" (default) or "recoil": the particle the silicon sees
|
||||
states = [{ex_mev = 0.0, weight = 1.0}, ...] fixed state weights, or
|
||||
states_csv = file with ecm_mev and one column ex_<Ex in MeV> per state (state cross sections)
|
||||
[efficiency] n_per_bin (default 400000), seed
|
||||
[beam], [bins], mode, dets as for the cross section.
|
||||
"""
|
||||
import argparse
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
import common
|
||||
import detectors
|
||||
|
||||
U = 931.49410242 # MeV per u
|
||||
EFFICIENCIES = ("eps_si", "eps_si_thr", "eps_counter", "eps_geometric")
|
||||
|
||||
|
||||
def two_body(t_beam, reaction, ex, cos_cm):
|
||||
"""Relativistic two-body kinematics, beam on target at rest -> ejectile (at cos_cm in the CM) + recoil (state ex).
|
||||
Returns the lab kinetic energy (MeV) and polar angle (rad) of the detected particle, and whether the channel is
|
||||
open at this beam energy."""
|
||||
m1, m2 = reaction["beam_mass_u"] * U, reaction["target_mass_u"] * U
|
||||
m3, m4 = reaction["ejectile_mass_u"] * U, reaction["recoil_mass_u"] * U + ex
|
||||
s = (m1 + m2) ** 2 + 2 * m2 * t_beam # invariant mass squared
|
||||
sqrt_s = np.sqrt(s)
|
||||
is_open = sqrt_s > m3 + m4
|
||||
e_total = t_beam + m1 + m2
|
||||
beta = np.sqrt(t_beam ** 2 + 2 * t_beam * m1) / e_total # of the CM in the lab
|
||||
gamma = e_total / sqrt_s
|
||||
p_cm = np.sqrt(np.clip((s - (m3 + m4) ** 2) * (s - (m3 - m4) ** 2), 0, None)) / (2 * sqrt_s)
|
||||
sin_cm = np.sqrt(1 - cos_cm ** 2)
|
||||
|
||||
# the recoil goes the opposite way in the CM
|
||||
mass, sign = (m3, 1.0) if reaction.get("detect", "ejectile") == "ejectile" else (m4, -1.0)
|
||||
e_cm = np.sqrt(p_cm ** 2 + mass ** 2)
|
||||
p_z = gamma * (sign * p_cm * cos_cm + beta * e_cm)
|
||||
energy = gamma * (e_cm + beta * sign * p_cm * cos_cm)
|
||||
theta = np.arctan2(p_cm * sin_cm, p_z)
|
||||
return np.where(is_open, energy - mass, 0.0), theta, is_open
|
||||
|
||||
|
||||
def q_value_mev(reaction):
|
||||
return (reaction["beam_mass_u"] + reaction["target_mass_u"]
|
||||
- reaction["ejectile_mass_u"] - reaction["recoil_mass_u"]) * U
|
||||
|
||||
|
||||
def state_weights(reaction, ecm, states_table):
|
||||
"""Excitation energies of the residual nucleus and their probabilities at this E_cm. States that are closed
|
||||
(Ex >= Q + E_cm) get weight 0; if none is open, the ground state alone."""
|
||||
if states_table is None:
|
||||
states = reaction.get("states", [{"ex_mev": 0.0, "weight": 1.0}])
|
||||
ex = np.array([s["ex_mev"] for s in states], float)
|
||||
w = np.array([s["weight"] for s in states], float)
|
||||
else:
|
||||
columns = [c for c in states_table.columns if c.startswith("ex_")]
|
||||
ex = np.array([float(c[3:]) for c in columns])
|
||||
# the table row nearest this E_cm, among rows with any cross section (below the first such row, its weights)
|
||||
rows = states_table[states_table[columns].sum(axis=1) > 0]
|
||||
row = rows.iloc[np.argmin(np.abs(rows["ecm_mev"].to_numpy() - max(ecm, rows["ecm_mev"].min())))]
|
||||
w = row[columns].to_numpy(float)
|
||||
w = np.where(ex < q_value_mev(reaction) + ecm, w, 0.0)
|
||||
if w.sum() <= 0:
|
||||
w = (ex == 0).astype(float)
|
||||
return ex, w / w.sum()
|
||||
|
||||
|
||||
def efficiency_of_bin(det, reaction, track, z_range, cos_range, ecm, states_table, n, rng):
|
||||
"""Toy for one bin: n reactions with vertices uniform in z_range and cos(theta_cm) uniform in cos_range."""
|
||||
z = rng.uniform(z_range[0], z_range[1], n)
|
||||
t_beam = track.energy_at(z, nominal=True)
|
||||
ex_states, weights = state_weights(reaction, ecm, states_table)
|
||||
ex = rng.choice(ex_states, size=n, p=weights)
|
||||
cos_cm = rng.uniform(cos_range[0], cos_range[1], n)
|
||||
energy, theta, is_open = two_body(t_beam, reaction, ex, cos_cm)
|
||||
is_open &= t_beam > 0
|
||||
|
||||
on_si = is_open & detectors.hits_silicon(det, z, theta)
|
||||
passed = {"eps_si": on_si,
|
||||
"eps_si_thr": on_si & (energy > detectors.SI_THRESHOLD_MEV[det]),
|
||||
"eps_counter": on_si & detectors.crosses_counter(z, theta)}
|
||||
passed["eps_geometric"] = passed["eps_counter"] & detectors.clears_apertures(z, theta)
|
||||
|
||||
phi = detectors.PHI_FRACTION[det]
|
||||
row = {"ground_state_weight": float(weights[ex_states == 0].sum()), "frac_open": float(is_open.mean())}
|
||||
for name, ok in passed.items():
|
||||
p = ok.mean()
|
||||
row[name] = phi * p
|
||||
row[f"{name}_rel_err"] = np.sqrt(p * (1 - p) / n) / p if p > 0 else np.nan # binomial
|
||||
return row
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
parser = argparse.ArgumentParser(description="Step 2: geometric efficiency per bin (detectors.py).")
|
||||
parser.add_argument("config", help="TOML config file")
|
||||
args = parser.parse_args(argv)
|
||||
config, full_path = common.read_config(args.config)
|
||||
|
||||
for det in config.get("dets", [""]):
|
||||
c = common.config_for_detector(config, det)
|
||||
reaction, settings = c["reaction"], c.get("efficiency", {})
|
||||
mode = c.get("mode", "excitation")
|
||||
k = common.ecm_per_beam_energy(reaction)
|
||||
track = common.load_beam_track(c["beam"], full_path)
|
||||
bins, window = common.make_bins(c, track, k)
|
||||
states_table = pd.read_csv(full_path(reaction["states_csv"]), comment="#") if "states_csv" in reaction else None
|
||||
rng = np.random.default_rng(settings.get("seed", 20261016))
|
||||
n = int(settings.get("n_per_bin", 400_000))
|
||||
|
||||
rows = []
|
||||
for _, b in bins.iterrows():
|
||||
if b["dz_mm"] <= 0: # past the beam's stopping point
|
||||
rows.append({name: np.nan for name in EFFICIENCIES})
|
||||
continue
|
||||
if mode == "excitation":
|
||||
ecm = b["ecm_nominal_mev"]
|
||||
cos_range = (-1.0, 1.0)
|
||||
else:
|
||||
ecm = k * track.energy_at(0.5 * (b["z_up_mm"] + b["z_down_mm"]), nominal=True)
|
||||
cos_range = (np.cos(np.radians(b["theta_hi_deg"])), np.cos(np.radians(b["theta_lo_deg"])))
|
||||
rows.append(efficiency_of_bin(det, reaction, track, (b["z_up_mm"], b["z_down_mm"]), cos_range,
|
||||
ecm, states_table, n, rng))
|
||||
table = pd.concat([bins.reset_index(drop=True), pd.DataFrame(rows)], axis=1)
|
||||
|
||||
states_text = reaction.get("states_csv", reaction.get("states", "ground state only"))
|
||||
header = [f"{c.get('title', '')} {det.upper()}: geometric efficiency per bin ({mode} mode), detectors.py",
|
||||
f"beam track: {track.description}; Q = {q_value_mev(reaction):.4f} MeV; detected: "
|
||||
f"{reaction.get('detect', 'ejectile')}; states: {states_text}",
|
||||
f"isotropic CM, straight tracks, {n} reactions per bin; azimuthal coverage {detectors.PHI_FRACTION[det]:.4f}",
|
||||
"eps_geometric = silicon + counter + apertures; eps_si_thr adds the Si threshold at the vertex energy",
|
||||
"written by geometric_efficiency.py"]
|
||||
out = common.output_file(config, full_path, "eps", det)
|
||||
common.write_csv(table, header, out)
|
||||
x_col = "ecm_nominal_mev" if mode == "excitation" else "theta_c_deg"
|
||||
print(f"--- {det}: -> {out}")
|
||||
print(table[[x_col, *EFFICIENCIES]].to_string(index=False, float_format=lambda v: f"{v:.4g}"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
7146
cross_section/inputs/beam_tracks.csv
Executable file
7146
cross_section/inputs/beam_tracks.csv
Executable file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user