libatomprobe
Library for Atom Probe Tomography (APT) computation
polefigure.cpp
Go to the documentation of this file.
1 
2 #include <iostream>
3 #include <cstdlib>
4 #include <fstream>
5 #include <memory>
6 #include <map>
7 
8 #include "atomprobe/atomprobe.h"
9 
10 #include <gsl/gsl_linalg.h>
11 
12 using namespace std;
13 using namespace AtomProbe;
14 
15 void gsl_print_matrix(gsl_matrix *m)
16 {
17  cerr << "[" << endl;
18  for (size_t i = 0; i < m->size1; i++)
19  {
20  for (size_t j = 0; j < m->size2; j++)
21  {
22  cerr << gsl_matrix_get(m,i,j) << " ";
23  }
24  cerr << endl;
25 
26  }
27  cerr << "]" << endl;
28 
29 }
30 
31 
33 
34 int main(int argc, char *argv[])
35 {
36  if(! (argc==6 || argc ==7 || argc == 8))
37  {
38  cerr << "Simulates the projection coordinates in an APT pole figure (simple cubic lattice), using the method of Perry & Brandon [1]. (Make spherical shell and project)" << endl;
39  cerr << endl;
40  cerr << "Usage: LATTICENAME SCALE_TO_LATTICE THICKNESS_TO_LATTICE FOCUS FLIGHT_PATH [Forwards_vector] [right_vector|rotation_angle]" << endl;
41  cerr << "LATTICENAME : Any of \"Simple\" (simple cubic), FCC or BCC." << endl;
42  cerr << "SCALE_TO_LATTICE : This ratio is the density of atoms to project, i.e the number of atoms in the simulation in one direction." << endl;
43  cerr << "THICKNESS_TO_LATTICE: Number of lattice slices to take from shell. Moore suggests 0.1" << endl;
44  cerr << "FOCUS : The centre projection point for the modified stereographic projection. Sometimes called \"Image compression factor\". Must be >=1" << endl;
45  cerr << "FLIGHT_PATH : The units here are arbitrary, and just scale the projection" << endl;
46 
47  cerr << "These values are dimensionless, as the projection problem can be expressed as a dimensionlessly (i.e. independently of lattice parameter)" << endl;
48  cerr << endl;
49 
50  cerr << endl;
51  cerr << endl;
52  cerr << "Forwards vector : The vector to place at the centre of the pole figure, eg 0,1,0" << endl;
53  cerr << "Right vector : The vector to place as the right-most point of the pole figure, eg 1,0,1. If provided, this must be orthogonal to the forwards vector" << endl;
54  cerr << " Instead of the right vector, you can provide a rotation angle (degrees). This will rotate the crystal around 0,0,1 (world) after the initial transformation" << endl;
55 
56  cerr << endl;
57  cerr << endl;
58 
59  cerr << "[1] A. J. Perry & D. G. Brandon, The bond structure of computer-simulated field-ion images, Philosophical Magazine" << endl;
60 
61  cerr << endl;
62  cerr << "Example:" << argv[0] << " 10 4 1" << endl;
63 
64  cerr << endl;
65  cerr << "You can rotate the pole centre by specifying the central atom vector, and the the right hand vector" << endl;
66  cerr << "Example:" << argv[0] << " 10 4 (1,1,1) (0,0,1)" << endl;
67 
68 
69  cerr << endl;
70  cerr << "Output:" << endl;
71  cerr << "The output is a 2-column vector with the detector positions. These are in the units given by the FLIGHT_PATH" << endl;
72  cerr << "To work out if these would land on any given detector, you must perform this computation externally to this program. All possible visible points from the projection will be shown" << endl;
73 
74  }
75 
76  //Parse numerical arguments
77  //---
78  float scaleToLattice,thicknessToLattice,focus,flightPath;
79  if(stream_cast(scaleToLattice,argv[2]))
80  return 1;
81  if(stream_cast(thicknessToLattice,argv[3]))
82  return 1;
83  if(stream_cast(focus,argv[4]))
84  return 1;
85  if(stream_cast(flightPath,argv[5]))
86  return 1;
87  //---
88 
89 
90  //Check if user specified orientation vectors
91  //----
92  bool specifiedForwards=false;
93  if(argc == 7)
94  specifiedForwards=true;
95  bool specifiedRight=false;
96  if(argc == 8)
97  specifiedRight=true;
98  //----
99 
100  //Check argument validity
101  //--
102  if(scaleToLattice <= 1e-5)
103  {
104  cerr << "Scale/Lattice ratio too small!" << endl;
105  return 1;
106  }
107 
108  if(thicknessToLattice <=1e-5)
109  {
110  cerr << "Thickness/lattice ratio too small" << endl;
111  return 1;
112  }
113 
114 
115  if(focus <=-1)
116  {
117  cerr << "Projection focus must be greater than -1" << endl;
118  return 1;
119  }
120 
121  if(flightPath < 0)
122  {
123  cerr << "Flight path must be positive" << endl;
124  return 1;
125  }
126  //--
127 
128 
129  //See if we want to post-rotate the crystal
130  float postRotateAngle=0.0f;
131  //Parse orientation vectors, if supplied
132  //----
133  bool rotate=false;
134  Point3D forwardsVec,rightVec;
135  if(specifiedForwards || specifiedRight)
136  {
137  if(!forwardsVec.parse(argv[6]))
138  {
139  cerr << "Error parsing forwards vector (centre of figure)" << endl;
140  return 1;
141  }
142  forwardsVec.normalise();
143 
144  if(specifiedRight)
145  {
146  if(!rightVec.parse(argv[7]))
147  {
148  if(stream_cast(postRotateAngle,argv[7]))
149  {
150  cerr << "Error parsing right vector/rotation angle " << endl;
151  return 1;
152  }
153  else
154  {
155  //Convert from deg to rad
156  postRotateAngle*=M_PI/180.0f;
157  rightVec=Point3D(0,0,1).crossProd(forwardsVec);
158  cerr << "Right vector is :" << rightVec << endl;
159  }
160  }
161  }
162  else
163  {
164  //Compute the right vector as normal to 0,0,1 and our new forwards vector
165  rightVec=Point3D(0,0,1).crossProd(forwardsVec);
166  cerr << "Right vector is :" << rightVec << endl;
167  }
168 
169  rightVec.normalise();
170 
171  if(fabs(forwardsVec.sqrMag() -1.0f) > 1e-3 ||
172  fabs(rightVec.sqrMag() -1.0f) > 1e-3)
173  {
174  cerr << "Input vectors could not be normalised." << endl;
175  return 1;
176  }
177 
178  if(fabs(forwardsVec.dotProd(rightVec)) > 1e-3)
179  {
180  cerr << "Requested pole figure vectors are not orthogonal. " << endl;
181  return 1;
182  }
183 
184  rotate=true;
185  }
186  else
187  {
188  forwardsVec=Point3D(0,0,1);
189  rightVec=Point3D(1,0,0);
190  }
191  //----
192 
193 
194  //Convert name of lattice to type identifier
195  //--
196  enum
197  {
198  LATTICE_SIMPLE_CUBIC,
199  LATTICE_FCC,
200  LATTICE_BCC,
201  };
202 
203  map<string,unsigned int> latticeNameMap;
204  latticeNameMap["simple"] = LATTICE_SIMPLE_CUBIC;
205  latticeNameMap["fcc"] = LATTICE_FCC;
206  latticeNameMap["bcc"] = LATTICE_BCC;
207 
208 
209  string latticeName=argv[1];
210  latticeName=lowercase(latticeName);
211 
212  unsigned int latticeType;
213  auto it =latticeNameMap.find(latticeName);
214  if(it== latticeNameMap.end())
215  {
216  cerr << "Unsupported or unknown lattice:" << latticeName << endl;
217  return 1;
218  }
219 
220  latticeType=it->second;
221 
222 
223 
224  //Generate a lattice in the upper quadrant
225  Point3D p(scaleToLattice,scaleToLattice,scaleToLattice);
226 
227  //Select the crystal lattice to generate
228  CrystalGen *gen;
229  switch(latticeType)
230  {
231  case LATTICE_SIMPLE_CUBIC:
232  gen=(new SimpleCubicGen(1,1,p));
233  break;
234  case LATTICE_FCC:
235  {
236  const float massToCharge[3]={1,1,1};
237  gen=(new FaceCentredCubicGen(1,1,massToCharge,p));
238  break;
239  }
240  case LATTICE_BCC:
241  {
242  gen=(new BodyCentredCubicGen(1,1,2,p));
243  break;
244  }
245  default:
246  {
247  cerr << "Unknown lattice. Aborting" << endl;
248  return 1;
249  }
250  }
251 
252  gen->generateLattice();
253  //Copy the lattice into all 8 quadrants
254  gen->mirrorOut();
255 
256  vector<Point3D> h;
257  gen->extractPositions(h);
258  cerr << "Full lattice contains :" << h.size() << "pts" << endl;
259 
260  delete gen;
261 
262  //Perform rotation, if required
263  //==
264  if(rotate)
265  {
266 
267  //Compute the rotation matrix between the two vectors
268  gsl_matrix *rMat = gsl_matrix_alloc(3,3);
269  computeRotationMatrix(Point3D(0,0,1),Point3D(1,0,0),forwardsVec,rightVec,rMat);
270 
271 
272  //Compute the inverse matrix to take us from {forwardsvec,rightvec} -> {(0,0,1),(1,0,0)}
273  // The inverse of an orthonomal matrix is transpose
274  gsl_matrix_transpose(rMat);
275 
276  for(unsigned int ui=0;ui<h.size();ui++)
277  h[ui].transform3x3(rMat);
278 
279  //User requested post-rotation to bring the pole figure into alignment
280  if(postRotateAngle!=0.0f)
281  {
282  float sR = sin(postRotateAngle);
283  float cR = cos(postRotateAngle);
284 #pragma omp parallel for
285  for(unsigned int ui=0;ui<h.size();ui++)
286  {
287  //Perform a 2D rotation of H
288  Point3D pt;
289  pt = h[ui];
290  pt[0] = h[ui][0]*cR - h[ui][1]*sR;
291  pt[1] = h[ui][0]*sR + h[ui][1]*cR;
292  h[ui] = pt;
293  }
294 
295  //compute the above rotation as a matrix
296  gsl_matrix *m=gsl_matrix_alloc(3,3);
297  gsl_matrix_set_identity(m);
298  gsl_matrix_set(m,0,0,cR);
299  gsl_matrix_set(m,0,1,-sR);
300  gsl_matrix_set(m,1,0,sR);
301  gsl_matrix_set(m,0,1,cR);
302 
303  //transpose to invert the operaiton
304  gsl_matrix_transpose(m);
305 
306  //multiply this by rMat
307  gsl_matrix *tmp=gsl_matrix_alloc(3,3);
308  gsl_matrix_mult(rMat,m,tmp);
309 
310  std::swap(tmp,rMat);
311  gsl_matrix_free(tmp);
312 
313  }
314 
315  //Transpose to obtain reverse transformation matrix
316  gsl_matrix_transpose(rMat);
317 
318  //Print the reverse matrix
319  cerr << "Reverse transformation (World -> crystal)" << endl;
320  cerr << "-----------------" << endl;
321  gsl_print_matrix(rMat);
322  cerr << "-----------------" << endl;
323 
324  gsl_matrix_free(rMat);
325  }
326 
327  //==
328  //Keep only the upper plane
329  {
330 
331  vector<Point3D> quadPts;
332  for(unsigned int ui=0;ui<h.size();ui++)
333  {
334  if(h[ui][2] >=0)
335  quadPts.push_back(h[ui]);
336  }
337 
338  h.swap(quadPts);
339  }
340 
341 
342  //Compute which points are to stay within the slice.
343  // discard the rest
344  // FIXME: This would be better written as lambda function passed to the generator
345  // to reduce memory requirements
346  vector<Point3D> pts;
347  float deltaMin=(scaleToLattice-thicknessToLattice)*(scaleToLattice-thicknessToLattice);
348  float deltaMax=(scaleToLattice)*(scaleToLattice);
349  for(size_t ui=0;ui<h.size();ui++)
350  {
351  float rSqr;
352  rSqr = ( h[ui].sqrMag());
353 
354  if( rSqr > deltaMin && rSqr < deltaMax)
355  pts.push_back(h[ui]);
356 
357  }
358  cerr << "Sliced out :" << pts.size() << " pts" << endl;
359 
360  if(!pts.size())
361  {
362  cerr << "No points outputted. Consider increasing your lattice density (scale/lattice) or slice thickness" << endl;
363  return 1;
364  }
365 
366 
367  savePosFile(pts,1,"sliced-lattice.pos");
368 
369  ModifiedFocusSphericProjection mSp(focus);
370  vector<pair<float,float> > pXY;
371  //The FOV reported by maxFOV is the full angle, not the half-angle
372  float maxFOV = mSp.getMaxFOV()/2.0f;
373  for(size_t ui=0;ui<pts.size() ;ui++)
374  {
375  //Convert from the spherical slice to spherical coordinates
376  float eta,phi,r;
377  pts[ui].getISOSpherical(r,eta,phi);
378 
379  //Convert from spherical coordinates to projection coords
380  float theta;
381  theta=mSp.etaToTheta(eta);
382  //project these coordinates using the above projection
383  float pX,pY;
384  if( eta < maxFOV && mSp.toPlanar(theta,phi,pX,pY))
385  {
386  pXY.push_back(make_pair(pX,pY));
387  }
388  }
389 
390  //Conversion factor to scale up to detector coordinates
391  // this arises from similar triangles, where one triangle
392  // is the m+1 (x-axis) and projected coord (p)
393  float flightFactor=flightPath/(focus+1);
394 
395 
396  ofstream fProj("projection.txt");
397  if(!fProj)
398  {
399  cerr << "Failed to open output file, aborting" << endl;
400  return 1;
401  }
402 
403 
404  //Print arguments used to call program to file
405  fProj << "# Arguments used: ";
406  for(unsigned int ui=1;ui<argc;ui++)
407  fProj << argv[ui] << " ";
408  fProj << endl;
409 
410  //record the projected XY coords, scaling up by the flight factor
411  for(size_t ui=0;ui<pXY.size();ui++)
412  {
413  fProj << pXY[ui].first*flightFactor << " , " << pXY[ui].second*flightFactor << endl;
414  }
415 
416  cerr << "Wrote detector coordinates to projection.txt" << endl;
417 
418  cerr << "Max FOV:" << mSp.getMaxFOV()*180.0/M_PI << endl;
419 
420 
421 
422 
423 
424  return 0;
425 }
void computeRotationMatrix(const Point3D &ur1, const Point3D &ur2, const Point3D &r1, const Point3D &r2, gsl_matrix *m)
Definition: rotations.cpp:83
virtual void mirrorOut()
Definition: generate.cpp:33
Point3D crossProd(const Point3D &pt) const
Calculate the cross product of this and another point.
Definition: point3D.cpp:285
void gsl_matrix_mult(const gsl_matrix *A, const gsl_matrix *B, gsl_matrix *&res, bool alloc=false)
Definition: misc.cpp:24
STL namespace.
virtual unsigned int generateLattice()=0
float sqrMag() const
Returns magnitude^2, taking this as a position vector.
Definition: point3D.cpp:332
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
A 3D point data class storage.
Definition: point3D.h:39
void gsl_print_matrix(gsl_matrix *m)
Definition: polefigure.cpp:15
bool parse(const std::string &s)
Set from string representation.
Definition: point3D.cpp:39
#define M_PI
Definition: kd-example.cpp:34
virtual bool toPlanar(float theta, float phi, float &fx, float &fy) const
Convert from stereographic (NOT SPHERICAL) coordinates to plane.
Definition: projection.cpp:327
float etaToTheta(float eta) const
Convert the spherical angle to an azimuthal angle. See docs/figures for examples. ...
Definition: projection.cpp:233
void normalise()
Make point unit magnitude, maintaining direction.
Definition: point3D.cpp:337
std::string lowercase(std::string s)
Return a lowercase version for a given string.
float getMaxFOV() const
Obtain angle spherical angle, eta, (i.e. angle from point of sphere, sphere centre and x axis) of the...
Definition: projection.cpp:287
int main(int argc, char *argv[])
Definition: polefigure.cpp:34
bool stream_cast(T1 &result, const T2 &obj)
Template function to cast and object to another by the stringstream.
Definition: stringFuncs.h:32
float dotProd(const Point3D &pt) const
Calculate the dot product of this and another point.
Definition: point3D.cpp:279
virtual void extractPositions(std::vector< Point3D > &data)
Definition: generate.cpp:23