EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
KalmanFilter.h
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file KalmanFilter.h
1 //
2 // AYK (ayk@bnl.gov)
3 //
4 // Kalman filter class routines; ported from HERMES/OLYMPUS sources;
5 // cleaned up 2014/10/10;
6 //
7 
8 #include <map>
9 
10 #include <KfMatrix.h>
11 #include <KalmanNode.h>
12 
13 #ifndef _KALMAN_FILTER_
14 #define _KALMAN_FILTER_
15 
16 // Possible bits in Transport() call "mode" flag; in particular
17 // CPU-consuming derivatives are not always needed;
18 #define _CALCULATE_DERIVATIVES_ 0x0001
19 #define _CALCULATE_PROCESS_NOISE_ 0x0002
20 
21 // Possible failure bits in filter/smoother/chain return codes; NEVER change already
22 // defined bits; if ever add new bits, check (and possibly update) _FATAL_FAILURE_MASK_;
23 #define _TFUN_FAILURE_ 0x0001
24 #define _NFUN_FAILURE_ 0x0002
25 #define _XFUN_FAILURE_ 0x0004
26 #define _HFUN_FAILURE_ 0x0008
27 #define _DSINV_FAILURE_ 0x0010
28 #define _POSITIVITY_FIX_ 0x0020
29 #define _CHAIN_FAILURE_ 0x0040
30 // These 3 bits are at present not considered as fatal ones;
31 #define _NDF_FAILURE_ 0x0080
32 #define _STRUST_FAILURE_ 0x0100
33 #define _WORST_NODE_IMMUTABLE_ 0x0200
34 
35 // Certain bits mean fatal error of the whole chain; other ones
36 // are more or less harmless and (like NDF failure) just mean
37 // that track is probably of a low general quality;
38 #define _FATAL_FAILURE_MASK_ 0x005F
39 
40 // Possible 'mode' argument values for KalmanChain(); see code
41 // for further details;
42 #define _TRUST_SMOOTHER_FCN_ 0x0001
43 #define _TRUST_FILTER_FCN_ 0x0002
44 
45 #define _KF_RETURN_(ret, message, what) \
46  _RETURN_((ret), (mVerbosityLevel >= what ? message : 0))
47 
48 //#define _KF_RETURN_ERROR_ (ret, message) _KF_RETURN_(ret,message,KalmanFilter::Error)
49 //#define _KF_RETURN_WARNING_(ret, message) _KF_RETURN_(ret,message,KalmanFilter::Warning)
50 //#define _KF_RETURN_INFO_ (ret, message) _KF_RETURN_(ret,message,KalmanFilter::Info)
51 
52 // Looks ugly, but suffices to append extra bits in KalmanChain() return code;
53 #define _CHAIN_RETURN_(ret) return((ret)|mExtraReturnBits)
54 
55 // Cases with higher off-diagonal correlation coefficients are
56 // indeed very rare and in general suspicious;
57 #define _MAX_FIXABLE_POSITIVITY_SCREWUP_DEFAULT_ (1.20)
58 // Fix too high off-diagonal correlation coefficients to this value;
59 #define _POSITIVITY_CORRELATION_FIX_DEFAULT_ (0.99)
60 
61 // Keep this stuff until find a better way to detect situations with
62 // too small filtered residual covariance matrix RF[][];
63 #define _RF_CUTOFF_DEFAULT_ (1E-11)
64 
65 #define _FIRED_RPLANE_PREFIX_ "fired-node-min="
66 
67 // If worst hit has probability below this value (and depending on
68 // kalman_chain() "mode") declare hit as outlier;
69 // NB: 1-dim chi^2 tail integrals:
70 // >=1 sigma from 0: 0.3173
71 // >=2 sigma from 0: 0.0455
72 // >=3 sigma from 0: 0.0027
73 // >=4 sigma from 0: <0.0001
74 #define _MIN_SMOOTHER_CHI_SQUARE_CCDF_DEFAULT_ (0.001)
75 
76 // If probability of overall filter chi^2 is below this value
77 // (and depending on kalman_chain() "mode") try to remove yet
78 // another node;
79 #define _MIN_FILTER_CHI_SQUARE_CCDF_DEFAULT_ (0.003)
80 
81 class KalmanFilter {
82  public:
83  // The constructor & destructor; the latter one should be done properly
84  // at some point; in fact however I'm always using execution-long class,
85  // so it automatically gets cleaned up once application exits;
86  KalmanFilter(int sdim);
88 
89  // This does not really help, since I'd like to use them as loop variables;
90  // by all means never change: must be Forward=0 and Backward=1 therefore;
93 
94  void SetVerbosity(Verbosity verb) { mVerbosityLevel = verb; };
95 
96  void SetXmCalculationFlag(bool flag) { mXmCalculationFlag = flag; };
97 
98  // FIXME: seems to not be used in magnet-off mode;
99  void SetNodeGapMax (double value) { mNodeGapMax = value; };
100 
104 
105  void SetPositivityFixParameters(double maxFixablePositivityScrewup,
106  double positivityCorrelationFix) {
107  mMaxFixablePositivityScrewup = maxFixablePositivityScrewup;
108  mPositivityCorrelationFix = positivityCorrelationFix;
109  };
110 
112 
113  // Default way to allocate new nodes and simplified wrapper;
114  KalmanNode *AddNode(const char *name, double z, int mdim,
115  const bool nonLinearTransportFlags[2]);
116  KalmanNode *AddNodeWrapper(const char *name, const char *format, double z, int mdim);
117 
118  int Configure(const StringList *config/*, const char *format*/);
119 
121  unsigned FullChain (KalmanNode *start, KalmanNode *end, KalmanFilter::Direction fb, int mode);
122 
123  virtual void BuildNodeList();
124  void ResetFiredFlags();
125  void HackGroupHitCountLimit(unsigned min);
127 
128  //unsigned GetNodeCount() const { return mNodeCount; };
129 
130  KalmanNode *GetHead() const { return mHead; };
131  KalmanNode *GetTail() const { return mTail; };
132 
133  int GetFilterNdf() const { return mNdf; };
134  double GetFilterChiSquare() const { return mFilterChiSquare; };
135  double GetFilterChiSquareCCDF() const { return mFilterChiSquareCCDF; };
136 
137 #if _OLD_
138  // It looks like users should have no access to this routine (call FullChain() instead);
139 #endif
140  int SmootherPass();
141 
142  void SetExtraNdfCount(int count) { mExtraNdfCount = count; };
143 
144  protected:
145  std::multimap<double, KalmanNode*> mKalmanNodePool;
146 
147  // NB: there is a certain trick involved here -> see respective TrKalmanFilter call;
148  virtual KalmanNode *AllocateNode() { return new KalmanNode();};
149 
150  virtual bool NeedNonLinearTransport(double z) const { return true; };
151 
152  private:
153  //
154  // Generic variables;
155  //
156 
157  // State vector dimension: 4 (no field) or 5 (with magnetic field);
158  int sDim;
159 
160  // 0 - so Verbosity::Never - per default (no printouts);
162 
163  // Usually don't need xm[] vector calculation; FIXME: there are other parts
164  // which not always needed -> optimize and use similar flags;
166 
167  // Several node groups can be declared; smoother should take
168  // care to guarantee that when removing outliers min number of
169  // nodes in each group is granted;
171  // Total number of nodes; useless variable in fact;
172  //unsigned mNodeCount;
173 
174  // Max allowed gap between two neighboring Kalman nodes; extra nodes
175  // are added automatically (CHECK!) during initialization stage (if needed);
176  double mNodeGapMax;
177 
178  // Configuration variables which define filter-smoother chain behavior;
180 
181  //
182  // Buffer matrices; SU[][] will be a unity matrix of respective dimension;
183  //
184 
185  KfMatrix *SMTX; //[SDIM][SDIM]
186  KfVector *SVEC, *QVEC; //[SDIM]
187  KfMatrix *SU; //[SDIM][SDIM]
188 
189  // Well, it would probably make sense to use C++ STL container class here;
190  // however the overhead to port 2-way navigation may be too big; in particular
191  // will have to replace KalmanNode pointers by set (or list) iterators;
192  // so leave things as they are?;
194 
195  // Well, say a strict cov.matrix setting at the head node can be seen as
196  // an extra measurement; clearly the core KF code can not judge on that;
197  // as of 2015/11/05 this value will actually be added to mNdf (and therefore
198  // accounted not only in CCDF calculation, but in GetFilterNdf() call);
200 
201  // Parameters which guide KF core code how to handle symmetric matrix
202  // numeric instabilities; FIXME: need to look into this more carefully;
207 
208  //
209  // Filter working variables;
210  //
211 
212  // Make smoother aware about what was the last successful filter pass direction;
214  // Also save last working filter conditions (for the smoother);
216 
217  // Number of degrees of freedom for the current filter pass; NB: may indeed
218  // vary from event to event depending on the actual number of hits used;
219  int mNdf;
220  // chi^2 and complement of its cumulative distribution function for
221  // the given number of degrees of freedom; one value for the whole chain;
223 
224  //
225  // Smoother working variables;
226  //
227 
228  // Node with the worst smoother chi^2 after the last smoother pass;
230  // Same, but either not coupled to any node group or belonging
231  // to a node group where mFiredNodeNumMin is not reached yet;
233 
234  //
235  // Complete chain working variables;
236  //
237 
238  // Number of nodes rejected during filter-smoother pass;
240 
241  // If for instance smoother had a recoverable trouble, it is
242  // convenient to record it in some extra bitmask instead of returning
243  // non-zero code and keep track on it in KalmanChain(); these bits will
244  // be OR'ed with the rest of Kalman chain return code;
246 
247  //
248  // Private methods;
249  //
250 
251  // Service matrix calculation for the current filter pass;
252  unsigned Calculate_x0_FR_Q(KalmanNode *start, KalmanNode *end,
253  KalmanFilter::Direction fb, unsigned mode);
254 
255  // Core KF calculations;
257 
258  // chi^2, CCDF's, etc after the last pass;
259  int CalculateFilterStat();
260 
261  // H-projector matrix calculation; it would be more natural to put these
262  // functions as virtual into KalmanNode, but it does not work; think later;
263  virtual int CalculateHMatrix(KalmanNode *node) = 0;
264 
265  // Main transport routine; should be provided by user;
266  virtual int Transport(KalmanNode *from, KalmanFilter::Direction fb, unsigned mode) = 0;
267 
268  // Extra transport code (process noise and dE/dx losses calculation
269  // in case of tracking); may be empty I guess?;
270  virtual int TransportExtra(KalmanNode *from, KalmanFilter::Direction fb, unsigned mode) {
271  return 0;
272  };
273 };
274 
275 #endif