BornAgain  1.19.79
Open-source research software to simulate and fit neutron and x-ray reflectometry and grazing-incidence small-angle scattering
Interference2DParaCrystal.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file Sample/Aggregate/Interference2DParaCrystal.cpp
6 //! @brief Implements class Interference2DParaCrystal.
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/Math/IntegratorGK.h"
17 #include "Fit/Param/RealLimits.h"
18 #include <limits>
19 
21  double damping_length, double domain_size_1,
22  double domain_size_2)
23  : IInterference(0)
24  , m_integrate_xi(false)
25  , m_damping_length(damping_length)
26 {
27  m_lattice.reset(lattice.clone());
28  setDomainSizes(domain_size_1, domain_size_2);
30  RealLimits::nonnegative().check("DomainSize1", m_domain_sizes[0]);
31  RealLimits::nonnegative().check("DomainSize2", m_domain_sizes[1]);
32 }
33 
35 
37 {
39  m_domain_sizes[1]);
40  result->setPositionVariance(m_position_var);
41  if (m_pdf1 && m_pdf2)
42  result->setProbabilityDistributions(*m_pdf1, *m_pdf2);
43  result->setIntegrationOverXi(m_integrate_xi);
44  return result;
45 }
46 
47 //! Sets the probability distributions (Fourier transformed) for the two lattice directions.
48 //! @param pdf_1: probability distribution in first lattice direction
49 //! @param pdf_2: probability distribution in second lattice direction
50 
52  const IProfile2D& pdf_2)
53 {
54  m_pdf1.reset(pdf_1.clone());
55  m_pdf2.reset(pdf_2.clone());
56 }
57 
58 //! Sets the damping length.
59 //! @param damping_length: the damping (coherence) length of the paracrystal in nanometers
60 
62 {
63  m_damping_length = damping_length;
64 }
65 
67 {
68  double area = m_lattice->unitCellArea();
69  return area == 0.0 ? 0.0 : 1.0 / area;
70 }
71 
72 std::vector<const INode*> Interference2DParaCrystal::nodeChildren() const
73 {
74  return std::vector<const INode*>() << m_pdf1 << m_pdf2 << m_lattice;
75 }
76 
78 {
79  if (!m_integrate_xi)
80  return interferenceForXi(m_lattice->rotationAngle(), q.x(), q.y());
81  return RealIntegrator().integrate(
82  [&](double xi) -> double { return interferenceForXi(xi, q.x(), q.y()); }, 0.0,
83  M_TWOPI)
84  / M_TWOPI;
85 }
86 
87 //! Sets the sizes of coherence domains.
88 //! @param size_1: coherence domain size along the first basis vector in nanometers
89 //! @param size_2: coherence domain size along the second basis vector in nanometers
90 
91 void Interference2DParaCrystal::setDomainSizes(double size_1, double size_2)
92 {
93  m_domain_sizes[0] = size_1;
94  m_domain_sizes[1] = size_2;
95 }
96 
98  double delta, double& q_pa_1,
99  double& q_pa_2) const
100 {
101  q_pa_1 = qx * std::cos(gamma) + qy * std::sin(gamma);
102  q_pa_2 = qx * std::cos(gamma + delta) + qy * std::sin(gamma + delta);
103 }
104 
105 //! Returns interference function for fixed angle xi.
106 double Interference2DParaCrystal::interferenceForXi(double xi, double qx, double qy) const
107 {
108  // don't touch order of computation; problems under Windows
109  double rx = interference1D(qx, qy, xi, 0);
110  double ry = interference1D(qx, qy, xi + m_lattice->latticeAngle(), 1);
111  return rx * ry;
112 }
113 
114 //! Returns interference function for fixed xi in the dimension determined by the given index.
115 double Interference2DParaCrystal::interference1D(double qx, double qy, double xi,
116  size_t index) const
117 {
118  if (index > 1)
119  throw std::runtime_error("Interference2DParaCrystal::"
120  "interference1D() -> Error! Index of interference function "
121  "probability must be < 2");
122  if (!m_pdf1 || !m_pdf2)
123  throw std::runtime_error("Interference2DParaCrystal::"
124  "interference1D() -> Error! Probability distributions for "
125  "interference function not properly initialized");
126 
127  double length = index ? m_lattice->length2() : m_lattice->length1();
128  int n = static_cast<int>(std::abs(m_domain_sizes[index] / length));
129  auto nd = static_cast<double>(n);
130  complex_t fp = FTPDF(qx, qy, xi, index);
131  if (n < 1)
132  return ((1.0 + fp) / (1.0 - fp)).real();
133  if (std::norm(1.0 - fp) < std::numeric_limits<double>::epsilon())
134  return nd;
135  // for (1-fp)*nd small, take the series expansion to second order in nd*(1-fp)
136  if (std::abs(1.0 - fp) * nd < 2e-4) {
137  complex_t intermediate =
138  (nd - 1.0) / 2.0 + (nd * nd - 1.0) * (fp - 1.0) / 6.0
139  + (nd * nd * nd - 2.0 * nd * nd - nd + 2.0) * (fp - 1.0) * (fp - 1.0) / 24.0;
140  return 1.0 + 2.0 * intermediate.real();
141  }
142  complex_t tmp;
143  if (std::abs(fp) == 0.0
144  || std::log(std::abs(fp)) * nd < std::log(std::numeric_limits<double>::min()))
145  tmp = 0.0;
146  else
147  tmp = std::pow(fp, n);
148  complex_t intermediate = fp / (1.0 - fp) - fp * (1.0 - tmp) / nd / (1.0 - fp) / (1.0 - fp);
149  return 1.0 + 2.0 * intermediate.real();
150 }
151 
152 complex_t Interference2DParaCrystal::FTPDF(double qx, double qy, double xi, size_t index) const
153 {
154  double length = (index ? m_lattice->length2() : m_lattice->length1());
155 
156  const IProfile2D* pdf = (index ? m_pdf2.get() : m_pdf1.get());
157  double qa = qx * length * std::cos(xi) + qy * length * std::sin(xi);
158  complex_t phase = exp_I(qa);
159  // transform q to principal axes:
160  double qp1, qp2;
161  double gamma = xi + pdf->gamma();
162  double delta = pdf->delta();
163  transformToPrincipalAxes(qx, qy, gamma, delta, qp1, qp2);
164  double amplitude = pdf->standardizedFT2D(qp1, qp2);
165  complex_t result = phase * amplitude;
166  if (m_damping_length != 0.0)
167  result *= std::exp(-length / m_damping_length);
168  return result;
169 }
170 
171 std::vector<double> Interference2DParaCrystal::domainSizes() const
172 {
173  return {m_domain_sizes[0], m_domain_sizes[1]};
174 }
175 
176 //! Enables/disables averaging over the lattice rotation angle.
177 //! @param integrate_xi: integration flag
178 
180 {
181  m_integrate_xi = integrate_xi;
182  m_lattice->setRotationEnabled(!m_integrate_xi); // deregister Xi in the case of integration
183 }
184 
186 {
187  if (!m_lattice)
188  throw std::runtime_error("Interference2DParaCrystal::lattice() -> Error. "
189  "No lattice defined.");
190  return *m_lattice;
191 }
#define M_TWOPI
Definition: Constants.h:54
Defines classes RealIntegrator, ComplexIntegrator.
Defines class Interference2DParaCrystal.
Defines class RealLimits.
Abstract base class of interference functions.
Definition: IInterference.h:24
double m_position_var
Definition: IInterference.h:53
Interface for two-dimensional distributions in Fourier space.
Definition: Profiles2D.h:29
double delta() const
Angle in direct space between X- and Y-axis of distribution.
Definition: Profiles2D.h:42
IProfile2D * clone() const override=0
double gamma() const
Definition: Profiles2D.h:39
virtual double standardizedFT2D(double qx, double qy) const =0
Fourier transformed distribution for q in X,Y coordinates the original distribution (in real space) i...
Interference function of a 2D paracrystal.
~Interference2DParaCrystal() override
double interferenceForXi(double xi, double qx, double qy) const
Returns interference function for fixed angle xi.
Interference2DParaCrystal * clone() const override
bool m_integrate_xi
Integrate over the orientation xi.
std::unique_ptr< Lattice2D > m_lattice
double iff_without_dw(R3 q) const override
Calculates the structure factor without Debye-Waller factor.
const Lattice2D & lattice() const
double interference1D(double qx, double qy, double xi, size_t index) const
Returns interference function for fixed xi in the dimension determined by the given index.
std::vector< const INode * > nodeChildren() const override
Returns all children.
void setDampingLength(double damping_length)
Sets the damping length.
double m_domain_sizes[2]
Coherence domain sizes.
complex_t FTPDF(double qx, double qy, double xi, size_t index) const
double particleDensity() const override
If defined by this interference function's parameters, returns the particle density (per area)....
void transformToPrincipalAxes(double qx, double qy, double gamma, double delta, double &q_pa_1, double &q_pa_2) const
void setProbabilityDistributions(const IProfile2D &pdf_1, const IProfile2D &pdf_2)
Sets the probability distributions (Fourier transformed) for the two lattice directions.
Interference2DParaCrystal(const Lattice2D &lattice, double damping_length, double domain_size_1, double domain_size_2)
std::unique_ptr< IProfile2D > m_pdf1
std::unique_ptr< IProfile2D > m_pdf2
void setDomainSizes(double size_1, double size_2)
Sets the sizes of coherence domains.
double m_damping_length
Damping length for removing delta function singularity at q=0.
void setIntegrationOverXi(bool integrate_xi)
Enables/disables averaging over the lattice rotation angle.
std::vector< double > domainSizes() const
A two-dimensional Bravais lattice.
Definition: Lattice2D.h:23
Lattice2D * clone() const override=0
To integrate a real function of a real variable.
Definition: IntegratorGK.h:28
double integrate(const std::function< double(double)> &f, double lmin, double lmax)
void check(const std::string &name, double value) const
Throws if value is outside limits. Parameter 'name' is for exception message.
Definition: RealLimits.cpp:170
static RealLimits nonnegative()
Creates an object which can have only positive values with 0. included.
Definition: RealLimits.cpp:124
double gamma(double x)