├── .gitignore ├── test_data ├── fcc_nbrs.dat ├── FeCu_nbrs.dat ├── fcc_positions.dat ├── graphene_nbrs.dat ├── graphene_pos.dat └── FeCu_positions.dat ├── datagen ├── formatting_string.txt ├── todo.txt ├── graphene.py ├── square_facets.py ├── planar_graphs.py ├── wb.py ├── fundamental_mappings.py ├── quat_utils.py ├── formatter.py ├── analysis.py ├── graph_gen.py ├── generators.py ├── graph_tools.py └── renderscript_diamond_structures.py ├── LICENSE ├── README.md ├── unittest.hpp ├── ptm_alloy_types.h ├── ptm_neighbour_ordering.h ├── compute_ptm_atom.h ├── ptm_normalize_vertices.h ├── ptm_canonical_coloured.h ├── ptm_graph_tools.h ├── ptm_polar.h ├── ptm_multishell.h ├── ptm_convex_hull_incremental.h ├── ptm_structure_matcher.h ├── ptm_graph_data.h ├── Makefile ├── ptm_deformation_gradient.cpp ├── ptm_functions.h ├── ptm_graph_tools.cpp ├── ptm_normalize_vertices.cpp ├── ptm_initialize_data.cpp ├── ptm_multishell.cpp ├── ptm_alloy_types.cpp ├── main.cpp ├── ptm_canonical_coloured.cpp ├── ptm_voronoi_config.h ├── ptm_neighbour_ordering.cpp ├── compute_ptm_atom.cpp ├── ptm_initialize_data.h ├── ptm_quat.h ├── ptm_alt_templates.h └── ptm_polar.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.pyc 3 | x86_64 4 | i686 5 | *.so 6 | *.a 7 | -------------------------------------------------------------------------------- /test_data/fcc_nbrs.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmla/polyhedral-template-matching/HEAD/test_data/fcc_nbrs.dat -------------------------------------------------------------------------------- /test_data/FeCu_nbrs.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmla/polyhedral-template-matching/HEAD/test_data/FeCu_nbrs.dat -------------------------------------------------------------------------------- /test_data/fcc_positions.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmla/polyhedral-template-matching/HEAD/test_data/fcc_positions.dat -------------------------------------------------------------------------------- /test_data/graphene_nbrs.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmla/polyhedral-template-matching/HEAD/test_data/graphene_nbrs.dat -------------------------------------------------------------------------------- /test_data/graphene_pos.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmla/polyhedral-template-matching/HEAD/test_data/graphene_pos.dat -------------------------------------------------------------------------------- /test_data/FeCu_positions.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmla/polyhedral-template-matching/HEAD/test_data/FeCu_positions.dat -------------------------------------------------------------------------------- /datagen/formatting_string.txt: -------------------------------------------------------------------------------- 1 | clang-format -style='{BasedOnStyle: LLVM, UseTab: ForIndentation, TabWidth: 1, IndentWidth: 1}' ptm_index.cpp 2 | -------------------------------------------------------------------------------- /datagen/todo.txt: -------------------------------------------------------------------------------- 1 | update ovito merge request 2 | update ovito documentation 3 | update LAMMPS 4 | update LAMMPS documentation 5 | make LAMMPS example 6 | 7 | add elemental numbers to ovito neighbourlist function 8 | update python interface for OVITO 9 | better testing framework 10 | 11 | clang format all code 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 PM Larsen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # polyhedral-template-matching 2 | Polyhedral Template Matching algorithm for analysis of molecular dynamics simulation data 3 | 4 | The method is now included in OVITO, LAMMPS, and ASAP. 5 | 6 | OVITO PTM documentation: https://www.ovito.org/docs/current/particles.modifiers.polyhedral_template_matching.php 7 | 8 | LAMMPS PTM documentation: https://lammps.sandia.gov/doc/compute_ptm_atom.html 9 | 10 | ASAP PTM documentation: https://wiki.fysik.dtu.dk/asap/Local%20crystalline%20order 11 | 12 | 13 | 14 | If you use PTM in a publication, please cite: 15 | 16 | Peter Mahler Larsen and Søren Schmidt and Jakob Schiøtz; 17 | "Robust structural identification via polyhedral template matching"; 18 | Modelling Simul. Mater. Sci. Eng. 24 (2016) 055007 19 | 20 | Article available at: 21 | https://doi.org/10.1088/0965-0393/24/5/055007 22 | 23 | Preprint available at: 24 | http://arxiv.org/abs/1603.05143 25 | 26 | 27 | Send me an email if you would like help integrating PTM into your framework. My email address is [firstname].[middlename].[lastname]@gmail.com 28 | -------------------------------------------------------------------------------- /unittest.hpp: -------------------------------------------------------------------------------- 1 | /*Copyright (c) 2016 PM Larsen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | */ 9 | 10 | #ifndef UNITTEST_HPP 11 | #define UNITTEST_HPP 12 | 13 | namespace ptm { 14 | 15 | uint64_t run_tests(); 16 | 17 | } 18 | 19 | #endif 20 | 21 | 22 | -------------------------------------------------------------------------------- /ptm_alloy_types.h: -------------------------------------------------------------------------------- 1 | /*Copyright (c) 2016 PM Larsen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | */ 9 | 10 | #ifndef PTM_ALLOY_TYPES_H 11 | #define PTM_ALLOY_TYPES_H 12 | 13 | #include "ptm_initialize_data.h" 14 | 15 | namespace ptm { 16 | 17 | int32_t find_alloy_type(const refdata_t* ref, int8_t* mapping, int32_t* numbers); 18 | 19 | } 20 | 21 | #endif 22 | 23 | -------------------------------------------------------------------------------- /ptm_neighbour_ordering.h: -------------------------------------------------------------------------------- 1 | /*Copyright (c) 2016 PM Larsen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | */ 9 | 10 | #ifndef PTM_NEIGHBOUR_ORDERING_H 11 | #define PTM_NEIGHBOUR_ORDERING_H 12 | 13 | #include 14 | 15 | namespace ptm { 16 | 17 | void* voronoi_initialize_local(); 18 | void voronoi_uninitialize_local(void* ptr); 19 | 20 | } 21 | 22 | #endif 23 | 24 | -------------------------------------------------------------------------------- /compute_ptm_atom.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- ---------------------------------------------------------- 2 | LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator 3 | http://lammps.sandia.gov, Sandia National Laboratories 4 | Steve Plimpton, sjplimp@sandia.gov 5 | 6 | Copyright (2003) Sandia Corporation. Under the terms of Contract 7 | DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains 8 | certain rights in this software. This software is distributed under 9 | the GNU General Public License. 10 | 11 | See the README file in the top-level LAMMPS directory. 12 | ------------------------------------------------------------------------- */ 13 | 14 | #ifdef COMPUTE_CLASS 15 | 16 | ComputeStyle(ptm/atom,ComputePTMAtom) 17 | 18 | #else 19 | 20 | #ifndef LMP_COMPUTE_PTM_ATOM_H 21 | #define LMP_COMPUTE_PTM_ATOM_H 22 | 23 | #include "compute.h" 24 | 25 | namespace LAMMPS_NS { 26 | 27 | class ComputePTMAtom : public Compute { 28 | public: 29 | ComputePTMAtom(class LAMMPS *, int, char **); 30 | ~ComputePTMAtom(); 31 | void init(); 32 | void init_list(int, class NeighList *); 33 | void compute_peratom(); 34 | double memory_usage(); 35 | 36 | private: 37 | int nmax; 38 | int32_t input_flags; 39 | double rmsd_threshold; 40 | class NeighList *list; 41 | double **output; 42 | }; 43 | 44 | } 45 | 46 | #endif 47 | #endif 48 | 49 | -------------------------------------------------------------------------------- /ptm_normalize_vertices.h: -------------------------------------------------------------------------------- 1 | /*Copyright (c) 2016 PM Larsen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | */ 9 | 10 | #ifndef PTM_NORMALIZE_VERTICES_H 11 | #define PTM_NORMALIZE_VERTICES_H 12 | 13 | namespace ptm { 14 | 15 | void subtract_barycentre(int num, double (*points)[3], double (*normalized)[3]); 16 | double normalize_vertices(int num, double (*points)[3], double (*normalized)[3]); 17 | 18 | } 19 | 20 | #endif 21 | 22 | -------------------------------------------------------------------------------- /ptm_canonical_coloured.h: -------------------------------------------------------------------------------- 1 | /*Copyright (c) 2016 PM Larsen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | */ 9 | 10 | #ifndef PTM_CANONICAL_COLOURED_H 11 | #define PTM_CANONICAL_COLOURED_H 12 | 13 | #include 14 | 15 | namespace ptm { 16 | 17 | int canonical_form_coloured(int num_facets, int8_t facets[][3], int num_nodes, int8_t* degree, int8_t* colours, int8_t* canonical_labelling, int8_t* best_code, uint64_t* p_hash); 18 | } 19 | 20 | #endif 21 | 22 | -------------------------------------------------------------------------------- /ptm_graph_tools.h: -------------------------------------------------------------------------------- 1 | /*Copyright (c) 2016 PM Larsen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | */ 9 | 10 | #ifndef PTM_GRAPH_TOOLS_H 11 | #define PTM_GRAPH_TOOLS_H 12 | 13 | #include 14 | #include "ptm_constants.h" 15 | 16 | namespace ptm { 17 | 18 | bool build_facet_map(int num_facets, int8_t facets[][3], int8_t common[PTM_MAX_NBRS][PTM_MAX_NBRS]); 19 | int graph_degree(int num_facets, int8_t facets[][3], int num_nodes, int8_t* degree); 20 | 21 | } 22 | 23 | #endif 24 | 25 | -------------------------------------------------------------------------------- /ptm_polar.h: -------------------------------------------------------------------------------- 1 | /*Copyright (c) 2016 PM Larsen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | */ 9 | 10 | #ifndef PTM_POLAR_H 11 | #define PTM_POLAR_H 12 | 13 | #include 14 | #include 15 | 16 | namespace ptm { 17 | 18 | int polar_decomposition_3x3(double* _A, bool right_sided, double* U, double* P); 19 | void InnerProduct(double *A, int num, const double (*coords1)[3], double (*coords2)[3], int8_t* permutation); 20 | int FastCalcRMSDAndRotation(double *A, double E0, double *p_nrmsdsq, double *q, double* U); 21 | 22 | } 23 | 24 | #endif 25 | 26 | -------------------------------------------------------------------------------- /datagen/graphene.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from sympy import * 3 | import matplotlib.pyplot as plt 4 | 5 | def gen_template(): 6 | 7 | first = [(np.sin(i * 2 * np.pi / 3), np.cos(i * 2 * np.pi / 3), 0) for i in range(3)] 8 | first = np.array(first) 9 | second = [] 10 | for a in first: 11 | for d in first: 12 | b = a + (d[0], -d[1], d[2]) 13 | if np.linalg.norm(b) > 1E-3: 14 | second += [b] 15 | angles = [np.arctan2(y, x) for x, y, _ in second] 16 | indices = np.argsort(angles)[::-1] 17 | indices = np.roll(indices, -1) 18 | second = np.array(second)[indices] 19 | 20 | template = [(0, 0, 0)] + list(first) + list(second) 21 | template = np.array(template) 22 | 23 | if 1: 24 | xs, ys, _ = zip(*template) 25 | plt.scatter(xs, ys) 26 | for i, (x, y) in enumerate(zip(xs, ys)): 27 | plt.text(x, y, str(i)) 28 | plt.show() 29 | return 30 | 31 | #print template 32 | scale = np.mean(np.linalg.norm(template[1:], axis=1)) 33 | #print scale 34 | scale = (3 + 6 * sqrt(3)) / 9 35 | #print scale.evalf() 36 | #print scale 37 | 38 | 39 | sym = Matrix([ 40 | [ 0, 0, 0], 41 | [ 0, 1, 0], 42 | [ sqrt(3)/2, -sqrt(1)/2, 0], 43 | [-sqrt(3)/2, -sqrt(1)/2, 0], 44 | [-sqrt(3)/2, 3*sqrt(1)/2, 0], 45 | [ sqrt(3)/2, 3*sqrt(1)/2, 0], 46 | [ sqrt(3), 0, 0], 47 | [ sqrt(3)/2, -3*sqrt(1)/2, 0], 48 | [-sqrt(3)/2, -3*sqrt(1)/2, 0], 49 | [ -sqrt(3), 0, 0]]) 50 | sym = sym / scale 51 | 52 | for i in range(len(sym)): 53 | sym[i] = sym[i].expand().simplify() 54 | print sym 55 | 56 | 57 | #return template / scale 58 | 59 | gen_template() 60 | -------------------------------------------------------------------------------- /ptm_multishell.h: -------------------------------------------------------------------------------- 1 | /*Copyright (c) 2016 PM Larsen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | */ 9 | 10 | #ifndef PTM_MULTISHELL_H 11 | #define PTM_MULTISHELL_H 12 | 13 | #include 14 | 15 | namespace ptm { 16 | 17 | typedef struct 18 | { 19 | int ordering[PTM_MAX_INPUT_POINTS]; 20 | size_t nbr_indices[PTM_MAX_INPUT_POINTS]; 21 | int32_t numbers[PTM_MAX_INPUT_POINTS]; 22 | double points[PTM_MAX_INPUT_POINTS][3]; 23 | } atomicenv_t; 24 | 25 | 26 | int calculate_two_shell_neighbour_ordering( int num_inner, int num_outer, 27 | size_t atom_index, int (get_neighbours)(void* vdata, size_t _unused_lammps_variable, size_t atom_index, int num, int* ordering, size_t* nbr_indices, int32_t* numbers, double (*nbr_pos)[3]), void* nbrlist, 28 | ptm::atomicenv_t* output); 29 | } 30 | 31 | #endif 32 | 33 | -------------------------------------------------------------------------------- /ptm_convex_hull_incremental.h: -------------------------------------------------------------------------------- 1 | /*Copyright (c) 2016 PM Larsen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | */ 9 | 10 | #ifndef PTM_CONVEX_HULL_INCREMENTAL_H 11 | #define PTM_CONVEX_HULL_INCREMENTAL_H 12 | 13 | 14 | #include 15 | #include 16 | #include "ptm_constants.h" 17 | 18 | namespace ptm { 19 | 20 | typedef struct 21 | { 22 | int8_t facets[PTM_MAX_FACETS][3]; 23 | double plane_normal[PTM_MAX_FACETS][3]; 24 | bool processed[PTM_MAX_POINTS]; 25 | int initial_vertices[4]; 26 | double barycentre[3]; 27 | int num_facets; 28 | int num_prev; 29 | bool ok; 30 | 31 | } convexhull_t; 32 | 33 | void add_facet(const double (*points)[3], int a, int b, int c, int8_t* facet, double* plane_normal, double* barycentre); 34 | int get_convex_hull(int num_points, const double (*points)[3], convexhull_t* ch, int8_t simplex[][3]); 35 | 36 | } 37 | 38 | #endif 39 | 40 | -------------------------------------------------------------------------------- /datagen/square_facets.py: -------------------------------------------------------------------------------- 1 | import math 2 | import numpy as np 3 | import itertools 4 | import scipy.spatial 5 | 6 | 7 | def get_square_facet_components(points): 8 | 9 | facets = np.sort(scipy.spatial.ConvexHull(points).simplices) 10 | 11 | square = [] 12 | for i, f in enumerate(facets): 13 | p = points[f] 14 | v = p[1:] - p[0] 15 | v = [e / np.linalg.norm(e) for e in v] 16 | dot = np.dot(*v) 17 | dot = min(1, max(-1, dot)) 18 | angle = np.arccos(dot) 19 | if abs(math.degrees(angle) - 60) > 1E-3: 20 | square += [i] 21 | 22 | return facets[square] 23 | 24 | def get_long_edges(points, simplices): 25 | 26 | orientated = [] 27 | for s in simplices: 28 | 29 | best = (0, -1) 30 | for i in range(3): 31 | r = np.roll(s, i) 32 | p = points[r] 33 | d = np.linalg.norm(p[0] - p[1]) 34 | best = max(best, (d, tuple(r))) 35 | _, r = best 36 | orientated += [r] 37 | return np.array(orientated) 38 | 39 | def merge_triangles(orientated): 40 | 41 | d = dict() 42 | for t in orientated: 43 | key = tuple(sorted(t[:2])) 44 | if key not in d: 45 | d[key] = [] 46 | d[key] += [t] 47 | 48 | l = [] 49 | for k, v in d.items(): 50 | l += [np.unique(v)] 51 | return np.array(l) 52 | 53 | def order_squares(points, squares): 54 | 55 | cs = np.array(list(itertools.permutations(range(4)))) 56 | 57 | ordered = [] 58 | for s in squares: 59 | best = (float("inf"), None) 60 | for c in cs: 61 | p = points[s[c]] 62 | q = points[s[np.roll(c, 1)]] 63 | ds = np.linalg.norm(p - q, axis=1) 64 | m = max(np.abs(ds - ds[0])) 65 | best = min(best, (m, list(c), ds)) 66 | m, c, ds = best 67 | ordered += [s[c]] 68 | return np.array(ordered) 69 | 70 | def get_square_facets(points): 71 | 72 | simplices = get_square_facet_components(points) 73 | orientated = get_long_edges(points, simplices) 74 | 75 | squares = merge_triangles(orientated) 76 | ordered = order_squares(points, squares) 77 | return ordered 78 | 79 | -------------------------------------------------------------------------------- /datagen/planar_graphs.py: -------------------------------------------------------------------------------- 1 | import math 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | from mpl_toolkits.mplot3d import Axes3D 5 | from mpl_toolkits.mplot3d.art3d import Poly3DCollection 6 | 7 | 8 | def plot_points(points, colours=None, labels=None): 9 | 10 | fig = plt.figure()#figsize=(16,14)) 11 | fig.set_tight_layout(True) 12 | ax = fig.add_subplot(111, projection='3d', proj_type='ortho') 13 | 14 | if colours is not None: 15 | for (index, c) in zip([0, 1, 2, 3], ['C0', 'C1', 'C2', 'C3']): 16 | indices = np.where(colours == index)[0] 17 | (xs, ys, zs) = zip(*points[indices]) 18 | ax.scatter(xs, ys, zs, c=c) 19 | else: 20 | (xs, ys, zs) = zip(*points) 21 | ax.scatter(xs, ys, zs) 22 | #for i, e in enumerate(points): 23 | # c = 'rb'[i < 3] 24 | # plt.plot([0, e[0]], [0, e[1]], [0, e[2]], c=c) 25 | 26 | if labels is not None: 27 | for p, l in zip(points, labels): 28 | ax.text(p[0], p[1], p[2], l, size=30, color='k') 29 | 30 | (xs, ys, zs) = zip(*points) 31 | lim = max([abs(e) for e in xs+ys+zs]) 32 | ax.set_xlim(-lim, lim) 33 | ax.set_ylim(-lim, lim) 34 | ax.set_zlim(-lim, lim) 35 | plt.show() 36 | 37 | def _plot_hull(points, simplices, labels=None): 38 | 39 | triangles = points[simplices] 40 | 41 | fig = plt.figure()#figsize=(16,14)) 42 | fig.set_tight_layout(True) 43 | ax = fig.add_subplot(111, projection='3d') 44 | 45 | if 1: 46 | color = (0.0, 0.0, 1., 0.2) 47 | 48 | tri = Poly3DCollection(triangles) 49 | tri.set_facecolor(color) 50 | ax.add_collection3d(tri) 51 | 52 | for t in triangles: 53 | (xs, ys, zs) = zip(*[t[0], t[1], t[2], t[0]]) 54 | ax.plot(xs, ys, zs, c='k') 55 | 56 | if labels is not None: 57 | for p, l in zip(points, labels): 58 | ax.text(p[0], p[1], p[2], l, size=30, color='k') 59 | 60 | (xs, ys, zs) = zip(*points) 61 | lim = max([abs(e) for e in xs+ys+zs]) 62 | #ax.set_xlim(-lim, lim) 63 | #ax.set_ylim(-lim, lim) 64 | #ax.set_zlim(-lim, lim) 65 | plt.show() 66 | -------------------------------------------------------------------------------- /ptm_structure_matcher.h: -------------------------------------------------------------------------------- 1 | /*Copyright (c) 2016 PM Larsen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | */ 9 | 10 | #ifndef PTM_STRUCTURE_MATCHER_H 11 | #define PTM_STRUCTURE_MATCHER_H 12 | 13 | #include "ptm_initialize_data.h" 14 | #include "ptm_constants.h" 15 | 16 | 17 | namespace ptm { 18 | 19 | typedef struct 20 | { 21 | double rmsd; 22 | double scale; 23 | double q[4]; //rotation in quaternion form (rigid body transformation) 24 | int8_t mapping[PTM_MAX_POINTS]; 25 | const refdata_t* ref_struct; 26 | } result_t; 27 | 28 | int match_general(const refdata_t* s, double (*ch_points)[3], double (*points)[3], convexhull_t* ch, result_t* res); 29 | int match_fcc_hcp_ico(double (*ch_points)[3], double (*points)[3], int32_t flags, convexhull_t* ch, result_t* res); 30 | int match_dcub_dhex(double (*ch_points)[3], double (*points)[3], int32_t flags, convexhull_t* ch, result_t* res); 31 | int match_graphene(double (*points)[3], result_t* res); 32 | 33 | } 34 | 35 | #endif 36 | 37 | -------------------------------------------------------------------------------- /ptm_graph_data.h: -------------------------------------------------------------------------------- 1 | /*Copyright (c) 2016 PM Larsen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | */ 9 | 10 | #ifndef PTM_GRAPH_DATA_H 11 | #define PTM_GRAPH_DATA_H 12 | 13 | #include 14 | #include "ptm_constants.h" 15 | 16 | namespace ptm { 17 | 18 | typedef struct 19 | { 20 | int id; 21 | uint64_t hash; 22 | int automorphism_index; 23 | int num_automorphisms; 24 | int8_t canonical_labelling[PTM_MAX_POINTS]; 25 | int8_t facets[PTM_MAX_FACETS][3]; 26 | } graph_t; 27 | 28 | #define NUM_SC_GRAPHS 1 29 | #define NUM_ICO_GRAPHS 1 30 | #define NUM_FCC_GRAPHS 8 31 | #define NUM_HCP_GRAPHS 16 32 | #define NUM_BCC_GRAPHS 218 33 | #define NUM_DCUB_GRAPHS 12 34 | #define NUM_DHEX_GRAPHS 24 35 | 36 | extern int8_t automorphisms[][PTM_MAX_POINTS]; 37 | 38 | extern graph_t graphs_sc[NUM_SC_GRAPHS]; 39 | extern graph_t graphs_fcc[NUM_FCC_GRAPHS]; 40 | extern graph_t graphs_hcp[NUM_HCP_GRAPHS]; 41 | extern graph_t graphs_ico[NUM_ICO_GRAPHS]; 42 | extern graph_t graphs_bcc[NUM_BCC_GRAPHS]; 43 | extern graph_t graphs_dcub[NUM_DCUB_GRAPHS]; 44 | extern graph_t graphs_dhex[NUM_DHEX_GRAPHS]; 45 | 46 | } 47 | 48 | #endif 49 | 50 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Generic GNUMakefile 2 | 3 | # Just a snippet to stop executing under other make(1) commands 4 | # that won't understand these lines 5 | ifneq (,) 6 | This makefile requires GNU Make. 7 | endif 8 | 9 | PROGRAM = benchmark 10 | CPP_FILES = main.cpp unittest.cpp\ 11 | ptm_alloy_types.cpp\ 12 | ptm_canonical_coloured.cpp \ 13 | ptm_convex_hull_incremental.cpp \ 14 | ptm_deformation_gradient.cpp \ 15 | ptm_graph_data.cpp\ 16 | ptm_graph_tools.cpp \ 17 | ptm_index.cpp\ 18 | ptm_initialize_data.cpp \ 19 | ptm_multishell.cpp\ 20 | ptm_neighbour_ordering.cpp\ 21 | ptm_normalize_vertices.cpp \ 22 | ptm_polar.cpp \ 23 | ptm_quat.cpp \ 24 | ptm_structure_matcher.cpp \ 25 | ptm_voronoi_cell.cpp 26 | 27 | #COBJS := $(patsubst %.c, %.o, $(C_FILES)) 28 | CPPOBJS := $(patsubst %.cpp, %.o, $(CPP_FILES)) 29 | LDFLAGS = 30 | LDLIBS = -lm #-fno-omit-frame-pointer -fsanitize=address 31 | 32 | #CC = gcc 33 | CPP = g++ 34 | 35 | HEADER_FILES = ptm_alloy_types.h\ 36 | ptm_canonical_coloured.h \ 37 | ptm_convex_hull_incremental.h \ 38 | ptm_deformation_gradient.h\ 39 | ptm_fundamental_mappings.h \ 40 | ptm_graph_data.h\ 41 | ptm_graph_tools.h \ 42 | ptm_index.h \ 43 | ptm_initialize_data.h \ 44 | ptm_multishell.h\ 45 | ptm_neighbour_ordering.h \ 46 | ptm_normalize_vertices.h \ 47 | ptm_polar.h \ 48 | ptm_quat.h \ 49 | ptm_structure_matcher.h \ 50 | ptm_voronoi_cell.h 51 | 52 | OBJDIR = . 53 | 54 | #C_OBJECT_FILES = $(C_SRC_FILES:%.c=$(OBJDIR)/%.o) 55 | CPP_OBJECT_FILES = $(CPP_SRC_FILES:%.cpp=$(OBJDIR)/%.o) 56 | C_OBJECT_MODULE_FILE = $(C_SRC_MODULE_FILE:%.c=$(OBJDIR)/%.o) 57 | 58 | #CFLAGS = -std=c99 -g -O3 -Wall -Wextra 59 | CPPFLAGS = -g -O3 -std=c++11 -Wall -Wextra -Wvla -pedantic #-fno-omit-frame-pointer -fsanitize=address 60 | 61 | 62 | all: $(PROGRAM) 63 | 64 | #$(PROGRAM): $(COBJS) $(CPPOBJS) 65 | # $(CC) -c $(CFLAGS) $(COBJS) 66 | # $(CPP) -c $(CPPFLAGS) $(CPPOBJS) 67 | # $(CPP) $(COBJS) $(CPPOBJS) -o $(PROGRAM) $(LDLIBS) $(LDFLAGS) 68 | 69 | $(PROGRAM): $(CPPOBJS) 70 | $(CPP) -c $(CPPFLAGS) $(CPPOBJS) 71 | $(CPP) $(CPPOBJS) -o $(PROGRAM) $(LDLIBS) $(LDFLAGS) 72 | 73 | # These are the pattern matching rules. In addition to the automatic 74 | # variables used here, the variable $* that matches whatever % stands for 75 | # can be useful in special cases. 76 | %.o: %.c 77 | $(CC) $(CFLAGS) -c $< -o $@ 78 | 79 | %: %.c 80 | $(CC) $(CFLAGS) -o $@ $< 81 | 82 | 83 | -------------------------------------------------------------------------------- /datagen/wb.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | def walk(best_code, prev, cur, degree, m, common, vertex_colours, edge_colours): 4 | 5 | m = np.copy(m) 6 | num_edges = np.sum(m) 7 | num_vertices = m.shape[0] 8 | 9 | index = [-1 for i in range(num_vertices)] 10 | 11 | index[prev] = 0 12 | code = [0] 13 | n = 1 14 | 15 | for it in range(1, num_edges): 16 | 17 | m[prev,cur] = 0 18 | 19 | if index[cur] == -1: 20 | next = common[prev,cur] 21 | elif m[cur,prev] == 1: 22 | next = prev 23 | else: 24 | next = common[prev,cur] 25 | while m[cur,next] == 0: 26 | next = common[next,cur] 27 | 28 | if index[cur] == -1: 29 | index[cur] = n 30 | n += 1 31 | 32 | #if index[cur] < best_code[it]: 33 | # return ([-1], None) 34 | 35 | code += [index[cur]] 36 | if vertex_colours is not None: 37 | code += [vertex_colours[cur]] 38 | if edge_colours is not None: 39 | code += [edge_colours[(prev, cur)]] 40 | prev = cur 41 | cur = next 42 | 43 | return (code, index) 44 | 45 | def weinberg(facets, right_only=True, vertex_colours=None, edge_colours=None): 46 | 47 | edges = [] 48 | for (a, b, c) in facets: 49 | edges += [(a, b)] 50 | edges += [(b, c)] 51 | edges += [(c, a)] 52 | degree = np.bincount(np.array(edges).reshape(-1)) 53 | n = len(degree) 54 | 55 | m = np.zeros((n, n)).astype(np.int8) 56 | for (a, b) in edges: 57 | m[a,b] = 1 58 | m[b,a] = 1 59 | 60 | common_r = np.zeros((n, n)).astype(np.int8) 61 | common_l = np.zeros((n, n)).astype(np.int8) 62 | for (a, b, c) in facets: 63 | common_r[a,b] = c 64 | common_r[b,c] = a 65 | common_r[c,a] = b 66 | 67 | common_l[b,a] = c 68 | common_l[c,b] = a 69 | common_l[a,c] = b 70 | 71 | max_edge_degree = max([(degree[a], degree[b]) for (a, b) in edges]) 72 | 73 | automorphisms = [] 74 | best_code = [-1] * len(edges) 75 | 76 | sides = ['right', 'left'] 77 | if right_only: 78 | sides = ['right'] 79 | 80 | for side in sides: 81 | common = [common_l, common_r][side == 'right'] 82 | for (a, b) in edges: 83 | if 1 and (degree[a], degree[b]) == max_edge_degree: 84 | (code, index) = walk(best_code, a, b, degree, m, common, vertex_colours, edge_colours) 85 | 86 | if code > best_code: 87 | best_code = code 88 | automorphisms = [index] 89 | elif code == best_code: 90 | automorphisms += [index] 91 | 92 | return (tuple(best_code), automorphisms) 93 | 94 | -------------------------------------------------------------------------------- /ptm_deformation_gradient.cpp: -------------------------------------------------------------------------------- 1 | /*Copyright (c) 2016 PM Larsen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | */ 9 | 10 | #include "ptm_deformation_gradient.h" 11 | 12 | namespace ptm { 13 | 14 | void calculate_deformation_gradient(int num_points, const double (*ideal_points)[3], int8_t* mapping, double (*normalized)[3], const double (*penrose)[3], double* F, double* res) 15 | { 16 | for (int i = 0;i<3;i++) 17 | { 18 | for (int j = 0;j<3;j++) 19 | { 20 | double acc = 0.0; 21 | for (int k = 0;k 14 | #include 15 | #include "ptm_initialize_data.h" 16 | #include "ptm_constants.h" 17 | 18 | 19 | //------------------------------------ 20 | // function declarations 21 | //------------------------------------ 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | 27 | int ptm_index( ptm_local_handle_t local_handle, 28 | size_t atom_index, int (get_neighbours)(void* vdata, size_t _unused_lammps_variable, size_t atom_index, int num, int* ordering, size_t* nbr_indices, int32_t* numbers, double (*nbr_pos)[3]), void* nbrlist, 29 | int32_t flags, bool output_conventional_orientation, //inputs 30 | int32_t* p_type, int32_t* p_alloy_type, double* p_scale, double* p_rmsd, double* q, double* F, double* F_res, double* U, double* P, double* p_interatomic_distance, double* p_lattice_constant, 31 | int* p_best_template_index, const double (**p_best_template)[3], int8_t* output_indices); //outputs 32 | 33 | 34 | int ptm_remap_template( int type, bool output_conventional_orientation, int input_template_index, double* qtarget, double* q, 35 | double* p_disorientation, int8_t* mapping, const double (**p_best_template)[3]); 36 | 37 | int ptm_undo_conventional_orientation(int type, int input_template_index, double* q, int8_t* mapping); 38 | 39 | int ptm_preorder_neighbours(void* _voronoi_handle, int num_input_points, double (*input_points)[3], uint64_t* res); 40 | void ptm_index_to_permutation(int n, uint64_t k, int* permuted); 41 | 42 | 43 | #ifdef __cplusplus 44 | } 45 | #endif 46 | 47 | #endif 48 | 49 | -------------------------------------------------------------------------------- /ptm_graph_tools.cpp: -------------------------------------------------------------------------------- 1 | /*Copyright (c) 2016 PM Larsen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | */ 9 | 10 | #include 11 | #include 12 | #include "ptm_graph_tools.h" 13 | #include "ptm_constants.h" 14 | 15 | 16 | namespace ptm { 17 | 18 | bool build_facet_map(int num_facets, int8_t facets[][3], int8_t common[PTM_MAX_NBRS][PTM_MAX_NBRS]) 19 | { 20 | memset(common, -1, sizeof(int8_t) * PTM_MAX_NBRS * PTM_MAX_NBRS); 21 | 22 | for (int i = 0;i 11 | 12 | namespace ptm { 13 | 14 | void subtract_barycentre(int num, double (*points)[3], double (*normalized)[3]) 15 | { 16 | //calculate barycentre 17 | double sum[3] = {0, 0, 0}; 18 | for (int i=0;i 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "ptm_initialize_data.h" 18 | 19 | 20 | static void make_facets_clockwise(int num_facets, int8_t (*facets)[3], const double (*points)[3]) 21 | { 22 | double plane_normal[3]; 23 | double origin[3] = {0, 0, 0}; 24 | 25 | for (int i = 0;inum_graphs;i++) 32 | { 33 | int8_t code[2 * PTM_MAX_EDGES]; 34 | int8_t degree[PTM_MAX_NBRS]; 35 | int _max_degree = ptm::graph_degree(s->num_facets, s->graphs[i].facets, s->num_nbrs, degree); 36 | assert(_max_degree <= s->max_degree); 37 | 38 | make_facets_clockwise(s->num_facets, s->graphs[i].facets, &s->points[1]); 39 | int ret = ptm::canonical_form_coloured(s->num_facets, s->graphs[i].facets, s->num_nbrs, degree, colours, s->graphs[i].canonical_labelling, (int8_t*)&code[0], &s->graphs[i].hash); 40 | if (ret != 0) 41 | return ret; 42 | } 43 | 44 | return PTM_NO_ERROR; 45 | } 46 | 47 | bool ptm_initialized = false; 48 | int ptm_initialize_global() 49 | { 50 | if (ptm_initialized) 51 | return PTM_NO_ERROR; 52 | 53 | int8_t colours[PTM_MAX_POINTS] = {0}; 54 | int8_t dcolours[PTM_MAX_POINTS] = {1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 55 | 56 | int ret = initialize_graphs(&ptm::structure_sc, colours); 57 | ret |= initialize_graphs(&ptm::structure_fcc, colours); 58 | ret |= initialize_graphs(&ptm::structure_hcp, colours); 59 | ret |= initialize_graphs(&ptm::structure_ico, colours); 60 | ret |= initialize_graphs(&ptm::structure_bcc, colours); 61 | ret |= initialize_graphs(&ptm::structure_dcub, dcolours); 62 | ret |= initialize_graphs(&ptm::structure_dhex, dcolours); 63 | 64 | if (ret == PTM_NO_ERROR) 65 | ptm_initialized = true; 66 | 67 | return ret; 68 | } 69 | 70 | ptm_local_handle_t ptm_initialize_local() 71 | { 72 | assert(ptm_initialized); 73 | return (ptm_local_handle_t)ptm::voronoi_initialize_local(); 74 | } 75 | 76 | void ptm_uninitialize_local(ptm_local_handle_t ptr) 77 | { 78 | ptm::voronoi_uninitialize_local(ptr); 79 | } 80 | 81 | -------------------------------------------------------------------------------- /datagen/fundamental_mappings.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import itertools 3 | import quat_utils 4 | import scipy.optimize 5 | import scipy.spatial 6 | import rmsd 7 | 8 | 9 | def invert_array(t): 10 | 11 | inverted = np.zeros(len(t)).astype(np.int) 12 | for i, e in enumerate(t): 13 | inverted[e] = i 14 | return np.array(inverted) 15 | 16 | def get_mappings(structure): 17 | 18 | n = len(structure) 19 | cs = itertools.permutations(range(n), 3) 20 | 21 | keeps = {} 22 | 23 | for c in cs: 24 | Q = rmsd.kabsch(structure[:3], structure[list(c)]).T 25 | assert np.linalg.det(Q) > 0.5 26 | 27 | rotated = np.dot(structure, Q.T) 28 | ds = scipy.spatial.distance.cdist(rotated, structure) 29 | 30 | _, res = scipy.optimize.linear_sum_assignment(ds) 31 | res = invert_array(res) 32 | obj = rmsd.rmsd(rotated[res], structure) 33 | if obj > 1E-3: 34 | continue 35 | 36 | key = tuple(res) 37 | if key not in keeps: 38 | keeps[key] = Q 39 | 40 | return keeps 41 | 42 | def find(structure, generators=None): 43 | 44 | print structure 45 | structure = structure[1:] 46 | keeps = get_mappings(structure) 47 | 48 | data = [] 49 | for mapping, Q in sorted(keeps.items()): 50 | q = quat_utils.rotation_matrix_to_quaternion(Q) 51 | indices = np.where(np.abs(q) < 1E-5)[0] 52 | q[indices] = 0 53 | 54 | if generators is not None: 55 | generators = np.array(generators) 56 | ds0 = np.linalg.norm(q - generators, axis=1) 57 | ds1 = np.linalg.norm(q + generators, axis=1) 58 | ds = np.min((ds0, ds1), axis=0) 59 | assert np.min(ds) < 1E-4 60 | index = np.argmin(ds) 61 | data += [(index, mapping, q)] 62 | else: 63 | data += [(len(data), mapping, q)] 64 | 65 | indices, _, qs = zip(*sorted(data)) 66 | assert indices == tuple(range(len(indices))) 67 | for index, mapping, q in sorted(data): 68 | 69 | mapping = [0] + list(np.array(mapping) + 1) 70 | print "{" + ", ".join([str(e).rjust(2) for e in mapping]) + "},"#, q 71 | 72 | _qs = [] 73 | for q in qs: 74 | t = [1, 0.1, 0.01, 0.001] 75 | if np.dot(-q, t) > np.dot(q, t): 76 | q = -q 77 | _qs += [(np.dot(q, t), tuple(q))] 78 | _qs = sorted(_qs, reverse=True) 79 | qs = np.array([e[1] for e in _qs]) 80 | for q in qs: 81 | print "{" + ", ".join([("%.14f" % e).rjust(18) for e in q]) + "}," 82 | 83 | 84 | def get_mappings(ref, structure): 85 | 86 | n = len(structure) 87 | cs = itertools.permutations(range(n), 3) 88 | 89 | keeps = {} 90 | 91 | for c in cs: 92 | Q = rmsd.kabsch(ref[:3], structure[list(c)]).T 93 | assert np.linalg.det(Q) > 0.5 94 | 95 | rotated = np.dot(structure, Q.T) 96 | ds = scipy.spatial.distance.cdist(rotated, ref) 97 | 98 | _, res = scipy.optimize.linear_sum_assignment(ds) 99 | res = invert_array(res) 100 | obj = rmsd.rmsd(rotated[res], structure) 101 | if obj > 1E-3: 102 | continue 103 | 104 | key = tuple(res) 105 | if key not in keeps: 106 | keeps[key] = Q 107 | 108 | return keeps 109 | 110 | 111 | def find(structures, generators=None): 112 | 113 | generators = np.array(generators) 114 | structures = [structure[1:] for structure in structures] 115 | ref = structures[0] 116 | 117 | data = [] 118 | for j, structure in enumerate(structures): 119 | 120 | for i, q in enumerate(generators): 121 | U = quat_utils.quaternion_to_rotation_matrix(q) 122 | rotated = np.dot(structure, U.T) 123 | ds = scipy.spatial.distance.cdist(ref, rotated) 124 | _, mapping = scipy.optimize.linear_sum_assignment(ds) 125 | obj = rmsd.rmsd(ref, rotated[mapping]) 126 | if obj > 1E-3: 127 | continue 128 | 129 | data += [(i, j, mapping, q)] 130 | 131 | indices, js, _, qs = zip(*sorted(data)) 132 | print js 133 | 134 | assert indices == tuple(range(len(indices))) 135 | for index, js, mapping, q in sorted(data): 136 | 137 | mapping = [0] + list(np.array(mapping) + 1) 138 | print "{" + ", ".join([str(e).rjust(2) for e in mapping]) + "},"#, q 139 | 140 | _qs = [] 141 | for q in qs: 142 | t = [1, 0.1, 0.01, 0.001] 143 | if np.dot(-q, t) > np.dot(q, t): 144 | q = -q 145 | _qs += [(np.dot(q, t), tuple(q))] 146 | _qs = sorted(_qs, reverse=True) 147 | qs = np.array([e[1] for e in _qs]) 148 | for q in qs: 149 | print "{" + ", ".join([("%.14f" % e).rjust(18) for e in q]) + "}," 150 | 151 | -------------------------------------------------------------------------------- /datagen/quat_utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def flip_up(q): 5 | if q[0] < 0: 6 | q = -q 7 | return q 8 | 9 | 10 | def multiply(r, a): 11 | 12 | b0 = r[0] * a[0] - r[1] * a[1] - r[2] * a[2] - r[3] * a[3] 13 | b1 = r[0] * a[1] + r[1] * a[0] + r[2] * a[3] - r[3] * a[2] 14 | b2 = r[0] * a[2] - r[1] * a[3] + r[2] * a[0] + r[3] * a[1] 15 | b3 = r[0] * a[3] + r[1] * a[2] - r[2] * a[1] + r[3] * a[0] 16 | return flip_up(np.array([b0, b1, b2, b3])) 17 | 18 | 19 | def random_quaternions(n): 20 | 21 | x0 = np.random.uniform(0, 1, n) 22 | x1 = np.random.uniform(0, 2 * np.pi, n) 23 | x2 = np.random.uniform(0, 2 * np.pi, n) 24 | 25 | r1, r2 = np.sqrt(1 - x0), np.sqrt(x0) 26 | s1, c1 = np.sin(x1), np.cos(x1) 27 | s2, c2 = np.sin(x2), np.cos(x2) 28 | 29 | return np.array([s1 * r1, c1 * r1, s2 * r2, c2 * r2]).T 30 | 31 | 32 | def multiply_first_component(r, a): 33 | return r[0] * a[0] - r[1] * a[1] - r[2] * a[2] - r[3] * a[3] 34 | 35 | 36 | def rotate_into_fundamental_zone(q, generators): 37 | 38 | index = np.argmax([abs(multiply_first_component(q, g)) for g in generators]) 39 | return multiply(q, generators[index]) 40 | 41 | 42 | def map_points_out(basis_points, basis_weights, superset, subset, map_indices): 43 | 44 | assert( len(map_indices) * len(subset) == len(superset) ) 45 | 46 | superset = np.array(superset) 47 | subset = np.array(subset) 48 | 49 | mapped_points = [] 50 | mapped_weights = [] 51 | for g in superset[map_indices]: 52 | for b, w in zip(basis_points, basis_weights): 53 | r = multiply(b, g) 54 | #r = rotate_into_fundamental_zone(r, subset) 55 | mapped_points += [r] 56 | mapped_weights += [w] 57 | 58 | return np.array(mapped_points), np.array(mapped_weights) 59 | 60 | 61 | def quaternion_to_rotation_matrix(q): 62 | 63 | a, b, c, d = q 64 | 65 | u0 = a*a + b*b - c*c - d*d 66 | u1 = 2*b*c - 2*a*d 67 | u2 = 2*b*d + 2*a*c 68 | 69 | u3 = 2*b*c + 2*a*d 70 | u4 = a*a - b*b + c*c - d*d 71 | u5 = 2*c*d - 2*a*b 72 | 73 | u6 = 2*b*d - 2*a*c 74 | u7 = 2*c*d + 2*a*b 75 | u8 = a*a - b*b - c*c + d*d 76 | 77 | return np.array([[u0, u1, u2], [u3, u4, u5], [u6, u7, u8]]) 78 | 79 | 80 | def rodrigues_to_quaternion(r): 81 | 82 | s = 1 / np.sqrt(1 + np.linalg.norm(r)**2) 83 | return np.array([s, s * r[0], s * r[1], s * r[2]]) 84 | 85 | 86 | def rotation_matrix_to_quaternion(u): 87 | 88 | r11, r12, r13 = u[0] 89 | r21, r22, r23 = u[1] 90 | r31, r32, r33 = u[2] 91 | 92 | q0 = (1.0 + r11 + r22 + r33) / 4.0 93 | q1 = (1.0 + r11 - r22 - r33) / 4.0 94 | q2 = (1.0 - r11 + r22 - r33) / 4.0 95 | q3 = (1.0 - r11 - r22 + r33) / 4.0 96 | q = np.array([q0, q1, q2, q3]) 97 | q = np.sqrt(np.maximum(0, q)) 98 | 99 | i = np.argmax(q) 100 | if i == 0: 101 | q[1] *= np.sign(r32 - r23) 102 | q[2] *= np.sign(r13 - r31) 103 | q[3] *= np.sign(r21 - r12) 104 | 105 | elif i == 1: 106 | q[0] *= np.sign(r32 - r23) 107 | q[2] *= np.sign(r21 + r12) 108 | q[3] *= np.sign(r13 + r31) 109 | 110 | elif i == 2: 111 | q[0] *= np.sign(r13 - r31) 112 | q[1] *= np.sign(r21 + r12) 113 | q[3] *= np.sign(r32 + r23) 114 | 115 | elif i == 3: 116 | q[0] *= np.sign(r21 - r12) 117 | q[1] *= np.sign(r31 + r13) 118 | q[2] *= np.sign(r32 + r23) 119 | 120 | return q / np.linalg.norm(q) 121 | 122 | 123 | #euler angle conventions are taken from EMSoft 124 | def quaternion_to_euler(q): 125 | 126 | qq = q**2 127 | q03 = qq[0] + qq[3] 128 | q12 = qq[1] + qq[2] 129 | chi = np.sqrt(q03 * q12) 130 | if chi == 0: 131 | if q12 == 0: 132 | phi = 0 133 | phi2 = 0 134 | phi1 = np.arctan2(-2 * q[0] * q[3], qq[0] - qq[3]) 135 | else: 136 | phi = np.pi 137 | phi2 = 0 138 | phi1 = np.arctan2(2 * q[1] * q[2], qq[1] - qq[2]) 139 | else: 140 | phi = np.arctan2(2 * chi, q03 - q12) 141 | phi1 = np.arctan2((-q[0] * q[2] + q[1] * q[3]) / chi, (-q[0] * q[1] - q[2] * q[3]) / chi) 142 | phi2 = np.arctan2((+q[0] * q[2] + q[1] * q[3]) / chi, (-q[0] * q[1] + q[2] * q[3]) / chi) 143 | 144 | res = np.array([phi1, phi, phi2]) % (2 * np.pi) 145 | res[1] %= np.pi 146 | return res 147 | 148 | 149 | #euler angle conventions are taken from EMSoft 150 | def euler_to_quaternion(e): 151 | 152 | ee = np.array(e) / 2 153 | cphi = np.cos(ee[1]) 154 | sphi = np.sin(ee[1]) 155 | cm = np.cos(ee[0] - ee[2]) 156 | sm = np.sin(ee[0] - ee[2]) 157 | cp = np.cos(ee[0] + ee[2]) 158 | sp = np.sin(ee[0] + ee[2]) 159 | 160 | res = np.array([cphi * cp, -sphi * cm, -sphi * sm, -cphi * sp]) 161 | return flip_up(res) 162 | 163 | -------------------------------------------------------------------------------- /ptm_multishell.cpp: -------------------------------------------------------------------------------- 1 | /*Copyright (c) 2016 PM Larsen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | */ 9 | 10 | //todo: normalize vertices 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "ptm_constants.h" 20 | #include "ptm_voronoi_cell.h" 21 | #include "ptm_multishell.h" 22 | #include "ptm_normalize_vertices.h" 23 | 24 | 25 | namespace ptm { 26 | 27 | typedef struct 28 | { 29 | int rank; 30 | int inner; 31 | int ordering; 32 | size_t atom_index; 33 | int32_t number; 34 | double offset[3]; 35 | } sorthelper_t; 36 | 37 | static bool sorthelper_compare(sorthelper_t const& a, sorthelper_t const& b) 38 | { 39 | return a.rank < b.rank; 40 | } 41 | 42 | #define MAX_INNER 4 43 | 44 | int calculate_two_shell_neighbour_ordering( int num_inner, int num_outer, 45 | size_t atom_index, int (get_neighbours)(void* vdata, size_t _unused_lammps_variable, size_t atom_index, int num, int* ordering, size_t* nbr_indices, int32_t* numbers, double (*nbr_pos)[3]), void* nbrlist, 46 | ptm::atomicenv_t* output) 47 | { 48 | assert(num_inner <= MAX_INNER); 49 | 50 | ptm::atomicenv_t central; 51 | int num_input_points = get_neighbours(nbrlist, -1, atom_index, PTM_MAX_INPUT_POINTS, central.ordering, central.nbr_indices, central.numbers, central.points); 52 | if (num_input_points < num_inner + 1) 53 | return -1; 54 | 55 | std::unordered_set claimed; 56 | for (int i=0;iordering[i] = central.ordering[i]; 59 | output->nbr_indices[i] = central.nbr_indices[i]; 60 | output->numbers[i] = central.numbers[i]; 61 | memcpy(output->points[i], central.points[i], 3 * sizeof(double)); 62 | 63 | claimed.insert(central.nbr_indices[i]); 64 | } 65 | 66 | int num_inserted = 0; 67 | sorthelper_t data[MAX_INNER * PTM_MAX_INPUT_POINTS]; 68 | for (int i=0;i= num_outer || already_claimed) 108 | continue; 109 | 110 | output->ordering[1 + num_inner + num_outer * inner + counts[inner]] = data[i].ordering; 111 | 112 | output->nbr_indices[1 + num_inner + num_outer * inner + counts[inner]] = nbr_atom_index; 113 | output->numbers[1 + num_inner + num_outer * inner + counts[inner]] = data[i].number; 114 | memcpy(output->points[1 + num_inner + num_outer * inner + counts[inner]], &data[i].offset, 3 * sizeof(double)); 115 | claimed.insert(nbr_atom_index); 116 | 117 | counts[inner]++; 118 | num_found++; 119 | if (num_found >= num_inner * num_outer) 120 | break; 121 | } 122 | 123 | if (num_found != num_inner * num_outer) 124 | return -1; 125 | 126 | return 0; 127 | } 128 | 129 | } 130 | 131 | -------------------------------------------------------------------------------- /ptm_alloy_types.cpp: -------------------------------------------------------------------------------- 1 | /*Copyright (c) 2016 PM Larsen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | */ 9 | 10 | #include 11 | #include "ptm_constants.h" 12 | #include "ptm_initialize_data.h" 13 | 14 | namespace ptm { 15 | 16 | #define NUM_ALLOY_TYPES 3 17 | static uint32_t typedata[NUM_ALLOY_TYPES][3] = { 18 | {PTM_MATCH_FCC, PTM_ALLOY_L10, 0x00000db6}, 19 | {PTM_MATCH_FCC, PTM_ALLOY_L12_CU, 0x00000492}, 20 | {PTM_MATCH_FCC, PTM_ALLOY_L12_AU, 0x00001ffe}, 21 | }; 22 | 23 | static bool test_pure(int num_nbrs, int32_t* numbers) 24 | { 25 | for (int i=1;inum_nbrs+1;i++) 52 | binary[i] = numbers[mapping[i]] == numbers[0] ? 0 : 1; 53 | 54 | for (int i=1;inum_nbrs+1;i++) 59 | if (binary[i] != binary[0]) 60 | return false; 61 | 62 | return true; 63 | } 64 | 65 | static int32_t canonical_alloy_representation(const refdata_t* ref, int8_t* mapping, int32_t* numbers) 66 | { 67 | int8_t binary[PTM_MAX_POINTS]; 68 | for (int i=0;inum_nbrs+1;i++) 69 | binary[i] = numbers[mapping[i]] == numbers[0] ? 0 : 1; 70 | 71 | int8_t temp[PTM_MAX_POINTS]; 72 | uint32_t best = 0xFFFFFFFF; 73 | for (int j=0;jnum_mappings;j++) 74 | { 75 | for (int i=0;inum_nbrs+1;i++) 76 | temp[ref->mapping[j][i]] = binary[i]; 77 | 78 | uint32_t code = 0; 79 | for (int i=0;inum_nbrs+1;i++) 80 | code |= (temp[i] << i); 81 | 82 | best = std::min(best, code); 83 | } 84 | 85 | return best; 86 | } 87 | 88 | int32_t find_alloy_type(const refdata_t* ref, int8_t* mapping, int32_t* numbers) 89 | { 90 | for (int i=0;inum_nbrs+1;i++) 91 | if (numbers[i] == -1) 92 | return PTM_ALLOY_NONE; 93 | 94 | if (test_pure(ref->num_nbrs, numbers)) 95 | return PTM_ALLOY_PURE; 96 | 97 | if (!test_binary(ref->num_nbrs, numbers)) 98 | return PTM_ALLOY_NONE; 99 | 100 | uint32_t code = canonical_alloy_representation(ref, mapping, numbers); 101 | for (int i=0;itype == typedata[i][0] && code == typedata[i][2]) 103 | return typedata[i][1]; 104 | 105 | if (ref->type == PTM_MATCH_BCC) 106 | if (test_shell_structure(ref, mapping, numbers, 8)) 107 | return PTM_ALLOY_B2; 108 | 109 | if (ref->type == PTM_MATCH_DCUB || ref->type == PTM_MATCH_DHEX) 110 | if (test_shell_structure(ref, mapping, numbers, 4)) 111 | return PTM_ALLOY_SIC; 112 | 113 | 114 | if (ref->type == PTM_MATCH_GRAPHENE) 115 | if (test_shell_structure(ref, mapping, numbers, 3)) 116 | return PTM_ALLOY_BN; 117 | 118 | return PTM_ALLOY_NONE; 119 | } 120 | 121 | } 122 | 123 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "ptm_functions.h" 9 | #include "unittest.hpp" 10 | 11 | using namespace std; 12 | 13 | //#define _MAX_NBRS 50 //diamond 14 | #define _MAX_NBRS 24 //fcc, other 15 | //#define _MAX_NBRS 6 //graphene 16 | 17 | 18 | static int read_file(const char* path, uint8_t** p_buf, size_t* p_fsize) 19 | { 20 | size_t fsize = 0, num_read = 0; 21 | uint8_t* buf = NULL; 22 | 23 | FILE* fin = fopen(path, "rb"); 24 | if (fin == NULL) 25 | return -1; 26 | 27 | int ret = fseek(fin, 0, SEEK_END); 28 | if (ret != 0) 29 | goto cleanup; 30 | 31 | fsize = ftell(fin); 32 | ret = fseek(fin, 0, SEEK_SET); 33 | if (ret != 0) 34 | goto cleanup; 35 | 36 | buf = (uint8_t*)malloc(fsize); 37 | if (buf == NULL) 38 | { 39 | ret = -1; 40 | goto cleanup; 41 | } 42 | 43 | num_read = fread(buf, 1, fsize, fin); 44 | if (num_read != fsize) 45 | { 46 | ret = -1; 47 | goto cleanup; 48 | } 49 | 50 | cleanup: 51 | if (ret != 0) 52 | { 53 | free(buf); 54 | buf = NULL; 55 | } 56 | 57 | *p_fsize = fsize; 58 | *p_buf = buf; 59 | fclose(fin); 60 | return ret; 61 | } 62 | 63 | typedef struct 64 | { 65 | double (*positions)[3]; 66 | int32_t* nbrs; 67 | 68 | } demonbrdata_t; 69 | 70 | static int get_neighbours(void* vdata, size_t central_index, size_t atom_index, int num, int* ordering, size_t* nbr_indices, int32_t* numbers, double (*nbr_pos)[3]) 71 | { 72 | demonbrdata_t* data = (demonbrdata_t*)vdata; 73 | double (*positions)[3] = data->positions; 74 | int32_t* nbrs = data->nbrs; 75 | 76 | memcpy(nbr_pos[0], positions[atom_index], 3 * sizeof(double)); 77 | nbr_indices[0] = atom_index; 78 | if (numbers != NULL) 79 | numbers[0] = 0; 80 | 81 | int n = std::min(num - 1, _MAX_NBRS); 82 | for (int j=0;j 1] 61 | for line in lines: 62 | line = line.replace('{', '') 63 | line = line.replace('}', '') 64 | line = line.replace(' ', '') 65 | line = line.replace('\t', '') 66 | line = line.split(',')[:-1] 67 | data += [line] 68 | 69 | nmax = max([max([len(e) for e in row]) for row in data]) 70 | print nmax 71 | 72 | output = [] 73 | for row in data: 74 | print "{ " + ", ".join([e.rjust(nmax) for e in row]) + " }," 75 | go() 76 | -------------------------------------------------------------------------------- /datagen/analysis.py: -------------------------------------------------------------------------------- 1 | import sympy 2 | import numpy as np 3 | 4 | 5 | sqrt = np.sqrt 6 | array = np.array 7 | 8 | sqrt = sympy.sqrt 9 | array = sympy.Matrix 10 | 11 | def norm(x): 12 | return sqrt(sum([e**2 for e in x])) 13 | 14 | def go(): 15 | 16 | p = array([-sqrt(2)/4, sqrt(3) / sqrt(2)/6, -sqrt(3)/12]) 17 | p = array([0, 0, sqrt(3)/4]) 18 | p = array([0, -sqrt(3)/sqrt(2)/3, -sqrt(3)/12]) 19 | q = array([sqrt(2)/4, -sqrt(6)/4, 0]) 20 | q = array([0, 0, -sqrt(3)/4]) 21 | q = array([sqrt(2.0)/4.0, -sqrt(6.0)/4.0, 0.0]) 22 | q = array([-sqrt(2.0)/2.0, 0.0, 0.0]) 23 | q = array([-sqrt(2.0)/4.0, sqrt(6.0)/4.0, 0.0]) 24 | q = array([sqrt(2.0)/4.0, sqrt(6.0)/4.0, 0.0]) 25 | q = array([sqrt(2.0)/2.0, 0.0, 0.0]) 26 | q = array([-sqrt(2.0)/4.0, -sqrt(6.0)/4.0, 0.0]) 27 | q = array([-sqrt(2.0)/4.0, sqrt(6.0)/12.0, -sqrt(3.0)/3.0]) 28 | q = array([sqrt(2.0)/4.0, sqrt(6.0)/12.0, -sqrt(3.0)/3.0]) 29 | q = array([0.0, -sqrt(6.0)/6.0, -sqrt(3.0)/3.0]) 30 | q = array([0.0, -sqrt(6.0)/6.0, sqrt(3.0)/3.0]) 31 | q = array([sqrt(2.0)/4.0, sqrt(6.0)/12.0, sqrt(3.0)/3.0]) 32 | q = array([-sqrt(2.0)/4.0, sqrt(6.0)/12.0, sqrt(3.0)/3.0]) 33 | 34 | c = sqrt(3) / norm(p) 35 | 36 | r = sqrt(8)/sqrt(3) 37 | k = (4 * norm(c * p) + 12 * norm(c * r * p)) / 16 38 | 39 | v = [(e).expand().simplify() for e in c * p / k] 40 | v = [(e).expand().simplify() for e in r * c * q / k] 41 | print v 42 | print [e.evalf() for e in v] 43 | 44 | def go(): 45 | 46 | q = array([4*sqrt(2)/(sqrt(3) + 6*sqrt(2)), 4*sqrt(6)/(3*(sqrt(3) + 6*sqrt(2))), 16*sqrt(3)/(3*(sqrt(3) + 6*sqrt(2)))]) 47 | v = sqrt(norm(q)**2 - norm([4*sqrt(2)/(sqrt(3) + 6*sqrt(2))])**2) 48 | print (v.expand().simplify()) 49 | 50 | def go(): 51 | 52 | sqrt = np.sqrt 53 | dhex = np.array([ 54 | [ 0, 0, 0 ], 55 | [ -4*sqrt(2)/(sqrt(3)+6*sqrt(2)), 4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), 4*sqrt(3)/(3*sqrt(3)+18*sqrt(2)) ], 56 | [ 0, 0, -4*sqrt(3)/(sqrt(3)+6*sqrt(2)) ], 57 | [ 4*sqrt(2)/(sqrt(3)+6*sqrt(2)), 4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), 4*sqrt(3)/(3*sqrt(3)+18*sqrt(2)) ], 58 | [ 0, -8*sqrt(6)/(3*sqrt(3)+18*sqrt(2)), 4*sqrt(3)/(3*sqrt(3)+18*sqrt(2)) ], 59 | [ -8*sqrt(2)/(sqrt(3)+6*sqrt(2)), 0, 0 ], 60 | [ -4*sqrt(2)/(sqrt(3)+6*sqrt(2)), 4*sqrt(6)/(sqrt(3)+6*sqrt(2)), 0 ], 61 | [ -4*sqrt(2)/(sqrt(3)+6*sqrt(2)), 4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), 16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) ], 62 | [ -4*sqrt(2)/(sqrt(3)+6*sqrt(2)), 4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), -16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) ], 63 | [ 4*sqrt(2)/(sqrt(3)+6*sqrt(2)), 4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), -16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) ], 64 | [ 0, -8*sqrt(6)/(3*sqrt(3)+18*sqrt(2)), -16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) ], 65 | [ 4*sqrt(2)/(sqrt(3)+6*sqrt(2)), 4*sqrt(6)/(sqrt(3)+6*sqrt(2)), 0 ], 66 | [ 8*sqrt(2)/(sqrt(3)+6*sqrt(2)), 0, 0 ], 67 | [ 4*sqrt(2)/(sqrt(3)+6*sqrt(2)), 4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), 16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) ], 68 | [ 4*sqrt(2)/(sqrt(3)+6*sqrt(2)), -4*sqrt(6)/(sqrt(3)+6*sqrt(2)), 0 ], 69 | [ -4*sqrt(2)/(sqrt(3)+6*sqrt(2)), -4*sqrt(6)/(sqrt(3)+6*sqrt(2)), 0 ], 70 | [ 0, -8*sqrt(6)/(3*sqrt(3)+18*sqrt(2)), 16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) ], 71 | ]) 72 | 73 | hcp = np.array([ 74 | [ 0, 0, 0 ], 75 | [ 0.5, -sqrt(3)/2, 0 ], 76 | [ -1, 0, 0 ], 77 | [ -0.5, sqrt(3)/6, -sqrt(6)/3 ], 78 | [ 0.5, sqrt(3)/6, -sqrt(6)/3 ], 79 | [ 0, -sqrt(3)/3, -sqrt(6)/3 ], 80 | [ -0.5, sqrt(3)/2, 0 ], 81 | [ 0.5, sqrt(3)/2, 0 ], 82 | [ 1, 0, 0 ], 83 | [ -0.5, -sqrt(3)/2, 0 ], 84 | [ 0, -sqrt(3)/3, sqrt(6)/3 ], 85 | [ 0.5, sqrt(3)/6, sqrt(6)/3 ], 86 | [ -0.5, sqrt(3)/6, sqrt(6)/3 ] 87 | ]) 88 | 89 | alex = np.array([ 90 | [-sqrt(2.0)/4, sqrt(3.0/2.0)/6, -sqrt(3.0)/12], 91 | [0, -sqrt(3.0/2.0)/3, -sqrt(3.0)/12], 92 | [sqrt(2.0)/4, sqrt(3.0/2.0)/6, -sqrt(3.0)/12], 93 | [0, 0, sqrt(3.0)/4], 94 | [sqrt(2.0)/4.0, -sqrt(6.0)/4.0, 0.0], 95 | [-sqrt(2.0)/2.0, 0.0, 0.0], 96 | [-sqrt(2.0)/4.0, sqrt(6.0)/4.0, 0.0], 97 | [sqrt(2.0)/4.0, sqrt(6.0)/4.0, 0.0], 98 | [sqrt(2.0)/2.0, 0.0, 0.0], 99 | [-sqrt(2.0)/4.0, -sqrt(6.0)/4.0, 0.0], 100 | [-sqrt(2.0)/4.0, sqrt(6.0)/12.0, -sqrt(3.0)/3.0], 101 | [sqrt(2.0)/4.0, sqrt(6.0)/12.0, -sqrt(3.0)/3.0], 102 | [0.0, -sqrt(6.0)/6.0, -sqrt(3.0)/3.0], 103 | [0.0, -sqrt(6.0)/6.0, sqrt(3.0)/3.0], 104 | [sqrt(2.0)/4.0, sqrt(6.0)/12.0, sqrt(3.0)/3.0], 105 | [-sqrt(2.0)/4.0, sqrt(6.0)/12.0, sqrt(3.0)/3.0], 106 | ]) 107 | 108 | better = np.array([ 109 | [ 0, 0, 0 ], 110 | [ 1, 0, 0 ], 111 | [ -0.5, -sqrt(3)/2, 0 ], 112 | [ -0.5, -sqrt(3)/6, -sqrt(6)/3 ], 113 | [ 0, sqrt(3)/3, -sqrt(6)/3 ], 114 | [ 0.5, -sqrt(3)/6, -sqrt(6)/3 ], 115 | [ -1, 0, 0 ], 116 | [ -0.5, sqrt(3)/2, 0 ], 117 | [ 0.5, sqrt(3)/2, 0 ], 118 | [ 0.5, -sqrt(3)/2, 0 ], 119 | [ 0.5, -sqrt(3)/6, sqrt(6)/3 ], 120 | [ 0, sqrt(3)/3, sqrt(6)/3 ], 121 | [ -0.5, -sqrt(3)/6, sqrt(6)/3 ], 122 | ]) 123 | 124 | import planar_graphs 125 | planar_graphs.plot_points(better) 126 | 127 | def _go(): 128 | 129 | ps = np.array([(0.000000, 0.000000, 0.000000), 130 | (-0.945297, -1.028387, 0.000000), 131 | (1.363258, -0.304458, 0.000000), 132 | (-0.417961, 1.332845, 0.000000), 133 | (-2.308555, -0.723929, 0.000000), 134 | (-0.527337, -2.361232, 0.000000), 135 | (2.308555, 0.723929, 0.000000), 136 | (1.781219, -1.637303, 0.000000), 137 | (0.527337, 2.361232, 0.000000), 138 | (-1.781219, 1.637303, 0.000000)]) 139 | 140 | inv = np.array([[ 0, 0, 0 ], 141 | [ 0, 2./63+4*sqrt(3)/63, 0 ], 142 | [ sqrt(3)/63+2./21, -2*sqrt(3)/63-1./63, 0 ], 143 | [ -2./21-sqrt(3)/63, -2*sqrt(3)/63-1./63, 0 ], 144 | [ -2./21-sqrt(3)/63, 1./21+2*sqrt(3)/21, 0 ], 145 | [ sqrt(3)/63+2./21, 1./21+2*sqrt(3)/21, 0 ], 146 | [ 2*sqrt(3)/63+4./21, 0, 0 ], 147 | [ sqrt(3)/63+2./21, -2*sqrt(3)/21-1./21, 0 ], 148 | [ -2./21-sqrt(3)/63, -2*sqrt(3)/21-1./21, 0 ], 149 | [ -4./21-2*sqrt(3)/63, 0, 0 ]]) 150 | 151 | print np.dot(inv.T, ps) 152 | go() 153 | -------------------------------------------------------------------------------- /ptm_canonical_coloured.cpp: -------------------------------------------------------------------------------- 1 | /*Copyright (c) 2016 PM Larsen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include "ptm_graph_tools.h" 14 | #include "ptm_constants.h" 15 | 16 | namespace ptm { 17 | 18 | static bool weinberg_coloured(int num_nodes, int num_edges, int8_t common[PTM_MAX_NBRS][PTM_MAX_NBRS], int8_t* colours, int8_t* best_code, int8_t* canonical_labelling, int a, int b) 19 | { 20 | bool m[PTM_MAX_NBRS][PTM_MAX_NBRS]; 21 | memset(m, 0, sizeof(bool) * PTM_MAX_NBRS * PTM_MAX_NBRS); 22 | 23 | int8_t index[PTM_MAX_NBRS]; 24 | memset(index, -1, sizeof(int8_t) * PTM_MAX_NBRS); 25 | 26 | 27 | int n = 0; 28 | index[a] = colours[a] * num_nodes + n++; 29 | if (index[a] > best_code[0]) 30 | return false; 31 | 32 | bool winning = false; 33 | if (index[a] < best_code[0]) 34 | { 35 | best_code[0] = index[a]; 36 | winning = true; 37 | } 38 | 39 | int c = -1; 40 | for (int it=1;it<2*num_edges;it++) 41 | { 42 | bool newvertex = index[b] == -1; 43 | 44 | if (newvertex) 45 | index[b] = colours[b] * num_nodes + n++; 46 | 47 | if (!winning && index[b] > best_code[it]) 48 | return false; 49 | 50 | if (winning || index[b] < best_code[it]) 51 | { 52 | winning = true; 53 | best_code[it] = index[b]; 54 | } 55 | 56 | if (newvertex) 57 | { 58 | //When a new vertex is reached, take the right-most edge 59 | //relative to the edge on which the vertex is reached. 60 | 61 | c = common[a][b]; 62 | } 63 | else if (m[b][a] == false) 64 | { 65 | //When an old vertex is reached on a new path, go back 66 | //in the opposite direction. 67 | 68 | c = a; 69 | } 70 | else 71 | { 72 | //When an old vertex is reached on an old path, leave the 73 | //vertex on the right-most edge that has not previously 74 | //been traversed in that direction. 75 | 76 | c = common[a][b]; 77 | while (m[b][c] == true) 78 | c = common[c][b]; 79 | } 80 | 81 | m[a][b] = true; 82 | a = b; 83 | b = c; 84 | } 85 | 86 | if (winning) 87 | { 88 | memcpy(canonical_labelling, index, sizeof(int8_t) * num_nodes); 89 | return true; 90 | } 91 | 92 | return false; 93 | } 94 | 95 | int canonical_form_coloured(int num_facets, int8_t facets[][3], int num_nodes, int8_t* degree, int8_t* colours, int8_t* canonical_labelling, int8_t* best_code, uint64_t* p_hash) 96 | { 97 | int8_t common[PTM_MAX_NBRS][PTM_MAX_NBRS] = {{0}}; 98 | int num_edges = 3 * num_facets / 2; 99 | if (!build_facet_map(num_facets, facets, common)) 100 | return -1; 101 | 102 | memset(best_code, SCHAR_MAX, sizeof(int8_t) * 2 * PTM_MAX_EDGES); 103 | 104 | bool equal = true; 105 | for (int i = 1;i=0;i--) 161 | canonical_labelling[i+1] = (canonical_labelling[i] % num_nodes) + 1; 162 | canonical_labelling[0] = 0; 163 | 164 | uint64_t hash = 0; 165 | for (int i = 0;i<2 * num_edges;i++) 166 | { 167 | uint64_t e = best_code[i]; 168 | e += i % 8; 169 | e &= 0xF; 170 | e <<= (4 * i) % 64; 171 | hash ^= e; 172 | } 173 | 174 | *p_hash = hash; 175 | return PTM_NO_ERROR; 176 | } 177 | 178 | } 179 | 180 | -------------------------------------------------------------------------------- /ptm_voronoi_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | Voro++ Copyright (c) 2008, The Regents of the University of California, through 3 | Lawrence Berkeley National Laboratory (subject to receipt of any required 4 | approvals from the U.S. Dept. of Energy). All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | (1) Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | (2) Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | (3) Neither the name of the University of California, Lawrence Berkeley 17 | National Laboratory, U.S. Dept. of Energy nor the names of its contributors may 18 | be used to endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 25 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 28 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | You are under no obligation whatsoever to provide any bug fixes, patches, or 33 | upgrades to the features, functionality or performance of the source code 34 | ("Enhancements") to anyone; however, if you choose to make your Enhancements 35 | available either publicly, or directly to Lawrence Berkeley National 36 | Laboratory, without imposing a separate written license agreement for such 37 | Enhancements, then you hereby grant the following license: a non-exclusive, 38 | royalty-free perpetual license to install, use, modify, prepare derivative 39 | works, incorporate into other computer software, distribute, and sublicense 40 | such enhancements or derivative works thereof, in binary and source code form. 41 | */ 42 | 43 | 44 | // Voro++, a 3D cell-based Voronoi library 45 | // 46 | // Author : Chris H. Rycroft (LBL / UC Berkeley) 47 | // Email : chr@alum.mit.edu 48 | // Date : August 30th 2011 49 | // 50 | // Modified by PM Larsen for use in Polyhedral Template Matching 51 | 52 | /** \file config.hh 53 | * \brief Master configuration file for setting various compile-time options. */ 54 | 55 | #ifndef PTM_VOROPP_CONFIG_HH 56 | #define PTM_VOROPP_CONFIG_HH 57 | 58 | namespace ptm_voro { 59 | 60 | // These constants set the initial memory allocation for the Voronoi cell 61 | /** The initial memory allocation for the number of vertices. */ 62 | const int init_vertices=256; 63 | /** The initial memory allocation for the maximum vertex order. */ 64 | const int init_vertex_order=64; 65 | /** The initial memory allocation for the number of regular vertices of order 66 | * 3. */ 67 | const int init_3_vertices=256; 68 | /** The initial memory allocation for the number of vertices of higher order. 69 | */ 70 | const int init_n_vertices=8; 71 | /** The initial buffer size for marginal cases used by the suretest class. */ 72 | const int init_marginal=64; 73 | /** The initial size for the delete stack. */ 74 | const int init_delete_size=256; 75 | /** The initial size for the auxiliary delete stack. */ 76 | const int init_delete2_size=256; 77 | /** The initial size for the wall pointer array. */ 78 | const int init_wall_size=32; 79 | /** The default initial size for the ordering class. */ 80 | const int init_ordering_size=4096; 81 | /** The initial size of the pre_container chunk index. */ 82 | const int init_chunk_size=256; 83 | 84 | // If the initial memory is too small, the program dynamically allocates more. 85 | // However, if the limits below are reached, then the program bails out. 86 | /** The maximum memory allocation for the number of vertices. */ 87 | const int max_vertices=16777216; 88 | /** The maximum memory allocation for the maximum vertex order. */ 89 | const int max_vertex_order=2048; 90 | /** The maximum memory allocation for the any particular order of vertex. */ 91 | const int max_n_vertices=16777216; 92 | /** The maximum buffer size for marginal cases used by the suretest class. */ 93 | const int max_marginal=16777216; 94 | /** The maximum size for the delete stack. */ 95 | const int max_delete_size=16777216; 96 | /** The maximum size for the auxiliary delete stack. */ 97 | const int max_delete2_size=16777216; 98 | /** The maximum amount of particle memory allocated for a single region. */ 99 | const int max_particle_memory=16777216; 100 | /** The maximum size for the wall pointer array. */ 101 | const int max_wall_size=2048; 102 | /** The maximum size for the ordering class. */ 103 | const int max_ordering_size=67108864; 104 | /** The maximum size for the pre_container chunk index. */ 105 | const int max_chunk_size=65536; 106 | 107 | /** The chunk size in the pre_container classes. */ 108 | const int pre_container_chunk_size=1024; 109 | 110 | #ifndef VOROPP_VERBOSE 111 | /** Voro++ can print a number of different status and debugging messages to 112 | * notify the user of special behavior, and this macro sets the amount which 113 | * are displayed. At level 0, no messages are printed. At level 1, messages 114 | * about unusual cases during cell construction are printed, such as when the 115 | * plane routine bails out due to floating point problems. At level 2, general 116 | * messages about memory expansion are printed. At level 3, technical details 117 | * about memory management are printed. */ 118 | #define VOROPP_VERBOSE 0 119 | #endif 120 | 121 | /** If a point is within this distance of a cutting plane, then the code 122 | * assumes that point exactly lies on the plane. */ 123 | const double tolerance=1e-11; 124 | 125 | /** If a point is within this distance of a cutting plane, then the code stores 126 | * whether this point is inside, outside, or exactly on the cutting plane in 127 | * the marginal cases buffer, to prevent the test giving a different result on 128 | * a subsequent evaluation due to floating point rounding errors. */ 129 | const double tolerance2=2e-11; 130 | 131 | /** The square of the tolerance, used when deciding whether some squared 132 | * quantities are large enough to be used. */ 133 | const double tolerance_sq=tolerance*tolerance; 134 | 135 | /** A large number that is used in the computation. */ 136 | const double large_number=1e30; 137 | 138 | /** A radius to use as a placeholder when no other information is available. */ 139 | const double default_radius=0.5; 140 | 141 | /** The maximum number of shells of periodic images to test over. */ 142 | const int max_unit_voro_shells=10; 143 | 144 | /** A guess for the optimal number of particles per block, used to set up the 145 | * container grid. */ 146 | const double optimal_particles=5.6; 147 | 148 | /** If this is set to 1, then the code reports any instances of particles being 149 | * put outside of the container geometry. */ 150 | #define VOROPP_REPORT_OUT_OF_BOUNDS 0 151 | 152 | /** Voro++ returns this status code if there is a file-related error, such as 153 | * not being able to open file. */ 154 | #define VOROPP_FILE_ERROR 1 155 | 156 | /** Voro++ returns this status code if there is a memory allocation error, if 157 | * one of the safe memory limits is exceeded. */ 158 | #define VOROPP_MEMORY_ERROR 2 159 | 160 | /** Voro++ returns this status code if there is any type of internal error, if 161 | * it detects that representation of the Voronoi cell is inconsistent. This 162 | * status code will generally indicate a bug, and the developer should be 163 | * contacted. */ 164 | #define VOROPP_INTERNAL_ERROR 3 165 | 166 | /** Voro++ returns this status code if it could not interpret the command line 167 | * arguments passed to the command line utility. */ 168 | #define VOROPP_CMD_LINE_ERROR 4 169 | 170 | } 171 | 172 | #endif 173 | -------------------------------------------------------------------------------- /ptm_neighbour_ordering.cpp: -------------------------------------------------------------------------------- 1 | /*Copyright (c) 2016 PM Larsen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | */ 9 | 10 | //todo: normalize vertices 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "ptm_constants.h" 20 | #include "ptm_voronoi_cell.h" 21 | #include "ptm_neighbour_ordering.h" 22 | #include "ptm_normalize_vertices.h" 23 | 24 | 25 | namespace ptm { 26 | 27 | typedef struct 28 | { 29 | double area; 30 | double dist; 31 | int ordering; 32 | } sorthelper_t; 33 | 34 | static bool sorthelper_compare(sorthelper_t const& a, sorthelper_t const& b) 35 | { 36 | if (a.area > b.area) 37 | return true; 38 | 39 | if (a.area < b.area) 40 | return false; 41 | 42 | if (a.dist < b.dist) 43 | return true; 44 | 45 | return false; 46 | } 47 | 48 | static double dot_product(double* a, double* b) 49 | { 50 | return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; 51 | } 52 | 53 | static void cross_product(double* a, double* b, double* c) 54 | { 55 | c[0] = a[1] * b[2] - a[2] * b[1]; 56 | c[1] = a[2] * b[0] - a[0] * b[2]; 57 | c[2] = a[0] * b[1] - a[1] * b[0]; 58 | } 59 | 60 | static double calculate_solid_angle(double* R1, double* R2, double* R3) //norms of R1-R3 must be 1 61 | { 62 | double R2R3[3]; 63 | cross_product(R2, R3, R2R3); 64 | double numerator = dot_product(R1, R2R3); 65 | 66 | double r1r2 = dot_product(R1, R2); 67 | double r2r3 = dot_product(R2, R3); 68 | double r3r1 = dot_product(R3, R1); 69 | 70 | double denominator = 1 + r1r2 + r3r1 + r2r3; 71 | return fabs(2 * atan2(numerator, denominator)); 72 | } 73 | 74 | //todo: change voronoi code to return errors rather than exiting 75 | static int calculate_voronoi_face_areas(int num_points, const double (*_points)[3], double* normsq, double max_norm, ptm_voro::voronoicell_neighbor* v, bool calc_solid_angles, 76 | std::vector& nbr_indices, std::vector& face_areas) 77 | { 78 | const double k = 10 * max_norm; 79 | v->init(-k,k,-k,k,-k,k); 80 | 81 | for (int i=0;inplane(x,y,z,normsq[i],i); 87 | } 88 | 89 | v->neighbors(nbr_indices); 90 | 91 | std::vector face_vertices; 92 | std::vector vertices; 93 | 94 | v->face_vertices(face_vertices); 95 | v->vertices(0, 0, 0, vertices); 96 | 97 | size_t num_vertices = vertices.size() / 3; 98 | for (size_t i=0;inumber_of_faces(); 111 | 112 | size_t c = 0; 113 | for (int current_face=0;current_face= 0) 119 | { 120 | double solid_angle = 0; 121 | int u = face_vertices[c]; 122 | int v = face_vertices[c+1]; 123 | for (int i=2;i nbr_indices(num + 6); 168 | std::vector face_areas(num + 6); 169 | int ret = calculate_voronoi_face_areas(num, points, normsq, max_norm, voronoi_handle, true, nbr_indices, face_areas); 170 | if (ret != 0) 171 | return ret; 172 | 173 | double areas[PTM_MAX_INPUT_POINTS] = {0}; 174 | for (size_t i=0;i= 0) 178 | areas[index] = face_areas[i]; 179 | } 180 | 181 | for (int i=0;i 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "atom.h" 26 | #include "comm.h" 27 | #include "compute_ptm_atom.h" 28 | #include "error.h" 29 | #include "force.h" 30 | #include "memory.h" 31 | #include "modify.h" 32 | #include "neigh_list.h" 33 | #include "neigh_request.h" 34 | #include "neighbor.h" 35 | #include "pair.h" 36 | #include "update.h" 37 | 38 | #include "ptm_functions.h" 39 | 40 | #define NUM_COLUMNS 7 41 | #define PTM_LAMMPS_UNKNOWN -1 42 | #define PTM_LAMMPS_OTHER 0 43 | 44 | using namespace LAMMPS_NS; 45 | 46 | static const char cite_user_ptm_package[] = 47 | "USER-PTM package:\n\n" 48 | "@Article{larsen2016ptm,\n" 49 | " author={Larsen, Peter Mahler and Schmidt, S{\\o}ren and Schi{\\o}tz, " 50 | "Jakob},\n" 51 | " title={Robust structural identification via polyhedral template " 52 | "matching},\n" 53 | " journal={Modelling~Simul.~Mater.~Sci.~Eng.},\n" 54 | " year={2016},\n" 55 | " number={5},\n" 56 | " volume={24},\n" 57 | " pages={055007},\n" 58 | " DOI = {10.1088/0965-0393/24/5/055007}" 59 | "}\n\n"; 60 | 61 | /* ---------------------------------------------------------------------- */ 62 | 63 | ComputePTMAtom::ComputePTMAtom(LAMMPS *lmp, int narg, char **arg) 64 | : Compute(lmp, narg, arg), list(NULL), output(NULL) { 65 | if (narg != 5) 66 | error->all(FLERR, "Illegal compute ptm/atom command"); 67 | 68 | char *structures = arg[3]; 69 | char *ptr = structures; 70 | 71 | const char *strings[] = {"fcc", "hcp", "bcc", "ico", "sc", 72 | "dcub", "dhex", "graphene", "all", "default"}; 73 | int num_strings = sizeof(strings) / sizeof(const char*); 74 | 75 | int32_t flags[] = { 76 | PTM_CHECK_FCC, 77 | PTM_CHECK_HCP, 78 | PTM_CHECK_BCC, 79 | PTM_CHECK_ICO, 80 | PTM_CHECK_SC, 81 | PTM_CHECK_DCUB, 82 | PTM_CHECK_DHEX, 83 | PTM_CHECK_GRAPHENE, 84 | PTM_CHECK_ALL, 85 | PTM_CHECK_FCC | PTM_CHECK_HCP | PTM_CHECK_BCC | PTM_CHECK_ICO}; 86 | 87 | input_flags = 0; 88 | while (*ptr != '\0') { 89 | 90 | bool found = false; 91 | for (int i = 0; i < num_strings; i++) { 92 | int len = strlen(strings[i]); 93 | if (strncmp(ptr, strings[i], len) == 0) { 94 | input_flags |= flags[i]; 95 | ptr += len; 96 | found = true; 97 | break; 98 | } 99 | } 100 | 101 | if (!found) 102 | error->all(FLERR, 103 | "Illegal compute ptm/atom command (invalid structure type)"); 104 | 105 | if (*ptr == '\0') 106 | break; 107 | 108 | if (*ptr != '-') 109 | error->all(FLERR, 110 | "Illegal compute ptm/atom command (invalid structure type)"); 111 | 112 | ptr++; 113 | } 114 | 115 | double threshold = force->numeric(FLERR, arg[4]); 116 | if (threshold < 0.0) 117 | error->all(FLERR, 118 | "Illegal compute ptm/atom command (threshold is negative)"); 119 | rmsd_threshold = threshold; 120 | if (rmsd_threshold == 0) 121 | rmsd_threshold = INFINITY; 122 | 123 | peratom_flag = 1; 124 | size_peratom_cols = NUM_COLUMNS; 125 | create_attribute = 1; 126 | nmax = 0; 127 | } 128 | 129 | /* ---------------------------------------------------------------------- */ 130 | 131 | ComputePTMAtom::~ComputePTMAtom() { memory->destroy(output); } 132 | 133 | /* ---------------------------------------------------------------------- */ 134 | 135 | void ComputePTMAtom::init() { 136 | if (force->pair == NULL) 137 | error->all(FLERR, "Compute ptm/atom requires a pair style be defined"); 138 | 139 | int count = 0; 140 | for (int i = 0; i < modify->ncompute; i++) 141 | if (strcmp(modify->compute[i]->style, "ptm/atom") == 0) 142 | count++; 143 | if (count > 1 && comm->me == 0) 144 | error->warning(FLERR, "More than one compute ptm/atom defined"); 145 | 146 | // need an occasional full neighbor list 147 | 148 | int irequest = neighbor->request(this, instance_me); 149 | neighbor->requests[irequest]->pair = 0; 150 | neighbor->requests[irequest]->compute = 1; 151 | neighbor->requests[irequest]->half = 0; 152 | neighbor->requests[irequest]->full = 1; 153 | neighbor->requests[irequest]->occasional = 1; 154 | } 155 | 156 | /* ---------------------------------------------------------------------- */ 157 | 158 | void ComputePTMAtom::init_list(int /* id */, NeighList *ptr) { list = ptr; } 159 | 160 | /* ---------------------------------------------------------------------- */ 161 | 162 | typedef struct 163 | { 164 | double **x; 165 | int *numneigh; 166 | int **firstneigh; 167 | int *ilist; 168 | int nlocal; 169 | 170 | } ptmnbrdata_t; 171 | 172 | 173 | typedef struct { 174 | int index; 175 | double d; 176 | } ptmnbr_t; 177 | 178 | static bool sorthelper_compare(ptmnbr_t const &a, ptmnbr_t const &b) { 179 | return a.d < b.d; 180 | } 181 | 182 | static int get_neighbours(void* vdata, size_t central_index, size_t atom_index, int num, size_t* nbr_indices, int32_t* numbers, double (*nbr_pos)[3]) 183 | { 184 | ptmnbrdata_t* data = (ptmnbrdata_t*)vdata; 185 | 186 | double **x = data->x; 187 | double *pos = x[atom_index]; 188 | 189 | int *jlist = NULL; 190 | int jnum = 0; 191 | if (atom_index < data->nlocal) { 192 | jlist = data->firstneigh[atom_index]; 193 | jnum = data->numneigh[atom_index]; 194 | } 195 | else { 196 | jlist = data->firstneigh[central_index]; 197 | jnum = data->numneigh[central_index]; 198 | } 199 | 200 | std::vector nbr_order; 201 | 202 | for (int jj = 0; jj < jnum; jj++) { 203 | int j = jlist[jj]; 204 | j &= NEIGHMASK; 205 | if (j == atom_index) 206 | continue; 207 | 208 | double dx = pos[0] - x[j][0]; 209 | double dy = pos[1] - x[j][1]; 210 | double dz = pos[2] - x[j][2]; 211 | double rsq = dx * dx + dy * dy + dz * dz; 212 | 213 | ptmnbr_t nbr = {j, rsq}; 214 | nbr_order.push_back(nbr); 215 | } 216 | 217 | std::sort(nbr_order.begin(), nbr_order.end(), &sorthelper_compare); 218 | int num_nbrs = std::min(num - 1, (int)nbr_order.size()); 219 | 220 | nbr_pos[0][0] = nbr_pos[0][1] = nbr_pos[0][2] = 0; 221 | nbr_indices[0] = atom_index; 222 | numbers[0] = 0; 223 | for (int jj = 0; jj < num_nbrs; jj++) { 224 | 225 | int j = nbr_order[jj].index; 226 | nbr_pos[jj + 1][0] = x[j][0] - pos[0]; 227 | nbr_pos[jj + 1][1] = x[j][1] - pos[1]; 228 | nbr_pos[jj + 1][2] = x[j][2] - pos[2]; 229 | 230 | nbr_indices[jj + 1] = j; 231 | numbers[jj + 1] = 0; 232 | } 233 | 234 | return num_nbrs + 1; 235 | } 236 | 237 | void ComputePTMAtom::compute_peratom() { 238 | // PTM global initialization. If already initialized this function does 239 | // nothing. 240 | ptm_initialize_global(); 241 | 242 | // initialize PTM local storage 243 | ptm_local_handle_t local_handle = ptm_initialize_local(); 244 | 245 | invoked_peratom = update->ntimestep; 246 | 247 | // grow arrays if necessary 248 | if (atom->nmax > nmax) { 249 | memory->destroy(output); 250 | nmax = atom->nmax; 251 | 252 | memory->create(output, nmax, NUM_COLUMNS, "ptm:ptm_output"); 253 | array_atom = output; 254 | } 255 | 256 | // invoke full neighbor list (will copy or build if necessary) 257 | neighbor->build_one(list); 258 | 259 | int inum = list->inum; 260 | int *ilist = list->ilist; 261 | int *numneigh = list->numneigh; 262 | int **firstneigh = list->firstneigh; 263 | 264 | double **x = atom->x; 265 | int *mask = atom->mask; 266 | ptmnbrdata_t nbrlist = {x, numneigh, firstneigh, ilist, atom->nlocal}; 267 | 268 | for (int ii = 0; ii < inum; ii++) { 269 | 270 | int i = ilist[ii]; 271 | output[i][0] = PTM_LAMMPS_UNKNOWN; 272 | if (!(mask[i] & groupbit)) 273 | continue; 274 | 275 | int jnum = numneigh[i]; 276 | if (jnum <= 0) 277 | continue; 278 | 279 | 280 | // now run PTM 281 | int32_t type, alloy_type; 282 | double scale, rmsd, interatomic_distance; 283 | double q[4]; 284 | bool standard_orientations = false; 285 | ptm_index(local_handle, i, get_neighbours, (void*)&nbrlist, 286 | input_flags, standard_orientations, 287 | &type, &alloy_type, &scale, &rmsd, q, 288 | NULL, NULL, NULL, NULL, &interatomic_distance, NULL, NULL); 289 | 290 | if (rmsd > rmsd_threshold) { 291 | type = PTM_MATCH_NONE; 292 | } 293 | 294 | // printf("%d type=%d rmsd=%f\n", i, type, rmsd); 295 | 296 | if (type == PTM_MATCH_NONE) { 297 | type = PTM_LAMMPS_OTHER; 298 | rmsd = INFINITY; 299 | } 300 | 301 | output[i][0] = type; 302 | output[i][1] = rmsd; 303 | output[i][2] = interatomic_distance; 304 | output[i][3] = q[0]; 305 | output[i][4] = q[1]; 306 | output[i][5] = q[2]; 307 | output[i][6] = q[3]; 308 | } 309 | 310 | // printf("finished ptm analysis\n"); 311 | ptm_uninitialize_local(local_handle); 312 | } 313 | 314 | /* ---------------------------------------------------------------------- 315 | memory usage of local atom-based array 316 | ------------------------------------------------------------------------- */ 317 | 318 | double ComputePTMAtom::memory_usage() { 319 | double bytes = nmax * NUM_COLUMNS * sizeof(double); 320 | bytes += nmax * sizeof(double); 321 | return bytes; 322 | } 323 | -------------------------------------------------------------------------------- /ptm_initialize_data.h: -------------------------------------------------------------------------------- 1 | /*Copyright (c) 2016 PM Larsen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | */ 9 | 10 | #ifndef PTM_INITIALIZE_DATA_H 11 | #define PTM_INITIALIZE_DATA_H 12 | 13 | 14 | #include "ptm_graph_data.h" 15 | #include "ptm_graph_tools.h" 16 | #include "ptm_deformation_gradient.h" 17 | #include "ptm_fundamental_mappings.h" 18 | #include "ptm_neighbour_ordering.h" 19 | #include "ptm_canonical_coloured.h" 20 | #include "ptm_convex_hull_incremental.h" 21 | #include "ptm_alt_templates.h" 22 | #include "ptm_quat.h" 23 | 24 | 25 | namespace ptm { 26 | 27 | typedef struct 28 | { 29 | int type; 30 | int num_nbrs; 31 | int num_facets; 32 | int max_degree; 33 | int num_graphs; 34 | graph_t* graphs; 35 | const double (*points)[3]; 36 | const double (*points_alt1)[3]; 37 | const double (*points_alt2)[3]; 38 | const double (*points_alt3)[3]; 39 | const double (*penrose)[3]; 40 | const double (*penrose_alt1)[3]; 41 | const double (*penrose_alt2)[3]; 42 | const double (*penrose_alt3)[3]; 43 | int num_mappings; 44 | const int8_t (*mapping)[PTM_MAX_POINTS]; 45 | int num_conventional_mappings; 46 | const int8_t (*mapping_conventional)[PTM_MAX_POINTS]; 47 | const int8_t (*mapping_conventional_inverse)[PTM_MAX_POINTS]; 48 | const int8_t *template_indices; 49 | const double (*qconventional)[4]; 50 | } refdata_t; 51 | 52 | 53 | const refdata_t structure_sc = { PTM_MATCH_SC, //.type 54 | 6, //.num_nbrs 55 | 8, //.num_facets 56 | 4, //.max_degree 57 | NUM_SC_GRAPHS, //.num_graphs 58 | graphs_sc, //.graphs 59 | ptm_template_sc, //.points 60 | NULL, //.points_alt1 61 | NULL, //.points_alt2 62 | NULL, //.points_alt3 63 | penrose_sc, //.penrose 64 | NULL, //.penrose_alt1 65 | NULL, //.penrose_alt2 66 | NULL, //.penrose_alt3 67 | NUM_CUBIC_MAPPINGS, //.num_mappings 68 | mapping_sc, //.mapping 69 | 0, //.num_conventional_mappings 70 | NULL, //.mapping_conventional 71 | NULL, //.mapping_conventional_inverse 72 | NULL, //.template_indices 73 | NULL, //.qconventional 74 | }; 75 | 76 | const refdata_t structure_fcc = { PTM_MATCH_FCC, //.type 77 | 12, //.num_nbrs 78 | 20, //.num_facets 79 | 6, //.max_degree 80 | NUM_FCC_GRAPHS, //.num_graphs 81 | graphs_fcc, //.graphs 82 | ptm_template_fcc, //.points 83 | NULL, //.points_alt1 84 | NULL, //.points_alt2 85 | NULL, //.points_alt3 86 | penrose_fcc, //.penrose 87 | NULL, //.penrose_alt1 88 | NULL, //.penrose_alt2 89 | NULL, //.penrose_alt3 90 | NUM_CUBIC_MAPPINGS, //.num_mappings 91 | mapping_fcc, //.mapping 92 | 0, //.num_conventional_mappings 93 | NULL, //.mapping_conventional 94 | NULL, //.mapping_conventional_inverse 95 | NULL, //.template_indices 96 | NULL, //.qconventional 97 | }; 98 | 99 | const refdata_t structure_hcp = { PTM_MATCH_HCP, //.type 100 | 12, //.num_nbrs 101 | 20, //.num_facets 102 | 6, //.max_degree 103 | NUM_HCP_GRAPHS, //.num_graphs 104 | graphs_hcp, //.graphs 105 | ptm_template_hcp, //.points 106 | ptm_template_hcp_alt1, //.points_alt1 107 | NULL, //.points_alt2 108 | NULL, //.points_alt3 109 | penrose_hcp, //.penrose 110 | penrose_hcp_alt1, //.penrose_alt1 111 | NULL, //.penrose_alt2 112 | NULL, //.penrose_alt3 113 | NUM_HEX_MAPPINGS, //.num_mappings 114 | mapping_hcp, //.mapping 115 | NUM_CONVENTIONAL_HEX_MAPPINGS, //.num_conventional_mappings 116 | mapping_hcp_conventional, //.mapping_conventional 117 | mapping_hcp_conventional_inverse, //.mapping_conventional_inverse 118 | template_indices_hcp, //.template_indices 119 | ptm::generator_hcp_conventional, //.qconventional 120 | }; 121 | 122 | const refdata_t structure_ico = { PTM_MATCH_ICO, //.type 123 | 12, //.num_nbrs 124 | 20, //.num_facets 125 | 6, //.max_degree 126 | NUM_ICO_GRAPHS, //.num_graphs 127 | graphs_ico, //.graphs 128 | ptm_template_ico, //.points 129 | NULL, //.points_alt1 130 | NULL, //.points_alt2 131 | NULL, //.points_alt3 132 | penrose_ico, //.penrose 133 | NULL, //.penrose_alt1 134 | NULL, //.penrose_alt2 135 | NULL, //.penrose_alt3 136 | NUM_ICO_MAPPINGS, //.num_mappings 137 | mapping_ico, //.mapping 138 | 0, //.num_conventional_mappings 139 | NULL, //.mapping_conventional 140 | NULL, //.mapping_conventional_inverse 141 | NULL, //.template_indices 142 | NULL, //.qconventional 143 | }; 144 | 145 | const refdata_t structure_bcc = { PTM_MATCH_BCC, //.type 146 | 14, //.num_nbrs 147 | 24, //.num_facets 148 | 8, //.max_degree 149 | NUM_BCC_GRAPHS, //.num_graphs 150 | graphs_bcc, //.graphs 151 | ptm_template_bcc, //.points 152 | NULL, //.points_alt1 153 | NULL, //.points_alt2 154 | NULL, //.points_alt3 155 | penrose_bcc, //.penrose 156 | NULL, //.penrose_alt1 157 | NULL, //.penrose_alt2 158 | NULL, //.penrose_alt3 159 | NUM_CUBIC_MAPPINGS, //.num_mappings 160 | mapping_bcc, //.mapping 161 | 0, //.num_conventional_mappings 162 | NULL, //.mapping_conventional 163 | NULL, //.mapping_conventional_inverse 164 | NULL, //.template_indices 165 | NULL, //.qconventional 166 | }; 167 | 168 | const refdata_t structure_dcub = { PTM_MATCH_DCUB, //.type 169 | 16, //.num_nbrs 170 | 28, //.num_facets 171 | 8, //.max_degree 172 | NUM_DCUB_GRAPHS, //.num_graphs 173 | graphs_dcub, //.graphs 174 | ptm_template_dcub, //.points 175 | ptm_template_dcub_alt1, //.points_alt1 176 | NULL, //.points_alt2 177 | NULL, //.points_alt3 178 | penrose_dcub, //.penrose 179 | penrose_dcub_alt1, //.penrose_alt1 180 | NULL, //.penrose_alt2 181 | NULL, //.penrose_alt3 182 | NUM_DCUB_MAPPINGS, //.num_mappings 183 | mapping_dcub, //.mapping 184 | NUM_CONVENTIONAL_DCUB_MAPPINGS, //.num_conventional_mappings 185 | mapping_dcub_conventional, //.mapping_conventional 186 | mapping_dcub_conventional_inverse, //.mapping_conventional_inverse 187 | template_indices_dcub, //.template_indices 188 | generator_cubic, //.qconventional 189 | }; 190 | 191 | const refdata_t structure_dhex = { PTM_MATCH_DHEX, //.type 192 | 16, //.num_nbrs 193 | 28, //.num_facets 194 | 8, //.max_degree 195 | NUM_DHEX_GRAPHS, //.num_graphs 196 | graphs_dhex, //.graphs 197 | ptm_template_dhex, //.points 198 | ptm_template_dhex_alt1, //.points_alt1 199 | ptm_template_dhex_alt2, //.points_alt2 200 | ptm_template_dhex_alt3, //.points_alt3 201 | penrose_dhex, //.penrose 202 | penrose_dhex_alt1, //.penrose_alt1 203 | penrose_dhex_alt2, //.penrose_alt2 204 | penrose_dhex_alt3, //.penrose_alt3 205 | NUM_DHEX_MAPPINGS, //.num_mappings 206 | mapping_dhex, //.mapping 207 | NUM_CONVENTIONAL_DHEX_MAPPINGS, //.num_conventional_mappings 208 | mapping_dhex_conventional, //.mapping_conventional 209 | mapping_dhex_conventional_inverse, //.mapping_conventional_inverse 210 | template_indices_dhex, //.template_indices 211 | generator_hcp_conventional, //.qconventional 212 | }; 213 | 214 | const refdata_t structure_graphene = { PTM_MATCH_GRAPHENE, //.type 215 | 9, //.num_nbrs 216 | -1, //.num_facets 217 | -1, //.max_degree 218 | -1, //.num_graphs 219 | NULL, //.graphs 220 | ptm_template_graphene, //.points 221 | ptm_template_graphene_alt1, //.points_alt1 222 | NULL, //.points_alt2 223 | NULL, //.points_alt3 224 | penrose_graphene, //.penrose 225 | penrose_graphene_alt1, //.penrose_alt1 226 | NULL, //.penrose_alt2 227 | NULL, //.penrose_alt3 228 | -1, //.num_mappings 229 | mapping_graphene, //.mapping 230 | NUM_CONVENTIONAL_GRAPHENE_MAPPINGS, //.num_conventional_mappings 231 | mapping_graphene_conventional, //.mapping_conventional 232 | mapping_graphene_conventional_inverse, //.mapping_conventional_inverse 233 | template_indices_graphene, //.template_indices 234 | generator_hcp_conventional, //.qconventional 235 | }; 236 | 237 | const refdata_t* const refdata[] = { NULL, 238 | &structure_fcc, 239 | &structure_hcp, 240 | &structure_bcc, 241 | &structure_ico, 242 | &structure_sc, 243 | &structure_dcub, 244 | &structure_dhex, 245 | &structure_graphene }; 246 | } 247 | 248 | #ifdef __cplusplus 249 | extern "C" { 250 | #endif 251 | 252 | typedef struct ptm_local_handle* ptm_local_handle_t; 253 | ptm_local_handle_t ptm_initialize_local(); 254 | void ptm_uninitialize_local(ptm_local_handle_t ptr); 255 | int ptm_initialize_global(); 256 | 257 | //------------------------------------ 258 | // global initialization switch 259 | //------------------------------------ 260 | extern bool ptm_initialized; 261 | 262 | 263 | #ifdef __cplusplus 264 | } 265 | #endif 266 | 267 | 268 | #endif 269 | 270 | -------------------------------------------------------------------------------- /datagen/graph_gen.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from wb import weinberg 3 | from graph_tools import * 4 | import square_facets 5 | 6 | 7 | def clockwise(a, b, c): 8 | return np.dot(np.cross(a, b), c) < 0 9 | 10 | def make_clockwise(points, a, b, c): 11 | if not clockwise(points[a], points[b], points[c]): 12 | return (b, a, c) 13 | else: 14 | return (a, b, c) 15 | 16 | def generate_triangulations(points, colours, sqfacets, eqfacets): 17 | 18 | cs = np.array(list(itertools.product([0, 1], repeat=len(sqfacets)))) 19 | 20 | points = np.array([e / np.linalg.norm(e) for e in points]) 21 | 22 | triangulations = [] 23 | for ts in cs: 24 | facets = [] 25 | for f in eqfacets: 26 | unique = np.unique(colours[f]) 27 | if len(unique) > 1: 28 | facets += [f] 29 | else: 30 | m = unique[0] 31 | a, b, c = f 32 | facets += [(m, a, b)] 33 | facets += [(m, b, c)] 34 | facets += [(m, c, a)] 35 | 36 | for c, sqf in zip(ts, sqfacets): 37 | indices = [0, 1, 2, 3] 38 | f0 = sqf[np.roll(indices, 0 + c)[:3]] 39 | f1 = sqf[np.roll(indices, 2 + c)[:3]] 40 | facets += [f0, f1] 41 | facets = np.array([make_clockwise(points, *f) for f in facets]) 42 | triangulations += [facets] 43 | return np.array(triangulations) 44 | 45 | def is_equilateral(ps): 46 | 47 | angles = [] 48 | for i in range(3): 49 | 50 | anchor = ps[i] 51 | vs = [ps[(i+1)%3] - anchor, ps[(i+2)%3] - anchor] 52 | a, b = np.array([e / np.linalg.norm(e) for e in vs]) 53 | dot = min(1, max(-1, np.dot(a, b))) 54 | angles += [np.arccos(dot)] 55 | angles = np.rad2deg(angles) 56 | return all([abs(e - 60) < 1E-3 for e in angles]) 57 | 58 | def crap_calc_rmsd(P, Q): 59 | delta = P - Q 60 | return np.mean(inner1d(delta, delta)) 61 | 62 | def crap_kabsch(P, Q): 63 | A = np.dot(P.T, Q) 64 | 65 | V, S, W = np.linalg.svd(A) 66 | U = np.dot(V, W) 67 | return U 68 | 69 | def get_unique_graphs(points, shape=None): 70 | 71 | assert(len(points) in [6, 12, 14]) 72 | if not shape: 73 | raise Exception("no shape specified") 74 | 75 | data = [] 76 | combs = get_facet_combinations(points) 77 | colour_codes = [] 78 | 79 | for combit, (facets, num_triangles) in enumerate(combs): 80 | 81 | edges = facets_to_edges(facets) 82 | code, automorphisms = weinberg(facets) 83 | new = not any([code == e[0] for e in data]) 84 | 85 | if shape in ['fcc', 'hcp']: 86 | deltas = points[edges[:,0]] - points[edges[:,1]] 87 | distances = np.linalg.norm(deltas, axis=1) 88 | threshold = np.mean([min(distances), max(distances)]) 89 | colours = distances < threshold 90 | 91 | edge_colours = dict() 92 | for (a, b), colour in zip(edges, colours): 93 | edge_colours[(a, b)] = colour 94 | edge_colours[(b, a)] = colour 95 | 96 | (colour_code, _dummy) = weinberg(facets, edge_colours=edge_colours) 97 | new = colour_code not in colour_codes 98 | 99 | if shape == 'bcc': 100 | (colour_code, _dummy) = weinberg(facets, vertex_colours=[0] * 8 + [1] * 6) 101 | new = colour_code not in colour_codes 102 | 103 | print combit, len(combs) 104 | m = automorphisms[0] 105 | automorphisms = [np.array(list(invert_array(e)[m]) + [len(e)]) for e in automorphisms] 106 | 107 | if not new: 108 | continue 109 | 110 | ideal_points = {'fcc': ideal_fcc, 'bcc': ideal_bcc, 'hcp': ideal_hcp, 'ico': ideal_ico, 'sc': ideal_sc}[shape] 111 | 112 | facets = [tuple(e) for e in np.sort(facets)] 113 | d = {} 114 | for i, k in enumerate(facets): 115 | if shape != 'bcc': 116 | d[k] = int(i < num_triangles) 117 | else: 118 | d[k] = sum([e < 8 for e in k]) 119 | 120 | clrs = [] 121 | auts = [] 122 | n = 0 123 | for aut in automorphisms: 124 | 125 | _facets = [tuple(sorted([aut[e] for e in f])) for f in facets] 126 | for e in _facets: 127 | assert(e in facets) 128 | 129 | clr = tuple([d[f] for f in _facets]) 130 | if clr not in clrs: 131 | clrs += [clr] 132 | auts += [tuple(aut)] 133 | print len(clrs), '/', len(automorphisms) 134 | 135 | trial_points = ideal_points.copy()[:-1] 136 | trial_points += random_perturbation[:len(trial_points)] 137 | trial_points = add_centre_subtract_mean(trial_points) 138 | trial_points = trial_points / np.mean(np.linalg.norm(trial_points, axis=1)) 139 | 140 | rmsds = [] 141 | for automorphism in automorphisms: 142 | mapped = ideal_points[automorphism] 143 | 144 | U = crap_kabsch(mapped, trial_points) 145 | rmsd = crap_calc_rmsd(np.dot(mapped, U), trial_points) 146 | rmsd = rmsd**0.5 147 | rmsds += [int(round(1E9*rmsd))] 148 | 149 | unique_rmsds = list(set(rmsds)) 150 | d = dict() 151 | for i, rmsd in enumerate(rmsds): 152 | if rmsd not in d: 153 | d[rmsd] = [] 154 | d[rmsd] += [i] 155 | unique_automorphisms = [automorphisms[min(e)] for e in d.values()] 156 | unique_automorphisms = sorted([e.tolist() for e in unique_automorphisms]) 157 | unique_automorphisms = [np.array(e) for e in unique_automorphisms] 158 | print "num unique:", len(unique_automorphisms) 159 | assert(len(unique_automorphisms) == len(clrs)) 160 | 161 | for e in unique_automorphisms: 162 | assert tuple(e.tolist()) in auts 163 | 164 | unique_automorphisms = [np.array([0] + list((e[:-1]+1))) for e in unique_automorphisms] #reorder automorphisms 165 | 166 | data += [(code, edges, facets, unique_automorphisms)] 167 | if shape in ['fcc', 'hcp', 'bcc']: 168 | colour_codes += [colour_code] 169 | return data 170 | 171 | 172 | def get_unique_diamond_graphs(name): 173 | 174 | assert name in ['dcub', 'dhex'] 175 | if name == 'dcub': 176 | points, colours = get_diamond_cubic_points() 177 | else: 178 | points, colours = get_diamond_hexagonal_points() 179 | points = points[:-1] 180 | colours = colours[:-1] 181 | 182 | sqfacets = square_facets.get_square_facets(points) 183 | squares = points[sqfacets] 184 | eqfacets = get_equilateral_facets(points) 185 | triangulations = generate_triangulations(points, colours, sqfacets, eqfacets) 186 | 187 | vertex_colours = np.array([0] * 4 + [1] * (len(points) - 4)) 188 | 189 | unique = set() 190 | data = [] 191 | colour_codes = [] 192 | for t in triangulations: 193 | 194 | #facets = t - np.min(t) 195 | facets = t 196 | colour_code, automorphisms = weinberg(facets, vertex_colours=vertex_colours, edge_colours=None) 197 | new = colour_code not in colour_codes 198 | 199 | m = automorphisms[0] 200 | automorphisms = [np.array(list(invert_array(e)[m]) + [len(e)]) for e in automorphisms] 201 | 202 | if not new: 203 | continue 204 | 205 | ideal_points = {'dcub': ideal_dcub, 'dhex': ideal_dhex}[name] 206 | 207 | facets = [tuple(e) for e in np.sort(facets)] 208 | d = {} 209 | for i, k in enumerate(facets): 210 | d[k] = 2 * sum([e < 4 for e in k]) + is_equilateral(ideal_points[list(k)]) 211 | 212 | clrs = [] 213 | auts = [] 214 | n = 0 215 | for aut in automorphisms: 216 | 217 | _facets = [tuple(sorted([aut[e] for e in f])) for f in facets] 218 | for e in _facets: 219 | assert(e in facets) 220 | 221 | clr = tuple([d[f] for f in _facets]) 222 | if clr not in clrs: 223 | clrs += [clr] 224 | auts += [tuple(aut)] 225 | print len(clrs), '/', len(automorphisms) 226 | 227 | trial_points = ideal_points.copy()[:-1] 228 | trial_points += random_perturbation[:len(trial_points)] 229 | trial_points = add_centre_subtract_mean(trial_points) 230 | trial_points = trial_points / np.mean(np.linalg.norm(trial_points, axis=1)) 231 | 232 | rmsds = [] 233 | for automorphism in automorphisms: 234 | mapped = ideal_points[automorphism] 235 | 236 | U = crap_kabsch(mapped, trial_points) 237 | rmsd = crap_calc_rmsd(np.dot(mapped, U), trial_points) 238 | rmsd = rmsd**0.5 239 | rmsds += [int(round(1E9*rmsd))] 240 | 241 | unique_rmsds = list(set(rmsds)) 242 | d = dict() 243 | for i, rmsd in enumerate(rmsds): 244 | if rmsd not in d: 245 | d[rmsd] = [] 246 | d[rmsd] += [i] 247 | unique_automorphisms = [automorphisms[min(e)] for e in d.values()] 248 | unique_automorphisms = sorted([e.tolist() for e in unique_automorphisms]) 249 | unique_automorphisms = [np.array(e) for e in unique_automorphisms] 250 | print "num unique:", len(unique_automorphisms) 251 | assert(len(unique_automorphisms) == len(clrs)) 252 | 253 | for e in unique_automorphisms: 254 | assert tuple(e.tolist()) in auts 255 | 256 | unique_automorphisms = [np.array([0] + list((e[:-1]+1))) for e in unique_automorphisms] #reorder automorphisms 257 | 258 | data += [(colour_code, None, facets, unique_automorphisms)] 259 | colour_codes += [colour_code] 260 | return data 261 | 262 | 263 | _length = 0.02 264 | random_perturbation = np.random.uniform(-1, 1, (17, 3)) 265 | for i, e in enumerate(random_perturbation): 266 | random_perturbation[i] = _length * e / np.linalg.norm(e) 267 | 268 | def print_graph_data(_id, num_vertices, edges, facets, automorphisms, automorphism_index, num_automorphisms): 269 | 270 | lid = "%d" % _id 271 | lhash = "0" 272 | l1 = "%d" % automorphism_index 273 | l2 = "%d" % num_automorphisms 274 | lcan = "{" + ", ".join(["0"] * (num_vertices + 1)) + "}" 275 | l3 = "{%s}" % (','.join(["{%d,%d,%d}" % (a, b, c) for (a, b, c) in facets])) 276 | return "{" + ",\n".join([lid, lhash, l1, l2, lcan, l3]) + "}" 277 | 278 | def dat_to_string(dat, name, auts): 279 | 280 | l = ["graph_t graphs_%s[NUM_%s_GRAPHS] = {" % (name, name.upper())] 281 | 282 | for i, (Gref, edges, facets, automorphisms) in enumerate(dat): 283 | if type(Gref) == tuple: 284 | num_vertices = max(Gref) + 1 285 | else: 286 | num_vertices = len(Gref.degree()) 287 | 288 | if len(automorphisms) == 1: 289 | s = print_graph_data(i, num_vertices, edges, facets, automorphisms, 0, len(automorphisms)) + ',' 290 | else: 291 | s = print_graph_data(i, num_vertices, edges, facets, automorphisms, len(auts), len(automorphisms)) + ',' 292 | for e in automorphisms: 293 | auts += [e] 294 | l += [s] 295 | 296 | l += ['};'] 297 | return "\n\n".join(l), auts 298 | 299 | import hashlib 300 | def md5(fname): 301 | hash_md5 = hashlib.md5() 302 | with open(fname, "rb") as f: 303 | for chunk in iter(lambda: f.read(4096), b""): 304 | hash_md5.update(chunk) 305 | return hash_md5.hexdigest() 306 | 307 | def go(): 308 | structures = [ ('sc', ideal_sc), 309 | ('ico', ideal_ico), 310 | ('fcc', ideal_fcc), 311 | ('hcp', ideal_hcp), 312 | ('bcc', ideal_bcc) ][:] 313 | 314 | auts = [range(17)] 315 | dump = [] 316 | n = 0 317 | for (name, points) in structures: 318 | 319 | dat = get_unique_graphs(points[:-1], shape=name) 320 | sizes = sorted([len(automorphisms) for (G, edges, facets, automorphisms) in dat], reverse=True) 321 | print len(dat), sum(sizes), sizes 322 | n += sum(sizes) 323 | 324 | s, auts = dat_to_string(dat, name, auts) 325 | dump += [s] 326 | 327 | for name in ['dcub', 'dhex']: 328 | dat = get_unique_diamond_graphs(name) 329 | s, auts = dat_to_string(dat, name, auts) 330 | dump += [s] 331 | 332 | print "sum:", n 333 | 334 | for i, e in enumerate(auts): 335 | e = list(e) 336 | e += [-1] * (len(auts[0]) - len(e)) 337 | auts[i] = e 338 | print auts 339 | auts = np.array(auts) 340 | print auts.shape 341 | 342 | s = "int8_t automorphisms[%d][17] = {\n\t" % (len(auts)) + "\n\t".join(['{ ' + ", ".join([str(e).rjust(2) for e in aut]) + '},' for aut in auts]) + "\n};\n\n" 343 | 344 | output_string = s + "\n\n".join(dump) + '\n' 345 | open('dumped_data.txt', 'w').write(output_string) 346 | checksum = md5('dumped_data.txt') 347 | print checksum 348 | #assert checksum.lower() == 'c5257bc0f0de736c4632b24be661fe21' 349 | #print "ok" 350 | 351 | if __name__ == "__main__": 352 | go() 353 | -------------------------------------------------------------------------------- /datagen/generators.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from numpy import sqrt 3 | 4 | 5 | generator_laue_C1 = [ [1.0, 0, 0, 0] ] 6 | 7 | generator_laue_C2 = [ [1.0, 0, 0, 0], 8 | [0, 0, 0, 1.0] ] 9 | 10 | generator_laue_C3 = [ [1.0, 0, 0, 0], 11 | [0.5, 0, 0, np.sqrt(3)/2], 12 | [0.5, 0, 0, -np.sqrt(3)/2] ] 13 | 14 | generator_laue_C4 = [ [1.0, 0, 0, 0], 15 | [np.sqrt(2)/2, 0, 0, np.sqrt(2)/2], 16 | [0, 0, 0, 1.0], 17 | [np.sqrt(2)/2, 0, 0, -np.sqrt(2)/2] ] 18 | 19 | generator_laue_C6 = [ [1.0, 0, 0, 0], 20 | [np.sqrt(3)/2, 0, 0, 0.5], 21 | [0.5, 0, 0, np.sqrt(3)/2], 22 | [0, 0, 0, 1.0], 23 | [0.5, 0, 0, -np.sqrt(3)/2], 24 | [np.sqrt(3)/2, 0, 0, -0.5] ] 25 | 26 | generator_laue_D2 = [ [1.0, 0, 0, 0], 27 | [0, 0, 0, 1.0], 28 | [0, 1.0, 0, 0], 29 | [0, 0, 1.0, 0] ] 30 | 31 | generator_laue_D3 = [ [1.0, 0, 0, 0], 32 | [0.5, 0, 0, np.sqrt(3)/2], 33 | [0, 1.0, 0, 0], 34 | [0.5, 0, 0, -np.sqrt(3)/2], 35 | [0, -0.5, np.sqrt(3)/2, 0], 36 | [0, 0.5, np.sqrt(3)/2, 0] ] 37 | 38 | generator_laue_D4 = [ [1.0, 0, 0, 0], 39 | [np.sqrt(2)/2, 0, 0, np.sqrt(2)/2], 40 | [0, 1.0, 0, 0], 41 | [0, 0, 0, 1.0], 42 | [0, -np.sqrt(2)/2, np.sqrt(2)/2, 0], 43 | [np.sqrt(2)/2, 0, 0, -np.sqrt(2)/2], 44 | [0, 0, 1.0, 0], 45 | [0, np.sqrt(2)/2, np.sqrt(2)/2, 0] ] 46 | 47 | generator_laue_D6 = [ [1.0, 0, 0, 0], 48 | [np.sqrt(3)/2, 0, 0, 0.5], 49 | [0, 1.0, 0, 0], 50 | [0.5, 0, 0, np.sqrt(3)/2], 51 | [0, -np.sqrt(3)/2, 0.5, 0], 52 | [0, 0, 0, 1.0], 53 | [0, -0.5, np.sqrt(3)/2, 0], 54 | [0.5, 0, 0, -np.sqrt(3)/2], 55 | [0, 0, 1.0, 0], 56 | [np.sqrt(3)/2, 0, 0, -0.5], 57 | [0, 0.5, np.sqrt(3)/2, 0], 58 | [0, np.sqrt(3)/2, 0.5, 0] ] 59 | 60 | generator_laue_T = [ [1.0, 0, 0, 0], 61 | [0, 0, 0, 1.0], 62 | [0.5, 0.5, 0.5, 0.5], 63 | [0.5, -0.5, 0.5, -0.5], 64 | [0.5, -0.5, -0.5, -0.5], 65 | [0.5, 0.5, 0.5, -0.5], 66 | [0, 1.0, 0, 0], 67 | [0.5, 0.5, -0.5, 0.5], 68 | [0, 0, 1.0, 0], 69 | [0.5, 0.5, -0.5, -0.5], 70 | [0.5, -0.5, 0.5, 0.5], 71 | [0.5, -0.5, -0.5, 0.5] ] 72 | 73 | generator_cubic = [ [ 1, 0, 0, 0], 74 | [ np.sqrt(2)/2, np.sqrt(2)/2, 0, 0], 75 | [ np.sqrt(2)/2, 0, np.sqrt(2)/2, 0], 76 | [ np.sqrt(2)/2, 0, 0, np.sqrt(2)/2], 77 | [ np.sqrt(2)/2, 0, 0, -np.sqrt(2)/2], 78 | [ np.sqrt(2)/2, 0, -np.sqrt(2)/2, 0], 79 | [ np.sqrt(2)/2, -np.sqrt(2)/2, -0, -0], 80 | [ 0.5, 0.5, 0.5, 0.5], 81 | [ 0.5, 0.5, 0.5, -0.5], 82 | [ 0.5, 0.5, -0.5, 0.5], 83 | [ 0.5, 0.5, -0.5, -0.5], 84 | [ 0.5, -0.5, 0.5, 0.5], 85 | [ 0.5, -0.5, 0.5, -0.5], 86 | [ 0.5, -0.5, -0.5, 0.5], 87 | [ 0.5, -0.5, -0.5, -0.5], 88 | [ 0, 1, 0, 0], 89 | [ 0, np.sqrt(2)/2, np.sqrt(2)/2, 0], 90 | [ 0, np.sqrt(2)/2, 0, np.sqrt(2)/2], 91 | [ 0, np.sqrt(2)/2, 0, -np.sqrt(2)/2], 92 | [ 0, np.sqrt(2)/2, -np.sqrt(2)/2, 0], 93 | [ 0, 0, 1, 0], 94 | [ 0, 0, np.sqrt(2)/2, np.sqrt(2)/2], 95 | [ 0, 0, np.sqrt(2)/2, -np.sqrt(2)/2], 96 | [ 0, 0, 0, 1]] 97 | 98 | generator_hcp = [ 99 | [ 1, 0, 0, 0 ], 100 | [ 0.5, 0, 0, np.sqrt(3)/2 ], 101 | [ 0.5, -0, -0, -np.sqrt(3)/2 ], 102 | [ 0, np.sqrt(3)/2, 0.5, 0 ], 103 | [ 0, np.sqrt(3)/2, -0.5, 0 ], 104 | [ 0, 0, 1, 0 ], 105 | ] 106 | 107 | generator_ico = [ 108 | [ 1.00000000000000, 0.00000000000000, 0.00000000000000, 0.00000000000000], 109 | [ 0.80901699437495, 0.50000000000000, 0.16245984811645, 0.26286555605957], 110 | [ 0.80901699437495, 0.50000000000000, -0.16245984811645, -0.26286555605957], 111 | [ 0.80901699437495, 0.30901699437495, 0.42532540417602, -0.26286555605957], 112 | [ 0.80901699437495, 0.30901699437495, -0.42532540417602, 0.26286555605957], 113 | [ 0.80901699437495, 0.00000000000000, 0.52573111211913, 0.26286555605957], 114 | [ 0.80901699437495, 0.00000000000000, 0.00000000000000, 0.58778525229247], 115 | [ 0.80901699437495, 0.00000000000000, 0.00000000000000, -0.58778525229247], 116 | [ 0.80901699437495, 0.00000000000000, -0.52573111211913, -0.26286555605957], 117 | [ 0.80901699437495, -0.30901699437495, 0.42532540417602, -0.26286555605957], 118 | [ 0.80901699437495, -0.30901699437495, -0.42532540417602, 0.26286555605957], 119 | [ 0.80901699437495, -0.50000000000000, 0.16245984811645, 0.26286555605957], 120 | [ 0.80901699437495, -0.50000000000000, -0.16245984811645, -0.26286555605957], 121 | [ 0.50000000000000, 0.80901699437495, 0.26286555605957, -0.16245984811645], 122 | [ 0.50000000000000, 0.80901699437495, -0.26286555605957, 0.16245984811645], 123 | [ 0.50000000000000, 0.50000000000000, 0.68819096023559, 0.16245984811645], 124 | [ 0.50000000000000, 0.50000000000000, 0.16245984811645, -0.68819096023559], 125 | [ 0.50000000000000, 0.50000000000000, -0.16245984811645, 0.68819096023559], 126 | [ 0.50000000000000, 0.50000000000000, -0.68819096023559, -0.16245984811645], 127 | [ 0.50000000000000, 0.30901699437495, 0.42532540417602, 0.68819096023559], 128 | [ 0.50000000000000, 0.30901699437495, -0.42532540417602, -0.68819096023559], 129 | [ 0.50000000000000, 0.00000000000000, 0.85065080835204, -0.16245984811645], 130 | [ 0.50000000000000, -0.00000000000000, 0.52573111211913, -0.68819096023559], 131 | [ 0.50000000000000, 0.00000000000000, -0.52573111211913, 0.68819096023559], 132 | [ 0.50000000000000, -0.00000000000000, -0.85065080835204, 0.16245984811645], 133 | [ 0.50000000000000, -0.30901699437495, 0.42532540417602, 0.68819096023559], 134 | [ 0.50000000000000, -0.30901699437495, -0.42532540417602, -0.68819096023559], 135 | [ 0.50000000000000, -0.50000000000000, 0.68819096023559, 0.16245984811645], 136 | [ 0.50000000000000, -0.50000000000000, 0.16245984811645, -0.68819096023559], 137 | [ 0.50000000000000, -0.50000000000000, -0.16245984811645, 0.68819096023559], 138 | [ 0.50000000000000, -0.50000000000000, -0.68819096023559, -0.16245984811645], 139 | [ 0.50000000000000, -0.80901699437495, 0.26286555605957, -0.16245984811645], 140 | [ 0.50000000000000, -0.80901699437495, -0.26286555605957, 0.16245984811645], 141 | [ 0.30901699437495, 0.80901699437495, 0.26286555605957, 0.42532540417602], 142 | [ 0.30901699437495, 0.80901699437495, -0.26286555605957, -0.42532540417602], 143 | [ 0.30901699437495, 0.50000000000000, 0.68819096023559, -0.42532540417602], 144 | [ 0.30901699437495, 0.50000000000000, -0.68819096023559, 0.42532540417602], 145 | [ 0.30901699437495, 0.00000000000000, 0.85065080835204, 0.42532540417602], 146 | [ 0.30901699437495, 0.00000000000000, 0.00000000000000, 0.95105651629515], 147 | [ 0.30901699437495, -0.00000000000000, -0.00000000000000, -0.95105651629515], 148 | [ 0.30901699437495, -0.00000000000000, -0.85065080835204, -0.42532540417602], 149 | [ 0.30901699437495, -0.50000000000000, 0.68819096023559, -0.42532540417602], 150 | [ 0.30901699437495, -0.50000000000000, -0.68819096023559, 0.42532540417602], 151 | [ 0.30901699437495, -0.80901699437495, 0.26286555605957, 0.42532540417602], 152 | [ 0.30901699437495, -0.80901699437495, -0.26286555605957, -0.42532540417602], 153 | [ 0.00000000000000, 1.00000000000000, 0.00000000000000, 0.00000000000000], 154 | [ 0.00000000000000, 0.80901699437495, 0.58778525229247, 0.00000000000000], 155 | [ 0.00000000000000, 0.80901699437495, 0.26286555605957, -0.52573111211913], 156 | [ 0.00000000000000, 0.80901699437495, -0.26286555605957, 0.52573111211913], 157 | [ 0.00000000000000, 0.80901699437495, -0.58778525229247, 0.00000000000000], 158 | [ 0.00000000000000, 0.50000000000000, 0.68819096023559, 0.52573111211913], 159 | [ 0.00000000000000, 0.50000000000000, 0.16245984811645, 0.85065080835204], 160 | [ -0.00000000000000, 0.50000000000000, -0.16245984811645, -0.85065080835204], 161 | [ -0.00000000000000, 0.50000000000000, -0.68819096023559, -0.52573111211913], 162 | [ 0.00000000000000, 0.30901699437495, 0.95105651629515, 0.00000000000000], 163 | [ -0.00000000000000, 0.30901699437495, 0.42532540417602, -0.85065080835204], 164 | [ 0.00000000000000, 0.30901699437495, -0.42532540417602, 0.85065080835204], 165 | [ -0.00000000000000, 0.30901699437495, -0.95105651629515, -0.00000000000000], 166 | [ 0.00000000000000, 0.00000000000000, 0.85065080835204, -0.52573111211913], 167 | [ 0.00000000000000, 0.00000000000000, 0.52573111211913, 0.85065080835204], 168 | ] 169 | 170 | generator_dcub = [ 171 | [ 1.00000000000000, 0.00000000000000, 0.00000000000000, 0.00000000000000], 172 | [ 0.50000000000000, 0.50000000000000, 0.50000000000000, 0.50000000000000], 173 | [ 0.50000000000000, 0.50000000000000, 0.50000000000000, -0.50000000000000], 174 | [ 0.50000000000000, 0.50000000000000, -0.50000000000000, 0.50000000000000], 175 | [ 0.50000000000000, 0.50000000000000, -0.50000000000000, -0.50000000000000], 176 | [ 0.50000000000000, -0.50000000000000, 0.50000000000000, 0.50000000000000], 177 | [ 0.50000000000000, -0.50000000000000, 0.50000000000000, -0.50000000000000], 178 | [ 0.50000000000000, -0.50000000000000, -0.50000000000000, 0.50000000000000], 179 | [ 0.50000000000000, -0.50000000000000, -0.50000000000000, -0.50000000000000], 180 | [ 0.00000000000000, 1.00000000000000, 0.00000000000000, 0.00000000000000], 181 | [ 0.00000000000000, 0.00000000000000, 1.00000000000000, 0.00000000000000], 182 | [ 0.00000000000000, 0.00000000000000, 0.00000000000000, 1.00000000000000], 183 | ] 184 | 185 | generator_dhex = [ 186 | [ 1.00000000000000, 0.00000000000000, 0.00000000000000, 0.00000000000000], 187 | [ 0.50000000000000, 0.00000000000000, 0.00000000000000, 0.86602540378444], 188 | [ 0.50000000000000, -0.00000000000000, -0.00000000000000, -0.86602540378444], 189 | ] 190 | 191 | 192 | generator_hcp_conventional = [ 193 | [ 1, 0, 0, 0 ], 194 | [ sqrt(3)/2, 0, 0, 0.5 ], 195 | [ sqrt(3)/2, 0, 0, -0.5 ], 196 | [ 0.5, 0, 0, sqrt(3)/2 ], 197 | [ 0.5, 0, 0, -sqrt(3)/2 ], 198 | [ 0, 1, 0, 0 ], 199 | [ 0, sqrt(3)/2, 0.5, 0 ], 200 | [ 0, sqrt(3)/2, -0.5, 0 ], 201 | [ 0, 0.5, sqrt(3)/2, 0 ], 202 | [ 0, 0.5, -sqrt(3)/2, 0 ], 203 | [ 0, 0, 1, 0 ], 204 | [ 0, 0, 0, 1 ], 205 | ] 206 | 207 | -------------------------------------------------------------------------------- /datagen/graph_tools.py: -------------------------------------------------------------------------------- 1 | import math 2 | import numpy as np 3 | import scipy.spatial 4 | import itertools 5 | from numpy.core.umath_tests import inner1d 6 | from numpy import sqrt 7 | 8 | def get_ico(): 9 | l = np.array([ 10 | [-0, 0, 1], 11 | [ 0, -0, -1], 12 | [-2 * np.sqrt(1./5 * (5./8 - np.sqrt(5) / 8)), (1+np.sqrt(5)) / (2 * np.sqrt(5)), -np.sqrt(1./5)], 13 | [ 2 * np.sqrt(1./5 * (5./8 - np.sqrt(5) / 8)), -(1+np.sqrt(5)) / (2 * np.sqrt(5)), np.sqrt(1./5)], 14 | [ 0, -np.sqrt(4./5), -np.sqrt(1./5)], 15 | [ 0, np.sqrt(4./5), np.sqrt(1./5)], 16 | [ 2 * np.sqrt(1./5 * (5./8 + np.sqrt(5)/8)), -(np.sqrt(5) - 1) / (2 * np.sqrt(5)), -np.sqrt(1./5)], 17 | [-2 * np.sqrt(1./5 * (5./8 + np.sqrt(5)/8)), (np.sqrt(5) - 1) / (2 * np.sqrt(5)), np.sqrt(1./5)], 18 | [-2 * np.sqrt(1./5 * (5./8 + np.sqrt(5)/8)), -(np.sqrt(5) - 1) / (2 * np.sqrt(5)), -np.sqrt(1./5)], 19 | [ 2 * np.sqrt(1./5 * (5./8 + np.sqrt(5)/8)), (np.sqrt(5) - 1) / (2 * np.sqrt(5)), np.sqrt(1./5)], 20 | [ 2 * np.sqrt(1./5 * (5./8 - np.sqrt(5)/8)), (1+np.sqrt(5)) / (2 * np.sqrt(5)), -np.sqrt(1./5)], 21 | [-2 * np.sqrt(1./5 * (5./8 - np.sqrt(5)/8)), -(1+np.sqrt(5)) / (2 * np.sqrt(5)), np.sqrt(1./5)], 22 | [ 0, 0, 0], 23 | ]) 24 | return np.array(l) / np.linalg.norm(l[0]) 25 | 26 | def get_fcc(): 27 | l = [ 28 | [ 1, 1, 0], 29 | [ 0, 1, 1], 30 | [ 1, 0, 1], 31 | [-1, -1, 0], 32 | [ 0, -1, -1], 33 | [-1, 0, -1], 34 | [-1, 1, 0], 35 | [ 0, -1, 1], 36 | [-1, 0, 1], 37 | [ 1, -1, 0], 38 | [ 0, 1, -1], 39 | [ 1, 0, -1], 40 | [0, 0, 0]] 41 | return np.array(l) / np.linalg.norm(l[0]) 42 | 43 | def get_bcc(): 44 | 45 | l = [ 46 | [ 1, 1, 1], 47 | [-1, 1, 1], 48 | [ 1, 1, -1], 49 | [-1, -1, 1], 50 | [ 1, -1, 1], 51 | [-1, 1, -1], 52 | [-1, -1, -1], 53 | [ 1, -1, -1], 54 | [ 2, 0, 0], 55 | [-2, 0, 0], 56 | [ 0, 2, 0], 57 | [ 0, -2, 0], 58 | [ 0, 0, 2], 59 | [ 0, 0, -2], 60 | 61 | [0, 0, 0]] 62 | return np.array(l) / np.mean(np.linalg.norm(l[:-1], axis=1)) 63 | 64 | def get_hcp(): 65 | 66 | l = [ 67 | [ 3*sqrt(2), -3*sqrt(6), 0 ], 68 | [ -6*sqrt(2), 0, 0 ], 69 | [ -3*sqrt(2), sqrt(6), -4*sqrt(3) ], 70 | [ 3*sqrt(2), sqrt(6), -4*sqrt(3) ], 71 | [ 0, -2*sqrt(6), -4*sqrt(3) ], 72 | [ -3*sqrt(2), 3*sqrt(6), 0 ], 73 | [ 3*sqrt(2), 3*sqrt(6), 0 ], 74 | [ 6*sqrt(2), 0, 0 ], 75 | [ -3*sqrt(2), -3*sqrt(6), 0 ], 76 | [ 0, -2*sqrt(6), 4*sqrt(3) ], 77 | [ 3*sqrt(2), sqrt(6), 4*sqrt(3) ], 78 | [ -3*sqrt(2), sqrt(6), 4*sqrt(3) ], 79 | 80 | [0, 0, 0]] 81 | 82 | return np.array(l) / np.linalg.norm(l[0]) 83 | 84 | def get_sc(): 85 | l = [ 86 | [ 0., 0., -1.], 87 | [ 0., 0., 1.], 88 | [ 0., -1., 0.], 89 | [ 0., 1., 0.], 90 | [-1., 0., 0.], 91 | [ 1., 0., 0.], 92 | [ 0., 0., 0.] 93 | ] 94 | return np.array(l) 95 | 96 | def get_graphene(): 97 | 98 | sqrt = np.sqrt 99 | l = np.array([ 100 | [ 0, -3./11+6*sqrt(3)/11, 0 ], 101 | [ -3*sqrt(3)/22+9./11, -3*sqrt(3)/11+3./22, 0 ], 102 | [ -9./11+3*sqrt(3)/22, -3*sqrt(3)/11+3./22, 0 ], 103 | [ -9./11+3*sqrt(3)/22, -9./22+9*sqrt(3)/11, 0 ], 104 | [ -3*sqrt(3)/22+9./11, -9./22+9*sqrt(3)/11, 0 ], 105 | [ -3*sqrt(3)/11+18./11, 0, 0 ], 106 | [ -3*sqrt(3)/22+9./11, -9*sqrt(3)/11+9./22, 0 ], 107 | [ -9./11+3*sqrt(3)/22, -9*sqrt(3)/11+9./22, 0 ], 108 | [ -18./11+3*sqrt(3)/11, 0, 0 ], 109 | [ 0, 0, 0 ], 110 | ]) 111 | return l 112 | 113 | 114 | def get_equilateral_facets(points): 115 | 116 | facets = np.sort(scipy.spatial.ConvexHull(points).simplices) 117 | facets = np.array(sorted([tuple(e) for e in facets])) 118 | 119 | eq = [] 120 | for i, f in enumerate(facets): 121 | p = points[f] 122 | v = p[1:] - p[0] 123 | v = [e / np.linalg.norm(e) for e in v] 124 | dot = np.dot(*v) 125 | dot = min(1, max(-1, dot)) 126 | angle = np.arccos(dot) 127 | if abs(math.degrees(angle) - 60) < 1E-3: 128 | eq += [i] 129 | 130 | return facets[eq] 131 | 132 | def select_inner_facets(points, facets, name): 133 | 134 | if name == 'dhex': 135 | return facets[[2,0,5,7]] 136 | elif name == 'dcub': 137 | return facets[[0,6,5,7]] 138 | 139 | midpoints = np.mean(points[facets], axis=1) 140 | cs = np.array(list(itertools.combinations(range(len(midpoints)), 4))) 141 | 142 | print midpoints 143 | print midpoints[[0,6,5,7]] 144 | asdf 145 | 146 | data = [] 147 | for c in cs: 148 | p = midpoints[c] 149 | if all([e[2] < np.max(midpoints[:,2]) * 0.99 for e in p]): 150 | continue 151 | 152 | dots = [np.dot(p[i], p[j]) for i in range(len(c)) for j in range(i+1, len(c))] 153 | mdev = max([abs(e - dots[0]) for e in dots]) 154 | data += [(mdev, list(c))] 155 | for e in sorted(data): 156 | print e 157 | data.sort() 158 | _, c = data[0] 159 | return facets[c] 160 | 161 | def get_inner_points(points, facets, name): 162 | 163 | facets = select_inner_facets(points, facets, name) 164 | 165 | z = [[0,0,0]] 166 | inner = [np.mean(np.concatenate((z, points[f])), axis=0) for f in facets] 167 | return np.array(inner) 168 | 169 | def colour_vertices(inner, points): 170 | 171 | ds = scipy.spatial.distance.cdist(points, inner) 172 | colours = np.argmin(ds, axis=1) 173 | indices = np.argsort(colours) 174 | points = points[indices] 175 | colours = np.concatenate(([0, 1, 2, 3], colours[indices])) 176 | return np.concatenate((inner, points)), colours 177 | 178 | def get_diamond_points(points, name): 179 | facets = get_equilateral_facets(points) 180 | inner = get_inner_points(points, facets, name) 181 | return colour_vertices(inner, points) 182 | 183 | def get_diamond_cubic_points(): 184 | points, colours = get_diamond_points(ideal_fcc[:-1], 'dcub') 185 | points /= np.mean(np.linalg.norm(points, axis=1)) 186 | points = np.concatenate((points, [[0, 0, 0]])) 187 | colours = np.concatenate((colours, [-1])) 188 | return points, colours 189 | 190 | def get_diamond_hexagonal_points(): 191 | points, colours = get_diamond_points(ideal_hcp[:-1], 'dhex') 192 | points /= np.mean(np.linalg.norm(points, axis=1)) 193 | points = np.concatenate((points, [[0, 0, 0]])) 194 | colours = np.concatenate((colours, [-1])) 195 | return points, colours 196 | 197 | 198 | def facets_to_edges(facets): 199 | 200 | edges = [] 201 | for (a, b, c) in facets: 202 | edges += [tuple(sorted([a, b]))] 203 | edges += [tuple(sorted([b, c]))] 204 | edges += [tuple(sorted([c, a]))] 205 | return np.array(sorted(list(set(edges)))) 206 | 207 | def add_centre_subtract_mean(points): 208 | points = np.concatenate((points, [[0, 0, 0]])) 209 | return points - np.mean(points, axis=0) 210 | 211 | def calc_plane_norms(triangles): 212 | plane_normals = np.cross(triangles[:,0] - triangles[:,2], triangles[:,1] - triangles[:,2]) 213 | plane_normals /= np.linalg.norm(plane_normals, axis=1)[:, np.newaxis] 214 | return plane_normals 215 | 216 | def invert_array(t): 217 | 218 | inverted = np.zeros(len(t)).astype(np.int) 219 | for i, e in enumerate(t): 220 | inverted[e] = i 221 | return np.array(inverted) 222 | 223 | def get_paired_squares(facets, points): 224 | 225 | data = [] 226 | for i in range(len(facets)): 227 | for j in range(i+1, len(facets)): 228 | if len(set(facets[i]).intersection(set(facets[j]))) == 2: 229 | 230 | indices = np.array([facets[i], facets[j]]) 231 | norms = calc_plane_norms(points[indices]) 232 | if np.abs(np.dot(norms[0], norms[1])) > 0.9: 233 | data += [(facets[i], facets[j])] 234 | return data 235 | 236 | def make_facets_clockwise(facets, points): 237 | 238 | triangles = points[facets] 239 | us = triangles[:,1] - triangles[:,0] 240 | vs = triangles[:,2] - triangles[:,0] 241 | 242 | crosses = np.cross(us, vs) 243 | dots = inner1d(triangles[:,0], crosses) 244 | clockwise = dots < 0 245 | 246 | for i, e in enumerate(clockwise): 247 | if not e: 248 | (a, b, c) = facets[i] 249 | facets[i] = (b, a, c) 250 | return facets 251 | 252 | def get_facet_combinations(points): 253 | 254 | facets = np.sort(scipy.spatial.ConvexHull(points).simplices) 255 | squares = get_paired_squares(facets, points) 256 | 257 | _squares = [tuple(e) for f in squares for e in f] 258 | triangle_facets = [tuple(sorted(e)) for e in facets if tuple(e) not in _squares] 259 | 260 | square_edges = [] 261 | for (a, b) in squares: 262 | common_edge = set(a).intersection(set(b)) 263 | common_edge = tuple(sorted(list(common_edge))) 264 | square_edges += [common_edge] 265 | 266 | sq_facets = [] 267 | for (a, b) in squares: 268 | 269 | perimeter = set(a) | set(b) 270 | assert(len(perimeter) == 4) 271 | 272 | common_edge = set(a) & set(b) 273 | assert(len(common_edge) == 2) 274 | 275 | new_edge = perimeter - common_edge 276 | assert(len(new_edge) == 2) 277 | 278 | common_edge = tuple(sorted(list(common_edge))) 279 | new_edge = tuple(sorted(list(new_edge))) 280 | 281 | facet_pair0 = [tuple(sorted(list(perimeter - set([common_edge[i]])))) for i in range(2)] 282 | facet_pair1 = [tuple(sorted(list(perimeter - set([new_edge[i]])))) for i in range(2)] 283 | sq_facets += [(facet_pair0, facet_pair1)] 284 | 285 | cs = [list(e) for e in itertools.product(*sq_facets)] 286 | 287 | comb = [] 288 | for i, c in enumerate(cs): 289 | 290 | square_facets = [e for f in c for e in f] 291 | facets = triangle_facets + square_facets 292 | facets = make_facets_clockwise(np.array(facets), points) 293 | comb += [(facets, len(triangle_facets))] 294 | return comb 295 | 296 | 297 | ideal_fcc = get_fcc() 298 | ideal_hcp = get_hcp() 299 | ideal_bcc = get_bcc() 300 | ideal_ico = get_ico() 301 | ideal_sc = get_sc() 302 | ideal_dcub, _ = get_diamond_cubic_points() 303 | ideal_dhex, _ = get_diamond_hexagonal_points() 304 | ideal_graphene = get_graphene() 305 | 306 | import quat_utils 307 | U0 = quat_utils.quaternion_to_rotation_matrix([ sqrt(3)/2, 0, 0, 0.5 ]) 308 | U1 = quat_utils.quaternion_to_rotation_matrix([ 0, sqrt(3)/2, 0.5, 0 ]) 309 | U2 = quat_utils.quaternion_to_rotation_matrix([ 0, 0.5, sqrt(3)/2, 0 ]) 310 | 311 | U3 = quat_utils.quaternion_to_rotation_matrix([ sqrt(2)/2, sqrt(2)/2, 0, 0 ]) 312 | 313 | 314 | ideal_hcp_alt = np.dot(ideal_hcp, U0.T) 315 | ideal_graphene_alt = np.dot(ideal_graphene, U0.T) 316 | 317 | ideal_dhex_alt1 = np.dot(ideal_dhex, U0.T) 318 | ideal_dhex_alt2 = np.dot(ideal_dhex, U1.T) 319 | ideal_dhex_alt3 = np.dot(ideal_dhex, U2.T) 320 | 321 | ideal_dcub_alt1 = np.dot(ideal_dcub, U3.T) 322 | 323 | print ideal_hcp_alt 324 | asdf 325 | 326 | 327 | def print_val(f): 328 | e = " %.15f" % f 329 | if f >= 0: 330 | e = " " + e 331 | return e 332 | 333 | if __name__ == "__main__": 334 | 335 | import fundamental_mappings 336 | import generators 337 | 338 | for structures in [[ideal_fcc], [ideal_hcp, ideal_hcp_alt], [ideal_bcc], [ideal_ico], [ideal_sc], [ideal_dcub, ideal_dcub_alt1], [ideal_dhex, ideal_dhex_alt1, ideal_dhex_alt2, ideal_dhex_alt3], [ideal_graphene, ideal_graphene_alt]][1:2]: 339 | 340 | structures = [np.array(list(structure[-1:]) + list(structure[:-1])) for structure in structures] 341 | 342 | ''' 343 | structure = np.array([ 344 | [0,0,0], 345 | [ 3*sqrt(2), -3*sqrt(6), 0 ], 346 | [ -6*sqrt(2), 0, 0 ], 347 | [ -3*sqrt(2), 3*sqrt(6), 0 ], 348 | [ 3*sqrt(2), 3*sqrt(6), 0 ], 349 | [ 6*sqrt(2), 0, 0 ], 350 | [ -3*sqrt(2), -3*sqrt(6), 0 ], 351 | ]) 352 | ''' 353 | 354 | #print 355 | #for row in structure: 356 | # print "{", ",".join([print_val(e) for e in row]), "}," 357 | 358 | fundamental_mappings.find(structures, generators.generator_hcp_conventional) 359 | asdf 360 | 361 | M = np.dot(structure.T, structure) 362 | mpi_scale = np.trace(M) / 3 363 | print mpi_scale 364 | 365 | inv = structure / mpi_scale 366 | pinv = np.linalg.pinv(structure).T 367 | assert np.linalg.norm(inv - pinv) < 1E-9 368 | continue 369 | 370 | lines = [] 371 | for p in structure / mpi_scale: 372 | line = "{ " + ", ".join([("%.15f" % e).rjust(18) for e in p]) + " }" 373 | #line = line.replace(".000000000000", ". ") 374 | #line = line.replace(".500000000000", ".5 ") 375 | lines += [line] 376 | print ",\n".join(lines) 377 | print 378 | 379 | -------------------------------------------------------------------------------- /datagen/renderscript_diamond_structures.py: -------------------------------------------------------------------------------- 1 | import ovito 2 | from ovito import * 3 | from ovito.data import * 4 | from ovito.vis import * 5 | import numpy as np 6 | import math 7 | import os 8 | import multiprocessing 9 | import itertools 10 | 11 | 12 | def get_templates(): 13 | 14 | sqrt = np.sqrt 15 | 16 | SC = [ [ 0. , 0. , 0. ], 17 | [ 0. , 0. , -1. ], 18 | [ 0. , 0. , 1. ], 19 | [ 0. , -1. , 0. ], 20 | [ 0. , 1. , 0. ], 21 | [ -1. , 0. , 0. ], 22 | [ 1. , 0. , 0. ] ] 23 | 24 | FCC = [ [ 0. , 0. , 0. ], 25 | [ 0. , 0.707106781187, 0.707106781187 ], 26 | [ 0. , -0.707106781187, -0.707106781187 ], 27 | [ 0. , 0.707106781187, -0.707106781187 ], 28 | [ 0. , -0.707106781187, 0.707106781187 ], 29 | [ 0.707106781187, 0. , 0.707106781187 ], 30 | [ -0.707106781187, 0. , -0.707106781187 ], 31 | [ 0.707106781187, 0. , -0.707106781187 ], 32 | [ -0.707106781187, 0. , 0.707106781187 ], 33 | [ 0.707106781187, 0.707106781187, 0. ], 34 | [ -0.707106781187, -0.707106781187, 0. ], 35 | [ 0.707106781187, -0.707106781187, 0. ], 36 | [ -0.707106781187, 0.707106781187, 0. ] ] 37 | 38 | HCP = [ [ 0. , 0. , 0. ], 39 | [ 0.707106781186, 0. , 0.707106781186 ], 40 | [ -0.235702260395, -0.942809041583, -0.235702260395 ], 41 | [ 0.707106781186, 0.707106781186, 0. ], 42 | [ -0.235702260395, -0.235702260395, -0.942809041583 ], 43 | [ 0. , 0.707106781186, 0.707106781186 ], 44 | [ -0.942809041583, -0.235702260395, -0.235702260395 ], 45 | [ -0.707106781186, 0.707106781186, 0. ], 46 | [ 0. , 0.707106781186, -0.707106781186 ], 47 | [ 0.707106781186, 0. , -0.707106781186 ], 48 | [ 0.707106781186, -0.707106781186, 0. ], 49 | [ -0.707106781186, 0. , 0.707106781186 ], 50 | [ 0. , -0.707106781186, 0.707106781186 ] ] 51 | 52 | ICO = [ [ 0. , 0. , 0. ], 53 | [ 0. , 0.525731112119, 0.850650808352 ], 54 | [ 0. , -0.525731112119, -0.850650808352 ], 55 | [ 0. , 0.525731112119, -0.850650808352 ], 56 | [ 0. , -0.525731112119, 0.850650808352 ], 57 | [ -0.525731112119, -0.850650808352, 0. ], 58 | [ 0.525731112119, 0.850650808352, 0. ], 59 | [ 0.525731112119, -0.850650808352, 0. ], 60 | [ -0.525731112119, 0.850650808352, 0. ], 61 | [ -0.850650808352, 0. , -0.525731112119 ], 62 | [ 0.850650808352, 0. , 0.525731112119 ], 63 | [ 0.850650808352, 0. , -0.525731112119 ], 64 | [ -0.850650808352, 0. , 0.525731112119 ] ] 65 | 66 | BCC = [ [ 0. , 0. , 0. ], 67 | [ -0.541451884327, -0.541451884327, -0.541451884327 ], 68 | [ 0.541451884327, 0.541451884327, 0.541451884327 ], 69 | [ 0.541451884327, -0.541451884327, -0.541451884327 ], 70 | [ -0.541451884327, 0.541451884327, 0.541451884327 ], 71 | [ -0.541451884327, 0.541451884327, -0.541451884327 ], 72 | [ 0.541451884327, -0.541451884327, 0.541451884327 ], 73 | [ -0.541451884327, -0.541451884327, 0.541451884327 ], 74 | [ 0.541451884327, 0.541451884327, -0.541451884327 ], 75 | [ 0. , 0. , -1.082903768655 ], 76 | [ 0. , 0. , 1.082903768655 ], 77 | [ 0. , -1.082903768655, 0. ], 78 | [ 0. , 1.082903768655, 0. ], 79 | [ -1.082903768655, 0. , 0. ], 80 | [ 1.082903768655, 0. , 0. ] ] 81 | 82 | DCUB = [[ 0. , 0. , 0. ], 83 | [ -0.391491627053, 0.391491627053, 0.391491627053 ], 84 | [ -0.391491627053, -0.391491627053, -0.391491627053 ], 85 | [ 0.391491627053, -0.391491627053, 0.391491627053 ], 86 | [ 0.391491627053, 0.391491627053, -0.391491627053 ], 87 | [ -0.782983254107, 0. , 0.782983254107 ], 88 | [ -0.782983254107, 0.782983254107, 0. ], 89 | [ 0. , 0.782983254107, 0.782983254107 ], 90 | [ -0.782983254107, -0.782983254107, 0. ], 91 | [ -0.782983254107, 0. , -0.782983254107 ], 92 | [ 0. , -0.782983254107, -0.782983254107 ], 93 | [ 0. , -0.782983254107, 0.782983254107 ], 94 | [ 0.782983254107, -0.782983254107, 0. ], 95 | [ 0.782983254107, 0. , 0.782983254107 ], 96 | [ 0. , 0.782983254107, -0.782983254107 ], 97 | [ 0.782983254107, 0. , -0.782983254107 ], 98 | [ 0.782983254107, 0.782983254107, 0. ] ] 99 | 100 | DHEX = [[ 0. , 0. , 0. ], 101 | [ -0.391491627053, -0.391491627053, -0.391491627053 ], 102 | [ 0.391491627053, -0.391491627053, 0.391491627053 ], 103 | [ -0.391491627053, 0.391491627053, 0.391491627053 ], 104 | [ 0.391491627053, 0.391491627053, -0.391491627053 ], 105 | [ -0.260994418036, -1.043977672142, -0.260994418036 ], 106 | [ -1.043977672142, -0.260994418036, -0.260994418036 ], 107 | [ -0.260994418036, -0.260994418036, -1.043977672142 ], 108 | [ 0.782983254107, 0. , 0.782983254107 ], 109 | [ 0.782983254107, -0.782983254107, 0. ], 110 | [ 0. , -0.782983254107, 0.782983254107 ], 111 | [ 0. , 0.782983254107, 0.782983254107 ], 112 | [ -0.782983254107, 0.782983254107, 0. ], 113 | [ -0.782983254107, 0. , 0.782983254107 ], 114 | [ 0.782983254107, 0.782983254107, 0. ], 115 | [ 0. , 0.782983254107, -0.782983254107 ], 116 | [ 0.782983254107, 0. , -0.782983254107 ] ] 117 | 118 | k7 = (6*sqrt(3) - 3) / 22 119 | k8 = (18-3*sqrt(3)) / 22 120 | GRP = np.array( 121 | [[ 0, 0, 0 ], 122 | [ 0, 2*k7, 0 ], 123 | [ k8, - k7, 0 ], 124 | [ -k8, -k7, 0 ], 125 | [ -k8, 3*k7, 0 ], 126 | [ k8, 3*k7, 0 ], 127 | [ 2*k8, 0, 0 ], 128 | [ k8, -3*k7, 0 ], 129 | [ -k8, -3*k7, 0 ], 130 | [ -2*k8, 0, 0 ] ]) 131 | 132 | return [np.array(e) for e in [SC, FCC, HCP, ICO, BCC, DCUB, DHEX, GRP]] 133 | 134 | def find_plane(positions): 135 | 136 | best = (0, None) 137 | cs = itertools.combinations(range(len(positions)), 3) 138 | for c in cs: 139 | qs = positions[list(c)] 140 | vs = qs[1:] - qs[0] 141 | normal = np.cross(vs[0], vs[1]) 142 | if np.linalg.norm(normal) < 1E-3: 143 | continue 144 | normal /= np.linalg.norm(normal) 145 | dots = np.abs(np.dot(positions - qs[0], normal)) 146 | count = len(np.where(dots < 1E-3)[0]) 147 | best = max(best, (count, tuple(c))) 148 | print(best) 149 | return np.array(best[1]) 150 | 151 | def project(positions, indices): 152 | 153 | a, b, c = positions[indices] 154 | 155 | x = a / np.linalg.norm(a) 156 | 157 | y = b / np.linalg.norm(b) 158 | y -= np.dot(x, y) * x 159 | y /= np.linalg.norm(y) 160 | 161 | z = np.cross(x, y) 162 | 163 | assert abs(np.dot(x, y)) < 1E-9 164 | assert abs(np.dot(y, z)) < 1E-9 165 | assert abs(np.dot(z, x)) < 1E-9 166 | basis = np.array([x, y, z]) 167 | 168 | return np.dot(positions, basis.T) 169 | 170 | def render_scene(folder, name): 171 | 172 | nameindex = dict() 173 | names = ['SC', 'FCC', 'HCP', 'ICO', 'BCC', 'DCUB', 'DHEX', 'GRP'] 174 | for i, n in enumerate(names): 175 | nameindex[n.lower()] = i 176 | 177 | index = nameindex[name] 178 | 179 | points = get_templates() 180 | positions = points[index] * 16 181 | num_atoms = len(positions) 182 | 183 | if name in [e.lower() for e in ['FCC', 'HCP', 'DCUB', 'DHEX']]: 184 | indices = find_plane(positions) 185 | positions = project(positions, indices) 186 | 187 | distances = [np.linalg.norm(p-q) for i, p in enumerate(positions[1:]) for j, q in enumerate(positions[1:]) if i np.linalg.norm(positions[1]) * 1.02)[0] 204 | for i in indices: 205 | print(i) 206 | type_prop.marray[i] = 3 207 | 208 | w = 1.3 209 | r = 2 210 | type_prop.type_list[0].radius = r 211 | type_prop.type_list[1].radius = r 212 | type_prop.type_list[2].radius = r 213 | 214 | xmax = np.max(positions[:,0]) 215 | ymax = np.max(positions[:,1]) 216 | zmax = np.max(positions[:,2]) 217 | 218 | # Create a simulation box. 219 | cell = SimulationCell() 220 | cell.matrix = [[xmax,0,0,0], [0,ymax,0,0], [0,0,xmax,0]] 221 | cell.pbc = (False, False, False) 222 | cell.display.line_width = 0.0 223 | 224 | # Create a data collection to hold the particle properties, the bonds, and the simulation cell. 225 | data = DataCollection() 226 | data.add(pos_prop) 227 | data.add(cell) 228 | data.add(type_prop) 229 | 230 | 231 | # Create a node and insert it into the scene. 232 | node = ObjectNode() 233 | node.source = data 234 | modifier = ovito.modifiers.CreateBondsModifier(mode=ovito.modifiers.CreateBondsModifier.Mode.Pairwise) 235 | modifier.bonds_display.color = (166/255.,206/255.,227/255.) 236 | 237 | if name == 'grp': 238 | modifier.set_pairwise_cutoff(type_prop.type_list[0].name, type_prop.type_list[0].name, cutoff) 239 | modifier.set_pairwise_cutoff(type_prop.type_list[0].name, type_prop.type_list[1].name, cutoff) 240 | else: 241 | modifier.set_pairwise_cutoff(type_prop.type_list[0].name, type_prop.type_list[0].name, 0) 242 | modifier.set_pairwise_cutoff(type_prop.type_list[0].name, type_prop.type_list[1].name, 0) 243 | 244 | if len(indices) == 0: 245 | modifier.set_pairwise_cutoff(type_prop.type_list[1].name, type_prop.type_list[1].name, cutoff) 246 | elif len(indices) == 6: 247 | modifier.set_pairwise_cutoff(type_prop.type_list[1].name, type_prop.type_list[1].name, cutoff) 248 | modifier.set_pairwise_cutoff(type_prop.type_list[1].name, type_prop.type_list[2].name, cutoff) 249 | elif len(indices) > 6: 250 | modifier.set_pairwise_cutoff(type_prop.type_list[1].name, type_prop.type_list[1].name, cutoff) 251 | modifier.set_pairwise_cutoff(type_prop.type_list[1].name, type_prop.type_list[2].name, cutoff) 252 | modifier.set_pairwise_cutoff(type_prop.type_list[2].name, type_prop.type_list[2].name, cutoff*2) 253 | 254 | modifier.bonds_display.width=w 255 | modifier.bonds_display.use_particle_colors=False 256 | node.modifiers.append(modifier) 257 | node.compute() 258 | 259 | dataset.scene_nodes.append(node) 260 | dataset.selected_node = node # Select the new node and adjust viewport cameras to show everything. 261 | #return 262 | 263 | vp = Viewport() 264 | vp.type = Viewport.Type.ORTHO 265 | vp.fov = 254 266 | 267 | vp.camera_pos = (0, 0, 0) 268 | vp.camera_dir = (0.8, -0.6, -0.1667) 269 | if name == 'grp': 270 | vp.camera_dir = (0, 0, -1) 271 | #vp.camera_pos = (0, 0, 0) 272 | #vp.camera_dir = (-0.3, 0.9, -0.3) 273 | vp.fov = 20 274 | if name == 'grp': 275 | vp.fov = 22 276 | 277 | size = (126, 126) 278 | vp.render(RenderSettings(filename = "ptm_schematic_%s.png" % name, size=size)) 279 | 280 | import sys 281 | def go(): 282 | if len(sys.argv) < 2: 283 | raise Exception("must provide a template index") 284 | 285 | index = int(sys.argv[1]) 286 | if index not in range(8): 287 | raise Exception("oh dear") 288 | 289 | folder = '.' 290 | names = ['SC', 'FCC', 'HCP', 'ICO', 'BCC', 'DCUB', 'DHEX', 'GRP'] 291 | for i, name in enumerate(names): 292 | if i != index: continue 293 | name = name.lower() 294 | print(name) 295 | render_scene(folder, name) 296 | return 297 | p = multiprocessing.Process(target=render_scene, args=(i, folder, name)) 298 | p.start() 299 | p.join() 300 | go() 301 | 302 | -------------------------------------------------------------------------------- /ptm_quat.h: -------------------------------------------------------------------------------- 1 | /*Copyright (c) 2016 PM Larsen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | */ 9 | 10 | #ifndef PTM_QUAT_H 11 | #define PTM_QUAT_H 12 | 13 | namespace ptm { 14 | 15 | const double generator_cubic[24][4] = { 16 | { 1, 0, 0, 0 }, 17 | { sqrt(2)/2, sqrt(2)/2, 0, 0 }, 18 | { sqrt(2)/2, 0, sqrt(2)/2, 0 }, 19 | { sqrt(2)/2, 0, 0, sqrt(2)/2 }, 20 | { sqrt(2)/2, 0, 0, -sqrt(2)/2 }, 21 | { sqrt(2)/2, 0, -sqrt(2)/2, 0 }, 22 | { sqrt(2)/2, -sqrt(2)/2, -0, -0 }, 23 | { 0.5, 0.5, 0.5, 0.5 }, 24 | { 0.5, 0.5, 0.5, -0.5 }, 25 | { 0.5, 0.5, -0.5, 0.5 }, 26 | { 0.5, 0.5, -0.5, -0.5 }, 27 | { 0.5, -0.5, 0.5, 0.5 }, 28 | { 0.5, -0.5, 0.5, -0.5 }, 29 | { 0.5, -0.5, -0.5, 0.5 }, 30 | { 0.5, -0.5, -0.5, -0.5 }, 31 | { 0, 1, 0, 0 }, 32 | { 0, sqrt(2)/2, sqrt(2)/2, 0 }, 33 | { 0, sqrt(2)/2, 0, sqrt(2)/2 }, 34 | { 0, sqrt(2)/2, 0, -sqrt(2)/2 }, 35 | { 0, sqrt(2)/2, -sqrt(2)/2, 0 }, 36 | { 0, 0, 1, 0 }, 37 | { 0, 0, sqrt(2)/2, sqrt(2)/2 }, 38 | { 0, 0, sqrt(2)/2, -sqrt(2)/2 }, 39 | { 0, 0, 0, 1 }, 40 | }; 41 | 42 | const double generator_diamond_cubic[12][4] = { 43 | { 1, 0, 0, 0 }, 44 | { 0.5, 0.5, 0.5, 0.5 }, 45 | { 0.5, 0.5, 0.5, -0.5 }, 46 | { 0.5, 0.5, -0.5, 0.5 }, 47 | { 0.5, 0.5, -0.5, -0.5 }, 48 | { 0.5, -0.5, 0.5, 0.5 }, 49 | { 0.5, -0.5, 0.5, -0.5 }, 50 | { 0.5, -0.5, -0.5, 0.5 }, 51 | { 0.5, -0.5, -0.5, -0.5 }, 52 | { 0, 1, 0, 0 }, 53 | { 0, 0, 1, 0 }, 54 | { 0, 0, 0, 1 }, 55 | }; 56 | 57 | const double generator_hcp[6][4] = { 58 | { 1, 0, 0, 0 }, 59 | { 0.5, 0, 0, sqrt(3)/2 }, 60 | { 0.5, 0, 0, -sqrt(3)/2 }, 61 | { 0, sqrt(3)/2, 0.5, 0 }, 62 | { 0, sqrt(3)/2, -0.5, 0 }, 63 | { 0, 0, 1, 0 }, 64 | }; 65 | 66 | 67 | const double generator_hcp_conventional[12][4] = { 68 | { 1, 0, 0, 0 }, 69 | { sqrt(3)/2, 0, 0, 0.5 }, 70 | { sqrt(3)/2, 0, 0, -0.5 }, 71 | { 0.5, 0, 0, sqrt(3)/2 }, 72 | { 0.5, 0, 0, -sqrt(3)/2 }, 73 | { 0, 1, 0, 0 }, 74 | { 0, sqrt(3)/2, 0.5, 0 }, 75 | { 0, sqrt(3)/2, -0.5, 0 }, 76 | { 0, 0.5, sqrt(3)/2, 0 }, 77 | { 0, 0.5, -sqrt(3)/2, 0 }, 78 | { 0, 0, 1, 0 }, 79 | { 0, 0, 0, 1 }, 80 | }; 81 | 82 | const double generator_diamond_hexagonal[3][4] = { 83 | { 1, 0, 0, 0 }, 84 | { 0.5, 0, 0, sqrt(3)/2 }, 85 | { 0.5, 0, 0, -sqrt(3)/2 }, 86 | }; 87 | 88 | const double generator_icosahedral[60][4] = { 89 | { 1, 0, 0, 0 }, 90 | { (1+sqrt(5))/4, 0.5, sqrt(25-10*sqrt(5))/10, sqrt(50-10*sqrt(5))/20 }, 91 | { (1+sqrt(5))/4, 0.5, -sqrt(25-10*sqrt(5))/10, -sqrt(50-10*sqrt(5))/20 }, 92 | { (1+sqrt(5))/4, 1/(1+sqrt(5)), sqrt(10*sqrt(5)+50)/20, -sqrt(50-10*sqrt(5))/20 }, 93 | { (1+sqrt(5))/4, 1/(1+sqrt(5)), -sqrt(10*sqrt(5)+50)/20, sqrt(50-10*sqrt(5))/20 }, 94 | { (1+sqrt(5))/4, 0, sqrt(50-10*sqrt(5))/10, sqrt(50-10*sqrt(5))/20 }, 95 | { (1+sqrt(5))/4, 0, 0, sqrt(5./8-sqrt(5)/8) }, 96 | { (1+sqrt(5))/4, 0, 0, -sqrt(5./8-sqrt(5)/8) }, 97 | { (1+sqrt(5))/4, 0, -sqrt(50-10*sqrt(5))/10, -sqrt(50-10*sqrt(5))/20 }, 98 | { (1+sqrt(5))/4, -1/(1+sqrt(5)), sqrt(10*sqrt(5)+50)/20, -sqrt(50-10*sqrt(5))/20 }, 99 | { (1+sqrt(5))/4, -1/(1+sqrt(5)), -sqrt(10*sqrt(5)+50)/20, sqrt(50-10*sqrt(5))/20 }, 100 | { (1+sqrt(5))/4, -0.5, sqrt(25-10*sqrt(5))/10, sqrt(50-10*sqrt(5))/20 }, 101 | { (1+sqrt(5))/4, -0.5, -sqrt(25-10*sqrt(5))/10, -sqrt(50-10*sqrt(5))/20 }, 102 | { 0.5, (1+sqrt(5))/4, sqrt(50-10*sqrt(5))/20, -sqrt(25-10*sqrt(5))/10 }, 103 | { 0.5, (1+sqrt(5))/4, -sqrt(50-10*sqrt(5))/20, sqrt(25-10*sqrt(5))/10 }, 104 | { 0.5, 0.5, sqrt((5+2*sqrt(5))/20), sqrt(25-10*sqrt(5))/10 }, 105 | { 0.5, 0.5, sqrt(25-10*sqrt(5))/10, -sqrt((5+2*sqrt(5))/20) }, 106 | { 0.5, 0.5, -sqrt(25-10*sqrt(5))/10, sqrt((5+2*sqrt(5))/20) }, 107 | { 0.5, 0.5, -sqrt((5+2*sqrt(5))/20), -sqrt(25-10*sqrt(5))/10 }, 108 | { 0.5, 1/(1+sqrt(5)), sqrt(10*sqrt(5)+50)/20, sqrt((5+2*sqrt(5))/20) }, 109 | { 0.5, 1/(1+sqrt(5)), -sqrt(10*sqrt(5)+50)/20, -sqrt((5+2*sqrt(5))/20) }, 110 | { 0.5, 0, sqrt((5+sqrt(5))/10), -sqrt(25-10*sqrt(5))/10 }, 111 | { 0.5, 0, sqrt(50-10*sqrt(5))/10, -sqrt((5+2*sqrt(5))/20) }, 112 | { 0.5, 0, -sqrt(50-10*sqrt(5))/10, sqrt((5+2*sqrt(5))/20) }, 113 | { 0.5, 0, -sqrt((5+sqrt(5))/10), sqrt(25-10*sqrt(5))/10 }, 114 | { 0.5, -1/(1+sqrt(5)), sqrt(10*sqrt(5)+50)/20, sqrt((5+2*sqrt(5))/20) }, 115 | { 0.5, -1/(1+sqrt(5)), -sqrt(10*sqrt(5)+50)/20, -sqrt((5+2*sqrt(5))/20) }, 116 | { 0.5, -0.5, sqrt((5+2*sqrt(5))/20), sqrt(25-10*sqrt(5))/10 }, 117 | { 0.5, -0.5, sqrt(25-10*sqrt(5))/10, -sqrt((5+2*sqrt(5))/20) }, 118 | { 0.5, -0.5, -sqrt(25-10*sqrt(5))/10, sqrt((5+2*sqrt(5))/20) }, 119 | { 0.5, -0.5, -sqrt((5+2*sqrt(5))/20), -sqrt(25-10*sqrt(5))/10 }, 120 | { 0.5, -(1+sqrt(5))/4, sqrt(50-10*sqrt(5))/20, -sqrt(25-10*sqrt(5))/10 }, 121 | { 0.5, -(1+sqrt(5))/4, -sqrt(50-10*sqrt(5))/20, sqrt(25-10*sqrt(5))/10 }, 122 | { 1/(1+sqrt(5)), (1+sqrt(5))/4, sqrt(50-10*sqrt(5))/20, sqrt(10*sqrt(5)+50)/20 }, 123 | { 1/(1+sqrt(5)), (1+sqrt(5))/4, -sqrt(50-10*sqrt(5))/20, -sqrt(10*sqrt(5)+50)/20 }, 124 | { 1/(1+sqrt(5)), 0.5, sqrt((5+2*sqrt(5))/20), -sqrt(10*sqrt(5)+50)/20 }, 125 | { 1/(1+sqrt(5)), 0.5, -sqrt((5+2*sqrt(5))/20), sqrt(10*sqrt(5)+50)/20 }, 126 | { 1/(1+sqrt(5)), 0, sqrt((5+sqrt(5))/10), sqrt(10*sqrt(5)+50)/20 }, 127 | { 1/(1+sqrt(5)), 0, 0, sqrt(1-1/(2*sqrt(5)+6)) }, 128 | { 1/(1+sqrt(5)), 0, 0, -sqrt(1-1/(2*sqrt(5)+6)) }, 129 | { 1/(1+sqrt(5)), 0, -sqrt((5+sqrt(5))/10), -sqrt(10*sqrt(5)+50)/20 }, 130 | { 1/(1+sqrt(5)), -0.5, sqrt((5+2*sqrt(5))/20), -sqrt(10*sqrt(5)+50)/20 }, 131 | { 1/(1+sqrt(5)), -0.5, -sqrt((5+2*sqrt(5))/20), sqrt(10*sqrt(5)+50)/20 }, 132 | { 1/(1+sqrt(5)), -(1+sqrt(5))/4, sqrt(50-10*sqrt(5))/20, sqrt(10*sqrt(5)+50)/20 }, 133 | { 1/(1+sqrt(5)), -(1+sqrt(5))/4, -sqrt(50-10*sqrt(5))/20, -sqrt(10*sqrt(5)+50)/20 }, 134 | { 0, 1, 0, 0 }, 135 | { 0, (1+sqrt(5))/4, sqrt(5./8-sqrt(5)/8), 0 }, 136 | { 0, (1+sqrt(5))/4, sqrt(50-10*sqrt(5))/20, -sqrt(50-10*sqrt(5))/10 }, 137 | { 0, (1+sqrt(5))/4, -sqrt(50-10*sqrt(5))/20, sqrt(50-10*sqrt(5))/10 }, 138 | { 0, (1+sqrt(5))/4, -sqrt(5./8-sqrt(5)/8), 0 }, 139 | { 0, 0.5, sqrt((5+2*sqrt(5))/20), sqrt(50-10*sqrt(5))/10 }, 140 | { 0, 0.5, sqrt(25-10*sqrt(5))/10, sqrt((5+sqrt(5))/10) }, 141 | { 0, 0.5, -sqrt(25-10*sqrt(5))/10, -sqrt((5+sqrt(5))/10) }, 142 | { 0, 0.5, -sqrt((5+2*sqrt(5))/20), -sqrt(50-10*sqrt(5))/10 }, 143 | { 0, 1/(1+sqrt(5)), sqrt(1-1/(2*sqrt(5)+6)), 0 }, 144 | { 0, 1/(1+sqrt(5)), sqrt(10*sqrt(5)+50)/20, -sqrt((5+sqrt(5))/10) }, 145 | { 0, 1/(1+sqrt(5)), -sqrt(10*sqrt(5)+50)/20, sqrt((5+sqrt(5))/10) }, 146 | { 0, 1/(1+sqrt(5)), -sqrt(1-1/(2*sqrt(5)+6)), 0 }, 147 | { 0, 0, sqrt((5+sqrt(5))/10), -sqrt(50-10*sqrt(5))/10 }, 148 | { 0, 0, sqrt(50-10*sqrt(5))/10, sqrt((5+sqrt(5))/10) }, 149 | }; 150 | 151 | 152 | int rotate_quaternion_into_cubic_fundamental_zone(double* q); 153 | int rotate_quaternion_into_diamond_cubic_fundamental_zone(double* q); 154 | int rotate_quaternion_into_icosahedral_fundamental_zone(double* q); 155 | int rotate_quaternion_into_hcp_fundamental_zone(double* q); 156 | int rotate_quaternion_into_hcp_conventional_fundamental_zone(double* q); 157 | int rotate_quaternion_into_diamond_hexagonal_fundamental_zone(double* q); 158 | 159 | void quat_rot(double* r, double* a, double* b); 160 | void normalize_quaternion(double* q); 161 | void quaternion_to_rotation_matrix(double* q, double* U); 162 | void rotation_matrix_to_quaternion(double* u, double* q); 163 | double quat_dot(double* a, double* b); 164 | double quat_misorientation(double* q1, double* q2); 165 | 166 | int map_quaternion_cubic(double* q, int i); 167 | int map_quaternion_diamond_cubic(double* q, int i); 168 | int map_quaternion_icosahedral(double* q, int i); 169 | int map_quaternion_hcp(double* q, int i); 170 | int map_quaternion_hcp_conventional(double* q, int i); 171 | int map_quaternion_diamond_hexagonal(double* q, int i); 172 | 173 | } 174 | 175 | #endif 176 | 177 | -------------------------------------------------------------------------------- /ptm_alt_templates.h: -------------------------------------------------------------------------------- 1 | /*Copyright (c) 2016 PM Larsen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | */ 9 | 10 | #ifndef PTM_ALT_TEMPLATES_H 11 | #define PTM_ALT_TEMPLATES_H 12 | 13 | #include 14 | 15 | 16 | const double ptm_template_hcp_alt1[PTM_NUM_POINTS_HCP][3] = { 17 | { 0, 0, 0 }, 18 | { 1, 0, 0 }, 19 | { -0.5, -sqrt(3)/2, 0 }, 20 | { -0.5, -sqrt(3)/6, -sqrt(6)/3 }, 21 | { 0, sqrt(3)/3, -sqrt(6)/3 }, 22 | { 0.5, -sqrt(3)/6, -sqrt(6)/3 }, 23 | { -1, 0, 0 }, 24 | { -0.5, sqrt(3)/2, 0 }, 25 | { 0.5, sqrt(3)/2, 0 }, 26 | { 0.5, -sqrt(3)/2, 0 }, 27 | { 0.5, -sqrt(3)/6, sqrt(6)/3 }, 28 | { 0, sqrt(3)/3, sqrt(6)/3 }, 29 | { -0.5, -sqrt(3)/6, sqrt(6)/3 }, 30 | }; 31 | 32 | const double ptm_template_dcub_alt1[PTM_NUM_POINTS_DCUB][3] = { 33 | { 0, 0, 0 }, 34 | { 4/(sqrt(3)+6*sqrt(2)), -4/(sqrt(3)+6*sqrt(2)), 4/(sqrt(3)+6*sqrt(2)) }, 35 | { 4/(sqrt(3)+6*sqrt(2)), 4/(sqrt(3)+6*sqrt(2)), -4/(sqrt(3)+6*sqrt(2)) }, 36 | { -4/(sqrt(3)+6*sqrt(2)), -4/(sqrt(3)+6*sqrt(2)), -4/(sqrt(3)+6*sqrt(2)) }, 37 | { -4/(sqrt(3)+6*sqrt(2)), 4/(sqrt(3)+6*sqrt(2)), 4/(sqrt(3)+6*sqrt(2)) }, 38 | { 8/(sqrt(3)+6*sqrt(2)), 0, 8/(sqrt(3)+6*sqrt(2)) }, 39 | { 0, -8/(sqrt(3)+6*sqrt(2)), 8/(sqrt(3)+6*sqrt(2)) }, 40 | { 8/(sqrt(3)+6*sqrt(2)), -8/(sqrt(3)+6*sqrt(2)), 0 }, 41 | { 0, 8/(sqrt(3)+6*sqrt(2)), -8/(sqrt(3)+6*sqrt(2)) }, 42 | { 8/(sqrt(3)+6*sqrt(2)), 0, -8/(sqrt(3)+6*sqrt(2)) }, 43 | { 8/(sqrt(3)+6*sqrt(2)), 8/(sqrt(3)+6*sqrt(2)), 0 }, 44 | { -8/(sqrt(3)+6*sqrt(2)), 0, -8/(sqrt(3)+6*sqrt(2)) }, 45 | { 0, -8/(sqrt(3)+6*sqrt(2)), -8/(sqrt(3)+6*sqrt(2)) }, 46 | { -8/(sqrt(3)+6*sqrt(2)), -8/(sqrt(3)+6*sqrt(2)), 0 }, 47 | { -8/(sqrt(3)+6*sqrt(2)), 8/(sqrt(3)+6*sqrt(2)), 0 }, 48 | { -8/(sqrt(3)+6*sqrt(2)), 0, 8/(sqrt(3)+6*sqrt(2)) }, 49 | { 0, 8/(sqrt(3)+6*sqrt(2)), 8/(sqrt(3)+6*sqrt(2)) }, 50 | }; 51 | 52 | const double ptm_template_dhex_alt1[PTM_NUM_POINTS_DHEX][3] = { 53 | { 0, 0, 0 }, 54 | { -4*sqrt(2)/(sqrt(3)+6*sqrt(2)), -4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), -4*sqrt(3)/(3*sqrt(3)+18*sqrt(2)) }, 55 | { 4*sqrt(2)/(sqrt(3)+6*sqrt(2)), -4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), -4*sqrt(3)/(3*sqrt(3)+18*sqrt(2)) }, 56 | { 0, 8*sqrt(6)/(3*sqrt(3)+18*sqrt(2)), -4*sqrt(3)/(3*sqrt(3)+18*sqrt(2)) }, 57 | { 0, 0, 4*sqrt(3)/(sqrt(3)+6*sqrt(2)) }, 58 | { -4*sqrt(2)/(sqrt(3)+6*sqrt(2)), -4*sqrt(6)/(sqrt(3)+6*sqrt(2)), 0 }, 59 | { -4*sqrt(2)/(sqrt(3)+6*sqrt(2)), -4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), -16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) }, 60 | { -8*sqrt(2)/(sqrt(3)+6*sqrt(2)), 0, 0 }, 61 | { 8*sqrt(2)/(sqrt(3)+6*sqrt(2)), 0, 0 }, 62 | { 4*sqrt(2)/(sqrt(3)+6*sqrt(2)), -4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), -16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) }, 63 | { 4*sqrt(2)/(sqrt(3)+6*sqrt(2)), -4*sqrt(6)/(sqrt(3)+6*sqrt(2)), 0 }, 64 | { 0, 8*sqrt(6)/(3*sqrt(3)+18*sqrt(2)), -16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) }, 65 | { -4*sqrt(2)/(sqrt(3)+6*sqrt(2)), 4*sqrt(6)/(sqrt(3)+6*sqrt(2)), 0 }, 66 | { 4*sqrt(2)/(sqrt(3)+6*sqrt(2)), 4*sqrt(6)/(sqrt(3)+6*sqrt(2)), 0 }, 67 | { 4*sqrt(2)/(sqrt(3)+6*sqrt(2)), -4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), 16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) }, 68 | { 0, 8*sqrt(6)/(3*sqrt(3)+18*sqrt(2)), 16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) }, 69 | { -4*sqrt(2)/(sqrt(3)+6*sqrt(2)), -4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), 16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) }, 70 | }; 71 | 72 | const double ptm_template_dhex_alt2[PTM_NUM_POINTS_DHEX][3] = { 73 | { 0, 0, 0 }, 74 | { 0, -8*sqrt(6)/(3*sqrt(3)+18*sqrt(2)), 4*sqrt(3)/(3*sqrt(3)+18*sqrt(2)) }, 75 | { -4*sqrt(2)/(sqrt(3)+6*sqrt(2)), 4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), 4*sqrt(3)/(3*sqrt(3)+18*sqrt(2)) }, 76 | { 4*sqrt(2)/(sqrt(3)+6*sqrt(2)), 4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), 4*sqrt(3)/(3*sqrt(3)+18*sqrt(2)) }, 77 | { 0, 0, -4*sqrt(3)/(sqrt(3)+6*sqrt(2)) }, 78 | { -4*sqrt(2)/(sqrt(3)+6*sqrt(2)), -4*sqrt(6)/(sqrt(3)+6*sqrt(2)), 0 }, 79 | { 0, -8*sqrt(6)/(3*sqrt(3)+18*sqrt(2)), 16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) }, 80 | { 4*sqrt(2)/(sqrt(3)+6*sqrt(2)), -4*sqrt(6)/(sqrt(3)+6*sqrt(2)), 0 }, 81 | { -4*sqrt(2)/(sqrt(3)+6*sqrt(2)), 4*sqrt(6)/(sqrt(3)+6*sqrt(2)), 0 }, 82 | { -4*sqrt(2)/(sqrt(3)+6*sqrt(2)), 4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), 16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) }, 83 | { -8*sqrt(2)/(sqrt(3)+6*sqrt(2)), 0, 0 }, 84 | { 4*sqrt(2)/(sqrt(3)+6*sqrt(2)), 4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), 16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) }, 85 | { 8*sqrt(2)/(sqrt(3)+6*sqrt(2)), 0, 0 }, 86 | { 4*sqrt(2)/(sqrt(3)+6*sqrt(2)), 4*sqrt(6)/(sqrt(3)+6*sqrt(2)), 0 }, 87 | { -4*sqrt(2)/(sqrt(3)+6*sqrt(2)), 4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), -16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) }, 88 | { 4*sqrt(2)/(sqrt(3)+6*sqrt(2)), 4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), -16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) }, 89 | { 0, -8*sqrt(6)/(3*sqrt(3)+18*sqrt(2)), -16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) }, 90 | }; 91 | 92 | const double ptm_template_dhex_alt3[PTM_NUM_POINTS_DHEX][3] = { 93 | { 0, 0, 0 }, 94 | { 4*sqrt(2)/(sqrt(3)+6*sqrt(2)), -4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), 4*sqrt(3)/(3*sqrt(3)+18*sqrt(2)) }, 95 | { -4*sqrt(2)/(sqrt(3)+6*sqrt(2)), -4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), 4*sqrt(3)/(3*sqrt(3)+18*sqrt(2)) }, 96 | { 0, 8*sqrt(6)/(3*sqrt(3)+18*sqrt(2)), 4*sqrt(3)/(3*sqrt(3)+18*sqrt(2)) }, 97 | { 0, 0, -4*sqrt(3)/(sqrt(3)+6*sqrt(2)) }, 98 | { 4*sqrt(2)/(sqrt(3)+6*sqrt(2)), -4*sqrt(6)/(sqrt(3)+6*sqrt(2)), 0 }, 99 | { 4*sqrt(2)/(sqrt(3)+6*sqrt(2)), -4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), 16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) }, 100 | { 8*sqrt(2)/(sqrt(3)+6*sqrt(2)), 0, 0 }, 101 | { -8*sqrt(2)/(sqrt(3)+6*sqrt(2)), 0, 0 }, 102 | { -4*sqrt(2)/(sqrt(3)+6*sqrt(2)), -4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), 16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) }, 103 | { -4*sqrt(2)/(sqrt(3)+6*sqrt(2)), -4*sqrt(6)/(sqrt(3)+6*sqrt(2)), 0 }, 104 | { 0, 8*sqrt(6)/(3*sqrt(3)+18*sqrt(2)), 16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) }, 105 | { 4*sqrt(2)/(sqrt(3)+6*sqrt(2)), 4*sqrt(6)/(sqrt(3)+6*sqrt(2)), 0 }, 106 | { -4*sqrt(2)/(sqrt(3)+6*sqrt(2)), 4*sqrt(6)/(sqrt(3)+6*sqrt(2)), 0 }, 107 | { -4*sqrt(2)/(sqrt(3)+6*sqrt(2)), -4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), -16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) }, 108 | { 0, 8*sqrt(6)/(3*sqrt(3)+18*sqrt(2)), -16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) }, 109 | { 4*sqrt(2)/(sqrt(3)+6*sqrt(2)), -4*sqrt(6)/(3*(sqrt(3)+6*sqrt(2))), -16*sqrt(3)/(3*(sqrt(3)+6*sqrt(2))) }, 110 | }; 111 | 112 | 113 | const double ptm_template_graphene_alt1[PTM_NUM_POINTS_GRAPHENE][3] = { 114 | { 0, 0, 0 }, 115 | { 3*sqrt(3)/22-9./11, -3./22+3*sqrt(3)/11, 0 }, 116 | { 9./11-3*sqrt(3)/22, -3./22+3*sqrt(3)/11, 0 }, 117 | { 0, -6*sqrt(3)/11+3./11, 0 }, 118 | { -18./11+3*sqrt(3)/11, 0, 0 }, 119 | { 3*sqrt(3)/22-9./11, -9./22+9*sqrt(3)/11, 0 }, 120 | { 9./11-3*sqrt(3)/22, -9./22+9*sqrt(3)/11, 0 }, 121 | { -3*sqrt(3)/11+18./11, 0, 0 }, 122 | { 9./11-3*sqrt(3)/22, -9*sqrt(3)/11+9./22, 0 }, 123 | { 3*sqrt(3)/22-9./11, -9*sqrt(3)/11+9./22, 0 }, 124 | }; 125 | 126 | #endif 127 | 128 | -------------------------------------------------------------------------------- /ptm_polar.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * -/_|:|_|_\- 3 | * 4 | * This code is a modification of D.L. Theobald's QCP rotation code. 5 | * It has been adapted to calculate the polar decomposition of a 3x3 matrix 6 | * Adaption by P.M. Larsen 7 | * 8 | * Original Author(s): Douglas L. Theobald 9 | * Department of Biochemistry 10 | * MS 009 11 | * Brandeis University 12 | * 415 South St 13 | * Waltham, MA 02453 14 | * USA 15 | * 16 | * dtheobald@brandeis.edu 17 | * 18 | * Pu Liu 19 | * Johnson & Johnson Pharmaceutical Research and Development, L.L.C. 20 | * 665 Stockton Drive 21 | * Exton, PA 19341 22 | * USA 23 | * 24 | * pliu24@its.jnj.com 25 | * 26 | * 27 | * If you use this QCP rotation calculation method in a publication, please 28 | * reference: 29 | * 30 | * Douglas L. Theobald (2005) 31 | * "Rapid calculation of RMSD using a quaternion-based characteristic 32 | * polynomial." 33 | * Acta Crystallographica A 61(4):478-480. 34 | * 35 | * Pu Liu, Dmitris K. Agrafiotis, and Douglas L. Theobald (2009) 36 | * "Fast determination of the optimal rotational matrix for macromolecular 37 | * superpositions." 38 | * Journal of Computational Chemistry 31(7):1561-1563. 39 | * 40 | * 41 | * Copyright (c) 2009-2013 Pu Liu and Douglas L. Theobald 42 | * All rights reserved. 43 | * 44 | * Redistribution and use in source and binary forms, with or without modification, are permitted 45 | * provided that the following conditions are met: 46 | * 47 | * * Redistributions of source code must retain the above copyright notice, this list of 48 | * conditions and the following disclaimer. 49 | * * Redistributions in binary form must reproduce the above copyright notice, this list 50 | * of conditions and the following disclaimer in the documentation and/or other materials 51 | * provided with the distribution. 52 | * * Neither the name of the nor the names of its contributors may be used to 53 | * endorse or promote products derived from this software without specific prior written 54 | * permission. 55 | * 56 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 57 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 58 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 59 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 60 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 61 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 62 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 63 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 64 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 65 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 66 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 67 | * 68 | * Source: started anew. 69 | * 70 | * Change History: 71 | * 2009/04/13 Started source 72 | * 2010/03/28 Modified FastCalcRMSDAndRotation() to handle tiny qsqr 73 | * If trying all rows of the adjoint still gives too small 74 | * qsqr, then just return identity matrix. (DLT) 75 | * 2010/06/30 Fixed prob in assigning A[9] = 0 in InnerProduct() 76 | * invalid mem access 77 | * 2011/02/21 Made CenterCoords use weights 78 | * 2011/05/02 Finally changed CenterCoords declaration in qcprot.h 79 | * Also changed some functions to static 80 | * 2011/07/08 put in fabs() to fix taking sqrt of small neg numbers, fp error 81 | * 2012/07/26 minor changes to comments and main.c, more info (v.1.4) 82 | * 83 | * 2016/05/29 QCP method adapted for polar decomposition of a 3x3 matrix, 84 | * for use in Polyhedral Template Matching. 85 | * 86 | ******************************************************************************/ 87 | 88 | #include 89 | #include 90 | #include 91 | #include "ptm_quat.h" 92 | 93 | 94 | namespace ptm { 95 | 96 | static void matmul_3x3(double* A, double* x, double* b) 97 | { 98 | b[0] = A[0] * x[0] + A[1] * x[3] + A[2] * x[6]; 99 | b[3] = A[3] * x[0] + A[4] * x[3] + A[5] * x[6]; 100 | b[6] = A[6] * x[0] + A[7] * x[3] + A[8] * x[6]; 101 | 102 | b[1] = A[0] * x[1] + A[1] * x[4] + A[2] * x[7]; 103 | b[4] = A[3] * x[1] + A[4] * x[4] + A[5] * x[7]; 104 | b[7] = A[6] * x[1] + A[7] * x[4] + A[8] * x[7]; 105 | 106 | b[2] = A[0] * x[2] + A[1] * x[5] + A[2] * x[8]; 107 | b[5] = A[3] * x[2] + A[4] * x[5] + A[5] * x[8]; 108 | b[8] = A[6] * x[2] + A[7] * x[5] + A[8] * x[8]; 109 | } 110 | 111 | static double matrix_determinant_3x3(double* A) 112 | { 113 | return A[0] * (A[4]*A[8] - A[5]*A[7]) 114 | - A[1] * (A[3]*A[8] - A[5]*A[6]) 115 | + A[2] * (A[3]*A[7] - A[4]*A[6]); 116 | } 117 | 118 | static void flip_matrix(double* A) 119 | { 120 | for (int i=0;i<9;i++) 121 | A[i] = -A[i]; 122 | } 123 | 124 | static bool optimal_quaternion(double* A, bool polar, double E0, double* p_nrmsdsq, double* qopt) 125 | { 126 | const double evecprec = 1e-6; 127 | const double evalprec = 1e-11; 128 | 129 | double Sxx = A[0], Sxy = A[1], Sxz = A[2], 130 | Syx = A[3], Syy = A[4], Syz = A[5], 131 | Szx = A[6], Szy = A[7], Szz = A[8]; 132 | 133 | double Sxx2 = Sxx * Sxx, Syy2 = Syy * Syy, Szz2 = Szz * Szz, 134 | Sxy2 = Sxy * Sxy, Syz2 = Syz * Syz, Sxz2 = Sxz * Sxz, 135 | Syx2 = Syx * Syx, Szy2 = Szy * Szy, Szx2 = Szx * Szx; 136 | 137 | double fnorm_squared = Sxx2 + Syy2 + Szz2 + Sxy2 + Syz2 + Sxz2 + Syx2 + Szy2 + Szx2; 138 | 139 | double SyzSzymSyySzz2 = 2.0 * (Syz * Szy - Syy * Szz); 140 | double Sxx2Syy2Szz2Syz2Szy2 = Syy2 + Szz2 - Sxx2 + Syz2 + Szy2; 141 | double SxzpSzx = Sxz + Szx; 142 | double SyzpSzy = Syz + Szy; 143 | double SxypSyx = Sxy + Syx; 144 | double SyzmSzy = Syz - Szy; 145 | double SxzmSzx = Sxz - Szx; 146 | double SxymSyx = Sxy - Syx; 147 | double SxxpSyy = Sxx + Syy; 148 | double SxxmSyy = Sxx - Syy; 149 | double Sxy2Sxz2Syx2Szx2 = Sxy2 + Sxz2 - Syx2 - Szx2; 150 | 151 | double C[3]; 152 | C[0] = Sxy2Sxz2Syx2Szx2 * Sxy2Sxz2Syx2Szx2 153 | + (Sxx2Syy2Szz2Syz2Szy2 + SyzSzymSyySzz2) * (Sxx2Syy2Szz2Syz2Szy2 - SyzSzymSyySzz2) 154 | + (-(SxzpSzx)*(SyzmSzy)+(SxymSyx)*(SxxmSyy-Szz)) * (-(SxzmSzx)*(SyzpSzy)+(SxymSyx)*(SxxmSyy+Szz)) 155 | + (-(SxzpSzx)*(SyzpSzy)-(SxypSyx)*(SxxpSyy-Szz)) * (-(SxzmSzx)*(SyzmSzy)-(SxypSyx)*(SxxpSyy+Szz)) 156 | + (+(SxypSyx)*(SyzpSzy)+(SxzpSzx)*(SxxmSyy+Szz)) * (-(SxymSyx)*(SyzmSzy)+(SxzpSzx)*(SxxpSyy+Szz)) 157 | + (+(SxypSyx)*(SyzmSzy)+(SxzmSzx)*(SxxmSyy-Szz)) * (-(SxymSyx)*(SyzpSzy)+(SxzmSzx)*(SxxpSyy-Szz)); 158 | 159 | C[1] = 8.0 * (Sxx*Syz*Szy + Syy*Szx*Sxz + Szz*Sxy*Syx - Sxx*Syy*Szz - Syz*Szx*Sxy - Szy*Syx*Sxz); 160 | C[2] = -2.0 * fnorm_squared; 161 | 162 | //Newton-Raphson 163 | double mxEigenV = polar ? sqrt(3 * fnorm_squared) : E0; 164 | if (mxEigenV > evalprec) 165 | { 166 | for (int i=0;i<50;i++) 167 | { 168 | double oldg = mxEigenV; 169 | double x2 = mxEigenV*mxEigenV; 170 | double b = (x2 + C[2])*mxEigenV; 171 | double a = b + C[1]; 172 | double delta = ((a * mxEigenV + C[0]) / (2 * x2 * mxEigenV + b + a)); 173 | mxEigenV -= delta; 174 | if (fabs(mxEigenV - oldg) < fabs(evalprec * mxEigenV)) 175 | break; 176 | } 177 | } 178 | else 179 | { 180 | mxEigenV = 0.0; 181 | } 182 | 183 | (*p_nrmsdsq) = std::max(0.0, 2.0 * (E0 - mxEigenV)); 184 | 185 | double a11 = SxxpSyy + Szz - mxEigenV; 186 | double a12 = SyzmSzy; 187 | double a13 = -SxzmSzx; 188 | double a14 = SxymSyx; 189 | 190 | double a21 = SyzmSzy; 191 | double a22 = SxxmSyy - Szz -mxEigenV; 192 | double a23 = SxypSyx; 193 | double a24 = SxzpSzx; 194 | 195 | double a31 = a13; 196 | double a32 = a23; 197 | double a33 = Syy - Sxx - Szz - mxEigenV; 198 | double a34 = SyzpSzy; 199 | 200 | double a41 = a14; 201 | double a42 = a24; 202 | double a43 = a34; 203 | double a44 = Szz - SxxpSyy - mxEigenV; 204 | 205 | double a3344_4334 = a33 * a44 - a43 * a34; 206 | double a3244_4234 = a32 * a44 - a42 * a34; 207 | double a3243_4233 = a32 * a43 - a42 * a33; 208 | double a3143_4133 = a31 * a43 - a41 * a33; 209 | double a3144_4134 = a31 * a44 - a41 * a34; 210 | double a3142_4132 = a31 * a42 - a41 * a32; 211 | double a1324_1423 = a13 * a24 - a14 * a23; 212 | double a1224_1422 = a12 * a24 - a14 * a22; 213 | double a1223_1322 = a12 * a23 - a13 * a22; 214 | double a1124_1421 = a11 * a24 - a14 * a21; 215 | double a1123_1321 = a11 * a23 - a13 * a21; 216 | double a1122_1221 = a11 * a22 - a12 * a21; 217 | 218 | double q[4][4]; 219 | q[0][0] = a12 * a3344_4334 - a13 * a3244_4234 + a14 * a3243_4233; 220 | q[0][1] = -a11 * a3344_4334 + a13 * a3144_4134 - a14 * a3143_4133; 221 | q[0][2] = a11 * a3244_4234 - a12 * a3144_4134 + a14 * a3142_4132; 222 | q[0][3] = -a11 * a3243_4233 + a12 * a3143_4133 - a13 * a3142_4132; 223 | 224 | q[1][0] = a22 * a3344_4334 - a23 * a3244_4234 + a24 * a3243_4233; 225 | q[1][1] = -a21 * a3344_4334 + a23 * a3144_4134 - a24 * a3143_4133; 226 | q[1][2] = a21 * a3244_4234 - a22 * a3144_4134 + a24 * a3142_4132; 227 | q[1][3] = -a21 * a3243_4233 + a22 * a3143_4133 - a23 * a3142_4132; 228 | 229 | q[2][0] = a32 * a1324_1423 - a33 * a1224_1422 + a34 * a1223_1322; 230 | q[2][1] = -a31 * a1324_1423 + a33 * a1124_1421 - a34 * a1123_1321; 231 | q[2][2] = a31 * a1224_1422 - a32 * a1124_1421 + a34 * a1122_1221; 232 | q[2][3] = -a31 * a1223_1322 + a32 * a1123_1321 - a33 * a1122_1221; 233 | 234 | q[3][0] = a42 * a1324_1423 - a43 * a1224_1422 + a44 * a1223_1322; 235 | q[3][1] = -a41 * a1324_1423 + a43 * a1124_1421 - a44 * a1123_1321; 236 | q[3][2] = a41 * a1224_1422 - a42 * a1124_1421 + a44 * a1122_1221; 237 | q[3][3] = -a41 * a1223_1322 + a42 * a1123_1321 - a43 * a1122_1221; 238 | 239 | double qsqr[4]; 240 | for (int i=0;i<4;i++) 241 | qsqr[i] = q[i][0]*q[i][0] + q[i][1]*q[i][1] + q[i][2]*q[i][2] + q[i][3]*q[i][3]; 242 | 243 | int bi = 0; 244 | double max = 0; 245 | for (int i=0;i<4;i++) 246 | { 247 | if (qsqr[i] > max) 248 | { 249 | bi = i; 250 | max = qsqr[i]; 251 | } 252 | } 253 | 254 | bool too_small = false; 255 | if (qsqr[bi] < evecprec) 256 | { 257 | //if qsqr is still too small, return the identity rotation. 258 | q[bi][0] = 1; 259 | q[bi][1] = 0; 260 | q[bi][2] = 0; 261 | q[bi][3] = 0; 262 | too_small = true; 263 | } 264 | else 265 | { 266 | double normq = sqrt(qsqr[bi]); 267 | q[bi][0] /= normq; 268 | q[bi][1] /= normq; 269 | q[bi][2] /= normq; 270 | q[bi][3] /= normq; 271 | } 272 | 273 | memcpy(qopt, q[bi], 4 * sizeof(double)); 274 | return !too_small; 275 | } 276 | 277 | int polar_decomposition_3x3(double* _A, bool right_sided, double* U, double* P) 278 | { 279 | double A[9]; 280 | memcpy(A, _A, 9 * sizeof(double)); 281 | 282 | double det = matrix_determinant_3x3(A); 283 | if (det < 0) 284 | flip_matrix(A); 285 | 286 | double q[4]; 287 | double nrmsdsq = 0; 288 | optimal_quaternion(A, true, -1, &nrmsdsq, q); 289 | q[0] = -q[0]; 290 | quaternion_to_rotation_matrix(q, U); 291 | 292 | if (det < 0) 293 | flip_matrix(U); 294 | 295 | double UT[9] = {U[0], U[3], U[6], U[1], U[4], U[7], U[2], U[5], U[8]}; 296 | 297 | if (right_sided) 298 | matmul_3x3(UT, _A, P); 299 | else 300 | matmul_3x3(_A, UT, P); 301 | 302 | return 0; 303 | } 304 | 305 | void InnerProduct(double *A, int num, const double (*coords1)[3], double (*coords2)[3], int8_t* permutation) 306 | { 307 | A[0] = A[1] = A[2] = A[3] = A[4] = A[5] = A[6] = A[7] = A[8] = 0.0; 308 | 309 | for (int i = 0; i < num; ++i) 310 | { 311 | double x1 = coords1[i][0]; 312 | double y1 = coords1[i][1]; 313 | double z1 = coords1[i][2]; 314 | 315 | double x2 = coords2[permutation[i]][0]; 316 | double y2 = coords2[permutation[i]][1]; 317 | double z2 = coords2[permutation[i]][2]; 318 | 319 | A[0] += x1 * x2; 320 | A[1] += x1 * y2; 321 | A[2] += x1 * z2; 322 | 323 | A[3] += y1 * x2; 324 | A[4] += y1 * y2; 325 | A[5] += y1 * z2; 326 | 327 | A[6] += z1 * x2; 328 | A[7] += z1 * y2; 329 | A[8] += z1 * z2; 330 | } 331 | } 332 | 333 | int FastCalcRMSDAndRotation(double *A, double E0, double *p_nrmsdsq, double *q, double* U) 334 | { 335 | optimal_quaternion(A, false, E0, p_nrmsdsq, q); 336 | quaternion_to_rotation_matrix(q, U); 337 | return 0; 338 | } 339 | 340 | } 341 | 342 | --------------------------------------------------------------------------------