oILAB
Loading...
Searching...
No Matches
GbContinuumImplementation.h
Go to the documentation of this file.
1//
2// Created by Nikhil Chandra Admal on 5/27/24.
3//
4
5#ifndef OILAB_GBCONTINUUMIMPLEMENTATION_H
6#define OILAB_GBCONTINUUMIMPLEMENTATION_H
7
8#include "Diff.h"
9#include <numbers>
10
11namespace oILAB {
12
13template <int dim>
15 const Eigen::Matrix<double, dim, dim - 1> &domain,
16 const std::map<OrderedTuplet<dim + 1>, VectorDimD> &xuPairs,
17 const std::array<Eigen::Index, dim - 1> &n,
18 const std::map<OrderedTuplet<dim + 1>, VectorDimD> &atoms,
19 const bool &verbosity)
20 : gbDomain(domain), xuPairs(xuPairs), n(n),
21 bbhat(calculateb(domain, xuPairs, n, atoms)), b(bbhat.first),
22 bhat(bbhat.second) {
23 uAverage.setZero();
24 for (const auto &[x, u] : xuPairs) {
25 uAverage = uAverage + u;
26 }
27 if (xuPairs.size() != 0)
28 uAverage = uAverage / xuPairs.size();
29
30 if (verbosity) {
31 std::cout << "-------------------------------------------------------------"
32 "-----------------"
33 << std::endl;
34 std::cout << "Constraints: " << std::endl;
35 }
36 for (const auto &[x, u] : xuPairs) {
37 if (verbosity)
38 std::cout << "x = " << atoms.at(x).transpose()
39 << "; displacement = " << u.transpose() << std::endl;
40 if ((u - displacement(x) - uAverage).norm() > FLT_EPSILON)
41 throw std::runtime_error(
42 "GBContinuum construction failed - unable to impose constraints.");
43 }
44 if (verbosity)
45 std::cout << std::endl;
46
47 }
48
49 template<int dim>
50 std::vector<PeriodicFunction<double,dim-1>>
52 {
53 auto bLocal= get_b();
54 auto unitcellLocal= bLocal[0].unitCell;
55
56 // dimension-dependent part
57 Diff<dim-1> dx({1,0},unitcellLocal,n);
58 Diff<dim-1> dy({0,1},unitcellLocal,n);
59
60 std::vector<PeriodicFunction<double,dim-1>> alpha;
61 for(int i=0; i<dim; ++i)
62 {
63 for(int j=0; j<dim-1; ++j)
64 {
65 PeriodicFunction<double,dim-1> alphaij(n, unitcellLocal);;
66 for(int k=0; k<dim-1; ++k)
67 {
68 if (k==j) continue;
69 PeriodicFunction<double,dim-1> dlbi_dxk(n,unitcellLocal);;
70 if (k==0)
71 dx.perform_op(bLocal[i].values.data(), dlbi_dxk.values.data());
72 else if (k==1)
73 dy.perform_op(bLocal[i].values.data(), dlbi_dxk.values.data());
74 if (j==0 && k==1)
75 alphaij.values= alphaij.values + dlbi_dxk.values;
76 else if (j==1 && k==0)
77 alphaij.values= alphaij.values - dlbi_dxk.values;
78 }
79 alpha.push_back(alphaij);
80 }
81 }
82 return alpha;
83 }
84
85 template<int dim>
86 std::vector<PeriodicFunction<double,dim-1>>
88 {
89 Eigen::Matrix<double,dim,dim-1> orthogonalVectors;
90 Eigen::Matrix<double,dim,dim-1> unitCell= b[0].unitCell;
91
92 // Form a rotation matrix to transform to a 2D coordinate system containing the grain boundary
93 for(int i=0; i<dim-1; ++i)
94 {
95 orthogonalVectors.col(i)= unitCell.col(i);
96 for(int j=0; j<i; ++j)
97 orthogonalVectors.col(i)-= unitCell.col(i).dot(orthogonalVectors.col(j))* orthogonalVectors.col(j);
98 orthogonalVectors.col(i)= orthogonalVectors.col(i).normalized();
99 }
100 Rotation<dim> rotation(orthogonalVectors);
101 assert((rotation*unitCell).row(2).norm()<FLT_EPSILON);
102
103 // transform the slip vectors b to the local coordinate system
104 std::vector<PeriodicFunction<double, dim - 1>> bLocal;
105 Eigen::Matrix<double,dim-1,dim-1> unitcellLocal= (rotation*unitCell).block(0,0,dim-1,dim-1);
106 for(int i= 0; i<dim; ++i) {
107 PeriodicFunction<double, dim - 1> temp(n,unitcellLocal);
108 for (int j = 0; j < dim; ++j)
109 temp.values = temp.values + rotation(i, j) * b[j].values;
110 bLocal.push_back(temp);
111 }
112 return bLocal;
113 }
114
115 template<int dim>
116 PeriodicFunction<double,dim-1>
117 GbContinuum<dim>::get_pi(const Eigen::Matrix<double,dim,dim-1>& domain,
118 const std::array<Eigen::Index,dim-1>& n,
119 const VectorDimD& point)
120 {
121 Eigen::Matrix<double,dim,dim-1> basisVectors(domain.transpose().completeOrthogonalDecomposition().pseudoInverse());
122 VectorDimD normal(domain.col(0).cross(domain.col(1)));
123 normal.normalize();
124
125 //DisplacementKernel f(normal,10);
126 // change 1e6 entry
127 DisplacementKernel f(normal);
129 return PeriodicFunction<double,dim-1>(n,domain,piFunction);
130 }
131
132
133 template<int dim>
135 GbContinuum<dim>::get_pihat(const Eigen::Matrix<double,dim,dim-1>& domain,
136 const std::array<Eigen::Index,dim-1>& n,
137 const VectorDimD& point)
138 {
139 Eigen::Matrix<double,dim,dim-1> basisVectors(domain.transpose().completeOrthogonalDecomposition().pseudoInverse());
140 VectorDimD normal(domain.col(0).cross(domain.col(1)));
141 normal.normalize();
142
143 std::vector<LatticeFunction<std::complex<double>,dim-1>> pihat;
144 //DisplacementKernel f(normal,10);
145
146 ShiftedDisplacementKernelFT pihatFunction(point,normal);
147 return LatticeFunction<std::complex<double>,dim-1> (n,basisVectors,pihatFunction);
148 }
149
150 template<int dim>
152 GbContinuum<dim>::calculateb(const Eigen::Matrix<double,dim,dim-1>& domain,
153 const std::map<OrderedTuplet<dim+1>,VectorDimD>& xuPairs,
154 const std::array<Eigen::Index,dim-1>& n,
155 const std::map<OrderedTuplet<dim+1>,VectorDimD>& atoms)
156 {
157 if (HhatInvComponents.size() ==0 ) HhatInvComponents= getHhatInvComponents(domain,n);
158 Eigen::Matrix<double,dim,dim-1> basisVectors(domain.transpose().completeOrthogonalDecomposition().pseudoInverse());
159 // lb0 - read as "local b0"
160 std::vector<PeriodicFunction<double,dim-1>> lb0, lb;
161 std::vector<LatticeFunction<std::complex<double>,dim-1>> lb0hat;
162 for(int i=0; i<dim; ++i)
163 {
164 lb0.push_back(PeriodicFunction<double,dim-1>(n,domain));
165 lb.push_back(PeriodicFunction<double,dim-1>(n,domain));
166 lb0hat.push_back(LatticeFunction<std::complex<double>,dim-1>(n,basisVectors));
167 }
168 std::vector<LatticeFunction<std::complex<double>,dim-1>> lbhat(lb0hat);
169
170
171 if(piPeriodicFunctions.empty()) {
172 for (const auto& [key, value]: atoms) {
173 // the cross product of the domain vectors has to be parallel to nA
174 Eigen::Matrix<double,dim,dim-1> basisVectors(domain.transpose().completeOrthogonalDecomposition().pseudoInverse());
175 VectorDimD normal(domain.col(0).cross(domain.col(1)));
176 normal.normalize();
177
178 VectorDimD perturbedValue= value;
179 if(abs(value.dot(normal)) < FLT_EPSILON) // belongs to the GB
180 {
181 if (key(dim)==1)
182 perturbedValue= value - (value.dot(normal) + FLT_EPSILON) * normal;
183 else if (key(dim)==2)
184 perturbedValue= value - (value.dot(normal) - FLT_EPSILON) * normal;
185 }
186
187 else if (key(dim)==-1 || key(dim)==-2) // belongs to (lattice 1 & grain 2) || (lattice 2 & grain 1)
188 {
189 perturbedValue= value - 2 * value.dot(normal) * normal;
190 }
191 piPeriodicFunctions.insert({key, get_pi(domain, n, perturbedValue)});
192 pihatLatticeFunctions.insert({key, get_pihat(domain, n, perturbedValue)});
193
194 /*
195 if(abs(value.dot(normal)) < FLT_EPSILON && key(dim)==1) // belongs to lattice 1 and on the GB
196 {
197 VectorDimD perturbedValue= value - (value.dot(normal) + FLT_EPSILON) * normal;
198 piPeriodicFunctions.insert({key, get_pi(domain, n, perturbedValue)});
199 pihatLatticeFunctions.insert({key, get_pihat(domain, n, perturbedValue)});
200 }
201 else if(abs(value.dot(normal)) < FLT_EPSILON && key(dim)==2) // belongs to lattice 2 and on the GB
202 {
203 VectorDimD perturbedValue= value - (value.dot(normal) - FLT_EPSILON) * normal;
204 piPeriodicFunctions.insert({key, get_pi(domain, n, perturbedValue)});
205 pihatLatticeFunctions.insert({key, get_pihat(domain, n, perturbedValue)});
206 }
207 else {
208 piPeriodicFunctions.insert({key, get_pi(domain, n, value)});
209 pihatLatticeFunctions.insert({key, get_pihat(domain, n, value)});
210 }
211 */
212 }
213 }
214
215 // matrices P and u
216 const int numberOfCslPoints= xuPairs.size();
217 Eigen::Matrix<std::complex<double>,Eigen::Dynamic,dim> uMatrix(numberOfCslPoints,dim);
218 Eigen::Matrix<std::complex<double>,Eigen::Dynamic,Eigen::Dynamic> P(numberOfCslPoints,numberOfCslPoints);
219
220
221 VectorDimD uAverage;
222 uAverage.setZero();
223 for(const auto& [xi,ui] : xuPairs)
224 {
225 uAverage= uAverage + ui;
226 }
227 if (xuPairs.size() != 0)
228 uAverage= uAverage/xuPairs.size();
229
230 int row= -1;
231 for(const auto& [xi,ui] : xuPairs)
232 {
233 row++;
234 //uMatrix.row(row)= ui;
235 uMatrix.row(row)= ui-uAverage;
236 int col= -1;
237 for(const auto& [xj,uj] : xuPairs)
238 {
239 col++;
240 P(row, col) = pihatLatticeFunctions.at(xj).dot(pihatLatticeFunctions.at(xi));
241 }
242
243 }
244
245 // Solve P \alpha = u
246 Eigen::Matrix<std::complex<double>,Eigen::Dynamic,dim> alpha(numberOfCslPoints,dim);
247 Eigen::LLT<Eigen::Matrix<std::complex<double>,Eigen::Dynamic,Eigen::Dynamic>> llt;
248 llt.compute(P);
249 alpha= llt.solve(uMatrix);
250
251 // calculate lb0 and lb0hat
252
253 for(int i=0; i<dim; ++i)
254 {
255 lb0hat[i].values.setZero();
256 int j= -1;
257 for(const auto& [xj,uj] : xuPairs) {
258 j++;
259 lb0hat[i].values = lb0hat[i].values + pihatLatticeFunctions.at(xj).values * alpha(j,i);
260 }
261 }
262 for(int i=0; i<dim; ++i) {
263 lb0[i].values.setZero();
264 int j = -1;
265 for (const auto &[xj, uj]: xuPairs) {
266 j++;
267 lb0[i].values = lb0[i].values + piPeriodicFunctions.at(xj).values * alpha(j,i).real();
268 }
269 }
270
271 // calculate lagrange multipliers, lbhat, and lb
272 Eigen::VectorXcd uFlattened;
273 uFlattened= uMatrix.reshaped();
274 Eigen::VectorXcd lm(dim*numberOfCslPoints);
275 Eigen::MatrixXcd M(dim*numberOfCslPoints,dim*numberOfCslPoints);
276 M.setZero();
277
278 for (int i=0; i<dim; ++i)
279 {
280 int j= -1;
281 for (const auto& [xj,uj] : xuPairs)
282 {
283 ++j;
284 int row= i*numberOfCslPoints + j;
285 for (int l=0; l<dim; ++l)
286 {
287 int k= -1;
288 for(const auto& [xk,uk] : xuPairs)
289 {
290 k++;
291 int col= l*numberOfCslPoints + k;
292
293 std::complex<double> temp;
294
295 if (i==0 && l==0) temp = (HhatInvComponents[0]*pihatLatticeFunctions.at(xk)).dot(pihatLatticeFunctions.at(xj));
296 if (i==1 && l==1) temp = (HhatInvComponents[1]*pihatLatticeFunctions.at(xk)).dot(pihatLatticeFunctions.at(xj));
297 if (i==2 && l==2) temp = (HhatInvComponents[2]*pihatLatticeFunctions.at(xk)).dot(pihatLatticeFunctions.at(xj));
298 if ((i==1 && l==2) || (i==2 && l==1)) temp = (HhatInvComponents[3]*pihatLatticeFunctions.at(xk)).dot(pihatLatticeFunctions.at(xj));
299 if ((i==0 && l==2) || (i==2 && l==0)) temp = (HhatInvComponents[4]*pihatLatticeFunctions.at(xk)).dot(pihatLatticeFunctions.at(xj));
300 if ((i==0 && l==1) || (i==1 && l==0)) temp = (HhatInvComponents[5]*pihatLatticeFunctions.at(xk)).dot(pihatLatticeFunctions.at(xj));
301 M(row, col) = temp;
302 }
303 }
304 }
305 }
306
307 // Solve M lm = u2
308 Eigen::LLT<Eigen::Matrix<std::complex<double>,Eigen::Dynamic,Eigen::Dynamic>> llt2;
309 llt2.compute(M);
310 lm= llt2.solve(uFlattened);
311
312 lbhat[0].values.setZero();
313 lbhat[1].values.setZero();
314 lbhat[2].values.setZero();
315
316 auto lmMatrix= lm.reshaped(numberOfCslPoints,dim);
317 // temp = \sum_k p_k * lm_k
318 std::vector<LatticeFunction<std::complex<double>,dim-1>> temp;
319
320 for (int i=0; i<dim; ++i) {
321 temp.push_back(LatticeFunction<std::complex<double>, dim - 1>(n, basisVectors));
322 int k= -1;
323 for (const auto& [xk,uk] : xuPairs) {
324 k++;
325 temp[i].values = temp[i].values + pihatLatticeFunctions.at(xk).values * lmMatrix(k, i);
326 }
327 }
328
329 lbhat[0].values= HhatInvComponents[0].values * temp[0].values +
330 HhatInvComponents[5].values * temp[1].values +
331 HhatInvComponents[4].values * temp[2].values;
332
333 lbhat[1].values= HhatInvComponents[5].values * temp[0].values +
334 HhatInvComponents[1].values * temp[1].values +
335 HhatInvComponents[3].values * temp[2].values;
336
337 lbhat[2].values= HhatInvComponents[4].values * temp[0].values +
338 HhatInvComponents[3].values * temp[1].values +
339 HhatInvComponents[2].values * temp[2].values;
340
341 for (int i=0; i<dim; ++i)
342 lb[i].values = lbhat[i].ifft().values.real();
343
344 //return std::make_pair(lb,lbhat);
345 return std::make_pair(lb,lbhat);
346 }
347
348 template<int dim>
350 {
351 VectorDimD u;
352
353 // u = f \star b
354 for(int i=0; i<dim; ++i)
355 u(i)= (bhat[i].dot(pihatLatticeFunctions.at(t))).real();
356
357 return u;
358
359 }
360
361
362 template<int dim>
364 {
365 VectorDimD u;
366
367 Eigen::Matrix<double,dim,dim-1> basisVectors(gbDomain.transpose().completeOrthogonalDecomposition().pseudoInverse());
368 VectorDimD normal(gbDomain.col(0).cross(gbDomain.col(1)));
369 normal.normalize();
370
371
372 ShiftedDisplacementKernelFT pihatFunction(t,normal);
373 auto lf= LatticeFunction<std::complex<double>,dim-1> (n,basisVectors,pihatFunction);
374
375 // u = f \star b
376 for(int i=0; i<dim; ++i) {
377 u(i) = bhat[i].dot(lf).real();
378 //assert(abs(bhat[i].dot(lf).imag()) < FLT_EPSILON);
379 }
380
381
382
383 return u;
384
385 }
386
387 template<int dim>
388 std::vector<LatticeFunction<std::complex<double>,dim-1>>
389 GbContinuum<dim>::getHhatInvComponents(const Eigen::Matrix<double, dim,dim-1>& domain,
390 const std::array<Eigen::Index,dim-1>& n)
391 {
392 Eigen::Matrix<double,Eigen::Dynamic,dim-1> basisVectors(domain.transpose().completeOrthogonalDecomposition().pseudoInverse());
393 std::vector<LatticeFunction<std::complex<double>,dim-1>> output;
394
395 output.push_back(LatticeFunction<std::complex<double>,dim-1>(n,basisVectors,HhatInvFunction(0,0,domain)));
396 output.push_back(LatticeFunction<std::complex<double>,dim-1>(n,basisVectors,HhatInvFunction(1,1,domain)));
397 output.push_back(LatticeFunction<std::complex<double>,dim-1>(n,basisVectors,HhatInvFunction(2,2,domain)));
398 output.push_back(LatticeFunction<std::complex<double>,dim-1>(n,basisVectors,HhatInvFunction(1,2,domain)));
399 output.push_back(LatticeFunction<std::complex<double>,dim-1>(n,basisVectors,HhatInvFunction(0,2,domain)));
400 output.push_back(LatticeFunction<std::complex<double>,dim-1>(n,basisVectors,HhatInvFunction(0,1,domain)));
401 return output;
402 }
403
404
405 /*----------------------------*/
406 DisplacementKernel::DisplacementKernel(const Eigen::Vector<double, Eigen::Dynamic>& _normal):
407 Function<DisplacementKernel, double>(),
408 normal(_normal)
409 {}
410 double DisplacementKernel::operator()(const Eigen::Vector<double, Eigen::Dynamic> &x) const
411 {
412 return -x.dot(normal) / (4 * std::numbers::pi * (std::pow(x.norm(), 3)));
413 }
414 /*----------------------------*/
416 const Eigen::VectorXd& normal):
417 x(x),normal(normal.normalized())
418 { }
419
420 std::complex<double> ShiftedDisplacementKernelFT::operator()(const Eigen::VectorXd& xi) const
421 {
422 double xNormalComponent= x.dot(normal);
423 auto xProjected= (x-xNormalComponent*normal).eval();
424 double xiBar= xi.norm();
425 std::complex<double> output= -0.5*std::exp(-2*std::numbers::pi* std::complex<double>(0,1) *
426 xProjected.dot(xi)
427 ) * std::exp(-2*std::numbers::pi*xiBar*abs(xNormalComponent));
428 if (abs(xNormalComponent) < DBL_EPSILON)
429 return output;
430 else
431 return output*xNormalComponent/abs(xNormalComponent);
432 }
433 /*----------------------------*/
434 HhatInvFunction::HhatInvFunction(const int& t, const int& i, const Eigen::MatrixXd& domain) :
435 t(t),i(i), e1(domain.col(0).normalized()), e3(domain.col(1).normalized())
436 { }
437 std::complex<double> HhatInvFunction::operator()(const Eigen::VectorXd& xi) const
438 {
439 if (xi.isZero())
440 return std::complex<double>(0,0);
441 std::complex<double> output(0,0);
442 double xi1= xi.dot(e1);
443 double xi3= xi.dot(e3);
444 double xi2= (xi-xi1*e1-xi3*e3).norm();
445
446 output= tensorHhat(t,i,(Eigen::VectorXd(3) << xi1,xi2,xi3).finished());
447 output= -std::pow(2*std::numbers::pi,2) * output;
448 return output;
449 }
450 /*----------------------------*/
451
452 } // namespace oILAB
453
454#endif
double operator()(const Eigen::Vector< double, Eigen::Dynamic > &x) const
DisplacementKernel(const Eigen::Vector< double, Eigen::Dynamic > &_normal)
Eigen::Vector< double, Eigen::Dynamic > normal
Definition GbContinuum.h:20
static LatticeFunction< std::complex< double >, dim-1 > get_pihat(const Eigen::Matrix< double, dim, dim-1 > &domain, const std::array< Eigen::Index, dim-1 > &n, const VectorDimD &point)
VectorDimD uAverage
Definition GbContinuum.h:88
GbContinuum(const Eigen::Matrix< double, dim, dim-1 > &domain, const std::map< OrderedTuplet< dim+1 >, VectorDimD > &xuPairs, const std::array< Eigen::Index, dim-1 > &n, const std::map< OrderedTuplet< dim+1 >, VectorDimD > &atoms, const bool &verbosity=false)
typename LatticeCore< dim >::VectorDimD VectorDimD
Definition GbContinuum.h:48
static FunctionFFTPair calculateb(const Eigen::Matrix< double, dim, dim-1 > &domain, const std::map< OrderedTuplet< dim+1 >, VectorDimD > &xuPairs, const std::array< Eigen::Index, dim-1 > &n, const std::map< OrderedTuplet< dim+1 >, VectorDimD > &points)
typename std::pair< std::vector< PeriodicFunction< double, dim-1 > >, std::vector< LatticeFunction< std::complex< double >, dim-1 > > > FunctionFFTPair
Definition GbContinuum.h:50
std::map< OrderedTuplet< dim+1 >, VectorDimD > atoms
Definition GbContinuum.h:87
static GbLatticeFunctions getHhatInvComponents(const Eigen::Matrix< double, dim, dim-1 > &domain, const std::array< Eigen::Index, dim-1 > &n)
std::vector< PeriodicFunction< double, dim-1 > > get_b() const
std::vector< PeriodicFunction< double, dim-1 > > get_alpha() const
VectorDimD displacement(const OrderedTuplet< dim+1 > &x) const
static PeriodicFunction< double, dim-1 > get_pi(const Eigen::Matrix< double, dim, dim-1 > &domain, const std::array< Eigen::Index, dim-1 > &n, const VectorDimD &point)
const std::map< OrderedTuplet< dim+1 >, VectorDimD > xuPairs
Definition GbContinuum.h:82
static std::complex< double > tensorHhat(const int &t, const int &i, const Eigen::VectorXd &xi)
const Eigen::VectorXd e1
Definition GbContinuum.h:39
const Eigen::VectorXd e3
Definition GbContinuum.h:39
HhatInvFunction(const int &t, const int &i, const Eigen::MatrixXd &domain)
std::complex< double > operator()(const Eigen::VectorXd &xi) const
std::complex< double > dot(const LatticeFunction< std::complex< double >, dim > &other) const
const Eigen::Matrix< double, Eigen::Dynamic, dim > unitCell
Eigen::Vector< double, Eigen::Dynamic > normal
Definition GbContinuum.h:30
Eigen::Vector< double, Eigen::Dynamic > x
Definition GbContinuum.h:30
std::complex< double > operator()(const Eigen::VectorXd &xi) const
ShiftedDisplacementKernelFT(const Eigen::VectorXd &x, const Eigen::VectorXd &normal)