Utility#

Directory: libfly/utility

The utility folder contains a collection of non-specific utilities, it is documented by file.

Versioning#

File: libfly/version.hpp

Defines a set of version macros, read by CMake.

Defines

FLY_VERSION_MAJOR#

The major version umber.

FLY_VERSION_MINOR#

The minor version umber.

FLY_VERSION_PATCH#

The patch umber.

Core#

File: libfly/core.hpp

Miscellaneous utilities.

Todo

refactor to trailing return type syntax.

Configuration macros#

FLY_SPATIAL_DIMS#

Specify the number of spatial dimensions at compile time, defaults to 3.

To customize, at the compilation configuration step append:

-DCMAKE_CXX_FLAGS='-DFLY_SPATIAL_DIMS=<number>'

replacing <number> with the number of dimensions.

Error handling#

struct RuntimeError : public std::runtime_error#

libFLY’s catchable error type.

Subclassed by fly::Spline::OutOfBounds


template<typename ...Args>
auto fly::error(fmt::format_string<Args...> fmt, Args&&... args) -> RuntimeError#

Utility to make a RuntimeError object using the {fmt} library to format the error message.

Parameters:
  • fmt – Format string.

  • args – Format arguments (forwarded to fmt::format).

Returns:

A fly::RuntimeError containing the formatted error message as by fmt::format(fmt, args...).


template<typename ...Args>
constexpr auto fly::verify(bool cond, fmt::format_string<Args...> fmt, Args&&... args) -> void#

Utility to check cond and throw RuntimeError if condition is false.

Shorthand for:

if (!cond) {
    throw fly::error(fmt, args...);
}

Parameters:
  • cond – Condition to check.

  • fmt – Format string.

  • args – Format arguments (forwarded to fmt::format).

Returns:

void.


ASSERT(expr, string_literal, ...)#

Use like fly::verify() but disabled if NDEBUG defined.

Defines, variables, etc.#

constexpr int fly::spatial_dims = 3#

The number of spatial dimensions.

Must be greater than or equal to 2. Configurable using the FLY_SPATIAL_DIMS macro.


enum class fly::Sign : int#

Strongly typed +/- sign.

Values:

enumerator plus#

Representing +1 or the positive direction.

enumerator minus#

Representing -1 or the negative direction.


using fly::Vec = Eigen::Vector<double, spatial_dims>#

Shorthand for creating an Eigen::Vector of doubles of length spatial_dims.


using fly::Mat = Eigen::Matrix<double, spatial_dims, spatial_dims>#

Shorthand for creating an spatial_dims x spatial_dims Eigen::Matrix of doubles.


template<typename T>
using fly::Arr = Eigen::Array<T, spatial_dims, 1>#

Shorthand for creating an spatial_dims x 1 Eigen::Array.

Template Parameters:

T – The scalar type of the Eigen::Array.


Meta programming#

template<typename ...T>
using fly::first_t = typename detail::First<T...>::type#

Extracts the first type from a parameter pack.


template<typename T>
using fly::remove_cref_t = std::remove_const_t<std::remove_reference_t<T>>#

Strip all reference and const qualifications from T.

Template Parameters:

T – The type to strip ref/const qualifications from.


template<typename...>
constexpr bool fly::always_false = false#

Non-deducible false for use in static_assert.

Example:

#include "libfly/utility/core.hpp"

template <typename T> struct referance_banned {};

template <typename T> struct referance_banned<T&> {
  // This static_assert will always fail but the compiler cannot deduce this
  // until it is instanciated as fly::always_false<T> could be specialised
  // at any point.
  static_assert(fly::always_false<T>, "T cannot be an l-value referances!");
};


template<template<class...> class Fn, class ...Args>
constexpr bool fly::is_detected_v = detail::detector<void, void, Fn, Args...>::value#

Utility to detect if a meta function has substitution failure.

False if Fn<Args...> triggers substitution failure, true otherwise.

Example:

#include <utility>

#include "libfly/utility/core.hpp"

template <typename T>
// Will error if T does not have foo member.
using has_foo = decltype(std::declval<T>().foo());

// If T supports the .foo() method call and return it, otherwise throw an error.
template <typename T>
auto foo_or_throw(T any) -> fly::detected_or_t<void, has_foo, T> {
  if constexpr (fly::is_detected_v<has_foo, T>) {
    return any.foo();
  } else {
    throw fly::error("Type 'T' does not have a foo method");
  }
}

Template Parameters:
  • Fn – A template template meta-function.

  • Args – Meta arguments to invoke with meta function.


template<class Default, template<class...> class Fn, class ...Args>
using fly::detected_or_t = typename detail::detector<Default, void, Fn, Args...>::type#

Utility to get the result of a meta function or a default value.

If Fn<Args...> triggers substitution failure returns Default otherwise, returns Fn<Args...>.

Example:

#include <utility>

#include "libfly/utility/core.hpp"

template <typename T>
// Will error if T does not have foo member.
using has_foo = decltype(std::declval<T>().foo());

// If T supports the .foo() method call and return it, otherwise throw an error.
template <typename T>
auto foo_or_throw(T any) -> fly::detected_or_t<void, has_foo, T> {
  if constexpr (fly::is_detected_v<has_foo, T>) {
    return any.foo();
  } else {
    throw fly::error("Type 'T' does not have a foo method");
  }
}

Template Parameters:
  • Default – The type to return in case of substitution failure.

  • Fn – A template template meta-function.

  • Args – Meta arguments to invoke with meta function.


template<typename From, typename To>
constexpr bool fly::is_narrowing_conversion_v = detail::is_narrowing_conversion_impl<From, To>::value#

Check if a numerical conversion is narrowing.

Leverages brace initialisation, adapted from: https://stackoverflow.com/a/67603594

Template Parameters:
  • From – The source type.

  • To – The target type.

Small functions#

template<typename ...Args>
auto fly::dprint(bool cond, fmt::format_string<Args...> fmt, Args&&... args) -> void#

Conditional printing utility.

If cond is true forwards fmt and args... to fmt::print. Useful for printing debug messages.


template<typename R, typename T>
constexpr auto fly::safe_cast(T x) -> std::enable_if_t<std::is_integral_v<R> && std::is_integral_v<T>, R>#

Cast integral types asserting that conversion is lossless.

Perform a static_cast from type T to R with bounds checking in debug builds.

Template Parameters:
  • TType of input x, must be integral.

  • R – Target type to cast to, must be integral.

Parameters:

x – The value to cast to a new type.

Returns:

Exactly static_cast<R>(x).


template<typename V, typename F>
auto fly::visit(V &&v, F &&f) -> decltype(auto)#

Utility to reverse argument order to std::visit.

Parameters:
  • v – A std::variant to pass to the visitor.

  • f – A callable that accepts every possible alternative in the variant v.

Returns:

The result of calling std::visit(std::forward<F>(f), std::forward<V>(v)).


template<typename T, typename F>
void fly::template_for(Arr<T> const &beg, Arr<T> const &end, F const &f)#

Invoke f with every combination of indexes between beg and end.

Effectively expands to:

for(T n = beg[spatial_dims]; n < end[spatial_dims]; n++){
    // ... //
    for(T j = beg[1]; j < end[1]; j++){
        for(T i = beg[0]; i < end[0]; i++){
            f(i, j,..., n);
        }
    }
}

Additionally, this will detect if f is callable with an array of indexes, e.g. (in the notation from above):

f(Arr<int>{i, j, ..., n});

Template Parameters:

T – The scalar type of the input arrays.

Parameters:
  • beg – Array of loop start indexes.

  • end – Array of loop end indexes.

  • f – Invokable to call with every combination of indexes.


template<typename C>
constexpr auto fly::ssize(C const &c) -> std::common_type_t<std::ptrdiff_t, std::make_signed_t<decltype(c.size())>>#

Get the signed size of a container.

See https://en.cppreference.com/w/cpp/iterator/size

Parameters:

c – Container to find the size of.

Returns:

The result of c.size() cast to an appropriate signed type.

Mathematical functions#

template<typename T>
constexpr auto fly::near(T a, T b, T atol = 1e-10, T ftol = 0.0001) -> std::enable_if_t<std::is_floating_point_v<T>, bool>#

Test if two floating point numbers are close.

Template Parameters:

TType of inputs, must be a floating point type.

Parameters:
  • a – First input.

  • b – Second input.

  • atol – Tolerance of absolute difference between a and b for them to be close.

  • ftol – Tolerance of fractional difference between a and b for them to be close.

Returns:

true if a and b are within atol or fractionally within ftol of each other.


template<typename T, int N>
auto fly::product_scan(Eigen::Array<T, N, 1> x) -> std::enable_if_t<std::is_arithmetic_v<T>, Eigen::Array<T, N, 1>>#

Compute the shifted prefix product of an array.

If the inputs are \(x_i\) the outputs are:

\[\begin{split}y_1 & = 1 \\ y_2 & = 1 \times x_1 \\ y_3 & = 1 \times x_1 \times x_2 \\ & \vdots \\ y_n & = 1 \times x_1\times x_{2} \times \dots \times x_{n-1}\end{split}\]

This is a common precursor operation when computing cell indices.

Template Parameters:
  • T – Scalar type of the input array, must be arithmetic.

  • N – Number of columns in the input array.

Parameters:

x – The input array.

Returns:

The shifted prefix product (see above) of x.


template<std::size_t Exp, typename T>
constexpr auto fly::ipow(T x) -> std::enable_if_t<std::is_arithmetic_v<T>, T>#

Compute integer powers of arithmetic types at compile time.

Computes:

\[\text{x}^{\text{Exp}}\]

Template Parameters:
  • Exp – The exponent.

  • T – The type of x, must be arithmetic.

Parameters:

x – The input parameter.

Returns:

x^Exp.


template<typename E1, typename E2>
constexpr auto fly::gdot(E1 const &a, E2 const &b)#

Generalised dot-product between two Eigen objects.

Computes:

\[\sum_{i} \sum_{j} a_{ij} b_{ij}\]

Parameters:
  • a – Array-like input.

  • b – Array-like input.

Returns:

The generalised dot-product (see above) of a and b.


template<typename E>
constexpr auto fly::gnorm(E &&r)#

Generic Frobenius norm of an Eigen::Array.

Computes:

\[\sqrt{\sum_{i} \sum_{j} r^2_{ij}}\]

Parameters:

r – Array-like input.

Returns:

The Frobenius norm (see above) of expr.


template<typename E>
constexpr auto fly::gnorm_sq(E const &r)#

Generic squared Frobenius norm of an Eigen object.

Computes:

\[\sum_{i} \sum_{j} r^2_{ij}\]

Parameters:

r – Array-like input.

Returns:

The squared Frobenius norm (see above) of r.


template<typename Scalar, int N>
auto fly::hyperplane_normal(Eigen::Matrix<Scalar, N, N> const &P) -> std::enable_if_t<N != Eigen::Dynamic, Eigen::Vector<Scalar, N>>#

Compute the unit normal of a hyperplane through a set of points.

See: StackExchange

Template Parameters:
  • Scalar – The scalar type of the input matrix.

  • N – The dimensions of the square-matrix p, must not be Eigen::Dynamic.

Parameters:

P – The square input-matrix, whose columns are the N-points the hyper plane will pass through.

Returns:

The unit normal of a hyperplane passing through the columns of P.

Classes#

template<class F, typename = std::enable_if_t<std::is_nothrow_invocable_v<F&&>>>
class Defer#

Basic implementation of a Golang like defer.

Example:

#include <vector>

#include "libfly/utility/core.hpp"

void add_count(std::vector<int>& counts) {
  //
  bool commit = false;

  counts.push_back(42);  // (1) direct action.

  // Lambda executed when the enclosing scope exits (function returns).
  fly::Defer _ = [&]() noexcept {
    if (!commit) {
      counts.pop_back();  // (2) rollback action.
    }
  };

  // ...                 // (3) other operations that may throw.

  commit = true;  // ...    (4) disable rollback actions if no throw.
}

Template Parameters:

F – The nullary invocable’s type, this MUST be deducted through CTAD by the deduction guide and it must be noexcept callable.

Public Functions

Defer(const Defer&) = delete#
Defer(Defer &&other) = delete#
inline constexpr Defer(F &&f)#

Construct a new Defer object.

Parameters:

f – Nullary invocable forwarded into object and invoked by destructor.

inline ~Defer() noexcept#

Call the invocable.

Defer &operator=(const Defer&) = delete#
Defer &operator=(Defer&&) = delete#

Timing#

File: libfly/timeit.hpp

template<typename F, typename ...Args>
auto fly::timeit(std::string_view name, F &&f, Args&&... args) -> std::invoke_result_t<F&&, Args&&...>#

Transparent function wrapper that measures the execution time of a function.

The execution time is printed to stdout. Garantees RVO.

Parameters:
  • name – Name of function being called, also printed to stdout.

  • f – Invocable to call.

  • args – Arguments to invoke f with.

Returns:

The result of invoking f with args… .

Random numbers#

File libfly/random.hpp

Pseudo random number generators (PRNG).

class Xoshiro#

A <random> compatible implementation of the xoshiro256** 1.0 PRNG.

From the original:

This is xoshiro256** 1.0, one of our all-purpose, rock-solid generators. It has excellent (sub-ns) speed, a state (256 bits) that is large enough for any parallel application, and it passes all tests we are aware of.

Public Types

using result_type = std::uint64_t#

Required by named requirement: UniformRandomBitGenerator.

Public Functions

inline explicit Xoshiro(std::array<result_type, 4> const &seed)#

Construct and seed the PRNG.

Parameters:

seed – The PRNG’s seed, must not be everywhere zero.

inline explicit Xoshiro(std::random_device &rd)#

Construct and seed the PRNG from std::random_device.

inline constexpr auto jump() noexcept -> void#

This is the jump function for the generator.

It is equivalent to 2^128 calls to operator(); it can be used to generate 2^128 non-overlapping sub-sequences for parallel computations.

Returns:

void.

inline constexpr auto long_jump() noexcept -> void#

This is the long-jump function for the generator.

It is equivalent to 2^192 calls to operator(); it can be used to generate 2^64 starting points, from each of which jump() will generate 2^64 non-overlapping sub-sequences for parallel distributed computations.

Returns:

void.

inline constexpr auto operator()() noexcept -> result_type#

Generate a random bit sequence and advance the state of the generator.

Returns:

A pseudo-random number.

Public Static Functions

static inline constexpr auto max() noexcept -> result_type#

Get the maximum value of the generator.

Returns:

The maximum value that Xoshiro::operator() can return.

static inline constexpr auto min() noexcept -> result_type#

Get the minimum value of the generator.

Returns:

The minimum value that Xoshiro::operator() can return.

Natural Splines#

File libfly/spline.hpp

Utility for working with natural cubic splines.

class Spline#

Computes a set of natural cubic spline coefficients for a uniformly tabulated function.

See Wikipedia algorithm: https://en.wikipedia.org/wiki/Spline_(mathematics)

Public Functions

Spline() = default#

Construct an empty Spline.

Spline(std::vector<double> y, double dx)#

Construct a new Spline object.

From (n + 1) evenly spaced tabulated values on the interval {0, dx, …, ndx}.

Parameters:
  • y – Tabulated values of the function f.

  • dx – Tabulation interval.

inline auto f(double x) const -> double#

Interpolate tabulated function.

Warning

This silently clamps x inside the tabulated region.

Parameters:

x – Point to interpolate f.

Returns:

f(x) The interpolated value of the function at x.

inline double fp(double x) const#

Interpolate tabulated function’s gradient.

Warning

This silently clamps x inside the tabulated region.

Parameters:

x – Point to interpolate f'.

Returns:

f'(x) The interpolated gradient of the function at x.

inline auto fpp(double x) const -> double#

Interpolate tabulated function’s second derivative.

Note

This may not be continuous or smooth.

Warning

This silently clamps x inside the tabulated region.

Parameters:

x – Point to interpolate f''.

Returns:

f''(x) The interpolated second derivative of the function at x.

struct OutOfBounds : public fly::RuntimeError#

An error type to signify that a value oustide the bounds of the tabulated function has been requested.

Public Functions

inline OutOfBounds(RuntimeError const &err)#

Construct a new Out Of Bounds object from a RuntimeError.

Lattices#

File libfly/lattice.hpp

Utilities for building and manipulating basic lattices of atoms.

Building/manipulating#

template<typename Map, typename ...T>
auto fly::motif_to_lattice(system::Supercell<Map, T...> motif, Arr<int> const &extents) -> system::Supercell<Map, T...>#

Utility to build a supercell by replicating a motif along each axis extents times.

Parameters:
  • motif – Containing the TypeMap and positions of the atoms - in fractional coordinates - the box is interpreted as the primitive cell.

  • extents – Number of primitive-cells along each axis of the supercell.

template<typename Map, typename ...T>
auto fly::add_atoms(system::Supercell<Map, T...> const &cell, std::vector<system::Atom<TypeID, T...>> const &atoms)#

Add atoms to a supercell.

Parameters:
  • cell – Input SuperCell.

  • atoms – Atoms to add to cell.

template<typename Map, typename ...T>
auto fly::remove_atoms(system::Supercell<Map, T...> const &cell, std::vector<Eigen::Index> const &bad) -> system::Supercell<Map, T...>#

Create a new supercell identical to cell but with the atoms in bad removed.

Parameters:
  • cell – Input SuperCell.

  • bad – Indexes of atoms to remove.

template<typename Map, typename ...T>
auto fly::remove_sphere(system::Supercell<Map, T...> const &cell, Eigen::Index centre, double r) -> system::Supercell<Map, T...>#

Create a new supercell identical to cell but with the atoms within r of the atom centre removed.

Parameters:
  • cell – Input SuperCell.

  • centreIndex of central atom.

  • r – Distance from centre to qualify for removal.

inline Vec fly::centroid(system::SoA<Position const&> x)#

Compute the centroid of a vector of atoms, x.

For a set of atoms with positions \(x_i\) this is defined as:

\[\frac{1}{N} \sum_{i=1}^{N}{x_i}\]

inline void fly::centroid_align(system::SoA<Position&> x, system::SoA<Position const&> with)#

Translate the atoms in x such that the centroid of x is the centroid of with.

Vacancies#

class DetectVacancies#

A class that can compare a defective lattice to a perfect lattice and detect the missing atoms.

Public Functions

DetectVacancies(double r_lat, system::Box const &box, system::viewSoA<TypeID, Position> perfect_lat)#

Construct a new Detect Vacancies object.

Parameters:
  • box – A description of the simulation space.

  • r_lat – The “radius” of a lattice point i.e. the minimum distance to be considered at that point.

  • perfect_lat – The perfect lattice, must consist only of atom of a single type.

std::vector<Vec> detect_vacancies(system::viewSoA<TypeID, Position> lat, int num_threads = 1)#

Find the canonical coordinate of the vacancies.

Parameters:
  • lat – The defective lattice.

  • num_threads – The number of (openMP) threads to use.

template<typename T, typename F>
auto fly::kruskal_max(std::vector<T> const &v, F const &norm) -> std::enable_if_t<std::is_invocable_r_v<double, F const&, T const&, T const&>, double>#

Get the longest edge in a minimum spanning tree of the nodes in v.

If there is less than two nodes then this function will return -1.

Parameters:
  • v – A vector of nodes.

  • norm – A function which computes the distance between two of the nodes in v.