ReflectedBgMaker.C

Deil Christoph, 10/15/2012 11:16 AM

Download (17.8 KB)

 
1
#include <algorithm>
2
#include <iomanip>
3

    
4
#define INFO_OUT_TAG "ReflectedBgMaker> " 
5
#define DEBUG 0
6
#include <utilities/debugging.hh>
7
#include <utilities/ConfigHandler.hh>
8
#include <crash/RotatedSystem.hh>
9
#include <crash/EquatorSystem.hh>
10
#include <sash/RunHeader.hh>
11

    
12
#include "ReflectedBgMaker.hh"
13

    
14
using std::cout;
15
using std::endl;
16

    
17
/**
18
 * Constructor
19
 *
20
 */
21
Background::ReflectedBgMaker::
22
ReflectedBgMaker(std::string name, Utilities::ConfigHandler& conf,
23
                 std::string input, bool init_now) :
24
  BgMaker(name.c_str(), conf, input, init_now)
25
{
26
  ConstructMaker(conf);
27
}
28
Background::ReflectedBgMaker::
29
ReflectedBgMaker(Utilities::ConfigHandler& conf,
30
                 std::string input, bool init_now) :
31
  BgMaker("ReflectedBgMaker", conf, input, init_now)
32
{
33
  ConstructMaker(conf);
34
}
35

    
36

    
37
void Background::ReflectedBgMaker::ConstructMaker(Utilities::ConfigHandler& conf)
38
{
39
  DEBUG_OUT << "Hello" << endl;
40

    
41
  fMaxNumberOffRegions = 100;
42
  fMinNumberOffRegions = 1;
43
  fMinOnOffDist = 0.1;
44
  fNumberOffRegions = 0;
45
  fRotationAngleDeg = 0.;
46
  fCurrentOffset = 0;
47
  fMaxOffRegionDist = 10.;
48
  fUseCircularOff = false; 
49

    
50
  std::string Name = "ReflectedBgMaker";
51

    
52
  //Read config options from the confighandler
53

    
54
  conf.SetSectionHelpText(Name, "Estimates background from a "
55
                          "series of on-region-shaped regions "
56
                          "that have the same offset from the observation "
57
                          "position as the test position. This BgMaker "
58
                          "is useful for making spectra, but is not well "
59
                          "suited for 2-D images." );
60
  
61
  conf.GetValue(Name, "RotationAngle", 
62
                fRotationAngleDeg, 
63
                "Rotation angle of the initial OFF "
64
                "region from the test position (deg)" );
65

    
66
  conf.GetValue(Name, "MaxNumberOffRegions", 
67
                fMaxNumberOffRegions,
68
                "Maximum allowed number of OFF regions");
69
  
70
  conf.GetValue(Name, "MinNumberOffRegions",
71
                fMinNumberOffRegions,
72
                "Minimum required number of OFF regions");
73
  
74
  conf.GetValue(Name, "MaxDistanceOffRegions", 
75
                fMaxOffRegionDist,
76
                "Maximum allowed distance between OFF regions and target");
77

    
78
  conf.GetValue(Name, "MinOnOffDistance", 
79
                fMinOnOffDist,
80
                "Minimum angular distance between the on region and "
81
                "any off region");
82
  
83
  fUseCircularOff = conf.GetFlag(Name, "UseCircularOff",
84
                                 "Use circular off regions "
85
                                 "(for backwards compatibility)" );
86
  
87
  // Make sure the exposure is weighted by area of the on and off regions
88
  ScaleExposureByRegionArea( true );
89
}
90

    
91

    
92
Background::ReflectedBgMaker::~ReflectedBgMaker() {
93
  DEBUG_OUT << "Goodbye!" << endl;
94
}
95

    
96

    
97
/**
98
 * Constructs OFF regions at the beginning of each run.
99
 * Calls CreateOffRegions().
100
 */
101
void  
102
Background::ReflectedBgMaker::
103
BeginRun() {
104

    
105
  // Look up the observation and test positions:
106

    
107
  // ON region gets automatically constructed by BgMaker::InitRun in
108
  // the Current BgStats object
109
  fNumberOffRegions = 0;
110

    
111
  const Tools::Compound& onregion ( GetBgStats()->GetOnRegion() );
112

    
113
  const Sash::RunHeader *runheader = fHess->Get<Sash::RunHeader>();
114
  if (!runheader) {
115
    WARN_OUT << "No runheader found" << endl;
116
    throw std::runtime_error( "No runheader was found!" );
117
  }
118

    
119
  const Stash::Coordinate &obspos_radec = runheader->GetObservationPosition().
120
    GetCoordinate(*(fHess->GetRADecJ2000System()));
121
  const Stash::Coordinate &testpos_radec = onregion.GetCentre().
122
    GetCoordinate(*(fHess->GetRADecJ2000System()));
123

    
124
  DEBUG_OUT << "Obspos =" << obspos_radec <<endl;
125
  DEBUG_OUT << "Testpos=" << testpos_radec <<endl;
126

    
127
  fCurrentOffset = testpos_radec.GetAngularDistance( obspos_radec ).
128
    GetDegrees();
129

    
130
  // Construct OFF regions
131
  Tools::Circle* circle = IsCircle(&onregion);
132
  if (circle)
133
    CreateOffRegionsForOnCircle(circle, obspos_radec);
134
  else
135
    CreateOffRegions(&onregion, obspos_radec);
136

    
137
  // Check that enough were found
138
  if ( GetBgStats()->GetOffRegion().GetNumRegions() < fMinNumberOffRegions) {
139
    WARN_OUT << "Run will be skipped: Too few off regions! (found "
140
             << GetBgStats()->GetOffRegion().GetNumRegions()
141
             << " / required " << fMinNumberOffRegions << " )" << std::endl;
142
    GetBgStats()->GetSkippedFlag() = true;   
143
  }
144

    
145
}
146

    
147
/**
148
 * Calculates exposure(On,Off) at the end of the run.
149
 * Sets exposureOn to alpha*NOff (with alpha=areaOn/areaOff)
150
 * and exposureOff to NOff
151
 */
152
void Background::ReflectedBgMaker::EndRun()
153
{
154
  DEBUG_OUT << std::endl;
155

    
156
  double alpha = GetBgStats()->GetOnRegion().GetArea()
157
    / GetBgStats()->GetOffRegion().GetArea();
158
  double nOff = GetBgStats()->GetRateStats().nOff;
159

    
160
  GetBgStats()->GetRateStats().exposureOff = nOff;
161
  GetBgStats()->GetRateStats().exposureOn  = alpha * nOff;
162

    
163
  DEBUG_OUT << "on region area:  " << GetBgStats()->GetOnRegion().GetArea() << endl;
164
  DEBUG_OUT << "off region area: " << GetBgStats()->GetOffRegion().GetArea() << endl;
165
  DEBUG_OUT << "-> alpha:        " << alpha << endl;
166
  DEBUG_OUT << "exposure off:    " << GetBgStats()->GetRateStats().exposureOff << endl;
167
  DEBUG_OUT << "-> exposure on:  " << GetBgStats()->GetRateStats().exposureOn << endl;
168
}
169

    
170
/*
171
 * Fill On events into OnMap for debug purposes
172
 */
173
bool Background::ReflectedBgMaker::IsOnMapEvent()
174
{
175

    
176
  DEBUG_OUT << std::endl;
177
  if(Background::BgMaker::IsOnEvent()) return true;
178
  else return false;
179
  
180
}
181

    
182
/*
183
 * Fill Off events into OffMap for debug purposes
184
 */
185
bool Background::ReflectedBgMaker::IsOffMapEvent()
186
{
187

    
188
  DEBUG_OUT << std::endl;
189
  if(Background::BgMaker::IsOffEvent()) return true;
190
  else return false;
191
  
192
}
193

    
194
/*
195
 * Leave OnExposureMap empty
196
 */
197
void Background::ReflectedBgMaker::FillOnExposure()
198
{
199

    
200
  DEBUG_OUT << std::endl;
201
  
202
}
203

    
204
/*
205
 * Leave OffExposureMap empty
206
 */
207
void Background::ReflectedBgMaker::FillOffExposure()
208
{
209

    
210
  DEBUG_OUT << std::endl;
211
  
212
}
213

    
214
/**
215
 * \brief Check if a region is a circle and returns a pointer to it.
216
 *
217
 * This is needed because circular on regions are created as
218
 * Tools::Compound objects containing one Tools::Circle,
219
 * and Tools::Compound is not a superclass of Tools::Circle.
220
 *
221
 * In that case a pointer to that Tools::Circle in the compound is returned.
222
 */
223
Tools::Circle* Background::ReflectedBgMaker::IsCircle(const Tools::Region* region) {
224
  // Is the type Tools::Circle?
225
  DEBUG_OUT << "region type: " << typeid(*region).name() << endl;
226
  if (typeid(*region) == typeid(Tools::Circle))
227
    return (Tools::Circle*)region;
228

    
229
  // Is the type Tools::Compound, but it contains exactly one circle?
230
  if (typeid(*region) == typeid(Tools::Compound)) {
231
    Tools::Compound* compound = (Tools::Compound*)region;
232
    DEBUG_OUT << "compound type: " << typeid(*compound).name() << endl;
233
    DEBUG_OUT << "NumRegions: " << compound->GetNumRegions() << endl;
234
    if (compound->GetNumRegions() == 1) {
235
      const Tools::Region* first_region = compound->GetRegions()[0];
236
      DEBUG_OUT << "First region type: " << typeid(*first_region).name() << endl;
237
      if (typeid(*first_region) == typeid(Tools::Circle))
238
        return (Tools::Circle*)first_region;
239
    }
240
  }
241
  return 0;
242
}
243

    
244

    
245
/**
246
 * \brief Create off regions on a circle around the observation position.
247
 *
248
 * OFF regions are found by incrementally rotating the ON region.
249
 * For each OFF region candidate, it is checked whether it overlaps with
250
 * an already existing OFF region or an exclusion region. To exclude any
251
 * potential contamination of OFF regions by signal, an additional safety
252
 * margin of fMinOnOffDist is required between the ON region and
253
 * any OFF region.
254
 *
255
 * OFF regions are placed in the so-called TangentialRADecSystem, which
256
 * is a Cartesian projection of the RADec system at the observation position.
257
 * This approach avoids a potential bias of the OFF area due to projection effects.
258
 *
259
 * The OFF regions are added to Offregions compound in CurrentBgStats
260
 *
261
 * \param onregion - Compound ON region
262
 * \param obspos   - Observation position in Tangential system
263
 *
264
 * \todo Make sure this also works for Tools::Compund ON regions
265
 */
266
bool
267
Background::ReflectedBgMaker::
268
CreateOffRegions(const Tools::Region* onregion,
269
                 const Stash::Coordinate &obspos_radec)
270
{
271
  INFO_OUT << "On region is not a circle. "
272
    "Creating off regions using the general algorithm." << endl;
273

    
274
  fNumberOffRegions = 0;
275

    
276
  // Skip run if the ON region contains the observation position
277
  if( onregion->Contains(obspos_radec) ) {
278
    WARN_OUT << "ON region contains the observation position." << std::endl;
279
    return false;
280
  }
281

    
282
  // test position
283
  const Stash::Coordinate testpos_radec = onregion->GetCentre().
284
    GetCoordinate(*fHess->GetRADecJ2000System());
285

    
286
  // Enlarge a copy of the on region by fMinOnOffDist. This copy is then
287
  // used to check for overlap with potential off regions, and thus
288
  // guarantees a proper spacing between the on region and any off
289
  Tools::Region *grownOn
290
    = onregion->GrowBoundary(fMinOnOffDist);
291

    
292
  DEBUG_OUT << "on region: " << *onregion << std::endl;
293
  DEBUG_OUT << "grown on region: " << *grownOn << std::endl;
294

    
295
  // Create off region only once, for reasons of speed,
296
  // and later only modify rotation angle and position
297
  Tools::Region* tmpOff = 0;
298

    
299
  if( fUseCircularOff ) {
300
    double off_radius = onregion->GetEquivalentCircleRadius();
301
    DEBUG_OUT << "using circular off regions with radius radius " 
302
              << off_radius << " deg" << std::endl;
303
    tmpOff = new Tools::Circle( testpos_radec, off_radius, "Off");
304
  }
305
  else {
306
    DEBUG_OUT << "using on-shaped off regions" << std::endl;
307
    tmpOff = (Tools::Region*)onregion->Clone("Off");
308
  }
309

    
310
  DEBUG_OUT << *tmpOff << std::endl;
311
  
312
  // Brute-force off region finding.
313
  // Rotate on region around observation position.
314
  // step size is adapted according to the ratio of equivalent circle radius
315
  // and offset.
316
  //
317
  // Slow, but reliable for even strangly shaped regions.
318
  double radius = onregion->GetEquivalentCircleRadius();
319
  double offset = testpos_radec.GetAngularDistance(obspos_radec).GetDegrees();
320
  double stepsize = asin(radius/offset) * 180.0 / M_PI / 5.;
321

    
322
  DEBUG_OUT << "equivalent circle radius: " << radius << std::endl;
323
  DEBUG_OUT << "offset:                   " << offset << std::endl;
324
  DEBUG_OUT << "stepsize:                 " << stepsize << std::endl;
325
  if( stepsize != stepsize ) {
326
    WARN_OUT << "Offset too small (" << offset 
327
             << "deg), no Off regions could be found." << std::endl;
328
    return false;
329
  }
330

    
331
  Stash::Lambda rotAngle(fRotationAngleDeg, Stash::Angle::Degrees);
332
  Stash::SmallAngle dRot(stepsize, Stash::Angle::Degrees);
333
  int numSteps = 360. / stepsize;
334

    
335
  do {
336

    
337
    tmpOff->SetRotation( rotAngle, obspos_radec );
338

    
339
    DEBUG_OUT << "Trying to find off region " << *tmpOff
340
              << " at rotation angle " 
341
              << rotAngle << std::endl;
342

    
343
    // add the new region only if it does not overlap with any other
344
    // exclusion, on, or off region. Leave fMinOnOffDist space
345
    // between the on and the off regions (use the grown on region)
346
    if( !(tmpOff->Overlaps(GetBgStats()->GetOffRegion()) )) {
347
      if( !(tmpOff->Overlaps(GetBgStats()->GetOnExclusionRegion()) )) {
348
        if( !tmpOff->Overlaps( *grownOn ) ) {
349

    
350
          // add off region to bgstats
351
          std::ostringstream offName;
352
          offName << std::fixed << std::setprecision(1)
353
                  << rotAngle.GetDegrees() << "deg";
354

    
355
          Tools::Region* newOff 
356
            = (Tools::Region*)tmpOff->Clone( offName.str().c_str() );
357
          GetBgStats()->GetOffRegion().AddRegion( newOff );
358

    
359
          fNumberOffRegions++;
360
          
361
          INFO_OUT << "Added new off region at "<< rotAngle.GetDegrees() << " deg from North at "
362
                   << newOff->GetTrueCentre().GetCoordinate(*fHess->GetRADecJ2000System()) << std::endl;
363
          DEBUG_OUT << "offset from observation position: " 
364
                    << newOff->GetTrueCentre().GetCoordinate(*fHess->GetRADecJ2000System())
365
            .GetAngularDistance( obspos_radec ).GetDegrees() << " deg" << std::endl;
366

    
367
          if( fNumberOffRegions == fMaxNumberOffRegions ) {
368
            INFO_OUT << "Reached maximum allowed number of OFF regions ("
369
                     << fMaxNumberOffRegions << "). Stopping creating new "
370
                     << "ones at your request." << std::endl;
371
            break;
372
          }
373
        }
374
      }
375
    }
376
    rotAngle += dRot;
377

    
378
  } while( --numSteps != 0 );
379

    
380
  delete grownOn;
381
  delete tmpOff;
382

    
383
  if ( fNumberOffRegions == 0) {
384
    WARN_OUT << "No Off regions found." << std::endl;
385
    return false;
386
  } 
387

    
388
  INFO_OUT << "Added "<< fNumberOffRegions
389
           << " OFF regions at offset " << fCurrentOffset << endl;
390
  return true;
391
}
392

    
393
/**
394
 * \brief Create off regions for a circular on region.
395
 *
396
 * The off regions are stored in CurrentBgStats.
397
 *
398
 * \param onregion - Compound ON region
399
 * \param obspos   - Observation position in Tangential system
400
 *
401
 * \note This is the common case that warrants a fast implementation,
402
 * the general algorithm is in CreateOffRegions().
403
 */
404
bool
405
Background::ReflectedBgMaker::
406
CreateOffRegionsForOnCircle(const Tools::Circle* onregion,
407
                            const Stash::Coordinate &obspos_radec)
408
{
409
  INFO_OUT << "On region is a circle. "
410
    "Creating off regions using the fast algorithm." << endl;
411
  DEBUG_OUT << "on region: " << *onregion << std::endl;
412

    
413
  fNumberOffRegions = 0;
414

    
415
  // Skip run if the ON region contains the observation position
416
  if( onregion->Contains(obspos_radec) ) {
417
    WARN_OUT << "ON region contains the observation position." << std::endl;
418
    return false;
419
  }
420

    
421
  // test position
422
  const Stash::Coordinate onpos_radec = onregion->GetCentre().
423
    GetCoordinate(*fHess->GetRADecJ2000System());
424
  DEBUG_OUT << "onpos_radec: " << onpos_radec << std::endl;
425

    
426
  // Create off region only once, for reasons of speed,
427
  // and later only modify rotation angle and position
428
  Tools::Region* tmpOff = (Tools::Circle*)onregion->Clone("Off");
429
  DEBUG_OUT << "off region: " << *tmpOff << std::endl;
430

    
431
  DEBUG_OUT << "--- OnExclusionRegion START --- " << endl
432
            << GetBgStats()->GetOnExclusionRegion() << endl
433
            << "--- OnExclusionRegion END --- " << endl;
434

    
435
  // Compute the relevant numbers needed for computing positions
436
  // segment = angle a test circle spans on the circle of radius
437
  // offset around the observation position
438
  double radius = onregion->GetR2();
439
  double offset = onpos_radec.GetAngularDistance(obspos_radec).GetDegrees();
440
  double segment = 2 * asin(radius/offset) * 180.0 / M_PI;
441
  // \note This scheme of deciding on the number of test positions
442
  // was chosen to match the general algorithm in CreateOffRegions()
443
  int steps_per_segment = 10;
444
  double stepsize = segment / steps_per_segment;
445
  int n_test_positions = 360 / segment * steps_per_segment;
446
  DEBUG_OUT << "radius:   " << radius << std::endl;
447
  DEBUG_OUT << "offset:   " << offset << std::endl;
448
  DEBUG_OUT << "segment: " << segment << std::endl;
449
  DEBUG_OUT << "steps_per_segment: " << steps_per_segment << std::endl;
450
  DEBUG_OUT << "stepsize: " << stepsize << std::endl;
451
  DEBUG_OUT << "n_test_positions: " << n_test_positions << std::endl;
452

    
453
  // Loop test positions from 0 to 360 deg
454
  double rot_angle = 0;
455
  do {
456

    
457
    // Rotate off region to current rot_angle
458
    Stash::Lambda rot_angle_lambda(rot_angle, Stash::Angle::Degrees);
459
    tmpOff->SetRotation( rot_angle_lambda, obspos_radec );
460
    // Compute the new position and offset to on center position
461
    const Stash::Coordinate tmpOffpos_radec = tmpOff->GetTrueCentre().
462
      GetCoordinate(*fHess->GetRADecJ2000System());
463
    double on_off_dist = tmpOffpos_radec.GetAngularDistance( onpos_radec ).GetDegrees();
464
    double off_radius = tmpOff->GetEquivalentCircleRadius();
465
    // The criterion is that the on and off circle must have a separation
466
    // of at least fMinOnOffDist, i.e. that the centers of the on and off
467
    // circles have a separation < allowed_dist defined like this:
468
    double allowed_dist = fMinOnOffDist + radius + off_radius;
469
    
470
    DEBUG_OUT << "rot_angle " << rot_angle << std::endl;
471
    DEBUG_OUT << "rot_angle_lambda " << rot_angle_lambda << std::endl;
472
    DEBUG_OUT << "tmpOff " << *tmpOff << std::endl;
473
    DEBUG_OUT << "tmpOff rotation " << tmpOff->GetRotationAngle() << std::endl;
474
    DEBUG_OUT << "tmpOffpos_radec " << tmpOffpos_radec << std::endl;
475
    DEBUG_OUT << "onpos_radec " << onpos_radec << std::endl;
476
    DEBUG_OUT << "on_off_dist " << on_off_dist << std::endl;
477
    DEBUG_OUT << "off_radius " << off_radius << std::endl;
478
    DEBUG_OUT << "allowed_dist " << allowed_dist << std::endl;
479

    
480
    // Skip to next test position if off region too close to on region
481
    if (on_off_dist < allowed_dist) {
482
      DEBUG_OUT << "Skipping rot_angle " << rot_angle << " because on_off_dist " << on_off_dist
483
                << " is less than " << fMinOnOffDist << std::endl;
484
      rot_angle += stepsize;
485
      continue;
486
    }
487

    
488
    // Skip to next test position if off region overlaps with exclusion region
489
    if (tmpOff->Overlaps(GetBgStats()->GetOnExclusionRegion())) {
490
      DEBUG_OUT << "Skipping rot_angle " << rot_angle
491
                << " because it overlaps with the exclusion region." << std::endl;
492
      rot_angle += stepsize;
493
      continue;
494
    }
495

    
496
    // Now we know this is a valid off region position.
497
    // We create it and then move on by one segment length
498
    // to avoid placing new off regions over existing ones.
499
    DEBUG_OUT << "Accepting rot_angle " << rot_angle << std::endl;
500

    
501
    // add off region to bgstats
502
    std::ostringstream offName;
503
    offName << std::fixed << std::setprecision(1) << rot_angle << "deg";
504

    
505
    Tools::Region* newOff = (Tools::Region*)tmpOff->Clone( offName.str().c_str() );
506
    GetBgStats()->GetOffRegion().AddRegion( newOff );
507

    
508
    INFO_OUT << "Added new off region at "<< rot_angle << " deg from North at "
509
             << newOff->GetTrueCentre().GetCoordinate(*fHess->GetRADecJ2000System()) << std::endl;
510
    DEBUG_OUT << "offset from observation position: "
511
              << newOff->GetTrueCentre().GetCoordinate(*fHess->GetRADecJ2000System())
512
      .GetAngularDistance( obspos_radec ).GetDegrees() << " deg" << std::endl;
513

    
514
    if( fNumberOffRegions == fMaxNumberOffRegions ) {
515
      INFO_OUT << "Reached maximum allowed number of OFF regions ("
516
               << fMaxNumberOffRegions << "). Stopping creating new "
517
               << "ones at your request." << std::endl;
518
      break;
519
    }
520

    
521
    fNumberOffRegions++;
522
    rot_angle += segment;
523
  } while( rot_angle < 360 );
524

    
525
  delete tmpOff;
526

    
527
  if (fNumberOffRegions == 0) {
528
    WARN_OUT << "No Off regions found." << std::endl;
529
    return false;
530
  }
531

    
532
  INFO_OUT << "Added "<< fNumberOffRegions << " OFF regions at offset " << fCurrentOffset << endl;
533
  return true;
534
}