source: trunk/MagicSoft/Mars/mfbase/MF.cc@ 7764

Last change on this file since 7764 was 7601, checked in by tbretz, 19 years ago
*** empty log message ***
File size: 15.1 KB
Line 
1/* ======================================================================== *\
2!
3! *
4! * This file is part of MARS, the MAGIC Analysis and Reconstruction
5! * Software. It is distributed to you in the hope that it can be a useful
6! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
7! * It is distributed WITHOUT ANY WARRANTY.
8! *
9! * Permission to use, copy, modify and distribute this software and its
10! * documentation for any purpose is hereby granted without fee,
11! * provided that the above copyright notice appear in all copies and
12! * that both that copyright notice and this permission notice appear
13! * in supporting documentation. It is provided "as is" without express
14! * or implied warranty.
15! *
16!
17!
18! Author(s): Thomas Bretz 01/2002 <mailto:tbretz@astro.uni-wuerzburg.de>
19!
20! Copyright: MAGIC Software Development, 2000-2005
21!
22!
23\* ======================================================================== */
24
25/////////////////////////////////////////////////////////////////////////////
26//
27// MF
28//
29// With this filter you can filter in all variables from Mars parameter
30// containers.
31//
32// In the constructor you can give the filter rule, like
33// "MHillas.fLength < 15"
34// Where MHillas is the name of the parameter container in the parameter
35// list and fLength is the name of the data member which should be used
36// for the filter rule. If the name of the container is use specified
37// (MyHillas) the name to give would be:
38// "MyHillas.fLength < 15"
39//
40// Also more difficult rules are allowed, like:
41// "MHillas.fWidth<0.5 && MHillas.fLength<0.6"
42//
43// You can also use parantheses:
44// "MHillas.fSize>200 || (MHillas.fWidth<0.5 && MHillas.fLength<0.6)"
45//
46// If you want to use mathematic expressions (as defined in MDataChain)
47// you must encapsulate it in {}-parantheses, eg:
48// "{log10(MHillas.fSize)}>3"
49//
50// For more details on available functions and expressions see MDataChain!
51//
52// The allowed logigal conditionals are:
53// &&: logical and
54// ||: logical or
55// ^: exclusive or
56//
57// As conditional signs, for now, only:
58// <, >
59// are allowed.
60//
61// --------> '==' is NOT supported!
62//
63//
64// Warning: There is no priority rule build in. So better use parantheses
65// to get correct results. The rule is parsed/evaluated from the left
66// to the right, which means:
67//
68// "MHillas.fSize>200 || MHillas.fWidth<0.5 && MHillas.fLength<0.6"
69//
70// is parses as
71//
72// "(MHillas.fSize>200 || MHillas.fWidth<0.5) && MHillas.fLength<0.6"
73//
74//
75// FIXME: The possibility to use also complete filters is missing.
76// Maybe we can use gInterpreter->Calc("") for this.
77// gROOT->ProcessLineFast("line");
78//
79/////////////////////////////////////////////////////////////////////////////
80#include "MF.h"
81
82#include <ctype.h> // isalnum, ...
83#include <stdlib.h> // strtod, ...
84#include <fstream> // ofstream, ...
85
86#include <TMethodCall.h>
87
88#include "MParList.h"
89
90#include "MFilterList.h"
91#include "MFDataChain.h"
92#include "MFDataMember.h"
93
94#include "MLog.h"
95#include "MLogManip.h"
96
97ClassImp(MF);
98
99using namespace std;
100
101const TString MF::gsDefName = "MF";
102const TString MF::gsDefTitle = "Filter setup by a text-rule";
103
104// --------------------------------------------------------------------------
105//
106// Default Constructor. Don't use.
107//
108MF::MF() : fF(NULL), fAllowEmpty(kFALSE)
109{
110 fName = gsDefName.Data();
111 fTitle = gsDefTitle.Data();
112}
113
114// --------------------------------------------------------------------------
115//
116// Constructor. For the text describing the filter rule please see
117// the class description above.
118//
119MF::MF(const char *text, const char *name, const char *title) : fAllowEmpty(kFALSE)
120{
121 fName = name ? name : gsDefName.Data();
122 fTitle = title ? title : gsDefTitle.Data();
123
124 *fLog << inf << "Parsing filter rule... " << flush;
125 if (!(fF=ParseString(text, 1)))
126 {
127 *fLog << err << dbginf << "Parsing '" << text << "' failed." << endl;
128 return;
129 }
130
131 *fLog << inf << "found: ";
132 fF->Print();
133 *fLog << endl;
134}
135
136// --------------------------------------------------------------------------
137//
138// Destructor. Delete filters.
139//
140MF::~MF()
141{
142 if (fF)
143 delete fF;
144}
145
146// --------------------------------------------------------------------------
147//
148// Returns the number of alphanumeric characters (including '.')
149// in the given string
150//
151Int_t MF::IsAlNum(TString txt) const
152{
153 int l = txt.Length();
154 for (int i = 0; i<l; i++)
155 if (!isalnum(txt[i]) && txt[i]!='.' && txt[i]!=';')
156 return i;
157
158 return l;
159}
160
161MFilter *MF::ParseRule(TString &txt, MFilter *filter0, Int_t level) const
162{
163 // For backward compatibility
164 txt.ReplaceAll("{", "(");
165 txt.ReplaceAll("}", ")");
166
167 const Int_t fg = txt.First('>');
168 const Int_t lg = txt.First('>');
169 const Int_t fl = txt.First('<');
170 const Int_t ll = txt.First('<');
171
172 if (fg<0 && fl<0)
173 {
174 MFilter *f = (MFilter*)GetNewObject(txt, MFilter::Class());
175 if (!f)
176 {
177 *fLog << err << dbginf << "Syntax Error: '" << txt << "' is neither a MFilter nor conditional sign found." << endl;
178 return NULL;
179 }
180 // FIXME: Search object through list...?
181 txt = "";
182 return f;
183 }
184
185 if (fg>=0 && fl>=0)
186 {
187 *fLog << err << dbginf << "Syntax Error: Two coditional signs found in " << txt << endl;
188 *fLog << "Currently you have to enclose all conditions in parantheses, like: \"(x<y) && (z<5)\"" << endl;
189 return NULL;
190 }
191 if (fg!=lg || fl!=ll)
192 {
193 *fLog << err << dbginf << "Syntax Error: Coditional sign found twice " << txt << endl;
194 return NULL;
195 }
196
197 const Int_t cond = fg<0 ? fl : fg;
198
199 const TString rule1 = txt(0, cond);
200 const TString rule2 = txt(cond+1, txt.Length());
201
202 Int_t lvl = gLog.GetDebugLevel();
203 gLog.SetDebugLevel(1);
204 MFilter *f = new MFDataChain(rule1.Data(), txt[cond], rule2.Data());
205 f->SetName(Form("Chain%02d%c", level, txt[cond]));
206 gLog.SetDebugLevel(lvl);
207
208 txt = "";
209 return f;
210}
211// --------------------------------------------------------------------------
212//
213// Parse a text string. Returns a corresponding filter of filter list.
214//
215MFilter *MF::ParseString(TString txt, Int_t level)
216{
217 MFilter *filter0=NULL;
218
219 TString type;
220 int nlist = 0;
221
222 while (!txt.IsNull())
223 {
224 MFilter *newfilter = NULL;
225
226 txt = txt.Strip(TString::kBoth);
227
228 //*fLog << all << setw(level) << " " << "Text: " << level << " '" << txt << "'" << endl;
229
230 switch (txt[0])
231 {
232 case '(':
233 {
234 //
235 // Search for the corresponding parantheses
236 //
237 Int_t first=1;
238 for (int cnt=0; first<txt.Length(); first++)
239 {
240 if (txt[first]=='(')
241 cnt++;
242 if (txt[first]==')')
243 cnt--;
244
245 if (cnt==-1)
246 break;
247 }
248
249 if (first==txt.Length())
250 {
251 *fLog << err << dbginf << "Syntax Error: ')' missing." << endl;
252 if (filter0)
253 delete filter0;
254 return NULL;
255 }
256
257 //
258 // Make a copy of the 'interieur' and delete the substringä
259 // including the parantheses
260 //
261 TString sub = txt(1, first-1);
262 txt.Remove(0, first+1);
263
264 //
265 // Parse the substring
266 //
267 newfilter = ParseString(sub, level+1);
268 if (!newfilter)
269 {
270 *fLog << err << dbginf << "Parsing '" << sub << "' failed." << endl;
271 if (filter0)
272 delete filter0;
273 return NULL;
274 }
275 }
276 break;
277
278 case ')':
279 *fLog << err << dbginf << "Syntax Error: Too many ')'" << endl;
280 if (filter0)
281 delete filter0;
282 return NULL;
283
284
285 case '&':
286 case '|':
287 case '^':
288 if (filter0)
289 {
290 //
291 // Check for the type of the conditional
292 //
293 TString is = txt[0];
294 txt.Remove(0, 1);
295
296 if (is==txt[0] && is!='^')
297 {
298 is += txt[0];
299 txt.Remove(0, 1);
300 }
301
302 //
303 // If no filter is available or the available filter
304 // is of a different conditional we have to create a new
305 // filter list with the new conditional
306 //
307 if (!filter0->InheritsFrom(MFilterList::Class()) || type!=is)
308 {
309 MFilterList *list = new MFilterList(is);
310 list->SetName(Form("List_%s_%d", (const char*)is, 10*level+nlist++));
311
312 list->SetOwner();
313 list->AddToList(filter0);
314 filter0 = list;
315
316 type = is;
317 }
318 continue;
319 }
320
321 *fLog << err << dbginf << "Syntax Error: First argument of condition missing." << endl;
322 if (filter0)
323 delete filter0;
324 return NULL;
325
326 default:
327 newfilter = ParseRule(txt, filter0, level++);
328 if (!newfilter)
329 {
330 if (filter0)
331 delete filter0;
332 return NULL;
333 }
334 }
335
336 if (!filter0)
337 {
338 filter0 = newfilter;
339 continue;
340 }
341
342 if (!filter0->InheritsFrom(MFilterList::Class()))
343 continue;
344
345 ((MFilterList*)filter0)->AddToList(newfilter);
346 }
347
348 return filter0;
349}
350
351// --------------------------------------------------------------------------
352//
353// PreProcess all filters.
354//
355Int_t MF::PreProcess(MParList *plist)
356{
357 if (!fF)
358 {
359 if (IsAllowEmpty())
360 return kTRUE;
361
362 *fLog << err << dbginf << "No filter rule available." << endl;
363 return kFALSE;
364 }
365
366 fF->SetAccelerator(GetAccelerator());
367
368 if (!fF->CallPreProcess(plist))
369 {
370 *fLog << err << dbginf << "PreProcessing filters in ";
371 *fLog << fName << " failed." << endl;
372 return kFALSE;
373 }
374
375 return kTRUE;
376}
377
378// --------------------------------------------------------------------------
379//
380// Process all filters.
381//
382Int_t MF::Process()
383{
384 return fF ? fF->CallProcess() : kTRUE;
385}
386
387// --------------------------------------------------------------------------
388//
389// Postprocess all filters.
390//
391Int_t MF::PostProcess()
392{
393 return fF ? fF->CallPostProcess() : kTRUE;
394}
395
396// --------------------------------------------------------------------------
397//
398// Return the result of the filter rule. If no filter available the
399// condition is always true
400//
401Bool_t MF::IsExpressionTrue() const
402{
403 return fF ? fF->IsConditionTrue() : kTRUE;
404}
405
406// --------------------------------------------------------------------------
407//
408// Return a comma seperated list of all data members used in the chain.
409// This is mainly used in MTask::AddToBranchList
410//
411TString MF::GetDataMember() const
412{
413 return fF ? fF->GetDataMember() : (TString)"";
414}
415
416void MF::StreamPrimitive(ofstream &out) const
417{
418 out << " MF " << GetUniqueName();
419
420 if (!fF)
421 {
422 out << ";" << endl;
423 return;
424 }
425
426 out << "(\"" << fF->GetRule() << "\"";
427 if (fName!=gsDefName || fTitle!=gsDefTitle)
428 {
429 out << "(\"" << fName << "\"";
430 if (fTitle!=gsDefTitle)
431 out << ", \"" << fTitle << "\"";
432 }
433 out << ");" << endl;
434
435}
436
437// --------------------------------------------------------------------------
438//
439// Print the rule.
440// if now filter is set up '<n/a>' is printed.
441//
442void MF::Print(Option_t *opt) const
443{
444 *fLog << all << underline << GetDescriptor() << endl;
445 if (fF)
446 fF->Print();
447 else
448 *fLog << "<n/a>";
449 *fLog << endl << endl;
450}
451
452// --------------------------------------------------------------------------
453//
454// Check for corresponding entries in resource file and setup filters.
455//
456// Assuming your MF-filter is called (Set/GetName): MyFilter
457//
458// First you can setup whether the filter is inverted or not:
459// MyFilter.Inverted: yes, no <default=no>
460//
461// Now setup the condition, eg:
462// MyFilter.Condition: MHillas.fSize<1000
463// or
464// MyFilter.Condition: MHillas.fSize>500 && MHillas.fSize<10000
465//
466// If you want to use more difficult filters you can split the
467// condition into subcondistions. Subcondistions are identified
468// by {}-brackets. Avoid trailing 0's! For example:
469//
470// MyFilter.Condition: MHillas.fSize>500 && {0} && {1}
471// MyFilter.0: MHillas.fSize>1000
472// MyFilter.1: MHillas.fSize<10000
473//
474// The numbering must be continous and start with 0. You can use
475// a subcondition more than once. All {}-brackets are simply replaced
476// by the correspodning conditions. The rules how conditions can
477// be written can be found in the class description of MF and MDataChain.
478//
479Int_t MF::ReadEnv(const TEnv &env, TString prefix, Bool_t print)
480{
481
482 /*
483 Bool_t rc = MFilter::ReadEnv(env, prefix, print);
484 if (rc==kERROR)
485 return kERROR;
486 */
487 if (IsEnvDefined(env, prefix, "Condition", print))
488 {
489 TString rule = GetEnvValue(env, prefix, "Condition", "");
490 rule.ReplaceAll(" ", "");
491
492 Int_t idx=0;
493 while (1)
494 {
495 TString cond;
496 if (IsEnvDefined(env, prefix, Form("%d", idx), print))
497 {
498 cond += "(";
499 cond += GetEnvValue(env, prefix, Form("%d", idx), "");
500 cond += ")";
501 }
502
503 if (cond.IsNull())
504 break;
505
506 rule.ReplaceAll(Form("{%d}", idx), cond);
507 idx++;
508 }
509
510 if (fF)
511 {
512 delete fF;
513 fF = 0;
514 }
515
516 if (rule.IsNull())
517 {
518 *fLog << warn << GetDescriptor() << " ::ReadEnv - WARNING: Empty condition found." << endl;
519 SetAllowEmpty();
520 return kTRUE;
521 }
522
523 SetAllowEmpty(kFALSE);
524
525 if (!(fF=ParseString(rule, 1)))
526 {
527 *fLog << err << "MF::ReadEnv - ERROR: Parsing '" << rule << "' failed." << endl;
528 return kERROR;
529 }
530
531 if (print)
532 {
533 *fLog << inf << "found: ";
534 fF->Print();
535 *fLog << endl;
536 }
537 }
538 else
539 {
540 if (!fF)
541 {
542 *fLog << warn << GetDescriptor() << " ::ReadEnv - WARNING: No condition found." << endl;
543 SetAllowEmpty();
544 return kFALSE;
545 }
546 }
547
548 if (fF->ReadEnv(env, prefix, print)==kERROR)
549 return kERROR;
550
551 // This is a workaround, because MFilter::ReadEnv and fF->ReadEnv
552 // might check for "Inverted" in this case both gets inverted
553 // and double-inversion is no inversion
554 /*
555 if (IsEnvDefined(env, prefix, "Inverted", print))
556 if (GetEnvValue(env, prefix, "Inverted", kFALSE)==kTRUE)
557 SetInverted(kFALSE);*/
558
559 return kTRUE;
560}
Note: See TracBrowser for help on using the repository browser.