1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
// ************************************************************************************************
//
// BornAgain: simulate and fit reflection and scattering
//
//! @file Sample/Aggregate/InterferenceRadialParacrystal.cpp
//! @brief Implements class InterferenceRadialParacrystal.
//!
//! @homepage http://www.bornagainproject.org
//! @license GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2018
//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
// ************************************************************************************************
#include "Sample/Aggregate/InterferenceRadialParacrystal.h"
#include "Base/Util/Assert.h"
#include <limits>
//! Constructor of interference function of radial paracrystal.
//! @param peak_distance: average distance to the next neighbor in nanometers
//! @param damping_length: the damping (coherence) length of the paracrystal in nanometers
InterferenceRadialParacrystal::InterferenceRadialParacrystal(double peak_distance,
double damping_length)
: IInterference(0)
, m_peak_distance(peak_distance)
, m_damping_length(damping_length)
, m_kappa(0.0)
, m_domain_size(0.0)
{
validateOrThrow();
}
InterferenceRadialParacrystal* InterferenceRadialParacrystal::clone() const
{
auto* result = new InterferenceRadialParacrystal(m_peak_distance, m_damping_length);
result->setPositionVariance(m_position_var);
if (m_pdf)
result->setProbabilityDistribution(*m_pdf);
result->setKappa(m_kappa);
result->setDomainSize(m_domain_size);
return result;
}
//! Sets size spacing coupling parameter of the Size Spacing Correlation Approximation.
void InterferenceRadialParacrystal::setKappa(double kappa)
{
m_kappa = kappa;
}
//! Sets domain size (finite size corrections).
//! @param size: size of coherence domain along the lattice main axis in nanometers
void InterferenceRadialParacrystal::setDomainSize(double size)
{
m_domain_size = size;
}
complex_t InterferenceRadialParacrystal::FTPDF(double qpar) const
{
complex_t phase = exp_I(qpar * m_peak_distance);
double amplitude = m_pdf->standardizedFT(qpar);
complex_t result = phase * amplitude;
if (m_damping_length)
result *= std::exp(-m_peak_distance / m_damping_length);
return result;
}
//! Sets one-dimensional probability distribution.
//! @param pdf: probability distribution (Fourier transform of probability density)
void InterferenceRadialParacrystal::setProbabilityDistribution(const IProfile1D& pdf)
{
m_pdf.reset(pdf.clone());
}
std::vector<const INode*> InterferenceRadialParacrystal::nodeChildren() const
{
return std::vector<const INode*>() << m_pdf;
}
double InterferenceRadialParacrystal::iff_without_dw(const R3& q) const
{
ASSERT(m_validated);
ASSERT(m_pdf);
double result = 0.0;
double qxr = q.x();
double qyr = q.y();
double qpar = std::sqrt(qxr * qxr + qyr * qyr);
int n = static_cast<int>(std::abs(m_domain_size / m_peak_distance));
auto nd = static_cast<double>(n);
complex_t fp = FTPDF(qpar);
if (n < 1) {
if (std::abs(1.0 - fp) < 10. * std::numeric_limits<double>::epsilon())
result = m_pdf->qSecondDerivative() / m_peak_distance / m_peak_distance;
else
result = ((1.0 + fp) / (1.0 - fp)).real();
} else {
if (std::norm(1.0 - fp) < 10. * std::numeric_limits<double>::epsilon())
result = nd;
// for (1-fp)*nd small, take the series expansion to second order in nd*(1-fp)
else if (std::abs(1.0 - fp) * nd < 2e-4) {
complex_t intermediate =
(nd - 1.0) / 2.0 + (nd * nd - 1.0) * (fp - 1.0) / 6.0
+ (nd * nd * nd - 2.0 * nd * nd - nd + 2.0) * (fp - 1.0) * (fp - 1.0) / 24.0;
result = 1.0 + 2.0 * intermediate.real();
} else {
complex_t tmp;
if (std::abs(fp) == 0.0
|| std::log(std::abs(fp)) * nd < std::log(std::numeric_limits<double>::min())) {
tmp = 0.0;
} else {
tmp = std::pow(fp, n);
}
complex_t intermediate =
fp / (1.0 - fp) - fp * (1.0 - tmp) / nd / (1.0 - fp) / (1.0 - fp);
result = 1.0 + 2.0 * intermediate.real();
}
}
return result;
}
std::string InterferenceRadialParacrystal::validate() const
{
std::vector<std::string> errs;
requestGe0(errs, m_peak_distance, "PeakDistance");
requestGe0(errs, m_damping_length, "DampingLength");
requestGe0(errs, m_kappa, "SizeSpaceCoupling");
requestGe0(errs, m_domain_size, "DomainSize");
if (!errs.empty())
return jointError(errs);
m_validated = true;
return "";
}
|