ATLAS Offline Software
Loading...
Searching...
No Matches
MuonDetDescr/MuonGeoModelTest/src/NSWGeoPlottingAlg.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4#include "NSWGeoPlottingAlg.h"
5
6#include <cmath>
7#include <format>
8
9#include "GaudiKernel/SystemOfUnits.h"
14#include "TFile.h"
15#include "TGraph.h"
16#include "TH1.h"
17#include "TH2D.h"
19#include "TrkSurfaces/Surface.h"
20
21namespace {
22std::string to_string(const Amg::Vector3D& v) {
23 std::stringstream sstr{};
24 sstr << "[x,y,z]=(" << v.x() << "," << v.y() << "," << v.z()
25 << ") [theta/eta/phi]=(" << (v.theta() / Gaudi::Units::degree) << ","
26 << v.eta() << "," << v.phi() << ")";
27 return sstr.str();
28}
29
30} // namespace
31namespace MuonGM{
33 auto out_file = std::make_unique<TFile>(m_outFile.value().c_str(), "RECREATE");
34 if (!out_file || !out_file->IsOpen() || out_file->IsZombie()) {
35
36 ATH_MSG_FATAL("Failed to create the output file " << m_outFile);
37 return StatusCode::FAILURE;
38 }
39 out_file->mkdir("SinglePads");
40 out_file->mkdir("ActiveSurfaces");
41 const MmIdHelper& mm_helper = m_idHelperSvc->mmIdHelper();
42 const sTgcIdHelper& st_helper = m_idHelperSvc->stgcIdHelper();
43 for (auto& id_graph : m_nswPads) {
44 std::unique_ptr<TGraph>& graph = id_graph.second;
45 const Identifier& id = id_graph.first;
46 bool is_mm = m_idHelperSvc->isMM(id);
47 const int stEta = m_idHelperSvc->stationEta(id);
48 const int ml = is_mm ? mm_helper.multilayer(id) : st_helper.multilayer(id);
49 const int lay = is_mm ? mm_helper.gasGap(id) : st_helper.gasGap(id);
50 const std::string ch_name =
51 (is_mm ? mm_helper.stationNameString(m_idHelperSvc->stationName(id))
52 : st_helper.stationNameString(m_idHelperSvc->stationName(id))) +
53 std::to_string(std::abs(stEta)) + (stEta > 0 ? "A" : "C") +
54 std::to_string(m_idHelperSvc->stationPhi(id)) + "W" +
55 std::to_string(ml) + "L" + std::to_string(lay);
56 TDirectory* dir = out_file->GetDirectory("SinglePads");
57 dir->WriteObject(graph.get(), ch_name.c_str());
58 graph.reset();
59 const int signed_lay = layerId(id);
60 std::unique_ptr<TGraph>& lay_graph = m_nswLayers[signed_lay];
61 if (!lay_graph)
62 continue;
63 std::string lay_name = std::string{is_mm ? "MMG" : "STG"} + "W" +
64 std::to_string(ml) + (stEta > 0 ? "A" : "C") +
65 std::to_string(lay);
66 out_file->WriteObject(lay_graph.get(), lay_name.c_str());
67 lay_graph.reset();
68 std::unique_ptr<TH1>& active_area = m_nswActiveAreas[signed_lay];
69 if (!active_area)
70 continue;
71 dir = out_file->GetDirectory("ActiveSurfaces");
72
73 dir->WriteObject(active_area.get(), lay_name.c_str());
74 active_area.reset();
75 }
76 return StatusCode::SUCCESS;
77}
79 ATH_CHECK(m_DetectorManagerKey.initialize());
80 ATH_CHECK(m_idHelperSvc.retrieve());
83 return StatusCode::SUCCESS;
84}
85StatusCode NSWGeoPlottingAlg::execute(const EventContext& ctx) {
86 if (m_alg_run)
87 return StatusCode::SUCCESS;
88 ATH_MSG_INFO("Executing NSWGeoPlottingAlg for the first time");
89
90 const MuonGM::MuonDetectorManager* detMgr{nullptr};
91
93
94 for (auto& id_graph : m_nswPads) {
95 const Identifier& id = id_graph.first;
96 std::unique_ptr<TGraph>& pad_graph = id_graph.second;
97 const bool is_mm = m_idHelperSvc->isMM(id);
98 const int signed_lay = layerId(id);
99 std::unique_ptr<TGraph>& wheel_graph = m_nswLayers[signed_lay];
100
101 const MuonGM::MMReadoutElement* mm_roe =
102 is_mm ? detMgr->getMMReadoutElement(id) : nullptr;
103 const MuonGM::sTgcReadoutElement* st_roe =
104 is_mm ? nullptr : detMgr->getsTgcReadoutElement(id);
105
106 const MuonGM::MuonChannelDesign* design =
107 // cppcheck-suppress nullPointer; https://trac.cppcheck.net/ticket/14369
108 is_mm ? mm_roe->getDesign(id) : st_roe->getDesign(id);
109
110 const Trk::TrkDetElementBase* roe =
111 (mm_roe ? static_cast<const Trk::TrkDetElementBase*>(mm_roe)
112 : static_cast<const Trk::TrkDetElementBase*>(st_roe));
113 auto fill_graphs = [&](const Amg::Vector2D& locPos, int strip) {
114 if ((is_mm || std::abs(locPos.y()) < 1.e-3) &&
115 design->channelNumber(locPos) != strip) {
116 ATH_MSG_ALWAYS(" Backmapping of the strip number did not work for "
117 << m_idHelperSvc->toString(id) << " local pos: "
118 << locPos.x() << " " << locPos.y() << " "
119 << " " << design->channelNumber(locPos) << " vs. "
120 << strip);
121 return false;
122 }
123
124 Amg::Vector3D globPos{Amg::Vector3D::Zero()};
125 roe->surface(id).localToGlobal(locPos, Amg::Vector3D::Zero(), globPos);
126 if (pad_graph)
127 pad_graph->SetPoint(pad_graph->GetN(), globPos.x(), globPos.y());
128 if (wheel_graph)
129 wheel_graph->SetPoint(wheel_graph->GetN(), globPos.x(), globPos.y());
130
131 return true;
132 };
133 const MmIdHelper& id_helper = m_idHelperSvc->mmIdHelper();
134 auto global_points = [&](const Identifier& id, Amg::Vector3D& left,
135 Amg::Vector3D& center, Amg::Vector3D& right) {
136 Amg::Vector2D l_cen{Amg::Vector2D::Zero()}, l_left{Amg::Vector2D::Zero()},
137 l_right{Amg::Vector2D::Zero()};
138 const MuonGM::MuonChannelDesign* design = nullptr;
139 if (mm_roe)
140 design = mm_roe->getDesign(id);
141 else if (st_roe)
142 design = st_roe->getDesign(id);
143 const int chan = id_helper.channel(id);
144 design->leftEdge(chan, l_left);
145 design->center(chan, l_cen);
146 design->rightEdge(chan, l_right);
147
148 roe->surface(id).localToGlobal(l_left, Amg::Vector3D::Zero(), left);
149 roe->surface(id).localToGlobal(l_cen, Amg::Vector3D::Zero(), center);
150 roe->surface(id).localToGlobal(l_right, Amg::Vector3D::Zero(), right);
151 };
152
153 const int n_strips =
154 // cppcheck-suppress nullPointer; https://trac.cppcheck.net/ticket/14369
155 (is_mm ? mm_roe->numberOfStrips(id) : st_roe->numberOfStrips(id));
156 for (int strip = design->numberOfMissingBottomStrips() + 1;
157 strip <= n_strips; strip += 1) {
158 {
159 Amg::Vector2D locPos{Amg::Vector2D::Zero()};
160 if (design->leftEdge(strip, locPos) && !fill_graphs(locPos, strip))
161 ATH_MSG_DEBUG("left edge -- channel " << m_idHelperSvc->toString(id)
162 << " does not have strip "
163 << strip << "... Why?");
164 }
165 {
166 Amg::Vector2D locPos{Amg::Vector2D::Zero()};
167 if (design->center(strip, locPos) && !fill_graphs(locPos, strip))
168 ATH_MSG_DEBUG("center -- channel " << m_idHelperSvc->toString(id)
169 << " does not have strip " << strip
170 << "... Why?");
171 }
172 {
173 Amg::Vector2D locPos{Amg::Vector2D::Zero()};
174 if (design->rightEdge(strip, locPos) && !fill_graphs(locPos, strip))
175 ATH_MSG_DEBUG("right edge -- channel " << m_idHelperSvc->toString(id)
176 << " does not have strip "
177 << strip << "... Why?");
178 }
179 }
180 auto uv_intersects = [&]() {
182 if (design->hasStereoAngle() || m_idHelperSvc->issTgc(id))
183 return StatusCode::SUCCESS;
184 const int gap = id_helper.gasGap(id);
185 const int ml = id_helper.multilayer(id);
186 if ((ml == 1 && gap == 2) || (ml == 2 && gap == 4))
187 return StatusCode::SUCCESS;
188
189 for (int strip = design->numberOfMissingBottomStrips() + 1;
190 strip <= n_strips; strip += 1) {
191 const int u_gap = ml == 1 ? 3 : 1;
192 const int v_gap = ml == 1 ? 4 : 2;
193 const Identifier x_id = id_helper.channelID(id, ml, gap, strip);
194 const Identifier u_id = id_helper.channelID(id, ml, u_gap, strip);
195 const Identifier v_id = id_helper.channelID(id, ml, v_gap, strip);
196 Amg::Vector3D x_center{Amg::Vector3D::Zero()},
197 u_center{Amg::Vector3D::Zero()}, v_center{Amg::Vector3D::Zero()};
198 Amg::Vector3D x_left{Amg::Vector3D::Zero()},
199 u_left{Amg::Vector3D::Zero()}, v_left{Amg::Vector3D::Zero()};
200 Amg::Vector3D x_right{Amg::Vector3D::Zero()},
201 u_right{Amg::Vector3D::Zero()}, v_right{Amg::Vector3D::Zero()};
202
203 global_points(x_id, x_left, x_center, x_right);
204 global_points(u_id, u_left, u_center, u_right);
205 global_points(v_id, v_left, v_center, v_right);
206
207 const Amg::Vector3D x_dir = (x_right - x_left).unit();
208 const Amg::Vector3D v_dir = (v_left - v_right).unit();
209 const Amg::Vector3D u_dir = (u_left - u_right).unit();
210
211 std::optional<double> uv_isect =
212 Amg::intersect<3>(v_center, v_dir, u_center, u_dir);
213
214 if (!uv_isect) {
215 ATH_MSG_ERROR("Failed to intersect the uv strips for identifiers "
216 << std::endl
217 << " *** " << m_idHelperSvc->toString(u_id) << " "
218 << to_string(u_dir) << std::endl
219 << " *** " << m_idHelperSvc->toString(v_id) << " "
220 << to_string(v_dir));
221 return StatusCode::FAILURE;
222 }
223 const Amg::Vector3D uv_ipoint = u_center + (*uv_isect) * u_dir;
224 const Amg::Vector2D cen_diff = (uv_ipoint - x_center).block<2, 1>(0, 0);
225 if (cen_diff.dot(cen_diff) > std::numeric_limits<float>::epsilon()) {
226 ATH_MSG_ERROR("Expect that the uv strips "
227 << std::endl
228 << " *** " << m_idHelperSvc->toString(u_id) << " "
229 << to_string(u_dir) << std::endl
230 << " *** " << m_idHelperSvc->toString(v_id) << " "
231 << to_string(v_dir)
232 << " intersect at the center of the corresponding x "
233 "strip. But they don't. "
234 << std::endl
235 << to_string(uv_ipoint) << std::endl
236 << " vs." << std::endl
237 << to_string(x_center));
238 }
239 ATH_MSG_DEBUG("Intersection of uv is in " << to_string(uv_ipoint) << " "
240 << to_string(x_center));
241
242 std::optional<double> ux_isect =
243 Amg::intersect<3>(u_center, u_dir, x_center, x_dir);
244 if (!ux_isect) {
245 ATH_MSG_ERROR("Failed to intersect the ux strips for identifiers "
246 << std::endl
247 << " *** " << m_idHelperSvc->toString(v_id) << " "
248 << to_string(u_dir) << std::endl
249 << " *** " << m_idHelperSvc->toString(x_id) << " "
250 << to_string(x_dir));
251 return StatusCode::FAILURE;
252 }
253 ATH_MSG_DEBUG("Intersection of xu is in "
254 << to_string(x_center + (*ux_isect) * x_dir) << " "
255 << to_string(x_center));
256
257 std::optional<double> vx_isect =
258 Amg::intersect<3>(v_center, v_dir, x_center, x_dir);
259 if (!ux_isect) {
260 ATH_MSG_ERROR("Failed to intersect the vx strips for identifiers "
261 << std::endl
262 << " *** " << m_idHelperSvc->toString(v_id) << " "
263 << to_string(v_dir) << std::endl
264 << " *** " << m_idHelperSvc->toString(x_id) << " "
265 << to_string(x_dir));
266 return StatusCode::FAILURE;
267 }
268 ATH_MSG_DEBUG("Intersection of vu is in "
269 << to_string(x_center + (*vx_isect) * x_dir) << " "
270 << to_string(x_center));
271 }
272 return StatusCode::SUCCESS;
273 };
274 if (false)
275 ATH_CHECK(uv_intersects());
276
277 std::unique_ptr<TH1>& surface_histo = m_nswActiveAreas[signed_lay];
278 if (!surface_histo) {
279 ATH_MSG_WARNING("No surface histo has been made for "
280 << m_idHelperSvc->toString(id) << " " << signed_lay);
281 continue;
282 }
283 const Amg::Vector3D& surf_cent = roe->center(id);
284 double d = std::max(std::max(design->xSize(), design->maxYSize()),
285 design->minYSize());
286 for (double x = surf_cent.x() - d; x <= surf_cent.x() + d; x += 1) {
287 for (double y = surf_cent.y() - d; y <= surf_cent.y() + d; y += 1) {
288 const Amg::Vector3D glob_pos{x, y, surf_cent.z()};
289 Amg::Vector2D lpos{Amg::Vector2D::Zero()};
290 if (!roe->surface(id).globalToLocal(glob_pos, glob_pos, lpos))
291 continue;
292 if ((is_mm && mm_roe->insideActiveBounds(id, lpos, 10., 10.)) ||
293 (!is_mm && st_roe->surface(id).insideBounds(lpos, 10., 10.))
294
295 ) {
296 surface_histo->Fill(x, y);
297 }
298 }
299 }
300 }
301 m_alg_run = true;
302 return StatusCode::SUCCESS;
303}
305 const MuonGM::MuonDetectorManager* detMgr{nullptr};
306 ATH_CHECK(detStore()->retrieve(detMgr));
307 const MmIdHelper& id_helper = m_idHelperSvc->mmIdHelper();
308 for (const std::string station : {"MML", "MMS"}) {
309 for (int phi = id_helper.stationPhiMin();
310 phi <= id_helper.stationPhiMax(); ++phi) {
311 for (int eta = -2; eta <= 2; ++eta) {
312 if (eta == 0) {
313 continue;
314 }
315 bool is_valid{false};
316
317 const Identifier station_id = id_helper.elementID(station, eta, phi, is_valid);
318 if (!is_valid) {
319 continue;
320 }
321 for (int ml = id_helper.multilayerMin();
322 ml <= id_helper.multilayerMax(); ++ml) {
323 const Identifier module_id = id_helper.multilayerID(station_id, ml);
324 for (int i_layer = 1; i_layer <= 4; ++i_layer) {
325 const Identifier id =
326 id_helper.channelID(module_id, ml, i_layer, 10);
327 m_nswPads[id] = std::make_unique<TGraph>();
329 int signed_layer = layerId(id);
330 if (!m_nswLayers[signed_layer]) {
331 m_nswLayers[signed_layer] = std::make_unique<TGraph>();
332 }
333 if (!m_nswActiveAreas[signed_layer]) {
334 m_nswActiveAreas[signed_layer] = std::make_unique<TH2D>(
335 std::to_string(m_nswActiveAreas.size()).c_str(),
336 "ActiveNSW;x [mm]; y [mm]", 1000, -5001, 5001., 1000,
337 -5001., 5001.);
338 }
339 }
340 }
341 }
342 }
343 }
344 return StatusCode::SUCCESS;
345}
347 int eta = m_idHelperSvc->stationEta(id);
348 if (m_idHelperSvc->issTgc(id)) {
349 const sTgcIdHelper& id_helper = m_idHelperSvc->stgcIdHelper();
350 int ml = id_helper.multilayer(id);
351 int lay = id_helper.gasGap(id);
352 int type = id_helper.channelType(id);
353 return (8 * (type == sTgcIdHelper::sTgcChannelTypes::Strip) + 4 * (ml - 1) +
354 (lay - 1)) *
355 (eta > 0 ? 1 : -1);
356 }
357 if (m_idHelperSvc->isMM(id)) {
358 const MmIdHelper& id_helper = m_idHelperSvc->mmIdHelper();
359 int ml = id_helper.multilayer(id);
360 int lay = id_helper.gasGap(id);
361 return (16 + 4 * (ml - 1) + (lay - 1)) * (eta > 0 ? 1 : -1);
362 }
363 return -666;
364}
366 const MuonGM::MuonDetectorManager* detMgr{nullptr};
367 ATH_CHECK(detStore()->retrieve(detMgr));
368 const sTgcIdHelper& id_helper = m_idHelperSvc->stgcIdHelper();
369 for (const std::string station : {"STS", "STL"}) {
370 for (int eta = id_helper.stationEtaMin(); eta <= id_helper.stationEtaMax();
371 ++eta) {
372 if (eta == 0)
373 continue;
374 for (int phi = id_helper.stationPhiMin();
375 phi <= id_helper.stationPhiMax(); ++phi) {
376 for (int ml = id_helper.multilayerMin();
377 ml <= id_helper.multilayerMax(); ++ml) {
378 bool is_valid{false};
379 Identifier station_id =
380 id_helper.elementID(station, eta, phi, is_valid);
381 if (!is_valid)
382 continue;
383 const Identifier module_id = id_helper.multilayerID(station_id, ml);
384 if (!detMgr->getsTgcReadoutElement(module_id))
385 continue;
386 for (int lay = 1; lay <= 4; ++lay) {
387 const Identifier strip_id = id_helper.channelID(
388 module_id, ml, lay, sTgcIdHelper::sTgcChannelTypes::Strip, 10);
389 const Identifier wire_id = id_helper.channelID(
390 module_id, ml, lay, sTgcIdHelper::sTgcChannelTypes::Wire, 10);
391 for (const Identifier& id : {strip_id, wire_id}) {
392 m_nswPads[id] = std::make_unique<TGraph>();
393 int signed_layer = layerId(id);
394 if (!m_nswLayers[signed_layer]) {
395 m_nswLayers[signed_layer] = std::make_unique<TGraph>();
396 }
397 if (!m_nswActiveAreas[signed_layer]) {
398 m_nswActiveAreas[signed_layer] = std::make_unique<TH2D>(
399 std::to_string(m_nswActiveAreas.size()).c_str(),
400 "ActiveNSW;x [mm]; y [mm]", 1000, -5001, 5001., 1000,
401 -5001., 5001.);
402 }
403 }
404 }
405 }
406 }
407 }
408 }
409 return StatusCode::SUCCESS;
410}
411}
Scalar eta() const
pseudorapidity method
Scalar phi() const
phi method
const PlainObject unit() const
This is a plugin that makes Eigen look like CLHEP & defines some convenience methods.
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_FATAL(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_ALWAYS(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
static std::string to_string(const std::vector< T > &v)
#define y
#define x
const ServiceHandle< StoreGateSvc > & detStore() const
TGraph * graph(const std::string &graphName, const std::string &tDir="", const std::string &stream="")
Simplify the retrieval of registered TGraphs.
Identifier channelID(int stationName, int stationEta, int stationPhi, int multilayer, int gasGap, int channel) const
Identifier elementID(int stationName, int stationEta, int stationPhi) const
Identifier multilayerID(const Identifier &channeldID) const
int channel(const Identifier &id) const override
static int stationPhiMin()
int gasGap(const Identifier &id) const override
get the hashes
static int multilayerMin()
static int multilayerMax()
static int stationPhiMax()
int multilayer(const Identifier &id) const
An MMReadoutElement corresponds to a single STGC module; therefore typicaly a barrel muon station con...
virtual int numberOfStrips(const Identifier &layerId) const override final
number of strips per layer
bool insideActiveBounds(const Identifier &id, const Amg::Vector2D &locpos, double tol1=0., double tol2=0.) const
boundary check Wrapper Trk::PlaneSurface::insideBounds() taking into account the passivated width
const MuonChannelDesign * getDesign(const Identifier &id) const
returns the MuonChannelDesign class for the given identifier
virtual const Trk::PlaneSurface & surface() const override
access to chamber surface (phi orientation), uses the first gas gap
The MuonDetectorManager stores the transient representation of the Muon Spectrometer geometry and pro...
const MMReadoutElement * getMMReadoutElement(const Identifier &id) const
access via extended identifier (requires unpacking)
const sTgcReadoutElement * getsTgcReadoutElement(const Identifier &id) const
access via extended identifier (requires unpacking)
std::map< int, std::unique_ptr< TGraph > > m_nswLayers
Map showing the edges of the 16 layers of the NSW.
std::map< Identifier, std::unique_ptr< TGraph > > m_nswPads
Map containing each PCB of the NSW seperately.
StatusCode execute(const EventContext &ctx) override
Execute method.
std::map< int, std::unique_ptr< TH1 > > m_nswActiveAreas
Map showing the active areas of the NSW to show the passivation.
SG::ReadCondHandleKey< MuonGM::MuonDetectorManager > m_DetectorManagerKey
ServiceHandle< Muon::IMuonIdHelperSvc > m_idHelperSvc
An sTgcReadoutElement corresponds to a single STGC module; therefore typicaly a barrel muon station c...
const MuonChannelDesign * getDesign(const Identifier &id) const
returns the MuonChannelDesign class for the given identifier
virtual int numberOfStrips(const Identifier &layerId) const override final
number of strips per layer
const std::string & stationNameString(const Identifier &id) const
virtual bool insideBounds(const Amg::Vector2D &locpos, double tol1=0., double tol2=0.) const override
This method calls the inside() method of the Bounds.
virtual bool globalToLocal(const Amg::Vector3D &glob, const Amg::Vector3D &mom, Amg::Vector2D &loc) const =0
Specified by each surface type: GlobalToLocal method without dynamic memory allocation - boolean chec...
virtual void localToGlobal(const Amg::Vector2D &locp, const Amg::Vector3D &mom, Amg::Vector3D &glob) const =0
Specified by each surface type: LocalToGlobal method without dynamic memory allocation.
This is the base class for all tracking detector elements with read-out relevant information.
virtual const Amg::Vector3D & center() const =0
Return the center of the element.
virtual const Surface & surface() const =0
Return surface associated with this detector element.
int multilayer(const Identifier &id) const
static int stationPhiMax()
int channelType(const Identifier &id) const
Identifier elementID(int stationName, int stationEta, int stationPhi) const
static int multilayerMax()
static int stationPhiMin()
static int stationEtaMin()
static int multilayerMin()
static int stationEtaMax()
int gasGap(const Identifier &id) const override
get the hashes
Identifier channelID(int stationName, int stationEta, int stationPhi, int multilayer, int gasGap, int channelType, int channel) const
Identifier multilayerID(const Identifier &channeldID) const
std::optional< double > intersect(const AmgVector(N)&posA, const AmgVector(N)&dirA, const AmgVector(N)&posB, const AmgVector(N)&dirB)
Calculates the point B' along the line B that's closest to a second line A.
Eigen::Matrix< double, 2, 1 > Vector2D
Eigen::Matrix< double, 3, 1 > Vector3D
Ensure that the Athena extensions are properly loaded.
Definition GeoMuonHits.h:27
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
bool center(int channel, Amg::Vector2D &pos) const
STRIPS ONLY: Returns the center on the strip.
bool leftEdge(int channel, Amg::Vector2D &pos) const
STRIPS ONLY: Returns the left edge of the strip.
int numberOfMissingBottomStrips() const
Returns the number of missing bottom strips.
bool rightEdge(int channel, Amg::Vector2D &pos) const
STRIPS ONLY: Returns the right edge of the strip.
double hasStereoAngle() const
returns whether the stereo angle is non-zero
int channelNumber(const Amg::Vector2D &pos) const
calculate local channel number, range 1=nstrips like identifiers. Returns -1 if out of range