BornAgain  1.18.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 scattering at grazing incidence
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/Types/Exceptions.h"
17 #include "Base/Utils/Integrator.h"
20 #include <limits>
21 
23  double damping_length,
24  double domain_size_1,
25  double domain_size_2)
26  : IInterferenceFunction(0), m_integrate_xi(false), m_damping_length(damping_length)
27 {
28  setName("Interference2DParaCrystal");
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 
36 //! Constructor of interference function of two-dimensional paracrystal.
37 //! @param length_1: length of first lattice vector in nanometers
38 //! @param length_2: length of second lattice vector in nanometers
39 //! @param alpha: angle between lattice vectors in radians
40 //! @param xi: rotation of lattice with respect to x-axis (beam direction) in radians
41 //! @param damping_length: the damping (coherence) length of the paracrystal in nanometers
42 
44  double length_2, double alpha,
45  double xi,
46  double damping_length)
47  : InterferenceFunction2DParaCrystal(BasicLattice(length_1, length_2, alpha, xi), damping_length,
48  0, 0)
49 {
50 }
51 
53 
55 {
58  ret->setPositionVariance(m_position_var);
59  if (m_pdf1 && m_pdf2)
60  ret->setProbabilityDistributions(*m_pdf1, *m_pdf2);
61  ret->setIntegrationOverXi(m_integrate_xi);
62  return ret;
63 }
64 
65 //! Sets the probability distributions (Fourier transformed) for the two lattice directions.
66 //! @param pdf_1: probability distribution in first lattice direction
67 //! @param pdf_2: probability distribution in second lattice direction
68 
70  const IFTDistribution2D& pdf_2)
71 {
72  m_pdf1.reset(pdf_1.clone());
73  registerChild(m_pdf1.get());
74  m_pdf2.reset(pdf_2.clone());
75  registerChild(m_pdf2.get());
76 }
77 
78 //! Sets the damping length.
79 //! @param damping_length: the damping (coherence) length of the paracrystal in nanometers
80 
82 {
83  m_damping_length = damping_length;
84 }
85 
87 {
88  double area = m_lattice->unitCellArea();
89  return area == 0.0 ? 0.0 : 1.0 / area;
90 }
91 
92 std::vector<const INode*> InterferenceFunction2DParaCrystal::getChildren() const
93 {
94  return std::vector<const INode*>() << m_pdf1 << m_pdf2 << m_lattice;
95 }
96 
98 {
99  m_qx = q.x();
100  m_qy = q.y();
101  if (!m_integrate_xi)
102  return interferenceForXi(m_lattice->rotationAngle());
103  return RealIntegrator().integrate([&](double xi) -> double { return interferenceForXi(xi); },
104  0.0, M_TWOPI)
105  / M_TWOPI;
106 }
107 
109 {
110  m_lattice.reset(lattice.clone());
111  registerChild(m_lattice.get());
112 }
113 
114 //! Creates square lattice.
115 //! @param lattice_length: length of first and second lattice vectors in nanometers
116 //! @param damping_length: the damping (coherence) length of the paracrystal in nanometers
117 //! @param domain_size_1: size of the coherent domain along the first basis vector in nanometers
118 //! @param domain_size_2: size of the coherent domain along the second basis vector in nanometers
119 
121 InterferenceFunction2DParaCrystal::createSquare(double lattice_length, double damping_length,
122  double domain_size_1, double domain_size_2)
123 {
124  auto result = new InterferenceFunction2DParaCrystal(
125  SquareLattice(lattice_length), damping_length, domain_size_1, domain_size_2);
126  result->setIntegrationOverXi(true);
127  return result;
128 }
129 
130 //! Creates hexagonal lattice.
131 //! @param lattice_length: length of first and second lattice vectors in nanometers
132 //! @param damping_length: the damping (coherence) length of the paracrystal in nanometers
133 //! @param domain_size_1: size of the coherent domain along the first basis vector in nanometers
134 //! @param domain_size_2: size of the coherent domain along the second basis vector in nanometers
135 
137 InterferenceFunction2DParaCrystal::createHexagonal(double lattice_length, double damping_length,
138  double domain_size_1, double domain_size_2)
139 {
140  auto result = new InterferenceFunction2DParaCrystal(
141  HexagonalLattice(lattice_length, 0.), damping_length, domain_size_1, domain_size_2);
142  result->setIntegrationOverXi(true);
143  return result;
144 }
145 
146 //! Sets the sizes of coherence domains.
147 //! @param size_1: coherence domain size along the first basis vector in nanometers
148 //! @param size_2: coherence domain size along the second basis vector in nanometers
149 
150 void InterferenceFunction2DParaCrystal::setDomainSizes(double size_1, double size_2)
151 {
152  m_domain_sizes[0] = size_1;
153  m_domain_sizes[1] = size_2;
154 }
155 
156 void InterferenceFunction2DParaCrystal::transformToPrincipalAxes(double qx, double qy, double gamma,
157  double delta, double& q_pa_1,
158  double& q_pa_2) const
159 {
160  q_pa_1 = qx * std::cos(gamma) + qy * std::sin(gamma);
161  q_pa_2 = qx * std::cos(gamma + delta) + qy * std::sin(gamma + delta);
162 }
163 
164 //! Returns interference function for fixed angle xi.
166 {
167  // don't touch order of computation; problems under Windows
168  double rx = interference1D(m_qx, m_qy, xi, 0);
169  double ry = interference1D(m_qx, m_qy, xi + m_lattice->latticeAngle(), 1);
170  return rx * ry;
171 }
172 
173 //! Returns interference function for fixed xi in the dimension determined by the given index.
174 double InterferenceFunction2DParaCrystal::interference1D(double qx, double qy, double xi,
175  size_t index) const
176 {
177  if (index > 1)
179  "InterferenceFunction2DParaCrystal::"
180  "interference1D() -> Error! Index of interference function "
181  "probability must be < 2");
182  if (!m_pdf1 || !m_pdf2)
184  "InterferenceFunction2DParaCrystal::"
185  "interference1D() -> Error! Probability distributions for "
186  "interference function not properly initialized");
187 
188  double length = index ? m_lattice->length2() : m_lattice->length1();
189  int n = static_cast<int>(std::abs(m_domain_sizes[index] / length));
190  double nd = static_cast<double>(n);
191  complex_t fp = FTPDF(qx, qy, xi, index);
192  if (n < 1)
193  return ((1.0 + fp) / (1.0 - fp)).real();
194  if (std::norm(1.0 - fp) < std::numeric_limits<double>::epsilon())
195  return nd;
196  // for (1-fp)*nd small, take the series expansion to second order in nd*(1-fp)
197  if (std::abs(1.0 - fp) * nd < 2e-4) {
198  complex_t intermediate =
199  (nd - 1.0) / 2.0 + (nd * nd - 1.0) * (fp - 1.0) / 6.0
200  + (nd * nd * nd - 2.0 * nd * nd - nd + 2.0) * (fp - 1.0) * (fp - 1.0) / 24.0;
201  return 1.0 + 2.0 * intermediate.real();
202  }
203  complex_t tmp;
204  if (std::abs(fp) == 0.0
205  || std::log(std::abs(fp)) * nd < std::log(std::numeric_limits<double>::min()))
206  tmp = 0.0;
207  else
208  tmp = std::pow(fp, n);
209  complex_t intermediate = fp / (1.0 - fp) - fp * (1.0 - tmp) / nd / (1.0 - fp) / (1.0 - fp);
210  return 1.0 + 2.0 * intermediate.real();
211 }
212 
213 complex_t InterferenceFunction2DParaCrystal::FTPDF(double qx, double qy, double xi,
214  size_t index) const
215 {
216  double length = (index ? m_lattice->length2() : m_lattice->length1());
217 
218  const IFTDistribution2D* pdf = (index ? m_pdf2.get() : m_pdf1.get());
219  double qa = qx * length * std::cos(xi) + qy * length * std::sin(xi);
220  complex_t phase = exp_I(qa);
221  // transform q to principal axes:
222  double qp1, qp2;
223  double gamma = xi + pdf->gamma();
224  double delta = pdf->delta();
225  transformToPrincipalAxes(qx, qy, gamma, delta, qp1, qp2);
226  double amplitude = pdf->evaluate(qp1, qp2);
227  complex_t result = phase * amplitude;
228  if (m_damping_length != 0.0)
229  result *= std::exp(-length / m_damping_length);
230  return result;
231 }
232 
234 {
235  return {m_domain_sizes[0], m_domain_sizes[1]};
236 }
237 
238 //! Enables/disables averaging over the lattice rotation angle.
239 //! @param integrate_xi: integration flag
240 
242 {
243  m_integrate_xi = integrate_xi;
244  m_lattice->setRotationEnabled(!m_integrate_xi); // deregister Xi in the case of integration
245 }
246 
248 {
249  if (!m_lattice)
250  throw std::runtime_error("InterferenceFunction2DParaCrystal::lattice() -> Error. "
251  "No lattice defined.");
252  return *m_lattice;
253 }
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 many exception classes in namespace Exceptionss.
Defines classes RealIntegrator, ComplexIntegrator.
Defines class InterferenceFunction2DParaCrystal.
#define M_TWOPI
Definition: MathConstants.h:49
Defines class ParameterPool.
Defines class RealParameter.
T y() const
Returns y-component in cartesian coordinate system.
Definition: BasicVector3D.h:66
T x() const
Returns x-component in cartesian coordinate system.
Definition: BasicVector3D.h:64
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
Pure virtual base class of interference functions.
void registerChild(INode *node)
Definition: INode.cpp:58
RealParameter & registerParameter(const std::string &name, double *parpointer)
void setName(const std::string &name)
Interference function of a 2D paracrystal.
double getParticleDensity() const override final
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)
static InterferenceFunction2DParaCrystal * createHexagonal(double lattice_length, double damping_length, double domain_size_1, double domain_size_2)
Creates hexagonal lattice.
double m_domain_sizes[2]
Coherence domain sizes.
double iff_without_dw(const kvector_t q) const override final
Calculates the structure factor without Debye-Waller factor.
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.
InterferenceFunction2DParaCrystal * clone() const override final
Returns a clone of this ISample object.
void setDampingLength(double damping_length)
Sets the damping length.
std::unique_ptr< IFTDistribution2D > m_pdf2
static InterferenceFunction2DParaCrystal * createSquare(double lattice_length, double damping_length, double domain_size_1, double domain_size_2)
Creates square lattice.
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.
std::vector< const INode * > getChildren() const override final
Returns a vector of children (const).
void transformToPrincipalAxes(double qx, double qy, double gamma, double delta, double &q_pa_1, double &q_pa_2) 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.
virtual Lattice2D * clone() const =0
To integrate a real function of a real variable.
Definition: Integrator.h:24
double integrate(const std::function< double(double)> &f, double lmin, double lmax)
Definition: Integrator.cpp:27
RealParameter & setNonnegative()
RealParameter & setUnit(const std::string &name)