00001 #include <psalg/psalg.h>
00002 #include <boost/python.hpp>
00003 #include <ndarray/ndarray.h>
00004 #include <algorithm>
00005 #include "AreaDetHist.h"
00006 #include "Hist.h"
00007
00008
00009
00010 namespace {
00011
00012 ndarray<unsigned,2> count_hits_1(const ndarray<const unsigned,2>&input,
00013 unsigned threshold) {
00014
00015
00016 ndarray<unsigned,2> output = make_ndarray<unsigned>(input.shape()[0],
00017 input.shape()[1]);
00018 std::fill_n(output.begin(), output.size(), 0);
00019
00020
00021 psalg::count_hits(input,threshold,output);
00022
00023
00024 return output;
00025 }
00026
00027
00028 ndarray<unsigned,2> count_hits_2(const ndarray<const unsigned,2>& input,
00029 const ndarray<const unsigned,2>& threshold) {
00030
00031
00032 ndarray<unsigned,2> output = make_ndarray<unsigned>(input.shape()[0],
00033 input.shape()[1]);
00034 std::fill_n(output.begin(), output.size(), 0);
00035
00036
00037 psalg::count_hits(input,threshold,output);
00038
00039
00040 return output;
00041 }
00042
00043
00044
00045
00046 ndarray<unsigned,2> sum_hits_1(const ndarray<const unsigned,2>& input,
00047 unsigned threshold, unsigned offset) {
00048
00049
00050 ndarray<unsigned,2> output = make_ndarray<unsigned>(input.shape()[0],
00051 input.shape()[1]);
00052 std::fill_n(output.begin(), output.size(), 0);
00053
00054
00055 psalg::sum_hits(input,threshold,offset,output);
00056
00057
00058 return output;
00059 }
00060
00061
00062 ndarray<unsigned,2> sum_hits_2 (const ndarray<const unsigned,2>& input,
00063 const ndarray<const unsigned,2>& threshold,
00064 unsigned offset) {
00065
00066
00067 ndarray<unsigned,2> output = make_ndarray<unsigned>(input.shape()[0],
00068 input.shape()[1]);
00069 std::fill_n(output.begin(), output.size(), 0);
00070
00071
00072 psalg::sum_hits(input,threshold,offset,output);
00073
00074
00075 return output;
00076 }
00077
00078
00079
00080
00081 ndarray<unsigned,2> count_excess_1(const ndarray<const unsigned,2>& input ,
00082 unsigned threshold) {
00083
00084
00085 ndarray<unsigned,2> output = make_ndarray<unsigned>(input.shape()[0],
00086 input.shape()[1]);
00087 std::fill_n(output.begin(), output.size(), 0);
00088
00089
00090 psalg::count_excess(input,threshold,output);
00091
00092
00093 return output;
00094 }
00095
00096
00097 ndarray<unsigned,2> count_excess_2 (const ndarray<const unsigned,2>& input,
00098 const ndarray<const unsigned,2>& threshold) {
00099
00100
00101 ndarray<unsigned,2> output = make_ndarray<unsigned>(input.shape()[0],
00102 input.shape()[1]);
00103 std::fill_n(output.begin(), output.size(), 0);
00104
00105
00106 psalg::count_excess(input,threshold,output);
00107
00108
00109 return output;
00110 }
00111
00112
00113
00114
00115 ndarray<unsigned,2> sum_excess_1(const ndarray<const unsigned,2>& input,
00116 unsigned threshold,
00117 unsigned offset) {
00118
00119
00120 ndarray<unsigned,2> output = make_ndarray<unsigned>(input.shape()[0],
00121 input.shape()[1]);
00122 std::fill_n(output.begin(), output.size(), 0);
00123
00124
00125 psalg::sum_excess(input,threshold,offset,output);
00126
00127
00128 return output;
00129 }
00130
00131
00132 ndarray<unsigned,2> sum_excess_2(const ndarray<const unsigned,2>& input,
00133 const ndarray<const unsigned,2>& threshold,
00134 unsigned offset) {
00135
00136
00137 ndarray<unsigned,2> output = make_ndarray<unsigned>(input.shape()[0],
00138 input.shape()[1]);
00139 std::fill_n(output.begin(), output.size(), 0);
00140
00141
00142 psalg::sum_excess(input,threshold,offset,output);
00143
00144
00145 return output;
00146 }
00147
00148
00149
00150
00151 ndarray<double,1> rolling_average_int32_t(const ndarray<const int32_t,1>& new_data,
00152 const ndarray<const double,1>& old_data,
00153 double fraction) {
00154
00155
00156 ndarray<double,1> avg = old_data.copy();
00157
00158
00159 psalg::rolling_average<int32_t>(new_data,avg,fraction);
00160
00161
00162 return avg;
00163 }
00164
00165
00166 ndarray<double,1> rolling_average_double(const ndarray<const double,1>& new_data,
00167 const ndarray<const double,1>& old_data,
00168 double fraction) {
00169
00170
00171 ndarray<double,1> avg = old_data.copy();
00172
00173
00174 psalg::rolling_average<double>(new_data,avg,fraction);
00175
00176
00177 return avg;
00178 }
00179
00180 };
00181
00182
00183 BOOST_PYTHON_MODULE(pypsalg_cpp)
00184 {
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195 ndarray<double,1> (*fimp)(const ndarray<const double,1>&,const ndarray<const double,1>&)
00196 = &psalg::finite_impulse_response;
00197 boost::python::def("finite_impulse_response",fimp,
00198 "Finite impulse response filter \n"
00199 "Creates the 1-dimensional filtered response array from the \n"
00200 "sample input array and the impulse response filter array."
00201 );
00202
00203
00204
00205 ndarray<double,1>(*moments_1)(const ndarray<const double,1>&,double,double) = &psalg::moments;
00206 ndarray<double,1>(*moments_2)(const ndarray<const double,1>&,const ndarray<const double,1>&,
00207 double,double) = &psalg::moments;
00208 ndarray<double,1>(*moments_3)(const ndarray<const unsigned,2>&,double) = &psalg::moments;
00209 ndarray<double,1>(*moments_4)(const ndarray<const unsigned,2>&,double,unsigned [][2]) = &psalg::moments;
00210 ndarray<double,1>(*moments_5)(const ndarray<const double,2>&,double) = &psalg::moments;
00211 ndarray<double,1>(*moments_6)(const ndarray<const double,2>&,double,unsigned [][2]) = &psalg::moments;
00212 ndarray<double,1>(*moments_7)(const ndarray<const unsigned,2>&,const ndarray<const unsigned,1>&,
00213 const ndarray<const unsigned,2>&,double) = &psalg::moments;
00214 ndarray<double,1>(*moments_8)(const ndarray<const unsigned,2>&,const ndarray<const unsigned,1>&,
00215 const ndarray<const unsigned,2>&,double,unsigned [][2]) = &psalg::moments;
00216 ndarray<double,1>(*moments_9)(const ndarray<const double,2>&,const ndarray<const unsigned,1>&,
00217 const ndarray<const unsigned,2>&,double) = &psalg::moments;
00218 ndarray<double,1>(*moments_10)(const ndarray<const double,2>&,const ndarray<const unsigned,1>&,
00219 const ndarray<const unsigned,2>&,double,unsigned [][2]) = &psalg::moments;
00220
00221 boost::python::def("moments_1D",moments_1,
00222 "Calculates moments of 1D array. \n"
00223 "Returns (sum of bin,\n"
00224 " sum of bin values*bin position, \n"
00225 " sum of bin values*bin position**2)\n\n"
00226 "The bin_position is calculate as bin_offset + bin_index*bin_scale"
00227 );
00228 boost::python::def("moments_1D",moments_2);
00229
00230
00231 boost::python::def("moments_2D",moments_3,
00232 "Calculate moments of 2-D array\n\n"
00233 "The moments are { sum of bins, \n"
00234 " sum of bin_values,\n"
00235 " sum of bin_values**2,\n"
00236 " sum of bin_value*bin_xposition, \n"
00237 " sum of bin_value*bin_yposition } \n\n"
00238 "The bin_value is calculated as the array element value minus the \n"
00239 "value_offset. The bin_xposition(yposition) is simply the array index \n"
00240 "for dimension 1(0).\n\n"
00241 "Integral = moments[1] \n"
00242 "Mean = moments[1]/moments[0] \n"
00243 "RMS = sqrt((moments[2]/moments[0] - (moments[1]/moments[0])**2)\n"
00244 "Contrast = sqrt(moments[0]*moments[2]/moments[1]**2 - 1) \n"
00245 "X-center-of-mass = moments[3]/moments[1]\n"
00246 "Y-center-of-mass = moments[4]/moments[1]\n"
00247 );
00248 boost::python::def("moments_2D",moments_4);
00249 boost::python::def("moments_2D",moments_5);
00250 boost::python::def("moments_2D",moments_6);
00251 boost::python::def("moments_2D",moments_7);
00252 boost::python::def("moments_2D",moments_8);
00253 boost::python::def("moments_2D",moments_9);
00254 boost::python::def("moments_2D",moments_10);
00255
00256
00257
00258
00259 boost::python::def("find_edges",&psalg::find_edges,
00260 "EdgeFinder \n\n"
00261 "Waveform pulse edge finder\n\n"
00262 "Generates an array of hit times and amplitudes for waveform\n"
00263 "leading (trailing) edges using a constant fraction discriminator\n"
00264 "algorithm. The baseline and minimum amplitude threshold are used\n"
00265 "for discriminating hits. The pulse height fraction at which the hit\n"
00266 "time is derived is also required as input. Note that if the threshold\n"
00267 "is less than the baseline value, then leading edges are 'falling' and \n"
00268 "trailing edges are \"rising\". In order for two pulses to be discriminated,\n"
00269 "the waveform samples below the two pulses must fall below (or above for\n"
00270 "negative pulses) the fractional value of the threshold; i.e. \n"
00271 "waveform[i] < fraction*(threshold+baseline).\n\n"
00272 "The results are stored in a 2D array such that result[i][0] is the time \n"
00273 "(waveform sample) of the i'th hit and result[i][1] is the maximum amplitude \n"
00274 "of the i'th hit."
00275 );
00276
00277
00278
00279 boost::python::def("count_hits",count_hits_1);
00280 boost::python::def("count_hits",count_hits_2,
00281 "Image hit finder\n\n"
00282 "Generates a 2D map of hits, where a hit is defined as a local maximum above"
00283 "some threshold. The threshold can be a single value or a map of values.\n\n"
00284 "The results are stored in a 2D array with the same dimensions as the input image.\n\n"
00285 "Increment an output element when the input element is a local maximum and is\n"
00286 "above threshold. Threshold is either a constant or a map of threshold values."
00287 );
00288
00289
00290
00291
00292 boost::python::def("sum_hits",sum_hits_1);
00293 boost::python::def("sum_hits",sum_hits_2,
00294 "Sum the input element's value into the output element when the input is a local\n"
00295 "maximum and is above threshold. The value of offset is subtracted from the\n"
00296 "input value before adding to the output."
00297 );
00298
00299
00300
00301
00302 boost::python::def("count_excess",count_excess_1);
00303 boost::python::def("count_excess",count_excess_2,
00304 "Increment output elements for all input elements above threshold.\n"
00305 "The threshold can be a single value or a map of values."
00306 );
00307
00308
00309
00310
00311 boost::python::def("sum_excess",sum_excess_1);
00312 boost::python::def("sum_excess",sum_excess_2,
00313 "Sum the input element's value into the output element when the input is\n"
00314 "above threshold. The value of offset is subtracted from the input value \n"
00315 "before adding to the output."
00316 );
00317
00318
00319
00320
00321 double (*find_peak_1)(const ndarray<const double,1>&,double,const ndarray<const double,1>&,unsigned&)
00322 = &psalg::find_peak;
00323 double (*find_peak_2)(const ndarray<const double,1>&,const ndarray<const double,1>&,
00324 const ndarray<const double,1>&,unsigned&) = &psalg::find_peak;
00325
00326 boost::python::def("find_peak",find_peak_1);
00327 boost::python::def("find_peak",find_peak_2);
00328 boost::python::def("find_peaks",&psalg::find_peaks,
00329 "1D Peak find\n\n"
00330 "Find the peak value in the array.\n"
00331 "Variable norm is number of entries summed into each bin."
00332 );
00333
00334
00335
00336
00337 ndarray<double,1> (*line_fit_1)(const ndarray<const double,1>&,const ndarray<const unsigned,1>&,double)
00338 = &psalg::line_fit;
00339 ndarray<double,1> (*line_fit_2)(const ndarray<const double,1>&,const ndarray<const unsigned,1>&,
00340 const ndarray<const double,1>&) = &psalg::line_fit;
00341
00342 boost::python::def("line_fit",line_fit_1);
00343 boost::python::def("line_fit",line_fit_2,
00344 "Variable norm is number of entries summed into each bin."
00345 );
00346
00347
00348
00349
00350 double (*dist_rms_1)(const ndarray<const double,1>&,double,const ndarray<const double,1>&)
00351 = &psalg::dist_rms;
00352 double (*dist_rms_2)(const ndarray<const double,1>&,const ndarray<const double,1>&,
00353 const ndarray<const double,1>&) = &psalg::dist_rms;
00354
00355 boost::python::def("dist_rms",dist_rms_2);
00356 boost::python::def("dist_rms",dist_rms_1,
00357 "Distribution Root-mean-square\n\n"
00358 "Width of distribution is estimated by the root-mean-square.\n"
00359 "A baseline polynomial { f(i) = b[0] + i*b[1] + i*i*b[2] + ... }\n"
00360 "is subtracted from each point prior to the rms calculation.\n"
00361 "Points below the baseline contribute negatively to the rms."
00362 );
00363
00364
00365
00366
00367 double (*dist_fwhm_1)(const ndarray<const double,1>&,double,const ndarray<const double,1>&)
00368 = &psalg::dist_fwhm;
00369 double (*dist_fwhm_2)(const ndarray<const double,1>&,const ndarray<const double,1>&,
00370 const ndarray<const double,1>&) = &psalg::dist_fwhm;
00371
00372 boost::python::def("dist_fwhm",dist_fwhm_2);
00373 boost::python::def("dist_fwhm",dist_fwhm_1,
00374 "Distribution Full-width-half-maximum\n\n"
00375 "Width of distribution is estimated by the minimum full-width\n"
00376 "half-maximum around the peak value."
00377 );
00378
00379
00380
00381
00382 ndarray<double,1> (*parab_interp_1)(const ndarray<const double,1>&,double,const ndarray<const double,1>&)
00383 = &psalg::parab_interp;
00384 ndarray<double,1> (*parab_interp_2)(const ndarray<const double,1>&,const ndarray<const double,1>&,
00385 const ndarray<const double,1>&) = &psalg::parab_interp;
00386
00387 boost::python::def("parab_interp",parab_interp_2);
00388 boost::python::def("parab_interp",parab_interp_1,
00389 "Parabolic interpolation\n\n"
00390 "Perform a quadratic interpolation around the peak of the distribution.\n"
00391 "A baseline polynomial { f(i) = b[0] + i*b[1] + i*i*b[2] + ... }\n"
00392 "is subtracted from each point prior to the calculation.\n"
00393 "Return value is an array of [ amplitude, position ]"
00394 );
00395
00396
00397
00398
00399 ndarray<double,1> (*parab_fit_1)(const ndarray<const double,1>&) = psalg::parab_fit;
00400 ndarray<double,1> (*parab_fit_2)(const ndarray<const double,1>&,unsigned,double) = psalg::parab_fit;
00401
00402 boost::python::def("parab_fit", parab_fit_2);
00403 boost::python::def("parab_fit", parab_fit_1,
00404 "Perform a least squares fit of the waveform to a 2nd-order polynomial.\n"
00405 "Assumes all points have equal uncertainty.\n"
00406 "Return value is an array of polynomial coefficients, such that \n"
00407 "y(x) = a[0] + a[1]*x + a[2]*x**2\n"
00408 "Maximum/minimum value is a[0]-a[1]*a[1]/(4*a[2]) at x=-a[1]/(2*a[2]).\n"
00409 "Return array is [0,0,0] when fit fails."
00410 );
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450 boost::python::def("commonmode_lroe", &psalg::commonModeLROE,
00451 "Calculate a common-mode in left-right halves for odd-even pixels");
00452 boost::python::def("project",&psalg::project,
00453 "Project ndarray\n\n"
00454 "Creates a 1-dimensional response array from the"
00455 "projection of an N-dimensional ndarray over a region of interest (inclusive).\n\n"
00456 "pdim is the dimension to project onto."
00457 "All other dimensions are integrated over the ROI"
00458 );
00459
00460
00461
00462
00463 boost::python::def("rolling_average",rolling_average_int32_t);
00464 boost::python::def("rolling_average",rolling_average_double,
00465 "Accumulate a rolling average where each accumulation contributes\n"
00466 "a fixed fraction to the average."
00467 );
00468
00469 static const char AreaDetClassDoc[] =
00470 "Class to manage histogramming of area detector ADU values";
00471 static const char AreaDetCtorDoc[] =
00472 "Arguments:\n"
00473 "- a 3 dimensional numpy array of doubles\n"
00474 "- int: lower limit of histogram\n"
00475 "- int: upper limit of histogram\n"
00476 "- bool: indicating whether to find isolated photons\n"
00477 "- double: threshold that the pixel should be above all neighbors\n"
00478 " (valid only if the findIsolatedPhotons set to True)";
00479 static const char AreaDetUpdateDoc[] =
00480 "Arguments:\n"
00481 "- a 3 dimensional numpy array of doubles that will be used\n"
00482 " to update histogram";
00483 static const char AreaDetGetDoc[] =
00484 "No arguments. Return histogram as numpy array.";
00485 boost::python::class_<pypsalg::AreaDetHist>("AreaDetHist", AreaDetClassDoc, boost::python::init<ndarray<double,3>,int,int,int,bool,double>(AreaDetCtorDoc))
00486 .def("update",&pypsalg::AreaDetHist::update, AreaDetUpdateDoc)
00487 .def("get",&pypsalg::AreaDetHist::get, AreaDetGetDoc)
00488 ;
00489
00490 void(pypsalg::Hist1D::* hist1DFillVal)(double,double) = &pypsalg::Hist1D::fill;
00491 void(pypsalg::Hist1D::* hist1DFillVec)(ndarray<double,1>,double) = &pypsalg::Hist1D::fill;
00492 void(pypsalg::Hist1D::* hist1DFillVecWeights)(ndarray<double,1>,ndarray<double,1>) = &pypsalg::Hist1D::fill;
00493
00494 static const char Hist1DClassDoc[] =
00495 "Class to manage 1D histograms";
00496 static const char Hist1DCtorDoc[] =
00497 "Arguments:\n"
00498 "- number of bins (integer)\n"
00499 "- lower edge of histogram (double)\n"
00500 "- upper edge of histogram (double)";
00501 static const char Hist1DFillValDoc[] =
00502 "Arguments:\n"
00503 "- coordinate of bin to increment (double)\n"
00504 "- amount to increment bin by (double)";
00505 static const char Hist1DFillVecDoc[] =
00506 "Arguments:\n"
00507 "- coordinates of bins to increment (numpy array of doubles)\n"
00508 "- single value to increment all bins by (double)";
00509 static const char Hist1DFillVecWeightsDoc[] =
00510 "- values of bins to increment (numpy array of doubles)\n"
00511 "- weights of bins (numpy array of doubles). array must be same\n"
00512 " length as array of values";
00513 static const char Hist1DGetDoc[] =
00514 "No arguments. Return histogram as numpy array.";
00515 static const char Hist1DAxisDoc[] =
00516 "No arguments. Return bin-centers as numpy array.";
00517 boost::python::class_<pypsalg::Hist1D>("Hist1D", Hist1DClassDoc, boost::python::init<int,double,double>(Hist1DCtorDoc))
00518 .def("fill",hist1DFillVal, Hist1DFillValDoc)
00519 .def("fill",hist1DFillVec, Hist1DFillVecDoc)
00520 .def("fill",hist1DFillVecWeights, Hist1DFillVecWeightsDoc)
00521 .def("get",&pypsalg::Hist1D::get, Hist1DGetDoc)
00522 .def("xaxis",&pypsalg::Hist1D::xaxis, Hist1DAxisDoc)
00523 ;
00524
00525 void(pypsalg::Hist2D::* hist2DFillVal)(double,double,double) = &pypsalg::Hist2D::fill;
00526 void(pypsalg::Hist2D::* hist2DFillVec)(ndarray<double,2>,double) = &pypsalg::Hist2D::fill;
00527 void(pypsalg::Hist2D::* hist2DFillVecWeights)(ndarray<double,2>) = &pypsalg::Hist2D::fill;
00528 void(pypsalg::Hist2D::* hist2DFill2VecWeight)(ndarray<double,1>,ndarray<double,1>,double) = &pypsalg::Hist2D::fill;
00529 void(pypsalg::Hist2D::* hist2DFill2VecWeights)(ndarray<double,1>,ndarray<double,1>,ndarray<double,1>) = &pypsalg::Hist2D::fill;
00530
00531 static const char Hist2DClassDoc[] =
00532 "Class to manage 2D histograms";
00533 static const char Hist2DCtorDoc[] =
00534 "Arguments:\n"
00535 "- number of X-bins (integer)\n"
00536 "- lower X-edge of histogram (double)\n"
00537 "- upper X-edge of histogram (double)\n"
00538 "- number of Y-bins (integer)\n"
00539 "- lower Y-edge of histogram (double)\n"
00540 "- upper Y-edge of histogram (double)";
00541 static const char Hist2DFillValDoc[] =
00542 "Arguments:\n"
00543 "- X-coordinate of bin to increment (double)\n"
00544 "- Y-coordinate of bin to increment (double)\n"
00545 "- amount to increment bin by (double)";
00546 static const char Hist2DFillVecDoc[] =
00547 "Arguments:\n"
00548 "- X/Y array of coordinates of bins to increment (2D numpy array of"
00549 " doubles) with shape (NPoints,2)\n"
00550 "- single value to increment all bins by (double)";
00551 static const char Hist2DFillVecWeightsDoc[] =
00552 "Arguments:\n"
00553 "- X/Y/Weight array of coordinates of bins to increment (2D numpy\n"
00554 " array of doubles) with shape (NPoints,3)";
00555 static const char Hist2DFill2VecWeightDoc[] =
00556 "Arguments:\n"
00557 "- 1D numpy array of X coordinates\n"
00558 "- 1D numpy array of y coordinates\n"
00559 "- amount to increment all bins by (double)";
00560 static const char Hist2DFill2VecWeightsDoc[] =
00561 "Arguments:\n"
00562 "- 1D numpy array of X coordinates (double)\n"
00563 "- 1D numpy array of y coordinates (double)\n"
00564 "- 1D numpy array of weights (double)";
00565 static const char Hist2DGetDoc[] =
00566 "No arguments. Return histogram as numpy array.";
00567 static const char Hist2DAxisDoc[] =
00568 "No arguments. Return bin-centers as numpy array.";
00569 boost::python::class_<pypsalg::Hist2D>("Hist2D", Hist2DClassDoc, boost::python::init<int,double,double,int,double,double>(Hist2DCtorDoc))
00570 .def("fill",hist2DFillVal, Hist2DFillValDoc)
00571 .def("fill",hist2DFillVec, Hist2DFillVecDoc)
00572 .def("fill",hist2DFillVecWeights, Hist2DFillVecWeightsDoc)
00573 .def("fill",hist2DFill2VecWeight, Hist2DFill2VecWeightDoc)
00574 .def("fill",hist2DFill2VecWeights, Hist2DFill2VecWeightsDoc)
00575 .def("get",&pypsalg::Hist2D::get, Hist2DGetDoc)
00576 .def("xaxis",&pypsalg::Hist2D::xaxis, Hist2DAxisDoc)
00577 .def("yaxis",&pypsalg::Hist2D::yaxis, Hist2DAxisDoc)
00578 ;
00579
00580 }