Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
L
libcifpp
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
open
libcifpp
Commits
e84282cb
Unverified
Commit
e84282cb
authored
Sep 06, 2023
by
Maarten L. Hekkelman
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
documenting symmetry and text
parent
8b2e02e1
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
264 additions
and
38 deletions
+264
-38
include/cif++/symmetry.hpp
+148
-33
include/cif++/text.hpp
+116
-5
No files found.
include/cif++/symmetry.hpp
View file @
e84282cb
...
@@ -38,15 +38,18 @@
...
@@ -38,15 +38,18 @@
#include <compare>
#include <compare>
#endif
#endif
/// \file cif++/symmetry.hpp
/** \file cif++/symmetry.hpp
/// This file contains code to do symmetry operations based on the
*
/// operations as specified in the International Tables.
* This file contains code to do symmetry operations based on the
* operations as specified in the International Tables.
*/
namespace
cif
namespace
cif
{
{
// --------------------------------------------------------------------
// --------------------------------------------------------------------
/// \brief Apply matrix transformation @a m on point @a pt and return the result
inline
point
operator
*
(
const
matrix3x3
<
float
>
&
m
,
const
point
&
pt
)
inline
point
operator
*
(
const
matrix3x3
<
float
>
&
m
,
const
point
&
pt
)
{
{
return
{
return
{
...
@@ -58,28 +61,40 @@ inline point operator*(const matrix3x3<float> &m, const point &pt)
...
@@ -58,28 +61,40 @@ inline point operator*(const matrix3x3<float> &m, const point &pt)
// --------------------------------------------------------------------
// --------------------------------------------------------------------
/// \brief the space groups we know
enum
class
space_group_name
enum
class
space_group_name
{
{
full
,
full
,
///< The *full* spacegroup
xHM
,
xHM
,
///< The *xHM* spacegroup
Hall
Hall
///< The *Hall* spacegroup
};
};
/// \brief For each known spacegroup we define a structure like this
struct
space_group
struct
space_group
{
{
const
char
*
name
;
const
char
*
name
;
///< The name according to *full*
const
char
*
xHM
;
const
char
*
xHM
;
///< The name according to *xHM*
const
char
*
Hall
;
const
char
*
Hall
;
///< The name according to *Hall*
int
nr
;
int
nr
;
///< The number for this spacegroup
};
};
/// \brief Global list of spacegroups
extern
CIFPP_EXPORT
const
space_group
kSpaceGroups
[];
extern
CIFPP_EXPORT
const
space_group
kSpaceGroups
[];
/// \brief Global for the size of the list of spacegroups
extern
CIFPP_EXPORT
const
std
::
size_t
kNrOfSpaceGroups
;
extern
CIFPP_EXPORT
const
std
::
size_t
kNrOfSpaceGroups
;
// --------------------------------------------------------------------
// --------------------------------------------------------------------
/**
* @brief Helper class to efficiently pack the data that
* makes up a symmetry operation
*
*/
struct
symop_data
struct
symop_data
{
{
/// \brief constructor
constexpr
symop_data
(
const
std
::
array
<
int
,
15
>
&
data
)
constexpr
symop_data
(
const
std
::
array
<
int
,
15
>
&
data
)
:
m_packed
((
data
[
0
]
bitand
0x03ULL
)
<<
34
bitor
:
m_packed
((
data
[
0
]
bitand
0x03ULL
)
<<
34
bitor
(
data
[
1
]
bitand
0x03ULL
)
<<
32
bitor
(
data
[
1
]
bitand
0x03ULL
)
<<
32
bitor
...
@@ -99,27 +114,32 @@ struct symop_data
...
@@ -99,27 +114,32 @@ struct symop_data
{
{
}
}
/// \brief compare
bool
operator
==
(
const
symop_data
&
rhs
)
const
bool
operator
==
(
const
symop_data
&
rhs
)
const
{
{
return
m_packed
==
rhs
.
m_packed
;
return
m_packed
==
rhs
.
m_packed
;
}
}
/// \brief sorting order
bool
operator
<
(
const
symop_data
&
rhs
)
const
bool
operator
<
(
const
symop_data
&
rhs
)
const
{
{
return
m_packed
<
rhs
.
m_packed
;
return
m_packed
<
rhs
.
m_packed
;
}
}
/// \brief return an int representing the value stored in the two bits at offset @a offset
inline
constexpr
int
unpack3
(
int
offset
)
const
inline
constexpr
int
unpack3
(
int
offset
)
const
{
{
int
result
=
(
m_packed
>>
offset
)
bitand
0x03
;
int
result
=
(
m_packed
>>
offset
)
bitand
0x03
;
return
result
==
3
?
-
1
:
result
;
return
result
==
3
?
-
1
:
result
;
}
}
/// \brief return an int representing the value stored in the three bits at offset @a offset
inline
constexpr
int
unpack7
(
int
offset
)
const
inline
constexpr
int
unpack7
(
int
offset
)
const
{
{
return
(
m_packed
>>
offset
)
bitand
0x07
;
return
(
m_packed
>>
offset
)
bitand
0x07
;
}
}
/// \brief return an array of 15 ints representing the values stored
constexpr
std
::
array
<
int
,
15
>
data
()
const
constexpr
std
::
array
<
int
,
15
>
data
()
const
{
{
return
{
return
{
...
@@ -154,8 +174,14 @@ struct symop_data
...
@@ -154,8 +174,14 @@ struct symop_data
uint64_t
m_packed
;
uint64_t
m_packed
;
};
};
/**
* @brief For each symmetry operator defined in the international tables
* we have an entry in this struct type. It contains the spacegroup
* number, the symmetry operations and the rotational number.
*/
struct
symop_datablock
struct
symop_datablock
{
{
/// \brief constructor
constexpr
symop_datablock
(
int
spacegroup
,
int
rotational_number
,
const
std
::
array
<
int
,
15
>
&
rt_data
)
constexpr
symop_datablock
(
int
spacegroup
,
int
rotational_number
,
const
std
::
array
<
int
,
15
>
&
rt_data
)
:
m_v
((
spacegroup
bitand
0xffffULL
)
<<
48
bitor
:
m_v
((
spacegroup
bitand
0xffffULL
)
<<
48
bitor
(
rotational_number
bitand
0xffULL
)
<<
40
bitor
(
rotational_number
bitand
0xffULL
)
<<
40
bitor
...
@@ -163,9 +189,9 @@ struct symop_datablock
...
@@ -163,9 +189,9 @@ struct symop_datablock
{
{
}
}
uint16_t
spacegroup
()
const
{
return
m_v
>>
48
;
}
uint16_t
spacegroup
()
const
{
return
m_v
>>
48
;
}
///< Return the spacegroup
symop_data
symop
()
const
{
return
symop_data
(
m_v
);
}
symop_data
symop
()
const
{
return
symop_data
(
m_v
);
}
///< Return the symmetry operation
uint8_t
rotational_number
()
const
{
return
(
m_v
>>
40
)
bitand
0xff
;
}
uint8_t
rotational_number
()
const
{
return
(
m_v
>>
40
)
bitand
0xff
;
}
///< Return the rotational_number
private
:
private
:
uint64_t
m_v
;
uint64_t
m_v
;
...
@@ -173,7 +199,10 @@ struct symop_datablock
...
@@ -173,7 +199,10 @@ struct symop_datablock
static_assert
(
sizeof
(
symop_datablock
)
==
sizeof
(
uint64_t
),
"Size of symop_data is wrong"
);
static_assert
(
sizeof
(
symop_datablock
)
==
sizeof
(
uint64_t
),
"Size of symop_data is wrong"
);
/// \brief Global containing the list of known symmetry operations
extern
CIFPP_EXPORT
const
symop_datablock
kSymopNrTable
[];
extern
CIFPP_EXPORT
const
symop_datablock
kSymopNrTable
[];
/// \brief Size of the list of known symmetry operations
extern
CIFPP_EXPORT
const
std
::
size_t
kSymopNrTableSize
;
extern
CIFPP_EXPORT
const
std
::
size_t
kSymopNrTableSize
;
// --------------------------------------------------------------------
// --------------------------------------------------------------------
...
@@ -186,13 +215,21 @@ class spacegroup;
...
@@ -186,13 +215,21 @@ class spacegroup;
class
rtop
;
class
rtop
;
struct
sym_op
;
struct
sym_op
;
/** @brief A class that encapsulates the symmetry operations as used in PDB files,
* i.e. a rotational number and a translation vector.
*
* The syntax in string format follows the syntax as used in mmCIF files, i.e.
* rotational number followed by underscore and the three translations where 5 is
* no movement.
*
* So the string 1_555 means no symmetry movement at all since the rotational number
* 1 always corresponds to the symmetry operation [x, y, z].
*/
/// @brief A class that encapsulates the symmetry operations as used in PDB files, i.e. a rotational number and a translation vector
/// The syntax in string format follows the syntax as used in mmCIF files, i.e. rotational number followed by underscore and the
/// three translations where 5 is no movement.
struct
sym_op
struct
sym_op
{
{
public
:
public
:
/// \brief constructor
sym_op
(
uint8_t
nr
=
1
,
uint8_t
ta
=
5
,
uint8_t
tb
=
5
,
uint8_t
tc
=
5
)
sym_op
(
uint8_t
nr
=
1
,
uint8_t
ta
=
5
,
uint8_t
tb
=
5
,
uint8_t
tc
=
5
)
:
m_nr
(
nr
)
:
m_nr
(
nr
)
,
m_ta
(
ta
)
,
m_ta
(
ta
)
...
@@ -201,26 +238,33 @@ struct sym_op
...
@@ -201,26 +238,33 @@ struct sym_op
{
{
}
}
/// \brief construct a sym_op based on the contents encoded in string @a s
explicit
sym_op
(
std
::
string_view
s
);
explicit
sym_op
(
std
::
string_view
s
);
/** @cond */
sym_op
(
const
sym_op
&
)
=
default
;
sym_op
(
const
sym_op
&
)
=
default
;
sym_op
(
sym_op
&&
)
=
default
;
sym_op
(
sym_op
&&
)
=
default
;
sym_op
&
operator
=
(
const
sym_op
&
)
=
default
;
sym_op
&
operator
=
(
const
sym_op
&
)
=
default
;
sym_op
&
operator
=
(
sym_op
&&
)
=
default
;
sym_op
&
operator
=
(
sym_op
&&
)
=
default
;
/** @endcond */
/// \brief return true if this sym_op is the identity operator
constexpr
bool
is_identity
()
const
constexpr
bool
is_identity
()
const
{
{
return
m_nr
==
1
and
m_ta
==
5
and
m_tb
==
5
and
m_tc
==
5
;
return
m_nr
==
1
and
m_ta
==
5
and
m_tb
==
5
and
m_tc
==
5
;
}
}
/// \brief quick test for unequal to identity
explicit
constexpr
operator
bool
()
const
explicit
constexpr
operator
bool
()
const
{
{
return
not
is_identity
();
return
not
is_identity
();
}
}
/// \brief return the content encoded in a string
std
::
string
string
()
const
;
std
::
string
string
()
const
;
#if defined(__cpp_impl_three_way_comparison)
#if defined(__cpp_impl_three_way_comparison)
/// \brief a default spaceship operator
constexpr
auto
operator
<=>
(
const
sym_op
&
rhs
)
const
=
default
;
constexpr
auto
operator
<=>
(
const
sym_op
&
rhs
)
const
=
default
;
#else
#else
constexpr
bool
operator
==
(
const
sym_op
&
rhs
)
const
constexpr
bool
operator
==
(
const
sym_op
&
rhs
)
const
...
@@ -242,6 +286,16 @@ static_assert(sizeof(sym_op) == 4, "Sym_op should be four bytes");
...
@@ -242,6 +286,16 @@ static_assert(sizeof(sym_op) == 4, "Sym_op should be four bytes");
namespace
literals
namespace
literals
{
{
/**
* @brief This operator allows you to write code like this:
*
* @code {.cpp}
* using namespace cif::literals;
*
* cif::sym_op so = "1_555"_symop;
* @endcode
*
*/
inline
sym_op
operator
""
_symop
(
const
char
*
text
,
size_t
length
)
inline
sym_op
operator
""
_symop
(
const
char
*
text
,
size_t
length
)
{
{
return
sym_op
({
text
,
length
});
return
sym_op
({
text
,
length
});
...
@@ -251,17 +305,33 @@ namespace literals
...
@@ -251,17 +305,33 @@ namespace literals
// --------------------------------------------------------------------
// --------------------------------------------------------------------
// The transformation class
// The transformation class
/**
* @brief A class you can use to apply symmetry transformations on points
*
* Transformations consist of two operations, a matrix transformation which
* is often a rotation followed by a translation.
*
* In case the matrix transformation is a pure rotation a quaternion
* is created to do the actual calculations. That's faster and more
* precise.
*/
class
transformation
class
transformation
{
{
public
:
public
:
/// \brief constructor taking a symop_data object @a data
transformation
(
const
symop_data
&
data
);
transformation
(
const
symop_data
&
data
);
/// \brief constructor taking a rotation matrix @a r and a translation vector @a t
transformation
(
const
matrix3x3
<
float
>
&
r
,
const
cif
::
point
&
t
);
transformation
(
const
matrix3x3
<
float
>
&
r
,
const
cif
::
point
&
t
);
/** @cond */
transformation
(
const
transformation
&
)
=
default
;
transformation
(
const
transformation
&
)
=
default
;
transformation
(
transformation
&&
)
=
default
;
transformation
(
transformation
&&
)
=
default
;
transformation
&
operator
=
(
const
transformation
&
)
=
default
;
transformation
&
operator
=
(
const
transformation
&
)
=
default
;
transformation
&
operator
=
(
transformation
&&
)
=
default
;
transformation
&
operator
=
(
transformation
&&
)
=
default
;
/** @endcond */
/// \brief operator() to perform the transformation on point @a pt and return the result
point
operator
()(
point
pt
)
const
point
operator
()(
point
pt
)
const
{
{
if
(
m_q
)
if
(
m_q
)
...
@@ -272,9 +342,13 @@ class transformation
...
@@ -272,9 +342,13 @@ class transformation
return
pt
+
m_translation
;
return
pt
+
m_translation
;
}
}
/// \brief return a transformation object that is the result of applying @a rhs after @a lhs
friend
transformation
operator
*
(
const
transformation
&
lhs
,
const
transformation
&
rhs
);
friend
transformation
operator
*
(
const
transformation
&
lhs
,
const
transformation
&
rhs
);
/// \brief return the inverse transformation for @a t
friend
transformation
inverse
(
const
transformation
&
t
);
friend
transformation
inverse
(
const
transformation
&
t
);
/// \brief return the inverse tranformation for this
transformation
operator
-
()
const
transformation
operator
-
()
const
{
{
return
inverse
(
*
this
);
return
inverse
(
*
this
);
...
@@ -283,7 +357,6 @@ class transformation
...
@@ -283,7 +357,6 @@ class transformation
friend
class
spacegroup
;
friend
class
spacegroup
;
private
:
private
:
// Most rotation matrices provided by the International Tables
// Most rotation matrices provided by the International Tables
// are really rotation matrices, in those cases we can construct
// are really rotation matrices, in those cases we can construct
// a quaternion. Unfortunately, that doesn't work for all of them
// a quaternion. Unfortunately, that doesn't work for all of them
...
@@ -298,22 +371,30 @@ class transformation
...
@@ -298,22 +371,30 @@ class transformation
// --------------------------------------------------------------------
// --------------------------------------------------------------------
// class cell
// class cell
/**
* @brief The cell class describes the dimensions and angles of a unit cell
* in a crystal
*/
class
cell
class
cell
{
{
public
:
public
:
/// \brief constructor
cell
(
float
a
,
float
b
,
float
c
,
float
alpha
=
90.
f
,
float
beta
=
90.
f
,
float
gamma
=
90.
f
);
cell
(
float
a
,
float
b
,
float
c
,
float
alpha
=
90.
f
,
float
beta
=
90.
f
,
float
gamma
=
90.
f
);
/// \brief constructor that takes the appropriate values from the *cell* category in datablock @a db
cell
(
const
datablock
&
db
);
cell
(
const
datablock
&
db
);
float
get_a
()
const
{
return
m_a
;
}
float
get_a
()
const
{
return
m_a
;
}
///< return dimension a
float
get_b
()
const
{
return
m_b
;
}
float
get_b
()
const
{
return
m_b
;
}
///< return dimension b
float
get_c
()
const
{
return
m_c
;
}
float
get_c
()
const
{
return
m_c
;
}
///< return dimension c
float
get_alpha
()
const
{
return
m_alpha
;
}
float
get_alpha
()
const
{
return
m_alpha
;
}
///< return angle alpha
float
get_beta
()
const
{
return
m_beta
;
}
float
get_beta
()
const
{
return
m_beta
;
}
///< return angle beta
float
get_gamma
()
const
{
return
m_gamma
;
}
float
get_gamma
()
const
{
return
m_gamma
;
}
///< return angle gamma
matrix3x3
<
float
>
get_orthogonal_matrix
()
const
{
return
m_orthogonal
;
}
matrix3x3
<
float
>
get_orthogonal_matrix
()
const
{
return
m_orthogonal
;
}
///< return the matrix to use to transform coordinates from fractional to orthogonal
matrix3x3
<
float
>
get_fractional_matrix
()
const
{
return
m_fractional
;
}
matrix3x3
<
float
>
get_fractional_matrix
()
const
{
return
m_fractional
;
}
///< return the matrix to use to transform coordinates from orthogonal to fractional
private
:
private
:
void
init
();
void
init
();
...
@@ -324,36 +405,55 @@ class cell
...
@@ -324,36 +405,55 @@ class cell
// --------------------------------------------------------------------
// --------------------------------------------------------------------
/// \brief Return the spacegroup number from the *symmetry* category in datablock @a db
int
get_space_group_number
(
const
datablock
&
db
);
int
get_space_group_number
(
const
datablock
&
db
);
/// \brief Return the spacegroup number for spacegroup named @a spacegroup
int
get_space_group_number
(
std
::
string_view
spacegroup
);
int
get_space_group_number
(
std
::
string_view
spacegroup
);
/// \brief Return the spacegroup number for spacegroup named @a spacegroup assuming space_group_name @a type
int
get_space_group_number
(
std
::
string_view
spacegroup
,
space_group_name
type
);
int
get_space_group_number
(
std
::
string_view
spacegroup
,
space_group_name
type
);
/**
* @brief class to encapsulate the list of transformations making up a spacegroup
*
*/
class
spacegroup
:
public
std
::
vector
<
transformation
>
class
spacegroup
:
public
std
::
vector
<
transformation
>
{
{
public
:
public
:
/// \brief constructor using the information in the *symmetry* category in datablock @a db
spacegroup
(
const
datablock
&
db
)
spacegroup
(
const
datablock
&
db
)
:
spacegroup
(
get_space_group_number
(
db
))
:
spacegroup
(
get_space_group_number
(
db
))
{
{
}
}
/// \brief constructor using the spacegroup named @a name
spacegroup
(
std
::
string_view
name
)
spacegroup
(
std
::
string_view
name
)
:
spacegroup
(
get_space_group_number
(
name
))
:
spacegroup
(
get_space_group_number
(
name
))
{
{
}
}
/// \brief constructor using the spacegroup named @a name assuming space_group_name @a type
spacegroup
(
std
::
string_view
name
,
space_group_name
type
)
spacegroup
(
std
::
string_view
name
,
space_group_name
type
)
:
spacegroup
(
get_space_group_number
(
name
,
type
))
:
spacegroup
(
get_space_group_number
(
name
,
type
))
{
{
}
}
/// \brief constructor using the spacegroup number @a nr
spacegroup
(
int
nr
);
spacegroup
(
int
nr
);
int
get_nr
()
const
{
return
m_nr
;
}
int
get_nr
()
const
{
return
m_nr
;
}
///< Return the nr
std
::
string
get_name
()
const
;
std
::
string
get_name
()
const
;
///< Return the name
/** \brief perform a spacegroup operation on point @a pt using
* cell @a c and sym_op @a symop
*/
point
operator
()(
const
point
&
pt
,
const
cell
&
c
,
sym_op
symop
)
const
;
point
operator
()(
const
point
&
pt
,
const
cell
&
c
,
sym_op
symop
)
const
;
/** \brief perform an inverse spacegroup operation on point @a pt using
* cell @a c and sym_op @a symop
*/
point
inverse
(
const
point
&
pt
,
const
cell
&
c
,
sym_op
symop
)
const
;
point
inverse
(
const
point
&
pt
,
const
cell
&
c
,
sym_op
symop
)
const
;
private
:
private
:
...
@@ -362,42 +462,55 @@ class spacegroup : public std::vector<transformation>
...
@@ -362,42 +462,55 @@ class spacegroup : public std::vector<transformation>
};
};
// --------------------------------------------------------------------
// --------------------------------------------------------------------
// A crystal combines a cell and a spacegroup.
/**
* @brief A crystal combines a cell and a spacegroup.
*
* The information in cell and spacegroup together make up all
* information you need to do symmetry calculations in a crystal
*/
class
crystal
class
crystal
{
{
public
:
public
:
/// \brief constructor using the information found in datablock @a db
crystal
(
const
datablock
&
db
)
crystal
(
const
datablock
&
db
)
:
m_cell
(
db
)
:
m_cell
(
db
)
,
m_spacegroup
(
db
)
,
m_spacegroup
(
db
)
{
{
}
}
/// \brief constructor using cell @a c and spacegroup @a sg
crystal
(
const
cell
&
c
,
const
spacegroup
&
sg
)
crystal
(
const
cell
&
c
,
const
spacegroup
&
sg
)
:
m_cell
(
c
)
:
m_cell
(
c
)
,
m_spacegroup
(
sg
)
,
m_spacegroup
(
sg
)
{
{
}
}
/** @cond */
crystal
(
const
crystal
&
)
=
default
;
crystal
(
const
crystal
&
)
=
default
;
crystal
(
crystal
&&
)
=
default
;
crystal
(
crystal
&&
)
=
default
;
crystal
&
operator
=
(
const
crystal
&
)
=
default
;
crystal
&
operator
=
(
const
crystal
&
)
=
default
;
crystal
&
operator
=
(
crystal
&&
)
=
default
;
crystal
&
operator
=
(
crystal
&&
)
=
default
;
/** @endcond */
const
cell
&
get_cell
()
const
{
return
m_cell
;
}
const
cell
&
get_cell
()
const
{
return
m_cell
;
}
///< Return the cell
const
spacegroup
&
get_spacegroup
()
const
{
return
m_spacegroup
;
}
const
spacegroup
&
get_spacegroup
()
const
{
return
m_spacegroup
;
}
///< Return the spacegroup
/// \brief Return the symmetry copy of point @a pt using symmetry operation @a symop
point
symmetry_copy
(
const
point
&
pt
,
sym_op
symop
)
const
point
symmetry_copy
(
const
point
&
pt
,
sym_op
symop
)
const
{
{
return
m_spacegroup
(
pt
,
m_cell
,
symop
);
return
m_spacegroup
(
pt
,
m_cell
,
symop
);
}
}
/// \brief Return the symmetry copy of point @a pt using the inverse of symmetry operation @a symop
point
inverse_symmetry_copy
(
const
point
&
pt
,
sym_op
symop
)
const
point
inverse_symmetry_copy
(
const
point
&
pt
,
sym_op
symop
)
const
{
{
return
m_spacegroup
.
inverse
(
pt
,
m_cell
,
symop
);
return
m_spacegroup
.
inverse
(
pt
,
m_cell
,
symop
);
}
}
std
::
tuple
<
float
,
point
,
sym_op
>
closest_symmetry_copy
(
point
a
,
point
b
)
const
;
/// \brief Return a tuple consisting of distance, new location and symmetry operation
/// for the point @a b with respect to point @a a.
std
::
tuple
<
float
,
point
,
sym_op
>
closest_symmetry_copy
(
point
a
,
point
b
)
const
;
private
:
private
:
cell
m_cell
;
cell
m_cell
;
...
@@ -407,11 +520,13 @@ class crystal
...
@@ -407,11 +520,13 @@ class crystal
// --------------------------------------------------------------------
// --------------------------------------------------------------------
// Symmetry operations on points
// Symmetry operations on points
/// \brief convenience function returning the fractional point @a pt in orthogonal coordinates for cell @a c
inline
point
orthogonal
(
const
point
&
pt
,
const
cell
&
c
)
inline
point
orthogonal
(
const
point
&
pt
,
const
cell
&
c
)
{
{
return
c
.
get_orthogonal_matrix
()
*
pt
;
return
c
.
get_orthogonal_matrix
()
*
pt
;
}
}
/// \brief convenience function returning the orthogonal point @a pt in fractional coordinates for cell @a c
inline
point
fractional
(
const
point
&
pt
,
const
cell
&
c
)
inline
point
fractional
(
const
point
&
pt
,
const
cell
&
c
)
{
{
return
c
.
get_fractional_matrix
()
*
pt
;
return
c
.
get_fractional_matrix
()
*
pt
;
...
...
include/cif++/text.hpp
View file @
e84282cb
...
@@ -44,6 +44,12 @@
...
@@ -44,6 +44,12 @@
#include <zeep/type-traits.hpp>
#include <zeep/type-traits.hpp>
#endif
#endif
/**
* \file text.hpp
*
* Various text manipulating routines
*/
namespace
cif
namespace
cif
{
{
...
@@ -52,18 +58,40 @@ namespace cif
...
@@ -52,18 +58,40 @@ namespace cif
// some basic utilities: Since we're using ASCII input only, we define for optimisation
// some basic utilities: Since we're using ASCII input only, we define for optimisation
// our own case conversion routines.
// our own case conversion routines.
/// \brief return whether string @a is equal to string @a b ignoring changes in character case
bool
iequals
(
std
::
string_view
a
,
std
::
string_view
b
);
bool
iequals
(
std
::
string_view
a
,
std
::
string_view
b
);
/// \brief compare string @a is to string @a b ignoring changes in character case
int
icompare
(
std
::
string_view
a
,
std
::
string_view
b
);
int
icompare
(
std
::
string_view
a
,
std
::
string_view
b
);
/// \brief return whether string @a is equal to string @a b ignoring changes in character case
bool
iequals
(
const
char
*
a
,
const
char
*
b
);
bool
iequals
(
const
char
*
a
,
const
char
*
b
);
/// \brief compare string @a is to string @a b ignoring changes in character case
int
icompare
(
const
char
*
a
,
const
char
*
b
);
int
icompare
(
const
char
*
a
,
const
char
*
b
);
/// \brief convert the string @a s to lower case in situ
void
to_lower
(
std
::
string
&
s
);
void
to_lower
(
std
::
string
&
s
);
/// \brief return a lower case copy of string @a s
std
::
string
to_lower_copy
(
std
::
string_view
s
);
std
::
string
to_lower_copy
(
std
::
string_view
s
);
/// \brief convert the string @a s to upper case in situ
void
to_upper
(
std
::
string
&
s
);
void
to_upper
(
std
::
string
&
s
);
// std::string toUpperCopy(const std::string &s);
/**
* @brief Join the strings in the range [ @a a, @a e ) using
* @a sep as separator
*
* Example usage:
*
* @code {.cpp}
* std::vector<std::string> v{ "aap", "noot", "mies" };
*
* assert(cif::join(v.begin(), v.end(), ", ") == "aap, noot, mies");
* @endcode
*
*/
template
<
typename
IterType
>
template
<
typename
IterType
>
std
::
string
join
(
IterType
b
,
IterType
e
,
std
::
string_view
sep
)
std
::
string
join
(
IterType
b
,
IterType
e
,
std
::
string_view
sep
)
{
{
...
@@ -91,12 +119,41 @@ std::string join(IterType b, IterType e, std::string_view sep)
...
@@ -91,12 +119,41 @@ std::string join(IterType b, IterType e, std::string_view sep)
return
s
.
str
();
return
s
.
str
();
}
}
/**
* @brief Join the strings in the array @a arr using @a sep as separator
*
* Example usage:
*
* @code {.cpp}
* std::list<std::string> v{ "aap", "noot", "mies" };
*
* assert(cif::join(v, ", ") == "aap, noot, mies");
* @endcode
*
*/
template
<
typename
V
>
template
<
typename
V
>
std
::
string
join
(
const
V
&
arr
,
std
::
string_view
sep
)
std
::
string
join
(
const
V
&
arr
,
std
::
string_view
sep
)
{
{
return
join
(
arr
.
begin
(),
arr
.
end
(),
sep
);
return
join
(
arr
.
begin
(),
arr
.
end
(),
sep
);
}
}
/**
* @brief Split the string in @a s based on the characters in @a separators
*
* Each of the characters in @a separators induces a split.
*
* When suppress_empty is true, empty strings are not produced in the
* resulting array.
*
* Example:
*
* @code {.cpp}
* auto v = cif::split("aap:noot,,mies", ":,", true);
*
* assert(v == std::vector{"aap", "noot", "mies"});
* @endcode
*
*/
template
<
typename
StringType
=
std
::
string_view
>
template
<
typename
StringType
=
std
::
string_view
>
std
::
vector
<
StringType
>
split
(
std
::
string_view
s
,
std
::
string_view
separators
,
bool
suppress_empty
=
false
)
std
::
vector
<
StringType
>
split
(
std
::
string_view
s
,
std
::
string_view
separators
,
bool
suppress_empty
=
false
)
{
{
...
@@ -124,15 +181,23 @@ std::vector<StringType> split(std::string_view s, std::string_view separators, b
...
@@ -124,15 +181,23 @@ std::vector<StringType> split(std::string_view s, std::string_view separators, b
return
result
;
return
result
;
}
}
/**
* @brief Replace all occurrences of @a what in string @a s with the string @a with
*
* The string @a with may be empty in which case each occurrence of @a what is simply
* deleted.
*/
void
replace_all
(
std
::
string
&
s
,
std
::
string_view
what
,
std
::
string_view
with
=
{});
void
replace_all
(
std
::
string
&
s
,
std
::
string_view
what
,
std
::
string_view
with
=
{});
#if defined(__cpp_lib_starts_ends_with)
#if defined(__cpp_lib_starts_ends_with)
/// \brief return whether string @a s starts with @a with
inline
bool
starts_with
(
std
::
string
s
,
std
::
string_view
with
)
inline
bool
starts_with
(
std
::
string
s
,
std
::
string_view
with
)
{
{
return
s
.
starts_with
(
with
);
return
s
.
starts_with
(
with
);
}
}
/// \brief return whether string @a s ends with @a with
inline
bool
ends_with
(
std
::
string_view
s
,
std
::
string_view
with
)
inline
bool
ends_with
(
std
::
string_view
s
,
std
::
string_view
with
)
{
{
return
s
.
ends_with
(
with
);
return
s
.
ends_with
(
with
);
...
@@ -140,11 +205,13 @@ inline bool ends_with(std::string_view s, std::string_view with)
...
@@ -140,11 +205,13 @@ inline bool ends_with(std::string_view s, std::string_view with)
#else
#else
/// \brief return whether string @a s starts with @a with
inline
bool
starts_with
(
std
::
string
s
,
std
::
string_view
with
)
inline
bool
starts_with
(
std
::
string
s
,
std
::
string_view
with
)
{
{
return
s
.
compare
(
0
,
with
.
length
(),
with
)
==
0
;
return
s
.
compare
(
0
,
with
.
length
(),
with
)
==
0
;
}
}
/// \brief return whether string @a s ends with @a with
inline
bool
ends_with
(
std
::
string_view
s
,
std
::
string_view
with
)
inline
bool
ends_with
(
std
::
string_view
s
,
std
::
string_view
with
)
{
{
return
s
.
length
()
>=
with
.
length
()
and
s
.
compare
(
s
.
length
()
-
with
.
length
(),
with
.
length
(),
with
)
==
0
;
return
s
.
length
()
>=
with
.
length
()
and
s
.
compare
(
s
.
length
()
-
with
.
length
(),
with
.
length
(),
with
)
==
0
;
...
@@ -154,6 +221,7 @@ inline bool ends_with(std::string_view s, std::string_view with)
...
@@ -154,6 +221,7 @@ inline bool ends_with(std::string_view s, std::string_view with)
#if defined(__cpp_lib_string_contains)
#if defined(__cpp_lib_string_contains)
/// \brief return whether string @a s contains @a q
inline
bool
contains
(
std
::
string_view
s
,
std
::
string_view
q
)
inline
bool
contains
(
std
::
string_view
s
,
std
::
string_view
q
)
{
{
return
s
.
contains
(
q
);
return
s
.
contains
(
q
);
...
@@ -161,6 +229,7 @@ inline bool contains(std::string_view s, std::string_view q)
...
@@ -161,6 +229,7 @@ inline bool contains(std::string_view s, std::string_view q)
#else
#else
/// \brief return whether string @a s contains @a q
inline
bool
contains
(
std
::
string_view
s
,
std
::
string_view
q
)
inline
bool
contains
(
std
::
string_view
s
,
std
::
string_view
q
)
{
{
return
s
.
find
(
q
)
!=
std
::
string_view
::
npos
;
return
s
.
find
(
q
)
!=
std
::
string_view
::
npos
;
...
@@ -168,20 +237,33 @@ inline bool contains(std::string_view s, std::string_view q)
...
@@ -168,20 +237,33 @@ inline bool contains(std::string_view s, std::string_view q)
#endif
#endif
/// \brief return whether string @a s contains @a q ignoring character case
bool
icontains
(
std
::
string_view
s
,
std
::
string_view
q
);
bool
icontains
(
std
::
string_view
s
,
std
::
string_view
q
);
/// \brief trim white space at the start of string @a s in situ
void
trim_left
(
std
::
string
&
s
);
void
trim_left
(
std
::
string
&
s
);
/// \brief trim white space at the end of string @a s in situ
void
trim_right
(
std
::
string
&
s
);
void
trim_right
(
std
::
string
&
s
);
/// \brief trim white space at both the start and the end of string @a s in situ
void
trim
(
std
::
string
&
s
);
void
trim
(
std
::
string
&
s
);
/// \brief return a string trimmed of white space at the start of string @a s
std
::
string
trim_left_copy
(
std
::
string_view
s
);
std
::
string
trim_left_copy
(
std
::
string_view
s
);
/// \brief return a string trimmed of white space at the end of string @a s
std
::
string
trim_right_copy
(
std
::
string_view
s
);
std
::
string
trim_right_copy
(
std
::
string_view
s
);
/// \brief return a string trimmed of white space at both the start and the end of string @a s
std
::
string
trim_copy
(
std
::
string_view
s
);
std
::
string
trim_copy
(
std
::
string_view
s
);
// To make life easier, we also define iless and iset using iequals
// To make life easier, we also define iless and iset using iequals
/// \brief an operator object you can use to compare strings ignoring their character case
struct
iless
struct
iless
{
{
/// \brief return the result of icompare for @a a and @a b
bool
operator
()(
const
std
::
string
&
a
,
const
std
::
string
&
b
)
const
bool
operator
()(
const
std
::
string
&
a
,
const
std
::
string
&
b
)
const
{
{
return
icompare
(
a
,
b
)
<
0
;
return
icompare
(
a
,
b
)
<
0
;
...
@@ -196,8 +278,10 @@ using iset = std::set<std::string, iless>;
...
@@ -196,8 +278,10 @@ using iset = std::set<std::string, iless>;
// --------------------------------------------------------------------
// --------------------------------------------------------------------
// This really makes a difference, having our own tolower routines
// This really makes a difference, having our own tolower routines
/// \brief global list containing the lower case version of each ASCII character
extern
CIFPP_EXPORT
const
uint8_t
kCharToLowerMap
[
256
];
extern
CIFPP_EXPORT
const
uint8_t
kCharToLowerMap
[
256
];
/// \brief a very fast tolower implementation
inline
char
tolower
(
int
ch
)
inline
char
tolower
(
int
ch
)
{
{
return
static_cast
<
char
>
(
kCharToLowerMap
[
static_cast
<
uint8_t
>
(
ch
)]);
return
static_cast
<
char
>
(
kCharToLowerMap
[
static_cast
<
uint8_t
>
(
ch
)]);
...
@@ -205,22 +289,37 @@ inline char tolower(int ch)
...
@@ -205,22 +289,37 @@ inline char tolower(int ch)
// --------------------------------------------------------------------
// --------------------------------------------------------------------
/** \brief return a tuple consisting of the category and item name for @a tag
*
* The category name is stripped of its leading underscore character.
*
* If no dot character was found, the category name is empty. That's for
* cif 1.0 formatted data.
*/
std
::
tuple
<
std
::
string
,
std
::
string
>
split_tag_name
(
std
::
string_view
tag
);
std
::
tuple
<
std
::
string
,
std
::
string
>
split_tag_name
(
std
::
string_view
tag
);
// --------------------------------------------------------------------
// --------------------------------------------------------------------
// generate a cif name, mainly used to generate asym_id's
/// \brief generate a cif name, used e.g. to generate asym_id's
std
::
string
cif_id_for_number
(
int
number
);
std
::
string
cif_id_for_number
(
int
number
);
// --------------------------------------------------------------------
// --------------------------------------------------------------------
// custom wordwrapping routine
/** \brief custom word wrapping routine.
*
* Wrap the text in @a text based on a maximum line width @a width using
* a dynamic programming approach to get the most efficient filling of
* the space.
*/
std
::
vector
<
std
::
string
>
word_wrap
(
const
std
::
string
&
text
,
size_t
width
);
std
::
vector
<
std
::
string
>
word_wrap
(
const
std
::
string
&
text
,
size_t
width
);
// --------------------------------------------------------------------
// --------------------------------------------------------------------
/// std::from_chars for floating point types.
/// \brief std::from_chars for floating point types.
///
/// These are optional, there's a selected_charconv class below that selects
/// These are optional, there's a selected_charconv class below that selects
/// the best option to used based on support by the stl library
/// the best option to use based on support by the stl library.
///
/// I.e. that in case of GNU < 12 (or something) the cif implementation will
/// I.e. that in case of GNU < 12 (or something) the cif implementation will
/// be used, all other cases will use the stl version.
/// be used, all other cases will use the stl version.
...
@@ -345,6 +444,7 @@ std::from_chars_result from_chars(const char *first, const char *last, FloatType
...
@@ -345,6 +444,7 @@ std::from_chars_result from_chars(const char *first, const char *last, FloatType
return
result
;
return
result
;
}
}
/// \brief duplication of std::chars_format for deficient STL implementations
enum
class
chars_format
enum
class
chars_format
{
{
scientific
=
1
,
scientific
=
1
,
...
@@ -353,6 +453,7 @@ enum class chars_format
...
@@ -353,6 +453,7 @@ enum class chars_format
general
=
fixed
|
scientific
general
=
fixed
|
scientific
};
};
/// \brief a simplistic implementation of std::to_chars for old STL implementations
template
<
typename
FloatType
,
std
::
enable_if_t
<
std
::
is_floating_point_v
<
FloatType
>
,
int
>
=
0
>
template
<
typename
FloatType
,
std
::
enable_if_t
<
std
::
is_floating_point_v
<
FloatType
>
,
int
>
=
0
>
std
::
to_chars_result
to_chars
(
char
*
first
,
char
*
last
,
FloatType
&
value
,
chars_format
fmt
)
std
::
to_chars_result
to_chars
(
char
*
first
,
char
*
last
,
FloatType
&
value
,
chars_format
fmt
)
{
{
...
@@ -392,6 +493,7 @@ std::to_chars_result to_chars(char *first, char *last, FloatType &value, chars_f
...
@@ -392,6 +493,7 @@ std::to_chars_result to_chars(char *first, char *last, FloatType &value, chars_f
return
result
;
return
result
;
}
}
/// \brief a simplistic implementation of std::to_chars for old STL implementations
template
<
typename
FloatType
,
std
::
enable_if_t
<
std
::
is_floating_point_v
<
FloatType
>
,
int
>
=
0
>
template
<
typename
FloatType
,
std
::
enable_if_t
<
std
::
is_floating_point_v
<
FloatType
>
,
int
>
=
0
>
std
::
to_chars_result
to_chars
(
char
*
first
,
char
*
last
,
FloatType
&
value
,
chars_format
fmt
,
int
precision
)
std
::
to_chars_result
to_chars
(
char
*
first
,
char
*
last
,
FloatType
&
value
,
chars_format
fmt
,
int
precision
)
{
{
...
@@ -431,6 +533,7 @@ std::to_chars_result to_chars(char *first, char *last, FloatType &value, chars_f
...
@@ -431,6 +533,7 @@ std::to_chars_result to_chars(char *first, char *last, FloatType &value, chars_f
return
result
;
return
result
;
}
}
/// \brief class that uses our implementation of std::from_chars and std::to_chars
template
<
typename
T
>
template
<
typename
T
>
struct
my_charconv
struct
my_charconv
{
{
...
@@ -445,6 +548,7 @@ struct my_charconv
...
@@ -445,6 +548,7 @@ struct my_charconv
}
}
};
};
/// \brief class that uses the STL implementation of std::from_chars and std::to_chars
template
<
typename
T
>
template
<
typename
T
>
struct
std_charconv
struct
std_charconv
{
{
...
@@ -459,9 +563,16 @@ struct std_charconv
...
@@ -459,9 +563,16 @@ struct std_charconv
}
}
};
};
/// \brief helper to find a from_chars function
template
<
typename
T
>
template
<
typename
T
>
using
from_chars_function
=
decltype
(
std
::
from_chars
(
std
::
declval
<
const
char
*>
(),
std
::
declval
<
const
char
*>
(),
std
::
declval
<
T
&>
()));
using
from_chars_function
=
decltype
(
std
::
from_chars
(
std
::
declval
<
const
char
*>
(),
std
::
declval
<
const
char
*>
(),
std
::
declval
<
T
&>
()));
/**
* @brief Helper to select the best implementation of charconv based on availability of the
* function in the std:: namespace
*
* @tparam T The type for which we want to find a from_chars/to_chars function
*/
template
<
typename
T
>
template
<
typename
T
>
using
selected_charconv
=
typename
std
::
conditional_t
<
std
::
experimental
::
is_detected_v
<
from_chars_function
,
T
>
,
std_charconv
<
T
>
,
my_charconv
<
T
>>
;
using
selected_charconv
=
typename
std
::
conditional_t
<
std
::
experimental
::
is_detected_v
<
from_chars_function
,
T
>
,
std_charconv
<
T
>
,
my_charconv
<
T
>>
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment