Step 1. Setting up a release directory for running the software

All alignment related code is combined in the 'Alignment' project. For running the TAlignment algorithm, the following packages are most relevant:

  • *Alignment/Escher contains the option files for running alignment jobs
  • Alignment/TAlignment contains the Tracker Alignment code
  • *Alignment/AlignmentDBVisualisationTool
  • *Alignment/AlignmentMonitoring
  • *Alignment/AlignTrTools

To set up a directory with the current latest version of the software do

   shell> LbLogin -c x86_64-slc6-gcc48-opt
   shell> lb-dev Alignment vXrY
   shell> cd ./AlignmentDev_vXrY

Depending on the status of the development, it may be needed to download the latest version of some packages. For a list of the packages to download, it's advised to look at the tag collector.

To compile with CMake, simply

   shell> make configure
   shell> make install

Whenever a new package is added to the project directory, make configure needs to be called before compiling with make install. If a package is removed, make purge is needed before re-configuring and compiling the project.

Step 2. Running an alignment job

Once the release is setup, in whatever directory is possible to run an alignment job with the following command:

   shell > ./<AlignmentProjectDirectory>/run gaudirun.py <optionsfile> <datafile>

For example, a job on a 2012 sample, can be run with

   shell > ./<AlignmentProjectDirectory>/run bash --norc
   shell > gaudirun.py $ESCHEROPTS/Escher-AlignHltD0.py $ESCHEROPTS/COLLISION12-Tests.py

(in order for this one to work a line containing "MaxNTHoles" has to be removed from TAlignement/python/TAlignment/TrackSelections.py)

This will essentially just run the reconstruction sequence starting from the raw data, accumulate the information relevant for alignment, and finally compute new constants and dump them to xml file in finalize.

If you run with the option --printsequence it wil show the sequence of algorithms that is run. Note the GaudiSequencer/AlignSequence sequence which contains the alignment specific algorithms, in particular

  • AlignMonitorSeq is a sequence that runs the same algorithms as the standard Brunel monitoring sequence and a few more. What is special about it is that if you run with the 'gaudiiter.py' script (see below), it actually clones the sequence such that you get monitoring histograms both for the first and the last iteration.
  • AlignAlgorithm/Alignment is the TAlignment algorithm: it takes as input a list of tracks and eventually vertices and accumulates the 'chisquare-derivatives' that are the input to the alignment update.
  • GaudiSequencer/WriteCondSeq contains the algorithms that write the xml in the 'finalize' stage of your gaudi job

To make it possible to run iterations or split your event processing over the different cores of a machine, some scrits are setup that replace gaudirun.py. They can be found in Escher/scripts:

* gaudiiter.py -n [number of iterations] -e [number of events] runs n iterations of the alignment within a single gaudiapplication. The alignment updates are triggered at the end of each iteration by a gaudi 'incident'.

* gaudipar.py -e [number of events] -p [number of cores] runs (like gaudirun.py) a single iteration. The event processing is split over multiple cores in a rather simple way: it runs several m (delfault 8) applications n paralel over the same input data. Each application has a fiter that selecs 1 out of every m events. After the event processing is finished, the alignment derivatives from the m processes are added up and the alignment constants are updated.

* gaudipariter.py -n [number of iterations] -e [number of events] -p [number of cores] runs several iterations splitting the processing over different cores. It actually just creates subdirectories for each iteration, runs gaudipar.py in that subdirectory, creates a new databases when the job is finished and uses that database as input to the next iteration.

The alignment database used as input can be specified in a job option file, but if you use gaudipariter.py then that is not a great idea, since it would simply run the same alignment n times. Therefore, to specify an input database use the -d option. You can specify multiple input databases. For example, to run the alignment of all tracking detectors on a recent 2012 run starting from the v2.3 database, use:

  shell> gaudipariter.py -e 50000 -n 10 -p 8\
           -d /afs/cern.ch/user/m/mamartin/public/AlignDB/v6.2series.db \
           $ESCHEROPTS/Escher-AlignHltD0.py \ 
           $ESCHEROPTS/COLLISION12-Tests.py \

For each iteration it creates a directory called Iter[number] which contains among others a file 'Alignment.db' with the output alignment database. It also contains a file histograms.root which contains all histograms.

The most relevant histograms for alignment can be plot with the AlignmentMonitoring package. Since it is not yet in the released software, one needs to download it (getpack Alignment/AlignmentMonitoring head) and run the script

  shell > ./<AlignmentProjectDirectory>/run bash
  shell > python $ALIGNMENTMONITORINGROOT/examples/TrackingMonitoringPlots.py

At the moment, the list of histograms files to run the script on is hardcoded, therefore one needs to copy it to his working directory and modify it accordingly. (TODO: Set a default list of plots and use an option parser to set the input files)

Similarly, the alignlog.txt file from the alignment that stores information of all the output from the alignment job, can be analysed as a script and interactively by using a tool from Alignment/AlignmentDBVisualizationTool

  shell > ./<AlignmentProjectDirectory>/run bash
  shell > python <AlignmentProjectDirectory>/Alignment/AlignmentDBVisualizationTool/examples/TrackerAlignmentResults.py

Run from the nightlies

   shell > lb-dev Alignment HEAD --nightly lhcb-prerelease Today
   shell> make configure
   shell> make install
   shell >  ./run gaudirun.py <opts> <data>

Snapshotting ONLINE database

   shell > source /sw/oracle/set_oraenv.sh
   shell > export CORAL_DBLOOKUP_PATH=/group/online/conddbserver/
   shell > export CORAL_AUTH_PATH=/group/online/conddbserver
   shell > SetupProject LHCb
   shell > CondDBAdmin_MakeSnapshot.py -s 2015-01-01UTC --options /cvmfs/lhcb.cern.ch/lib/lhcb/DBASE/Det/SQLDDDB/v7r10/options/SQLDDDB-Oracle.py ONLINE sqlite_file:ONLINE-2015.db/ONLINE

You should then find a snapshot in a file called ONLINE-2015.db

Step 3. Detailed configuration

The TAlignment framework is configured using the TAlignment configurable. The name is historical: the original application was for the alignment of OT and IT. (The functionality was never limited to these systems though.) The easiest way to use the software is to follow examples. There are several example job option files in the $ESCHERROOT/options directory. In the following we will try to explain in a bit more detail how the most important parts of the confuguration actually work. Together with the Escher examples, this should allow anybody to configure the alignment algorithm.

The most important fields of the TAlignment configurable are:

  • TrackLocation: the name of the eventstore container with tracks used for alignment
  • ElementsToAlign: a list of strings defining the alignment degrees of freedom
  • Constraints: the list of lagrange constraints
  • SurveyConstraints: the list of survey (of 'chisquare') constraints
  • WriteCondSubDetList: a list with the subdetector names for which xml files must be produced (usually you want this to match the degrees of freedom)

Specifying alignment degrees of freedom

Obviously, the most important part of the configuration is the specification of the alignment parameters. In the context of the alignment software we call the detector elements that we align for 'alignable objects' or simply 'alignables'. Each alignable represents a single detector element or a group of detector elements. Each alignable is calibrated for maximum 6 parameters namely, the 3 translations labelled Tx, Ty and Tz and three rotations labelled Rx, Ry and Rz (for rotations aound the X, Y and Z axis respectively). It is currently not possible to align for other degrees of freedom (also called 'deformations'), but this functionality can be added in the future.

Each alignable is configured with a string, that consists of two parts separated by a colon. The first part of the string specifies the detector element; the second part the parameters (Tx,Ty,Tz,Rx,Ry,Rz). The single string is then appended to the ElementsToAlign field of the TAlignment configurable. For example to align the right half of the Velo in all its degrees of freedom, one would do:

     from Configurables import TAlignment
     velorighthalf = "/dd/Structure/LHCb/BeforeMagnetRegion/Velo/VeloRight : Tx Ty Tz Rx Ry Rz"
     TAlignment().ElementsToAlign = [ velorighthalf ]

The strings between the parameters can be omitted, so you could use "TxTyTzRxRyRz" for the last part. (Note however, the inconsistency with the lagrange constarints below: in that case the spaces are essential. So, it is better to leave them in for now.)

To simplify the configuration a syntax has been developed that makes use of patterns: rather than specifying each alignable explicitly one uses a 'regular expression'. For example, to align for all velo modules in Tx and Ty, one could do

     velosensors = "/dd/Structure/LHCb/BeforeMagnetRegion/Velo/Velo(Right|Left)/Module.{1,2} : Tx Ty"
     TAlignment().ElementsToAlign = [ velosensors ]

The regular expressions work like the standard Perl (?) regular expressions. We use boost_regex for this. There is one very important caveat here: For now you need to specify every slash ('/') in the regular expression. (You could not specify the Velo modules by using e.g. ".*/Velo.*/Module.{2}".). The reason for this is performance: Rather than matching all elements in LHCb against the string, the string is matched 'level-by-level': We first find /dd/Structure/LHCb/BeforeMagnetRegion/Velo and then loop over all its daughters to find those that match "Velo(Right|Left)", etc. without this trick, we would need to decode the entire LHCb geometry in the initialization of the alignment.

We have now shown how to create alignables that correspond to a single detector element. All detector elements can be used provided that they have an alignment condition. (That holds for most of them.) In particular for the OT, it is important to specify alignables to correspond to a group of detector elements. For example, a physical OT module has two detector elements associated to it, one for the top and one for the bottom half. In the alignment we would like to treat these as a single object. Since the alignment parameters in the condition database are actually organized by elements, this will mean that the module constants appear twice in the database.

The geometry of the OT is also arranged such that (unfortunately) the hierarchy doesn't exactly correspond to the way the detector was built. In particular there is no concept of a C-frame in the OT geometry. To allow for the alignment of C-frames, we group all associated 'quarter' elements together. To 'update' the C-frame alignment, the constants of all these quarters are changed by the same amount in the alignment 'condition' in the database.

To specify a group of elements, we add prepend the strings introduced above with another one that represents the group name, the name of the alignable to which all elements belong. For example, to define the X translation of the first OT C-frame, one would use

   TAlignment().ElementsToAlign.append( "OT/T1X1UASide :  /dd/Structure/LHCb/AfterMagnetRegion/T/OT/T1/(X1|U)/Q(1|3) : Tx" )

Note that the regular expression has a different purpose here: it selects all elements that belong to the OT/T1X1UASide C-frame. To specify the alignment constants for all C-frames, you would still need to define each of them separately. (You cannot sue a regular expression for the name.)

Because this is clearly still too complicated, a python class Alignables was written that holds all expressions for the most commonly used alignables. E.g. to align all Velo modules and OT C-frames, you would do:

    from TAlignment.Alignables import Alignables 
    elements = Alignables()
    elements.VeloRModules("Tx Ty")
    elements.VeloPhiModules("Tx Ty")
    elements.OTCFrames("Tx Ty Tz")
    TAlignment().ElementsToAlign = list( elements )

You can find the available list in the $TALIGNMENTROOT/python/TAlignment/Alignables.py source code. If your favourite alignable isn't there, it should just be added.

By default all alignment constants are defined in the local frame of the detector element associated to the Alignable. For groups an average of the frames of the elements is taken. If for some reason you prefer all parameters to be defined in the global frame, you can modify the UseLocalFrame field of the TAlignment configureble:

   TAlignment().UseLocalFrame = False

Using Lagrange constraints

Lagrange constraints can used in any chisquare minimization problem to add 'exact' constraints. If we want to minimize the chisquare under the condition

\[ f(\alpha)=0\]

we add an additional term to the chisquare that looks like

\[\Delta\chi^2 = \lambda f(\alpha)\]

where$\lambda$ is the Lagrange parameter. By taking the derivatives to $\lambda$ and $\alpha$, you can see that if we add $\lambda$ as an additional parameter to the set of fit parameters, then the condition above is automatically satisfied if the chisquare is minimal.

In the alignment Lagrange constraints can be used to fix unconstrained or poorly constrained degrees of freedom. The Lagrange constraints are implemented in AlignConstraintTool for (linear combinations of) the following parameters:

Tx, Ty, Tz translation in x, y, z
Rx, Ry, Rz rotation about x, y, z
Szx, Szy shearing of x and y versus z, e.g. a transformation like delta-x = const * z
Szz, Sxx scaling in x or z, e.g. a transformation like =delta-z = const * z
SRz twist around z, so a transformation like delta-rot-z = const * z
PVx, PVy, PVz translation of average primary vertex
Each of these constraints is applied to a set of elements. By default that set is 'all' elements. For example, to fix the change in the average translation of all aligned objects, you can do
       # specify the list of constraints
       constraints = [ "Tx", "Ty", "Tz" ]
       TAlignment().Constraints = constraints

However, much more fine grained control can be obtained by specifying regular expressions that define the list of elements to which the constraint must be applied. For example, to fix only the average translation of all Velo-A, you can do

       constraints += [ "VeloA : .*?/VeloRight.*? : Tx Ty Tz"]

Note:

  • the string consists of 3 parts seperated by colons
  • the first part is the 'name' of the constraint. This name must be unique.
  • the second part is the regular expression. In this case it will select all alignable elements that contain '/VeloRight' in the name of any associated detector element.
  • the third part are the constraints. These must be seperated by a space.
In the log file the constraints will now appear as 'VeloATx', 'VeloATy', and 'VeloATz'.

The regular expression can also be replaced by the name of an alignable: this is useful if you want to constrain just a single element.

Some of the constraints have two modes of operation. By default the change of the linear combination of parameters is constraint to zero. However, by adding an extra argument 'total' you can also constrain the difference with respect to nominal of that combination of parameters. For example, the following

       constraints += [ "Velo : .*?/Velo(Right|Left).*? : Tx Ty Tz : total"]

will constrain the average position of the two velo halves exactly at the origin. If you replace 'total' by 'delta' you get the default behaviour: in that case the average position of the velo is not constraint to zero, but only the change in the average position (with respect to the input alignment) is fixed to zero.

Using chisquare ('survey') constraints

Survey constraints can be added as penalty terms to the chisquare. The penalty term is the square of the difference between an alignment parameter and a reference value divided by an uncertainty in the reference value:

\[\Delta\chi^2 = \left( \frac{ \alpha_{ref} - \alpha } { \sigma(\alpha_{ref}) } \right)^2 \]

Ideally, one would use a certain version of the geometry ('the surveyed geometry') as input to the alignment. In practise, this is very complicated. First, in the LHCb software framework one has access to only two geometries, namely 'ideal' and 'aligned' where the latter uses a particular set of alignment conditions (determined by the event time, see also below.) To run the alignment one will hardly ever start from the survey, except maybe the very first time we align the detector. Therefore, the survey geometry is lost for anything but the first alignment: one cannot access simultanesouly the 'current' geometry and the 'somewhere-in-the-past-surveyed' geometry.

Second, to make use of the survey the uncertainties need to make sense and correlations need to be taken into account. For example, the position of modules within an OT C-frame is known within 200 micron, yet the position of the C-frame itself has an uncertainty of 1mm. To represent that information consistently in a covariance matrix is hard.

Finally, a generic problem with such chisquare constraints is that their importance reduces if the number of tracks increases. Since a single track already gives information about the position of an element with an accuracy that comes close to the hit resolution, the survey input is very soon irrelevant: The survey simply cannot pull the alignment sufficiently.

Consequently, survey constraints are of limited value in tracking alignment. Therefore, in what follows a pragmatic approach has been taken, with as the most important limitation that no correlations can be specified. (Think about what this means: in the example above the OT module survey cannot be used.) To still allow for more precise 'local' than 'global' information, all constraints are specified in the local frame of the alignment element, so with respect to its mother. That wouldn't help if it could not be combined with another powerful feature of the alignment sofware: one can align the detector at different levels of granularity simultaneously.

Survey constraints can be specified in two ways. First, we can hardcode the 6 parameters with errors as follows:

       surveyconstraints = [ ""OT/T3X1UASide : -1.52 -1.25  0.0  0.0 0.0 0.0 : 0.5 0.5 0.5 0.0001 0.0001 0.0001"]
       TAlignment().SurveyConstraints = surveyconstraints

Like for the Lagrange constraints, the colon act as a token. Unlike the Lagrange constraints, the first string is not the name of the constraint, but the name of the element. The first set of 6 numbers are the parameters for the 6 degrees of freedom. The second set of 6 numbers are the uncertainties. (So, in this case the position coordinates have an uncertainty of 0.5 mm, while the rotation uncertainties are 0.1 mrad.)

A regular expression can be used to add constraints for more than one element at a time. For example, to fix all OT modules to their local nominal position, you can do

       surveyconstraints = [ "/.*?/OT/.*?/M. : 0 0 0 0 0 0 : 0.1 0.1 0.1 0.0001 0.0001 0.0001" ]

This constraint means: fix the position of a module to its nominal value in the local frame with an uncertainty of 100 micron.

Now, you may wonder why this is useful. Consider the following scenario: We want to align the modules within a C-frame in X. However, at the same time we want to align the C-frame itself as a global structure in X and Y. Naively, we could just try the following:

      elements.OTCFrames("TxTy")
      elements.OTModules("Tx")

However, we clearly have created a problem now: There is a redundancy in the alignment degrees of freedom because the C-frame movement is just an average movement of the modules. The solution is to add a chisquare term for the position of the modules inside the C-frame:

      elements.OTCFrames("TxTy")
      elements.OTModules("Tx"
      surveyconstraints = [ "/.*?/OT/.*?/M. : 0 0 0 0 0 0 : 0.1 0.1 0.1 0.0001 0.0001 0.0001" ]

The uncertainty of 0.1 mm in the constraints for the modules is large enough that it will not really affect the module position itself: modules can still move freely inside the C-frame. However, what will effectively happen now is that the C-frame is positioned such that the average deviation of the modules from their nominal geometry is minimal. The alignment problem is perfectly constrained despite the redundancy in parameter. So, chisquare constraints allow to align the detector at different levels of granularity simultaneously.

More useful than specifying constraints individually as above is to use a 'survey xml' as the reference point. For this, you can simply add the path to the file to the list of constraints

      surveyconstraints += ["myfile.xml"]

The matching to the alignable element is performed by matching the name of the condition. The uncertainties assigned to the parameters can only be specified by talking directly to the AlignChisquareConstraintTool. For example, the uncertainties of all TT conditions are set with:

        from Configurables import Al__AlignChisqConstraintTool as AlignChisqConstraintTool
        AlignChisqConstraintTool().XmlUncertainties += [ 
           "TTSystem             : 0.5 0.5 0.5 0.001 0.001 0.001",
           "TT.                  : 0.1 0.1 0.1 0.0005 0.0005 0.0005",
           "TT..Layer            : 0.1 0.1 0.1 0.0005 0.0005 0.0005",
           "TT..Layer.Side       : 0.1 0.1 0.1 0.0005 0.0005 0.0005",
           "TT..LayerR.Module.*? : 0.1 0.1 0.1 0.0005 0.0005 0.0005"]

(The TT..LayerSide does not exist as a condition, but it exists as an Alignable.)

To simplifiy the use of all of this a python class TAlignment/SurveyConstraints was written. It is already properly configured to use the best known survey for TT, IT and OT. (Velo, muon should be added soon.) If you do

      from TAlignment.SurveyConstraints import *
      surveyconstraints = SurveyConstraints()
      surveyconstraints.All()
      TAlignment().SurveyConstraints = list( surveyconstraints )

you'll get the survey constraints for every parameter that you align for. (Watch for warning in the log file: if the survey information is missing, it will actually tell you.) For IT and TT it uses the survey xml, which is collected in the Alignment/TAlignment/surveyxml directory.

Using the result from a different dataset as a constraint

You may have seen that your alignment spits out a file called 'alignderivatives.dat'. In this file the accumulated chisquare derivatives are persisted. You can take this file feed it to the 'AlignUpdateTool' and it will recompute the alignment exactly as it would if you would process the events first.

It is now possible to use the alignment result obtained with a different as constraint. This is for example useful if you want to combine information from field-off and field-on data. If 'alignderivativesfieldoff.dat' is the file that you from the last iteration of your magnet-off alignment, you simply 'add' this file as input to your magnet on alignment:

   AlignAlgorithm("Alignment").InputDataFiles = [ 'alignderivativesfieldoff.dat' ]

When the input file is added to information that from the evetns that you process, it is corrected for intermediate alignments. That means that (to first order) it is not necessary that you create the alignderivativesfieldoff.dat file with the same input alignment as that you process your new job with. (Without this feature you would need to rerun the field-off alignment for every iteration as well.)

There is one important caveat: you must be aligning for the same 'alignables' in your two jobs.

Other initialization issues

The derivatives are computed with respect to a certain version of the geometry. The Gaudi framework (more precisely the UpdateManagerSvc) uses the geometry corresponding to a particular time. The leads to two different problems.

First, the UpdateManagerSvc by default initializes the geometry using the current time. That could mean for example that if the velo is 'now' open (because we are not taking data), but if it was closed at the time of running, then the alignment derivatives will be computed with respect to an 'open' velo. This problem can be partially solved by specifying the time used at initialization:

     # specify the time of the 'first' event in ns
     EventClockSvc().InitialTime = 1260350949785664000

Second, during event processing the UpdateMnagerSvc will update the geometry according to the event time. Consequently, it could happen that the geometry changes while you are processing evens. The alignment software is currently not protected against such geometry changes.

Examples

Aligning IT boxes, layers and ladders simultaneously using survey

   # choose the alignable degrees of freedom
   elements = Alignables()
   elements.ITBoxes("Tx Ty Rz")
   elements.ITLayers("Tx")
   elements.ITLadders("Tx")
   # now load the survey
   surveyconstraints = SurveyConstraints()
   surveyconstraints.IT()
   # remember to 'cast' to a list when configuraing TAlignment:
   TAlignment().ElementsToAlign   = list(elements)
   TAlignment().SurveyConstraints = list(surveyconstraints)

Fixing Velo modules in a Velo half with exactly zero shearing

   # align modules in Tx and Ty
   elements.VeloModules("Tx Ty")
   # align velo halves position and rotations. the rotations will 'absorb' the module shearing.
   elements.VeloHalves("Rx Ry Tx Ty")

   # constrain the total xz and yz sheering of the modules to be exactly zero
   constraints.append( "VeloRightModulesShearing : .*?VeloRight/Module.{1,2} : Sxz Syz : total" ) 
   constraints.append( "VeloLeftModulesShearing  : .*?VeloLeft/Module.{1,2} : Sxz Syz : total" ) 

   # now also constrain the _change_ in the position of one of the two velo halves.
   # constraints.append( "VeloGlobal : .*?VeloRight : Tx Ty")

   # you could also fix the average fo the two halves
   # constraints.append( "VeloGlobal : .*?Velo(Right|Left) : Tx Ty")

   # or you could fix the _change_ in the position of the primary vertex
   constraints.append( "PVx PVy")

Fixing Velo modules in a Velo half using the nominal geometry as 'survey'

      elements.VeloModules("Tx Ty")
   elements.VeloHalves("Rx Ry Tx Ty")

   # pull the module positions to nominal (e.g. around the z-axis in
   # the local frame). this will effectively absorb the shearing into
   # the rotation of the parent half:
   surveyconstraints.append(".*?Velo(Left|Right)/Module.{1,2} : Tx : 0 +/- 0.02")
   surveyconstraints.append(".*?Velo(Left|Right)/Module.{1,2} : Ty : 0 +/- 0.02")

   # use anyof the methods above to fix (one of)  the half frames in space

-- WouterHulsbergen - 17-Dec-2009</verbatim>

Edit | Attach | Watch | Print version | History: r29 < r28 < r27 < r26 < r25 | Backlinks | Raw View | WYSIWYG | More topic actions
Topic revision: r29 - 2017-05-08 - LuciaGrillo
 
    • Cern Search Icon Cern Search
    • TWiki Search Icon TWiki Search
    • Google Search Icon Google Search

    LHCb All webs login

This site is powered by the TWiki collaboration platform Powered by PerlCopyright & 2008-2019 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback