29 return xinv * (sin(x) * xinv - cos(x));
39 template <
class Tparticle>
T operator()(
T q,
const Tparticle& a)
const 41 assert(q > 0 && a.radius > 0 &&
"Particle radius and q must be positive");
43 qR = 3. / (qR * qR * qR) * (sin(qR) - qR * cos(qR));
53 template <
class Tparticle> [[nodiscard]] constexpr
T operator()(
T,
const Tparticle&)
const 80 if constexpr (requires { scatterer.pos; }) {
97 template <
class Tscatterer> [[nodiscard]]
T operator()(
T,
const Tscatterer& scatterer)
const 99 if (scatterer.id < 0) {
102 assert(static_cast<size_t>(scatterer.id) <
atoms.size());
103 return static_cast<T>(
atoms[scatterer.id].scattering_f0);
122 #pragma GCC diagnostic push 123 #pragma GCC diagnostic ignored "-Wdouble-promotion" 125 template <
class Tformfactor, std::
floating_po
int T =
float>
class DebyeFormula 127 static constexpr
T r_cutoff_infty =
129 T q_mesh_min, q_mesh_max,
136 #pragma omp declare simd uniform(this) linear(m : 1) 138 inline T q_mesh(
int m) {
return q_mesh_min + m * q_mesh_step; }
146 void init_mesh(
T q_min,
T q_max,
T q_step)
148 if (q_step <= 0 || q_min <= 0 || q_max <= 0 || q_min > q_max ||
149 q_step / q_max < 4 * std::numeric_limits<T>::epsilon()) {
150 throw std::range_error(
"DebyeFormula: Invalid mesh parameters for q");
152 q_mesh_min = q_min <
T(1e-6) ? q_step : q_min;
154 q_mesh_step = q_step;
157 const int q_resolution =
numeric_cast<
int>(1.0 + std::floor((q_max - q_min) / q_step));
158 intensity.resize(q_resolution, 0.0);
159 sampling.resize(q_resolution, 0.0);
161 catch (std::overflow_error& e) {
162 throw std::range_error(
"DebyeFormula: Too many samples");
167 Tformfactor form_factor;
168 std::vector<T> intensity;
169 std::vector<T> sampling;
175 init_mesh(q_min, q_max, q_step);
179 :
DebyeFormula(r_cutoff_infty, q_min, q_max, q_step) {};
182 :
DebyeFormula(j.at(
"qmin").get<
double>(), j.at(
"qmax").get<
double>(),
183 j.at(
"dq").get<
double>(), j.value(
"cutoff", r_cutoff_infty)) {};
200 template <
class Tpvec>
void sample(
const Tpvec& p,
const T weight = 1,
const T volume = -1)
202 const int N = (int)p.size();
203 const int M = (int)intensity.size();
204 std::vector<T> intensity_sum(M, 0.0);
209 #pragma omp parallel default(shared) shared(intensity_sum) 211 std::vector<T> intensity_sum_private(M, 0.0);
212 #pragma omp for schedule(dynamic) 213 for (
int i = 0; i < N - 1; ++i) {
214 for (
int j = i + 1; j < N; ++j) {
215 T r =
T(Faunus::Geometry::Sphere::sqdist(
217 if (r < r_cutoff * r_cutoff) {
228 for (
int m = 0; m < M; ++m) {
229 const T q = q_mesh(m);
230 intensity_sum_private[m] += form_factor(q, p[i]) *
231 form_factor(q, p[j]) * std::sin(q * r) /
239 std::transform(intensity_sum.begin(), intensity_sum.end(),
240 intensity_sum_private.begin(), intensity_sum.begin(), std::plus<T>());
246 #pragma omp parallel for shared(sampling, intensity) 247 for (
int m = 0; m < M; ++m) {
248 const T q = q_mesh(m);
249 T intensity_self_sum = 0;
250 for (
int i = 0; i < N; ++i) {
251 intensity_self_sum += std::pow(form_factor(q, p[i]), 2);
253 T intensity_corr = 0;
254 if (r_cutoff < r_cutoff_infty && volume > 0) {
255 intensity_corr = 4 * pc::pi * N / (volume * std::pow(q, 3)) *
256 (q * r_cutoff * std::cos(q * r_cutoff) - std::sin(q * r_cutoff));
258 sampling[m] += weight;
259 if (intensity_self_sum !=
T{0}) {
261 ((2 * intensity_sum[m] + intensity_self_sum) / intensity_self_sum +
278 std::map<T, T> averaged_intensity;
279 for (
size_t m = 0; m < intensity.size(); ++m) {
280 const T average = intensity[m] / (sampling[m] !=
T(0.0) ? sampling[m] :
T(1.0));
281 averaged_intensity.emplace(q_mesh(m), average);
283 return averaged_intensity;
287 #pragma GCC diagnostic pop 303 typedef std::map<T, sampled_value> TSampledValueMap;
306 TSampledValueMap samples;
307 const T precision = 10000.0;
310 std::map<T, T> getSampling()
const 312 std::map<T, T> average;
313 for (
auto [key, sample] : samples) {
314 average.emplace(key, sample.value / sample.weight);
327 std::round(key_approx * precision) / precision;
328 samples[key].value += value * weight;
329 samples[key].weight += weight;
341 template <std::
floating_po
int T>
343 T precision =
T{10000})
350 std::map<T, Accumulator> bins;
351 for (
const auto& [key, value] : pairs) {
352 const T rounded = std::round(key * precision) / precision;
353 bins[rounded].sum += value;
354 bins[rounded].count++;
356 std::map<T, T> result;
357 for (
const auto& [key, acc] : bins) {
358 result[key] = acc.sum /
static_cast<T>(acc.count);
376 template <
class Tformfactor = FormFactorUnity<
double>,
typename T =
double, Algorithm method = SIMD,
377 typename TSamplingPolicy = SamplingPolicy<T>>
381 const std::vector<Point> directions = {
382 {1, 0, 0}, {0, 1, 0}, {0, 0, 1},
383 {1, 1, 0}, {0, 1, 1}, {1, 0, 1}, {-1, 1, 0}, {-1, 0, 1}, {0, -1, 1},
384 {1, 1, 1}, {-1, 1, 1}, {1, -1, 1}, {1, 1, -1}
388 Tformfactor form_factor;
389 using TSamplingPolicy::addSampling;
393 : p_max(q_multiplier)
402 template <
typename Tscatterers>
403 void sample(
const Tscatterers& scatterers,
const Point& boxlength)
405 const auto n = directions.size() *
static_cast<size_t>(p_max);
406 std::vector<std::pair<T, T>> q_intensity(n);
408 #pragma omp parallel for collapse(2) default(shared) 409 for (
size_t i = 0; i < directions.size(); ++i) {
410 for (
int p = 1; p <= p_max; ++p) {
412 2.0 * pc::pi * p * directions[i].cwiseQuotient(boxlength);
413 q_intensity[i *
static_cast<size_t>(p_max) + static_cast<size_t>(p - 1)] =
414 {
static_cast<T>(q.norm()), calculateIntensity(scatterers, q)};
419 addSampling(q, intensity);
423 template <
typename Tscatterers>
424 T calculateIntensity(
const Tscatterers& scatterers,
const Point& q)
const 428 T sum_f_squared = 0.0;
429 const auto q_norm = q.norm();
430 for (
const auto& scatterer : scatterers) {
432 const auto f = form_factor(q_norm, scatterer);
433 const auto qr =
static_cast<T>(q.dot(pos));
434 sum_cos += f * cos(qr);
435 sum_sin += f * sin(qr);
436 sum_f_squared += f * f;
438 if (sum_f_squared ==
T{0}) {
441 return std::norm(std::complex<T>(sum_cos, sum_sin)) / sum_f_squared;
444 int getQMultiplier() {
return p_max; }
446 using TSamplingPolicy::getSampling;
456 template <
class Tformfactor = FormFactorUnity<
float>, std::
floating_po
int T =
float,
457 typename TSamplingPolicy = SamplingPolicy<T>>
462 std::vector<Point> directions = {{1, 0, 0}, {1, 1, 0}, {1, 1, 1}};
465 Tformfactor form_factor;
466 using TSamplingPolicy::addSampling;
470 : p_max(q_multiplier)
474 template <
typename Tscatterers>
475 void sample(
const Tscatterers& scatterers,
const Point& boxlength)
477 const auto n = directions.size() *
static_cast<size_t>(p_max);
478 std::vector<std::pair<T, T>> q_intensity(n);
483 #pragma omp parallel for collapse(2) default(shared) 484 for (
size_t i = 0; i < directions.size(); ++i) {
485 for (
int p = 1; p <= p_max; ++p) {
487 2.0 * pc::pi * p * directions[i].cwiseQuotient(boxlength);
490 const auto q_norm = q.norm();
491 for (
const auto& scatterer : scatterers) {
493 const auto f = form_factor(q_norm, scatterer);
496 T product = std::cos(
T(q[0] * r[0]));
498 product *= std::cos(
T(q[1] * r[1]));
500 product *= std::cos(
T(q[2] * r[2]));
501 sum_f_cos += f * product;
502 sum_f_squared += f * f;
504 const T ipbc_factor =
505 std::pow(2, directions[i].count());
507 if (sum_f_squared !=
T{0}) {
508 intensity = (sum_f_cos * sum_f_cos) / sum_f_squared * ipbc_factor;
510 q_intensity[i *
static_cast<size_t>(p_max) + static_cast<size_t>(p - 1)] =
511 {
static_cast<T>(q_norm), intensity};
516 addSampling(q, intensity);
520 int getQMultiplier() {
return p_max; }
522 using TSamplingPolicy::getSampling;
Calculate scattering intensity using explicit q averaging in isotropic periodic boundary conditions (...
Definition: scatter.h:458
nlohmann::json json
JSON object.
Definition: json_support.h:10
Eigen::Vector3d Point
3D vector used for positions, velocities, forces etc.
Definition: coordinates.h:7
double T
floating point size
Definition: units.h:73
Definition: scatter.h:297
std::vector< Faunus::AtomData > atoms
Global instance of atom list.
Definition: atomdata.cpp:242
A policy for collecting samples.
Definition: scatter.h:294
std::map< T, T > averageByMagnitude(const std::vector< std::pair< T, T >> &pairs, T precision=T{10000})
Average values with equivalent keys (same rounded magnitude).
Definition: scatter.h:342
Cell list class templates.
Definition: actions.cpp:11
void addSampling(T key_approx, T value, T weight=1.0)
Definition: scatter.h:324
TOut numeric_cast(const TIn number)
Convert floating point number to integral number.
Definition: auxiliary.h:30
void sample(const Tscatterers &scatterers, const Point &boxlength)
https://gcc.gnu.org/gcc-9/porting_to.html#ompdatasharing #pragma omp parallel for collapse(2) default...
Definition: scatter.h:403
constexpr const auto & getPosition(const T &scatterer)
Helper to extract position from a scatterer or point.
Definition: scatter.h:78
Lightweight scatterer holding position and atom type id.
Definition: scatter.h:65
Calculate scattering intensity using explicit q averaging.
Definition: scatter.h:378