libatomprobe
Library for Atom Probe Tomography (APT) computation
dataFiles.cpp
Go to the documentation of this file.
1 /* dataFiles.cpp: Atom probe data file handling routines
2  * Copyright (C) 2014 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 
18 #include "atomprobe/io/dataFiles.h"
23 #include "helper/helpFuncs.h"
24 
26 
27 #include <vector>
28 #include <string>
29 #include <fstream>
30 #include <map>
31 #include <algorithm>
32 #include <sys/time.h>
33 
34 
35 namespace AtomProbe{
36 
37 using std::vector;
38 using std::string;
39 using std::ifstream;
40 using std::ofstream;
41 
42 //Pos file structure
43 //===========
45 typedef struct IONHIT
46 {
47 float pos[3];
49 } IONHIT;
50 //=========
51 
52 
53 enum
54 {
55  //Pos file constants
63 };
64 
65 enum
66 {
67  //tapsim loading error constants
70 };
71 
72 
73 const char *ATO_ERR_STRINGS[] = { "",
74  "Error opening file",
75  "File is empty",
76  "Filesize does not match expected format",
77  "File version number not <4, as expected",
78  "Unable to allocate memory to store data",
79  "Unable to detect endian-ness in file"
80  };
81 
82 const char *RECORDREAD_ERR_STRINGS[] = { "",
83  "Unable to determine filesize",
84  "Filesize indicates that file contains a non-integer number of entries",
85  "Unable to open file",
86  "Unable to allocate memory for reading file contents",
87  "Unable to perform read operation on file",
88  "Read past end of file requested",
89  "Entry in file appears to be invalid",
90  };
91 
92 template<class T>
93 unsigned int fixedRecordReader(const char *filename, bool (*recordReader)(const char *bufRead, const char *destBuf),
94  size_t recordSize, std::vector<T> &outputData)
95 {
96  size_t fileSize;
97  if(!getFilesize(filename, fileSize))
99 
100 
101  //Check we have an even number of entries
102  if(fileSize % recordSize )
104 
105  std::ifstream f(filename);
106 
107  if(!f)
109 
110 
111  size_t numFileEntries = fileSize/recordSize;
112 
113  char *buffer;
114  try{
115  buffer = new char[recordSize];
116  outputData.resize(numFileEntries);
117  }
118  catch(std::bad_alloc)
119  {
120  return RECORDREAD_ERR_NOMEM;
121  }
122 
123  for(size_t ui=0;ui<numFileEntries;ui++)
124  {
125 
126  if(!f.read(buffer,recordSize))
127  {
128  delete[] buffer;
130  }
131 
132  if(!(*recordReader)(buffer,(char *)(&outputData[ui])))
133  {
134  delete[] buffer;
135  return RECORDREAD_BAD_RECORD;
136  }
137  }
138 
139  delete[] buffer;
140  return 0;
141 }
142 
143 template<class T>
144 unsigned int fixedRecordChunkReader(const char *filename, bool (*recordReader)(const char *bufRead, const char *destBuf),
145  size_t recordSize, std::vector<T> &outputData,
146  unsigned int chunkSize,unsigned int chunkOffset, unsigned int &nEntriesLeft)
147 {
148  //Chunk size is in units of records
149  //Chunk offset is in units of chunks
150  //nEntriesLeft is in units of records
151 
152  //Obtain the size of the file
153  size_t fileSize;
154  if(!getFilesize(filename, fileSize))
156 
157  //Check we have an even number of entries
158  if(fileSize % recordSize )
160 
161  //Open file
162  std::ifstream f(filename);
163  if(!f)
165 
166 
167  size_t numFileEntries = fileSize/recordSize;
168  unsigned int entryOffset=chunkOffset*chunkSize; //offset in records
169 
170  //Caller should not specify read start from beyond file end
171  if(entryOffset > numFileEntries)
173 
174 
175  //Read either the minimum number of entries, or a chunk of entries
176  size_t nEntriesToRead = std::min(numFileEntries-entryOffset,(size_t)chunkSize);
177 
178  //If no entries to read, finished
179  if(!nEntriesToRead)
180  {
181  nEntriesLeft=0;
182  return 0;
183  }
184 
185  char *buffer;
186  try{
187  buffer = new char[recordSize];
188  outputData.resize(nEntriesToRead);
189  }
190  catch(std::bad_alloc)
191  {
192  return RECORDREAD_ERR_NOMEM;
193  }
194 
195  //Start loading chunks
196  f.seekg(entryOffset*recordSize);
197 
198  for(size_t ui=0; ui<nEntriesToRead; ui++)
199  {
200  //Read one record
201  if(!f.read(buffer,recordSize))
202  {
203  delete[] buffer;
205  }
206  //call reader function though func pointer
207  if(!(*recordReader)(buffer,(char *)(&outputData[ui])))
208  {
209  delete[] buffer;
210  return RECORDREAD_BAD_RECORD;
211  }
212  }
213 
214  nEntriesLeft=numFileEntries-(chunkOffset*chunkSize+nEntriesToRead);
215 
216  delete[] buffer;
217  return 0;
218 }
219 
220 
221 
222 
224 {
225  h[0]=x;
226  h[1]=y;
227  h[2]=z;
228  h[3]=massToCharge;
229 }
230 
232 {
233  IonHit h;
234  getIonHit(h);
235  return h;
236 }
237 
238 const char *getPosFileErrString(unsigned int errCode)
239 {
240  const char *posErrStrings[] = { "",
241  "Memory allocation failure on POS load",
242  "Error opening file for load",
243  "Pos file size appears to have non-integer number of entries",
244  "Pos file empty",
245  "Error reading from pos file (after open)",
246  "Error - Found NaN in pos file",
247  ""
248  };
249 
250  static_assert( ARRAYSIZE(posErrStrings)-1 == POS_FILE_ENUM_END, "POS Error string size and enum mismatched");
251 
252  return posErrStrings[errCode];
253 }
254 
256 unsigned int loadPosFile(vector<IonHit> &posIons,const char *posFile)
257 {
258 
259 
260  //buffersize must be a power of two and at least sizeof(IONHIT)
261  const unsigned int BUFFERSIZE=8192;
262  char *buffer=new char[BUFFERSIZE];
263 
264  if(!buffer)
265  return POS_ALLOC_FAIL;
266 
267  //open pos file
268  std::ifstream CFile(posFile,std::ios::binary);
269 
270  if(!CFile)
271  {
272  delete[] buffer;
273  return POS_OPEN_FAIL;
274  }
275 
276  CFile.seekg(0,std::ios::end);
277  std::ifstream::pos_type fileSize=CFile.tellg();
278  CFile.seekg(0,std::ios::beg);
279 
280  if(!fileSize)
281  {
282  delete[] buffer;
283  return POS_SIZE_EMPTY_ERR;
284  }
285 
286  //calculate the number of points stored in the POS file
287  IonHit hit;
288  unsigned int pointCount=0;
289  //regular case
290  std::ifstream::pos_type curBufferSize=BUFFERSIZE;
291 
292  if(fileSize % sizeof(IONHIT))
293  {
294  delete[] buffer;
295  return POS_SIZE_MODULUS_ERR;
296  }
297 
298  while(fileSize < curBufferSize)
299  curBufferSize = curBufferSize >> 1;
300 
301  try
302  {
303  posIons.resize(fileSize/(sizeof(IONHIT)));
304  }
305  catch(std::bad_alloc)
306  {
307  delete[] buffer;
308  return POS_ALLOC_FAIL;
309  }
310 
311 
312  do
313  {
314  while(CFile.tellg() <= fileSize-curBufferSize)
315  {
316  ASSERT(curBufferSize >= sizeof(IONHIT));
317  CFile.read(buffer,curBufferSize);
318  if(!CFile.good())
319  {
320  delete[] buffer;
321  return POS_READ_FAIL;
322  }
323 
324  IONHIT *hitStruct;
325  hitStruct = (IONHIT *)buffer;
326 
327 
328  unsigned int ui;
329  for(ui=0; ui<curBufferSize; ui+=(sizeof(IONHIT)))
330  {
331  hit.setPos(Point3D(hitStruct->pos));
332  hit.setMassToCharge(hitStruct->massToCharge);
333  //Data bytes stored in pos files are big
334  //endian. flip as required
335  #ifdef __LITTLE_ENDIAN__
336  hit.switchEndian();
337  #endif
338 
339  if(hit.hasNaN())
340  {
341  delete[] buffer;
342  return POS_NAN_LOAD_ERROR;
343  }
344 
345  posIons[pointCount] = hit;
346 
347  pointCount++;
348  hitStruct++;
349  }
350 
351  }
352 
353  curBufferSize = curBufferSize >> 1 ;
354  }while(curBufferSize >= sizeof(IONHIT));
355 
356  ASSERT((unsigned int)CFile.tellg() == fileSize);
357  ASSERT(pointCount*sizeof(IONHIT) == fileSize);
358  ASSERT(pointCount == posIons.size());
359  delete[] buffer;
360 
361  return 0;
362 }
363 
364 unsigned int loadPosFile(vector<IonHit> &posIons, const char *posFile, unsigned int limitCount)
365 {
366 
367 
368  //open pos file
369  std::ifstream CFile(posFile,std::ios::binary);
370 
371  if(!CFile)
372  return POS_OPEN_FAIL;
373 
374  CFile.seekg(0,std::ios::end);
375  auto fileSize=CFile.tellg();
376 
377  if(!fileSize)
378  return POS_SIZE_EMPTY_ERR;
379 
380  CFile.seekg(0,std::ios::beg);
381 
382  const unsigned int IONHIT_SIZE=16;
383  if(fileSize % IONHIT_SIZE)
384  return POS_SIZE_MODULUS_ERR;
385 
386  auto maxIons =fileSize/IONHIT_SIZE;
387  limitCount=std::min(limitCount,(unsigned int)maxIons);
388 
389  //If we are going to load the whole file, don't use a sampling method to do it.
390  if(limitCount == maxIons)
391  {
392  //Close the file
393  CFile.close();
394  //Try opening it using the normal functions
395  return loadPosFile(posIons,posFile);
396  }
397 
398  //Use a sampling method to load the pos file
399  std::vector<size_t> ionsToLoad;
400 
401  //Init GSL random generator
402  gsl_rng *rng = AtomProbe::randGen.getRng();
403  try
404  {
405  //Allocate space
406  posIons.resize(limitCount);
407 
408  randomIndices(ionsToLoad,limitCount,maxIons,rng);
409 
410  }
411  catch(std::bad_alloc)
412  {
413  return POS_ALLOC_FAIL;
414  }
415 
416 
417  //sort ions to be in increasing size
418  //NOTE: I tried to use a functor here to get progress
419  // It was not stable with parallel sort
420  std::sort(ionsToLoad.begin(),ionsToLoad.end());
421 
422 
423  //TODO: probably not very nice to the disk drive. would be better to
424  //scan ahead for contiguous data regions, and load that where possible.
425  //Or switch between different algorithms based upon ionsToLoad.size()/
426  std::ios::pos_type nextIonPos;
427 
428  char buffer[IONHIT_SIZE];
429  for(size_t ui=0;ui<ionsToLoad.size(); ui++)
430  {
431  nextIonPos = ionsToLoad[ui]*IONHIT_SIZE;
432 
433  if(CFile.tellg() !=nextIonPos )
434  CFile.seekg(nextIonPos);
435 
436 
437  CFile.read(buffer,IONHIT_SIZE);
438 
439  if(!CFile.good())
440  return POS_READ_FAIL;
441 
442  IONHIT *hitStruct;
443  hitStruct = (IONHIT*)buffer;
444  posIons[ui].setPos(Point3D(hitStruct->pos));
445  posIons[ui].setMassToCharge(hitStruct->massToCharge);
446 
447  //Data bytes stored in pos files are big
448  //endian. flip as required
449  #ifdef __LITTLE_ENDIAN__
450  posIons[ui].switchEndian();
451  #endif
452 
453  if(posIons[ui].hasNaN())
454  return POS_NAN_LOAD_ERROR;
455 
456 
457  }
458 
459  return 0;
460 }
461 
463 unsigned int savePosFile(const vector<IonHit> &ionVec, const char *filename, bool append)
464 {
465  auto flags = std::ios::binary;
466  if(append)
467  flags|=std::ios::app;
468 
469  std::ofstream CFile(filename,flags);
470  float floatBuffer[4];
471 
472  if(!CFile)
473  return 1;
474 
475  if(append)
476  CFile.seekp(std::ios::end);
477 
478 
479  for(unsigned int ui=0; ui<ionVec.size(); ui++)
480  {
481 
482  for(unsigned int uj=0;uj<4;uj++)
483  {
484  floatBuffer[uj]=ionVec[ui][uj];
485 #ifdef __LITTLE_ENDIAN__
486  floatSwapBytes(floatBuffer+uj);
487 #endif
488  }
489  CFile.write((char *)floatBuffer,4*sizeof(float));
490 
491 
492  if(!CFile)
493  return 1;
494  }
495  return 0;
496 }
497 
499 unsigned int savePosFile(const vector<Point3D> &ptVec, float mass, const char *filename, bool append)
500 {
501  auto flags = std::ios::binary;
502  if(append)
503  flags|=std::ios::app;
504 
505  std::ofstream CFile(filename,flags);
506  float floatBuffer[4];
507 
508  if(!CFile)
509  return 1;
510 
511  if(append)
512  CFile.seekp(std::ios::end);
513 
514 #ifdef __LITTLE_ENDIAN__
515  floatSwapBytes(&mass);
516 #endif
517 
518  for(unsigned int ui=0; ui<ptVec.size(); ui++)
519  {
520 
521  for(unsigned int uj=0;uj<3;uj++)
522  {
523  floatBuffer[uj]=ptVec[ui][uj];
524 #ifdef __LITTLE_ENDIAN__
525  floatSwapBytes(floatBuffer+uj);
526 #endif
527  }
528  CFile.write((char *)floatBuffer,3*sizeof(float));
529  CFile.write((char *)&mass,1*sizeof(float));
530 
531  if(!CFile)
532  return 1;
533  }
534  return 0;
535 }
536 
537 //Load a TAPSIM Binfile
538 unsigned int loadTapsimBinFile(vector<IonHit> &posIons, const char *posfile)
539 {
540  ifstream f(posfile,std::ios::binary);
541 
542  if(!f)
543  return TAPSIM_OPEN_FAIL;
544 
545  f.seekg(0,std::ios::end);
546  size_t fileSize;
547  fileSize = f.tellg();
548  f.seekg(0,std::ios::beg);
549 
550  //TAPSIM's binary geometery input format is not totally clearly documented
551  // but an example is provided. So best efforts are us.
552 
553  std::string str;
554  getline(f,str);
555 
556  vector<string> s;
557  splitStrsRef(str.c_str(),' ',s);
558 
559  const unsigned int HEADER_ENTRIES=4;
560  if(s.size() != HEADER_ENTRIES)
562 
563  bool binaryMode;
564 
565 
566  if(s[0] == "BINARY")
567  {
568  //Payload is in binary format
569  binaryMode=true;
570  }
571  else if(s[0] == "ASCII")
572  {
573  //payload is in ascii format
574  binaryMode=false;
575  }
576  else
578 
579  //maximum size of a TAPSIM record, in bytes
580  unsigned int MAX_RECORD_SIZE=18;
581  char *buffer=new char[MAX_RECORD_SIZE];
582  if(binaryMode)
583  {
584  size_t numEntries;
585  if(stream_cast(numEntries,s[1]))
586  {
587  delete[] buffer;
589  }
590 
591  //ID specifies atom type
592  bool idsPresent;
593  //There is an inconsistency between the example files given and the documentation
594  // The documentation says the ids are optional. The examples have the option disabled,
595  // but provide the IDs anyway - this changes the payload size.
596  if(s[2] == "1" || s[2] == "0" )
597  idsPresent=true;
598  else
599  {
600  delete[] buffer;
602  }
603 
604 
605  size_t numbersPresent;
606  if(s[3] == "1" )
607  numbersPresent=true;
608  else if (s[3] == "0")
609  numbersPresent=false;
610  else
611  {
612  delete[] buffer;
614  }
615 
616 
617  //Contrary to docuemntation, ids appear to be "short", not "unsigned int"
618  size_t recordSize=( (numbersPresent ? 1:0 )*sizeof(unsigned int) +
619  3*sizeof(float) + (idsPresent ?1:0)*sizeof(short));
620 
621  //Check that payload size matches expected size
622  if(fileSize -f.tellg() !=recordSize*numEntries)
623  {
624  delete[] buffer;
626  }
627 
628  IonHit h;
629  posIons.resize(numEntries);
630  for(size_t ui=0;ui<numEntries; ui++)
631  {
632  f.read(buffer,recordSize);
633 
634  //Transfer position data
635 #ifdef __LITTLE_ENDIAN__
636  h.setPos(Point3D((float*)(buffer)));
637 #elif __BIG_ENDIAN__
638  static_assert(false,"Big endian"); //TODO: IMPLEMENT ME
639 #endif
640  //assign the ID as the mass to charge
641  h.setMassToCharge(*( (short*)(buffer+12) ));
642 
643  posIons[ui]=h;
644  }
645 
646 #pragma omp parallel for
647  for(size_t ui=0;ui<numEntries;ui++)
648  {
649  //rescale back to nm, rather than in m
650  posIons[ui].setPos(posIons[ui].getPos()*1e9);
651  }
652  }
653  else
654  {
655  //TODO: IMPLEMENT ME
656  delete[] buffer;
658  }
659 
660  delete[] buffer;
661  return 0;
662 }
663 
664 
665 //write a TAPSIM binary file
666 //FIXME: Improved error reporting
667 unsigned int saveTapsimBin( const vector<IonHit> &posIons,std::ostream &f)
668 {
669  std::string data,tmp;
670  data="BINARY ";
671  stream_cast(tmp,posIons.size());
672  data+=tmp + " 0 0\n";
673 
674  f.write(data.c_str(),data.size());
675 
676 
677  ASSERT(sizeof(short) == 2);
678  for(size_t ui=0;ui<posIons.size();ui++)
679  {
680  float floats[3];
681  short id;
682 
683  posIons[ui].getPos().copyValueArr(floats);
684 
685  //shrink from "nm" to "m" as natural unit
686  for(size_t uj=0;uj<3;uj++)
687  {
688 #ifdef __BIG_ENDIAN__
689  floatSwapBytes(floats[uj]);
690 #endif
691  floats[uj]*=1e-9;
692  }
693 
694  id=posIons[ui].getMassToCharge();
695  f.write((const char *)floats,sizeof(float)*3);
696  f.write((const char *)&id,sizeof(short));
697 
698  }
699 
700  return 0;
701 }
702 
703 unsigned int saveTapsimBin( const vector<IonHit> &posIons,const char *filename)
704 {
705  ofstream f(filename,std::ios::binary);
706  if(!f)
707  return 1;
708 
709  return saveTapsimBin(posIons,f);
710 }
711 bool readEposRecord(const char *src, const char *dest)
712 {
713  static_assert(sizeof(float) == 4,"size of float not 4");
714 
715  EPOS_ENTRY *entry=(EPOS_ENTRY*)dest;
716  float *srcPtr = (float*)src;
717 
718  //Perform byte swapping and check nan/inf
719  for(size_t ui=0;ui<9;ui++)
720  {
721  floatSwapBytes(srcPtr+ui);
722  //Disallow NaNs/inf
723  if( std::isnan(srcPtr[ui]) || std::isinf(srcPtr[ui]))
724  return false;
725 
726 
727  }
728 
729  //Map the buffer locations to the locations within the struct
730  // remember that you cannot simply do a memcpy - this won't be portable
731  // due to struct alignment
732  entry->x= srcPtr[0];
733  entry->y= srcPtr[1];
734  entry->z= srcPtr[2];
735  entry->massToCharge= srcPtr[3];
736  entry->timeOfFlight= srcPtr[4];
737  entry->voltDC= srcPtr[5];
738  entry->voltPulse= srcPtr[6];
739  entry->xDetector= srcPtr[7];
740  entry->yDetector= srcPtr[8];
741 
742 
743  //FIXME: Byte swapping!
744 
745  int4SwapBytes((int32_t*)(src+(4*9)));
746  int4SwapBytes((int32_t*)(src+(4*10)));
747  entry->deltaPulse = *((int32_t*)(src+(4*9)));
748  entry->hitMultiplicity = *((int32_t*)(src+(4*10)));
749 
750 
751 
752  return true;
753 }
754 
755 
756 size_t loadEposFile(std::vector<EPOS_ENTRY> &outData,const char *filename)
757 {
758  static_assert(sizeof(EPOS_ENTRY) >= EPOS_RECORD_SIZE, "EPOS_RECORD_SIZE");
759 
760  size_t errcode;
761  errcode=fixedRecordReader(filename,readEposRecord, EPOS_RECORD_SIZE,outData);
762  return errcode;
763 }
764 
765 size_t chunkLoadEposFile(std::vector<EPOS_ENTRY> &outData, const char *filename,
766  unsigned int nChunksToRead, unsigned int chunkOffset, unsigned int &nChunksLeft)
767 {
768  static_assert(sizeof(EPOS_ENTRY) >= EPOS_RECORD_SIZE, "EPOS_RECORD_SIZE");
769 
770  size_t errcode;
772  outData,nChunksToRead,chunkOffset,nChunksLeft);
773  return errcode;
774 }
775 
776 const char *OPS_ENUM_ERRSTRINGS[] =
777 {
778  "",
779  "\"C\" line (exp. parameters) not in expected format",
780  "\"I\" line (Detector parameters) not in expected format",
781  "\"-\" line (Time-of-flights) not in expected format",
782  "\"V\" line (voltage data) not in expected format",
783  "Error interpreting \"V\" line (voltage data) data",
784  "Missing beta value from \"V\" line - needs to be in voltage, or in system header (\"C\" line",
785  "Incorrect number of \"V\" line (voltage data) data entries",
786  "Unknown linetype in data file",
787  "\"P\" line (detector channels) not in expected format",
788  "Unable to interpret channels line",
789  "Incorrect number of events in \"S\" (hit position) line",
790  "Unable to interpret event data on \"S\" (hit position) line",
791  "Unable to parse \"S\" (hit position) line event count (eg 8 in S8 ...)",
792  "\"S\" (hit position) line data not preceded by TOF data, as it should have been",
793  "Duplicate system data/experiment setup (\"C\") entry found -- there can only be one",
794  "Duplicate detector (\"I\") data entry found -- there can only be one",
795  "Trailing \"-\" line found -- should have be followed by a \"S\" line, but wasn't.",
796  "Duplicate\"-\" line found -- should have be followed by a \"S\" line, but wasn't.",
797  "Unable to open file",
798  "unable to read file, after opening",
799  "Abort requested"
800 };
801 
802 //Function to read POSAP "OPS" files, which contain data
803 //on voltage, positioned hits and time of flights
804 //as well as some instrument parameters
805 unsigned int readPosapOps(const char *file,
806  THREEDAP_EXPERIMENT &data,
807  unsigned int &badLine, unsigned int &progress,
808  std::atomic<bool> &wantAbort, unsigned int nDelayLines,bool strictMode)
809 {
810 
811  static_assert(ARRAYSIZE(OPS_ENUM_ERRSTRINGS) == OPSREADER_ENUM_END, "OPS error strings and error code enum sizes mismatched");
812 
813  const char COMMENT_MARKER='*';
814 
815  badLine=0;
816 
817  size_t totalFilesize;
818  if(!getFilesize(file,totalFilesize))
819  return OPSREADER_OPEN_ERR;
820 
821  std::ifstream f(file);
822 
823  if(!f)
824  return OPSREADER_OPEN_ERR;
825 
826 
827  enum
828  {
829  OPS_LINE_DASH,
830  OPS_LINE_OTHER_OR_NONE
831  };
832 
833  bool haveSystemParams=false;
834  bool haveDetectorSize=false;
835 
836  unsigned int lastLine=OPS_LINE_OTHER_OR_NONE;
837 
838  vector<string> strVec;
839  while(!f.eof())
840  {
841  std::string strLine;
842  getline(f,strLine);
843  badLine++;
844 
845 
846  if(f.eof())
847  break;
848 
849  if(!f.good())
850  return OPSREADER_READ_ERR;
851 
852  //If user wants to abort, please do so
853  if(wantAbort)
854  return OPSREADER_ABORT_ERR;
855 
856  //Remove any ws delimiters
857  strLine=stripWhite(strLine);
858 
859  //Is a blank or comment? Don't process line
860  if(!strLine.size() || strLine[0] == COMMENT_MARKER)
861  continue;
862 
863  //Check to see what type of line this is
864  strVec.clear();
865 
866  splitStrsRef(strLine.c_str(),"\t ",strVec);
867  stripZeroEntries(strVec);
868  switch(strLine[0])
869  {
870  //"C" line contains the system parameters
871  // flight path, pulse coupling coefficients (alpha, beta -- see Sebastien et al) and time zero offset.
872  // "New method for the calibration of three-dimensional atom-probe mass"
873  // Review of Scientific Instruments, 2001
874  case 'C':
875  {
876  if(haveSystemParams)
878 
879  haveSystemParams = true;
880 
881  //Should have something eg
882  // C 123 1.01 0.8 -123
883  if(strVec.size() != 5)
885 
886  if(stream_cast( data.params.flightPath,strVec[1]))
888 
889  if(stream_cast( data.params.alpha,strVec[2]))
891 
892  if(stream_cast( data.params.beta,strVec[3]))
894 
895  if(stream_cast( data.params.tZero,strVec[4]))
897 
898  lastLine=OPS_LINE_OTHER_OR_NONE;
899  break;
900  }
901  //"I" Line contains detector radius
902  case 'I':
903  {
904  if(haveDetectorSize)
906 
907  haveDetectorSize=true;
908 
909  //Should have I or IR
910  //I - No reflectron
911  //IR - reflectron
912  //and a floating pt number
913  if(strVec.size() != 2)
915 
916  if(stream_cast(data.params.detectorRadius,strVec[1]))
918  lastLine=OPS_LINE_OTHER_OR_NONE;
919 
920  break;
921  }
922  //"S" line contains the hit TOFs, and hit frequency
923  case 'S':
924  {
925  //OK, so this is the actual data -- each "S"" line should be
926  //preceded by a "-" line.
927  if(lastLine !=OPS_LINE_DASH)
928  {
929  if(strictMode)
931  else
932  {
933  continue; //OK, this is kinda a confusing situation, lets just get to the next line, which we hope is an S.
934  }
935  }
936 
937 
938 
939  //If we have less positions than TOFs, then something is wrong
940  //-- we have to discard the previous "-" line
941  // could have a wierd line that makes no sense (for eg, I found this):
942  //
943  //-151 13
944  //S0
945  // I assume that this means that
946  if((strVec.size()-1) /(nDelayLines+1)< data.eventData.back().size())
947  {
948  ASSERT(data.eventData.size())
949  //We have to discard the previous hit sequence
950  data.eventData.pop_back();
951  lastLine=OPS_LINE_OTHER_OR_NONE;
952  break;
953  }
954 
955 
956  //OK, so the event numbers are coded like
957  //"S8", where S indicates it is an event
958  //line, and the next numbers indicate the
959  //number of observed events
960  strVec[0]= strVec[0].substr(1);
961 
962  unsigned int numEvents;
963  if(stream_cast(numEvents,strVec[0]))
964  {
965  if(strictMode)
967  else
968  {
969  data.eventData.pop_back();
970  lastLine=OPS_LINE_OTHER_OR_NONE;
971  break;
972  }
973  }
974 
975 
976  if(numEvents != (strVec.size()-1)/(nDelayLines+1))
978 
979 
980  vector<SINGLE_HIT> &curHits=data.eventData.back();
981 
982  //The TOF must be preset by a "-" line, as checked above.
983  //The contents of this "S" line must have at least enough elements
984  //to cover the TOFs. The other hits are assigned a zero TOF
985  if(curHits.size()> numEvents)
986  {
987  if(strictMode)
989  else
990  {
991  data.eventData.pop_back();
992  lastLine=OPS_LINE_OTHER_OR_NONE;
993  break;
994  }
995  }
996 
997 
998  SINGLE_HIT h;
999  h.tof=0;
1000  curHits.resize(numEvents,h);
1001  vector<float> tofs;
1002 
1003  //Retrieve the TOF values, so we can redirect them
1004  //as the TOF values above's correspondence map
1005  //is stored at the end of the hit sequence, one
1006  //entry per hit
1007  //zero means untimed, otherwise TOF
1008  tofs.resize(curHits.size());
1009  for(unsigned int ui=0;ui<curHits.size();ui++)
1010  tofs[ui]=curHits[ui].tof;
1011 
1012 
1013  //Read up until the TOF timing map
1014  bool everythingOK=true;
1015  unsigned int timingIndex=curHits.size()*2+1;
1016  for(unsigned int ui=1;ui<timingIndex;ui+=2)
1017  {
1018  //Push the current hit into a vector
1019  unsigned int offset;
1020  offset=ui/2;
1021 
1022  if(stream_cast(curHits[offset].x,strVec[ui]))
1023  {
1024  if(strictMode)
1026  else
1027  {
1028  everythingOK=false;
1029  break;
1030  }
1031 
1032  }
1033 
1034  if(stream_cast(curHits[offset].y,strVec[ui+1]))
1035  {
1036  if(strictMode)
1038  else
1039  {
1040  everythingOK=false;
1041  break;
1042  }
1043  }
1044  }
1045 
1046  if(!everythingOK)
1047  {
1048  data.eventData.pop_back();
1049  lastLine=OPS_LINE_OTHER_OR_NONE;
1050  break;
1051  }
1052 
1053  vector<unsigned int> timeMap;
1054  //Read the TOF timing map
1055  for(unsigned int ui=timingIndex; ui<strVec.size();ui++)
1056  {
1057  unsigned int mapEntry;
1058  if(stream_cast(mapEntry,strVec[ui]))
1059  {
1060  if(strictMode)
1062  else
1063  {
1064  everythingOK=false;
1065  break;
1066 
1067  }
1068  }
1069 
1070  timeMap.push_back(mapEntry);
1071  }
1072 
1073  if(!everythingOK)
1074  {
1075  data.eventData.pop_back();
1076  lastLine=OPS_LINE_OTHER_OR_NONE;
1077  break;
1078  }
1079  ASSERT(timeMap.size() == curHits.size());
1080 
1081  //use the TOF timing map to assign TOF values
1082  //to observed TOF entries from the "-" line
1083  for(unsigned int ui=0;ui<curHits.size();ui++)
1084  {
1085  if(timeMap[ui])
1086  curHits[ui].tof=tofs[timeMap[ui]-1];
1087  else
1088  curHits[ui].tof=0;
1089  }
1090 
1091  lastLine=OPS_LINE_OTHER_OR_NONE;
1092  break;
1093  }
1094  //"-" line contains the detector hit positions
1095  case '-':
1096  {
1097  if(lastLine==OPS_LINE_DASH)
1099 
1100  if(strVec.size() < 2)
1102 
1103 
1104  size_t pulseDelta;
1105  if(stream_cast(pulseDelta,strVec[0].substr(1)))
1107 
1108  //First bit of string is the number of pulses
1109  //preceding this event
1110  if(data.eventPulseNumber.empty())
1111  data.eventPulseNumber.push_back(pulseDelta+1);
1112  else
1113  {
1114  data.eventPulseNumber.push_back(
1115  data.eventPulseNumber.back()+pulseDelta+1);
1116  }
1117 
1118  vector<float> tofs;
1119  for(unsigned int ui=1; ui< strVec.size(); ui++)
1120  {
1121  float timeOfFlight;
1122  if(stream_cast(timeOfFlight,strVec[ui]))
1124 
1125  tofs.push_back(timeOfFlight);
1126  }
1127 
1128  vector<SINGLE_HIT> s;
1129 
1130  s.resize(tofs.size());
1131  for(unsigned int ui=0;ui<tofs.size();ui++)
1132  s[ui].tof =tofs[ui];
1133  data.eventData.push_back(s);
1134 
1135  lastLine=OPS_LINE_DASH;
1136  break;
1137  }
1138  //\"V\" voltage data
1139  case 'V':
1140  {
1141 
1142  //V line may have
1143  if(! (strVec.size() == 4 || strVec.size() == 3) )
1145 
1146  VOLTAGE_DATA vDat;
1147  if(stream_cast(vDat.voltage,strVec[1]))
1149 
1150  float pulseV;
1151  if(stream_cast(pulseV,strVec[2]))
1153  vDat.pulseVolt=pulseV;
1154 
1155  if(strVec.size() ==4)
1156  {
1157  if(stream_cast(vDat.beta,strVec[3]))
1159  }
1160  else // 3 items
1161  {
1162  //If we have not seen the system parameter's beta, then we cannot compute a good effective voltage
1163  if(!haveSystemParams)
1165  vDat.beta = data.params.beta;
1166  }
1167 
1168  vDat.nextHitGroupOffset=data.eventData.size();
1169  data.voltageData.push_back(vDat);
1170  lastLine=OPS_LINE_OTHER_OR_NONE;
1171  break;
1172  }
1173  case 'P':
1174  {
1175  if(strVec.size() != 2)
1177 
1178  if(stream_cast(data.params.detectorChannels,strVec[1]))
1180  break;
1181  }
1182  case 'T':
1183  {
1184  //This is a timing, temperature and pressure line.
1185  // ignore.
1186  //FIXME: We should read these and report them
1187  break;
1188  }
1189  default:
1191  }
1192 
1193  progress=(float)f.tellg()/(float)totalFilesize*100.0f;
1194  }
1195 
1196  if(lastLine==OPS_LINE_DASH)
1197  {
1198  //Damn, we have a problem. We saw a "-" line,
1199  //but failed to find the next "S" line, which should have been there.
1200  //Something is wrong.
1201  if(strictMode)
1203 
1204  data.eventData.pop_back();
1205 
1206  }
1207 
1208  return 0;
1209 }
1210 
1211 
1212 unsigned int loadATOFile(const char *fileName, vector<ATO_ENTRY> &ions, unsigned int forceEndian)
1213 {
1214  //open pos file
1215  std::ifstream CFile(fileName,std::ios::binary);
1216 
1217  if(!CFile)
1218  return ATO_OPEN_FAIL;
1219 
1220  //Get the filesize
1221  CFile.seekg(0,std::ios::end);
1222  size_t fileSize=CFile.tellg();
1223 
1224 
1225  //There are differences in the format, unfortunately.
1226  // Gault et al, Atom Probe Microscopy says
1227  // - there are 14 entries of 4 bytes,
1228  // totalling "44" bytes - which cannot be correct. They however,
1229  // say that the XYZ is added later.
1230  // - Header is 2 32 binary
1231  // - File is serialised as little-endian
1232  // - Various incompatible versions exist. Unclear how to distinguish.
1233 
1234  // Larson et al say that
1235  // - there are 14 fields
1236  // - Header byte 0x05 (0-indexed) is version number, and only version 3 is outlined
1237  // - Pulsenumber can be subject to FP aliasing (bad storage, occurs for values > ~16.7M ),
1238  // - Aliasing errors must be handled, if reading this field
1239  // - File is in big-endian
1240 
1241 
1242  //In summary, we assume there are 14 entries, 4 bytes each, after an 8 byte header.
1243  // we assume that the endian-ness must be auto-detected somehow, as no sources
1244  // agree on file endian-ness. If we cannot detect it, we assume little endian
1245 
1246  //Known versions of file : 2, 5
1247 
1248  //Header (8 bytes), record 14 entries, 4 bytes each
1249 
1250  const size_t ATO_HEADER_SIZE=8;
1251  const size_t ATO_RECORD_SIZE = 14*4;
1252  const size_t ATO_MIN_FILESIZE = 8 + ATO_RECORD_SIZE;
1253 
1254  if(fileSize < ATO_MIN_FILESIZE)
1255  return ATO_EMPTY_FAIL;
1256 
1257 
1258  //calculate the number of points stored in the POS file
1259  IonHit hit;
1260  size_t pointCount=0;
1261  if((fileSize - ATO_HEADER_SIZE) % (ATO_RECORD_SIZE))
1262  return ATO_SIZE_ERR;
1263 
1264 
1265  //Check that the version number, stored at offxet 0x05 (1-indexed), is 3.
1266  CFile.seekg(4);
1267  unsigned int versionByte;
1268  CFile.read((char*)&versionByte,sizeof(unsigned int));
1269 
1270  //Assume that we can have a new version that doesn't affect the readout
1271  // assume that earlier versions are compatible. This means, for a random byte
1272  // in a random length (modulo) file,
1273  // we have a 1-5/255 chance of rejection from this test, and a 1/56 chance of
1274  // rejection from filesize, giving a ~0.02% chance of incorrect acceptance.
1275  // Known versions : 2, 3, 5
1276  if(!versionByte || versionByte > 5)
1277  return ATO_VERSIONCHECK_ERR;
1278 
1279 
1280  pointCount = (fileSize-ATO_HEADER_SIZE)/ATO_RECORD_SIZE;
1281 
1282  try
1283  {
1284  ions.resize(pointCount);
1285  }
1286  catch(std::bad_alloc)
1287  {
1288  return ATO_MEM_ERR;
1289  }
1290 
1291 
1292  //Heuristic to detect endianness.
1293  // - Randomly sample 100 pts from file, and check to see if, when interpreted either way,
1294  // there are any NaN
1295  //
1296 
1297 
1298  bool endianFlip;
1299 
1300  if(forceEndian)
1301  {
1302  ASSERT(forceEndian < 3);
1303 #ifdef __LITTLE_ENDIAN__
1304  endianFlip=(forceEndian == 2);
1305 #elif defined(__BIG_ENDIAN__)
1306  endianFlip=(forceEndian == 1);
1307 #endif
1308  }
1309  else
1310  {
1311  //Auto-detect endianness from file content
1312  size_t numToCheck=std::min(pointCount,(size_t)100);
1313 
1314  //Indicies of points to check
1315  vector<unsigned int> randomNumbers;
1316  gsl_rng *rng = randGen.getRng();
1317  randomIndices(randomNumbers,pointCount,
1318  numToCheck,rng);
1319 
1320  //Make the traverse in ascending order
1321  std::sort(randomNumbers.begin(),randomNumbers.end());
1322 
1323  //One for no endian-flip, one for flip
1324  bool badFloat[2]={ false,false };
1325  //Track the presence of unreasonably large numbers
1326  bool veryLargeNumber[2] = { false,false };
1327 
1328  //Skip through several records, looking for bad float data,
1329  auto buffer = new float[ATO_RECORD_SIZE/4];
1330  for(size_t ui=0;ui<numToCheck;ui++)
1331  {
1332  size_t offset;
1333  offset=randomNumbers[ui];
1334 
1335  CFile.seekg(ATO_HEADER_SIZE + ATO_RECORD_SIZE*offset);
1336  CFile.read((char*)buffer,ATO_RECORD_SIZE);
1337 
1338  const unsigned int BYTES_TO_CHECK[] = { 0,1,2,3,5,6,8,9,10 };
1339  const size_t CHECKBYTES = 9;
1340 
1341  //Check each field for inf/nan presence
1342  for(size_t uj=0;uj<CHECKBYTES;uj++)
1343  {
1344  if(std::isnan(buffer[BYTES_TO_CHECK[uj]]) ||
1345  std::isinf(buffer[BYTES_TO_CHECK[uj]]))
1346  badFloat[0]=true;
1347 
1348  //Flip the endian-ness
1349  floatSwapBytes(buffer+BYTES_TO_CHECK[uj]);
1350 
1351  if(std::isnan(buffer[BYTES_TO_CHECK[uj]]) ||
1352  std::isinf(buffer[BYTES_TO_CHECK[uj]]))
1353  badFloat[1]=true;
1354 
1355  //Swap it back
1356  floatSwapBytes(buffer+BYTES_TO_CHECK[uj]);
1357 
1358 
1359  }
1360 
1361 
1362  //Check for some very likely values.
1363  //Check for large negative masses
1364  if( buffer[3] < -1000.0f)
1365  veryLargeNumber[0] = true;
1366 
1367  // unlikely to exceed 1000 kV
1368  if( fabs(buffer[6]) > 1000.0f || fabs(buffer[10]) > 1000.0f)
1369  veryLargeNumber[0] = true;
1370 
1371  //Swap and try again
1372  floatSwapBytes(buffer+3);
1373  floatSwapBytes(buffer+6);
1374  floatSwapBytes(buffer+10);
1375  if( buffer[3] < -1000.0f)
1376  veryLargeNumber[1] = true;
1377 
1378  if( fabs(buffer[6]) > 1000.0f || fabs(buffer[10]) > 1000.0f)
1379  veryLargeNumber[1] = true;
1380  }
1381 
1382  delete[] buffer;
1383 
1384 
1385  //Now summarise the results
1386 
1387  //If we have a disagreement about bad-float-ness,
1388  // or stupid-number ness. then choose the good one.
1389  // Otherwise abandon detection
1390  if(badFloat[0] != badFloat[1])
1391  {
1392  endianFlip=(badFloat[0]);
1393  }
1394  else if(veryLargeNumber[0] != veryLargeNumber[1])
1395  {
1396  endianFlip=veryLargeNumber[0];
1397  }
1398  else
1399  {
1400  //Assume little endian
1401 #ifdef __LITTLE_ENDIAN__
1402  endianFlip= false;
1403 #else
1404  endianFlip=true;
1405 #endif
1406  }
1407  }
1408 
1409  //File records consist of 14 fields, some of which may not be initialised.
1410  // each being 4-byte IEEE little-endian float
1411  // It is unknown how to detect initialised state.
1412  // Field Data
1413  // 0-3 x,y,z,m/c in Angstrom (x,yz) or Da (m/c)
1414  // 4 clusterID, if set
1415  // - Ignore this field, as this information is redundant
1416  // 5 Approximate Pulse #, due to Float. Pt. limitation
1417  // 6 Standing Voltage (kV)
1418  // 7 TOF (us) (maybe corrected? maybe not?)
1419  // 8-9 Detector position (cm)
1420  // 10 Pulse voltage (kV)
1421  // 11 "Virtual voltage" for reconstruction.
1422  // - Ignore this field, as this information is redundant
1423  // 12,13 Fourier intensity
1424  // - Ignore these fields, as this information is redundant
1425  //Attempt to detect
1426  CFile.seekg(8);
1427 
1428  auto buffer = new float[ATO_RECORD_SIZE/4];
1429  size_t curPos=0;
1430 
1431  if(endianFlip)
1432  {
1433  //Read and swap
1434  while((size_t)CFile.tellg() < fileSize)
1435  {
1436  CFile.read((char*)buffer,ATO_RECORD_SIZE);
1437 
1438  for(size_t ui=0;ui<ATO_RECORD_SIZE;ui++)
1439  floatSwapBytes(buffer+ui);
1440 
1441  float *fBuf=(float*)buffer;
1442  ions[curPos].x = fBuf[0];
1443  ions[curPos].y = fBuf[1];
1444  ions[curPos].z = fBuf[2];
1445  ions[curPos].mass= fBuf[3];
1446  ions[curPos].approxPulse= fBuf[5];
1447  ions[curPos].voltage = fBuf[6];
1448  ions[curPos].tof= fBuf[7];
1449  ions[curPos].detectorX = fBuf[8];
1450  ions[curPos].detectorY = fBuf[9];
1451  ions[curPos].pulseVoltage= fBuf[10];
1452  curPos++;
1453 
1454  }
1455  }
1456  else
1457  {
1458  //read without swapping
1459  while((size_t)CFile.tellg() < fileSize)
1460  {
1461  CFile.read((char*)buffer,ATO_RECORD_SIZE);
1462 
1463  float *fBuf=(float*)buffer;
1464  ions[curPos].x = fBuf[0];
1465  ions[curPos].y = fBuf[1];
1466  ions[curPos].z = fBuf[2];
1467  ions[curPos].mass= fBuf[3];
1468  ions[curPos].approxPulse= fBuf[5];
1469  ions[curPos].voltage = fBuf[6];
1470  ions[curPos].tof= fBuf[7];
1471  ions[curPos].detectorX = fBuf[8];
1472  ions[curPos].detectorY = fBuf[9];
1473  ions[curPos].pulseVoltage= fBuf[10];
1474  curPos++;
1475 
1476  }
1477  }
1478 
1479  delete[] buffer;
1480 
1481 
1482  return 0;
1483 }
1484 
1485 bool strhas(const char *cpTest, const char *cpPossible)
1486 {
1487  while(*cpTest)
1488  {
1489  const char *search;
1490  search=cpPossible;
1491  while(*search)
1492  {
1493  if(*search == *cpTest)
1494  return true;
1495  search++;
1496  }
1497  cpTest++;
1498  }
1499 
1500  return false;
1501 }
1502 
1503 //A routine for loading numeric data from a text file
1504 unsigned int loadTextData(const char *cpFilename, vector<vector<float> > &dataVec,vector<string> &headerVec,
1505  const char *delim, bool allowNan, bool allowConvFails, unsigned int headerCount)
1506 {
1507  const unsigned int BUFFER_SIZE=8192;
1508  char inBuffer[BUFFER_SIZE];
1509 
1510  unsigned int num_fields=0;
1511 
1512 
1513 #if !defined(WIN32) && !defined(_WIN64)
1514  if(isNotDirectory(cpFilename) == false)
1515  return TEXT_ERR_FILE_OPEN;
1516 #endif
1517 
1518  dataVec.clear();
1519  //Open a file in text mode
1520  std::ifstream CFile(cpFilename);
1521 
1522  if(!CFile)
1523  return TEXT_ERR_FILE_OPEN;
1524 
1525  //Drop the headers, if any
1526  string str;
1527  vector<string> strVec;
1528  bool atHeader=true;
1529 
1530  vector<string> prevStrs;
1531  while(CFile.good() && !CFile.eof() && atHeader)
1532  {
1533  //Grab a line from the file
1534  if(!CFile.getline(inBuffer,BUFFER_SIZE))
1535  break;
1536 
1537  if(!CFile.good())
1538  return TEXT_ERR_FILE_FORMAT;
1539 
1540  prevStrs=strVec;
1541  //Split the strings around the deliminator c
1542  splitStrsRef(inBuffer,delim,strVec);
1543  stripZeroEntries(strVec);
1544 
1545 
1546  //Skip blank lines or lines that are only spaces
1547  if(strVec.empty())
1548  continue;
1549 
1550  num_fields = strVec.size();
1551  dataVec.resize(num_fields);
1552  //Check to see if we are in the header
1553  if(atHeader)
1554  {
1555  //Count down headers if we still
1556  if(headerCount !=-1)
1557  {
1558  headerCount--;
1559  if(!headerCount)
1560  {
1561  atHeader=false;
1562  prevStrs=strVec;
1563  continue;
1564  }
1565  }
1566 
1567  //If we have the right number of fields
1568  //we might be not in the header anymore
1569  if(num_fields >= 1 && strVec[0].size())
1570  {
1571  float f;
1572  //Assume we are not reading the header
1573  atHeader=false;
1574 
1575  vector<float> values;
1576  //Confirm by checking all values
1577  for(unsigned int ui=0; ui<num_fields; ui++)
1578  {
1579 
1580  //stream_cast will fail in the case "1 2" if there
1581  //is a tab delimiter specified. Check for only 02,3
1582  if(strVec[ui].find_first_not_of("0123456789.Ee+-Na")
1583  != string::npos)
1584  {
1585  atHeader=true;
1586  break;
1587  }
1588  //If any cast fails
1589  //we are in the header
1590  if(stream_cast(f,strVec[ui]))
1591  {
1592  if(!allowNan || strVec[ui] != "NaN")
1593  {
1594  atHeader=true;
1595  break;
1596  }
1597  }
1598 
1599  values.push_back(f);
1600  }
1601 
1602  if(!atHeader)
1603  break;
1604  }
1605  }
1606 
1607  }
1608 
1609  //Drop the blank bits from the field
1610  stripZeroEntries(prevStrs);
1611  //switch this out as being the header
1612  if(prevStrs.size() == num_fields)
1613  std::swap(headerVec,prevStrs);
1614 
1615  if(atHeader)
1616  {
1617  //re-wind back to the beginning of the file
1618  //as we didn't find a header/data split
1619  CFile.clear(); // clear EOF bit
1620  CFile.seekg(0,std::ios::beg);
1621  CFile.getline(inBuffer,BUFFER_SIZE);
1622 
1623 
1624  splitStrsRef(inBuffer,delim,strVec);
1625  stripZeroEntries(strVec);
1626  num_fields=strVec.size();
1627 
1628  }
1629 
1630  float f;
1631  while(CFile.good() && !CFile.eof())
1632  {
1633  if(strhas(inBuffer,"0123456789"))
1634  {
1635  //Split the strings around the tab char
1636  splitStrsRef(inBuffer,delim,strVec);
1637  stripZeroEntries(strVec);
1638 
1639  //Check the number of fields
1640  //=========
1641  if(strVec.size() != num_fields)
1642  return TEXT_ERR_FILE_NUM_FIELDS;
1643 
1644 
1645  for(unsigned int ui=0; ui<num_fields; ui++)
1646  {
1647  if(strVec[ui] == "NaN" && allowNan)
1648  f = NAN;
1649  else
1650  {
1651  std::stringstream ss;
1652  ss.clear();
1653  ss.str(strVec[ui]);
1654  ss >> f;
1655  if(ss.fail())
1656  {
1657  if(!allowConvFails)
1658  return TEXT_ERR_FILE_FORMAT;
1659  f=NAN;
1660  }
1661  }
1662  dataVec[ui].push_back(f);
1663 
1664  }
1665  //=========
1666 
1667  }
1668  //Grab a line from the file
1669  if(!CFile.getline(inBuffer,BUFFER_SIZE))
1670  break;
1671  }
1672 
1673  return 0;
1674 }
1675 
1676 
1677 
1678 }
1679 
struct AtomProbe::IONHIT IONHIT
Record as stored in a .POS file.
const char * RECORDREAD_ERR_STRINGS[]
Definition: dataFiles.cpp:82
Record as stored in a .POS file.
Definition: dataFiles.cpp:45
float flightPath
The flight path to the virtual (or real) projected tip image.
Definition: dataFiles.h:240
float voltage
Standing Voltage applied to specimen (V)
Definition: dataFiles.h:226
float massToCharge
Definition: ionHit.h:118
float z
Definition: ionHit.h:118
const char * OPS_ENUM_ERRSTRINGS[]
Definition: dataFiles.cpp:776
bool readEposRecord(const char *src, const char *dest)
Definition: dataFiles.cpp:711
const char * getPosFileErrString(unsigned int errMesg)
Definition: dataFiles.cpp:238
float voltDC
Definition: ionHit.h:123
int32_t hitMultiplicity
Definition: ionHit.h:131
unsigned int loadATOFile(const char *fileName, std::vector< ATO_ENTRY > &ions, unsigned int forceEndian=0)
Load a LAWATAP "ATO" file.
Definition: dataFiles.cpp:1212
std::string stripWhite(const std::string &str)
Strip whitespace, (eg tab,space) from either side of a string.
float voltPulse
Definition: ionHit.h:123
Data structure that contains the experiment information present in a 3Dap file.
Definition: dataFiles.h:254
bool isNotDirectory(const char *filename)
Definition: helpFuncs.cpp:170
unsigned int saveTapsimBin(const std::vector< IonHit > &posIons, std::ostream &f)
Write a tapsim file from a vector of IonHits.
Definition: dataFiles.cpp:667
const char * ATO_ERR_STRINGS[]
Human readable error messages for use with ATO reader return values.
Definition: dataFiles.cpp:73
THREEDAP_DATA params
Experiment parameters.
Definition: dataFiles.h:257
float pulseVolt
Pulse voltage applied to specimen (V)
Definition: dataFiles.h:228
size_t loadEposFile(std::vector< EPOS_ENTRY > &outData, const char *filename)
Load an entire "EPOS" File.
Definition: dataFiles.cpp:756
unsigned int loadTextData(const char *cpFilename, std::vector< std::vector< float > > &dataVec, std::vector< std::string > &headerVec, const char *delim="\", bool allowNan=true, bool allowConvFails=false, unsigned int headerCount=-1)
Load a CSV, TSV or similar text file. Assumes "C" Locale for input (ie "." as decimal separator)...
Definition: dataFiles.cpp:1504
unsigned int detectorChannels
The number of channels on the detector.
Definition: dataFiles.h:250
void setPos(const Point3D &pos)
Definition: ionhit.cpp:76
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
void splitStrsRef(const char *cpStr, const char delim, std::vector< std::string > &v)
Split string references using a single delimiter.
A 3D point data class storage.
Definition: point3D.h:39
float y
Definition: ionHit.h:118
float timeOfFlight
timeOfFlight in ns. Correction status does not appear to be specified
Definition: ionHit.h:123
const size_t EPOS_RECORD_SIZE
Definition: ionHit.h:144
void int4SwapBytes(int32_t *inInt)
Definition: endianTest.h:76
std::vector< VOLTAGE_DATA > voltageData
Voltage information.
Definition: dataFiles.h:261
bool getFilesize(const char *fname, size_t &size)
Definition: helpFuncs.cpp:107
#define ARRAYSIZE(f)
Definition: helpFuncs.h:34
float yDetector
Definition: ionHit.h:125
bool strhas(const char *cpTest, const char *cpPossible)
Definition: dataFiles.cpp:1485
unsigned int fixedRecordChunkReader(const char *filename, bool(*recordReader)(const char *bufRead, const char *destBuf), size_t recordSize, std::vector< T > &outputData, unsigned int chunkSize, unsigned int chunkOffset, unsigned int &nEntriesLeft)
Definition: dataFiles.cpp:144
unsigned int readPosapOps(const char *file, THREEDAP_EXPERIMENT &data, unsigned int &badLine, unsigned int &progress, std::atomic< bool > &wantAbort, unsigned int nDelayLines=2, bool strictMode=false)
Function to read POSAP "OPS" files.
Definition: dataFiles.cpp:805
std::vector<::std::vector< SINGLE_HIT > > eventData
vector of event vectors. Each interior event vector describes a group of hits on one pulse ...
Definition: dataFiles.h:259
float xDetector
X and Y coordinates on the detector in mm, nominally uncorrected.
Definition: ionHit.h:125
unsigned int loadPosFile(std::vector< IonHit > &posIons, const char *posFile)
Load a pos file directly into a single ion list.
Definition: dataFiles.cpp:256
unsigned int fixedRecordReader(const char *filename, bool(*recordReader)(const char *bufRead, const char *destBuf), size_t recordSize, std::vector< T > &outputData)
Definition: dataFiles.cpp:93
float tZero
Time offset for pulse, in nanoseconds.
Definition: dataFiles.h:246
Definition: ionHit.h:113
void floatSwapBytes(float *inFloat)
Definition: endianTest.h:58
float detectorRadius
The size of the detector, in mm.
Definition: dataFiles.h:248
float alpha
Pulse coupling coefficient (1 of 2)
Definition: dataFiles.h:242
float beta
Pulse coupling coefficient (2 of 2)
Definition: dataFiles.h:244
void setMassToCharge(float newMassToCharge)
Definition: ionhit.cpp:60
float tof
Reported time of flight.
Definition: dataFiles.h:217
void randomIndices(std::vector< T > &res, size_t num, size_t nMax, gsl_rng *rng)
Definition: sampling.h:113
This is a data holding class for POS file ions, from.
Definition: ionHit.h:36
#define ASSERT(f)
int32_t deltaPulse
Definition: ionHit.h:131
Single 3DAP hit event.
Definition: dataFiles.h:212
float beta
Coupling coefficient to specimen.
Definition: dataFiles.h:231
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 progress
Definition: kd-example.cpp:26
size_t nextHitGroupOffset
FIXME: Document me.
Definition: dataFiles.h:224
IonHit getIonHit() const
Definition: dataFiles.cpp:231
gsl_rng * getRng() const
Obtain a GSL random number generator.
Definition: atomprobe.h:115
float x
Definition: ionHit.h:118
std::vector< unsigned long long > eventPulseNumber
The pulse number associated with an event group.
Definition: dataFiles.h:263
RandNumGen randGen
Definition: atomprobe.cpp:29
void stripZeroEntries(std::vector< std::string > &s)
Voltage data structure – these updates occur periodically during the experiment. ...
Definition: dataFiles.h:221
unsigned int loadTapsimBinFile(vector< IonHit > &posIons, const char *posfile)
Definition: dataFiles.cpp:538
size_t chunkLoadEposFile(std::vector< EPOS_ENTRY > &outData, const char *filename, unsigned int chunkSize, unsigned int chunkOffset, unsigned int &nEntriesLeft)
Load an "EPOS" file, with a maximum chunk size.
Definition: dataFiles.cpp:765