Line data Source code
1 : // Copyright 2024, UChicago Argonne, LLC
2 : // All Rights Reserved
3 : // Software Name: NEML2 -- the New Engineering material Model Library, version 2
4 : // By: Argonne National Laboratory
5 : // OPEN SOURCE LICENSE (MIT)
6 : //
7 : // Permission is hereby granted, free of charge, to any person obtaining a copy
8 : // of this software and associated documentation files (the "Software"), to deal
9 : // in the Software without restriction, including without limitation the rights
10 : // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 : // copies of the Software, and to permit persons to whom the Software is
12 : // furnished to do so, subject to the following conditions:
13 : //
14 : // The above copyright notice and this permission notice shall be included in
15 : // all copies or substantial portions of the Software.
16 : //
17 : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 : // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 : // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 : // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 : // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 : // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 : // THE SOFTWARE.
24 :
25 : #include "neml2/tensors/crystallography.h"
26 :
27 : #include "neml2/tensors/Transformable.h"
28 : #include "neml2/tensors/tensors.h"
29 : #include "neml2/tensors/functions/cat.h"
30 : #include "neml2/misc/assertions.h"
31 :
32 : namespace neml2::crystallography
33 : {
34 : namespace crystal_symmetry_operators
35 : {
36 : Quaternion
37 24 : tetragonal(const TensorOptions & options)
38 : {
39 1008 : return Quaternion::create({{o, z, z, z},
40 : {z, z, o, z},
41 : {z, o, z, z},
42 : {z, z, z, o},
43 : {a, z, z, -a},
44 : {a, z, z, a},
45 : {z, a, a, z},
46 : {z, -a, a, z}},
47 264 : options);
48 816 : }
49 :
50 : Quaternion
51 20 : hexagonal(const TensorOptions & options)
52 : {
53 1240 : return Quaternion::create({{o, z, z, z},
54 : {-h, z, z, b},
55 : {h, z, z, b},
56 : {b, z, z, -h},
57 : {z, z, z, o},
58 : {b, z, z, h},
59 : {z, -h, b, z},
60 : {z, o, z, z},
61 : {z, h, b, z},
62 : {z, b, h, z},
63 : {z, z, o, z},
64 : {z, b, -h, z}},
65 300 : options);
66 1000 : }
67 :
68 : Quaternion
69 39 : cubic(const TensorOptions & options)
70 : {
71 4758 : return Quaternion::create({{o, z, z, z}, {h, h, h, h}, {-h, h, h, h}, {h, -h, h, h},
72 : {h, h, -h, h}, {-h, -h, -h, h}, {h, -h, -h, h}, {-h, -h, h, h},
73 : {-h, h, -h, h}, {z, z, o, z}, {z, z, z, o}, {z, o, z, z},
74 : {z, -a, z, a}, {z, a, z, a}, {a, z, a, z}, {a, z, -a, z},
75 : {z, z, -a, a}, {a, a, z, z}, {a, -a, z, z}, {z, z, a, a},
76 : {z, -a, a, z}, {a, z, z, -a}, {z, a, a, z}, {a, z, z, a}},
77 1053 : options);
78 3822 : }
79 : } // namespace crystal_symmetry_operators
80 :
81 : R2
82 75 : symmetry_operations_from_orbifold(const std::string & orbifold, const TensorOptions & options)
83 : {
84 : using namespace crystal_symmetry_operators;
85 :
86 75 : if (orbifold == "432")
87 35 : return transform_from_quaternion(cubic(options));
88 :
89 40 : if (orbifold == "23")
90 16 : return transform_from_quaternion(cubic(options).batch_index({indexing::Slice(0, 12)}));
91 :
92 36 : if (orbifold == "622")
93 4 : return transform_from_quaternion(hexagonal(options));
94 :
95 32 : if (orbifold == "32")
96 : return transform_from_quaternion(
97 28 : batch_cat({hexagonal(options).batch_index({indexing::Slice(0, 3)}),
98 24 : hexagonal(options).batch_index({indexing::Slice(9, 12)})}));
99 :
100 28 : if (orbifold == "6")
101 16 : return transform_from_quaternion(hexagonal(options).batch_index({indexing::Slice(0, 6)}));
102 :
103 24 : if (orbifold == "3")
104 16 : return transform_from_quaternion(hexagonal(options).batch_index({indexing::Slice(0, 3)}));
105 :
106 20 : if (orbifold == "42")
107 4 : return transform_from_quaternion(tetragonal(options));
108 :
109 16 : if (orbifold == "4")
110 : return transform_from_quaternion(
111 28 : batch_cat({tetragonal(options).batch_index({indexing::Slice(0, 1)}),
112 24 : tetragonal(options).batch_index({indexing::Slice(3, 6)})}));
113 :
114 12 : if (orbifold == "222")
115 16 : return transform_from_quaternion(tetragonal(options).batch_index({indexing::Slice(0, 4)}));
116 :
117 8 : if (orbifold == "2")
118 16 : return transform_from_quaternion(tetragonal(options).batch_index({indexing::Slice(0, 2)}));
119 :
120 4 : if (orbifold == "1")
121 16 : return transform_from_quaternion(tetragonal(options).batch_index({indexing::Slice(0, 1)}));
122 :
123 0 : throw NEMLException("Unknown crystal class " + orbifold);
124 120 : }
125 :
126 : Vec
127 62 : unique_bidirectional(const R2 & ops, const Vec & inp)
128 : {
129 62 : neml_assert_dbg(inp.batch_dim() == 0);
130 : // Batched tensor with all possible answers
131 62 : auto options = ops * inp;
132 : // I think we have to go one by one...
133 : // Slightly annoying that while Vec and ATensor are convertible a
134 : // list of Vecs aren't convertable into a TensorList
135 310 : std::vector<ATensor> unique{ATensor(options.batch_index({0}))};
136 62 : Vec unique_vecs = Vec(at::stack(unique));
137 1488 : for (Size i = 1; i < options.batch_size(0).concrete(); i++)
138 : {
139 2852 : auto vi = options.batch_index({i});
140 : // Compares list of vectors to vector to figure out if any are the same
141 2418 : auto same = [](const ATensor & a, const ATensor & b)
142 2418 : { return at::any(at::all(at::isclose(a, b), 1)); };
143 1426 : if (!(same(unique_vecs, vi).item<bool>() || same(unique_vecs, -vi).item<bool>()))
144 : {
145 248 : unique.emplace_back(vi);
146 248 : unique_vecs = Vec(at::stack(unique));
147 : }
148 1426 : }
149 : // Get the batch of all possible answers
150 124 : return unique_vecs;
151 1612 : }
152 :
153 : } // namespace neml2
|