next up previous contents
Next: UgliGeometry Up: The MINOS Off-line Software Previous: Conventions   Contents



Last significant change: 2004/03/11

plex$\cdot$us (plek'sus) n. pl. -us or -us$\cdot$es. Anat. A structure in the form of a network, esp. of nerves, blood vessels, or lymphatics: the solar plexus. [$<$ Lat., p.p. of plectere, to plait.]


The Plex package provides services for managing the logical connection between elements of the detector above the physical level. Thus it is not concerned with the placement of physical scintillator strips and steel sheets in 3-space, but instead is responsible for providing the navigation from strips to fibers to pixel spots to pixels (anodes) to photomultiplier tube and so forth. It allows reverse navigation as well (e.g. one-to-many as well as many-to-one).

A connection map representing one of the detectors is valid for a particular time and may evolve as components are added or re-configured (e.g. to correct a mis-cabling). Thus instantiating a plexus must involve constructing it for a particular detector at a particular time in its existence - a plexus will be constructed that has a time range that covers the requested time. The underlying mapping information will come from the database.

Figure 5.1:
Figure 5.2:

Diagram 5.1 is a schematic sketch of some of the detector components and how they relate. Figure 5.2 is an illustrative (i.e., non-authoritative) representation of the same concepts as seen from a UML5.1perspective.

Creating a plexus

The user does not work directly with a Plexus object, but instead works through a PlexHandle. These PlexHandles are lightweight proxy objects that do little work of any substance. During the creation of a PlexHandle a loan pool of real Plexus objects searched for an appropriate object; if no match exists one will be created. The PlexHandle then forwards member function calls to the correct Plexus. This allows the PlexHandle to share the larger underlying collection of information and it lowers the overhead of the creation and deletion of PlexHandles.

This approach also allows the PlexHandles to be created on the stack and thus avoids a common source of memory leaks. The lookup procedure is so fast that users should not be concerned with (or even attempt) to retain a handle, but rather should construct one from a VldContext whenever one is needed.

#include "RawData/RawRecord.h"
#include "Plex/PlexHandle.h"

  // somehow acquire a raw record, perhaps:
  RawRecord *rawrec = (RawRecord*) (mom->GetFragment("RawRecord"));

  // retrieve a copy of the VldContext object identifying the configuration
  VldContext vldc = rawrec->GetRawHeader()->GetVldContext();

  // create a PlexHandle (on the stack)
  PlexHandle ph(vldc)

  // work with PlexHandle and it gets automagically destroyed
  // when the code block ends, but the next invocation is
  // likely to acquire the same underlying Plexus without
  // significant overhead.

Configuring the Plex

The behaviour of the plex package can be modified by manipulating the configuration of the PlexLoanPool. To change a parameter from within C++ code one writes code like:
   // PlexLoanPool is a CfgConfigurable 
   PlexLoanPool* plpool  = PlexLoanPool::Instance();

On startup the interface checks the environmental variable ENV_PLEX which can contain a semi-colon separated list of configuration requests, e.g.:

   setenv ENV_PLEX "MaxUnref=2; Cache='/tmp/myplex.cache'; CacheWrite=0"
One can also show the current status of the PlexLoanPool using:
which gives something like:
   "PlexLoanPool Configuration", 3 entries. keys unlocked, values unlocked
       Cache = ''
       CacheWrite = 1
       MaxUnref = 1
    --- Shared PlexLoanPool ---
    Plexus has 1 references
            2001-07-24 00:00:04.000000000Z
            2001-08-06 00:00:00.000000000Z
            from source: , From Database
    --- Private PlexLoanPool ---
   End of PlexLoanPool

Managing shared plexii

As described in Section 5.2 the user normally interfaces with the package via the class PlexHandle. This allows the plex package to maintain a pool of Plexus objects to which handles can easily be attached as needed. Users might need to trade off memory requirements of holding multiple plexii vs. the time requirement of repeatedly building one for the pool. Users can configure the number of unreferenced plexii that are retained by the pool for shared use via the MaxUnref config value.

Caching a copy of the loan pool

During development when prototyping code in a rapid cycle the overhead of reconstructing a plex every time the job is run can be an annoyance. Some time can be save, of order $\sim40$s of $\sim70$s, by writing out the plex as a root file and reading it back rather than pulling the data from the database and building it from scratch. This approach can also be dangerous in that it bypasses the database and thus heavy reliance on it can lead to the use of outdated constants. It also has no protection against multiple jobs trying to read and write the file simultaneously.

Nevertheless, it is sometime useful. This behavour can be enabled by setting the Cache config variable to the name of the root file to use. One then runs the job once to build the necessary plex from the database and the job will write the file upon teardown of the PlexLoanPool at job end. Subsequent jobs will then read the cached file and can avoid the re-writing overhead by setting the CacheWrite config value to zero.


The most frequently encountered identifier is a PlexStripEndId. This identifies a logical scintillator strip-end; it encapsulates identifiers for the physical detector, plane, strip-in-plane it refers to. It also, when meaningful, identifies which end of the strip in the case of two-sided readout. The components can be extracted via Get methods. It derives from PlexPlaneId, so some of the Get methods are implemented there. The most common comparisons to other PlexStripEndIds are available via methods in the class:

#include "MessageService/MsgService.h"
#include "Plex/PlexStripEndId.h"

  PlexStripEndId seid = [...]
  PlexStripEndId another = [...]

  // decompose PlexStripEndId into components
  DetectorType::Detector_t detector = seid.GetDetector();
  PlaneView::PlaneView_t   view     = seid.GetPlaneView();
  UShort_t                 plane    = seid.GetPlane();
  UShort_t                 strip    = seid.GetStrip();
  StripEnd::StripEnd_t     end      = seid.GetEnd();

  // seid.Print() might yield "[   Far|   2 Ut|123|*E]"
  // for the far detector, plane 2 (U view, Total coverage), 
  // strip 123, whole strip, east end.

  // these tests should output exactly one message

  if ( ! seid.IsSameStrip(another) )
     MSG("XXX",Msg::kInfo) << " not the same (detector,plane,strip)" << endl;

  if ( seid.IsSameStripEnd(another) ) 
     MSG("XXX",Msg::kInfo) << " same (detector,plane,strip,end)" << endl;

  if ( seid.IsOppositeStripEnd(another) ) 
     MSG("XXX",Msg::kInfo) << " same (detector,plane,strip) && different (end)" << endl;


The PlexStripEndId to assign to a readout digitization is not unique because of optical (far detector) or electrical (near detector spectrometer) summing; this effect is colloquially, but mistakenly, referred to as ``multiplexing''. The PlexSEIdAltL is a list of PlexStripEndId alternatives. Starting with a RawChannelId the plexus can construct a list and hand it back to the user for refinement. To facilitate the refinement the PlexSEIdAltL manages the list with a weight associated with each alternative. Mechanisms for iterating over the list, setting the associated weights, removing entries and extracting the the PlexStripEndIds and weights are provided. The interpretation of the weight value is not specified - it could be $-\chi^2$, log-likelihood, etc. Any algorithm must be consistent in its usage; the only expectation is that large values are ``better''. There are a number of broad general categories of methods for the PlexSEIdAltL class: characterization, iteration, weight modification, addition, deletion.

An unexceptional list will normally have a consistent plane number and strip end enumeration and so GetPlane() and GetEnd() are generally well defined operations. It is conceivable that a miscabling could mix signals from different planes so that the mapping from RawChannelId to a list of PlexStripEndIds can't give a unique answer, but currently that isn't handled other than to note, as a printed warning, the exception.

The PlexSEIdAltL is a STL5.2 vector of PlexSEIdAltLItems each of which represent an alternative PlexStripEndId and associated fields. The associated data includes the PlexPixelSpotId (distinguishes pixel spots for the far and different pixels for the near spectrometer); the assigned weight; and when generated in conjunction with real digitization information, some calibration corrected quantities, such as photo-electrons, strip-to-strip corrected signal and times.

#include "Plex/PlexHandle.h"
#if !defined(__CINT__) || defined(__MAKECINT__)
#include "MessageService/MsgService.h"
CVSID("$Id: plexus.tex,v 1.7 2004/06/14 19:38:45 west Exp $");
#include <iostream>
using namespace std;
#define MSG(a,b) cout

void plex_tutor()
   Detector::Detector_t det = Detector::kFar;
   VldContext vldc(det,SimFlag::kData,VldTimeStamp());
   PlexHandle ph(vldc);

   PlexStripEndId seid(det,1,0,StripEnd::kEast);
   RawChannelId   rcid = ph.GetRawChannelId(seid);

   PlexSEIdAltL alt = ph.GetSEIdAltL(rcid);

   // loop over alternatives assigning increasing weights
   for (unsigned int i=0; i < alt.size(); ++i) {


   // retrieve values for the entry with the largest weight
   // the iterator cursor is left unchanged
   PlexStripEndId best_seid = alt.GetBestSEId();
   Float_t        best_wgt  = alt.GetBestWeight();
      << "Best " << best_seid << " weight " << best_wgt << endl;

   // Iteration using SetFirst()/Next(), SetLast()/Previous()
   // is now deprecated along with all the methods involving the
   // word "Current". PlexSEIdAltL is a STL vector and CINT has 
   // full access to its STL methods.

   // iterate forward, retrieve each value in turn
   while ( alt.IsValid() ) {  // while the cursor is still in range
      PlexStripEndId seid = alt.GetCurrentSEId();
      Float_t        wgt  = alt.GetCurrentWeight();
      MSG("test",Msg::kInfo) << seid << " had weight " << wgt << std::endl;

   // iterate backwards
   while ( alt.IsValid() ) {


The ordering of entries initially follows the order in which they were entered into the list. The list can be sorted by weight value but this must be via an explicit method call so as not to confuse iteration.

One can set all the weights to zero using ClearWeights(). The individual weight for the ``current'' position of the iterator cursor can be adjusted via SetCurrentWeight() or AddToCurrentWeight().

There is one method, AddStripEndId(), for adding to the list of strip-end, weight pairs but this is the purview of the plexus itself and isn't a normal operation for the user. The user of the list though may wish to delete selected entries and a variety of such facilities are provided:

Special Channels

In a number of cases (especially at CalDet) some electronics channels are used for readouts other than PMTs attached to scintillator strips. The mundane alternatives are pin diode and VA common mode readout; more specialized outputs include a PMT attached to the Flasher system; an ``or'' of the triggers; Cerenkov; Time-of-flight and mixed electronics timing fiducial markers.

The Plex can be queried by RawChannelId as to which ReadoutType (see Section [*]) is generating data on that channel. For readout types other than kScintStrip, kPinDiode and kVACommonMode this information is given to the Plex via the PLEXRAWCHANNELREADOUTTYPE table in the database, which should be kept up-to-date whenever special channels are rewired. The READOUTNAME column should start with the appropriate readout type name, but can have appended to it any useful descriptive text.

Since, for some of these special readouts, there might be multiple channels of this special type one can use the extra information in the READOUTNAME to select specific instances. Thus given:

 		RawChannelId 		READOUTNAME 		ReadoutType   

C:V--:0x00:1524 'FlashTrigPMT' kFlashTrigPMT
C:V--:0x00:05b0 'CerenkovAdc1' kCerenkov
C:V--:0x00:0590 'CerenkovAdc2' kCerenkov
C:V--:0x00:05d0 'CerenkovAdc3' kCerenkov
C:V--:0x00:1602 'TOFAdc1' kTOF
C:V--:0x00:1590 'TOFAdc2' kTOF
C:V--:0x00:15b0 'TOFAdc3' kTOF
one can look up a specific special channel via:
   RawChannelId rcid = ... // special channel...or not

   // get the readout type
   ReadoutType::Readout_t rtype = ph.GetReadoutType(rcid);
   if ( ReadoutType::ScintStrip   != rtype &&
        ReadoutType::PinDiode     != rtype &&
        ReadoutType::VACommonMode != rtype    ) {
      // not mundane, let's ask for more info
      std::string descript = ph.GetSpecialDescript(rcid);

   // look up the specific CerenkovAdc1 channel, if one is so labelled
   RawChannelId ckv1 = ph.GetSpecialChannelContains("CerenkovAdc1");

Far Detector Veto Shield

The construction of the far detector veto shield is an ad hoc afterthought. As a consequence it suffers from a lack of regularity and consistency. The veto shield only uses C and E modules; but which is used where and in what orientation forms no regular pattern. And while all normal planes, once installed, never change their orientation this is not true for the veto shield. Additional confusion arises because a numbering scheme was used in entering the strip-to-pixel mapping that lumps together modules in different orientations, in effect having non-planar ``planes''. Since a PlexPlaneId (and a PlexStripEndId which derives from it) are supposed to support the concept of asking for a PlaneView::PlaneView_t, this leads to ambiguity.

Figure 5.3: Definition of plane and strip numbering in the first section of the prototype veto shield. Red values are ``as entered'' into the Plex; Blue (and black) values are after renumbering so that individual modules are interpreted as independent planes. For individual modules the trailing letters indicate the module's type and orientation. The type can be one of: c, C, e, E; if the type is lowercase then the logical strip numbers count in the opposite direction as the construction/mapper numbering. For example, plane 528cN would have logical strips 0 to 19 counting east to west, but is placed so that mapper ``strip 1'' (indicated by the asterisk) is on the west side. The N and S indicate whether the F1 connector is on the North or South side.

Figure 5.4: Definition of plane and strip numbering in the second (3rd & 4th) section of the prototype veto shield. Each module is entered as a separate plane.

next up previous contents
Next: UgliGeometry Up: The MINOS Off-line Software Previous: Conventions   Contents
Minos software 2019-07-07