libatomprobe
Library for Atom Probe Tomography (APT) computation
reconstruction-simple.cpp
Go to the documentation of this file.
1 /* reconstruction-simple.cpp : Mass overlap deconvolution algorithm
2  * Copyright (C) 2017 Daniel Haley
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
19 
22 #include "atomprobe/io/dataFiles.h"
23 
25 
26 #include <vector>
27 #include <random>
28 
29 namespace AtomProbe{
30 
32 {
33  reconFOV=initialRadius=shankAngle=0;
34  detEfficiency=1;
35  evolutionMode=EVOLUTION_MODE_SHANKANGLE;
36 }
37 
39 {
40  projectionModel=model;
41 }
42 
44 {
45  evolutionMode=mode;
46 }
47 
49 {
50  ASSERT(detEfficiency > 0 && detEfficiency <=1);
51  detEfficiency =detEff;
52 }
53 
55 {
56  ASSERT(fp> 0);
57  flightPath=fp;
58 }
59 
61 {
62  ASSERT(angle< M_PI)
63  reconFOV=angle;
64 }
65 
67 {
68  ASSERT(shankAngle >=0 && shankAngle < M_PI/2.0f);
69  shankAngle=angle;
70 }
71 
72 
74 {
75  ASSERT(initialRadius >=0);
76  initialRadius=r;
77 }
78 
79 bool ReconstructionSphereOnCone::reconstruct(const std::vector<float> &detectorX,
80  const std::vector<float> &detectorY,
81  const std::vector<float> &tof,
82  const std::vector<float> &ionVolume,
83  std::vector<IonHit> &outputPts) const
84 {
85  ASSERT(evolutionMode == EVOLUTION_MODE_SHANKANGLE);
86  ASSERT(detectorX.size() == detectorY.size());
87  ASSERT(detectorX.size() == ionVolume.size());
88  outputPts.resize(detectorX.size());
89 
90 
91  //Distance from sphere apex to cone apex
92  double initZoff;
93 
94  //This is for the tangent to the sphere taking angle shankangle
95  //===========
96 
97  if(shankAngle > sqrt(std::numeric_limits<float>::epsilon()))
98  initZoff= initialRadius*(1.0/sin(shankAngle) -1.0) ;
99  else
100  initZoff=initialRadius;
101  //============
102 
103  float etaFOV = projectionModel->thetaToEta(reconFOV);
104 
105  //This is the initial height from origin to edge of cone
106  double distRatio=initialRadius/initZoff;
107 
108 
109  //set current Z offset to initial Z offset to sphere
110  double zOffset=initZoff;
111  //Set current radius
112  double currentR;
113 
114 
115  //Reconstruction is done in two passes to speed it up.
116  // the first pass cannot be readily parallelised, but the second can.
117 
118  //=== PASS 1 ==
119  //Switch between dynamic and static radius loops
120  // First just compute the Z position that corresponds to the centre of the recon sphere
121  // we do this so we can separate non-parallel and parallel regions
122  if(shankAngle > std::numeric_limits<float>::epsilon())
123  {
124  //This, divided by radius^2, gives inverse area of spherical cap
125  float areaFactor = 1.0f/(2.0f*M_PI*(1.0-cos(reconFOV)));
126 
127  for(unsigned int ui=0;ui<detectorX.size() ;ui++)
128  {
129 
130  //Use similar triangles to scale-up size
131  //R' = R/L*(L +x), where (L+x) is the current Z offset,
132  // L is the initial offset, and R is initial radius
133  // See doc/figures/shank-reconstruction.svg for diagram
134  currentR= distRatio*(zOffset);
135 
136  //set the delta from the cone apex
137  outputPts[ui][2]=zOffset;
138 
139  //Update Z-offset by shuffling by spherical cap area
140  zOffset +=(ionVolume[ui]/detEfficiency)*areaFactor/(currentR*currentR);
141 
142  }
143  }
144  else
145  {
146  //We can compute this exactly once, as it wont change
147  float areaSphericalCapInv;
148  areaSphericalCapInv = 1.0/(2.0*M_PI*initialRadius*initialRadius*(1.0-cos(reconFOV)));
149 
150 
151  //TODO: Parallel reduction sum
152  for(unsigned int ui=0;ui<detectorX.size() ;ui++)
153  {
154  //set the origin of the recon sphere, which we will later update with complete point
155  outputPts[ui][2]=(zOffset-initZoff);
156 
157  //Update Z-offset
158  zOffset +=(ionVolume[ui]/detEfficiency)*areaSphericalCapInv;
159 
160  }
161  }
162  //=============
163 
164  //=== PASS 2===
165  //Expand point to the full position on sphere
166 #ifdef _OPENMP
167  bool spin=false;
168 #endif
169 
170  #pragma omp parallel for
171  for(unsigned int ui=0;ui<detectorX.size() ;ui++)
172  {
173 #ifdef _OPENMP
174  if(spin)
175  continue;
176 #endif
177 
178  float px,py ;
179  projectionModel->scaleDown(flightPath,detectorX[ui],
180  detectorY[ui],px,py);
181 
182  float eta,phi;
183  if(!projectionModel->toAzimuthal(px,py,eta,phi))
184  {
185 #ifndef _OPENMP
186  return false;
187 #else
188  spin=true;
189 #endif
190  }
191 
192  currentR= distRatio*(outputPts[ui][2]);
193  outputPts[ui][0]=currentR*sin(eta)*cos(phi);
194  outputPts[ui][1]=currentR*sin(eta)*sin(phi);
195  outputPts[ui][2]+=currentR*(1.0f-cos(eta)) - initZoff;
196 
197 
198  outputPts[ui][3]=tof[ui];
199  }
200 
201 #ifdef _OPENMP
202  if(spin)
203  return false;
204 #endif
205  //=============
206 
207  return true;
208 }
209 
210 
211 #ifdef DEBUG
212 
213 bool reconstructTest()
214 {
215  //The goal of this is to "exercise" the reconstruction code
216  //Create a random "detector sequence"
217 
218  std::vector<float> dx, dy, tof,ionV;
219 
220  const unsigned int NUM_PTS=100000;
221 
222  //Ion volume is very exaggerated
223  ionV.resize(NUM_PTS,0.1);
224  dx.resize(NUM_PTS);
225  dy.resize(NUM_PTS);
226  tof.resize(NUM_PTS);
227 
228  std::random_device r;
229  std::mt19937 mtRnd(r());
230  std::uniform_real_distribution<float> ud(0,1.0f);
231 
232  //detector total width
233  const float DET_SIZE=40.0f; //mm
234  const float FLIGHT_PATH=42.0f; //mm
235  for(unsigned int ui=0;ui<NUM_PTS;ui++)
236  {
237  do
238  {
239  dx[ui] = 2.0*(ud(mtRnd)-0.5f); //Normalised -1->1 space
240  dy[ui] = 2.0*(ud(mtRnd)-0.5f);
241  } while(dx[ui]*dx[ui] + dy[ui]*dy[ui] > 1.0f);
242 
243  //Convert to detector size
244  // Divide by two,as the detector spans 0-1 sapace
245  dx[ui]*=DET_SIZE/2.0f;
246  dy[ui]*=DET_SIZE/2.0f;
247 
248  tof[ui]=ud(mtRnd)+0.5;
249  }
250 
251 
252 
254 
255  //Specify the model for reconstruction.
256  // Gnomonic is a sphere-centred projection
258  recons.setProjModel(&model);
259 
260  const float TIP_RADIUS=15.0f;
261  //Provide reconstruction parameters
262  recons.setInitialRadius(TIP_RADIUS);
263  recons.setReconFOV(atan(0.5*DET_SIZE/FLIGHT_PATH));
264  recons.setShankAngle(10.0f*M_PI/180.0f);
265  recons.setFlightPath(FLIGHT_PATH);
266  std::vector<IonHit> pts;
267  bool errRes;
268  errRes=recons.reconstruct(dx,dy,tof,ionV,pts);
269 
270  TEST(errRes,"Reconstruction should not fail");
271  TEST(pts.size() && (pts.size() <= NUM_PTS),"One event per reconstruction");
272 
273  BoundCube bc;
274  bc.setBounds(pts);
275  TEST(bc.isValid(),"Valid bounding cube");
276  //Boundcube should have a nonzero volume
277  TEST(bc.getVolume() > 1e-5,"Recon should have some volume");
278 
279  const double FUDGE=1.5;
280  TEST(bc.getSize(0) <= 2.0*TIP_RADIUS*FUDGE,
281  "Bounding box should not exceed final tip diameter");
282 
283  TEST(bc.getSize(1) <= 2.0*TIP_RADIUS*FUDGE,
284  "Bounding box should not exceed final tip diameter");
285 
286  savePosFile(pts,"reconstruct-test.pos");
287  return true;
288 }
289 #endif
290 }
void setFlightPath(float detEff)
Set flight path. Units must match detector units (eg mm)
void setReconFOV(float angle)
Set the angle (between 0 and pi radiaans). This is.
virtual float thetaToEta(float theta) const =0
#define TEST(f, g)
Definition: aptAssert.h:49
float getSize(unsigned int dim) const
Return the size of the side parallel to the given dimension.
Definition: boundcube.cpp:343
float getVolume() const
Obtain the volume of the cube.
Definition: boundcube.cpp:355
unsigned int savePosFile(const std::vector< Point3D > &points, float mass, const char *name, bool append=false)
Save a vector of Point3Ds into a pos file, using a fixed mass, return nonzero on error.
Definition: dataFiles.cpp:499
#define M_PI
Definition: kd-example.cpp:34
virtual bool toAzimuthal(float fx, float fy, float &theta, float &phi) const =0
Convert from plane coordinates to projection coords.
bool reconstructTest()
bool reconstruct(const std::vector< float > &detectorX, const std::vector< float > &detectorY, const std::vector< float > &timeOfFlight, const std::vector< float > &ionVolume, std::vector< IonHit > &outputPts) const
Using the current reconstruction model, reconstruct a detector sequence.
Helper class to define a bounding cube.
Definition: boundcube.h:29
#define ASSERT(f)
void setRadiusEvolutionMode(unsigned int evolutionMode)
Set the method of evolution for the shank, as per EVOLUTION_MODE_ enum.
void setInitialRadius(float rInit)
Set the initial tip radius.
void setShankAngle(float angle)
Set initial angle, in radiians.
void setDetectorEfficiency(float detEff)
Set detector efficiency in range (0,1].
void setProjModel(AtomProbe::SphericPlaneProjection *model)
Set a given projection model.
virtual void scaleDown(float flightLength, float detX, float detY, float &scaledX, float &scaledY) const =0
Convert from actual detector postion (eg. mm) and flight path to scaled-down transform.
void setBounds(float xMin, float yMin, float zMin, float xMax, float yMax, float zMax)
Set the bounds by passing in minima and maxima of each dimension.
Definition: boundcube.cpp:49