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