EGSnrc C++ class library  Report PIRS-898 (2021)
Iwan Kawrakow, Ernesto Mainegra-Hing, Frederic Tessier, Reid Townson and Blake Walters
egs_particle_track.cpp
Go to the documentation of this file.
1 /*
2 ###############################################################################
3 #
4 # EGSnrc egs++ particle tracks
5 # Copyright (C) 2015 National Research Council Canada
6 #
7 # This file is part of EGSnrc.
8 #
9 # EGSnrc is free software: you can redistribute it and/or modify it under
10 # the terms of the GNU Affero General Public License as published by the
11 # Free Software Foundation, either version 3 of the License, or (at your
12 # option) any later version.
13 #
14 # EGSnrc is distributed in the hope that it will be useful, but WITHOUT ANY
15 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
17 # more details.
18 #
19 # You should have received a copy of the GNU Affero General Public License
20 # along with EGSnrc. If not, see <http://www.gnu.org/licenses/>.
21 #
22 ###############################################################################
23 #
24 # Author: Georgi Gerganov, 2009
25 #
26 # Contributors: Iwan Kawrakow
27 # Reid Townson
28 # Alexandre Demelo
29 #
30 ###############################################################################
31 */
32 
39 #include "egs_particle_track.h"
40 #include <vector>
41 #include <algorithm>
42 
43 
44 
46  // calculate the new size of the vertex array
47  int new_size = m_size > 0 ? m_size*2 : 16;
48  // not enough ?? then allocate more
49  if (m_nVertices > new_size) {
50  new_size = m_nVertices;
51  }
52  Vertex **tmp = new Vertex* [new_size];
53  if (m_track) {
54  for (int i = 0; i < m_nVertices; ++i) {
55  tmp[i] = m_track[i];
56  }
57  delete [] m_track;
58  m_track = NULL;
59  }
60  m_track = tmp;
61  m_size = new_size;
62 }
63 
65  if (m_pInfo) {
66  delete m_pInfo;
67  }
68  m_pInfo = NULL;
69  if (m_track)
70  for (int i = 0; i < m_nVertices; i++) {
71  if (m_track[i]) {
72  delete m_track[i];
73  }
74  m_track[i] = NULL;
75  }
76  m_nVertices = 0;
77 }
78 
79 int EGS_ParticleTrack::writeTrack(ofstream *trsp, bool incltime) {
80  // no need to write the track if it has less than 2 vertices ...
81  if (m_nVertices < 2) {
82  return 1;
83  }
84 
85  trsp->write((char *)&m_nVertices, sizeof(int));
86  trsp->write((char *)m_pInfo, sizeof(ParticleInfo));
87 
88  // If user indicates to include time in inputfile then write the time index
89  // to the tracks file after the particle info.
90  if (incltime) {
91  trsp->write((char *) &time_index, sizeof(EGS_Float));
92  }
93  for (int i = 0; i < m_nVertices; i++) {
94  trsp->write((char *)m_track[i],sizeof(Vertex));
95  }
96  return 0;
97 }
98 
100  if ((v < 0) || (v >= m_nVertices)) {
101  return NULL;
102  }
103  return (m_track[v]);
104 }
105 
107  m_track[m_nVertices] = x;
108  m_nVertices++;
109  // resize the vertex array if necessary
110  if (m_nVertices >= m_size) {
111  grow();
112  }
113 }
114 
115 
117  // buffer full ? flush it
118  if (m_nTracks >= m_bufferSize) {
119  flushBuffer();
120  }
121  m_nTracks++;
122 
123  // mark the track as being scored
124  m_isScoring[m_nTracks-1] = true;
125 }
126 
128  if (m_nTracks >= m_bufferSize) {
129  flushBuffer();
130  }
131  m_nTracks++;
132 
133  // map the track on the stack
134  m_stackMap[stackIndex] = m_nTracks-1;
135  m_isScoring[m_nTracks-1] = true;
136 }
137 
139  if (m_nTracks >= m_bufferSize) {
140  flushBuffer();
141  }
142  m_nTracks++;
143  m_isScoring[m_nTracks-1] = true;
145 }
146 
148  int save = 0;
149  if (m_trspFile) {
150  for (int i = 0; i < m_nTracks; i++) {
151  // if the particle is not being scored anymore
152  if (!m_isScoring[i]) {
153  // output it to the file and free memory
154  if (!m_buffer[i]->writeTrack(m_trspFile,incltime)) {
155  m_totalTracks++;
156  }
157  m_buffer[i]->clearTrack();
158  }
159  else {
160  // still scoring it -> save the particle
161  m_buffer[save] = m_buffer[i];
162  m_isScoring[i] = false;
163  m_isScoring[save] = true;
164  for (int idx = 0; idx < m_bufferSize; idx++) {
165  if (m_stackMap[idx] == i) {
166  m_stackMap[idx] = save;
167  break;
168  }
169  }
170  save++;
171  }
172  }
173  updateHeader();
174  }
175  m_nTracks = save;
176 }
177 
179  if (m_trspFile) {
180  ostream::off_type pos = m_trspFile->tellp();
181 
182  //m_trspFile->seekp(0,ios::beg);
183  m_trspFile->seekp(sizeof(head_inctime) + sizeof(bool));
184 
185  m_trspFile->write((char *) &m_totalTracks, sizeof(int));
186  m_trspFile->seekp(pos,ios::beg);
187  }
188 }
189 
191  if ((tr < 0) || (tr >= m_nTracks)) {
192  return NULL;
193  }
194  return (m_buffer[tr]->getVertex(v));
195 }
196 
198  if (!m_isScoring[m_nTracks-1]) {
199  return;
200  }
202 }
203 
205  if (m_stackMap[stackIndex] < 0) {
206  return;
207  }
208  if (!m_isScoring[m_stackMap[stackIndex]]) {
209  return;
210  }
211  m_buffer[m_stackMap[stackIndex]]->addVertex(x);
212 }
213 
214 int EGS_ParticleTrackContainer::readDataFile(const char *filename) {
215  const char *func_name = "EGS_ParticleTrackContainer::readDataFile()";
216  ifstream *data = new ifstream(filename, ios::binary);
217  if (!data || data->fail() || !data->good()) {
218  egsWarning("%s: Unable to open track space file '%s'! No tracks loaded\n",
219  func_name, filename);
220  return -1;
221  }
222 
223  // Skip the first few bits related to the string head_inctime
224  data->seekg(sizeof(head_inctime));
225  // Read the boolean of whether or not time indices are included
226  data->read((char *)&incltime, sizeof(bool));
227 
228  data->read((char *)&m_totalTracks, sizeof(int));
229  egsInformation("%s: Reading %d tracks from '%s' ...\n", func_name, m_totalTracks, filename);
230  if (incltime) {
231  egsInformation("%s: Time indices are included in the data.\n", func_name);
232  }
233  else {
234  egsInformation("%s: Time indices are not included in the data.\n", func_name);
235  }
236 
237  m_nTracks = 0;
238  int totalVertices = 0;
241  m_stackMap = new int [m_totalTracks];
242  m_isScoring = new bool [m_totalTracks];
243  for (int i = 0; i < m_totalTracks; i++) {
244  int nvertices;
245  EGS_Float time;
246  m_buffer[i] = new EGS_ParticleTrack();
247  m_stackMap[i] = -1;
248  m_isScoring[i] = false;
250  data->read((char *)&nvertices,sizeof(int));
251  totalVertices += nvertices;
252  data->read((char *)pinfo,sizeof(EGS_ParticleTrack::ParticleInfo));
253  startNewTrack(pinfo);
254 
255  // If user indicates to include time in inputfile then read the time
256  // index from the tracks file after the particle info.
257  if (incltime) {
258  data->read((char *)&time,sizeof(EGS_Float));
259  }
260  for (int j = 0; j < nvertices; j++) {
262  data->read((char *)v,sizeof(EGS_ParticleTrack::Vertex));
263  addVertex(v);
264  }
265  }
266  egsInformation("%s: Tracks in memory: %d\n", func_name, m_nTracks);
267  egsInformation("%s: Total Vertices : %d\n", func_name, totalVertices);
268 
269  return 0;
270 }
271 
273  flushBuffer();
274 
275  // see how many tracks are still being scored
276  int scoring = -1;
277  if (m_isScoring) {
278  scoring = 0;
279  for (int i = 0; i < m_bufferSize; i++) {
280  if (m_isScoring[i]) {
281  scoring++;
282  }
283  }
284  }
285 
286  if (with_header) {
287  egsInformation("\nParticle track scoring results:\n");
288  egsInformation("=================================\n");
289  egsInformation(" Total events scored: %d\n", m_nEvents);
290  }
291  egsInformation(" Total tracks scored: %d\n", m_totalTracks);
292  egsInformation(" Still being scored: ");
293  if (scoring == -1) {
294  egsInformation("Unknown!?\n");
295  }
296  else {
297  egsInformation("%d\n", scoring);
298  if (scoring > 0) {
299  egsWarning(" *** There are particles still being tracked. This"
300  " should never happen if this is the end of the simulation!");
301  }
302  }
303  egsInformation(" Output file name: %s\n\n", m_trspFilename.c_str());
304  if (incltime) {
305  tracksFileSort();
306  }
307  readDataFile(m_trspFilename.c_str());
308 }
309 
310 void EGS_ParticleTrackContainer::tracksFileSort() {
311 
312  // Function to sort tracks file entries by time index. This is done by
313  // reading time indices, saving their positions in a vector of pairs,
314  // sorting the vector by time index, and then rewriting tracks into a new
315  // sorted file.
316 
317  const char *func_name = "EGS_ParticleTrackContainer::tracksFileSort()";
318  const char *trackfile = m_trspFilename.c_str();
319  ifstream *data = new ifstream(trackfile, ios::binary);
320 
321  // New sorted tracks file where data from the original tracksfile will be
322  // rewritten.
323  size_t lastindex = m_trspFilename.find_last_of(".ptracks");
324  string rawname = m_trspFilename.substr(0, lastindex-7);
325  string outstring = rawname+"-sorted.ptracks";
326  const char *outname = outstring.c_str();
327  ofstream *sortout = new ofstream(outname, ios::binary);
328 
329  // Skip the first few bits related to the string head_inctime
330  data->seekg(sizeof(head_inctime));
331  // Read the boolean of whether or not time indices are included
332  data->read((char *)&incltime, sizeof(bool));
333 
334  int position = data->tellg();
335 
336  if (!incltime) {
337  egsWarning("%s: Warning: Attempted to sort ptracks file that does not contain time index.\n", func_name);
338  sortout->close();
339  data->close();
340  return;
341  }
342 
343  int totalTrackNum;
344  data->read((char *)&totalTrackNum, sizeof(int));
345  egsInformation("%s: Sorting %d tracks from '%s' by time index ...\n", func_name, totalTrackNum, trackfile);
346 
347  // Write whether or not time index is included (it must be true)
348  sortout->write(head_inctime, sizeof(head_inctime));
349  sortout->write((char *)&incltime, sizeof(bool));
350 
351  sortout->write((char *)&totalTrackNum, sizeof(int));
352 
353  // Defining vector of pairs which will be used to sort.
354  vector<pair<EGS_Float, int>> vect;
355 
356  for (int i = 0; i < m_totalTracks; i++) {
357  int nvertices;
359  EGS_Float time;
360  int position = data->tellg(); // Get the position in the file (right before a new track starts).
361 
362  // Read in usual track info.
363  data->read((char *)&nvertices, sizeof(int));
364  data->read((char *)pinfo, sizeof(EGS_ParticleTrack::ParticleInfo));
365  data->read((char *)&time, sizeof(EGS_Float));
366  vect.push_back(make_pair(time, position)); // Add the time index/file position pair to the vector.
367 
368  // Determine the size of all the vertices for track i and skip these to
369  // get to the next track.
370  int vertsize = nvertices * sizeof(EGS_ParticleTrack::Vertex);
371  data->seekg(vertsize, ios_base::cur);
372  }
373 
374  // Using the built-in C++ sort function, sort all the pairs by their time
375  // index (first element in the pair).
376  sort(vect.begin(), vect.end());
377 
378  // Now, write the sorted tracks file by jumping around the unsorted file
379  // using the positions in the pairs vector.
380  for (int i = 0; i < m_totalTracks; i++) {
381  int nvertices;
383  EGS_Float time;
384  data->seekg(vect[i].second); // Go to file position right before track i.
385 
386  // Read in all track info for track i from unsorted.
387  data->read((char *)&nvertices, sizeof(int));
388  data->read((char *)pinfo, sizeof(EGS_ParticleTrack::ParticleInfo));
389  data->read((char *)&time, sizeof(EGS_Float));
390 
391  // Write all read track info to the sorted file (at the end of the file).
392  sortout->write((char *)&nvertices, sizeof(int));
393  sortout->write((char *)pinfo, sizeof(EGS_ParticleTrack::ParticleInfo));
394  sortout->write((char *)&time, sizeof(EGS_Float));
395 
396  for (int j = 0; j < nvertices; j++) {
397  // Read the nvertices from the unsorted file and write them to the end of the sorted file.
399  data->read((char *)v, sizeof(EGS_ParticleTrack::Vertex));
400  sortout->write((char *)v, sizeof(EGS_ParticleTrack::Vertex));
401  }
402  }
403 
404  sortout->close();
405  data->close();
406 
407  int removal = remove(trackfile); // Delete unsorted file.
408  if (removal) {
409  egsWarning("\nWarning: Failed to remove %s. Error code %d.\n", trackfile, errno);
410  egsWarning("Note that the sorted ptracks file will be left as a separate file, because your system seems to block file deletion: %s\n\n", outstring.c_str());
411  }
412  else {
413  int renaming = rename(outname, trackfile); // Rename sorted file to the unsorted file's old name.
414  if (renaming) {
415  egsWarning("Warning: Failed to rename %s to %s. Error code %d.\n", outname, trackfile, errno);
416  }
417  }
418 }
EGS_ParticleTrack::Vertex * getTrackVertex(int tr, int v)
Get the v vertex from the tr track.
string m_trspFilename
filename of output file
EGS_ParticleTrack ** m_buffer
the tracks array
void startNewTrack()
Start scoring a new track.
int m_nEvents
number of events scored
void reportResults(bool with_header=true)
Report results from the track scoring process so far.
int m_nTracks
number of tracks currently in memory
void updateHeader()
Update the output file's header.
int m_totalTracks
total number of tracks registered
int readDataFile(const char *filename)
Load particle data from the file called filename .
int m_bufferSize
max number of tracks in memory
ofstream * m_trspFile
the file to which data is output
void flushBuffer()
Write all track data to the file.
void addVertex(EGS_ParticleTrack::Vertex *x)
Add a vertex to the track currently being scorred.
A class representing a single track of a particle.
Vertex ** m_track
the array with the vertices
Vertex * getVertex(int v)
Gets vertex number v .
int m_nVertices
current number of vertices in track
void setParticleInfo(ParticleInfo *p)
Define the type of the particle being tracked.
void addVertex(Vertex *x)
Add additional point of interaction (Vertex) to the track.
void grow()
Resize the array containing the vertices.
int writeTrack(ofstream *trsp, bool incltime)
Write this track to a "track space" file.
void clearTrack()
Deallocate particle information and vertex data.
ParticleInfo * m_pInfo
type of the tracked particle
int m_size
current size of the vertex array
EGS_ParticleTrack class header file.
EGS_InfoFunction EGS_EXPORT egsInformation
Always use this function for reporting the progress of a simulation and any other type of information...
EGS_InfoFunction EGS_EXPORT egsWarning
Always use this function for reporting warnings.
Structure describing the particle being tracked.
Structure to store the data for each interaction along the track.