gVirtualXRay  2.0.10
VirtualX-RayImagingLibraryonGPU
Scintillator.inl
Go to the documentation of this file.
1 /*
2 
3 Copyright (c) 2022, Dr Franck P. Vidal (franck.p.vidal@fpvidal.net),
4 http://www.fpvidal.net/
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without modification,
8 are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11 this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14 this list of conditions and the following disclaimer in the documentation and/or
15 other materials provided with the distribution.
16 
17 3. Neither the name of the Bangor University nor the names of its contributors
18 may be used to endorse or promote products derived from this software without
19 specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
22 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
23 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 
32 */
33 
34 
64 //******************************************************************************
65 // Include
66 //******************************************************************************
67 #include <cmath>
68 #include <fstream>
69 #include <algorithm> // To sort the energy response
70 
71 #include <xraylib.h>
72 
73 #ifndef __Exception_h
74 #include "gVirtualXRay/Exception.h"
75 #endif
76 
77 #ifndef __FileDoesNotExistException_h
79 #endif
80 
81 #ifndef __Units_h
82 #include "gVirtualXRay/Units.h"
83 #endif
84 
85 #ifndef __Utilities_h
86 #include "gVirtualXRay/Utilities.h"
87 #endif
88 
89 
90 
91 //******************************************************************************
92 // namespace
93 //******************************************************************************
94 namespace gVirtualXRay {
95 
96 
97 //--------------------------------------------------
98 inline std::vector<double> logspace(const double &a,
99  const double &b,
100  const int &k)
101 //--------------------------------------------------
102 {
103  std::vector<double> logspace;
104  for (int i = 0; i < k; i++)
105  {
106  logspace.push_back(pow(10, i * (b - a) / (k - 1)));
107  }
108  return logspace;
109 }
110 
111 
112 //----------------------------------
114 //----------------------------------
115  m_thickness(0.0),
116  m_density(0.0)
117 //----------------------------------
118 {}
119 
120 
121 //----------------------------------
123 //----------------------------------
124 {
125  release();
126 }
127 
128 
129 //---------------------------------
131 //---------------------------------
132 {
133  m_material.clear();
134  m_thickness = 0.0;
135  m_density = 0.0;
136  m_p_energy_response.clear();
137 }
138 
139 
140 //-----------------------------------------------------------------
141 inline void Scintillator::setMaterial(const std::string& aMaterial)
142 //-----------------------------------------------------------------
143 {
144  m_material = aMaterial;
146 
147  if (m_thickness > 0.1 * um)
148  {
150  }
151 }
152 
153 
154 //-------------------------------------------------------------------------------
155 inline std::vector<std::string> Scintillator::getSupportedScintillatorMaterials()
156 //-------------------------------------------------------------------------------
157 {
158  std::vector<std::string> p_supported_scintillator_material_set;
159 
160  p_supported_scintillator_material_set.push_back("Cesium iodine");
161  p_supported_scintillator_material_set.push_back("Cadmium tungstate");
162  p_supported_scintillator_material_set.push_back("Yttrium oxyde");
163  p_supported_scintillator_material_set.push_back("Gadolinum oxyde");
164  p_supported_scintillator_material_set.push_back("Yttrium gadolinum oxyde");
165  p_supported_scintillator_material_set.push_back("Gadolinum silicate");
166  p_supported_scintillator_material_set.push_back("Gadox");
167  p_supported_scintillator_material_set.push_back("Gadox DRZ-Plus");
168  p_supported_scintillator_material_set.push_back("Sodium iodine");
169  p_supported_scintillator_material_set.push_back("Lanthanum hafnate");
170  p_supported_scintillator_material_set.push_back("Gadolinium gallium garnet");
171  p_supported_scintillator_material_set.push_back("Yttrium aluminium garnate");
172 
173  return p_supported_scintillator_material_set;
174 }
175 
176 
177 //-----------------------------------------------------------------------
178 inline void Scintillator::setThickness(const RATIONAL_NUMBER& aThickness)
179 //-----------------------------------------------------------------------
180 {
181  m_thickness = aThickness;
182 
183  if ((m_density > (0.1 * g / cm3)) && m_material.size())
184  {
186  }
187 }
188 
189 
190 //---------------------------------------------------------
191 inline const std::string& Scintillator::getMaterial() const
192 //---------------------------------------------------------
193 {
194  return m_material;
195 }
196 
197 //-------------------------------------------------
199 //-------------------------------------------------
200 {
201  return m_thickness;
202 }
203 
204 
205 //------------------------------------------------------------------------------------------------------
206 inline std::vector<std::pair<RATIONAL_NUMBER, RATIONAL_NUMBER> > Scintillator::getEnergyResponse() const
207 //------------------------------------------------------------------------------------------------------
208 {
209  return m_p_energy_response;
210 }
211 
212 
213 //---------------------------------------------------------------------------------------------------------------------------------
214 inline std::vector<std::pair<RATIONAL_NUMBER, RATIONAL_NUMBER> > Scintillator::getEnergyResponse(const std::string& aMaterial,
215  const RATIONAL_NUMBER& aThickness)
216 //---------------------------------------------------------------------------------------------------------------------------------
217 {
218  std::vector<std::pair<RATIONAL_NUMBER, RATIONAL_NUMBER> > p_energy_response;
219 
220  // The material and its thickness exist
221  // Evaluate the energy response if possible
222  if (aMaterial.size() > 0 && aThickness > 1 * um)
223  {
224  // See email received on Fri 28/10/2022
225 
226  // avec mu et mu_en les coef de ton scintillateur (dépendant de E bien sûr) et T l'épaisseur de ton scintillateur. La valeur déposée est donc entre 0 et E, et tu peux stocker une simple LUT du genre Edep=f(E).
227  // Dis-moi si qqch n'est pas claire. On essaiera de se planifier une visio en novembre pour la thèse.
228 
229  std::vector<double> energy_range = logspace(0., 2.48, 1000);
230 
231  std::string compound = getCompound(aMaterial);
232 
233  for (std::vector<double>::const_iterator ite_energy = energy_range.begin();
234  ite_energy != energy_range.end();
235  ++ite_energy)
236  {
237  // Energy in keV
238  double energy = *ite_energy * keV;
239 
240  double response = 0.0;
241  if (energy > EPSILON)
242  {
243  // mu / rho in cm2/g
244  double mu_rho = CS_Total_CP(compound.c_str(), energy / keV, 0);
245 
246  // mu_en / rho in cm2/g
247  double mu_en_rho = CS_Energy_CP(compound.c_str(), energy / keV, 0);
248 
249  // rho in g/cm3
250  double density = getDensity(aMaterial) / (g / cm3);
251 
252  // mu in cm-1
253  double mu = mu_rho * density;
254 
255  // mu_en in cm-1
256  double mu_en = mu_en_rho * density;
257 
258  // thickness in cm
259  double thickness = aThickness / cm;
260 
261  // Energy response
262  response = energy * (mu_en / mu) * (1.0 - exp(-mu * thickness));
263  }
264  p_energy_response.push_back(std::pair<RATIONAL_NUMBER, RATIONAL_NUMBER>(energy, response));
265  }
266  }
267 
268  return p_energy_response;
269 }
270 
271 
272 //---------------------------------------------------------------------------
273 inline RATIONAL_NUMBER Scintillator::getDensity(const std::string& aMaterial)
274 //---------------------------------------------------------------------------
275 {
276  // Cesium iodine
277  if (aMaterial == "CsI" || aMaterial == "Cesium iodine")
278  {
279  return 4.52 * g / cm3;
280  }
281  // Cadmium tungstate
282  else if (aMaterial == "CdWO4" || aMaterial == "CWO" || aMaterial == "Cadmium tungstate")
283  {
284  return 7.9 * g / cm3;
285  }
286  // Yttrium oxyde
287  else if (aMaterial == "Y2O3" || aMaterial == "Yttrium oxyde")
288  {
289  return 5.01 * g / cm3;
290  }
291  // Gadolinum oxyde
292  else if (aMaterial == "Gd2O3" || aMaterial == "Gadolinum oxyde")
293  {
294  return 7.55 * g / cm3;
295  }
296  // Yttrium gadolinum oxyde
297  else if (aMaterial == "(Y,Gd)2O3" || aMaterial == "YGO" || aMaterial == "Yttrium gadolinum oxyde")
298  {
299  return 5.9 * g / cm3;
300  }
301  // Gadox
302  else if (aMaterial == "Gd2O2S" || aMaterial == "Gadox" || aMaterial == "Gadolinum silicate" || aMaterial == "GOS")
303  {
304  return 7.34 * g / cm3;
305  }
306  // Gd2O2S DRZ-Plus
307  else if (aMaterial == "Gd2O2S DRZ-Plus" || aMaterial == "Gadox DRZ-Plus")
308  {
309  return 4.76 * g / cm3;
310  }
311  // Sodium iodine
312  else if (aMaterial == "NaI" || aMaterial == "Sodium iodine")
313  {
314  return 3.67 * g / cm3;
315  }
316  // Lanthanum hafnate
317  else if (aMaterial == "La2HfO7" || aMaterial == "Lanthanum hafnate")
318  {
319  return 7.9 * g / cm3;
320  }
321  // Gadolinium gallium garnet
322  else if (aMaterial == "Gd3Ga5O12" || aMaterial == "Gadolinium gallium garnet")
323  {
324  return 7.09 * g / cm3;
325  }
326  // Yttrium aluminium garnate
327  else if (aMaterial == "Y3Al5O12" || aMaterial == "Yttrium aluminium garnate" || aMaterial == "YAG")
328  {
329  return 4.55 * g / cm3;
330  }
331  else
332  {
333  throw Exception(__FILE__, __FUNCTION__, __LINE__,
334  std::string("This material (") + aMaterial + ") is not supported. Contact Franck Vidal if you think it should be added.");
335  }
336 }
337 
338 
339 //------------------------------------------------------------------------
340 inline std::string Scintillator::getCompound(const std::string& aMaterial)
341 //------------------------------------------------------------------------
342 {
343  // Cesium iodine
344  if (aMaterial == "CsI" || aMaterial == "Cesium iodine")
345  {
346  return "CsI";
347  }
348  // Cadmium tungstate
349  else if (aMaterial == "CdWO4" || aMaterial == "CWO" || aMaterial == "Cadmium tungstate")
350  {
351  return "CdWO4";
352  }
353  // Yttrium oxyde
354  else if (aMaterial == "Y2O3" || aMaterial == "Yttrium oxyde")
355  {
356  return "Y2O3";
357  }
358  // Gadolinum oxyde
359  else if (aMaterial == "Gd2O3" || aMaterial == "Gadolinum oxyde")
360  {
361  return "Gd2O3";
362  }
363  // Yttrium gadolinum oxyde
364  else if (aMaterial == "(Y,Gd)2O3" || aMaterial == "YGO" || aMaterial == "Yttrium gadolinum oxyde")
365  {
366  return "Y2Gd2O3";
367  }
368  // Gadox
369  else if (aMaterial == "Gd2O2S" || aMaterial == "Gadox" || aMaterial == "Gadolinum silicate" || aMaterial == "GOS")
370  {
371  return "Gd2O2S";
372  }
373  // Gd2O2S DRZ-Plus
374  else if (aMaterial == "Gd2O2S DRZ-Plus" || aMaterial == "Gadox DRZ-Plus")
375  {
376  return "Gd2O2S";
377  }
378  // Sodium iodine
379  else if (aMaterial == "NaI" || aMaterial == "Sodium iodine")
380  {
381  return "NaI";
382  }
383  // Lanthanum hafnate
384  else if (aMaterial == "La2HfO7" || aMaterial == "Lanthanum hafnate")
385  {
386  return "La2HfO7";
387  }
388  // Gadolinium gallium garnet
389  else if (aMaterial == "Gd3Ga5O12" || aMaterial == "Gadolinium gallium garnet")
390  {
391  return "Gd3Ga5O12";
392  }
393  // Yttrium aluminium garnate
394  else if (aMaterial == "Y3Al5O12" || aMaterial == "Yttrium aluminium garnate" || aMaterial == "YAG")
395  {
396  return "Y3Al5O12";
397  }
398  else
399  {
400  throw Exception(__FILE__, __FUNCTION__, __LINE__,
401  std::string("This material (") + aMaterial + ") is not supported. Contact Franck Vidal if you think it should be added.");
402  }
403 }
404 
405 
406 //--------------------------------------------------------------------------------
407 inline void Scintillator::loadEnergyResponse(const std::string& aFileName,
408  const RATIONAL_NUMBER& aUnitOfEnergy)
409 //--------------------------------------------------------------------------------
410 {
411  // Empty the current beam spectrum
412  m_p_energy_response.clear();
413 
414  // Open the file
415  std::ifstream energy_response_input_stream(aFileName.c_str());
416 
417  // The file is not open
418  if (!energy_response_input_stream.is_open())
419  {
420  throw FileDoesNotExistException(__FILE__, __FUNCTION__, __LINE__, aFileName);
421  }
422 
423  // Load the file
424  double input_energy;
425  double output_energy;
426  while (energy_response_input_stream >> input_energy >> output_energy)
427  {
428  // The record is valid
429  if (!energy_response_input_stream.eof())
430  {
431  input_energy *= aUnitOfEnergy;
432  output_energy *= aUnitOfEnergy;
433 
434  m_p_energy_response.push_back(std::pair<RATIONAL_NUMBER, RATIONAL_NUMBER>(input_energy, output_energy));
435  }
436  }
437 
438  // Sort the energy response on input energy
439  std::sort(m_p_energy_response.begin(), m_p_energy_response.end());
440 }
441 
442 
443 //--------------------------------------------------------------------------------------------------------
444 inline void Scintillator::setEnergyResponse(const std::vector<std::pair<float, float> >& anEnergyResponse)
445 //--------------------------------------------------------------------------------------------------------
446 {
447  m_p_energy_response = anEnergyResponse;
448 
449 /* for (std::vector<std::pair<float, float> >::iterator ite = m_energy_response.begin();
450  ite != m_energy_response.end();
451  ++ite)
452  {
453  ite->first *= aUnitOfEnergy;
454  ite->second *= aUnitOfEnergy;
455  }*/
456 }
457 
458 
459 //----------------------------------------------------------------------------------------------------------
460 inline void Scintillator::setEnergyResponse(const std::vector<std::pair<double, double> >& anEnergyResponse)
461 //----------------------------------------------------------------------------------------------------------
462 {
463  m_p_energy_response.clear();
464  m_p_energy_response.reserve(anEnergyResponse.size());
465 
466  for (std::vector<std::pair<double, double> >::const_iterator ite = anEnergyResponse.begin();
467  ite != anEnergyResponse.end();
468  ++ite)
469  {
470  m_p_energy_response.push_back(std::pair<RATIONAL_NUMBER, RATIONAL_NUMBER>(ite->first, ite->second));
471  }
472 }
473 
474 
475 //---------------------------------------------------------------------------------------------
477 //---------------------------------------------------------------------------------------------
478 {
479  // There is no energy response, consider the perfect case
480  if (!m_p_energy_response.size())
481  {
482  return anEnergy;
483  }
484 
485  // Deal with extreme cases
486  if (m_p_energy_response.front().first >= anEnergy) return m_p_energy_response.front().second;
487  if (m_p_energy_response.back().first <= anEnergy) return m_p_energy_response.back().second;
488 
489  // Find the closest energy in the energy response
490  int nearest_energy_idx = findNearestEnergyIdx(anEnergy);
491 
492  // Interpolate the values
493  RATIONAL_NUMBER corrected_energy = interpolate(m_p_energy_response[nearest_energy_idx].first,
494  m_p_energy_response[nearest_energy_idx + 1].first,
495  anEnergy,
496  m_p_energy_response[nearest_energy_idx].second,
497  m_p_energy_response[nearest_energy_idx + 1].second);
498 
499  return corrected_energy;
500 }
501 
502 
503 //----------------------------------------------------------------------------------
504 inline int Scintillator::findNearestEnergyIdx(const RATIONAL_NUMBER& anEnergy) const
505 //----------------------------------------------------------------------------------
506 {
507  // The energy response is empty
508  if (!m_p_energy_response.size())
509  {
510  throw OutOfBoundsException(__FILE__, __FUNCTION__, __LINE__);
511  }
512 
513  if (m_p_energy_response.front().first >= anEnergy) return 0;
514  if (m_p_energy_response.back().first <= anEnergy) return (m_p_energy_response.size() - 1);
515 
516  for (int i = 0; i < m_p_energy_response.size() - 1; ++i)
517  {
518  if (m_p_energy_response[i].first <= anEnergy && anEnergy <= m_p_energy_response[i + 1].first) return i;
519  }
520 
521  return (m_p_energy_response.size() - 1);
522 }
523 
524 
525 } // namespace gVirtualXRay
RATIONAL_NUMBER m_thickness
Definition: Scintillator.h:200
static RATIONAL_NUMBER getDensity(const std::string &aMaterial)
int findNearestEnergyIdx(const RATIONAL_NUMBER &anEnergy) const
Find the index of the closest input energy in the energy response.
static std::vector< std::string > getSupportedScintillatorMaterials()
Accessor on the list of scintillator materials currently supported.
const double cm3
cubic centimeter
Definition: Units.h:124
void setMaterial(const std::string &aMaterial)
RATIONAL_NUMBER getThickness()
FileDoesNotExistException is a class to handle exceptions when trying to open a read-only file that i...
Exception is a class to handle exceptions.
Definition: Exception.h:109
Class to handle exceptions when trying to open a read-only file that is not accessible.
#define EPSILON
Smallest value that can be stored with a real number.
Generic class to handle exceptions.
const std::string & getMaterial() const
const double um
micrometre
Definition: Units.h:108
float RATIONAL_NUMBER
Type of data used to store real numbers.
Definition: Types.h:107
const double keV
kiloelectron volt
Definition: Units.h:133
Some utility functions that do not fit anywhere else.
RATIONAL_NUMBER m_density
Definition: Scintillator.h:198
const double cm
centimeter
Definition: Units.h:106
std::vector< std::pair< RATIONAL_NUMBER, RATIONAL_NUMBER > > m_p_energy_response
Energy response. The keys of the map are the input energies.
Definition: Scintillator.h:203
static std::string getCompound(const std::string &aMaterial)
Units, such as meters, etc.
std::vector< std::pair< RATIONAL_NUMBER, RATIONAL_NUMBER > > getEnergyResponse() const
void setEnergyResponse(const std::vector< std::pair< float, float > > &anEnergyResponse)
std::vector< double > logspace(const double &a, const double &b, const int &k)
void release()
Release the data.
void loadEnergyResponse(const std::string &aFileName, const RATIONAL_NUMBER &aUnitOfEnergy)
Load the energy response of the detector from a TSV file.
Scintillator()
Default constructor.
const double g
gram
Definition: Units.h:148
void setThickness(const RATIONAL_NUMBER &aThickness)
double interpolate(const double &a_low, const double &a_high, const double &a0, const double &b_low, const double &b_high)
Definition: Utilities.inl:427
RATIONAL_NUMBER applyEnergyResponse(const RATIONAL_NUMBER &anEnergy) const
Apply the energy response if any.