BornAgain  1.18.0
Simulate and fit neutron and x-ray scattering at grazing incidence
PolyhedralFace Class Reference
Collaboration diagram for PolyhedralFace:

Public Member Functions

 PolyhedralFace (const std::vector< kvector_t > &_V=std::vector< kvector_t >(), bool _sym_S2=false)
 
double area () const
 
double pyramidalVolume () const
 
double radius3d () const
 
complex_t normalProjectionConj (cvector_t q) const
 
complex_t ff_n (int m, cvector_t q) const
 
complex_t ff (cvector_t q, bool sym_Ci) const
 
complex_t ff_2D (cvector_t qpa) const
 
void assert_Ci (const PolyhedralFace &other) const
 

Static Public Member Functions

static double diameter (const std::vector< kvector_t > &V)
 

Private Member Functions

void decompose_q (cvector_t q, complex_t &qperp, cvector_t &qpa) const
 
complex_t ff_n_core (int m, cvector_t qpa, complex_t qperp) const
 
complex_t edge_sum_ff (cvector_t q, cvector_t qpa, bool sym_Ci) const
 
complex_t expansion (complex_t fac_even, complex_t fac_odd, cvector_t qpa, double abslevel) const
 

Private Attributes

bool sym_S2
 
std::vector< PolyhedralEdgeedges
 
double m_area
 
kvector_t m_normal
 
double m_rperp
 
double m_radius_2d
 
double m_radius_3d
 

Static Private Attributes

static double qpa_limit_series = 3e-2
 
static int n_limit_series = 20
 

Detailed Description

A polygon, for form factor computation.

Definition at line 43 of file PolyhedralComponents.h.

Constructor & Destructor Documentation

◆ PolyhedralFace()

PolyhedralFace::PolyhedralFace ( const std::vector< kvector_t > &  V = std::vector<kvector_t>(),
bool  _sym_S2 = false 
)

Sets internal variables for given vertex chain.

Parameters
Voriented vertex list
_sym_S2true if face has a perpedicular two-fold symmetry axis

Definition at line 120 of file PolyhedralComponents.cpp.

120  : sym_S2(_sym_S2)
121 {
122  size_t NV = V.size();
123  if (!NV)
124  throw std::logic_error("Face with no edges");
125  if (NV < 3)
126  throw std::logic_error("Face with less than three edges");
127 
128  // compute radius in 2d and 3d
129  m_radius_2d = diameter(V) / 2;
130  m_radius_3d = 0;
131  for (const kvector_t& v : V)
132  m_radius_3d = std::max(m_radius_3d, v.mag());
133 
134  // Initialize list of 'edges'.
135  // Do not create an edge if two vertices are too close to each other.
136  // TODO This is implemented in a somewhat sloppy way: we just skip an edge if it would
137  // be too short. This leaves tiny open edges. In a clean implementation, we
138  // rather should merge adjacent vertices before generating edges.
139  for (size_t j = 0; j < NV; ++j) {
140  size_t jj = (j + 1) % NV;
141  if ((V[j] - V[jj]).mag() < 1e-14 * m_radius_2d)
142  continue; // distance too short -> skip this edge
143  edges.push_back(PolyhedralEdge(V[j], V[jj]));
144  }
145  size_t NE = edges.size();
146  if (NE < 3)
147  throw std::invalid_argument("Face has less than three non-vanishing edges");
148 
149  // compute n_k, rperp
150  m_normal = kvector_t();
151  for (size_t j = 0; j < NE; ++j) {
152  size_t jj = (j + 1) % NE;
153  kvector_t ee = edges[j].E().cross(edges[jj].E());
154  if (ee.mag2() == 0) {
155  throw std::logic_error("Two adjacent edges are parallel");
156  }
157  m_normal += ee.unit();
158  }
159  m_normal /= NE;
160  m_rperp = 0;
161  for (size_t j = 0; j < NV; ++j)
162  m_rperp += V[j].dot(m_normal);
163  m_rperp /= NV;
164  // assert that the vertices lay in a plane
165  for (size_t j = 1; j < NV; ++j)
166  if (std::abs(V[j].dot(m_normal) - m_rperp) > 1e-14 * m_radius_3d)
167  throw std::logic_error("Face is not planar");
168  // compute m_area
169  m_area = 0;
170  for (size_t j = 0; j < NV; ++j) {
171  size_t jj = (j + 1) % NV;
172  m_area += m_normal.dot(V[j].cross(V[jj])) / 2;
173  }
174  // only now deal with inversion symmetry
175  if (sym_S2) {
176  if (NE & 1)
177  throw std::logic_error("Odd #edges violates symmetry S2");
178  NE /= 2;
179  for (size_t j = 0; j < NE; ++j) {
180  if (((edges[j].R() - m_rperp * m_normal) + (edges[j + NE].R() - m_rperp * m_normal))
181  .mag()
182  > 1e-12 * m_radius_2d)
183  throw std::logic_error("Edge centers violate symmetry S2");
184  if ((edges[j].E() + edges[j + NE].E()).mag() > 1e-12 * m_radius_2d)
185  throw std::logic_error("Edge vectors violate symmetry S2");
186  }
187  // keep only half of the egdes
188  edges.erase(edges.begin() + NE, edges.end());
189  }
190 }
BasicVector3D< double > kvector_t
Definition: Vectors3D.h:21
double mag2() const
Returns magnitude squared of the vector.
auto dot(const BasicVector3D< U > &v) const
Returns dot product of vectors (antilinear in the first [=self] argument).
BasicVector3D< T > unit() const
Returns unit vector in direction of this. Throws for null vector.
One edge of a polygon, for form factor computation.
bool sym_S2
if true, then edges obtainable by inversion are not provided
double m_radius_2d
radius of enclosing cylinder
double m_rperp
distance of this polygon's plane from the origin, along 'm_normal'
static double diameter(const std::vector< kvector_t > &V)
Static method, returns diameter of circle that contains all vertices.
std::vector< PolyhedralEdge > edges
kvector_t m_normal
normal vector of this polygon's plane
double m_radius_3d
radius of enclosing sphere

References diameter(), BasicVector3D< T >::dot(), edges, m_area, m_normal, m_radius_2d, m_radius_3d, m_rperp, BasicVector3D< T >::mag2(), sym_S2, and BasicVector3D< T >::unit().

Here is the call graph for this function:

Member Function Documentation

◆ diameter()

double PolyhedralFace::diameter ( const std::vector< kvector_t > &  V)
static

Static method, returns diameter of circle that contains all vertices.

Definition at line 98 of file PolyhedralComponents.cpp.

99 {
100  double diameterFace = 0;
101  for (size_t j = 0; j < V.size(); ++j)
102  for (size_t jj = j + 1; jj < V.size(); ++jj)
103  diameterFace = std::max(diameterFace, (V[j] - V[jj]).mag());
104  return diameterFace;
105 }

Referenced by PolyhedralFace(), and Polyhedron::Polyhedron().

◆ area()

double PolyhedralFace::area ( ) const
inline

Definition at line 54 of file PolyhedralComponents.h.

54 { return m_area; }

References m_area.

◆ pyramidalVolume()

double PolyhedralFace::pyramidalVolume ( ) const
inline

Definition at line 55 of file PolyhedralComponents.h.

55 { return m_rperp * m_area / 3; }

References m_area, and m_rperp.

◆ radius3d()

double PolyhedralFace::radius3d ( ) const
inline

Definition at line 56 of file PolyhedralComponents.h.

56 { return m_radius_3d; }

References m_radius_3d.

◆ normalProjectionConj()

complex_t PolyhedralFace::normalProjectionConj ( cvector_t  q) const
inline

Returns conj(q)*normal [BasicVector3D::dot is antilinear in 'this' argument].

Definition at line 58 of file PolyhedralComponents.h.

58 { return q.dot(m_normal); }

References BasicVector3D< T >::dot(), and m_normal.

Here is the call graph for this function:

◆ ff_n()

complex_t PolyhedralFace::ff_n ( int  m,
cvector_t  q 
) const

Returns contribution qn*f_n [of order q^(n+1)] from this face to the polyhedral form factor.

Definition at line 235 of file PolyhedralComponents.cpp.

236 {
237  complex_t qn = q.dot(m_normal); // conj(q)*normal (dot is antilinear in 'this' argument)
238  if (std::abs(qn) < eps * q.mag())
239  return 0.;
240  complex_t qperp;
241  cvector_t qpa;
242  decompose_q(q, qperp, qpa);
243  double qpa_mag2 = qpa.mag2();
244  if (qpa_mag2 == 0.) {
245  return qn * pow(qperp * m_rperp, n) * m_area * ReciprocalFactorialArray[n];
246  } else if (sym_S2) {
247  return qn * (ff_n_core(n, qpa, qperp) + ff_n_core(n, -qpa, qperp)) / qpa_mag2;
248  } else {
249  complex_t tmp = ff_n_core(n, qpa, qperp);
250 #ifdef POLYHEDRAL_DIAGNOSTIC
251  if (diagnosis.debmsg >= 3)
252  std::cout << "DBX ff_n " << n << " " << qn << " " << tmp << " " << qpa_mag2 << "\n";
253 #endif
254  return qn * tmp / qpa_mag2;
255  }
256 }
std::complex< double > complex_t
Definition: Complex.h:20
double mag() const
Returns magnitude of the vector.
complex_t ff_n_core(int m, cvector_t qpa, complex_t qperp) const
Returns core contribution to f_n.
void decompose_q(cvector_t q, complex_t &qperp, cvector_t &qpa) const
Sets qperp and qpa according to argument q and to this polygon's normal.

References decompose_q(), BasicVector3D< T >::dot(), anonymous_namespace{PolyhedralComponents.cpp}::eps, ff_n_core(), m_area, m_normal, m_rperp, BasicVector3D< T >::mag(), BasicVector3D< T >::mag2(), anonymous_namespace{PolyhedralComponents.cpp}::ReciprocalFactorialArray, and sym_S2.

Here is the call graph for this function:

◆ ff()

complex_t PolyhedralFace::ff ( cvector_t  q,
bool  sym_Ci 
) const

Returns the contribution ff(q) of this face to the polyhedral form factor.

Definition at line 328 of file PolyhedralComponents.cpp.

329 {
330  complex_t qperp;
331  cvector_t qpa;
332  decompose_q(q, qperp, qpa);
333  double qpa_red = m_radius_2d * qpa.mag();
334  complex_t qr_perp = qperp * m_rperp;
335  complex_t ff0 = (sym_Ci ? 2. * I * sin(qr_perp) : exp_I(qr_perp)) * m_area;
336  if (qpa_red == 0) {
337  return ff0;
338  } else if (qpa_red < qpa_limit_series && !sym_S2) {
339  // summation of power series
340  complex_t fac_even;
341  complex_t fac_odd;
342  if (sym_Ci) {
343  fac_even = 2. * mul_I(sin(qr_perp));
344  fac_odd = 2. * cos(qr_perp);
345  } else {
346  fac_even = exp_I(qr_perp);
347  fac_odd = fac_even;
348  }
349  return ff0 + expansion(fac_even, fac_odd, qpa, std::abs(ff0));
350  } else {
351  // direct evaluation of analytic formula
352  complex_t prefac;
353  if (sym_S2)
354  prefac = sym_Ci ? -8. * sin(qr_perp) : 4. * mul_I(exp_I(qr_perp));
355  else
356  prefac = sym_Ci ? 4. : 2. * exp_I(qr_perp);
357 #ifdef POLYHEDRAL_DIAGNOSTIC
358  if (diagnosis.debmsg >= 2)
359  std::cout << " qrperp=" << qr_perp << " => prefac=" << prefac << "\n";
360 #endif
361  return prefac * edge_sum_ff(q, qpa, sym_Ci) / mul_I(qpa.mag2());
362  }
363 }
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
complex_t exp_I(complex_t z)
Returns exp(I*z), where I is the imaginary unit.
Definition: Complex.h:30
static double qpa_limit_series
determines when use power series
complex_t expansion(complex_t fac_even, complex_t fac_odd, cvector_t qpa, double abslevel) const
Returns sum of n>=1 terms of qpa expansion of 2d form factor.
complex_t edge_sum_ff(cvector_t q, cvector_t qpa, bool sym_Ci) const
Returns core contribution to analytic 2d form factor.

References decompose_q(), edge_sum_ff(), exp_I(), expansion(), I, m_area, m_radius_2d, m_rperp, BasicVector3D< T >::mag(), BasicVector3D< T >::mag2(), mul_I(), qpa_limit_series, and sym_S2.

Referenced by ff_2D().

Here is the call graph for this function:

◆ ff_2D()

complex_t PolyhedralFace::ff_2D ( cvector_t  qpa) const

Returns the two-dimensional form factor of this face, for use in a prism.

Definition at line 367 of file PolyhedralComponents.cpp.

368 {
369  if (std::abs(qpa.dot(m_normal)) > eps * qpa.mag())
370  throw std::logic_error("ff_2D called with perpendicular q component");
371  double qpa_red = m_radius_2d * qpa.mag();
372  if (qpa_red == 0) {
373  return m_area;
374  } else if (qpa_red < qpa_limit_series && !sym_S2) {
375  // summation of power series
376  return m_area + expansion(1., 1., qpa, std::abs(m_area));
377  } else {
378  // direct evaluation of analytic formula
379  complex_t ff = edge_sum_ff(qpa, qpa, false);
380  complex_t ret = (sym_S2 ? 4. : 2. / I) * ff / qpa.mag2();
381 #ifdef POLYHEDRAL_DIAGNOSTIC
382  if (diagnosis.debmsg >= 2)
383  std::cout << std::setprecision(16) << " ret=" << ret << " ff=" << ff << "\n";
384 #endif
385  return ret;
386  }
387 }
complex_t ff(cvector_t q, bool sym_Ci) const
Returns the contribution ff(q) of this face to the polyhedral form factor.

References BasicVector3D< T >::dot(), edge_sum_ff(), anonymous_namespace{PolyhedralComponents.cpp}::eps, expansion(), ff(), I, m_area, m_normal, m_radius_2d, BasicVector3D< T >::mag(), BasicVector3D< T >::mag2(), qpa_limit_series, and sym_S2.

Here is the call graph for this function:

◆ assert_Ci()

void PolyhedralFace::assert_Ci ( const PolyhedralFace other) const

Throws if deviation from inversion symmetry is detected. Does not check vertices.

Definition at line 391 of file PolyhedralComponents.cpp.

392 {
393  if (std::abs(m_rperp - other.m_rperp) > 1e-15 * (m_rperp + other.m_rperp))
394  throw std::logic_error("Faces with different distance from origin violate symmetry Ci");
395  if (std::abs(m_area - other.m_area) > 1e-15 * (m_area + other.m_area))
396  throw std::logic_error("Faces with different areas violate symmetry Ci");
397  if ((m_normal + other.m_normal).mag() > 1e-14)
398  throw std::logic_error("Faces do not have opposite orientation, violating symmetry Ci");
399 }

References m_area, m_normal, and m_rperp.

◆ decompose_q()

void PolyhedralFace::decompose_q ( cvector_t  q,
complex_t qperp,
cvector_t qpa 
) const
private

Sets qperp and qpa according to argument q and to this polygon's normal.

Definition at line 194 of file PolyhedralComponents.cpp.

195 {
196  qperp = m_normal.dot(q);
197  qpa = q - qperp * m_normal;
198  // improve numeric accuracy:
199  qpa -= m_normal.dot(qpa) * m_normal;
200  if (qpa.mag() < eps * std::abs(qperp))
201  qpa = cvector_t(0., 0., 0.);
202 }
BasicVector3D< std::complex< double > > cvector_t
Definition: Vectors3D.h:22

References BasicVector3D< T >::dot(), anonymous_namespace{PolyhedralComponents.cpp}::eps, m_normal, and BasicVector3D< T >::mag().

Referenced by ff(), and ff_n().

Here is the call graph for this function:

◆ ff_n_core()

complex_t PolyhedralFace::ff_n_core ( int  m,
cvector_t  qpa,
complex_t  qperp 
) const
private

Returns core contribution to f_n.

Definition at line 206 of file PolyhedralComponents.cpp.

207 {
208  cvector_t prevec = 2. * m_normal.cross(qpa); // complex conjugation will take place in .dot
209  complex_t ret = 0;
210  complex_t vfacsum = 0;
211  complex_t qrperp = qperp * m_rperp;
212  for (size_t i = 0; i < edges.size(); ++i) {
213  const PolyhedralEdge& e = edges[i];
214  complex_t vfac;
215  if (sym_S2 || i < edges.size() - 1) {
216  vfac = prevec.dot(e.E());
217  vfacsum += vfac;
218  } else {
219  vfac = -vfacsum; // to improve numeric accuracy: qcE_J = - sum_{j=0}^{J-1} qcE_j
220  }
221  complex_t tmp = e.contrib(m + 1, qpa, qrperp);
222  ret += vfac * tmp;
223 #ifdef POLYHEDRAL_DIAGNOSTIC
224  if (diagnosis.debmsg >= 4)
225  std::cout << std::scientific << std::showpos << std::setprecision(16)
226  << "DBX ff_n_core " << m << " " << vfac << " " << tmp
227  << " term=" << vfac * tmp << " sum=" << ret << "\n";
228 #endif
229  }
230  return ret;
231 }
auto cross(const BasicVector3D< U > &v) const
Returns cross product of vectors (linear in both arguments).
kvector_t E() const
complex_t contrib(int m, cvector_t qpa, complex_t qrperp) const
Returns sum_l=0^M/2 u^2l v^(M-2l) / (2l+1)!(M-2l)! - vperp^M/M!
std::string scientific(const T value, int n=10)
Returns scientific string representing given value of any numeric type.
Definition: StringUtils.h:54

References PolyhedralEdge::contrib(), BasicVector3D< T >::cross(), BasicVector3D< T >::dot(), PolyhedralEdge::E(), edges, m_normal, m_rperp, StringUtils::scientific(), and sym_S2.

Referenced by expansion(), and ff_n().

Here is the call graph for this function:

◆ edge_sum_ff()

complex_t PolyhedralFace::edge_sum_ff ( cvector_t  q,
cvector_t  qpa,
bool  sym_Ci 
) const
private

Returns core contribution to analytic 2d form factor.

Definition at line 296 of file PolyhedralComponents.cpp.

297 {
298  cvector_t prevec = m_normal.cross(qpa); // complex conjugation will take place in .dot
299  complex_t sum = 0;
300  complex_t vfacsum = 0;
301  for (size_t i = 0; i < edges.size(); ++i) {
302  const PolyhedralEdge& e = edges[i];
303  complex_t qE = e.qE(qpa);
304  complex_t qR = e.qR(qpa);
305  complex_t Rfac = sym_S2 ? sin(qR) : (sym_Ci ? cos(e.qR(q)) : exp_I(qR));
306  complex_t vfac;
307  if (sym_S2 || i < edges.size() - 1) {
308  vfac = prevec.dot(e.E());
309  vfacsum += vfac;
310  } else {
311  vfac = -vfacsum; // to improve numeric accuracy: qcE_J = - sum_{j=0}^{J-1} qcE_j
312  }
313  complex_t term = vfac * MathFunctions::sinc(qE) * Rfac;
314  sum += term;
315 #ifdef POLYHEDRAL_DIAGNOSTIC
316  if (diagnosis.debmsg >= 2)
317  std::cout << std::scientific << std::showpos << std::setprecision(16)
318  << " sum=" << sum << " term=" << term << " vf=" << vfac << " qE=" << qE
319  << " qR=" << qR << " sinc=" << MathFunctions::sinc(qE) << " Rfac=" << Rfac
320  << "\n";
321 #endif
322  }
323  return sum;
324 }
complex_t qE(cvector_t q) const
complex_t qR(cvector_t q) const
double sinc(double x)
sinc function:

References BasicVector3D< T >::cross(), BasicVector3D< T >::dot(), PolyhedralEdge::E(), edges, exp_I(), m_normal, PolyhedralEdge::qE(), PolyhedralEdge::qR(), StringUtils::scientific(), MathFunctions::sinc(), and sym_S2.

Referenced by ff(), and ff_2D().

Here is the call graph for this function:

◆ expansion()

complex_t PolyhedralFace::expansion ( complex_t  fac_even,
complex_t  fac_odd,
cvector_t  qpa,
double  abslevel 
) const
private

Returns sum of n>=1 terms of qpa expansion of 2d form factor.

Definition at line 260 of file PolyhedralComponents.cpp.

262 {
263 #ifdef POLYHEDRAL_DIAGNOSTIC
264  diagnosis.nExpandedFaces += 1;
265 #endif
266  complex_t sum = 0;
267  complex_t n_fac = I;
268  int count_return_condition = 0;
269  for (int n = 1; n < n_limit_series; ++n) {
270 #ifdef POLYHEDRAL_DIAGNOSTIC
271  diagnosis.maxOrder = std::max(diagnosis.maxOrder, n);
272 #endif
273  complex_t term = n_fac * (n & 1 ? fac_odd : fac_even) * ff_n_core(n, qpa, 0) / qpa.mag2();
274 #ifdef POLYHEDRAL_DIAGNOSTIC
275  if (diagnosis.debmsg >= 2)
276  std::cout << std::setprecision(16) << " sum=" << sum << " +term=" << term << "\n";
277 #endif
278  sum += term;
279  if (std::abs(term) <= eps * std::abs(sum) || std::abs(sum) < eps * abslevel)
280  ++count_return_condition;
281  else
282  count_return_condition = 0;
283  if (count_return_condition > 2)
284  return sum; // regular exit
285  n_fac = mul_I(n_fac);
286  }
287 #ifdef POLYHEDRAL_DIAGNOSTIC
288  if (!diagnosis.request_convergence)
289  return sum;
290 #endif
291  throw std::runtime_error("Series f(q_pa) not converged");
292 }
static int n_limit_series

References anonymous_namespace{PolyhedralComponents.cpp}::eps, ff_n_core(), I, BasicVector3D< T >::mag2(), mul_I(), and n_limit_series.

Referenced by ff(), and ff_2D().

Here is the call graph for this function:

Member Data Documentation

◆ qpa_limit_series

double PolyhedralFace::qpa_limit_series = 3e-2
staticprivate

determines when use power series

Definition at line 65 of file PolyhedralComponents.h.

Referenced by ff(), and ff_2D().

◆ n_limit_series

int PolyhedralFace::n_limit_series = 20
staticprivate

Definition at line 66 of file PolyhedralComponents.h.

Referenced by expansion().

◆ sym_S2

bool PolyhedralFace::sym_S2
private

if true, then edges obtainable by inversion are not provided

Definition at line 68 of file PolyhedralComponents.h.

Referenced by edge_sum_ff(), ff(), ff_2D(), ff_n(), ff_n_core(), and PolyhedralFace().

◆ edges

std::vector<PolyhedralEdge> PolyhedralFace::edges
private

Definition at line 69 of file PolyhedralComponents.h.

Referenced by edge_sum_ff(), ff_n_core(), and PolyhedralFace().

◆ m_area

double PolyhedralFace::m_area
private

Definition at line 70 of file PolyhedralComponents.h.

Referenced by area(), assert_Ci(), ff(), ff_2D(), ff_n(), PolyhedralFace(), and pyramidalVolume().

◆ m_normal

kvector_t PolyhedralFace::m_normal
private

normal vector of this polygon's plane

Definition at line 71 of file PolyhedralComponents.h.

Referenced by assert_Ci(), decompose_q(), edge_sum_ff(), ff_2D(), ff_n(), ff_n_core(), normalProjectionConj(), and PolyhedralFace().

◆ m_rperp

double PolyhedralFace::m_rperp
private

distance of this polygon's plane from the origin, along 'm_normal'

Definition at line 72 of file PolyhedralComponents.h.

Referenced by assert_Ci(), ff(), ff_n(), ff_n_core(), PolyhedralFace(), and pyramidalVolume().

◆ m_radius_2d

double PolyhedralFace::m_radius_2d
private

radius of enclosing cylinder

Definition at line 73 of file PolyhedralComponents.h.

Referenced by ff(), ff_2D(), and PolyhedralFace().

◆ m_radius_3d

double PolyhedralFace::m_radius_3d
private

radius of enclosing sphere

Definition at line 74 of file PolyhedralComponents.h.

Referenced by PolyhedralFace(), and radius3d().


The documentation for this class was generated from the following files: