Commit b8b92464b8d449e7e2ce77af8b93559d5531ab5b

Authored by Jürgen Knödlseder
1 parent 8ff45cf6

Support Python list slicing of obs containers (#2128)

pyext/GEbounds.i
... ... @@ -93,6 +93,35 @@ public:
93 93 * @brief GEbounds class extension
94 94 ***************************************************************************/
95 95 %extend GEbounds {
  96 + GEbounds* __getitem__(PyObject *param) {
  97 + if (PySlice_Check(param)) {
  98 + Py_ssize_t start = 0;
  99 + Py_ssize_t stop = 0;
  100 + Py_ssize_t step = 0;
  101 + Py_ssize_t len = self->size();
  102 + if (PySlice_GetIndices((PySliceObject*)param, len, &start, &stop, &step) == 0) {
  103 + GEbounds* ebounds = new GEbounds;
  104 + if (step > 0) {
  105 + for (int i = (int)start; i < (int)stop; i += (int)step) {
  106 + ebounds->append(self->emin(i), self->emax(i));
  107 + }
  108 + }
  109 + else {
  110 + for (int i = (int)start; i > (int)stop; i += (int)step) {
  111 + ebounds->append(self->emin(i), self->emax(i));
  112 + }
  113 + }
  114 + return ebounds;
  115 + }
  116 + else {
  117 + throw GException::invalid_argument("__getitem__(PyObject)",
  118 + "Invalid slice indices");
  119 + }
  120 + }
  121 + else {
  122 + throw GException::invalid_argument("__getitem__(PyObject)","");
  123 + }
  124 + }
96 125 GEbounds copy() {
97 126 return (*self);
98 127 }
... ...
pyext/GGti.i
... ... @@ -85,8 +85,34 @@ public:
85 85 * @brief GGti class extension
86 86 ***************************************************************************/
87 87 %extend GGti {
88   - int __len__() {
89   - return (self->size());
  88 + GGti* __getitem__(PyObject *param) {
  89 + if (PySlice_Check(param)) {
  90 + Py_ssize_t start = 0;
  91 + Py_ssize_t stop = 0;
  92 + Py_ssize_t step = 0;
  93 + Py_ssize_t len = self->size();
  94 + if (PySlice_GetIndices((PySliceObject*)param, len, &start, &stop, &step) == 0) {
  95 + GGti* gti = new GGti;
  96 + if (step > 0) {
  97 + for (int i = (int)start; i < (int)stop; i += (int)step) {
  98 + gti->append(self->tstart(i), self->tstop(i));
  99 + }
  100 + }
  101 + else {
  102 + for (int i = (int)start; i > (int)stop; i += (int)step) {
  103 + gti->append(self->tstart(i), self->tstop(i));
  104 + }
  105 + }
  106 + return gti;
  107 + }
  108 + else {
  109 + throw GException::invalid_argument("__getitem__(PyObject)",
  110 + "Invalid slice indices");
  111 + }
  112 + }
  113 + else {
  114 + throw GException::invalid_argument("__getitem__(PyObject)","");
  115 + }
90 116 }
91 117 GGti copy() {
92 118 return (*self);
... ...
pyext/GPhases.i
... ... @@ -63,8 +63,34 @@ public:
63 63 * @brief GPhases class extension
64 64 ***************************************************************************/
65 65 %extend GPhases {
66   - int __len__() {
67   - return (self->size());
  66 + GPhases* __getitem__(PyObject *param) {
  67 + if (PySlice_Check(param)) {
  68 + Py_ssize_t start = 0;
  69 + Py_ssize_t stop = 0;
  70 + Py_ssize_t step = 0;
  71 + Py_ssize_t len = self->size();
  72 + if (PySlice_GetIndices((PySliceObject*)param, len, &start, &stop, &step) == 0) {
  73 + GPhases* phases = new GPhases;
  74 + if (step > 0) {
  75 + for (int i = (int)start; i < (int)stop; i += (int)step) {
  76 + phases->append(self->pmin(i), self->pmax(i));
  77 + }
  78 + }
  79 + else {
  80 + for (int i = (int)start; i > (int)stop; i += (int)step) {
  81 + phases->append(self->pmin(i), self->pmax(i));
  82 + }
  83 + }
  84 + return phases;
  85 + }
  86 + else {
  87 + throw GException::invalid_argument("__getitem__(PyObject)",
  88 + "Invalid slice indices");
  89 + }
  90 + }
  91 + else {
  92 + throw GException::invalid_argument("__getitem__(PyObject)","");
  93 + }
68 94 }
69 95 GPhases copy() {
70 96 return (*self);
... ...
test/test_GApplication.py
... ... @@ -297,8 +297,8 @@ class Test(gammalib.GPythonTestSuite):
297 297 self.append(self._test_log, 'Test GLog')
298 298 self.append(self._test_app, 'Test GApplication')
299 299 self.append(self._test_pars, 'Test GApplicationPars')
300   - self.append(self._test_pars_access, 'Test GApplicationPars class parameter access')
301   - self.append(self._test_pars_access, 'Test GApplicationPars class slicing')
  300 + self.append(self._test_pars_access, 'Test GApplicationPars parameter access')
  301 + self.append(self._test_pars_access, 'Test GApplicationPars slicing')
302 302  
303 303 # Return
304 304 return
... ...
test/test_GModel.py
... ... @@ -115,8 +115,8 @@ class Test(gammalib.GPythonTestSuite):
115 115 self.name('model')
116 116  
117 117 # Append tests
118   - self.append(self._test_models_access, 'Test GModels class model access')
119   - self.append(self._test_models_slicing, 'Test GModels class slicing')
  118 + self.append(self._test_models_access, 'Test GModels model access')
  119 + self.append(self._test_models_slicing, 'Test GModels slicing')
120 120  
121 121 # Return
122 122 return
... ...
test/test_GObservation.py
... ... @@ -90,6 +90,46 @@ class Test(gammalib.GPythonTestSuite):
90 90 # Return times container
91 91 return times
92 92  
  93 + # Setup GGti container
  94 + def _setup_gti(self):
  95 + """
  96 + Setup GGti container
  97 +
  98 + Returns
  99 + -------
  100 + gti : `~gammalib.GGti`
  101 + GGti container
  102 + """
  103 + # Setup GTI container
  104 + gti = gammalib.GGti()
  105 + for i in range(10):
  106 + tstart = gammalib.GTime(float(i), 'sec')
  107 + tstop = gammalib.GTime(float(i+1), 'sec')
  108 + gti.append(tstart, tstop)
  109 +
  110 + # Return GTI container
  111 + return gti
  112 +
  113 + # Setup GPhases container
  114 + def _setup_phases(self):
  115 + """
  116 + Setup GPhases container
  117 +
  118 + Returns
  119 + -------
  120 + phases : `~gammalib.GPhases`
  121 + GPhases container
  122 + """
  123 + # Setup phases container
  124 + phases = gammalib.GPhases()
  125 + for i in range(10):
  126 + pmin = float(i)
  127 + pmax = float(i+1)
  128 + phases.append(pmin, pmax)
  129 +
  130 + # Return phases container
  131 + return phases
  132 +
93 133 # Setup GPhotons container
94 134 def _setup_photons(self):
95 135 """
... ... @@ -289,6 +329,60 @@ class Test(gammalib.GPythonTestSuite):
289 329 # Return
290 330 return
291 331  
  332 + # Test GEbounds class slicing
  333 + def _test_ebounds_slicing(self):
  334 + """
  335 + Test GEbounds class slicing
  336 + """
  337 + # Setup energies container
  338 + ebounds = gammalib.GEbounds(10, gammalib.GEnergy(1.0, 'MeV'),
  339 + gammalib.GEnergy(11.0, 'MeV'), False)
  340 +
  341 + # Test ebounds[start:end]
  342 + self.test_value(len(ebounds[3:5]), 2)
  343 + self.test_value(ebounds[3:5].emin(0).MeV(), 4.0)
  344 + self.test_value(ebounds[3:5].emin(1).MeV(), 5.0)
  345 +
  346 + # Test ebounds[start:]
  347 + self.test_value(len(ebounds[7:]), 3)
  348 + self.test_value(ebounds[7:].emin(0).MeV(), 8.0)
  349 + self.test_value(ebounds[7:].emin(1).MeV(), 9.0)
  350 + self.test_value(ebounds[7:].emin(2).MeV(), 10.0)
  351 +
  352 + # Test ebounds[:end]
  353 + self.test_value(len(ebounds[:2]), 2)
  354 + self.test_value(ebounds[:2].emin(0).MeV(), 1.0)
  355 + self.test_value(ebounds[:2].emin(1).MeV(), 2.0)
  356 +
  357 + # Test ebounds[:]
  358 + self.test_value(len(ebounds[:]), 10)
  359 + for i in range(10):
  360 + self.test_value(ebounds[:].emin(i).MeV(), float(i+1))
  361 +
  362 + # Test ebounds[start:end:step]
  363 + self.test_value(len(ebounds[3:7:2]), 2)
  364 + self.test_value(ebounds[3:7:2].emin(0).MeV(), 4.0)
  365 + self.test_value(ebounds[3:7:2].emin(1).MeV(), 6.0)
  366 +
  367 + # Test ebounds[start:end:step]
  368 + self.test_value(len(ebounds[6:3:-2]), 2)
  369 + self.test_value(ebounds[6:3:-2].emin(0).MeV(), 7.0)
  370 + self.test_value(ebounds[6:3:-2].emin(1).MeV(), 5.0)
  371 +
  372 + # Test ebounds[-start:]
  373 + self.test_value(len(ebounds[-2:]), 2)
  374 + self.test_value(ebounds[-2:].emin(0).MeV(), 9.0)
  375 + self.test_value(ebounds[-2:].emin(1).MeV(), 10.0)
  376 +
  377 + # Test ebounds[:-end]
  378 + self.test_value(len(ebounds[:-7]), 3)
  379 + self.test_value(ebounds[:-7].emin(0).MeV(), 1.0)
  380 + self.test_value(ebounds[:-7].emin(1).MeV(), 2.0)
  381 + self.test_value(ebounds[:-7].emin(2).MeV(), 3.0)
  382 +
  383 + # Return
  384 + return
  385 +
292 386 # Test GTime class
293 387 def _test_time(self):
294 388 """
... ... @@ -441,6 +535,112 @@ class Test(gammalib.GPythonTestSuite):
441 535 # Return
442 536 return
443 537  
  538 + # Test GGti class slicing
  539 + def _test_gti_slicing(self):
  540 + """
  541 + Test GGti class slicing
  542 + """
  543 + # Setup GTI container
  544 + gti = self._setup_gti()
  545 +
  546 + # Test gti[start:end]
  547 + self.test_value(len(gti[3:5]), 2)
  548 + self.test_value(gti[3:5].tstop(0).secs(), 4.0)
  549 + self.test_value(gti[3:5].tstop(1).secs(), 5.0)
  550 +
  551 + # Test gti[start:]
  552 + self.test_value(len(gti[7:]), 3)
  553 + self.test_value(gti[7:].tstop(0).secs(), 8.0)
  554 + self.test_value(gti[7:].tstop(1).secs(), 9.0)
  555 + self.test_value(gti[7:].tstop(2).secs(), 10.0)
  556 +
  557 + # Test gti[:end]
  558 + self.test_value(len(gti[:2]), 2)
  559 + self.test_value(gti[:2].tstop(0).secs(), 1.0)
  560 + self.test_value(gti[:2].tstop(1).secs(), 2.0)
  561 +
  562 + # Test gti[:]
  563 + self.test_value(len(gti[:]), 10)
  564 + for i in range(10):
  565 + self.test_value(gti[:].tstop(i).secs(), float(i+1))
  566 +
  567 + # Test gti[start:end:step]
  568 + self.test_value(len(gti[3:7:2]), 2)
  569 + self.test_value(gti[3:7:2].tstop(0).secs(), 4.0)
  570 + self.test_value(gti[3:7:2].tstop(1).secs(), 6.0)
  571 +
  572 + # Test gti[start:end:step]
  573 + self.test_value(len(gti[6:3:-2]), 2)
  574 + self.test_value(gti[6:3:-2].tstop(0).secs(), 7.0)
  575 + self.test_value(gti[6:3:-2].tstop(1).secs(), 5.0)
  576 +
  577 + # Test gti[-start:]
  578 + self.test_value(len(gti[-2:]), 2)
  579 + self.test_value(gti[-2:].tstop(0).secs(), 9.0)
  580 + self.test_value(gti[-2:].tstop(1).secs(), 10.0)
  581 +
  582 + # Test gti[:-end]
  583 + self.test_value(len(gti[:-7]), 3)
  584 + self.test_value(gti[:-7].tstop(0).secs(), 1.0)
  585 + self.test_value(gti[:-7].tstop(1).secs(), 2.0)
  586 + self.test_value(gti[:-7].tstop(2).secs(), 3.0)
  587 +
  588 + # Return
  589 + return
  590 +
  591 + # Test GPhases class slicing
  592 + def _test_phases_slicing(self):
  593 + """
  594 + Test GPhases class slicing
  595 + """
  596 + # Setup phase container
  597 + phases = self._setup_phases()
  598 +
  599 + # Test phases[start:end]
  600 + self.test_value(len(phases[3:5]), 2)
  601 + self.test_value(phases[3:5].pmax(0), 4.0)
  602 + self.test_value(phases[3:5].pmax(1), 5.0)
  603 +
  604 + # Test phases[start:]
  605 + self.test_value(len(phases[7:]), 3)
  606 + self.test_value(phases[7:].pmax(0), 8.0)
  607 + self.test_value(phases[7:].pmax(1), 9.0)
  608 + self.test_value(phases[7:].pmax(2), 10.0)
  609 +
  610 + # Test phases[:end]
  611 + self.test_value(len(phases[:2]), 2)
  612 + self.test_value(phases[:2].pmax(0), 1.0)
  613 + self.test_value(phases[:2].pmax(1), 2.0)
  614 +
  615 + # Test phases[:]
  616 + self.test_value(len(phases[:]), 10)
  617 + for i in range(10):
  618 + self.test_value(phases[:].pmax(i), float(i+1))
  619 +
  620 + # Test phases[start:end:step]
  621 + self.test_value(len(phases[3:7:2]), 2)
  622 + self.test_value(phases[3:7:2].pmax(0), 4.0)
  623 + self.test_value(phases[3:7:2].pmax(1), 6.0)
  624 +
  625 + # Test phases[start:end:step]
  626 + self.test_value(len(phases[6:3:-2]), 2)
  627 + self.test_value(phases[6:3:-2].pmax(0), 7.0)
  628 + self.test_value(phases[6:3:-2].pmax(1), 5.0)
  629 +
  630 + # Test phases[-start:]
  631 + self.test_value(len(phases[-2:]), 2)
  632 + self.test_value(phases[-2:].pmax(0), 9.0)
  633 + self.test_value(phases[-2:].pmax(1), 10.0)
  634 +
  635 + # Test phases[:-end]
  636 + self.test_value(len(phases[:-7]), 3)
  637 + self.test_value(phases[:-7].pmax(0), 1.0)
  638 + self.test_value(phases[:-7].pmax(1), 2.0)
  639 + self.test_value(phases[:-7].pmax(2), 3.0)
  640 +
  641 + # Return
  642 + return
  643 +
444 644 # Test GPhotons class access operators
445 645 def _test_photons_access(self):
446 646 """
... ... @@ -584,14 +784,15 @@ class Test(gammalib.GPythonTestSuite):
584 784 # Append tests
585 785 self.append(self._test_energy, 'Test GEnergy')
586 786 self.append(self._test_energies, 'Test GEnergies')
587   - self.append(self._test_energies_access, 'Test GEnergies energy access')
588   - self.append(self._test_energies_slicing, 'Test GEnergies slicing')
589 787 self.append(self._test_time, 'Test GTime')
  788 + self.append(self._test_energies_access, 'Test GEnergies energy access')
590 789 self.append(self._test_times_access, 'Test GTimes time access')
591   - self.append(self._test_times_slicing, 'Test GTimes slicing')
592 790 self.append(self._test_photons_access, 'Test GPhotons photon access')
593   - self.append(self._test_photons_slicing, 'Test GPhotons slicing')
594 791 self.append(self._test_observations_access, 'Test GObservations observation access')
  792 + self.append(self._test_energies_slicing, 'Test GEnergies slicing')
  793 + self.append(self._test_ebounds_slicing, 'Test GEbounds slicing')
  794 + self.append(self._test_times_slicing, 'Test GTimes slicing')
  795 + self.append(self._test_photons_slicing, 'Test GPhotons slicing')
595 796 self.append(self._test_observations_slicing, 'Test GObservations slicing')
596 797  
597 798 # Return
... ...