Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Static structure factor S(q) #660

Merged
merged 106 commits into from Sep 20, 2021
Merged
Show file tree
Hide file tree
Changes from 90 commits
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
0f23f16
Add static structure factor calculation (direct method). Not yet vali…
bdice Sep 4, 2020
710c387
Add sinc function.
bdice Sep 29, 2020
1542860
Add C++ implementation (untested, likely not correct yet).
bdice Sep 29, 2020
38e8e6d
Add C++ implementation.
bdice Sep 29, 2020
42eb797
Use normalization as private variable.
bdice Sep 30, 2020
3ec7b9e
Use util function for integration, fix integral domain.
bdice Sep 30, 2020
d177b26
Mark Histogram methods as const.
bdice Sep 30, 2020
0ea3c91
Use typename instead of class.
bdice Sep 30, 2020
aba583f
Add C++ direct method.
bdice Oct 1, 2020
0b3b34d
Use odd number of bins for Simpson's rule integration.
bdice Oct 1, 2020
8933c43
Intermediate cleanup.
bdice Oct 2, 2020
1e7bb7d
Intermediate work on reset/reduce.
bdice Oct 2, 2020
c356caa
Fixes to BondHistogramCompute.h
bdice Oct 2, 2020
59b1ece
Remove direct method from Python code since C++ version is complete.
bdice Oct 2, 2020
fcafd18
Update docs for StaticStructureFactor.
bdice Oct 2, 2020
0ac3cea
Skip invalid tests.
bdice Oct 2, 2020
251afd2
Add pure Python implementation to tests for validation.
bdice Oct 2, 2020
281452d
Fix ball query to exclude neighbors.
bdice Oct 3, 2020
a746545
Use direct method by default.
bdice Oct 5, 2020
0ede3aa
Cleanup changes from scikit-build.
bdice Nov 6, 2020
950640d
Fix cmake formatting of diffraction module.
bdice Nov 6, 2020
37a446b
Merge remote-tracking branch 'origin/master' into feature/structure-f…
bdice May 6, 2021
c3f9102
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 6, 2021
645e2ba
Merge branch 'master' into feature/structure-factor
bdice May 9, 2021
5089230
updated static structure factor documentation so that formula can be …
DomFijan May 25, 2021
0eb5c59
Changed documentation so that sinc definition is easier to see with a…
DomFijan May 25, 2021
0f059ff
Merge remote-tracking branch 'origin/master' into feature/structure-f…
bdice Jul 2, 2021
e16fca1
Use NeighborList with cutoff instead of all distances to avoid potent…
bdice Jul 2, 2021
51e8011
removed unsued import, still doesn't build
DomFijan Jul 2, 2021
1a84c02
typos
DomFijan Jul 5, 2021
dbbc70e
updated documentation, added support for partial cross-term structure…
emilybwsiew Jul 6, 2021
35d6969
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 6, 2021
2f5bdf9
style
DomFijan Jul 15, 2021
0974a53
Merge branch 'feature/structure-factor' of https://github.com/glotzer…
DomFijan Jul 15, 2021
da8a49b
Clean up docs / C++ bits.
bdice Aug 9, 2021
f96ba01
Include i-i bonds so that S(q) converges to 1.
bdice Aug 9, 2021
1c32443
Add docs for partial structure factor.
bdice Aug 9, 2021
1f551c4
Add n_total to C++.
bdice Aug 9, 2021
c7ccecd
Add n_total to pxd.
bdice Aug 9, 2021
30a6062
Merge remote-tracking branch 'origin/master' into feature/structure-f…
bdice Aug 9, 2021
3246842
Add N_total, fix tests.
bdice Aug 9, 2021
486fea8
Add tests for StaticStructureFactorDebye limiting cases and normaliza…
bdice Aug 9, 2021
96db0ab
Initialize members.
bdice Aug 10, 2021
f9bb946
Improve C++ style.
bdice Aug 10, 2021
996e07c
Fix class name in docs.
bdice Aug 10, 2021
37e1270
Fix tests to use better pytest style.
bdice Aug 10, 2021
5cff663
Add in missing prepare call and set tolerance for symmetry test appro…
vyasr Aug 12, 2021
3300e04
Merge branch 'master' into feature/structure-factor
vyasr Aug 12, 2021
5001436
Fix partial sum test.
bdice Aug 12, 2021
538ecf3
Clean up unused variables in Debye implementation.
bdice Aug 26, 2021
3ae18b4
Fix C++ style.
bdice Aug 26, 2021
a66d165
Add xfail test for S(0).
bdice Aug 26, 2021
3b24a87
Use lowercase raw strings.
bdice Aug 26, 2021
7d489f8
Set PBC to False for box passed to compute
DomFijan Sep 1, 2021
2c87d99
updated validation function to also not include PBC so that tests pass
DomFijan Sep 1, 2021
03dd664
reversed PBC = False; changed ball query to compute all distances and…
DomFijan Sep 3, 2021
52c2ab1
updated docs
DomFijan Sep 3, 2021
01ca1dc
added const to a variable
DomFijan Sep 3, 2021
766e650
Merge remote-tracking branch 'origin/master' into feature/structure-f…
DomFijan Sep 4, 2021
f055ca8
added test for validation against ASE
DomFijan Sep 8, 2021
e4797aa
added ase to test requirements in both setup.py and requirements/
DomFijan Sep 8, 2021
9e2841d
added missing =
DomFijan Sep 8, 2021
f15a5c4
python 3.6 CI testing takes requiremntes from circleci/ci-oldest-reqs…
DomFijan Sep 8, 2021
9e1aa01
changed required ase version to 3.21.1 to resolve the conflict in num…
DomFijan Sep 8, 2021
8a962b8
changed the name of histogram from StaticStructureFactorDebyeHistogra…
DomFijan Sep 10, 2021
0b3aeba
changed some auto assignments with concrete types
DomFijan Sep 10, 2021
4c36d6d
Update cpp/diffraction/StaticStructureFactorDebye.cc
DomFijan Sep 10, 2021
52f6b6e
Merge branch 'master' into feature/structure-factor
bdice Sep 12, 2021
a70731a
got rid of deconstructor
DomFijan Sep 13, 2021
8479aa3
Revert "changed some auto assignments with concrete types"
DomFijan Sep 13, 2021
7539d98
Throw exception if k_min<0
DomFijan Sep 13, 2021
a0ac897
consistency with #837 in this vs &
DomFijan Sep 13, 2021
3ae900c
added pytest optional skip for ase
DomFijan Sep 13, 2021
022b2c2
changed ase test to use calc_pattern instead of calculating manually
DomFijan Sep 13, 2021
c9a9a2f
Changed some of const assignments
DomFijan Sep 13, 2021
08be47b
changed east const to west const
DomFijan Sep 13, 2021
ab0ecd2
update the docs
DomFijan Sep 13, 2021
c273ff7
more docs updates
DomFijan Sep 13, 2021
0c680d4
plot function now obeys min_valid_k
DomFijan Sep 14, 2021
5680bdd
add Farrow2009 citation
andkerr Sep 17, 2021
8a2a6d3
update S(k) Debye docs
andkerr Sep 17, 2021
6990e72
fixed doc math issue for minvalidk
DomFijan Sep 17, 2021
66350e6
changed docs, this looks better
DomFijan Sep 17, 2021
73cbe69
freud/diffraction.pyx
DomFijan Sep 17, 2021
f0236de
fixed querypoints docs and system docs in compute
DomFijan Sep 17, 2021
1a7e81e
missing space in docs for :class:
DomFijan Sep 17, 2021
2f56ced
clang tidy - changed for loop to be range based
DomFijan Sep 18, 2021
100c3f1
Apply clang-tidy suggestion.
bdice Sep 19, 2021
2717c13
Test oldest requirements against ase 3.16.0.
bdice Sep 19, 2021
f871c79
Improve constness, references, and variable names.
bdice Sep 19, 2021
ade41ce
Revert NeighborQuery constructor and make_ball.
bdice Sep 19, 2021
c1e2ea7
Revert changes to Histogram constness (the methods return non-referen…
bdice Sep 19, 2021
e5230c0
Remove unused Simpson's integration.
bdice Sep 19, 2021
207846a
Remove unused imports.
bdice Sep 19, 2021
05c7aa7
Improve bib style.
bdice Sep 19, 2021
b4205b5
Merge branch 'master' into feature/structure-factor
bdice Sep 19, 2021
35af69b
Apply fixes to Debye from Direct implementation branch.
bdice Sep 20, 2021
c535179
Update changelog and credits.
bdice Sep 20, 2021
f3ccf0c
Fix documentation.
bdice Sep 20, 2021
780f24b
Remove unused includes.
bdice Sep 20, 2021
48d8f72
Refactor min_valid_k.
bdice Sep 20, 2021
99dec5a
clang-format
bdice Sep 20, 2021
b3d2bc5
Update C++ docs.
bdice Sep 20, 2021
2895702
Documentation revisions.
bdice Sep 20, 2021
4ebe6b5
Pin to newest ase==3.22.0.
bdice Sep 20, 2021
bf2ed8f
Update tests_require.
bdice Sep 20, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .circleci/ci-oldest-reqs.txt
@@ -1,3 +1,4 @@
ase==3.16.0
cmake==3.6.3.post1
cython==0.29.14
garnett==0.7.1
Expand Down
2 changes: 2 additions & 0 deletions cpp/CMakeLists.txt
Expand Up @@ -8,6 +8,7 @@ endif()

add_subdirectory(cluster)
add_subdirectory(density)
add_subdirectory(diffraction)
add_subdirectory(environment)
add_subdirectory(locality)
add_subdirectory(order)
Expand All @@ -19,6 +20,7 @@ add_library(
libfreud SHARED
$<TARGET_OBJECTS:_cluster>
$<TARGET_OBJECTS:_density>
$<TARGET_OBJECTS:_diffraction>
$<TARGET_OBJECTS:_environment>
$<TARGET_OBJECTS:_locality>
$<TARGET_OBJECTS:_order>
Expand Down
5 changes: 5 additions & 0 deletions cpp/diffraction/CMakeLists.txt
@@ -0,0 +1,5 @@
add_library(_diffraction OBJECT StaticStructureFactorDebye.h
StaticStructureFactorDebye.cc)

target_include_directories(_diffraction
PUBLIC ${PROJECT_SOURCE_DIR}/cpp/density)
108 changes: 108 additions & 0 deletions cpp/diffraction/StaticStructureFactorDebye.cc
@@ -0,0 +1,108 @@
// Copyright (c) 2010-2020 The Regents of the University of Michigan
// This file is from the freud project, released under the BSD 3-Clause License.

#include <algorithm>
#include <cmath>
#include <limits>
#include <stdexcept>

#include "Box.h"
#include "ManagedArray.h"
#include "NeighborComputeFunctional.h"
#include "NeighborQuery.h"
#include "RDF.h"
#include "StaticStructureFactorDebye.h"
#include "utils.h"

/*! \file StaticStructureFactorDebye.cc
\brief Routines for computing static structure factors.
*/

namespace freud { namespace diffraction {

StaticStructureFactorDebye::StaticStructureFactorDebye(unsigned int bins, float k_max, float k_min)
{
if (bins == 0)
{
throw std::invalid_argument("StaticStructureFactorDebye requires a nonzero number of bins.");
}
if (k_max <= 0)
{
throw std::invalid_argument("StaticStructureFactorDebye requires k_max to be positive.");
}
DomFijan marked this conversation as resolved.
Show resolved Hide resolved
if (k_min < 0)
{
throw std::invalid_argument("StaticStructureFactorDebye requires k_min to be non-negative.");
}
if (k_max <= k_min)
{
throw std::invalid_argument(
"StaticStructureFactorDebye requires that k_max must be greater than k_min.");
}

// Construct the Histogram object that will be used to track the structure factor
const auto axes = S_kHistogram::Axes {std::make_shared<util::RegularAxis>(bins, k_min, k_max)};
m_histogram = S_kHistogram(axes);
m_local_histograms = S_kHistogram::ThreadLocalHistogram(m_histogram);
m_min_valid_k = std::numeric_limits<float>::infinity();
m_structure_factor.prepare(bins);
}

void StaticStructureFactorDebye::accumulate(const freud::locality::NeighborQuery* neighbor_query,
bdice marked this conversation as resolved.
Show resolved Hide resolved
const vec3<float>* query_points, unsigned int n_query_points,
unsigned int n_total)
{
const auto& box = neighbor_query->getBox();

// The r_max should be just less than half of the smallest side length of the box
const auto box_L = box.getL();
const auto min_box_length
= box.is2D() ? std::min(box_L.x, box_L.y) : std::min(box_L.x, std::min(box_L.y, box_L.z));
const auto r_max = std::nextafter(float(0.5) * min_box_length, float(0));
const auto* const points = neighbor_query->getPoints();
const auto n_points = neighbor_query->getNPoints();

// The minimum k value of validity is 4 * pi / L, where L is the smallest side length.
// This is equal to 2 * pi / r_max.
m_min_valid_k = std::min(m_min_valid_k, freud::constants::TWO_PI / r_max);

std::vector<float> distances(n_points * n_query_points);
box.computeAllDistances(points, n_points, query_points, n_query_points, distances.data());

const auto k_bin_centers = m_histogram.getBinCenters()[0];

util::forLoopWrapper(0, m_histogram.getAxisSizes()[0], [&](size_t begin, size_t end) {
for (size_t k_index = begin; k_index < end; ++k_index)
{
const auto k = k_bin_centers[k_index];
double S_k = 0.0;
bdice marked this conversation as resolved.
Show resolved Hide resolved
for (const auto& distance : distances)
{
S_k += util::sinc(k * distance);
}
S_k /= static_cast<double>(n_total);
m_local_histograms.increment(k_index, S_k);
};
});
m_frame_counter++;
m_reduce = true;
}

void StaticStructureFactorDebye::reduce()
{
m_structure_factor.prepare(m_histogram.shape());
m_local_histograms.reduceInto(m_structure_factor);

// Normalize by the frame count if necessary
if (m_frame_counter > 1)
{
util::forLoopWrapper(0, m_structure_factor.size(), [&](size_t begin, size_t end) {
for (size_t k_index = begin; k_index < end; ++k_index)
{
m_structure_factor[k_index] /= static_cast<float>(m_frame_counter);
}
});
}
}

}; }; // namespace freud::diffraction
91 changes: 91 additions & 0 deletions cpp/diffraction/StaticStructureFactorDebye.h
@@ -0,0 +1,91 @@
// Copyright (c) 2010-2020 The Regents of the University of Michigan
// This file is from the freud project, released under the BSD 3-Clause License.

#ifndef STATIC_STRUCTURE_FACTOR_DEBYE_H
#define STATIC_STRUCTURE_FACTOR_DEBYE_H

#include "Box.h"
#include "Histogram.h"
#include "NeighborQuery.h"

/*! \file StaticStructureFactorDebye.h
\brief Routines for computing static structure factors.

Computes structure factors from the Fourier transform of a radial distribution function (RDF).
This method is not capable of resolving k-vectors smaller than the magnitude 4 * pi / L, where L is the
smallest side length of the system's periodic box.
*/

namespace freud { namespace diffraction {

class StaticStructureFactorDebye
{
using S_kHistogram = util::Histogram<float>;

public:
//! Constructor
StaticStructureFactorDebye(unsigned int bins, float k_max, float k_min = 0);

//! Compute the structure factor S(k) using the Debye formula
void accumulate(const freud::locality::NeighborQuery* neighbor_query, const vec3<float>* query_points,
unsigned int n_query_points, unsigned int n_total);

//! Reduce thread-local arrays onto the primary data arrays.
void reduce();

//! Return thing_to_return after reducing if necessary.
template<typename U> U& reduceAndReturn(U& thing_to_return)
{
if (m_reduce)
{
reduce();
}
m_reduce = false;
return thing_to_return;
}

//! Reset the histogram to all zeros
void reset()
{
m_local_histograms.reset();
m_frame_counter = 0;
m_reduce = true;
}

//! Get the structure factor
const util::ManagedArray<float>& getStructureFactor()
{
return reduceAndReturn(m_structure_factor);
}

//! Get the k bin edges
std::vector<float> getBinEdges() const
{
return m_histogram.getBinEdges()[0];
}

//! Get the k bin centers
std::vector<float> getBinCenters() const
{
return m_histogram.getBinCenters()[0];
}

//! Get the minimum valid k value
float getMinValidK() const
{
return m_min_valid_k;
}

private:
S_kHistogram m_histogram; //!< Histogram to hold computed structure factor
S_kHistogram::ThreadLocalHistogram
m_local_histograms; //!< Thread local histograms for TBB parallelism
util::ManagedArray<float> m_structure_factor; //!< The computed structure factor
unsigned int m_frame_counter {0}; //!< Number of frames calculated
float m_min_valid_k {0}; //!< The minimum valid k-value based on the computed box
bool m_reduce {true}; //!< Whether to reduce
};

}; }; // namespace freud::diffraction

#endif // STATIC_STRUCTURE_FACTOR_DEBYE_H
14 changes: 14 additions & 0 deletions cpp/locality/NeighborQuery.h
Expand Up @@ -51,6 +51,20 @@ struct QueryArgs
{
QueryArgs() = default;

//! Constructor
QueryArgs(QueryType mode, unsigned int num_neighbors, float r_max, float r_min, float r_guess,
float scale, bool exclude_ii)
: mode(mode), num_neighbors(num_neighbors), r_max(r_max), r_min(r_min), r_guess(r_guess),
scale(scale), exclude_ii(exclude_ii)
{}

//! Ball query factory
static QueryArgs make_ball(float r_max, float r_min = 0, bool exclude_ii = DEFAULT_EXCLUDE_II)
{
return QueryArgs(QueryType::ball, DEFAULT_NUM_NEIGHBORS, r_max, r_min, DEFAULT_R_GUESS, DEFAULT_SCALE,
exclude_ii);
}

bdice marked this conversation as resolved.
Show resolved Hide resolved
QueryType mode {DEFAULT_MODE}; //! Whether to perform a ball or k-nearest neighbor query.
unsigned int num_neighbors {DEFAULT_NUM_NEIGHBORS}; //! The number of nearest neighbors to find.
float r_max {DEFAULT_R_MAX}; //! The cutoff distance within which to find neighbors.
Expand Down
9 changes: 5 additions & 4 deletions cpp/util/Histogram.h
Expand Up @@ -489,7 +489,7 @@ template<typename T> class Histogram
* variadic templating to accept an arbitrary set of float values and
* construct a vector out of them.
*/
std::pair<std::vector<float>, Weight<T>> getValueVector(float value) const
const std::pair<std::vector<float>, Weight<T>> getValueVector(float value) const
{
return {{value}, Weight<T>()};
}
Expand All @@ -499,14 +499,14 @@ template<typename T> class Histogram
* variadic templating to accept an arbitrary set of float values and
* construct a vector out of them.
*/
std::pair<std::vector<float>, Weight<T>> getValueVector(Weight<T> weight) const
const std::pair<std::vector<float>, Weight<T>> getValueVector(Weight<T> weight) const
{
return {{}, weight};
}

//! The recursive case for constructing a vector of values (see base-case function docs).
template<typename... FloatsOrWeight>
std::pair<std::vector<float>, Weight<T>> getValueVector(float value, FloatsOrWeight... values) const
const std::pair<std::vector<float>, Weight<T>> getValueVector(float value, FloatsOrWeight... values) const
{
std::pair<std::vector<float>, Weight<T>> tmp = getValueVector(values...);
tmp.first.insert(tmp.first.begin(), value);
Expand All @@ -515,7 +515,8 @@ template<typename T> class Histogram

//! The recursive case for constructing a vector of values (see base-case function docs).
template<typename... FloatsOrWeight>
std::pair<std::vector<float>, Weight<T>> getValueVector(Weight<T> weight, FloatsOrWeight... values) const
const std::pair<std::vector<float>, Weight<T>> getValueVector(Weight<T> weight,
FloatsOrWeight... values) const
{
std::pair<std::vector<float>, Weight<T>> tmp = getValueVector(values...);
tmp.second = weight;
Expand Down
56 changes: 55 additions & 1 deletion cpp/util/utils.h
Expand Up @@ -3,6 +3,7 @@

#include <algorithm>
#include <cmath>
#include <stdexcept>
#include <tbb/blocked_range.h>
#include <tbb/blocked_range2d.h>
#include <tbb/parallel_for.h>
Expand All @@ -21,11 +22,64 @@ inline float clamp(float v, float lo, float hi)
\returns The remainder of a/b, between min(0, b) and max(0, b).
\note This is the same behavior of the modulus operator % in Python (but not C++).
*/
template<class Scalar> inline Scalar modulusPositive(Scalar a, Scalar b)
template<typename Scalar> inline Scalar modulusPositive(Scalar a, Scalar b)
{
return std::fmod(std::fmod(a, b) + b, b);
}

//! Sinc function, sin(x)/x
/*! \param x Argument.
\returns Sin of x divided by x.
\note There is no factor of pi in this definition (some conventions include pi).
*/
template<typename Scalar> inline Scalar sinc(Scalar x)
{
if (x == 0)
{
return 1;
}
return std::sin(x) / x;
}

//! Simpson's Rule numerical integration
/*! \param integrand Callable that returns the integrand value for a provided bin index.
\param num_bins Number of bins to integrate over. Must be odd.
\param dx Step size between bins.
\note The integration summation is performed in double-precision regardless of Scalar type.
*/
template<typename Integrand> inline double simpson_integrate(Integrand& integrand, size_t num_bins, double dx)
{
if (num_bins % 2 != 1)
{
// This only implements the easiest case of Simpson's rule.
// Even numbers of bins require additional logic.
throw std::invalid_argument("The number of integration bins must be odd.");
}

double integral = 0.0;

// Simpson's rule uses prefactors 1, 4, 2, 4, 2, ..., 4, 1
auto simpson_prefactor = [=](size_t bin) {
if (bin == 0 || bin == num_bins - 1)
{
return 1.0;
}
if (bin % 2 == 0)
{
return 2.0;
}
return 4.0;
};

for (size_t bin_index = 0; bin_index < num_bins; bin_index++)
{
integral += simpson_prefactor(bin_index) * integrand(bin_index);
}

integral *= dx / 3.0;
return integral;
}

tommy-waltmann marked this conversation as resolved.
Show resolved Hide resolved
//! Wrapper for for-loop to allow the execution in parallel or not.
/*! \param begin Beginning index.
* \param end Ending index.
Expand Down
1 change: 1 addition & 0 deletions doc/source/modules/diffraction.rst
Expand Up @@ -8,6 +8,7 @@ Diffraction Module
:nosignatures:

freud.diffraction.DiffractionPattern
freud.diffraction.StaticStructureFactorDebye

.. rubric:: Details

Expand Down
25 changes: 25 additions & 0 deletions doc/source/reference/freud.bib
@@ -1,3 +1,15 @@
@article{Farrow2009,
author = "Farrow, Christopher L. and Billinge, Simon J. L.",
title = "{Relationship between the atomic pair distribution function and small-angle scattering: implications for modeling of nanoparticles}",
journal = "Acta Crystallographica Section A",
year = "2009",
volume = "65",
number = "3",
pages = "232--239",
doi = {10.1107/S0108767309009714},
url = {https://doi.org/10.1107/S0108767309009714}
}

@article{Filion_2010,
author = {Filion, L. and Hermes, M. and Ni, R. and Dijkstra, M.},
date-added = {2019-10-03 00:25:57 -0400},
Expand Down Expand Up @@ -205,3 +217,16 @@ @article{Mickel2013
url = {https://doi.org/10.1063/1.4774084},
eprint = {arXiv:1209.6180}
}

@article{Liu2016,
author = {Hongjun Liu and Stephen J. Paddison},
title = {Direct calculation of the X-ray structure factor of ionic liquids},
journal = {Phys. Chem. Chem. Phys.},
year = {2016},
volume = {18},
issue = {16},
pages = {11000-11007},
publisher = {The Royal Society of Chemistry},
doi = {10.1039/C5CP06199G},
url = {https://dx.doi.org/10.1039/C5CP06199G}
}