From 1cd8e2c66fc6c5fb9a5bdaee8e59e0a172889343 Mon Sep 17 00:00:00 2001 From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de> Date: Fri, 14 Aug 2020 10:22:55 +0200 Subject: [PATCH] Ripples: move profile ff computation to namespace ripples --- Core/Correlations/FTDistributions1D.cpp | 2 +- Core/HardParticle/ProfileRipple1.cpp | 25 +---- Core/HardParticle/ProfileRipple1.h | 1 - Core/HardParticle/ProfileRipple2.cpp | 28 +---- Core/HardParticle/ProfileRipple2.h | 2 +- Core/HardParticle/Ripples.cpp | 61 +++++++++++ Core/HardParticle/Ripples.h | 4 + Core/StandardSamples/TwoDimLatticeBuilder.cpp | 3 +- auto/Wrap/libBornAgainCore.py | 8 ++ auto/Wrap/libBornAgainCore_wrap.cpp | 102 ++++++++++++++++++ 10 files changed, 182 insertions(+), 54 deletions(-) diff --git a/Core/Correlations/FTDistributions1D.cpp b/Core/Correlations/FTDistributions1D.cpp index d76afd283b3..bfc4d918a58 100644 --- a/Core/Correlations/FTDistributions1D.cpp +++ b/Core/Correlations/FTDistributions1D.cpp @@ -29,7 +29,7 @@ const double CosineDistributionFactor = 1.0 / 3.0 - 2.0 / M_PI / M_PI; IFTDistribution1D::IFTDistribution1D(const NodeMeta& meta, const std::vector<double>& PValues) : INode(nodeMetaUnion({{"Omega", "nm", "Half-width", 0, INF, 1.}}, meta), PValues), - m_omega(m_P[0]) + m_omega(m_P[0]) { } diff --git a/Core/HardParticle/ProfileRipple1.cpp b/Core/HardParticle/ProfileRipple1.cpp index c2bf9699a23..6255ac8d7ad 100644 --- a/Core/HardParticle/ProfileRipple1.cpp +++ b/Core/HardParticle/ProfileRipple1.cpp @@ -14,8 +14,8 @@ #include "Core/HardParticle/ProfileRipple1.h" #include "Core/Basics/Exceptions.h" -#include "Core/Basics/MathConstants.h" #include "Core/Parametrization/RealParameter.h" +#include "Core/HardParticle/Ripples.h" #include "Core/Shapes/RippleCosine.h" // from Shapes/ #include "Core/Tools/MathFunctions.h" #include "Fit/Tools/RealLimits.h" @@ -41,28 +41,7 @@ double ProfileRipple1::radialExtension() const //! Complex form factor. complex_t ProfileRipple1::factor_yz(complex_t qy, complex_t qz) const { - complex_t factor = m_width / M_PI; - - // analytical expressions for some particular cases - if (qz == 0.) { - if (qy == 0.) - return factor * M_PI_2 * m_height; - complex_t aaa = qy * m_width / (M_TWOPI); - complex_t aaa2 = aaa * aaa; - if (aaa2 == 1.) - return factor * M_PI_4 * m_height; - return factor * M_PI_2 * m_height * MathFunctions::sinc(qy * m_width * 0.5) / (1.0 - aaa2); - } - - // numerical integration otherwise - const complex_t ay = qy * m_width / M_TWOPI; - const complex_t az = complex_t(0, 1) * qz * (m_height / 2); - - const auto integrand = [&](double u) -> complex_t { - return sin(u) * exp(az * std::cos(u)) * (ay == 0. ? u : sin(ay * u) / ay); - }; - complex_t integral = ComplexIntegrator().integrate(integrand, 0, M_PI); - return factor * integral * exp(az) * (m_height / 2); + return ripples::profile_yz_cosine(qy, qz, m_width, m_height); } void ProfileRipple1::onChange() diff --git a/Core/HardParticle/ProfileRipple1.h b/Core/HardParticle/ProfileRipple1.h index c0155a08a0d..e4b00dbdc19 100644 --- a/Core/HardParticle/ProfileRipple1.h +++ b/Core/HardParticle/ProfileRipple1.h @@ -16,7 +16,6 @@ #define BORNAGAIN_CORE_HARDPARTICLE_PROFILERIPPLE1_H #include "Core/Scattering/IFormFactorBorn.h" -#include "Core/Tools/Integrator.h" //! Base class for form factors with a cosine ripple profile in the yz plane. diff --git a/Core/HardParticle/ProfileRipple2.cpp b/Core/HardParticle/ProfileRipple2.cpp index 83c05077f8e..123dc6afa88 100644 --- a/Core/HardParticle/ProfileRipple2.cpp +++ b/Core/HardParticle/ProfileRipple2.cpp @@ -15,6 +15,7 @@ #include "Core/HardParticle/ProfileRipple2.h" #include "Core/Basics/Exceptions.h" #include "Core/Basics/MathConstants.h" +#include "Core/HardParticle/Ripples.h" #include "Core/Parametrization/RealParameter.h" #include "Core/Shapes/RippleSawtooth.h" // from Shapes/ #include "Core/Tools/MathFunctions.h" @@ -42,32 +43,7 @@ double ProfileRipple2::radialExtension() const //! Complex form factor. complex_t ProfileRipple2::factor_yz(complex_t qy, complex_t qz) const { - complex_t result; - const complex_t factor = m_height * m_width; - const complex_t qyW2 = qy * m_width * 0.5; - const complex_t qyd = qy * m_asymmetry; - const complex_t qzH = qz * m_height; - const complex_t a = qzH + qyd; - // dimensionless scale factors - const double a_scale = std::abs(a); - const double w_scale = std::abs(qyW2); - - if (w_scale < 1.e-5) { // |q_y*W| << 1 - if (a_scale < 1e-5) { // |q_y*W| << 1 && |q_z*H + q_y*d| << 1 - // relative error is O((q_y*W)^2) and O((q_z*H + q_y*d)^2) - result = exp_I(-qyd) * (0.5 + mul_I(a) / 6.); - } else { - // relative error is O((q_y*W)^2) - result = exp_I(-qyd) * (1.0 + mul_I(a) - exp_I(a)) / (a * a); - } - } else { - const complex_t gamma_p = (a + qyW2) * 0.5; - const complex_t gamma_m = (a - qyW2) * 0.5; - result = exp_I(gamma_m) * MathFunctions::sinc(gamma_p) - - exp_I(gamma_p) * MathFunctions::sinc(gamma_m); - result = mul_I(exp_I(-qyd) * result / (qyW2 * 2.)); - } - return factor * result; + return ripples::profile_yz_triangular(qy, qz, m_width, m_height, m_asymmetry); } void ProfileRipple2::onChange() diff --git a/Core/HardParticle/ProfileRipple2.h b/Core/HardParticle/ProfileRipple2.h index 718c78dc849..069a9954b0a 100644 --- a/Core/HardParticle/ProfileRipple2.h +++ b/Core/HardParticle/ProfileRipple2.h @@ -18,7 +18,7 @@ #include "Core/Scattering/IFormFactorBorn.h" #include "Core/Tools/Integrator.h" -//! Base class for form factors with a cosine ripple profile in the yz plane. +//! Base class for form factors with a triangular ripple profile in the yz plane. class BA_CORE_API_ ProfileRipple2 : public IFormFactorBorn { diff --git a/Core/HardParticle/Ripples.cpp b/Core/HardParticle/Ripples.cpp index a2d88df4678..cd7dee8f5ab 100644 --- a/Core/HardParticle/Ripples.cpp +++ b/Core/HardParticle/Ripples.cpp @@ -13,6 +13,8 @@ // ************************************************************************** // #include "Core/HardParticle/Ripples.h" +#include "Core/Tools/Integrator.h" +#include "Core/Basics/MathConstants.h" #include "Core/Tools/MathFunctions.h" complex_t ripples::factor_x_box(complex_t q, double r) @@ -29,3 +31,62 @@ complex_t ripples::factor_x_Lorentz(complex_t q, double r) { return r / (1.0 + (q * r) * (q * r)); } + +//! Complex form factor of triangular ripple. +complex_t ripples::profile_yz_cosine(complex_t qy, complex_t qz, double width, double height) +{ + complex_t factor = width / M_PI; + + // analytical expressions for some particular cases + if (qz == 0.) { + if (qy == 0.) + return factor * M_PI_2 * height; + complex_t aaa = qy * width / (M_TWOPI); + complex_t aaa2 = aaa * aaa; + if (aaa2 == 1.) + return factor * M_PI_4 * height; + return factor * M_PI_2 * height * MathFunctions::sinc(qy * width * 0.5) / (1.0 - aaa2); + } + + // numerical integration otherwise + const complex_t ay = qy * width / M_TWOPI; + const complex_t az = complex_t(0, 1) * qz * (height / 2); + + const auto integrand = [&](double u) -> complex_t { + return sin(u) * exp(az * std::cos(u)) * (ay == 0. ? u : sin(ay * u) / ay); + }; + complex_t integral = ComplexIntegrator().integrate(integrand, 0, M_PI); + return factor * integral * exp(az) * (height / 2); +} + +//! Complex form factor of triangular ripple. +complex_t ripples::profile_yz_triangular(complex_t qy, complex_t qz, double width, double height, + double asymmetry) +{ + complex_t result; + const complex_t factor = height * width; + const complex_t qyW2 = qy * width * 0.5; + const complex_t qyd = qy * asymmetry; + const complex_t qzH = qz * height; + const complex_t a = qzH + qyd; + // dimensionless scale factors + const double a_scale = std::abs(a); + const double w_scale = std::abs(qyW2); + + if (w_scale < 1.e-5) { // |q_y*W| << 1 + if (a_scale < 1e-5) { // |q_y*W| << 1 && |q_z*H + q_y*d| << 1 + // relative error is O((q_y*W)^2) and O((q_z*H + q_y*d)^2) + result = exp_I(-qyd) * (0.5 + mul_I(a) / 6.); + } else { + // relative error is O((q_y*W)^2) + result = exp_I(-qyd) * (1.0 + mul_I(a) - exp_I(a)) / (a * a); + } + } else { + const complex_t gamma_p = (a + qyW2) * 0.5; + const complex_t gamma_m = (a - qyW2) * 0.5; + result = exp_I(gamma_m) * MathFunctions::sinc(gamma_p) + - exp_I(gamma_p) * MathFunctions::sinc(gamma_m); + result = mul_I(exp_I(-qyd) * result / (qyW2 * 2.)); + } + return factor * result; +} diff --git a/Core/HardParticle/Ripples.h b/Core/HardParticle/Ripples.h index 92f37c36fbc..e700b70800c 100644 --- a/Core/HardParticle/Ripples.h +++ b/Core/HardParticle/Ripples.h @@ -25,6 +25,10 @@ complex_t factor_x_box(complex_t q, double l); complex_t factor_x_Gauss(complex_t q, double l); complex_t factor_x_Lorentz(complex_t q, double l); +complex_t profile_yz_cosine(complex_t qy, complex_t qz, double width, double height); +complex_t profile_yz_triangular(complex_t qy, complex_t qz, double width, double height, + double asymmetry); + } // namespace ripples #endif // BORNAGAIN_CORE_HARDPARTICLE_RIPPLES_H diff --git a/Core/StandardSamples/TwoDimLatticeBuilder.cpp b/Core/StandardSamples/TwoDimLatticeBuilder.cpp index 06e645d858e..ec77b444569 100644 --- a/Core/StandardSamples/TwoDimLatticeBuilder.cpp +++ b/Core/StandardSamples/TwoDimLatticeBuilder.cpp @@ -155,8 +155,7 @@ MultiLayer* RotatedSquareLatticeBuilder::buildSample() const std::unique_ptr<InterferenceFunction2DLattice> P_interference_function{ InterferenceFunction2DLattice::createSquare(10.0 * Units::nanometer, 30.0 * Units::degree)}; FTDecayFunction2DCauchy pdf(300.0 * Units::nanometer / 2.0 / M_PI, - 100.0 * Units::nanometer / 2.0 / M_PI, - 30.0 * Units::degree); + 100.0 * Units::nanometer / 2.0 / M_PI, 30.0 * Units::degree); P_interference_function->setDecayFunction(pdf); ParticleLayout particle_layout; diff --git a/auto/Wrap/libBornAgainCore.py b/auto/Wrap/libBornAgainCore.py index 46912fc9f6e..761dac428f7 100644 --- a/auto/Wrap/libBornAgainCore.py +++ b/auto/Wrap/libBornAgainCore.py @@ -17715,6 +17715,14 @@ def factor_x_Lorentz(q, l): """ return _libBornAgainCore.factor_x_Lorentz(q, l) + +def profile_yz_cosine(qy, qz, width, height): + r"""profile_yz_cosine(complex_t qy, complex_t qz, double width, double height) -> complex_t""" + return _libBornAgainCore.profile_yz_cosine(qy, qz, width, height) + +def profile_yz_triangular(qy, qz, width, height, asymmetry): + r"""profile_yz_triangular(complex_t qy, complex_t qz, double width, double height, double asymmetry) -> complex_t""" + return _libBornAgainCore.profile_yz_triangular(qy, qz, width, height, asymmetry) class FormFactorGaussSphere(IFormFactorBorn): r"""Proxy of C++ FormFactorGaussSphere class.""" diff --git a/auto/Wrap/libBornAgainCore_wrap.cpp b/auto/Wrap/libBornAgainCore_wrap.cpp index 9a12df0aca5..d2ae11d1204 100644 --- a/auto/Wrap/libBornAgainCore_wrap.cpp +++ b/auto/Wrap/libBornAgainCore_wrap.cpp @@ -99998,6 +99998,106 @@ fail: } +SWIGINTERN PyObject *_wrap_profile_yz_cosine(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + complex_t arg1 ; + complex_t arg2 ; + double arg3 ; + double arg4 ; + std::complex< double > val1 ; + int ecode1 = 0 ; + std::complex< double > val2 ; + int ecode2 = 0 ; + double val3 ; + int ecode3 = 0 ; + double val4 ; + int ecode4 = 0 ; + PyObject *swig_obj[4] ; + complex_t result; + + if (!SWIG_Python_UnpackTuple(args, "profile_yz_cosine", 4, 4, swig_obj)) SWIG_fail; + ecode1 = SWIG_AsVal_std_complex_Sl_double_Sg_(swig_obj[0], &val1); + if (!SWIG_IsOK(ecode1)) { + SWIG_exception_fail(SWIG_ArgError(ecode1), "in method '" "profile_yz_cosine" "', argument " "1"" of type '" "complex_t""'"); + } + arg1 = static_cast< complex_t >(val1); + ecode2 = SWIG_AsVal_std_complex_Sl_double_Sg_(swig_obj[1], &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "profile_yz_cosine" "', argument " "2"" of type '" "complex_t""'"); + } + arg2 = static_cast< complex_t >(val2); + ecode3 = SWIG_AsVal_double(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "profile_yz_cosine" "', argument " "3"" of type '" "double""'"); + } + arg3 = static_cast< double >(val3); + ecode4 = SWIG_AsVal_double(swig_obj[3], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "profile_yz_cosine" "', argument " "4"" of type '" "double""'"); + } + arg4 = static_cast< double >(val4); + result = ripples::profile_yz_cosine(arg1,arg2,arg3,arg4); + resultobj = SWIG_From_std_complex_Sl_double_Sg_(static_cast< std::complex<double> >(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_profile_yz_triangular(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + complex_t arg1 ; + complex_t arg2 ; + double arg3 ; + double arg4 ; + double arg5 ; + std::complex< double > val1 ; + int ecode1 = 0 ; + std::complex< double > val2 ; + int ecode2 = 0 ; + double val3 ; + int ecode3 = 0 ; + double val4 ; + int ecode4 = 0 ; + double val5 ; + int ecode5 = 0 ; + PyObject *swig_obj[5] ; + complex_t result; + + if (!SWIG_Python_UnpackTuple(args, "profile_yz_triangular", 5, 5, swig_obj)) SWIG_fail; + ecode1 = SWIG_AsVal_std_complex_Sl_double_Sg_(swig_obj[0], &val1); + if (!SWIG_IsOK(ecode1)) { + SWIG_exception_fail(SWIG_ArgError(ecode1), "in method '" "profile_yz_triangular" "', argument " "1"" of type '" "complex_t""'"); + } + arg1 = static_cast< complex_t >(val1); + ecode2 = SWIG_AsVal_std_complex_Sl_double_Sg_(swig_obj[1], &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "profile_yz_triangular" "', argument " "2"" of type '" "complex_t""'"); + } + arg2 = static_cast< complex_t >(val2); + ecode3 = SWIG_AsVal_double(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "profile_yz_triangular" "', argument " "3"" of type '" "double""'"); + } + arg3 = static_cast< double >(val3); + ecode4 = SWIG_AsVal_double(swig_obj[3], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "profile_yz_triangular" "', argument " "4"" of type '" "double""'"); + } + arg4 = static_cast< double >(val4); + ecode5 = SWIG_AsVal_double(swig_obj[4], &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "profile_yz_triangular" "', argument " "5"" of type '" "double""'"); + } + arg5 = static_cast< double >(val5); + result = ripples::profile_yz_triangular(arg1,arg2,arg3,arg4,arg5); + resultobj = SWIG_From_std_complex_Sl_double_Sg_(static_cast< std::complex<double> >(result)); + return resultobj; +fail: + return NULL; +} + + SWIGINTERN PyObject *_wrap_new_FormFactorGaussSphere__SWIG_0(PyObject *SWIGUNUSEDPARM(self), Py_ssize_t nobjs, PyObject **swig_obj) { PyObject *resultobj = 0; std::vector< double,std::allocator< double > > arg1 ; @@ -131691,6 +131791,8 @@ static PyMethodDef SwigMethods[] = { "complex_t ripples::factor_x_Lorentz(complex_t q, double l)\n" "\n" ""}, + { "profile_yz_cosine", _wrap_profile_yz_cosine, METH_VARARGS, "profile_yz_cosine(complex_t qy, complex_t qz, double width, double height) -> complex_t"}, + { "profile_yz_triangular", _wrap_profile_yz_triangular, METH_VARARGS, "profile_yz_triangular(complex_t qy, complex_t qz, double width, double height, double asymmetry) -> complex_t"}, { "new_FormFactorGaussSphere", _wrap_new_FormFactorGaussSphere, METH_VARARGS, "\n" "FormFactorGaussSphere(vdouble1d_t P)\n" "new_FormFactorGaussSphere(double mean_radius) -> FormFactorGaussSphere\n" -- GitLab