Commit 20133af489caec0efc4be818c501c97a8fa678a1
Committed by
Jürgen Knödlseder
1 parent
a9168621
Created a sub method to compute the necessary reflected regions separation angle…
…. As the necessary separation angle depends on the rotation angle but also on the rectangle extension (overlaps may easily occur) the computation is not so easy: I implemented a rough (slightly overestimating) algorithm which chooses the maximum opening angle between the corners of the rectangle with respect to the pointing position. Extended implementation of _reflected_regions() method to allow the placement of rectangular off regions, which is essentially finally placing a rectangle instead of a circle.
Showing
1 changed file
with
121 additions
and
3 deletions
cscripts/csphagen.py
| ... | ... | @@ -363,6 +363,116 @@ class csphagen(ctools.csobservation): |
| 363 | 363 | # Return |
| 364 | 364 | return |
| 365 | 365 | |
| 366 | + def _compute_region_separation(self, pnt_dir): | |
| 367 | + """ Compute the separation angle for reflected off regions in radians. | |
| 368 | + | |
| 369 | + Returns | |
| 370 | + ------- | |
| 371 | + angle : `float` | |
| 372 | + Separation angle of two off regions (rad) | |
| 373 | + """ | |
| 374 | + # Initialise the result | |
| 375 | + separation = -1 | |
| 376 | + | |
| 377 | + # Compute offset of reflected regions to pointing position | |
| 378 | + offset = pnt_dir.dist_deg(self._src_dir) | |
| 379 | + | |
| 380 | + if self._srcshape == 'CIRCLE': | |
| 381 | + | |
| 382 | + # Compute apparent diameter of the circle | |
| 383 | + separation = 2.0 * self._rad / offset | |
| 384 | + | |
| 385 | + elif self._srcshape == 'RECT': | |
| 386 | + | |
| 387 | + # Compute the opening angle towards combinations of rectangle corners. | |
| 388 | + # This method overestimates the real need of space between the | |
| 389 | + # rectangles, so the method may be optimised to gain more off regions! | |
| 390 | + # Anyway, it is assured that the off regions will never overlap. | |
| 391 | + | |
| 392 | + # Get the corners | |
| 393 | + cs = [self._src_reg[0].get_corner(icorner) for icorner in range(4)] | |
| 394 | + | |
| 395 | + # Auxillary, local method for opening angle computation | |
| 396 | + def __compute_posang(pnt_dir,a,b): | |
| 397 | + """ Compute the difference in position angle to the pointing in | |
| 398 | + degrees. | |
| 399 | + """ | |
| 400 | + posang_a = pnt_dir.posang_deg(a) %360 | |
| 401 | + posang_b = pnt_dir.posang_deg(b) %360 | |
| 402 | + posang = abs(posang_a - posang_b) | |
| 403 | + return posang | |
| 404 | + | |
| 405 | + # Compute the 6 opening angles | |
| 406 | + combinations = [[0,1], [0,2], [0,3], [1,2], [1,3], [2,3]] | |
| 407 | + angles = [__compute_posang(pnt_dir, cs[i], cs[j]) for i,j in combinations] | |
| 408 | + | |
| 409 | + # The desired separation is the maximum opening angle | |
| 410 | + separation = max(angles) *gammalib.deg2rad | |
| 411 | + | |
| 412 | + | |
| 413 | + ## Different approach: | |
| 414 | + ## Find the separation angle for which two rectangles to not overlap | |
| 415 | + ## Issue: Scanning not optimal. | |
| 416 | + # posang = pnt_dir.posang_deg(self._src_dir) | |
| 417 | + # dphi_incr = 0.1 | |
| 418 | + # dphi = dphi_incr | |
| 419 | + # print("Src ctr direction: ra={} dec={}".format(self._src_dir.ra_deg(), self._src_dir.dec_deg())) | |
| 420 | + # print(self._src_reg) | |
| 421 | + # while dphi<360: | |
| 422 | + | |
| 423 | + # ctr_dir = pnt_dir.clone() | |
| 424 | + # ctr_dir.rotate_deg(posang + dphi, offset) | |
| 425 | + # print("Query ctr direction: ra={} dec={}".format(ctr_dir.ra_deg(), ctr_dir.dec_deg())) | |
| 426 | + | |
| 427 | + # # Create test rectangle | |
| 428 | + # reg = gammalib.GSkyRegionRect(ctr_dir | |
| 429 | + # ,self._reg_width | |
| 430 | + # ,self._reg_height | |
| 431 | + # ,self._reg_posang + dphi | |
| 432 | + # ) | |
| 433 | + # print(reg) | |
| 434 | + | |
| 435 | + # # Check test rectangle corner containment in reference rectangle | |
| 436 | + # is_corner_contained = self._src_reg.overlaps(reg) | |
| 437 | + # # is_corner_contained = False | |
| 438 | + # # for icorner in range(4): | |
| 439 | + # # corner_dir = reg.get_corner(icorner) | |
| 440 | + # # is_corner_contained |= self._src_reg.contains(corner_dir) | |
| 441 | + | |
| 442 | + # # Check if any corner was contained | |
| 443 | + # if not is_corner_contained: | |
| 444 | + # diameter = dphi | |
| 445 | + # break | |
| 446 | + | |
| 447 | + # # Increment dphi | |
| 448 | + # dphi += dphi_incr | |
| 449 | + | |
| 450 | + | |
| 451 | + ## Very different approach: Compute the width along the circular | |
| 452 | + ## region placement path. | |
| 453 | + ## Issue: Long regions will overlap in the inner part. | |
| 454 | + # def l(w,h,phi): | |
| 455 | + # """ Super complicated method to compute the height along dec-axis | |
| 456 | + # of a rotated rectangle. | |
| 457 | + # """ | |
| 458 | + # phi = np.deg2rad(phi) % (2*np.pi) | |
| 459 | + # phi_c = np.arctan2(w,h) | |
| 460 | + # if phi>=(0.5*np.pi) and (phi<np.pi): | |
| 461 | + # phi = np.pi-phi | |
| 462 | + # elif (phi>=np.pi) and (phi<(1.5*np.pi)): | |
| 463 | + # phi -=np.pi | |
| 464 | + # elif (phi>=(1.5*np.pi)) and (phi<(2*np.pi)): | |
| 465 | + # phi = 2*np.pi - phi | |
| 466 | + | |
| 467 | + # if (0<=phi) and (phi<phi_c): | |
| 468 | + # result = h/2/np.cos(phi) | |
| 469 | + # else: | |
| 470 | + # result = w/2/np.sin(phi) | |
| 471 | + # return 2*result | |
| 472 | + | |
| 473 | + # Return | |
| 474 | + return separation | |
| 475 | + | |
| 366 | 476 | def _reflected_regions(self, obs): |
| 367 | 477 | """ |
| 368 | 478 | Calculate list of reflected regions for a single observation (pointing) |
| ... | ... | @@ -402,7 +512,7 @@ class csphagen(ctools.csobservation): |
| 402 | 512 | # ... otherwise |
| 403 | 513 | else: |
| 404 | 514 | posang = pnt_dir.posang_deg(self._src_dir) |
| 405 | - if self._srcshape == 'CIRCLE': | |
| 515 | + if (self._srcshape == 'CIRCLE') or (self._srcshape == 'RECT'): | |
| 406 | 516 | |
| 407 | 517 | # Determine number of background regions to skip |
| 408 | 518 | N_skip = self['bkgregskip'].integer() |
| ... | ... | @@ -412,7 +522,7 @@ class csphagen(ctools.csobservation): |
| 412 | 522 | # Compute the angular separation of reflected regions wrt |
| 413 | 523 | # camera center. The factor 1.05 ensures background regions |
| 414 | 524 | # do not overlap due to numerical precision issues |
| 415 | - alpha = 1.05 * 2.0 * self._rad / offset | |
| 525 | + alpha = 1.05 * self._compute_region_separation(pnt_dir) | |
| 416 | 526 | |
| 417 | 527 | # Compute number of reflected regions by dividing the angular |
| 418 | 528 | # separation by 2 pi. |
| ... | ... | @@ -437,7 +547,15 @@ class csphagen(ctools.csobservation): |
| 437 | 547 | while dphi <= dphi_max: |
| 438 | 548 | ctr_dir = pnt_dir.clone() |
| 439 | 549 | ctr_dir.rotate_deg(posang + dphi, offset) |
| 440 | - region = gammalib.GSkyRegionCircle(ctr_dir, self._rad) | |
| 550 | + if self._srcshape == 'CIRCLE': | |
| 551 | + region = gammalib.GSkyRegionCircle(ctr_dir, self._rad) | |
| 552 | + elif self._srcshape == 'RECT': | |
| 553 | + # Adjust the posang of the rectangle correspondingly | |
| 554 | + region = gammalib.GSkyRegionRect(ctr_dir | |
| 555 | + ,self._reg_width | |
| 556 | + ,self._reg_height | |
| 557 | + ,self._reg_posang + dphi | |
| 558 | + ) | |
| 441 | 559 | if self._has_exclusion: |
| 442 | 560 | if self._excl_reg.overlaps(region): |
| 443 | 561 | ... | ... |