BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
Polyhedron.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file Sample/HardParticle/Polyhedron.cpp
6 //! @brief Implements class Polyhedron.
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 
15 //! The mathematics implemented here is described in full detail in a paper
16 //! by Joachim Wuttke, entitled
17 //! "Numerically stable form factor of any polygon and polyhedron"
18 
20 #include "Base/Math/Functions.h"
22 #include <boost/format.hpp>
23 #include <iomanip>
24 #include <iostream>
25 #include <stdexcept> // need overlooked by g++ 5.4
26 
27 namespace {
28 const double eps = 2e-16;
29 const double q_limit_series = 1e-2;
30 const int n_limit_series = 20;
31 } // namespace
32 
33 Polyhedron::Polyhedron(const PolyhedralTopology& topology, double z_bottom,
34  const std::vector<kvector_t>& vertices)
35 {
36 
37  m_vertices.clear();
38  for (const kvector_t& vertex : vertices)
39  m_vertices.push_back(vertex - kvector_t{0, 0, z_bottom});
40 
41  try {
42  m_z_bottom = z_bottom;
43  m_sym_Ci = topology.symmetry_Ci;
44 
45  double diameter = 0;
46  for (size_t j = 0; j < vertices.size(); ++j)
47  for (size_t jj = j + 1; jj < vertices.size(); ++jj)
48  diameter = std::max(diameter, (vertices[j] - vertices[jj]).mag());
49 
50  m_faces.clear();
51  for (const PolygonalTopology& tf : topology.faces) {
52  std::vector<kvector_t> corners; // of one face
53  for (int i : tf.vertexIndices)
54  corners.push_back(vertices[i]);
55  if (PolyhedralFace::diameter(corners) <= 1e-14 * diameter)
56  continue; // skip ridiculously small face
57  m_faces.push_back(PolyhedralFace(corners, tf.symmetry_S2));
58  }
59  if (m_faces.size() < 4)
60  throw std::logic_error("Less than four non-vanishing faces");
61 
62  m_radius = 0;
63  m_volume = 0;
64  for (const PolyhedralFace& Gk : m_faces) {
65  m_radius = std::max(m_radius, Gk.radius3d());
66  m_volume += Gk.pyramidalVolume();
67  }
68  if (m_sym_Ci) {
69  if (m_faces.size() & 1)
70  throw std::logic_error("Odd #faces violates symmetry Ci");
71  size_t N = m_faces.size() / 2;
72  // for this tests, m_faces must be in a specific order
73  for (size_t k = 0; k < N; ++k)
74  m_faces[k].assert_Ci(m_faces[2 * N - 1 - k]);
75  // keep only half of the faces
76  m_faces.erase(m_faces.begin() + N, m_faces.end());
77  }
78  } catch (std::invalid_argument& e) {
79  throw std::invalid_argument(std::string("Invalid parameterization of Polyhedron: ")
80  + e.what());
81  } catch (std::logic_error& e) {
82  throw std::logic_error(std::string("Bug in Polyhedron: ") + e.what()
83  + " [please report to the maintainers]");
84  } catch (std::exception& e) {
85  throw std::runtime_error(std::string("Unexpected exception in Polyhedron: ") + e.what()
86  + " [please report to the maintainers]");
87  }
88 }
89 
90 Polyhedron::~Polyhedron() = default;
91 
93 {
94  // just one test; one could do much more ...
95  double pyramidal_volume = 0;
96  for (const auto& Gk : m_faces)
97  pyramidal_volume += Gk.pyramidalVolume();
98  pyramidal_volume /= m_faces.size();
99  for (const auto& Gk : m_faces)
100  if (std::abs(Gk.pyramidalVolume() - pyramidal_volume) > 160 * eps * pyramidal_volume) {
101  std::cerr << std::setprecision(16)
102  << "Bug: pyr_volume(this face)=" << Gk.pyramidalVolume()
103  << " vs pyr_volume(avge)=" << pyramidal_volume << "\n";
104  throw std::runtime_error("Deviant pyramidal volume in Platonic Polyhedron");
105  }
106 }
107 
108 double Polyhedron::volume() const
109 {
110  return m_volume;
111 }
112 double Polyhedron::radius() const
113 {
114  return m_radius;
115 }
116 
117 const std::vector<kvector_t>& Polyhedron::vertices()
118 {
119  return m_vertices;
120 }
121 
122 //! Returns the form factor F(q) of this polyhedron, respecting the offset z_bottom.
123 
125 {
126  try {
127  return exp_I(-m_z_bottom * q.z()) * evaluate_centered(q);
128  } catch (std::logic_error& e) {
129  throw std::logic_error(std::string("Bug in Polyhedron: ") + e.what()
130  + " [please report to the maintainers]");
131  } catch (std::runtime_error& e) {
132  throw std::runtime_error(std::string("Numeric computation failed in Polyhedron: ")
133  + e.what() + " [please report to the maintainers]");
134  } catch (std::exception& e) {
135  throw std::runtime_error(std::string("Unexpected exception in Polyhedron: ") + e.what()
136  + " [please report to the maintainers]");
137  }
138 }
139 
140 //! Returns the form factor F(q) of this polyhedron, with origin at z=0.
141 
143 {
144  double q_red = m_radius * q.mag();
145 #ifdef ALGORITHM_DIAGNOSTIC
146  polyhedralDiagnosis.reset();
147 #endif
148  if (q_red == 0) {
149  return m_volume;
150  } else if (q_red < q_limit_series) {
151  // summation of power series
152 #ifdef ALGORITHM_DIAGNOSTIC
153  polyhedralDiagnosis.algo = 100;
154 #endif
155  complex_t sum = 0;
156  complex_t n_fac = (m_sym_Ci ? -2 : -1) / q.mag2();
157  int count_return_condition = 0;
158  for (int n = 2; n < n_limit_series; ++n) {
159  if (m_sym_Ci && n & 1)
160  continue;
161 #ifdef ALGORITHM_DIAGNOSTIC
162  polyhedralDiagnosis.order = std::max(polyhedralDiagnosis.order, n);
163 #endif
164  complex_t term = 0;
165  for (const PolyhedralFace& Gk : m_faces) {
166  complex_t tmp = Gk.ff_n(n + 1, q);
167  term += tmp;
168  }
169  term *= n_fac;
170 #ifdef ALGORITHM_DIAGNOSTIC_LEVEL2
171  polyhedralDiagnosis.msg +=
172  boost::str(boost::format(" + term(n=%2i) = %23.17e+i*%23.17e\n") % n % term.real()
173  % term.imag());
174 #endif
175  sum += term;
176  if (std::abs(term) <= eps * std::abs(sum) || std::abs(sum) < eps * m_volume)
177  ++count_return_condition;
178  else
179  count_return_condition = 0;
180  if (count_return_condition > 2)
181  return m_volume + sum; // regular exit
182  n_fac = m_sym_Ci ? -n_fac : mul_I(n_fac);
183  }
184  throw std::runtime_error("Series F(q) not converged");
185  } else {
186  // direct evaluation of analytic formula (coefficients may involve series)
187 #ifdef ALGORITHM_DIAGNOSTIC
188  polyhedralDiagnosis.algo = 200;
189 #endif
190  complex_t sum = 0;
191  for (const PolyhedralFace& Gk : m_faces) {
192  complex_t qn = Gk.normalProjectionConj(q); // conj(q)*normal
193  if (std::abs(qn) < eps * q.mag())
194  continue;
195  complex_t term = qn * Gk.ff(q, m_sym_Ci);
196 #ifdef ALGORITHM_DIAGNOSTIC //_LEVEL2
197  polyhedralDiagnosis.msg += boost::str(boost::format(" + face_ff = %23.17e+i*%23.17e\n")
198  % term.real() % term.imag());
199 #endif
200  sum += term;
201  }
202 #ifdef ALGORITHM_DIAGNOSTIC //_LEVEL2
203  polyhedralDiagnosis.msg +=
204  boost::str(boost::format(" -> raw sum = %23.17e+i*%23.17e; divisor = %23.17e\n")
205  % sum.real() % sum.imag() % q.mag2());
206 #endif
207  return sum / I / q.mag2();
208  }
209 }
constexpr complex_t I
Definition: Complex.h:21
complex_t mul_I(complex_t z)
Returns product I*z, where I is the imaginary unit.
Definition: Complex.h:24
std::complex< double > complex_t
Definition: Complex.h:20
complex_t exp_I(complex_t z)
Returns exp(I*z), where I is the imaginary unit.
Definition: Complex.h:30
Defines functions in namespace Math.
Defines classes PolygonalTopology, PolyhedralTopology.
Defines class Polyhedron.
double mag2() const
Returns magnitude squared of the vector.
double mag() const
Returns magnitude of the vector.
T z() const
Returns z-component in cartesian coordinate system.
Definition: BasicVector3D.h:67
For internal use in PolyhedralFace.
std::vector< int > vertexIndices
A polygon, for form factor computation.
static double diameter(const std::vector< kvector_t > &V)
Static method, returns diameter of circle that contains all vertices.
For internal use in IFormFactorPolyhedron.
std::vector< PolygonalTopology > faces
void assert_platonic() const
Definition: Polyhedron.cpp:92
double volume() const
Definition: Polyhedron.cpp:108
double m_z_bottom
Definition: Polyhedron.h:45
double m_volume
Definition: Polyhedron.h:50
Polyhedron()=delete
bool m_sym_Ci
if true, then faces obtainable by inversion are not provided
Definition: Polyhedron.h:46
std::vector< PolyhedralFace > m_faces
Definition: Polyhedron.h:48
complex_t evaluate_centered(const cvector_t &q) const
Returns the form factor F(q) of this polyhedron, with origin at z=0.
Definition: Polyhedron.cpp:142
complex_t evaluate_for_q(const cvector_t &q) const
needed for topZ, bottomZ computation
Definition: Polyhedron.cpp:124
const std::vector< kvector_t > & vertices()
Definition: Polyhedron.cpp:117
std::vector< kvector_t > m_vertices
Definition: Polyhedron.h:51
double m_radius
Definition: Polyhedron.h:49
double radius() const
Definition: Polyhedron.cpp:112