BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
SimpleUnitConverters.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file Device/Detector/SimpleUnitConverters.cpp
6 //! @brief Implements IUnitConverter classes.
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2018
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
16 #include "Base/Const/Units.h"
17 #include "Base/Math/Constants.h"
18 #include "Device/Beam/Beam.h"
23 #include "Device/Unit/AxisNames.h"
24 #include <algorithm>
25 #include <cmath>
26 #include <stdexcept>
27 
28 namespace {
29 double getQ(double wavelength, double angle)
30 {
31  return 4.0 * M_PI * std::sin(angle) / wavelength;
32 }
33 } // namespace
34 
35 // ************************************************************************************************
36 // class UnitConverterSimple
37 // ************************************************************************************************
38 
40  : m_wavelength(beam.wavelength())
41  , m_alpha_i(-beam.direction().alpha())
42  , m_phi_i(beam.direction().phi())
43 {
44 }
45 
47 {
48  return m_axis_data_table.size();
49 }
50 
51 void UnitConverterSimple::addAxisData(std::string name, double min, double max,
52  Axes::Units default_units, size_t nbins)
53 {
54  AxisData axis_data{name, min, max, default_units, nbins};
55  m_axis_data_table.push_back(axis_data);
56 }
57 
58 double UnitConverterSimple::calculateMin(size_t i_axis, Axes::Units units_type) const
59 {
60  checkIndex(i_axis);
61  units_type = substituteDefaultUnits(units_type);
62  const auto& axis_data = m_axis_data_table[i_axis];
63  if (units_type == Axes::Units::NBINS)
64  return 0.0;
65  return calculateValue(i_axis, units_type, axis_data.min);
66 }
67 
68 double UnitConverterSimple::calculateMax(size_t i_axis, Axes::Units units_type) const
69 {
70  checkIndex(i_axis);
71  units_type = substituteDefaultUnits(units_type);
72  const auto& axis_data = m_axis_data_table[i_axis];
73  if (units_type == Axes::Units::NBINS)
74  return static_cast<double>(axis_data.nbins);
75  return calculateValue(i_axis, units_type, axis_data.max);
76 }
77 
78 size_t UnitConverterSimple::axisSize(size_t i_axis) const
79 {
80  checkIndex(i_axis);
81  return m_axis_data_table[i_axis].nbins;
82 }
83 
84 std::vector<Axes::Units> UnitConverterSimple::availableUnits() const
85 {
86  return {Axes::Units::NBINS, Axes::Units::RADIANS, Axes::Units::DEGREES};
87 }
88 
89 std::unique_ptr<IAxis> UnitConverterSimple::createConvertedAxis(size_t i_axis,
90  Axes::Units units) const
91 {
92  const double min = calculateMin(i_axis, units);
93  const double max = calculateMax(i_axis, units);
94  const auto& axis_name = axisName(i_axis, units);
95  const auto axis_size = axisSize(i_axis);
96  return std::make_unique<FixedBinAxis>(axis_name, axis_size, min, max);
97 }
98 
100  : m_axis_data_table(other.m_axis_data_table)
101  , m_wavelength(other.m_wavelength)
102  , m_alpha_i(other.m_alpha_i)
103  , m_phi_i(other.m_phi_i)
104 {
105 }
106 
107 void UnitConverterSimple::addDetectorAxis(const IDetector& detector, size_t i_axis)
108 {
109  const auto& axis = detector.axis(i_axis);
110  const auto* p_roi = detector.regionOfInterest();
111  const auto& axis_name = axisName(i_axis);
112  if (!p_roi) {
113  addAxisData(axis_name, axis.lowerBound(), axis.upperBound(), defaultUnits(), axis.size());
114  return;
115  }
116  auto P_roi_axis = p_roi->clipAxisToRoi(i_axis, axis);
117  addAxisData(axis_name, P_roi_axis->lowerBound(), P_roi_axis->upperBound(), defaultUnits(),
118  P_roi_axis->size());
119 }
120 
121 // ************************************************************************************************
122 // class SphericalConverter
123 // ************************************************************************************************
124 
126  : UnitConverterSimple(beam)
127 {
128  if (detector.dimension() != 2)
129  throw std::runtime_error("Error in SphericalConverter constructor: "
130  "detector has wrong dimension: "
131  + std::to_string(static_cast<int>(detector.dimension())));
132  addDetectorAxis(detector, 0);
133  addDetectorAxis(detector, 1);
134 }
135 
137 
139 {
140  return new SphericalConverter(*this);
141 }
142 
143 std::vector<Axes::Units> SphericalConverter::availableUnits() const
144 {
145  auto result = UnitConverterSimple::availableUnits();
146  result.push_back(Axes::Units::QSPACE);
147  return result;
148 }
149 
151 {
152  return Axes::Units::DEGREES;
153 }
154 
156 {
157 }
158 
159 double SphericalConverter::calculateValue(size_t i_axis, Axes::Units units_type, double value) const
160 {
161  switch (units_type) {
162  case Axes::Units::RADIANS:
163  return value;
164  case Axes::Units::DEGREES:
165  return Units::rad2deg(value);
166  case Axes::Units::QSPACE: {
168  if (i_axis == 0) {
169  const auto k_f = vecOfLambdaAlphaPhi(m_wavelength, 0.0, value);
170  return (k_i - k_f).y();
171  } else if (i_axis == 1) {
172  const auto k_f = vecOfLambdaAlphaPhi(m_wavelength, value, 0.0);
173  return (k_f - k_i).z();
174  }
175  throw std::runtime_error("Error in SphericalConverter::calculateValue: "
176  "incorrect axis index: "
177  + std::to_string(static_cast<int>(i_axis)));
178  }
179  case Axes::Units::QXQY: {
181  if (i_axis == 0) {
182  const auto k_f = vecOfLambdaAlphaPhi(m_wavelength, 0.0, value);
183  return (k_i - k_f).y();
184  } else if (i_axis == 1) {
185  const auto k_f = vecOfLambdaAlphaPhi(m_wavelength, value, 0.0);
186  return (k_f - k_i).x();
187  }
188  throw std::runtime_error("Error in SphericalConverter::calculateValue: "
189  "incorrect axis index: "
190  + std::to_string(static_cast<int>(i_axis)));
191  }
192  default:
193  throwUnitsError("SphericalConverter::calculateValue", availableUnits());
194  }
195 }
196 
197 std::vector<std::map<Axes::Units, std::string>> SphericalConverter::createNameMaps() const
198 {
199  std::vector<std::map<Axes::Units, std::string>> result;
200  result.push_back(AxisNames::InitSphericalAxis0());
201  result.push_back(AxisNames::InitSphericalAxis1());
202  return result;
203 }
204 
205 // ************************************************************************************************
206 // class RectangularConverter
207 // ************************************************************************************************
208 
210  : UnitConverterSimple(beam)
211 {
212  if (detector.dimension() != 2)
213  throw std::runtime_error("Error in RectangularConverter constructor: "
214  "detector has wrong dimension: "
215  + std::to_string(static_cast<int>(detector.dimension())));
216  addDetectorAxis(detector, 0);
217  addDetectorAxis(detector, 1);
218  m_detector_pixel.reset(detector.regionOfInterestPixel());
219 }
220 
222 
224 {
225  return new RectangularConverter(*this);
226 }
227 
228 std::vector<Axes::Units> RectangularConverter::availableUnits() const
229 {
230  auto result = UnitConverterSimple::availableUnits();
231  result.push_back(Axes::Units::QSPACE);
232  result.push_back(Axes::Units::MM);
233  return result;
234 }
235 
237 {
238  return Axes::Units::MM;
239 }
240 
242  : UnitConverterSimple(other), m_detector_pixel(other.m_detector_pixel->clone())
243 {
244 }
245 
246 double RectangularConverter::calculateValue(size_t i_axis, Axes::Units units_type,
247  double value) const
248 {
249  if (units_type == Axes::Units::MM)
250  return value;
251  const auto k00 = m_detector_pixel->getPosition(0.0, 0.0);
252  const auto k01 = m_detector_pixel->getPosition(0.0, 1.0);
253  const auto k10 = m_detector_pixel->getPosition(1.0, 0.0);
254  const auto& max_pos = i_axis == 0 ? k10 : k01; // position of max along given axis
255  const double shift = value - m_axis_data_table[i_axis].min;
256  const auto k_f = normalizeToWavelength(k00 + shift * (max_pos - k00).unit());
257  switch (units_type) {
258  case Axes::Units::RADIANS:
259  return axisAngle(i_axis, k_f);
260  case Axes::Units::DEGREES:
261  return Units::rad2deg(axisAngle(i_axis, k_f));
262  case Axes::Units::QSPACE: {
264  if (i_axis == 0)
265  return (k_i - k_f).y();
266  if (i_axis == 1)
267  return (k_f - k_i).z();
268  throw std::runtime_error("Error in RectangularConverter::calculateValue: "
269  "incorrect axis index: "
270  + std::to_string(static_cast<int>(i_axis)));
271  }
272  case Axes::Units::QXQY: {
274  if (i_axis == 0)
275  return (k_i - k_f).y();
276  if (i_axis == 1)
277  return (k_f - k_i).x();
278  throw std::runtime_error("Error in RectangularConverter::calculateValue: "
279  "incorrect axis index: "
280  + std::to_string(static_cast<int>(i_axis)));
281  }
282  default:
283  throwUnitsError("RectangularConverter::calculateValue", availableUnits());
284  }
285 }
286 
287 std::vector<std::map<Axes::Units, std::string>> RectangularConverter::createNameMaps() const
288 {
289  std::vector<std::map<Axes::Units, std::string>> result;
290  result.push_back(AxisNames::InitRectangularAxis0());
291  result.push_back(AxisNames::InitRectangularAxis1());
292  return result;
293 }
294 
296 {
297  if (m_wavelength <= 0.0)
298  throw std::runtime_error("Error in RectangularConverter::normalizeToWavelength: "
299  "wavelength <= 0");
300  double K = M_TWOPI / m_wavelength;
301  return vector.unit() * K;
302 }
303 
304 double RectangularConverter::axisAngle(size_t i_axis, kvector_t k_f) const
305 {
306  if (i_axis == 0)
307  return k_f.phi();
308  if (i_axis == 1)
309  return M_PI_2 - k_f.theta();
310  throw std::runtime_error("Error in RectangularConverter::axisAngle: "
311  "incorrect axis index: "
312  + std::to_string(static_cast<int>(i_axis)));
313 }
314 
315 // ************************************************************************************************
316 // class OffSpecularConverter
317 // ************************************************************************************************
318 
320  const IAxis& alpha_axis)
321  : UnitConverterSimple(beam)
322 {
323  if (detector.dimension() != 2)
324  throw std::runtime_error("Error in OffSpecularConverter constructor: "
325  "detector has wrong dimension: "
326  + std::to_string(static_cast<int>(detector.dimension())));
327  addAxisData(axisName(0), alpha_axis.lowerBound(), alpha_axis.upperBound(), defaultUnits(),
328  alpha_axis.size());
329  addDetectorYAxis(detector);
330 }
331 
333 
335 {
336  return new OffSpecularConverter(*this);
337 }
338 
340 {
341  return Axes::Units::DEGREES;
342 }
343 
345  : UnitConverterSimple(other)
346 {
347 }
348 
349 double OffSpecularConverter::calculateValue(size_t, Axes::Units units_type, double value) const
350 {
351  switch (units_type) {
352  case Axes::Units::RADIANS:
353  return value;
354  case Axes::Units::DEGREES:
355  return Units::rad2deg(value);
356  default:
357  throwUnitsError("OffSpecularConverter::calculateValue", availableUnits());
358  }
359 }
360 
361 std::vector<std::map<Axes::Units, std::string>> OffSpecularConverter::createNameMaps() const
362 {
363  std::vector<std::map<Axes::Units, std::string>> result;
364  result.push_back(AxisNames::InitOffSpecularAxis0());
365  result.push_back(AxisNames::InitOffSpecularAxis1());
366  return result;
367 }
368 
370 {
371  const auto& axis = detector.axis(1);
372  const auto* p_roi = detector.regionOfInterest();
373  const auto& axis_name = axisName(1);
374  std::unique_ptr<IAxis> P_new_axis;
375  if (p_roi) {
376  P_new_axis = p_roi->clipAxisToRoi(1, axis);
377  } else {
378  P_new_axis.reset(axis.clone());
379  }
380  if (!P_new_axis)
381  throw std::runtime_error("Error in OffSpecularConverter::addDetectorYAxis: "
382  "could not retrieve the y-axis of the detector");
383  if (const auto* P_rect_det = dynamic_cast<const RectangularDetector*>(&detector)) {
384  std::unique_ptr<RectangularPixel> P_det_pixel(P_rect_det->regionOfInterestPixel());
385  const auto k00 = P_det_pixel->getPosition(0.0, 0.0);
386  const auto k01 = P_det_pixel->getPosition(0.0, 1.0);
387  const double alpha_f_min = M_PI_2 - k00.theta();
388  const double alpha_f_max = M_PI_2 - k01.theta();
389  addAxisData(axis_name, alpha_f_min, alpha_f_max, defaultUnits(), P_new_axis->size());
390  } else if (dynamic_cast<const SphericalDetector*>(&detector)) {
391  const double alpha_f_min = P_new_axis->lowerBound();
392  const double alpha_f_max = P_new_axis->upperBound();
393  addAxisData(axis_name, alpha_f_min, alpha_f_max, defaultUnits(), P_new_axis->size());
394  } else {
395  throw std::runtime_error("Error in OffSpecularConverter::addDetectorYAxis: "
396  "wrong detector type");
397  }
398 }
399 
400 // ************************************************************************************************
401 // class DepthProbeConverter
402 // ************************************************************************************************
403 
404 const std::string z_axis_name = "Position [nm]";
405 
406 DepthProbeConverter::DepthProbeConverter(const Beam& beam, const IAxis& alpha_axis,
407  const IAxis& z_axis)
408  : UnitConverterSimple(beam)
409 {
410  const auto& alpha_axis_name = axisName(0);
411  const auto& z_axis_name = axisName(1);
412  addAxisData(alpha_axis_name, alpha_axis.lowerBound(), alpha_axis.upperBound(), defaultUnits(),
413  alpha_axis.size());
415  z_axis.size());
416 }
417 
419 
421 {
422  return new DepthProbeConverter(*this);
423 }
424 
425 std::vector<Axes::Units> DepthProbeConverter::availableUnits() const
426 {
427  auto result = UnitConverterSimple::availableUnits();
428  result.push_back(Axes::Units::QSPACE);
429  return result;
430 }
431 
433  : UnitConverterSimple(other)
434 {
435 }
436 
437 double DepthProbeConverter::calculateValue(size_t i_axis, Axes::Units units_type,
438  double value) const
439 {
440  checkUnits(units_type);
441  if (i_axis == 1)
442  return value; // unit conversions are not applied to sample position axis
443  switch (units_type) {
444  case Axes::Units::DEGREES:
445  return Units::rad2deg(value);
446  case Axes::Units::QSPACE:
447  return getQ(m_wavelength, value);
448  default:
449  return value;
450  }
451 }
452 
453 std::vector<std::map<Axes::Units, std::string>> DepthProbeConverter::createNameMaps() const
454 {
455  std::vector<std::map<Axes::Units, std::string>> result;
456  result.push_back(AxisNames::InitSpecAxis());
457  result.push_back(AxisNames::InitSampleDepthAxis());
458  return result;
459 }
460 
462 {
463  const auto& available_units = availableUnits();
464  if (std::find(available_units.begin(), available_units.end(), units_type)
465  == available_units.cend())
466  throwUnitsError("DepthProbeConverter::checkUnits", available_units);
467 }
Defines namespace AxisNames.
Defines class Beam.
Defines M_PI and some more mathematical constants.
#define M_TWOPI
Definition: Constants.h:54
#define M_PI_2
Definition: Constants.h:45
#define M_PI
Definition: Constants.h:44
kvector_t vecOfLambdaAlphaPhi(double _lambda, double _alpha, double _phi)
Definition: Direction.cpp:19
Defines class RectangularDetector.
Defines class RectangularPixel.
Defines class RegionOfInterest.
const std::string z_axis_name
Defines interface UnitConverterSimple and its subclasses.
Defines class SphericalDetector.
Defines some unit conversion factors and other constants in namespace Units.
BasicVector3D< T > unit() const
Returns unit vector in direction of this. Throws for null vector.
double theta() const
Returns polar angle.
double phi() const
Returns azimuth angle.
An incident neutron or x-ray beam.
Definition: Beam.h:27
DepthProbeConverter class handles the unit translations for depth probe simulations Its default units...
std::vector< Axes::Units > availableUnits() const override
Returns the list of all available units.
DepthProbeConverter(const Beam &beam, const IAxis &alpha_axis, const IAxis &z_axis)
~DepthProbeConverter() override
double calculateValue(size_t, Axes::Units units_type, double value) const override
void checkUnits(Axes::Units units_type) const
Axes::Units defaultUnits() const override
std::vector< std::map< Axes::Units, std::string > > createNameMaps() const override
DepthProbeConverter * clone() const override
Interface for one-dimensional axes.
Definition: IAxis.h:25
virtual double upperBound() const =0
Returns value of last point of axis.
virtual size_t size() const =0
retrieve the number of bins
virtual double lowerBound() const =0
Returns value of first point of axis.
Abstract 2D detector interface.
Definition: IDetector2D.h:31
const RegionOfInterest * regionOfInterest() const override
Returns region of interest if exists.
Definition: IDetector2D.cpp:43
Abstract detector interface.
Definition: IDetector.h:36
size_t dimension() const
Returns actual dimensionality of the detector (number of defined axes)
Definition: IDetector.cpp:46
const IAxis & axis(size_t index) const
Definition: IDetector.cpp:56
virtual const RegionOfInterest * regionOfInterest() const =0
Returns region of interest if exists.
Axes::Units substituteDefaultUnits(Axes::Units units) const
void checkIndex(size_t i_axis) const
virtual Axes::Units defaultUnits() const =0
void throwUnitsError(std::string method, std::vector< Axes::Units > available) const
std::string axisName(size_t i_axis, Axes::Units units_type=Axes::Units::DEFAULT) const
IUnitConverter class that handles the unit translations for off-specular simulations with a spherical...
OffSpecularConverter(const IDetector2D &detector, const Beam &beam, const IAxis &alpha_axis)
std::vector< std::map< Axes::Units, std::string > > createNameMaps() const override
OffSpecularConverter * clone() const override
Axes::Units defaultUnits() const override
~OffSpecularConverter() override
double calculateValue(size_t i_axis, Axes::Units units_type, double value) const override
void addDetectorYAxis(const IDetector2D &detector)
IUnitConverter class that handles the unit translations for rectangular detectors Its default units a...
kvector_t normalizeToWavelength(kvector_t vector) const
double calculateValue(size_t i_axis, Axes::Units units_type, double value) const override
RectangularConverter(const RectangularDetector &detector, const Beam &beam)
~RectangularConverter() override
std::vector< Axes::Units > availableUnits() const override
Returns the list of all available units.
double axisAngle(size_t i_axis, kvector_t k_f) const
std::vector< std::map< Axes::Units, std::string > > createNameMaps() const override
Axes::Units defaultUnits() const override
RectangularConverter * clone() const override
std::unique_ptr< RectangularPixel > m_detector_pixel
A flat rectangular detector with axes and resolution function.
RectangularPixel * regionOfInterestPixel() const
IUnitConverter class that handles the unit translations for spherical detectors Its default units are...
std::vector< Axes::Units > availableUnits() const override
Returns the list of all available units.
~SphericalConverter() override
double calculateValue(size_t i_axis, Axes::Units units_type, double value) const override
SphericalConverter(const SphericalDetector &detector, const Beam &beam)
std::vector< std::map< Axes::Units, std::string > > createNameMaps() const override
SphericalConverter * clone() const override
Axes::Units defaultUnits() const override
A detector with coordinate axes along angles phi and alpha.
Interface for objects that provide axis translations to different units for IDetector objects.
void addDetectorAxis(const IDetector &detector, size_t i_axis)
double calculateMax(size_t i_axis, Axes::Units units_type) const override
void addAxisData(std::string name, double min, double max, Axes::Units default_units, size_t nbins)
std::vector< AxisData > m_axis_data_table
size_t axisSize(size_t i_axis) const override
double calculateMin(size_t i_axis, Axes::Units units_type) const override
std::unique_ptr< IAxis > createConvertedAxis(size_t i_axis, Axes::Units units) const override
virtual double calculateValue(size_t i_axis, Axes::Units units_type, double value) const =0
virtual size_t dimension() const override
UnitConverterSimple(const Beam &beam)
std::vector< Axes::Units > availableUnits() const override
Returns the list of all available units.
std::map< Axes::Units, std::string > InitOffSpecularAxis1()
Definition: AxisNames.cpp:73
std::map< Axes::Units, std::string > InitRectangularAxis0()
Definition: AxisNames.cpp:41
std::map< Axes::Units, std::string > InitRectangularAxis1()
Definition: AxisNames.cpp:52
std::map< Axes::Units, std::string > InitSphericalAxis0()
Definition: AxisNames.cpp:20
std::map< Axes::Units, std::string > InitSphericalAxis1()
Definition: AxisNames.cpp:30
std::map< Axes::Units, std::string > InitSpecAxis()
Definition: AxisNames.cpp:82
std::map< Axes::Units, std::string > InitOffSpecularAxis0()
Definition: AxisNames.cpp:65
std::map< Axes::Units, std::string > InitSampleDepthAxis()
Definition: AxisNames.cpp:108
QString const & name(EShape k)
Definition: particles.cpp:21
double rad2deg(double angle)
Definition: Units.h:55