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}
86 if (m_alg_run)
87 return StatusCode::SUCCESS;
88 ATH_MSG_INFO("Executing NSWGeoPlottingAlg for the first time");
89 const EventContext& ctx = Gaudi::Hive::currentContext();
90
91 const MuonGM::MuonDetectorManager* detMgr{nullptr};
92
94
95 for (auto& id_graph : m_nswPads) {
96 const Identifier& id = id_graph.first;
97 std::unique_ptr<TGraph>& pad_graph = id_graph.second;
98 const bool is_mm = m_idHelperSvc->isMM(id);
99 const int signed_lay = layerId(id);
100 std::unique_ptr<TGraph>& wheel_graph = m_nswLayers[signed_lay];
101
102 const MuonGM::MMReadoutElement* mm_roe =
103 is_mm ? detMgr->getMMReadoutElement(id) : nullptr;
104 const MuonGM::sTgcReadoutElement* st_roe =
105 is_mm ? nullptr : detMgr->getsTgcReadoutElement(id);
106
107 const MuonGM::MuonChannelDesign* design =
108 // cppcheck-suppress nullPointer; https://trac.cppcheck.net/ticket/14369
109 is_mm ? mm_roe->getDesign(id) : st_roe->getDesign(id);
110
111 const Trk::TrkDetElementBase* roe =
112 (mm_roe ? static_cast<const Trk::TrkDetElementBase*>(mm_roe)
113 : static_cast<const Trk::TrkDetElementBase*>(st_roe));
114 auto fill_graphs = [&](const Amg::Vector2D& locPos, int strip) {
115 if ((is_mm || std::abs(locPos.y()) < 1.e-3) &&
116 design->channelNumber(locPos) != strip) {
117 ATH_MSG_ALWAYS(" Backmapping of the strip number did not work for "
118 << m_idHelperSvc->toString(id) << " local pos: "
119 << locPos.x() << " " << locPos.y() << " "
120 << " " << design->channelNumber(locPos) << " vs. "
121 << strip);
122 return false;
123 }
124
125 Amg::Vector3D globPos{Amg::Vector3D::Zero()};
126 roe->surface(id).localToGlobal(locPos, Amg::Vector3D::Zero(), globPos);
127 if (pad_graph)
128 pad_graph->SetPoint(pad_graph->GetN(), globPos.x(), globPos.y());
129 if (wheel_graph)
130 wheel_graph->SetPoint(wheel_graph->GetN(), globPos.x(), globPos.y());
131
132 return true;
133 };
134 const MmIdHelper& id_helper = m_idHelperSvc->mmIdHelper();
135 auto global_points = [&](const Identifier& id, Amg::Vector3D& left,
136 Amg::Vector3D& center, Amg::Vector3D& right) {
137 Amg::Vector2D l_cen{Amg::Vector2D::Zero()}, l_left{Amg::Vector2D::Zero()},
138 l_right{Amg::Vector2D::Zero()};
139 const MuonGM::MuonChannelDesign* design = nullptr;
140 if (mm_roe)
141 design = mm_roe->getDesign(id);
142 else if (st_roe)
143 design = st_roe->getDesign(id);
144 const int chan = id_helper.channel(id);
145 design->leftEdge(chan, l_left);
146 design->center(chan, l_cen);
147 design->rightEdge(chan, l_right);
148
149 roe->surface(id).localToGlobal(l_left, Amg::Vector3D::Zero(), left);
150 roe->surface(id).localToGlobal(l_cen, Amg::Vector3D::Zero(), center);
151 roe->surface(id).localToGlobal(l_right, Amg::Vector3D::Zero(), right);
152 };
153
154 const int n_strips =
155 // cppcheck-suppress nullPointer; https://trac.cppcheck.net/ticket/14369
156 (is_mm ? mm_roe->numberOfStrips(id) : st_roe->numberOfStrips(id));
157 for (int strip = design->numberOfMissingBottomStrips() + 1;
158 strip <= n_strips; strip += 1) {
159 {
160 Amg::Vector2D locPos{Amg::Vector2D::Zero()};
161 if (design->leftEdge(strip, locPos) && !fill_graphs(locPos, strip))
162 ATH_MSG_DEBUG("left edge -- channel " << m_idHelperSvc->toString(id)
163 << " does not have strip "
164 << strip << "... Why?");
165 }
166 {
167 Amg::Vector2D locPos{Amg::Vector2D::Zero()};
168 if (design->center(strip, locPos) && !fill_graphs(locPos, strip))
169 ATH_MSG_DEBUG("center -- channel " << m_idHelperSvc->toString(id)
170 << " does not have strip " << strip
171 << "... Why?");
172 }
173 {
174 Amg::Vector2D locPos{Amg::Vector2D::Zero()};
175 if (design->rightEdge(strip, locPos) && !fill_graphs(locPos, strip))
176 ATH_MSG_DEBUG("right edge -- channel " << m_idHelperSvc->toString(id)
177 << " does not have strip "
178 << strip << "... Why?");
179 }
180 }
181 auto uv_intersects = [&]() {
183 if (design->hasStereoAngle() || m_idHelperSvc->issTgc(id))
184 return StatusCode::SUCCESS;
185 const int gap = id_helper.gasGap(id);
186 const int ml = id_helper.multilayer(id);
187 if ((ml == 1 && gap == 2) || (ml == 2 && gap == 4))
188 return StatusCode::SUCCESS;
189
190 for (int strip = design->numberOfMissingBottomStrips() + 1;
191 strip <= n_strips; strip += 1) {
192 const int u_gap = ml == 1 ? 3 : 1;
193 const int v_gap = ml == 1 ? 4 : 2;
194 const Identifier x_id = id_helper.channelID(id, ml, gap, strip);
195 const Identifier u_id = id_helper.channelID(id, ml, u_gap, strip);
196 const Identifier v_id = id_helper.channelID(id, ml, v_gap, strip);
197 Amg::Vector3D x_center{Amg::Vector3D::Zero()},
198 u_center{Amg::Vector3D::Zero()}, v_center{Amg::Vector3D::Zero()};
199 Amg::Vector3D x_left{Amg::Vector3D::Zero()},
200 u_left{Amg::Vector3D::Zero()}, v_left{Amg::Vector3D::Zero()};
201 Amg::Vector3D x_right{Amg::Vector3D::Zero()},
202 u_right{Amg::Vector3D::Zero()}, v_right{Amg::Vector3D::Zero()};
203
204 global_points(x_id, x_left, x_center, x_right);
205 global_points(u_id, u_left, u_center, u_right);
206 global_points(v_id, v_left, v_center, v_right);
207
208 const Amg::Vector3D x_dir = (x_right - x_left).unit();
209 const Amg::Vector3D v_dir = (v_left - v_right).unit();
210 const Amg::Vector3D u_dir = (u_left - u_right).unit();
211
212 std::optional<double> uv_isect =
213 Amg::intersect<3>(v_center, v_dir, u_center, u_dir);
214
215 if (!uv_isect) {
216 ATH_MSG_ERROR("Failed to intersect the uv strips for identifiers "
217 << std::endl
218 << " *** " << m_idHelperSvc->toString(u_id) << " "
219 << to_string(u_dir) << std::endl
220 << " *** " << m_idHelperSvc->toString(v_id) << " "
221 << to_string(v_dir));
222 return StatusCode::FAILURE;
223 }
224 const Amg::Vector3D uv_ipoint = u_center + (*uv_isect) * u_dir;
225 const Amg::Vector2D cen_diff = (uv_ipoint - x_center).block<2, 1>(0, 0);
226 if (cen_diff.dot(cen_diff) > std::numeric_limits<float>::epsilon()) {
227 ATH_MSG_ERROR("Expect that the uv strips "
228 << std::endl
229 << " *** " << m_idHelperSvc->toString(u_id) << " "
230 << to_string(u_dir) << std::endl
231 << " *** " << m_idHelperSvc->toString(v_id) << " "
232 << to_string(v_dir)
233 << " intersect at the center of the corresponding x "
234 "strip. But they don't. "
235 << std::endl
236 << to_string(uv_ipoint) << std::endl
237 << " vs." << std::endl
238 << to_string(x_center));
239 }
240 ATH_MSG_DEBUG("Intersection of uv is in " << to_string(uv_ipoint) << " "
241 << to_string(x_center));
242
243 std::optional<double> ux_isect =
244 Amg::intersect<3>(u_center, u_dir, x_center, x_dir);
245 if (!ux_isect) {
246 ATH_MSG_ERROR("Failed to intersect the ux strips for identifiers "
247 << std::endl
248 << " *** " << m_idHelperSvc->toString(v_id) << " "
249 << to_string(u_dir) << std::endl
250 << " *** " << m_idHelperSvc->toString(x_id) << " "
251 << to_string(x_dir));
252 return StatusCode::FAILURE;
253 }
254 ATH_MSG_DEBUG("Intersection of xu is in "
255 << to_string(x_center + (*ux_isect) * x_dir) << " "
256 << to_string(x_center));
257
258 std::optional<double> vx_isect =
259 Amg::intersect<3>(v_center, v_dir, x_center, x_dir);
260 if (!ux_isect) {
261 ATH_MSG_ERROR("Failed to intersect the vx strips for identifiers "
262 << std::endl
263 << " *** " << m_idHelperSvc->toString(v_id) << " "
264 << to_string(v_dir) << std::endl
265 << " *** " << m_idHelperSvc->toString(x_id) << " "
266 << to_string(x_dir));
267 return StatusCode::FAILURE;
268 }
269 ATH_MSG_DEBUG("Intersection of vu is in "
270 << to_string(x_center + (*vx_isect) * x_dir) << " "
271 << to_string(x_center));
272 }
273 return StatusCode::SUCCESS;
274 };
275 if (false)
276 ATH_CHECK(uv_intersects());
277
278 std::unique_ptr<TH1>& surface_histo = m_nswActiveAreas[signed_lay];
279 if (!surface_histo) {
280 ATH_MSG_WARNING("No surface histo has been made for "
281 << m_idHelperSvc->toString(id) << " " << signed_lay);
282 continue;
283 }
284 const Amg::Vector3D& surf_cent = roe->center(id);
285 double d = std::max(std::max(design->xSize(), design->maxYSize()),
286 design->minYSize());
287 for (double x = surf_cent.x() - d; x <= surf_cent.x() + d; x += 1) {
288 for (double y = surf_cent.y() - d; y <= surf_cent.y() + d; y += 1) {
289 const Amg::Vector3D glob_pos{x, y, surf_cent.z()};
290 Amg::Vector2D lpos{Amg::Vector2D::Zero()};
291 if (!roe->surface(id).globalToLocal(glob_pos, glob_pos, lpos))
292 continue;
293 if ((is_mm && mm_roe->insideActiveBounds(id, lpos, 10., 10.)) ||
294 (!is_mm && st_roe->surface(id).insideBounds(lpos, 10., 10.))
295
296 ) {
297 surface_histo->Fill(x, y);
298 }
299 }
300 }
301 }
302 m_alg_run = true;
303 return StatusCode::SUCCESS;
304}
306 const MuonGM::MuonDetectorManager* detMgr{nullptr};
307 ATH_CHECK(detStore()->retrieve(detMgr));
308 const MmIdHelper& id_helper = m_idHelperSvc->mmIdHelper();
309 for (const std::string station : {"MML", "MMS"}) {
310 for (int phi = id_helper.stationPhiMin();
311 phi <= id_helper.stationPhiMax(); ++phi) {
312 for (int eta = -2; eta <= 2; ++eta) {
313 if (eta == 0) {
314 continue;
315 }
316 bool is_valid{false};
317
318 const Identifier station_id = id_helper.elementID(station, eta, phi, is_valid);
319 if (!is_valid) {
320 continue;
321 }
322 for (int ml = id_helper.multilayerMin();
323 ml <= id_helper.multilayerMax(); ++ml) {
324 const Identifier module_id = id_helper.multilayerID(station_id, ml);
325 for (int i_layer = 1; i_layer <= 4; ++i_layer) {
326 const Identifier id =
327 id_helper.channelID(module_id, ml, i_layer, 10);
328 m_nswPads[id] = std::make_unique<TGraph>();
330 int signed_layer = layerId(id);
331 if (!m_nswLayers[signed_layer]) {
332 m_nswLayers[signed_layer] = std::make_unique<TGraph>();
333 }
334 if (!m_nswActiveAreas[signed_layer]) {
335 m_nswActiveAreas[signed_layer] = std::make_unique<TH2D>(
336 std::to_string(m_nswActiveAreas.size()).c_str(),
337 "ActiveNSW;x [mm]; y [mm]", 1000, -5001, 5001., 1000,
338 -5001., 5001.);
339 }
340 }
341 }
342 }
343 }
344 }
345 return StatusCode::SUCCESS;
346}
348 int eta = m_idHelperSvc->stationEta(id);
349 if (m_idHelperSvc->issTgc(id)) {
350 const sTgcIdHelper& id_helper = m_idHelperSvc->stgcIdHelper();
351 int ml = id_helper.multilayer(id);
352 int lay = id_helper.gasGap(id);
353 int type = id_helper.channelType(id);
354 return (8 * (type == sTgcIdHelper::sTgcChannelTypes::Strip) + 4 * (ml - 1) +
355 (lay - 1)) *
356 (eta > 0 ? 1 : -1);
357 }
358 if (m_idHelperSvc->isMM(id)) {
359 const MmIdHelper& id_helper = m_idHelperSvc->mmIdHelper();
360 int ml = id_helper.multilayer(id);
361 int lay = id_helper.gasGap(id);
362 return (16 + 4 * (ml - 1) + (lay - 1)) * (eta > 0 ? 1 : -1);
363 }
364 return -666;
365}
367 const MuonGM::MuonDetectorManager* detMgr{nullptr};
368 ATH_CHECK(detStore()->retrieve(detMgr));
369 const sTgcIdHelper& id_helper = m_idHelperSvc->stgcIdHelper();
370 for (const std::string station : {"STS", "STL"}) {
371 for (int eta = id_helper.stationEtaMin(); eta <= id_helper.stationEtaMax();
372 ++eta) {
373 if (eta == 0)
374 continue;
375 for (int phi = id_helper.stationPhiMin();
376 phi <= id_helper.stationPhiMax(); ++phi) {
377 for (int ml = id_helper.multilayerMin();
378 ml <= id_helper.multilayerMax(); ++ml) {
379 bool is_valid{false};
380 Identifier station_id =
381 id_helper.elementID(station, eta, phi, is_valid);
382 if (!is_valid)
383 continue;
384 const Identifier module_id = id_helper.multilayerID(station_id, ml);
385 if (!detMgr->getsTgcReadoutElement(module_id))
386 continue;
387 for (int lay = 1; lay <= 4; ++lay) {
388 const Identifier strip_id = id_helper.channelID(
389 module_id, ml, lay, sTgcIdHelper::sTgcChannelTypes::Strip, 10);
390 const Identifier wire_id = id_helper.channelID(
391 module_id, ml, lay, sTgcIdHelper::sTgcChannelTypes::Wire, 10);
392 for (const Identifier& id : {strip_id, wire_id}) {
393 m_nswPads[id] = std::make_unique<TGraph>();
394 int signed_layer = layerId(id);
395 if (!m_nswLayers[signed_layer]) {
396 m_nswLayers[signed_layer] = std::make_unique<TGraph>();
397 }
398 if (!m_nswActiveAreas[signed_layer]) {
399 m_nswActiveAreas[signed_layer] = std::make_unique<TH2D>(
400 std::to_string(m_nswActiveAreas.size()).c_str(),
401 "ActiveNSW;x [mm]; y [mm]", 1000, -5001, 5001., 1000,
402 -5001., 5001.);
403 }
404 }
405 }
406 }
407 }
408 }
409 }
410 return StatusCode::SUCCESS;
411}
412}
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.
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