@@ -143,23 +143,23 @@ namespace galsim {
143143 }
144144
145145#if 0
146- Got a start on implementing the absorption length lookup,
147- but couldn't get the python - C++ wrapper working, so I went
148- back to the analytic function - Craig Lage 17-Feb-17
149- double Silicon::AbsLength(double lambda)
150- {
151- // Looks up the absorption length and returns
152- // an interpolated value
153- int nminus;
154- double aminus, aplus, dlambda;
155- dlambda = _abs_data[2] - _abs_data[0];
156- // index of
157- nminus = (int) ((lambda - _abs_data[0]) / dlambda) * 2;
158- if (nminus < 0) return _abs_data[1];
159- else if (nminus > _nabs - 4) return _abs_data[_nabs - 1];
160- else return _abs_data[nminus+1] +
161- (lambda - _abs_data[nminus]) * (_abs_data[nminus+3] - _abs_data[nminus+1]);
162- }
146+ // Got a start on implementing the absorption length lookup,
147+ // but couldn't get the python - C++ wrapper working, so I went
148+ // back to the analytic function - Craig Lage 17-Feb-17
149+ double Silicon::AbsLength(double lambda)
150+ {
151+ // Looks up the absorption length and returns
152+ // an interpolated value
153+ int nminus;
154+ double aminus, aplus, dlambda;
155+ dlambda = _abs_data[2] - _abs_data[0];
156+ // index of
157+ nminus = (int) ((lambda - _abs_data[0]) / dlambda) * 2;
158+ if (nminus < 0) return _abs_data[1];
159+ else if (nminus > _nabs - 4) return _abs_data[_nabs - 1];
160+ else return _abs_data[nminus+1] +
161+ (lambda - _abs_data[nminus]) * (_abs_data[nminus+3] - _abs_data[nminus+1]);
162+ }
163163#endif
164164
165165 template <typename T>
@@ -236,7 +236,7 @@ namespace galsim {
236236 if (!target.getBounds ().includes (Position<int >(ix,iy))) return false ;
237237
238238 const double zfit = 12.0 ;
239- const double zfactor = tanh (zconv / zfit);
239+ const double zfactor = std:: tanh (zconv / zfit);
240240 const int minx = target.getXMin ();
241241 const int miny = target.getYMin ();
242242 const int maxx = target.getXMax ();
@@ -251,23 +251,94 @@ namespace galsim {
251251 return testpoly.contains (Point (x,y));
252252 }
253253
254- template <typename T>
255- double Silicon::accumulate (const PhotonArray& photons, UniformDeviate ud,
256- ImageView<T> target)
254+ // Helper function to calculate how far down into the silicon the photon converts into
255+ // an electron.
256+ void calculateConversionDepth (const PhotonArray& photons, std::vector<double >& depth,
257+ UniformDeviate ud)
257258 {
258259 const double log10_over_250 = std::log (10 .) / 250 .;
259260
260- // Create and read in pixel distortions
261- const int xoff[9 ] = {0 ,1 ,1 ,0 ,-1 ,-1 ,-1 ,0 ,1 }; // Displacements to neighboring pixels
262- const int yoff[9 ] = {0 ,0 ,1 ,1 ,1 ,0 ,-1 ,-1 ,-1 }; // Displacements to neighboring pixels
263- int n=0 ;
261+ const int nphotons = photons.size ();
262+ for (int i=0 ; i<nphotons; ++i) {
263+ // Determine the distance the photon travels into the silicon
264+ double si_length;
265+ if (photons.hasAllocatedWavelengths ()) {
266+ double lambda = photons.getWavelength (i); // in nm
267+ // The below is an approximation. ToDo: replace with lookup table
268+ // double abs_length = pow(10.0,((lambda - 500.0) / 250.0)); // in microns
269+ double abs_length = std::exp ((lambda - 500.0 ) * log10_over_250); // in microns
270+ // double abs_length = AbsLength(lambda); // in microns
271+ si_length = -abs_length * log (1.0 - ud ()); // in microns
272+ #ifdef DEBUGLOGGING
273+ if (i % 1000 == 0 ) {
274+ dbg<<" lambda = " <<lambda<<std::endl;
275+ dbg<<" si_length = " <<si_length<<std::endl;
276+ }
277+ #endif
278+ } else {
279+ // If no wavelength info, assume conversion takes place near the top.
280+ si_length = 5.0 ;
281+ }
264282
265- // PhotonArray
266- GaussianDeviate gd (ud,0 ,1 ); // Random variable from Standard Normal dist.
267- int zerocount = 0 , nearestcount = 0 , othercount = 0 , misscount = 0 ;
283+ // Next we partition the si_length into x,y,z. Assuming dz is positive downward
284+ if (photons.hasAllocatedAngles ()) {
285+ double dxdz = photons.getDXDZ (i);
286+ double dydz = photons.getDYDZ (i);
287+ double dz = si_length / std::sqrt (1.0 + dxdz*dxdz + dydz*dydz); // in microns
288+ depth[i] = std::min (95.0 , dz); // max 95 microns
289+ #ifdef DEBUGLOGGING
290+ if (i % 1000 == 0 ) {
291+ dbg<<" dxdz = " <<dxdz<<std::endl;
292+ dbg<<" dydz = " <<dydz<<std::endl;
293+ dbg<<" dz = " <<dz<<std::endl;
294+ }
295+ #endif
296+ } else {
297+ depth[i] = si_length;
298+ }
299+ }
300+ }
268301
269- Bounds<int > b = target.getBounds ();
302+ static const int xoff[9 ] = {0 ,1 ,1 ,0 ,-1 ,-1 ,-1 ,0 ,1 }; // Displacements to neighboring pixels
303+ static const int yoff[9 ] = {0 ,0 ,1 ,1 ,1 ,0 ,-1 ,-1 ,-1 }; // Displacements to neighboring pixels
270304
305+ // Break this bit out mostly to make it easier when profiling to see how much it would help
306+ // to further optimize this part of the code.
307+ template <typename T>
308+ bool searchNeighbors (const Silicon& silicon, int & ix, int & iy, double x, double y, double zconv,
309+ ImageView<T> target, int & step)
310+ {
311+ // The following code finds which pixel we are in given
312+ // pixel distortion due to the brighter-fatter effect
313+ // The following are set up to start the search in the undistorted pixel, then
314+ // search in the nearest neighbor first if it's not in the undistorted pixel.
315+ if ((x > y) && (x > 1.0 - y)) step = 1 ;
316+ else if ((x < y) && (x < 1.0 - y)) step = 7 ;
317+ else if ((x < y) && (x > 1.0 - y)) step = 3 ;
318+ else step = 5 ;
319+ int n=step;
320+ for (int m=1 ; m<9 ; m++) {
321+ int ix_off = ix + xoff[n];
322+ int iy_off = iy + yoff[n];
323+ double x_off = x - xoff[n];
324+ double y_off = y - yoff[n];
325+ if (silicon.insidePixel (ix_off, iy_off, x_off, y_off, zconv, target)) {
326+ xdbg<<" Found in pixel " <<n<<" , ix = " <<ix<<" , iy = " <<iy
327+ <<" , x=" <<x<<" , y = " <<y<<" , target(ix,iy)=" <<target (ix,iy)<<std::endl;
328+ ix = ix_off;
329+ iy = iy_off;
330+ return true ;
331+ }
332+ n = ((n-1 ) + step) % 8 + 1 ;
333+ // This is intended to start with the nearest neighbor, then cycle through others.
334+ }
335+ return false ;
336+ }
337+
338+ template <typename T>
339+ double Silicon::accumulate (const PhotonArray& photons, UniformDeviate ud, ImageView<T> target)
340+ {
341+ Bounds<int > b = target.getBounds ();
271342 if (!b.isDefined ())
272343 throw std::runtime_error (" Attempting to PhotonArray::addTo an Image with"
273344 " undefined Bounds" );
@@ -279,6 +350,9 @@ namespace galsim {
279350 dbg<<" total nphotons = " <<photons.size ()<<std::endl;
280351 dbg<<" hasAllocatedWavelengths = " <<photons.hasAllocatedWavelengths ()<<std::endl;
281352 dbg<<" hasAllocatedAngles = " <<photons.hasAllocatedAngles ()<<std::endl;
353+ double Irr = 0 .;
354+ double Irr0 = 0 .;
355+ int zerocount=0 , neighborcount=0 , misscount=0 ;
282356#endif
283357
284358 int nx = b.getXMax () - b.getXMin () + 1 ;
@@ -287,50 +361,30 @@ namespace galsim {
287361 buildPolylist (_imagepolys, nx, ny, _numVertices);
288362 dbg<<" Built poly list\n " ;
289363
290- #ifdef DEBUGLOGGING
291- double Irr = 0 .;
292- double Irr0 = 0 .;
293- #endif
364+ const int nphotons = photons.size ();
365+ std::vector<double > depth (nphotons);
366+ calculateConversionDepth (photons, depth, ud);
367+
368+ GaussianDeviate gd (ud,0 ,1 ); // Random variable from Standard Normal dist.
369+
294370 double addedFlux = 0 .;
295371 double next_recalc = _nrecalc;
296- const int nphotons = photons.size ();
297372 for (int i=0 ; i<nphotons; i++) {
298373 // Update shapes every _nrecalc electrons
299374 if (addedFlux > next_recalc) {
300375 updatePixelDistortions (target);
301376 next_recalc = addedFlux + _nrecalc;
302377 }
303378
304- // First we get the location where the photon strikes the silicon:
379+ // Get the location where the photon strikes the silicon:
305380 double x0 = photons.getX (i); // in pixels
306381 double y0 = photons.getY (i); // in pixels
307- // Next we determine the distance the photon travels into the silicon
308- double si_length;
309- if (photons.hasAllocatedWavelengths ()) {
310- double lambda = photons.getWavelength (i); // in nm
311- // The below is an approximation. ToDo: replace with lookup table
312- // double abs_length = pow(10.0,((lambda - 500.0) / 250.0)); // in microns
313- double abs_length = std::exp ((lambda - 500.0 ) * log10_over_250); // in microns
314- // double abs_length = AbsLength(lambda); // in microns
315- si_length = -abs_length * log (1.0 - ud ()); // in microns
316- #ifdef DEBUGLOGGING
317- if (i % 1000 == 0 ) {
318- dbg<<" lambda = " <<lambda<<std::endl;
319- dbg<<" si_length = " <<si_length<<std::endl;
320- }
321- #endif
322- } else {
323- // If no wavelength info, assume conversion takes place near the top.
324- si_length = 5.0 ;
325- }
326382
327- // Next we partition the si_length into x,y,z. Assuming dz is positive downward
328- double zconv;
383+ double zconv; // This is the reverse of depth. zconv is how far above the substrate.
329384 if (photons.hasAllocatedAngles ()) {
330385 double dxdz = photons.getDXDZ (i);
331386 double dydz = photons.getDYDZ (i);
332- double dz = si_length / std::sqrt (1.0 + dxdz*dxdz + dydz*dydz); // in microns
333- dz = std::min (95.0 , dz); // max 95 microns
387+ double dz = depth[i];
334388 x0 += dxdz * dz / 10.0 ; // in pixels
335389 y0 += dydz * dz / 10.0 ; // in pixels
336390 zconv = 100.0 - dz; // Conversion depth in microns
@@ -342,14 +396,14 @@ namespace galsim {
342396 }
343397#endif
344398 } else {
345- zconv = 100.0 - si_length ;
399+ zconv = 100.0 - depth[i] ;
346400 }
347401 if (zconv < 0.0 ) continue ; // Throw photon away if it hits the bottom
348402 // TODO: Do something more realistic if it hits the bottom.
349403
350404 // Now we add in a displacement due to diffusion
351405 if (_diffStep != 0 .) {
352- double diffStep = fmax (0.0 , _diffStep * (zconv - 10.0 ) / 100.0 ); // in microns
406+ double diffStep = std::max (0.0 , _diffStep * (zconv - 10.0 ) / 100.0 ); // in microns
353407 x0 += diffStep * gd () / 10.0 ; // in pixels
354408 y0 += diffStep * gd () / 10.0 ; // in pixels
355409 }
@@ -367,65 +421,44 @@ namespace galsim {
367421 // Now we find the undistorted pixel
368422 int ix = int (floor (x0 + 0.5 ));
369423 int iy = int (floor (y0 + 0.5 ));
424+
425+ #ifdef DEBUGLOGGING
370426 int ix0 = ix;
371427 int iy0 = iy;
428+ #endif
372429
373430 double x = x0 - ix + 0.5 ;
374431 double y = y0 - iy + 0.5 ;
375432 // (ix,iy) are the undistorted pixel coordinates.
376433 // (x,y) are the coordinates within the pixel, centered at the lower left
377434
378- // The following code finds which pixel we are in given
379- // pixel distortion due to the brighter-fatter effect
380- bool foundPixel = false ;
381- // The following are set up to start the search in the undistorted pixel, then
382- // search in the nearest neighbor first if it's not in the undistorted pixel.
383- int step;
384- if ((x > y) && (x > 1.0 - y)) step = 1 ;
385- else if ((x < y) && (x < 1.0 - y)) step = 7 ;
386- else if ((x < y) && (x > 1.0 - y)) step = 3 ;
387- else step = 5 ;
388- int n=0 ;
389- int m_found;
390- for (int m=0 ; m<9 ; m++) {
391- int ix_off = ix + xoff[n];
392- int iy_off = iy + yoff[n];
393- double x_off = x - xoff[n];
394- double y_off = y - yoff[n];
395- if (insidePixel (ix_off, iy_off, x_off, y_off, zconv, target)) {
396- xdbg<<" Found in pixel " <<n<<" , ix = " <<ix<<" , iy = " <<iy
397- <<" , x=" <<x<<" , y = " <<y<<" , target(ix,iy)=" <<target (ix,iy)<<std::endl;
398- if (m == 0 ) zerocount += 1 ;
399- else if (m == 1 ) nearestcount += 1 ;
400- else othercount +=1 ;
401- ix = ix_off;
402- iy = iy_off;
403- m_found = m;
404- foundPixel = true ;
405- break ;
406- }
407- n = ((n-1 )+step) % 8 + 1 ;
408- // This is intended to start with the nearest neighbor, then cycle through others.
435+ // First check the obvious choice, since this will usually work.
436+ bool foundPixel = insidePixel (ix, iy, x, y, zconv, target);
437+ #ifdef DEBUGLOGGING
438+ if (foundPixel) ++zerocount;
439+ #endif
440+
441+ // Then check neighbors
442+ int step; // We might need this below, so let searchNeighbors return it.
443+ if (!foundPixel) {
444+ foundPixel = searchNeighbors (*this , ix, iy, x, y, zconv, target, step);
445+ #ifdef DEBUGLOGGING
446+ if (foundPixel) ++neighborcount;
447+ #endif
409448 }
449+
450+ // Rarely, we won't find it in the undistorted pixel or any of the neighboring pixels.
451+ // If we do arrive here due to roundoff error of the pixel boundary, put the electron
452+ // in the undistorted pixel or the nearest neighbor with equal probability.
410453 if (!foundPixel) {
411454 xdbg<<" Not found in any pixel\n " ;
412455 xdbg<<" ix,iy = " <<ix<<' ,' <<iy<<" x,y = " <<x<<' ,' <<y<<std::endl;
413- // We should never arrive here, since this means we didn't find it in the
414- // undistorted pixel or any of the neighboring pixels. However, sometimes (about
415- // 0.1% of the time) we do arrive here due to roundoff error of the pixel boundary.
416- // When this happens, I put the electron in the undistorted pixel or the nearest
417- // neighbor with equal probability.
418- misscount += 1 ;
419- if (ud () > 0.5 ) {
420- n = 0 ;
421- zerocount +=1 ;
422- } else {
423- n = step;
424- nearestcount +=1 ;
425- }
456+ int n = (ud () > 0.5 ) ? 0 : step;
426457 ix = ix + xoff[n];
427458 iy = iy + yoff[n];
428- foundPixel = true ;
459+ #ifdef DEBUGLOGGING
460+ ++misscount;
461+ #endif
429462 }
430463#if 0
431464 // (ix, iy) now give the actual pixel which will receive the charge
@@ -455,8 +488,8 @@ namespace galsim {
455488 Irr /= addedFlux;
456489 Irr0 /= addedFlux;
457490 dbg<<" Irr = " <<Irr<<" cf. Irr0 = " <<Irr0<<std::endl;
458- dbg << " Found " << zerocount << " photons in undistorted pixel, " << nearestcount ;
459- dbg << " in closest neighbor, " << othercount << " in other neighbor. " << misscount;
491+ dbg << " Found " << zerocount << " photons in undistorted pixel, " << neighborcount ;
492+ dbg << " in one of the neighbors, and " << misscount;
460493 dbg << " not in any pixel\n " << std::endl;
461494#endif
462495 return addedFlux;
0 commit comments