Commit f3da83abb4d8a0f71f8d34925446084a3cfb1dc7

Authored by Jürgen Knödlseder
1 parent 285fb246

Enhance precision of GCTAEdisp2D::compute_ebounds_src

ChangeLog
1   -2017-03-24
  1 +2017-03-27
2 2  
3 3 * Version 1.3.0 released
4 4 =========================
5 5  
  6 + Enhance precision of GCTAEdisp2D::compute_ebounds_src
6 7 Add conversion methods between GEbounds and GEnergies (#1977)
7 8 Add GModelSpectralSmoothBrokenPlaw class (#1948)
8 9 Implement lazy loading of energy dispersion in GCTAEdisp2D (#1940)
... ...
1 1 New Features and Important Changes in GammaLib 1.3.0.dev1
2 2  
3   -24 March 2017
  3 +27 March 2017
4 4  
5 5  
6 6 1. Introduction
... ... @@ -154,6 +154,12 @@ Lazy loading of the energy dispersion in GCTAEdisp2D has been implemented by
154 154 adding a fetch() method to the class. Now the energy dispersion is only loaded
155 155 when needed, for example for function evaluation or Monte Carlo sampling (#1940).
156 156  
  157 +The GCTAEdisp2D::compute_ebounds_src method now loops over the migration to
  158 +enhance the precision in the true energy boundaries that are returned by this
  159 +method. In addition, the validity range of the table is now stored in members
  160 +of the class, and a non-zero energy dispersion is only returned for valid
  161 +elements of the response table.
  162 +
157 163  
158 164 21. COMPTEL interface
159 165 ---------------------
... ...
inst/cta/include/GCTAEdisp2D.hpp
... ... @@ -119,7 +119,10 @@ private:
119 119 const double& phi = 0.0,
120 120 const double& zenith = 0.0,
121 121 const double& azimuth = 0.0) const;
  122 + void set_indices(void);
  123 + void set_boundaries(void);
122 124 void set_max_edisp(void) const;
  125 + void normalize_table(void);
123 126  
124 127 // Protected classes
125 128 class edisp_kern : public GFunction {
... ... @@ -138,13 +141,19 @@ private:
138 141 };
139 142  
140 143 // Members
141   - mutable GFilename m_filename; //!< Name of Edisp response file
142   - GCTAResponseTable m_edisp; //!< Edisp response table
143   - mutable bool m_fetched; //!< Signals that Edisp has been fetched
144   - int m_inx_etrue; //!< True energy index
145   - int m_inx_migra; //!< Migration index
146   - int m_inx_theta; //!< Theta index
147   - int m_inx_matrix; //!< Matrix
  144 + mutable GFilename m_filename; //!< Name of Edisp response file
  145 + GCTAResponseTable m_edisp; //!< Edisp response table
  146 + mutable bool m_fetched; //!< Signals that Edisp has been fetched
  147 + int m_inx_etrue; //!< True energy index
  148 + int m_inx_migra; //!< Migration index
  149 + int m_inx_theta; //!< Theta index
  150 + int m_inx_matrix; //!< Matrix
  151 + double m_logEsrc_min; //!< Minimum logE (log10(E/TeV))
  152 + double m_logEsrc_max; //!< Maximum logE (log10(E/TeV))
  153 + double m_migra_min; //!< Minimum migration
  154 + double m_migra_max; //!< Maximum migration
  155 + double m_theta_min; //!< Minimum theta (radians)
  156 + double m_theta_max; //!< Maximum theta (radians)
148 157  
149 158 // Computation cache
150 159 mutable bool m_ebounds_obs_computed;
... ... @@ -198,17 +207,4 @@ const GCTAResponseTable&amp; GCTAEdisp2D::table(void) const
198 207 return (m_edisp);
199 208 }
200 209  
201   -
202   -/***********************************************************************//**
203   - * @brief Set response table
204   - *
205   - * @param[in] table Response table.
206   - ***************************************************************************/
207   -inline
208   -void GCTAEdisp2D::table(const GCTAResponseTable& table)
209   -{
210   - m_edisp = table;
211   - return;
212   -}
213   -
214 210 #endif /* GCTAEDISP2D_HPP */
... ...
inst/cta/src/GCTAEdisp2D.cpp
... ... @@ -198,22 +198,33 @@ double GCTAEdisp2D::operator()(const double&amp; logEobs,
198 198 // Initalize edisp
199 199 double edisp = 0.0;
200 200  
201   - // Compute Eobs/Esrc
202   - double EobsOverEsrc = std::exp((logEobs-logEsrc) * gammalib::ln10);
  201 + // Continue only if logEsrc and theta are in validity range
  202 + if ((logEsrc >= m_logEsrc_min) && (logEsrc <= m_logEsrc_max) &&
  203 + (theta >= m_theta_min) && (theta <= m_theta_max)) {
203 204  
204   - // Setup argument vector
205   - double arg[3];
206   - arg[m_inx_etrue] = logEsrc;
207   - arg[m_inx_migra] = EobsOverEsrc;
208   - arg[m_inx_theta] = theta;
  205 + // Compute Eobs/Esrc
  206 + double EobsOverEsrc = std::exp((logEobs-logEsrc) * gammalib::ln10);
209 207  
210   - // Compute edisp
211   - edisp = m_edisp(m_inx_matrix, arg[0], arg[1], arg[2]);
  208 + // Continue only if migration is in validity range
  209 + if ((EobsOverEsrc >= m_migra_min) && (EobsOverEsrc <= m_migra_max)) {
212 210  
213   - // Make sure that energy dispersion is non-negative
214   - if (edisp < 0.0) {
215   - edisp = 0.0;
216   - }
  211 + // Setup argument vector
  212 + double arg[3];
  213 + arg[m_inx_etrue] = logEsrc;
  214 + arg[m_inx_migra] = EobsOverEsrc;
  215 + arg[m_inx_theta] = theta;
  216 +
  217 + // Compute edisp
  218 + edisp = m_edisp(m_inx_matrix, arg[0], arg[1], arg[2]);
  219 +
  220 + // Make sure that energy dispersion is non-negative
  221 + if (edisp < 0.0) {
  222 + edisp = 0.0;
  223 + }
  224 +
  225 + } // endif: migration was in valid range
  226 +
  227 + } // endif: true energy and offset angle was in valid range
217 228  
218 229 // Return
219 230 return edisp;
... ... @@ -260,6 +271,41 @@ GCTAEdisp2D* GCTAEdisp2D::clone(void) const
260 271  
261 272  
262 273 /***********************************************************************//**
  274 + * @brief Assign response table
  275 + *
  276 + * @param[in] table Response table.
  277 + *
  278 + * Assigns the response table for an energy dispersion.
  279 + ***************************************************************************/
  280 +void GCTAEdisp2D::table(const GCTAResponseTable& table)
  281 +{
  282 + // Assign response table
  283 + m_edisp = table;
  284 +
  285 + // Set indices
  286 + set_indices();
  287 +
  288 + // Set true energy axis to logarithmic scale
  289 + m_edisp.axis_log10(m_inx_etrue);
  290 +
  291 + // Set offset angle axis to radians
  292 + m_edisp.axis_radians(m_inx_theta);
  293 +
  294 + // Normalize energy dispersion table
  295 + normalize_table();
  296 +
  297 + // Set maximum energy dispersion value
  298 + set_max_edisp();
  299 +
  300 + // Set table boundaries
  301 + set_boundaries();
  302 +
  303 + // Return
  304 + return;
  305 +}
  306 +
  307 +
  308 +/***********************************************************************//**
263 309 * @brief Read energy dispersion from FITS table
264 310 *
265 311 * @param[in] table FITS table.
... ... @@ -291,12 +337,6 @@ void GCTAEdisp2D::read(const GFitsTable&amp; table)
291 337 // Read energy dispersion table
292 338 m_edisp.read(table);
293 339  
294   - // Get mandatory indices (throw exception if not found)
295   - m_inx_etrue = m_edisp.axis("ETRUE");
296   - m_inx_migra = m_edisp.axis("MIGRA");
297   - m_inx_theta = m_edisp.axis("THETA");
298   - m_inx_matrix = m_edisp.table("MATRIX");
299   -
300 340 // Throw an exception if the table is not two-dimensional
301 341 if (m_edisp.axes() != 3) {
302 342 std::string msg = "Expected three-dimensional energy dispersion "
... ... @@ -307,94 +347,24 @@ void GCTAEdisp2D::read(const GFitsTable&amp; table)
307 347 throw GException::invalid_value(G_READ, msg);
308 348 }
309 349  
  350 + // Set table indices
  351 + set_indices();
  352 +
310 353 // Set true energy axis to logarithmic scale
311 354 m_edisp.axis_log10(m_inx_etrue);
312 355  
313 356 // Set offset angle axis to radians
314 357 m_edisp.axis_radians(m_inx_theta);
315 358  
316   - // Get axes dimensions
317   - int etrue_size = m_edisp.axis_bins(m_inx_etrue);
318   - int migra_size = m_edisp.axis_bins(m_inx_migra);
319   - int theta_size = m_edisp.axis_bins(m_inx_theta);
320   -
321   - // Now normalize the migration matrix vectors. We do this by integrating
322   - // for each true energy and offset angle the measured energy. We perform
323   - // here two passes as the ebounds_obs() method depends on the content of
324   - // the matrix, and by having two passes we are sure that we get a
325   - // consistent energy interval.
326   - for (int pass = 0; pass < 2; ++pass) {
327   -
328   - // Initialise sums
329   - std::vector<double> sums;
330   - sums.reserve(theta_size*etrue_size);
331   -
332   - // Loop over offset angle.
333   - for (int i_theta = 0; i_theta < theta_size; ++i_theta) {
334   -
335   - // Get offset angle (in radians)
336   - double theta = 0.5 * (m_edisp.axis_lo(m_inx_theta,i_theta) +
337   - m_edisp.axis_hi(m_inx_theta,i_theta)) *
338   - gammalib::deg2rad;
339   -
340   - // Loop over true photon energy
341   - for (int i_etrue = 0; i_etrue < etrue_size; ++i_etrue) {
342   -
343   - // Get energy
344   - double emin = std::log10(m_edisp.axis_lo(m_inx_etrue,i_etrue));
345   - double emax = std::log10(m_edisp.axis_hi(m_inx_etrue,i_etrue));
346   - double logEsrc = 0.5*(emin+emax);
347   -
348   - // Initialise integration
349   - double sum = 0.0;
350   -
351   - // Get integration boundaries
352   - GEbounds ebounds = ebounds_obs(logEsrc, theta);
353   -
354   - // Loop over all energy intervals
355   - for (int i = 0; i < ebounds.size(); ++i) {
356   -
357   - // Get energy boundaries
358   - emin = ebounds.emin(i).log10TeV();
359   - emax = ebounds.emax(i).log10TeV();
360   -
361   - // Setup integration function
362   - edisp_kern integrand(this, logEsrc, theta);
363   - GIntegral integral(&integrand);
364   -
365   - // Set integration precision
366   - integral.eps(1.0e-6);
367   -
368   - // Do Romberg integration
369   - sum += integral.romberg(emin, emax);
370   -
371   - } // endfor: looped over energy intervals
372   -
373   - // Store sum
374   - sums.push_back(sum);
375   -
376   - } // endfor: looped over Etrue
377   -
378   - } // endfor: looped over theta
379   -
380   - // Normalize vectors of migration matrix for all etrue and theta.
381   - for (int i_theta = 0, inx = 0; i_theta < theta_size; ++i_theta) {
382   - for (int i_etrue = 0; i_etrue < etrue_size; ++i_etrue, ++inx) {
383   - double sum = sums[inx];
384   - if (sum > 0.0) {
385   - int offset = i_etrue + (i_theta*migra_size) * etrue_size;
386   - for (int k = 0, i = offset; k < migra_size; ++k, i += etrue_size) {
387   - m_edisp(m_inx_matrix,i) /= sum;
388   - }
389   - }
390   - }
391   - }
392   -
393   - } // endfor: made 2 passes
  359 + // Normalize energy dispersion table
  360 + normalize_table();
394 361  
395 362 // Set maximum energy dispersion value
396 363 set_max_edisp();
397 364  
  365 + // Set table boundaries
  366 + set_boundaries();
  367 +
398 368 // Return
399 369 return;
400 370 }
... ... @@ -857,11 +827,17 @@ void GCTAEdisp2D::init_members(void)
857 827 // Initialise members
858 828 m_filename.clear();
859 829 m_edisp.clear();
860   - m_fetched = false;
861   - m_inx_etrue = 0;
862   - m_inx_migra = 1;
863   - m_inx_theta = 2;
864   - m_inx_matrix = 0;
  830 + m_fetched = false;
  831 + m_inx_etrue = 0;
  832 + m_inx_migra = 1;
  833 + m_inx_theta = 2;
  834 + m_inx_matrix = 0;
  835 + m_logEsrc_min = 0.0;
  836 + m_logEsrc_max = 0.0;
  837 + m_migra_min = 0.0;
  838 + m_migra_max = 0.0;
  839 + m_theta_min = 0.0;
  840 + m_theta_max = 0.0;
865 841  
866 842 // Initialise computation cache
867 843 m_ebounds_obs_computed = false;
... ... @@ -889,13 +865,19 @@ void GCTAEdisp2D::init_members(void)
889 865 void GCTAEdisp2D::copy_members(const GCTAEdisp2D& edisp)
890 866 {
891 867 // Copy members
892   - m_filename = edisp.m_filename;
893   - m_edisp = edisp.m_edisp;
894   - m_fetched = edisp.m_fetched;
895   - m_inx_etrue = edisp.m_inx_etrue;
896   - m_inx_migra = edisp.m_inx_migra;
897   - m_inx_theta = edisp.m_inx_theta;
898   - m_inx_matrix = edisp.m_inx_matrix;
  868 + m_filename = edisp.m_filename;
  869 + m_edisp = edisp.m_edisp;
  870 + m_fetched = edisp.m_fetched;
  871 + m_inx_etrue = edisp.m_inx_etrue;
  872 + m_inx_migra = edisp.m_inx_migra;
  873 + m_inx_theta = edisp.m_inx_theta;
  874 + m_inx_matrix = edisp.m_inx_matrix;
  875 + m_logEsrc_min = edisp.m_logEsrc_min;
  876 + m_logEsrc_max = edisp.m_logEsrc_max;
  877 + m_migra_min = edisp.m_migra_min;
  878 + m_migra_max = edisp.m_migra_max;
  879 + m_theta_min = edisp.m_theta_min;
  880 + m_theta_max = edisp.m_theta_max;
899 881  
900 882 // Copy computation cache
901 883 m_ebounds_obs_computed = edisp.m_ebounds_obs_computed;
... ... @@ -1063,7 +1045,8 @@ void GCTAEdisp2D::compute_ebounds_src(const double&amp; theta,
1063 1045 const double eps = 1.0e-12;
1064 1046  
1065 1047 // Determine number of true energy bins
1066   - int n_bins = m_edisp.axis_bins(m_inx_etrue);
  1048 + int n_bins = m_edisp.axis_bins(m_inx_etrue);
  1049 + int n_migras = m_edisp.axis_bins(m_inx_migra);
1067 1050  
1068 1051 // Loop over Eobs
1069 1052 for (int iobs = 0; iobs < n_bins; ++iobs) {
... ... @@ -1080,40 +1063,54 @@ void GCTAEdisp2D::compute_ebounds_src(const double&amp; theta,
1080 1063 bool maxFound = false;
1081 1064  
1082 1065 // Find minimum boundary
1083   - for (int isrc = 0; isrc < n_bins; ++isrc) {
  1066 + for (int imigra = 0; imigra < n_migras; ++imigra) {
1084 1067  
1085   - // Set Esrc
1086   - double Esrc = std::sqrt(m_edisp.axis_hi(m_inx_etrue,isrc) *
1087   - m_edisp.axis_lo(m_inx_etrue,isrc));
1088   - double logEsrc = std::log10(Esrc);
  1068 + // Get mean migra
  1069 + double migra = 0.5 * (m_edisp.axis_lo(m_inx_migra,imigra) +
  1070 + m_edisp.axis_lo(m_inx_migra,imigra));
  1071 +
  1072 + // Fall through if migration is zero
  1073 + if (migra == 0.0) {
  1074 + continue;
  1075 + }
  1076 +
  1077 + // Compute log10 of true energy
  1078 + double logEsrc = logEobs - std::log10(migra);
1089 1079  
1090 1080 // Get matrix term
1091 1081 double edisp = operator()(logEobs, logEsrc, theta);
1092 1082  
1093 1083 // Find first non-negligible matrix term
1094 1084 if (edisp >= eps) {
1095   - minFound = true;
1096   - logEsrcMin = logEsrc;
  1085 + maxFound = true;
  1086 + logEsrcMax = logEsrc;
1097 1087 break;
1098 1088 }
1099 1089  
1100 1090 } // endfor: find minimum boundary
1101 1091  
1102 1092 // Find maximum boundary
1103   - for (int isrc = n_bins-1; isrc >= 0; isrc--) {
  1093 + for (int imigra = n_migras-1; imigra >= 0; imigra--) {
  1094 +
  1095 + // Get mean migra
  1096 + double migra = 0.5 * (m_edisp.axis_lo(m_inx_migra,imigra) +
  1097 + m_edisp.axis_lo(m_inx_migra,imigra));
  1098 +
  1099 + // Fall through if migration is zero
  1100 + if (migra == 0.0) {
  1101 + continue;
  1102 + }
1104 1103  
1105   - // Set Esrc
1106   - double Esrc = std::sqrt(m_edisp.axis_hi(m_inx_etrue,isrc) *
1107   - m_edisp.axis_lo(m_inx_etrue,isrc));
1108   - double logEsrc = std::log10(Esrc);
  1104 + // Compute log10 of true energy
  1105 + double logEsrc = logEobs - std::log10(migra);
1109 1106  
1110 1107 // Get matrix term
1111 1108 double edisp = operator()(logEobs, logEsrc, theta);
1112 1109  
1113 1110 // Find first non-negligible matrix term
1114 1111 if (edisp >= eps) {
1115   - maxFound = true;
1116   - logEsrcMax = logEsrc;
  1112 + minFound = true;
  1113 + logEsrcMin = logEsrc;
1117 1114 break;
1118 1115 }
1119 1116  
... ... @@ -1143,6 +1140,56 @@ void GCTAEdisp2D::compute_ebounds_src(const double&amp; theta,
1143 1140  
1144 1141  
1145 1142 /***********************************************************************//**
  1143 + * @brief Set table indices
  1144 + *
  1145 + * Sets the data members m_inx_etrue, m_inx_migra, m_inx_theta, and
  1146 + * m_inx_matrix.
  1147 + ***************************************************************************/
  1148 +void GCTAEdisp2D::set_indices(void)
  1149 +{
  1150 + // Get mandatory indices (throw exception if not found)
  1151 + m_inx_etrue = m_edisp.axis("ETRUE");
  1152 + m_inx_migra = m_edisp.axis("MIGRA");
  1153 + m_inx_theta = m_edisp.axis("THETA");
  1154 + m_inx_matrix = m_edisp.table("MATRIX");
  1155 +
  1156 + // Return
  1157 + return;
  1158 +}
  1159 +
  1160 +
  1161 +/***********************************************************************//**
  1162 + * @brief Set energy dispersion boundaries
  1163 + *
  1164 + * Sets the data members m_logEsrc_min, m_logEsrc_max, m_migra_min,
  1165 + * m_migra_max, m_theta_min and m_theta_max that define the validity range
  1166 + * of the energy dispersion.
  1167 + ***************************************************************************/
  1168 +void GCTAEdisp2D::set_boundaries(void)
  1169 +{
  1170 + // Get number of bins
  1171 + int netrue = m_edisp.axis_bins(m_inx_etrue);
  1172 + int nmigra = m_edisp.axis_bins(m_inx_migra);
  1173 + int ntheta = m_edisp.axis_bins(m_inx_theta);
  1174 +
  1175 + // Compute minimum and maximum logEsrc boundaries
  1176 + m_logEsrc_min = std::log10(m_edisp.axis_lo(m_inx_etrue, 0));
  1177 + m_logEsrc_max = std::log10(m_edisp.axis_hi(m_inx_etrue, netrue-1));
  1178 +
  1179 + // Compute minimum and maximum migration boundaries
  1180 + m_migra_min = m_edisp.axis_lo(m_inx_migra, 0);
  1181 + m_migra_max = m_edisp.axis_hi(m_inx_migra, nmigra-1);
  1182 +
  1183 + // Compute minimum and maximum theta boundaries
  1184 + m_theta_min = m_edisp.axis_lo(m_inx_theta, 0) * gammalib::deg2rad;
  1185 + m_theta_max = m_edisp.axis_hi(m_inx_theta, ntheta-1) * gammalib::deg2rad;
  1186 +
  1187 + // Return
  1188 + return;
  1189 +}
  1190 +
  1191 +
  1192 +/***********************************************************************//**
1146 1193 * @brief Set maximum energy dispersion value
1147 1194 ***************************************************************************/
1148 1195 void GCTAEdisp2D::set_max_edisp(void) const
... ... @@ -1164,6 +1211,95 @@ void GCTAEdisp2D::set_max_edisp(void) const
1164 1211  
1165 1212  
1166 1213 /***********************************************************************//**
  1214 + * @brief Normalize energy dispersion table
  1215 + ***************************************************************************/
  1216 +void GCTAEdisp2D::normalize_table(void)
  1217 +{
  1218 + // Get axes dimensions
  1219 + int etrue_size = m_edisp.axis_bins(m_inx_etrue);
  1220 + int migra_size = m_edisp.axis_bins(m_inx_migra);
  1221 + int theta_size = m_edisp.axis_bins(m_inx_theta);
  1222 +
  1223 + // Now normalize the migration matrix vectors. We do this by integrating
  1224 + // for each true energy and offset angle the measured energy. We perform
  1225 + // here two passes as the ebounds_obs() method depends on the content of
  1226 + // the matrix, and by having two passes we are sure that we get a
  1227 + // consistent energy interval.
  1228 + for (int pass = 0; pass < 2; ++pass) {
  1229 +
  1230 + // Initialise sums
  1231 + std::vector<double> sums;
  1232 + sums.reserve(theta_size*etrue_size);
  1233 +
  1234 + // Loop over offset angle.
  1235 + for (int i_theta = 0; i_theta < theta_size; ++i_theta) {
  1236 +
  1237 + // Get offset angle (in radians)
  1238 + double theta = 0.5 * (m_edisp.axis_lo(m_inx_theta,i_theta) +
  1239 + m_edisp.axis_hi(m_inx_theta,i_theta)) *
  1240 + gammalib::deg2rad;
  1241 +
  1242 + // Loop over true photon energy
  1243 + for (int i_etrue = 0; i_etrue < etrue_size; ++i_etrue) {
  1244 +
  1245 + // Get energy
  1246 + double emin = std::log10(m_edisp.axis_lo(m_inx_etrue,i_etrue));
  1247 + double emax = std::log10(m_edisp.axis_hi(m_inx_etrue,i_etrue));
  1248 + double logEsrc = 0.5*(emin+emax);
  1249 +
  1250 + // Initialise integration
  1251 + double sum = 0.0;
  1252 +
  1253 + // Get integration boundaries
  1254 + GEbounds ebounds = ebounds_obs(logEsrc, theta);
  1255 +
  1256 + // Loop over all energy intervals
  1257 + for (int i = 0; i < ebounds.size(); ++i) {
  1258 +
  1259 + // Get energy boundaries
  1260 + emin = ebounds.emin(i).log10TeV();
  1261 + emax = ebounds.emax(i).log10TeV();
  1262 +
  1263 + // Setup integration function
  1264 + edisp_kern integrand(this, logEsrc, theta);
  1265 + GIntegral integral(&integrand);
  1266 +
  1267 + // Set integration precision
  1268 + integral.eps(1.0e-6);
  1269 +
  1270 + // Do Romberg integration
  1271 + sum += integral.romberg(emin, emax);
  1272 +
  1273 + } // endfor: looped over energy intervals
  1274 +
  1275 + // Store sum
  1276 + sums.push_back(sum);
  1277 +
  1278 + } // endfor: looped over Etrue
  1279 +
  1280 + } // endfor: looped over theta
  1281 +
  1282 + // Normalize vectors of migration matrix for all etrue and theta.
  1283 + for (int i_theta = 0, inx = 0; i_theta < theta_size; ++i_theta) {
  1284 + for (int i_etrue = 0; i_etrue < etrue_size; ++i_etrue, ++inx) {
  1285 + double sum = sums[inx];
  1286 + if (sum > 0.0) {
  1287 + int offset = i_etrue + (i_theta*migra_size) * etrue_size;
  1288 + for (int k = 0, i = offset; k < migra_size; ++k, i += etrue_size) {
  1289 + m_edisp(m_inx_matrix,i) /= sum;
  1290 + }
  1291 + }
  1292 + }
  1293 + }
  1294 +
  1295 + } // endfor: made 2 passes
  1296 +
  1297 + // Return
  1298 + return;
  1299 +}
  1300 +
  1301 +
  1302 +/***********************************************************************//**
1167 1303 * @brief Integration kernel for edisp_kern() class
1168 1304 *
1169 1305 * @param[in] x Function value.
... ...