Program Listing for File symmetry.hpp
↰ Return to documentation for file (cif++/symmetry.hpp
)
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "cif++/exports.hpp"
#include "cif++/matrix.hpp"
#include "cif++/point.hpp"
#include <array>
#include <cstdint>
#include <string>
#if defined(__cpp_impl_three_way_comparison)
#include <compare>
#endif
namespace cif
{
// --------------------------------------------------------------------
inline point operator*(const matrix3x3<float> &m, const point &pt)
{
return {
m(0, 0) * pt.m_x + m(0, 1) * pt.m_y + m(0, 2) * pt.m_z,
m(1, 0) * pt.m_x + m(1, 1) * pt.m_y + m(1, 2) * pt.m_z,
m(2, 0) * pt.m_x + m(2, 1) * pt.m_y + m(2, 2) * pt.m_z
};
}
// --------------------------------------------------------------------
enum class space_group_name
{
full,
xHM,
Hall
};
struct space_group
{
const char *name;
const char *xHM;
const char *Hall;
int nr;
};
extern CIFPP_EXPORT const space_group kSpaceGroups[];
extern CIFPP_EXPORT const std::size_t kNrOfSpaceGroups;
// --------------------------------------------------------------------
struct symop_data
{
constexpr symop_data(const std::array<int, 15> &data)
: m_packed((data[0] bitand 0x03ULL) << 34 bitor
(data[1] bitand 0x03ULL) << 32 bitor
(data[2] bitand 0x03ULL) << 30 bitor
(data[3] bitand 0x03ULL) << 28 bitor
(data[4] bitand 0x03ULL) << 26 bitor
(data[5] bitand 0x03ULL) << 24 bitor
(data[6] bitand 0x03ULL) << 22 bitor
(data[7] bitand 0x03ULL) << 20 bitor
(data[8] bitand 0x03ULL) << 18 bitor
(data[9] bitand 0x07ULL) << 15 bitor
(data[10] bitand 0x07ULL) << 12 bitor
(data[11] bitand 0x07ULL) << 9 bitor
(data[12] bitand 0x07ULL) << 6 bitor
(data[13] bitand 0x07ULL) << 3 bitor
(data[14] bitand 0x07ULL) << 0)
{
}
bool operator==(const symop_data &rhs) const
{
return m_packed == rhs.m_packed;
}
bool operator<(const symop_data &rhs) const
{
return m_packed < rhs.m_packed;
}
inline constexpr int unpack3(int offset) const
{
int result = (m_packed >> offset) bitand 0x03;
return result == 3 ? -1 : result;
}
inline constexpr int unpack7(int offset) const
{
return (m_packed >> offset) bitand 0x07;
}
constexpr std::array<int, 15> data() const
{
return {
unpack3(34),
unpack3(32),
unpack3(30),
unpack3(28),
unpack3(26),
unpack3(24),
unpack3(22),
unpack3(20),
unpack3(18),
unpack7(15),
unpack7(12),
unpack7(9),
unpack7(6),
unpack7(3),
unpack7(0)
};
}
private:
friend struct symop_datablock;
const uint64_t kPackMask = (~0ULL >> (64 - 36));
symop_data(uint64_t v)
: m_packed(v bitand kPackMask)
{
}
uint64_t m_packed;
};
struct symop_datablock
{
constexpr symop_datablock(int spacegroup, int rotational_number, const std::array<int, 15> &rt_data)
: m_v((spacegroup bitand 0xffffULL) << 48 bitor
(rotational_number bitand 0xffULL) << 40 bitor
symop_data(rt_data).m_packed)
{
}
uint16_t spacegroup() const { return m_v >> 48; }
symop_data symop() const { return symop_data(m_v); }
uint8_t rotational_number() const { return (m_v >> 40) bitand 0xff; }
private:
uint64_t m_v;
};
static_assert(sizeof(symop_datablock) == sizeof(uint64_t), "Size of symop_data is wrong");
extern CIFPP_EXPORT const symop_datablock kSymopNrTable[];
extern CIFPP_EXPORT const std::size_t kSymopNrTableSize;
// --------------------------------------------------------------------
// Some more symmetry related stuff here.
class datablock;
class cell;
class spacegroup;
class rtop;
struct sym_op;
struct sym_op
{
public:
sym_op(uint8_t nr = 1, uint8_t ta = 5, uint8_t tb = 5, uint8_t tc = 5)
: m_nr(nr)
, m_ta(ta)
, m_tb(tb)
, m_tc(tc)
{
}
explicit sym_op(std::string_view s);
sym_op(const sym_op &) = default;
sym_op(sym_op &&) = default;
sym_op &operator=(const sym_op &) = default;
sym_op &operator=(sym_op &&) = default;
constexpr bool is_identity() const
{
return m_nr == 1 and m_ta == 5 and m_tb == 5 and m_tc == 5;
}
explicit constexpr operator bool() const
{
return not is_identity();
}
std::string string() const;
#if defined(__cpp_impl_three_way_comparison)
constexpr auto operator<=>(const sym_op &rhs) const = default;
#else
constexpr bool operator==(const sym_op &rhs) const
{
return m_nr == rhs.m_nr and m_ta == rhs.m_ta and m_tb == rhs.m_tb and m_tc == rhs.m_tc;
}
constexpr bool operator!=(const sym_op &rhs) const
{
return not operator==(rhs);
}
#endif
uint8_t m_nr;
uint8_t m_ta, m_tb, m_tc;
};
static_assert(sizeof(sym_op) == 4, "Sym_op should be four bytes");
namespace literals
{
inline sym_op operator""_symop(const char *text, size_t length)
{
return sym_op({ text, length });
}
} // namespace literals
// --------------------------------------------------------------------
// The transformation class
class transformation
{
public:
transformation(const symop_data &data);
transformation(const matrix3x3<float> &r, const cif::point &t);
transformation(const transformation &) = default;
transformation(transformation &&) = default;
transformation &operator=(const transformation &) = default;
transformation &operator=(transformation &&) = default;
point operator()(point pt) const
{
if (m_q)
pt.rotate(m_q);
else
pt = m_rotation * pt;
return pt + m_translation;
}
friend transformation operator*(const transformation &lhs, const transformation &rhs);
friend transformation inverse(const transformation &t);
transformation operator-() const
{
return inverse(*this);
}
friend class spacegroup;
private:
// Most rotation matrices provided by the International Tables
// are really rotation matrices, in those cases we can construct
// a quaternion. Unfortunately, that doesn't work for all of them
void try_create_quaternion();
matrix3x3<float> m_rotation;
quaternion m_q;
point m_translation;
};
// --------------------------------------------------------------------
// class cell
class cell
{
public:
cell(float a, float b, float c, float alpha = 90.f, float beta = 90.f, float gamma = 90.f);
cell(const datablock &db);
float get_a() const { return m_a; }
float get_b() const { return m_b; }
float get_c() const { return m_c; }
float get_alpha() const { return m_alpha; }
float get_beta() const { return m_beta; }
float get_gamma() const { return m_gamma; }
matrix3x3<float> get_orthogonal_matrix() const { return m_orthogonal; }
matrix3x3<float> get_fractional_matrix() const { return m_fractional; }
private:
void init();
float m_a, m_b, m_c, m_alpha, m_beta, m_gamma;
matrix3x3<float> m_orthogonal, m_fractional;
};
// --------------------------------------------------------------------
int get_space_group_number(const datablock &db);
int get_space_group_number(std::string_view spacegroup);
int get_space_group_number(std::string_view spacegroup, space_group_name type);
class spacegroup : public std::vector<transformation>
{
public:
spacegroup(const datablock &db)
: spacegroup(get_space_group_number(db))
{
}
spacegroup(std::string_view name)
: spacegroup(get_space_group_number(name))
{
}
spacegroup(std::string_view name, space_group_name type)
: spacegroup(get_space_group_number(name, type))
{
}
spacegroup(int nr);
int get_nr() const { return m_nr; }
std::string get_name() const;
point operator()(const point &pt, const cell &c, sym_op symop) const;
point inverse(const point &pt, const cell &c, sym_op symop) const;
private:
int m_nr;
size_t m_index;
};
// --------------------------------------------------------------------
class crystal
{
public:
crystal(const datablock &db)
: m_cell(db)
, m_spacegroup(db)
{
}
crystal(const cell &c, const spacegroup &sg)
: m_cell(c)
, m_spacegroup(sg)
{
}
crystal(const crystal &) = default;
crystal(crystal &&) = default;
crystal &operator=(const crystal &) = default;
crystal &operator=(crystal &&) = default;
const cell &get_cell() const { return m_cell; }
const spacegroup &get_spacegroup() const { return m_spacegroup; }
point symmetry_copy(const point &pt, sym_op symop) const
{
return m_spacegroup(pt, m_cell, symop);
}
point inverse_symmetry_copy(const point &pt, sym_op symop) const
{
return m_spacegroup.inverse(pt, m_cell, symop);
}
std::tuple<float, point, sym_op> closest_symmetry_copy(point a, point b) const;
private:
cell m_cell;
spacegroup m_spacegroup;
};
// --------------------------------------------------------------------
// Symmetry operations on points
inline point orthogonal(const point &pt, const cell &c)
{
return c.get_orthogonal_matrix() * pt;
}
inline point fractional(const point &pt, const cell &c)
{
return c.get_fractional_matrix() * pt;
}
// --------------------------------------------------------------------
} // namespace cif