BornAgain  1.19.79
Open-source research software to simulate and fit neutron and x-ray reflectometry and grazing-incidence small-angle scattering
Interference2DLattice.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file Sample/Aggregate/Interference2DLattice.cpp
6 //! @brief Implements class Interference2DLattice.
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 <algorithm>
20 
21 namespace {
22 
23 // maximum value for qx*Lambdax and qy*lambday
24 const int nmax = 20;
25 // minimum number of neighboring reciprocal lattice points to use
26 const int min_points = 4;
27 
28 std::pair<double, double> transformToRecLatticeCoordinates(double qX, double qY, double a, double b,
29  double alpha, double gamma)
30 {
31  double qa = (a * qX * std::cos(gamma) - a * qY * std::sin(gamma)) / M_TWOPI;
32  double qb = (b * qX * std::cos(alpha - gamma) + b * qY * std::sin(alpha - gamma)) / M_TWOPI;
33  return {qa, qb};
34 }
35 
36 //! Calculates bounding values of reciprocal lattice coordinates that contain the centered
37 //! rectangle with a corner defined by qX and qY
38 std::pair<double, double> boundingReciprocalLatticeCoordinates(double qX, double qY, double a,
39  double b, double alpha, double gamma)
40 {
41  auto q_bounds_1 = transformToRecLatticeCoordinates(qX, qY, a, b, alpha, gamma);
42  auto q_bounds_2 = transformToRecLatticeCoordinates(qX, -qY, a, b, alpha, gamma);
43  double qa_max = std::max(std::abs(q_bounds_1.first), std::abs(q_bounds_2.first));
44  double qb_max = std::max(std::abs(q_bounds_1.second), std::abs(q_bounds_2.second));
45  return {qa_max, qb_max};
46 }
47 
48 } // namespace
49 
51  : IInterference(0)
52  , m_integrate_xi(false)
53 {
54  m_lattice.reset(lattice.clone());
55  if (!m_lattice)
56  throw std::runtime_error("Interference2DLattice::initialize_rec_vectors() -> "
57  "Error. No lattice defined yet");
58 
59  BasicLattice2D base_lattice(m_lattice->length1(), m_lattice->length2(),
60  m_lattice->latticeAngle(), 0.);
61  m_sbase = base_lattice.reciprocalBases();
62 }
63 
65 
67 {
68  auto* result = new Interference2DLattice(*m_lattice);
69  result->setPositionVariance(m_position_var);
70  result->setIntegrationOverXi(integrationOverXi());
71  if (m_decay)
72  result->setDecayFunction(*m_decay);
73  return result;
74 }
75 
76 //! Sets two-dimensional decay function.
77 //! @param decay: two-dimensional decay function in reciprocal space
79 {
80  m_decay.reset(decay.clone());
81  if (!m_decay)
82  throw std::runtime_error("Interference2DLattice::initialize_calc_factors"
83  " -> Error! No decay function defined.");
84 
85  // number of reciprocal lattice points to use
86  auto q_bounds = boundingReciprocalLatticeCoordinates(
87  nmax / m_decay->decayLengthX(), nmax / m_decay->decayLengthY(), m_lattice->length1(),
88  m_lattice->length2(), m_lattice->latticeAngle(), m_decay->gamma());
89  m_na = static_cast<int>(std::lround(q_bounds.first + 0.5));
90  m_nb = static_cast<int>(std::lround(q_bounds.second + 0.5));
91  m_na = std::max(m_na, min_points);
92  m_nb = std::max(m_nb, min_points);
93 }
94 
96 {
97  m_integrate_xi = integrate_xi;
98  m_lattice->setRotationEnabled(!m_integrate_xi); // deregister Xi in the case of integration
99 }
100 
102 {
103  if (!m_lattice)
104  throw std::runtime_error("Interference2DLattice::lattice() -> Error. "
105  "No lattice defined.");
106  return *m_lattice;
107 }
108 
110 {
111  double area = m_lattice->unitCellArea();
112  return area == 0.0 ? 0.0 : 1.0 / area;
113 }
114 
115 std::vector<const INode*> Interference2DLattice::nodeChildren() const
116 {
117  return std::vector<const INode*>() << m_decay << m_lattice;
118 }
119 
121 {
122  if (!m_decay)
123  throw std::runtime_error("Interference2DLattice::evaluate"
124  " -> Error! No decay function defined.");
125  if (!m_integrate_xi)
126  return interferenceForXi(m_lattice->rotationAngle(), q.x(), q.y());
127  return RealIntegrator().integrate(
128  [&](double xi) -> double { return interferenceForXi(xi, q.x(), q.y()); }, 0.0,
129  M_TWOPI)
130  / M_TWOPI;
131 }
132 
133 double Interference2DLattice::interferenceForXi(double xi, double qx, double qy) const
134 {
135  double result = 0.0;
136  auto q_frac = calculateReciprocalVectorFraction(qx, qy, xi);
137 
138  for (int i = -m_na - 1; i < m_na + 2; ++i) {
139  for (int j = -m_nb - 1; j < m_nb + 2; ++j) {
140  const double px = q_frac.first + i * m_sbase.m_asx + j * m_sbase.m_bsx;
141  const double py = q_frac.second + i * m_sbase.m_asy + j * m_sbase.m_bsy;
142  result += interferenceAtOneRecLatticePoint(px, py);
143  }
144  }
145  return particleDensity() * result;
146 }
147 
149 {
150  if (!m_decay)
151  throw std::runtime_error("Interference2DLattice::interferenceAtOneRecLatticePoint"
152  " -> Error! No decay function defined.");
153  double gamma = m_decay->gamma();
154  auto qXY = rotateOrthonormal(qx, qy, gamma);
155  return m_decay->decayFT2D(qXY.first, qXY.second);
156 }
157 
158 // Rotate by angle gamma between orthonormal systems
159 std::pair<double, double> Interference2DLattice::rotateOrthonormal(double qx, double qy,
160  double gamma) const
161 {
162  double q_X = qx * std::cos(gamma) + qy * std::sin(gamma);
163  double q_Y = -qx * std::sin(gamma) + qy * std::cos(gamma);
164  return {q_X, q_Y};
165 }
166 
167 // (qx, qy) are in the global reciprocal reference frame
168 // the returned values (qx_frac, qy_frac) are in the rotated frame with first lattice basis
169 // vector aligned with the real-space x-axis (same frame as the one stored in m_sbase)
170 std::pair<double, double>
171 Interference2DLattice::calculateReciprocalVectorFraction(double qx, double qy, double xi) const
172 {
173  double a = m_lattice->length1();
174  double b = m_lattice->length2();
175  double alpha = m_lattice->latticeAngle();
176  // first rotate the input to the system of m_sbase:
177  double qx_rot = qx * std::cos(xi) + qy * std::sin(xi);
178  double qy_rot = -qx * std::sin(xi) + qy * std::cos(xi);
179 
180  // find the reciprocal lattice coordinates of (qx_rot, qy_rot):
181  int qa_int = static_cast<int>(std::lround(a * qx_rot / M_TWOPI));
182  int qb_int = static_cast<int>(
183  std::lround(b * (qx_rot * std::cos(alpha) + qy_rot * std::sin(alpha)) / M_TWOPI));
184  // take the fractional part only (in m_sbase coordinates)
185  double qx_frac = qx_rot - qa_int * m_sbase.m_asx - qb_int * m_sbase.m_bsx;
186  double qy_frac = qy_rot - qa_int * m_sbase.m_asy - qb_int * m_sbase.m_bsy;
187  return {qx_frac, qy_frac};
188 }
#define M_TWOPI
Definition: Constants.h:54
Defines classes RealIntegrator, ComplexIntegrator.
Defines class Interference2DLattice.
Defines interface class IProfile1D, and children thereof.
Defines interface class IProfile2D, and children thereof.
A two-dimensional Bravais lattice with no special symmetry.
Definition: Lattice2D.h:51
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
IProfile2D * clone() const override=0
Interference function of a 2D lattice.
std::unique_ptr< Lattice2D > m_lattice
bool m_integrate_xi
Integrate over the orientation xi.
Interference2DLattice(const Lattice2D &lattice)
void setDecayFunction(const IProfile2D &decay)
Sets two-dimensional decay function.
std::pair< double, double > rotateOrthonormal(double qx, double qy, double gamma) const
Returns reciprocal coordinates in the coordinate system rotated by the angle gamma.
Interference2DLattice * clone() const override
const Lattice2D & lattice() const
std::vector< const INode * > nodeChildren() const override
Returns all children.
void setIntegrationOverXi(bool integrate_xi)
double iff_without_dw(R3 q) const override
Calculates the structure factor without Debye-Waller factor.
double interferenceForXi(double xi, double qx, double qy) const
Lattice2D::ReciprocalBases m_sbase
reciprocal lattice is stored without xi
std::pair< double, double > calculateReciprocalVectorFraction(double qx, double qy, double xi) const
Returns qx,qy coordinates of q - qint, where qint is a reciprocal lattice vector bounding the recipro...
std::unique_ptr< IProfile2D > m_decay
int m_nb
determines the number of reciprocal lattice points to use
double interferenceAtOneRecLatticePoint(double qx, double qy) const
Returns interference from a single reciprocal lattice vector.
~Interference2DLattice() override
double particleDensity() const override
Returns the particle density associated with this 2d lattice.
A two-dimensional Bravais lattice.
Definition: Lattice2D.h:23
ReciprocalBases reciprocalBases() const
Definition: Lattice2D.cpp:34
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)
double gamma(double x)
double m_asy
x,y coordinates of a*
Definition: Lattice2D.h:30
double m_bsy
x,y coordinates of b*
Definition: Lattice2D.h:31