AnodePlane.cxx
Go to the documentation of this file.
4 
8 
13 
15 #include "WireCellUtil/String.h"
16 
17 #include <string>
18 
21 
22 
23 using namespace WireCell;
24 using namespace std;
25 using WireCell::String::format;
26 
27 
29  : m_ident(0)
30  , l(Log::logger("geom"))
31 {
32 }
33 
34 
35 
36 const int default_nimpacts = 10;
37 
38 WireCell::Configuration Gen::AnodePlane::default_configuration() const
39 {
41 
42  /// This number is used to take from the wire file the anode
43  put(cfg, "ident", 0);
44 
45  /// Name of a IWireSchema component.
46  // note: this used to be a "wires" parameter directly specifying a filename.
47  put(cfg, "wire_schema", "");
48 
49  // Number of impact positions per wire pitch. This should likely
50  // match exactly what the field response functions use.
51  put(cfg, "nimpacts", default_nimpacts);
52 
53  // The AnodePlane has to two faces, one of which may be "null".
54  // Face 0 or "front" is the one that is toward the positive X-axis
55  // (wire coordinate system). A face consists of wires and some
56  // bounds on the X-coordinate in the form of three planes:
57  //
58  // - response :: The response plane demarks the location where the
59  // complex fields near the wires are considered sufficiently
60  // regular.
61  //
62  // - anode and cathode :: These two planes bracket the region in X
63  // - for the volume associated with the face. The transverse
64  // - range is determined by the wires. If anode is not given then
65  // - response will be used instead. Along with wires, these
66  // - determine the sensitive bounding box of the AnodeFaces.
67  //
68  // eg: faces: [
69  // {response: 10*wc.cm - 6*wc.mm, 2.5604*wc.m},
70  // null,
71  // ]
72 
73  cfg["faces"][0] = Json::nullValue;
74  cfg["faces"][1] = Json::nullValue;
75 
76  return cfg;
77 }
78 
80 
81  // To deal with wrapped wires we need to temporarily hold on to
82  // channels in their concrete form, indexec by chanel ident. This
83  // struct explicitly does NOT free its new channels as it is
84  // assumed they will all become wrapped in IChannel::pointers.
85  std::unordered_map<int, SimpleChannel*> chid2sch;
86 
87  // Only call ONCE per iwire.
88  void operator()(IWire::pointer iwire) {
89  const int chid = iwire->channel();
90  SimpleChannel* sc = nullptr;
91  auto it = chid2sch.find(chid);
92  if (it == chid2sch.end()) {
93  sc = new SimpleChannel(chid);
94  chid2sch[chid] = sc;
95  }
96  else {
97  sc = it->second;
98  }
99  sc->add(iwire);
100  }
101 
102  SimpleChannel* operator()(int chid) {
103  return chid2sch[chid];
104  }
105 };
106 
107 
109 {
110  m_faces.clear();
111  m_ident = get<int>(cfg, "ident", 0);
112 
113  // check for obsolete config params:
114  if (!cfg["wires"].isNull()) {
115  l->warn("AnodePlane configuration is obsolete.");
116  l->warn("Use \"wire_store\" to name components instead of directly giving file names");
117  l->warn("This job will likely throw an exception next");
118  }
119 
120  // AnodePlane used to have the mistake of tight coupling with
121  // field response functions.
122  if (!cfg["fields"].isNull() or !cfg["field_response"].isNull()) {
123  l->warn("AnodePlane does not require any field response functions.");
124  }
125 
126  auto jfaces = cfg["faces"];
127  if (jfaces.isNull() or jfaces.empty() or (jfaces[0].isNull() and jfaces[1].isNull())) {
128  l->critical("at least two faces need to be defined, got:\n{}", jfaces);
129  THROW(ValueError() << errmsg{"AnodePlane: error in configuration"});
130  }
131 
132 
133  const int nimpacts = get<int>(cfg, "nimpacts", default_nimpacts);
134 
135 
136  // get wire schema
137  const string ws_name = get<string>(cfg, "wire_schema");
138  if (ws_name.empty()) {
139  l->critical("\"wire_schema\" parameter must specify an IWireSchema component");
140  THROW(ValueError() << errmsg{"\"wire_schema\" parameter must specify an IWireSchema component"});
141  }
142  auto iws = Factory::find_tn<IWireSchema>(ws_name); // throws if not found
143  const WireSchema::Store& ws_store = iws->wire_schema_store();
144 
145  // keep track which channels we know about in this anode.
146  m_channels.clear();
147 
148  const WireSchema::Anode& ws_anode = ws_store.anode(m_ident);
149 
150  std::vector<WireSchema::Face> ws_faces = ws_store.faces(ws_anode);
151  const size_t nfaces = ws_faces.size();
152 
153  channel_wire_collector_t chwcollector;
154 
155  m_faces.resize(nfaces);
156  // note, WireSchema requires front/back face ordering in an anode
157  for (size_t iface=0; iface<nfaces; ++iface) {
158  const auto& ws_face = ws_faces[iface];
159  std::vector<WireSchema::Plane> ws_planes = ws_store.planes(ws_face);
160  const size_t nplanes = ws_planes.size();
161 
162  // location of imaginary boundary planes
163  bool sensitive_face = true;
164  auto jface = jfaces[(int)iface];
165  if (jface.isNull()) {
166  sensitive_face = false;
167  l->debug("anode {} face {} is not sensitive", m_ident, iface);
168  }
169  const double response_x = jface["response"].asDouble();
170  const double anode_x = get(jface, "anode", response_x);
171  const double cathode_x = jface["cathode"].asDouble();
172  l->debug("AnodePlane: X planes: \"cathode\"@ {}m, \"response\"@{}m, \"anode\"@{}m",
173  cathode_x / units::m, response_x / units::m, anode_x/units::m);
174 
175  IWirePlane::vector planes(nplanes);
176  // note, WireSchema requires U/V/W plane ordering in a face.
177  for (size_t iplane=0; iplane<nplanes; ++iplane) {
178  const auto& ws_plane = ws_planes[iplane];
179 
180  WirePlaneId wire_plane_id(iplane2layer[iplane], iface, m_ident);
181  if (wire_plane_id.index() < 0) {
182  l->critical("Bad wire plane id: {}", wire_plane_id.ident());
183  THROW(ValueError() << errmsg{format("bad wire plane id: %d", wire_plane_id.ident())});
184  }
185 
186  // (wire,pitch) directions
187  auto wire_pitch_dirs = ws_store.wire_pitch(ws_plane);
188  auto ecks_dir = wire_pitch_dirs.first.cross(wire_pitch_dirs.second);
189 
190  std::vector<int> plane_chans = ws_store.channels(ws_plane);
191  m_channels.insert(m_channels.end(), plane_chans.begin(), plane_chans.end());
192 
193  std::vector<WireSchema::Wire> ws_wires = ws_store.wires(ws_plane);
194  const size_t nwires = ws_wires.size();
195  IWire::vector wires(nwires);
196 
197  // note, WireSchema requires wire-in-plane ordering
198  for (size_t iwire=0; iwire<nwires; ++iwire) {
199  const auto& ws_wire = ws_wires[iwire];
200  const int chanid = plane_chans[iwire];
201 
202  Ray ray(ws_wire.tail, ws_wire.head);
203  auto iwireptr = make_shared<SimpleWire>(wire_plane_id, ws_wire.ident,
204  iwire, chanid, ray,
205  ws_wire.segment);
206  wires[iwire] = iwireptr;
207  m_c2wires[chanid].push_back(iwireptr);
208  m_c2wpid[chanid] = wire_plane_id.ident();
209  chwcollector(iwireptr);
210  } // wire
211 
212  const BoundingBox bb = ws_store.bounding_box(ws_plane);
213  const Ray bb_ray = bb.bounds();
214  const Vector plane_center = 0.5*(bb_ray.first + bb_ray.second);
215 
216  const double pitchmin = wire_pitch_dirs.second.dot(wires[0]->center() - plane_center);
217  const double pitchmax = wire_pitch_dirs.second.dot(wires[nwires-1]->center() - plane_center);
218  const Vector pimpos_origin(response_x, plane_center.y(), plane_center.z());
219 
220  l->debug("AnodePlane: face:{}, plane:{}, origin:{} mm",
221  iface, iplane, pimpos_origin/units::mm);
222 
223  Pimpos* pimpos = new Pimpos(nwires, pitchmin, pitchmax,
224  wire_pitch_dirs.first, wire_pitch_dirs.second,
225  pimpos_origin, nimpacts);
226 
227  IChannel::vector plane_channels;
228  {
229  for (auto w : wires) {
230  if (w->segment() > 0) {
231  continue;
232  }
233 
234  const int chanid = w->channel();
235  SimpleChannel* sch = chwcollector(chanid);
236  sch->set_index(plane_channels.size());
237  IChannel::pointer ich(sch);
238  m_ichannels[chanid] = ich;
239  plane_channels.push_back(ich);
240  }
241  }
242  sort(wires.begin(), wires.end(), IWireCompareIndex()); // redundant?
243  planes[iplane] = make_shared<WirePlane>(ws_plane.ident, pimpos, wires, plane_channels);
244 
245  // Last iteration, use W plane to define volume
246  if (iplane == nplanes-1) {
247  const double mean_pitch = (pitchmax - pitchmin) / (nwires-1);
248  BoundingBox sensvol;
249  if (sensitive_face) {
250  auto v1 = bb_ray.first;
251  auto v2 = bb_ray.second;
252  // Enlarge to anode/cathode planes in X and by 1/2 pitch in Z.
253  Point p1( anode_x, v1.y(), std::min(v1.z(), v2.z()) - 0.5*mean_pitch);
254  sensvol(p1);
255  Point p2(cathode_x, v2.y(), std::max(v1.z(), v2.z()) + 0.5*mean_pitch);
256  sensvol(p2);
257  }
258 
259  l->debug("AnodePlane: face:{} with {} planes and sensvol: {}",
260  ws_face.ident, planes.size(), sensvol.bounds());
261 
262  m_faces[iface] = make_shared<AnodeFace>(ws_face.ident, planes, sensvol);
263  }
264  } // plane
265  } // face
266 
267  // remove any duplicate channels
268  std::sort(m_channels.begin(), m_channels.end());
269  auto chend = std::unique(m_channels.begin(), m_channels.end());
270  m_channels.resize( std::distance(m_channels.begin(), chend) );
271 
272 }
273 
274 
275 IAnodeFace::pointer Gen::AnodePlane::face(int ident) const
276 {
277  for (auto ptr : m_faces) {
278  if (ptr->ident() == ident) {
279  return ptr;
280  }
281  }
282  return nullptr;
283 }
284 
285 
287 {
288  const WirePlaneId bogus(0xFFFFFFFF);
289 
290  auto got = m_c2wpid.find(channel);
291  if (got == m_c2wpid.end()) {
292  return bogus;
293  }
294  return WirePlaneId(got->second);
295 }
296 
298 {
299  auto it = m_ichannels.find(chident);
300  if (it == m_ichannels.end()) {
301  return nullptr;
302  }
303  return it->second;
304 }
305 
306 std::vector<int> Gen::AnodePlane::channels() const
307 {
308  return m_channels;
309 }
310 
311 IWire::vector Gen::AnodePlane::wires(int channel) const
312 {
313  auto it = m_c2wires.find(channel);
314  if (it == m_c2wires.end()) {
315  return IWire::vector();
316  }
317  return it->second;
318 }
std::pair< Point, Point > Ray
A line segment running from a first (tail) to a second (head) point.
Definition: Point.h:21
const int nwires
int ident() const
Unit ID as integer.
Definition: WirePlaneId.cxx:21
static const double m
Definition: Units.h:79
std::shared_ptr< const IChannel > pointer
Definition: IData.h:19
void operator()(IWire::pointer iwire)
Definition: AnodePlane.cxx:88
T y(const T &val)
Definition: D3Vector.h:56
boost::error_info< struct tag_errmsg, std::string > errmsg
Definition: Exceptions.h:54
struct vector vector
STL namespace.
void put(Configuration &cfg, const std::string &dotpath, const T &val)
Put value in configuration at the dotted path.
SimpleChannel * operator()(int chid)
Definition: AnodePlane.cxx:102
cfg
Definition: dbjson.py:29
std::vector< pointer > vector
Definition: IData.h:21
const WirePlaneLayer_t iplane2layer[3]
Definition: WirePlaneId.h:14
static QStrList * l
Definition: config.cpp:1044
std::vector< pointer > vector
Vector of shared pointers.
Definition: IComponent.h:36
def configure(cfg)
Definition: cuda.py:34
std::shared_ptr< Interface > pointer
Definition: Interface.h:16
JAVACC_STRING_TYPE String
Definition: JavaCC.h:22
#define THROW(e)
Definition: Exceptions.h:25
std::vector< int > faces
Definition: WireSchema.h:41
double distance(double x1, double y1, double z1, double x2, double y2, double z2)
static int max(int a, int b)
def wire_plane_id(plane, face, apa)
Definition: schema.py:236
logptr_t logger(std::string name)
Definition: Logging.cxx:71
Thrown when a wrong value has been encountered.
Definition: Exceptions.h:37
static const double mm
Definition: Units.h:73
WIRECELL_FACTORY(AnodePlane, WireCell::Gen::AnodePlane, WireCell::IAnodePlane, WireCell::IConfigurable) using namespace WireCell
Definition: Main.h:22
T min(sqlite3 *const db, std::string const &table_name, std::string const &column_name)
Definition: statistics.h:55
const void * ptr(const T *p)
Definition: format.h:3138
T dot(const D3Vector &rhs) const
Return the dot product of this vector and the other.
Definition: D3Vector.h:80
Pimpos pimpos(nwires, min_wire_pitch, max_wire_pitch)
Pitch-Impact-Position.
Definition: Pimpos.h:36
def center(depos, point)
Definition: depos.py:117
Json::Value Configuration
Definition: Configuration.h:50
const GenericPointer< typename T::ValueType > & pointer
Definition: pointer.h:1124
T z(const T &val)
Definition: D3Vector.h:57
std::unordered_map< int, SimpleChannel * > chid2sch
Definition: AnodePlane.cxx:85
std::string resolve(const std::string &filename)
Definition: Persist.cxx:99
const Ray & bounds() const
Return the ray representing the bounds.
Definition: BoundingBox.h:36
int index() const
Layer as index number (0,1 or 2). -1 if unknown.
Definition: WirePlaneId.cxx:34
const int default_nimpacts
Definition: AnodePlane.cxx:36