libatomprobe
Library for Atom Probe Tomography (APT) computation
autocorrelate.cpp
Go to the documentation of this file.
1 /*
2 * autocorrelate.cpp - Autocorrelation function implementation for Atom Probe
3 * Copyright (C) 2015 Daniel Haley
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include <iostream>
20 #include <fstream>
21 #include <limits>
22 
23 #include "atomprobe/atomprobe.h"
24 
25 #ifdef _OPENMP
26 #include <omp.h>
27 #endif
28 using namespace AtomProbe;
29 using namespace std;
30 
31 #ifdef DEBUG
32 #include <cassert>
33 #define ASSERT(f) {assert(f);}
34 #else
35 #define ASSERT(f) {}
36 #endif
37 
38 const unsigned int PROGRESS_BAR_SIZE=50;
39 
41 unsigned int treeProgressValue=0;
42 
43 bool callback()
44 {
45  if(!treeProgressBar)
46  return true;
47 
48 #ifdef _OPENMP
49  if(!omp_get_thread_num())
50 #endif
51 
52  treeProgressBar->update(treeProgressValue);
53 
54  return true;
55 }
56 
57 //Input must be in vector[bin][ions] form
58 template<class T>
59 void dumpAutocorrelation(const vector<vector<T> > &autoCorrelateFunc,
60  const RangeFile &rangeFile, float binStep, std::ostream &strm )
61 {
62  strm << "Per-Species Autocorrelation function:" << endl;
63  strm << "r " << "\t";
64  for(unsigned int uj=0;uj<rangeFile.getNumIons();uj++)
65  strm << rangeFile.getName(uj) << "\t" ;
66  strm << endl;
67 
68  if(!autoCorrelateFunc.size())
69  return;
70 
71  ASSERT(rangeFile.getNumIons() == autoCorrelateFunc[0].size());
72 
73  for(unsigned int ui=0;ui<autoCorrelateFunc.size();ui++)
74  {
75  strm << binStep*ui << "\t";
76 
77  for(unsigned int uj=0;uj<autoCorrelateFunc[ui].size();uj++)
78  {
79  strm <<autoCorrelateFunc[ui][uj] << "\t";
80  }
81  strm << endl;
82  }
83 
84 }
85 
86 int main(int argc, char *argv[])
87 {
88  if(!(argc == 6 || argc ==7) )
89  {
90  cerr << "USAGE : " << argv[0] << " POSFILE RANGEFILE Radius binStep Sampling [outputfile]" << endl;
91  return 1;
92  }
93 
94  std::string posFilename,rangeFilename, outputFile;
95  float sampleFactor,scanRadius,binStep;
96 
97  posFilename=argv[1];
98  rangeFilename=argv[2];
99  if(argc ==7)
100  outputFile=argv[6];
101 
102  if(stream_cast(sampleFactor,argv[5]))
103  {
104  cerr << "unable to understand sampling factor. Aborting." << endl;
105  return 1;
106  }
107  if(stream_cast(scanRadius,argv[3]))
108  {
109  cerr << "unable to understand scan radius. Aborting." << endl;
110  return 1;
111  }
112 
113  if(stream_cast(binStep,argv[4]))
114  {
115  cerr << "unable to understand bin step size. Aborting." << endl;
116  return 1;
117  }
118 
119  if(sampleFactor < 0.0f || sampleFactor > 1.0f)
120  {
121  cerr << "sampling factor must lie in range [0,1]"<<endl ;
122  return 1;
123  }
124 
125  if(scanRadius <=0.0f)
126  {
127  cerr << "Scan radius must be positive" << endl;
128  return 1;
129  }
130 
131  if(binStep <=0.0f)
132  {
133  cerr << "Bin step size must be positive" << endl;
134  return 1;
135  }
136 
137  std::ofstream of;
138  if(!outputFile.empty())
139  {
140  of.open(outputFile.c_str());
141  if(!of)
142  {
143  cerr << "Unable to open outputfile :" << outputFile << endl;
144  return 1;
145  }
146  }
147  //Load ion data
148  //--
149  unsigned int errCode;
150  vector<IonHit> ions;
151  cerr << "Loading pos data from " <<posFilename << "..." << endl;
152  errCode=loadPosFile(ions,posFilename.c_str());
153 
154  if(errCode)
155  {
156  cerr << "Error loading posfile :" << endl;
157  cerr << getPosFileErrString(errCode) << endl;
158  return 1;
159  }
160 
161  cerr << "\tLoaded :" << ions.size() << " ions." << endl;
162  //--
163 
164  //load range data
165  //--
166  RangeFile rangeFile;
167  if(!rangeFile.openGuessFormat(rangeFilename.c_str()))
168  {
169  cerr << "Error loading rangefile :" <<
170  rangeFile.getErrString() << endl;
171  return 1;
172  }
173  //--
174 
175  //Apply the rangefile
176  //--
177  cerr << "Ranging...";
178  rangeFile.range(ions);
179  cerr << "Done" << endl;
180  cerr << "\tRanged:" << ions.size() << " ions." << endl;
181 
182  if(ions.empty())
183  {
184  cerr << "No ions after ranging. Aborting." << endl;
185  return 1;
186  }
187  //--
188 
189 
190  //Sample the data
191  //--
192  if(sampleFactor < 1.0f)
193  {
194  {
195  cerr << "Sampling...";
196  vector<IonHit> sampled;
197  sampleIons(ions,sampleFactor,sampled);
198  sampled.swap(ions);
199  }
200  cerr << "Done" << endl;
201  cerr << "\tSampled:" << ions.size() << " ions." << endl;
202 
203  if(ions.empty())
204  {
205  cerr << "No ions after sampling. Aborting." << endl;
206  return 1;
207  }
208  }
209  //--
210 
211 
212  K3DTreeExact tree;
213  BoundCube b;
214  //Initialise the KD tree
215  //--
216  cerr << "Building search structures....";
217  treeProgressBar= new ProgressBar();
218  treeProgressBar->setLength(PROGRESS_BAR_SIZE);
219  treeProgressBar->init();
220 
221  tree.setCallback(callback);
223  tree.resetPts(ions,false);
224  tree.build();
225  delete treeProgressBar;
226  treeProgressBar=0;
227 
228  tree.getBoundCube(b);
229 
230  if(b.isFlat())
231  {
232  cerr << "Data structure not OK. Aborting" << endl;
233  cerr << "Does the input data form a volume?" << endl;
234  return 1;
235  }
236  //--
237 
238 
239  unsigned int binCount = scanRadius/binStep;
240 
241  if(!binCount)
242  {
243  cerr << "Search radius :" << scanRadius << " is too small, needs to be bigger than the bin size:" << binStep << endl;
244  return 1;
245  }
246 
247  //First dimension is bin count, second is composition
248 
249  cerr << "Computing autocorrelation..." ;
250  ProgressBar progressBar;
251  progressBar.setLength(PROGRESS_BAR_SIZE);
252  progressBar.init();
253 
254 
255  unsigned int numCounted =0;
256 
257  vector<vector<unsigned int> > overallCompositionTable;
258  //Zero final composition table
259  //--
260  overallCompositionTable.resize(binCount);
261  for(unsigned int ui=0;ui<overallCompositionTable.size();ui++)
262  overallCompositionTable[ui].resize(rangeFile.getNumIons(),0);
263  //--
264 
265 
266 
267  //Perform composition scan to find atom counts as a function of radius and atom type
268  //--
269 #pragma omp parallel for
270  for(unsigned int ui=0;ui<ions.size();ui++)
271  {
272  vector<size_t> pointIdx;
273  vector<vector<size_t> > compositionTable;
274 
275  //Zero composition table
276  compositionTable.resize(binCount);
277  for(unsigned int uj=0;uj<compositionTable.size();uj++)
278  compositionTable[uj].resize(rangeFile.getNumIons(),0);
279 
280  //Find all points in radius
281  tree.findUntaggedInRadius(ions[ui].getPos(),b,scanRadius, pointIdx);
282  numCounted++;
283 #ifdef _OPENMP
284  if(!omp_get_thread_num())
285 #endif
286  progressBar.update((float)numCounted/ions.size()*100.0f);
287 
288 
289 
290  if(pointIdx.empty())
291  continue;
292 
293  //Compute local composition frequency table.
294  // This is essentially the building blocks for an RDF
295  for(size_t uj=0;uj<pointIdx.size();uj++)
296  {
297  unsigned int ionId;
298  float mass,radius;
299  //target ion
300  IonHit p;
301  p = ions[tree.getOrigIndex(pointIdx[uj])];
302  mass=p.getMassToCharge();
303  ionId = rangeFile.getIonID(mass);
304  ASSERT(ionId < rangeFile.getNumIons());
305 
306  //compute distance between ions
307  radius = sqrt(p.getPos().sqrDist(ions[ui].getPos()));
308 
309  //Do not record points that are at the same location
310  if(radius <=sqrt(std::numeric_limits<float>::epsilon()))
311  continue;
312 
313  unsigned int ionBin;
314  ionBin = (unsigned int) (radius/binStep);
315  if(ionBin == binCount && ionBin)
316  ionBin--;
317 
318  compositionTable[ionBin][ionId]++;
319  }
320 
321  #pragma omp critical
322  {
323  for(size_t uj=0;uj<compositionTable.size();uj++)
324  {
325  for(size_t uk=0;uk<compositionTable[uj].size();uk++)
326  {
327  overallCompositionTable[uj][uk]+=compositionTable[uj][uk];
328  }
329  }
330  }
331 
332  }
333  //--
334 
335  //force finalisation of progress bar
336  progressBar.update(100);
337 
338  //Convert the raw count data to composition frequency
339  //First entry is bin, second is ion ID
340  vector<vector<float> > compPct;
341 
342  compPct.resize(binCount);
343  for(unsigned int ui=0;ui<binCount;ui++)
344  computeComposition(overallCompositionTable[ui],compPct[ui]);
345 
346 
347  //switch from vector[bin][iontype] to vector[iontype][bin]
349 
350  //Compute mean composition, per ion
351  vector<float> meanComp;
352  meanComp.resize(rangeFile.getNumIons());
353 
354  //loop over ion types
355  for(unsigned int ui=0;ui<compPct.size();ui++)
356  {
357  double res;
358  res=0;
359  //loop over radius
360  for(unsigned int uj=0;uj<compPct[ui].size();uj++)
361  res+=compPct[ui][uj];
362 
363  res/=compPct[ui].size();
364  meanComp[ui]=res;
365  }
366 
367  //Zero-centre the concentration data
368  //loop over ions
369  for(unsigned int ui=0;ui<compPct.size();ui++)
370  {
371  //loop over radius
372  for(unsigned int uj=0;uj<compPct[ui].size();uj++)
373  compPct[ui][uj]-=meanComp[ui];
374  }
375 
376  vector<vector<float> > &deviationComp=compPct;
377 
378  //Compute the auto correlation function, per species
379  //---
380 
381  vector<vector<float> > autoCorrelateFunc;
382  vector<float> denominator;
383  autoCorrelateFunc.resize(binCount);
384  //Compute denominator
385  unsigned int numIons=rangeFile.getNumIons();
386  denominator.resize(numIons);
387  for(unsigned int ui=0;ui<numIons;ui++)
388  {
389  double normConstant;
390  normConstant=0;
391  for(unsigned int uj=0;uj<deviationComp[ui].size();uj++)
392  normConstant+=deviationComp[ui][uj]*deviationComp[ui][uj];
393 
394  //double->float conversion
395  denominator[ui] = normConstant;
396  }
397 
398  //Compute numerator. Note that in "Atom Probe Field Ion Microscopy"
399  // ISBN : 0198513879 ; pp 322, Equation 5.64 does not really handle
400  // boundary conditions, but instead reduces the autocorrelation lag maximum
401  autoCorrelateFunc.resize(numIons);
402  for(unsigned int ui=0;ui<numIons;ui++)
403  {
404  autoCorrelateFunc[ui].resize(binCount);
405  // Ki is the index to k in equation 5.64
406  for(unsigned int ki=0; ki<binCount; ki++)
407  {
408  double numValue;
409  numValue=0;
410  for(unsigned int uj=0;uj<binCount-ki; uj++)
411  {
412  numValue+=deviationComp[ui][uj]*deviationComp[ui][uj+ki];
413  }
414 
415  if(denominator[ui] > std::numeric_limits<float>::epsilon())
416  autoCorrelateFunc[ui][ki] = numValue / denominator[ui];
417  else
418  autoCorrelateFunc[ui][ki] = 0;
419 
420  }
421 
422 
423  }
424 
425  //Autocorrelation vector in form vector[ion][bin],
426  // so transpose to vector[bin][ion]
427  transposeVector(autoCorrelateFunc);
428 
429  if(outputFile.empty())
430  dumpAutocorrelation(autoCorrelateFunc,rangeFile,binStep,cerr);
431  else
432  {
433  cerr << "Wrote output to:" << outputFile << endl;
434  dumpAutocorrelation(autoCorrelateFunc,rangeFile,binStep,of);
435  std::ofstream debugOf;
436  std::string s;
437  s= outputFile + "-debugrdf";
438  debugOf.open(s.c_str());
439  dumpAutocorrelation(overallCompositionTable,rangeFile,binStep,debugOf);
440  }
441 
442 
443 }
float sqrDist(const Point3D &pt) const
Returns the square of distance to another Point3D.
Definition: point3D.cpp:272
void range(std::vector< IonHit > &ionHits) const
Clips out ions that are not inside the rangefile&#39;s ranges.
Definition: ranges.cpp:2783
void getBoundCube(BoundCube &b)
obtain the bounding rectangular prism volume for all elements in the KD tree
std::string getName(unsigned int ionID, bool shortName=true) const
Get the short name or long name of a specified ionID.
Definition: ranges.cpp:2975
void setProgressPointer(unsigned int *p)
unsigned int getIonID(float mass) const
Get the ion&#39;s ID from a specified mass.
Definition: ranges.cpp:2884
Point3D getPos() const
Definition: ionhit.cpp:117
const char * getPosFileErrString(unsigned int errMesg)
Definition: dataFiles.cpp:238
void computeComposition(const std::vector< unsigned int > &countData, std::vector< float > &compositionData)
Definition: composition.cpp:29
STL namespace.
3D specific KD tree
Definition: K3DTree-exact.h:54
void transposeVector(std::vector< std::vector< T > > &v)
Perform an out-of-place tranposition of a given vector.
Definition: misc.h:81
#define ASSERT(f)
size_t getOrigIndex(size_t treeIndex) const
void findUntaggedInRadius(const Point3D &queryPt, const BoundCube &b, float radius, std::vector< size_t > &result)
Find untagged points within a given radius.
unsigned int treeProgressValue
void sampleIons(const std::vector< IonHit > &ions, float sampleFactor, std::vector< IonHit > &sampled, bool strongRandom=true)
Definition: sampling.cpp:30
void setCallback(bool(*cb)(void))
int main(int argc, char *argv[])
void dumpAutocorrelation(const vector< vector< T > > &autoCorrelateFunc, const RangeFile &rangeFile, float binStep, std::ostream &strm)
float getMassToCharge() const
Definition: ionhit.cpp:65
unsigned int loadPosFile(std::vector< IonHit > &posIons, const char *posFile)
Load a pos file directly into a single ion list.
Definition: dataFiles.cpp:256
bool callback()
void init()
Draw the initial progress bar.
Definition: progress.cpp:35
Helper class to define a bounding cube.
Definition: boundcube.h:29
ProgressBar * treeProgressBar
void resetPts(std::vector< Point3D > &pts, bool clear=true)
Supply points to KD tree. Ouput vector will be erased if clear=true.
const unsigned int PROGRESS_BAR_SIZE
bool build()
Build the KD tree using the previously supplied points.
bool isFlat() const
Returns true if any bound is of null thickness.
Definition: boundcube.cpp:136
void setLength(unsigned int l)
Set the number of markers in the progress bar.
Definition: progress.h:40
This is a data holding class for POS file ions, from.
Definition: ionHit.h:36
Data storage and retrieval class for various range files.
Definition: ranges.h:95
bool stream_cast(T1 &result, const T2 &obj)
Template function to cast and object to another by the stringstream.
Definition: stringFuncs.h:32
unsigned int getNumIons() const
Get the number of unique ions.
Definition: ranges.cpp:2863
std::string getErrString() const
Retrieve the translated error associated with the current range file state.
Definition: ranges.cpp:3355
void update(unsigned int newProgress)
Draw the progress bar as needed, using the given progress value [0,100].
Definition: progress.cpp:58