Skip to content

Commit

Permalink
Resolve scattering analysis review comments
Browse files Browse the repository at this point in the history
  • Loading branch information
rc83 committed Feb 6, 2020
1 parent 6e06e3d commit d9fda29
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 36 deletions.
21 changes: 12 additions & 9 deletions docs/_docs/analysis.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,19 +149,21 @@ and as a function of separation, _r_. In addition, the radial distribution funct

### Structure Factor

The isotropically averaged static structure factor between
$N$ point scatterers is calculated using the [Debye formula](http://doi.org/dmb9wm),
The isotropically averaged static structure factor between $N$ point scatterers is calculated using
the [Debye formula](http://doi.org/dmb9wm),

$$
S(q) = 1 + \frac{2}{N} \left \langle
\sum_{i=1}^{N-1}\sum_{j=i+1}^N \frac{\sin(qr_{ij})}{qr_{ij}}
\right \rangle
$$

The selected `molecules` can be treated either as single
point scatterers (`com=true`) or as a group of individual
point scatterers of equal intensity, i.e. with a
form factor of unity.
The selected `molecules` can be treated either as single point scatterers (`com=true`) or as a group of individual
point scatterers of equal intensity, i.e., with a form factor of unity.

The computation of the structure factor is rather computationally intensive task, scaling quadratically with the number
of particles and linearly with the number of scattering vector mesh points. If OpenMP is available, multiple threads
may be utilized in parallel to speed it up the analysis.

`scatter` | Description
----------- | ------------------------------------------
Expand All @@ -174,10 +176,11 @@ form factor of unity.
`com=true` | Treat molecular mass centers as single point scatterers
`pmax=15` | Multiples of $(h,k,l)$ when using the `explicit` scheme
`scheme=explicit` | The following schemes are available: `debye`, `explicit`
`stepsave=false` | Save every sample to disk

The `explicit` scheme is recommended for cuboids with PBC and the calculation is performed by explicitly
averaging the following equation over the 3+6+4 directions obtained by permuting
the crystallographic index `[100]`, `[110]`, `[111]` to define the scattering vector
The `explicit` scheme is recommended for cuboids with PBC and the calculation is performed by explicitly averaging
the following equation over the 3+6+4 directions obtained by permuting the crystallographic index
`[100]`, `[110]`, `[111]` to define the scattering vector
$\mathbf{q} = 2\pi p/L(h,k,l)$ where $p=1,2,\dots,p\_{max}$.

$$
Expand Down
1 change: 0 additions & 1 deletion docs/_docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,4 +177,3 @@ Instead of uploading to anaconda.org, install a local copy directly after the bu
~~~ bash
conda install -c USER faunus --use-local
~~~

15 changes: 14 additions & 1 deletion docs/_docs/running.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,20 @@ Tip: Redirect the standard error output to a log file.
faunus -v 5 -i in.json 2>> error.log
~~~

## Message Passing Interface (MPI)
## Parallelization

### OpenMP

Several routines in Faunus can run in parallel using multiple threads. The only prerequisite is that Faunus was
compiled with OpenMP support (which is default). The number of threads is controlled with an environment variable.
The following example demonstrates how to run Faunus using 4 threads:

~~~ bash
export OMP_NUM_THREADS=4
faunus -i in.json
~~~

### Message Passing Interface (MPI)

Only few routines in Faunus are currently parallelisable using MPI, for example
parallel tempering, and penalty function energies.
Expand Down
1 change: 1 addition & 0 deletions docs/schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,7 @@ properties:
scheme:
description: Scattering method
enum: [debye, explicit]
stepsave: {type: boolean, default: false, description: Save every sample to disk}
required: [nstep, molecules, scheme]
additionalProperties: false

Expand Down
4 changes: 1 addition & 3 deletions src/analysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1319,9 +1319,7 @@ void ScatteringFunction::_sample() {
}

// zero-padded suffix to use with `save_after_sample`
std::string suffix = std::to_string(cnt);
suffix = std::string(7 - suffix.length(), '0') + suffix;

std::string suffix = fmt::format("{:07d}", cnt);
switch (scheme) {
case DEBYE:
debye->sample(p, spc.geo.getVolume());
Expand Down
3 changes: 1 addition & 2 deletions src/io.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ void write(std::ostream &stream, const std::map<TKey, TValue> &data, const std::
* @param filename
* @param data
*/
template<typename TKey, typename TValue>
void write(const std::string &filename, const std::map<TKey, TValue>& data) {
template <typename TKey, typename TValue> void write(const std::string &filename, const std::map<TKey, TValue> &data) {
if (!data.empty()) {
std::ofstream file(filename);
write(file, data);
Expand Down
41 changes: 21 additions & 20 deletions src/scatter.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ template <class Tformfactor, class T = float> class DebyeFormula {
* @return the scattering vector magnitude q at the mesh point m
*/
#pragma omp declare simd uniform(this) linear(m:1)
T q_mesh(int m) {
inline T q_mesh(int m) {
return q_mesh_min + m * q_mesh_step;
}

Expand All @@ -83,13 +83,14 @@ template <class Tformfactor, class T = float> class DebyeFormula {
q_mesh_min = q_min < 1e-6 ? q_step : q_min; // ensure that q > 0
q_mesh_max = q_max;
q_mesh_step = q_step;
// resolution of the 1D mesh approximation of the scattering vector magnitude q
const size_t q_resolution = 1 + (size_t)((q_max - q_min) / q_step);
if (q_resolution > std::numeric_limits<int>::max()) { // we use int index variable
try {
// resolution of the 1D mesh approximation of the scattering vector magnitude q
const int q_resolution = numeric_cast<int>(1 + std::floor((q_max - q_min) / q_step));
intensity.resize(q_resolution, 0.0);
sampling.resize(q_resolution, 0.0);
} catch (std::overflow_error &e) {
throw std::range_error("DebyeFormula: Too many samples");
}
intensity.resize(q_resolution, 0.0);
sampling.resize(q_resolution, 0.0);
}

Geometry::Sphere geo = Geometry::Sphere(r_cutoff_infty / 2); //!< geometry to use for distance calculations
Expand Down Expand Up @@ -180,16 +181,16 @@ template <class Tformfactor, class T = float> class DebyeFormula {
/**
* @return a tuple of min, max, and step parameters of a q-mash
*/
std::tuple<T, T, T> getQMeshParameters() {
auto getQMeshParameters() {
return std::make_tuple(q_mesh_min, q_mesh_max, q_mesh_step);
}

/**
* @return a map containing q (key) and average intensity (value)
*/
std::map<double, double> getIntensity() {
std::map<double, double> averaged_intensity;
for (int m = 0; m < intensity.size(); ++m) {
auto getIntensity() {
std::map<T, T> averaged_intensity;
for (size_t m = 0; m < intensity.size(); ++m) {
const T average = intensity[m] / (sampling[m] != 0.0 ? sampling[m] : 1.0);
averaged_intensity.emplace(q_mesh(m), average);
}
Expand All @@ -209,16 +210,16 @@ template <typename T> class SamplingPolicy {
T value;
T weight;
};
typedef std::map<T, sampled_value> TSampledValues;
typedef std::map<T, sampled_value> TSampledValueMap;
private:
TSampledValues samples;
TSampledValueMap samples;
const T precision = 10000.0; //!< precision of the key for better binning

public:
std::map<double, double> getSampling() {
std::map<double, double> average;
for (auto &kv : samples) {
average.emplace(kv.first, kv.second.value / kv.second.weight);
std::map<T, T> getSampling() const {
std::map<T, T> average;
for (auto [key, sample] : samples) {
average.emplace(key, sample.value / sample.weight);
}
return average;
}
Expand All @@ -230,14 +231,14 @@ template <typename T> class SamplingPolicy {
*/
void addSampling(T key_approx, T value, T weight = 1.0) {
const T key = std::round(key_approx * precision) / precision; // round |q| for better binning
samples[key].value += value;
samples[key].value += value * weight;
samples[key].weight += weight;
}
};


/**
* @brief Structure factor calculation using explicit q averaging.
* @brief Calculate structure factor using explicit q averaging.
*
* This averages over the thirteen permutations of the Miller index [100], [110], [101] using:
*
Expand Down Expand Up @@ -315,7 +316,7 @@ template <typename T = float, typename TSamplingPolicy = SamplingPolicy<T>> clas


/**
* @brief Structure factor calculation using explicit q averaging in isotropic periodic boundary conditions (IPBC).
* @brief Calculate structure factor using explicit q averaging in isotropic periodic boundary conditions (IPBC).
*
* The sample directions reduce to 3 compared to 13 in regular periodic boundary conditions. Overall simplification
* shall yield roughly 10 times faster computation.
Expand All @@ -335,7 +336,7 @@ template <typename T = float, typename TSamplingPolicy = SamplingPolicy<T>> clas
// https://gcc.gnu.org/gcc-9/porting_to.html#ompdatasharing
// #pragma omp parallel for collapse(2) default(none) shared(directions, p_max, positions, boxlength)
#pragma omp parallel for collapse(2) default(shared)
for (int i = 0; i < directions.size(); ++i) {
for (size_t i = 0; i < directions.size(); ++i) {
for (int p = 1; p <= p_max; ++p) { // loop over multiples of q
const Point q = (2 * pc::pi * p / boxlength) * directions[i]; // scattering vector
T sum_cos = 0;
Expand Down

0 comments on commit d9fda29

Please sign in to comment.