forked from lammps/lammps
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfix_orient_eco.cpp
581 lines (477 loc) · 22.2 KB
/
fix_orient_eco.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
// clang-format off
/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratdir_veces
Steve Plimpton, [email protected]
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing authors: Adrian A. Schratt and Volker Mohles (ICAMS)
------------------------------------------------------------------------- */
#include "fix_orient_eco.h"
#include "atom.h"
#include "citeme.h"
#include "comm.h"
#include "error.h"
#include "force.h"
#include "math_const.h"
#include "memory.h"
#include "neigh_list.h"
#include "neigh_request.h"
#include "neighbor.h"
#include "pair.h"
#include "respa.h"
#include "update.h"
#include <cmath>
using namespace LAMMPS_NS;
using namespace FixConst;
using namespace MathConst;
static const char cite_fix_orient_eco[] =
"fix orient/eco command:\n\n"
"@Article{Schratt20,\n"
" author = {A. A. Schratt, V. Mohles},\n"
" title = {Efficient calculation of the ECO driving force for atomistic simulations of grain boundary motion},\n"
" journal = {Computational Materials Science},\n"
" volume = {182},\n"
" year = {2020},\n"
" pages = {109774},\n"
" doi = {j.commatsci.2020.109774},\n"
" url = {https://doi.org/10.1016/j.commatsci.2020.109774}\n"
"}\n\n";
struct FixOrientECO::Nbr {
double duchi; // potential derivative
double real_phi[2][3]; // real part of wave function
double imag_phi[2][3]; // imaginary part of wave function
};
/* ---------------------------------------------------------------------- */
FixOrientECO::FixOrientECO(LAMMPS *lmp, int narg, char **arg) :
Fix(lmp, narg, arg),
dir_filename(nullptr), order(nullptr), nbr(nullptr), list(nullptr)
{
if (lmp->citeme) lmp->citeme->add(cite_fix_orient_eco);
MPI_Comm_rank(world, &me);
if (narg != 7) error->all(FLERR, "Illegal fix orient/eco command");
scalar_flag = 1; // computes scalar
global_freq = 1; // values can be computed at every timestep
extscalar = 1; // scalar scales with # of atoms
peratom_flag = 1; // quantities are per atom quantities
size_peratom_cols = 2; // # of per atom quantities
peratom_freq = 1;
energy_global_flag = 1;
// parse input parameters
u_0 = utils::numeric(FLERR, arg[3],false,lmp);
sign = (u_0 >= 0.0 ? 1 : -1);
eta = utils::numeric(FLERR, arg[4],false,lmp);
r_cut = utils::numeric(FLERR, arg[5],false,lmp);
// read reference orientations from file
// work on rank 0 only
dir_filename = utils::strdup(arg[6]);
if (me == 0) {
char line[IMGMAX];
char *result;
int count;
FILE *infile = utils::open_potential(dir_filename,lmp,nullptr);
if (infile == nullptr)
error->one(FLERR,"Cannot open fix orient/eco file {}: {}",
dir_filename, utils::getsyserror());
for (int i = 0; i < 6; ++i) {
result = fgets(line, IMGMAX, infile);
if (!result) error->one(FLERR, "Fix orient/eco file read failed");
count = sscanf(line, "%lg %lg %lg", &dir_vec[i][0], &dir_vec[i][1], &dir_vec[i][2]);
if (count != 3) error->one(FLERR, "Fix orient/eco file read failed");
}
fclose(infile);
// calculate reciprocal lattice vectors
get_reciprocal();
squared_cutoff = r_cut * r_cut;
inv_squared_cutoff = 1.0 / squared_cutoff;
half_u = 0.5 * u_0;
inv_eta = 1.0 / eta;
}
// initializations
MPI_Bcast(&dir_vec[0][0], 18, MPI_DOUBLE, 0, world); // communicate direct lattice vectors
MPI_Bcast(&reciprocal_vectors[0][0][0], 18, MPI_DOUBLE, 0, world); // communicate reciprocal vectors
MPI_Bcast(&squared_cutoff, 1, MPI_DOUBLE, 0, world); // communicate squared cutoff radius
MPI_Bcast(&inv_squared_cutoff, 1, MPI_DOUBLE, 0, world); // communicate inverse squared cutoff radius
MPI_Bcast(&half_u, 1, MPI_DOUBLE, 0, world); // communicate half potential energy
MPI_Bcast(&inv_eta, 1, MPI_DOUBLE, 0, world); // communicate inverse threshold
// set comm size needed by this Fix
if (u_0 != 0) comm_forward = sizeof(Nbr) / sizeof(double);
added_energy = 0.0; // initial energy
nmax = atom->nmax;
nbr = (Nbr *) memory->smalloc(nmax * sizeof(Nbr), "orient/eco:nbr");
memory->create(order, nmax, 2, "orient/eco:order");
array_atom = order;
// zero the array since a variable may access it before first run
for (int i = 0; i < atom->nlocal; ++i) order[i][0] = order[i][1] = 0.0;
}
/* ---------------------------------------------------------------------- */
FixOrientECO::~FixOrientECO() {
memory->destroy(order);
memory->sfree(nbr);
delete[] dir_filename;
}
/* ---------------------------------------------------------------------- */
int FixOrientECO::setmask() {
int mask = 0;
mask |= POST_FORCE;
mask |= POST_FORCE_RESPA;
return mask;
}
/* ---------------------------------------------------------------------- */
void FixOrientECO::init() {
// get this processors rank
MPI_Comm_rank(world, &me);
// compute normalization factor
int neigh = get_norm();
if (me == 0) {
utils::logmesg(lmp," fix orient/eco: cutoff={} norm_fac={} "
"neighbors={}\n", r_cut, norm_fac, neigh);
}
inv_norm_fac = 1.0 / norm_fac;
// check parameters
if (r_cut > force->pair->cutforce) {
error->all(FLERR, "Cutoff radius used by fix orient/eco must be smaller than force cutoff");
}
// communicate normalization factor
MPI_Bcast(&norm_fac, 1, MPI_DOUBLE, 0, world);
MPI_Bcast(&inv_norm_fac, 1, MPI_DOUBLE, 0, world);
if (utils::strmatch(update->integrate_style,"^respa")) {
ilevel_respa = ((Respa *) update->integrate)->nlevels - 1;
if (respa_level >= 0) ilevel_respa = MIN(respa_level, ilevel_respa);
}
// need a full neighbor list
// perpetual list, built whenever re-neighboring occurs
int irequest = neighbor->request(this, instance_me);
neighbor->requests[irequest]->pair = 0;
neighbor->requests[irequest]->fix = 1;
neighbor->requests[irequest]->half = 0;
neighbor->requests[irequest]->full = 1;
}
/* ---------------------------------------------------------------------- */
void FixOrientECO::init_list(int /* id */, NeighList *ptr) {
list = ptr;
}
/* ---------------------------------------------------------------------- */
void FixOrientECO::setup(int vflag) {
if (utils::strmatch(update->integrate_style,"^verlet"))
post_force(vflag);
else {
((Respa *) update->integrate)->copy_flevel_f(ilevel_respa);
post_force_respa(vflag,ilevel_respa, 0);
((Respa *) update->integrate)->copy_f_flevel(ilevel_respa);
}
}
/* ---------------------------------------------------------------------- */
void FixOrientECO::post_force(int /* vflag */) {
// set local variables
int ii, i, jj, j; // loop variables and atom IDs
int k; // variable to loop over 3 reciprocal directions
int lambda; // variable to loop over 2 crystals
int dim; // variable to loop over 3 spatial components
double dx, dy, dz; // stores current interatomic vector
double squared_distance; // stores current squared distance
double chi; // stores current order parameter
double weight; // stores current weight function
double scalar_product; // stores current scalar product
double omega; // phase of sine transition
double omega_pre = MY_PI2 * inv_eta; // prefactor for omega
double duchi_pre = half_u * MY_PI * inv_eta * inv_norm_fac; // prefactor for duchi
double sin_om; // stores the value of the sine transition
// initialize added energy at this step
added_energy = 0.0;
// set local pointers and neighbor lists
double **x = atom->x;
double **f = atom->f;
int *mask = atom->mask;
int nall = atom->nlocal + atom->nghost;
int inum = list->inum;
int jnum;
int *ilist = list->ilist;
int *jlist;
int *numneigh = list->numneigh;
int **firstneigh = list->firstneigh;
// insure nbr and order data structures are adequate size
if (nall > nmax) {
nmax = nall;
memory->destroy(nbr);
memory->destroy(order);
nbr = (Nbr *) memory->smalloc(nmax * sizeof(Nbr), "orient/eco:nbr");
memory->create(order, nmax, 2, "orient/eco:order");
array_atom = order;
}
// loop over owned atoms and compute order parameter
for (ii = 0; ii < inum; ++ii) {
i = ilist[ii];
jlist = firstneigh[i];
jnum = numneigh[i];
// initializations
chi = 0.0;
for (k = 0; k < 3; ++k) {
nbr[i].real_phi[0][k] = nbr[i].real_phi[1][k] = 0.0;
nbr[i].imag_phi[0][k] = nbr[i].imag_phi[1][k] = 0.0;
}
// loop over all neighbors of atom i
for (jj = 0; jj < jnum; ++jj) {
j = jlist[jj];
j &= NEIGHMASK;
// vector difference
dx = x[i][0] - x[j][0];
dy = x[i][1] - x[j][1];
dz = x[i][2] - x[j][2];
squared_distance = dx * dx + dy * dy + dz * dz;
if (squared_distance < squared_cutoff) {
squared_distance *= inv_squared_cutoff;
weight = squared_distance * (squared_distance - 2.0) + 1.0;
for (lambda = 0; lambda < 2; ++lambda) {
for (k = 0; k < 3; ++k) {
scalar_product = reciprocal_vectors[lambda][k][0] * dx + reciprocal_vectors[lambda][k][1] * dy + reciprocal_vectors[lambda][k][2] * dz;
nbr[i].real_phi[lambda][k] += weight * cos(scalar_product);
nbr[i].imag_phi[lambda][k] += weight * sin(scalar_product);
}
}
}
}
// collect contributions
for (k = 0; k < 3; ++k) {
chi += (nbr[i].real_phi[0][k] * nbr[i].real_phi[0][k] + nbr[i].imag_phi[0][k] * nbr[i].imag_phi[0][k] -
nbr[i].real_phi[1][k] * nbr[i].real_phi[1][k] - nbr[i].imag_phi[1][k] * nbr[i].imag_phi[1][k]);
}
chi *= inv_norm_fac;
order[i][0] = chi;
// compute normalized order parameter
// and potential energy
if (chi > eta) {
added_energy += half_u;
nbr[i].duchi = 0.0;
order[i][1] = sign;
} else if (chi < -eta) {
added_energy -= half_u;
nbr[i].duchi = 0.0;
order[i][1] = -sign;
} else {
omega = omega_pre * chi;
sin_om = sin(omega);
added_energy += half_u * sin_om;
nbr[i].duchi = duchi_pre * cos(omega);
order[i][1] = sign * sin_om;
}
// compute product with potential derivative
for (k = 0; k < 3; ++k) {
for (lambda = 0; lambda < 2; ++lambda) {
nbr[i].real_phi[lambda][k] *= nbr[i].duchi;
nbr[i].imag_phi[lambda][k] *= nbr[i].duchi;
}
}
}
// set additional local variables
double gradient_ii_cos[2][3][3]; // gradient ii cosine term
double gradient_ii_sin[2][3][3]; // gradient ii sine term
double gradient_ij_vec[2][3][3]; // gradient ij vector term
double gradient_ij_sca[2][3]; // gradient ij scalar term
double weight_gradient_prefactor; // gradient prefactor
double weight_gradient[3]; // gradient of weight
double cos_scalar_product; // cosine of scalar product
double sin_scalar_product; // sine of scalar product
double gcos_scalar_product; // gradient weight function * cosine of scalar product
double gsin_scalar_product; // gradient weight function * sine of scalar product
// compute force only if synthetic
// potential is not zero
if (u_0 != 0.0) {
// communicate to acquire nbr data for ghost atoms
comm->forward_comm_fix(this);
// loop over all atoms
for (ii = 0; ii < inum; ++ii) {
i = ilist[ii];
jlist = firstneigh[i];
jnum = numneigh[i];
const bool no_boundary_atom = (nbr[i].duchi == 0.0);
// skip atoms not in group
if (!(mask[i] & groupbit)) continue;
// initializations
for (k = 0; k < 3; ++k) {
for (lambda = 0; lambda < 2; ++lambda) {
for (dim = 0; dim < 3; ++dim) {
gradient_ii_cos[lambda][k][dim] = 0.0;
gradient_ii_sin[lambda][k][dim] = 0.0;
gradient_ij_vec[lambda][k][dim] = 0.0;
}
gradient_ij_sca[lambda][k] = 0.0;
}
}
// loop over all neighbors of atom i
// for those within squared_cutoff, compute force
for (jj = 0; jj < jnum; ++jj) {
j = jlist[jj];
j &= NEIGHMASK;
// do not compute force on atom i if it is far from boundary
if ((nbr[j].duchi == 0.0) && no_boundary_atom) continue;
// vector difference
dx = x[i][0] - x[j][0];
dy = x[i][1] - x[j][1];
dz = x[i][2] - x[j][2];
squared_distance = dx * dx + dy * dy + dz * dz;
if (squared_distance < squared_cutoff) {
// compute force on atom i
// need weight and its gradient
squared_distance *= inv_squared_cutoff;
weight = squared_distance * (squared_distance - 2.0) + 1.0;
weight_gradient_prefactor = 4.0 * (squared_distance - 1.0) * inv_squared_cutoff;
weight_gradient[0] = weight_gradient_prefactor * dx;
weight_gradient[1] = weight_gradient_prefactor * dy;
weight_gradient[2] = weight_gradient_prefactor * dz;
// (1) compute scalar product and sine and cosine of it
// (2) compute product of sine and cosine with gradient of weight function
// (3) compute gradient_ii_cos and gradient_ii_sin by summing up result of (2)
// (4) compute gradient_ij_vec and gradient_ij_sca
for (lambda = 0; lambda < 2; ++lambda) {
for (k = 0; k < 3; ++k) {
scalar_product = reciprocal_vectors[lambda][k][0] * dx + reciprocal_vectors[lambda][k][1] * dy + reciprocal_vectors[lambda][k][2] * dz;
cos_scalar_product = cos(scalar_product);
sin_scalar_product = sin(scalar_product);
for (dim = 0; dim < 3; ++dim) {
gradient_ii_cos[lambda][k][dim] += (gcos_scalar_product = weight_gradient[dim] * cos_scalar_product);
gradient_ii_sin[lambda][k][dim] += (gsin_scalar_product = weight_gradient[dim] * sin_scalar_product);
gradient_ij_vec[lambda][k][dim] += (nbr[j].real_phi[lambda][k] * gcos_scalar_product - nbr[j].imag_phi[lambda][k] * gsin_scalar_product);
}
gradient_ij_sca[lambda][k] += weight * (nbr[j].real_phi[lambda][k] * sin_scalar_product + nbr[j].imag_phi[lambda][k] * cos_scalar_product);
}
}
}
}
// sum contributions
for (k = 0; k < 3; ++k) {
for (dim = 0; dim < 3; ++dim) {
f[i][dim] -= (nbr[i].real_phi[0][k] * gradient_ii_cos[0][k][dim] + nbr[i].imag_phi[0][k] * gradient_ii_sin[0][k][dim] + gradient_ij_vec[0][k][dim] + reciprocal_vectors[1][k][dim] * gradient_ij_sca[1][k]);
f[i][dim] += (nbr[i].real_phi[1][k] * gradient_ii_cos[1][k][dim] + nbr[i].imag_phi[1][k] * gradient_ii_sin[1][k][dim] + gradient_ij_vec[1][k][dim] + reciprocal_vectors[0][k][dim] * gradient_ij_sca[0][k]);
}
}
}
}
}
/* ---------------------------------------------------------------------- */
void FixOrientECO::post_force_respa(int vflag, int ilevel, int /* iloop */) {
if (ilevel == ilevel_respa) post_force(vflag);
}
/* ---------------------------------------------------------------------- */
double FixOrientECO::compute_scalar() {
double added_energy_total;
MPI_Allreduce(&added_energy, &added_energy_total, 1, MPI_DOUBLE, MPI_SUM, world);
return added_energy_total;
}
/* ---------------------------------------------------------------------- */
int FixOrientECO::pack_forward_comm(int n, int *list, double *buf, int /*pbc_flag*/, int */*pbc*/) {
int i, j;
int m = 0;
for (i = 0; i < n; ++i) {
j = list[i];
*((Nbr*)&buf[m]) = nbr[j];
m += sizeof(Nbr)/sizeof(double);
}
return m;
}
/* ---------------------------------------------------------------------- */
void FixOrientECO::unpack_forward_comm(int n, int first, double *buf) {
int i;
int last = first + n;
int m = 0;
for (i = first; i < last; ++i) {
nbr[i] = *((Nbr*) &buf[m]);
m += sizeof(Nbr) / sizeof(double);
}
}
/* ----------------------------------------------------------------------
memory usage of local atom-based arrays
------------------------------------------------------------------------- */
double FixOrientECO::memory_usage() {
double bytes = (double)nmax * sizeof(Nbr);
bytes += (double)2 * nmax * sizeof(double);
return bytes;
}
/* ----------------------------------------------------------------------
reciprocal lattice vectors from file input
------------------------------------------------------------------------- */
void FixOrientECO::get_reciprocal() {
// volume of unit cell A
double vol = 0.5 * (dir_vec[0][0] * (dir_vec[1][1] * dir_vec[2][2] - dir_vec[2][1] * dir_vec[1][2]) + dir_vec[1][0] * (dir_vec[2][1] * dir_vec[0][2] - dir_vec[0][1] * dir_vec[2][2]) + dir_vec[2][0] * (dir_vec[0][1] * dir_vec[1][2] - dir_vec[1][1] * dir_vec[0][2])) / MY_PI;
double i_vol = 1.0 / vol;
// grain A: reciprocal_vectors 0
reciprocal_vectors[0][0][0] = (dir_vec[1][1] * dir_vec[2][2] - dir_vec[2][1] * dir_vec[1][2]) * i_vol;
reciprocal_vectors[0][0][1] = (dir_vec[1][2] * dir_vec[2][0] - dir_vec[2][2] * dir_vec[1][0]) * i_vol;
reciprocal_vectors[0][0][2] = (dir_vec[1][0] * dir_vec[2][1] - dir_vec[2][0] * dir_vec[1][1]) * i_vol;
// grain A: reciprocal_vectors 1
reciprocal_vectors[0][1][0] = (dir_vec[2][1] * dir_vec[0][2] - dir_vec[0][1] * dir_vec[2][2]) * i_vol;
reciprocal_vectors[0][1][1] = (dir_vec[2][2] * dir_vec[0][0] - dir_vec[0][2] * dir_vec[2][0]) * i_vol;
reciprocal_vectors[0][1][2] = (dir_vec[2][0] * dir_vec[0][1] - dir_vec[0][0] * dir_vec[2][1]) * i_vol;
// grain A: reciprocal_vectors 2
reciprocal_vectors[0][2][0] = (dir_vec[0][1] * dir_vec[1][2] - dir_vec[1][1] * dir_vec[0][2]) * i_vol;
reciprocal_vectors[0][2][1] = (dir_vec[0][2] * dir_vec[1][0] - dir_vec[1][2] * dir_vec[0][0]) * i_vol;
reciprocal_vectors[0][2][2] = (dir_vec[0][0] * dir_vec[1][1] - dir_vec[1][0] * dir_vec[0][1]) * i_vol;
// volume of unit cell B
vol = 0.5 * (dir_vec[3][0] * (dir_vec[4][1] * dir_vec[5][2] - dir_vec[5][1] * dir_vec[4][2]) + dir_vec[4][0] * (dir_vec[5][1] * dir_vec[3][2] - dir_vec[3][1] * dir_vec[5][2]) + dir_vec[5][0] * (dir_vec[3][1] * dir_vec[4][2] - dir_vec[4][1] * dir_vec[3][2])) / MY_PI;
i_vol = 1.0 / vol;
// grain B: reciprocal_vectors 0
reciprocal_vectors[1][0][0] = (dir_vec[4][1] * dir_vec[5][2] - dir_vec[5][1] * dir_vec[4][2]) * i_vol;
reciprocal_vectors[1][0][1] = (dir_vec[4][2] * dir_vec[5][0] - dir_vec[5][2] * dir_vec[4][0]) * i_vol;
reciprocal_vectors[1][0][2] = (dir_vec[4][0] * dir_vec[5][1] - dir_vec[5][0] * dir_vec[4][1]) * i_vol;
// grain B: reciprocal_vectors 1
reciprocal_vectors[1][1][0] = (dir_vec[5][1] * dir_vec[3][2] - dir_vec[3][1] * dir_vec[5][2]) * i_vol;
reciprocal_vectors[1][1][1] = (dir_vec[5][2] * dir_vec[3][0] - dir_vec[3][2] * dir_vec[5][0]) * i_vol;
reciprocal_vectors[1][1][2] = (dir_vec[5][0] * dir_vec[3][1] - dir_vec[3][0] * dir_vec[5][1]) * i_vol;
// grain B: reciprocal_vectors 2
reciprocal_vectors[1][2][0] = (dir_vec[3][1] * dir_vec[4][2] - dir_vec[4][1] * dir_vec[3][2]) * i_vol;
reciprocal_vectors[1][2][1] = (dir_vec[3][2] * dir_vec[4][0] - dir_vec[4][2] * dir_vec[3][0]) * i_vol;
reciprocal_vectors[1][2][2] = (dir_vec[3][0] * dir_vec[4][1] - dir_vec[4][0] * dir_vec[3][1]) * i_vol;
}
/* ----------------------------------------------------------------------
normalization factor
------------------------------------------------------------------------- */
int FixOrientECO::get_norm() {
// set up local variables
double delta[3]; // relative position
double squared_distance; // squared distance of atoms i and j
double weight; // weight function for atoms i and j
double wsum = 0.0; // sum of all weight functions
double scalar_product; // scalar product
double reesum[3] = {0.0, 0.0, 0.0}; // sum of real part
double imesum[3] = {0.0, 0.0, 0.0}; // sum of imaginary part
int max_co = 4; // will produce wrong results for rcut > 3 * lattice constant
int neigh = 0; // count number of neighbors used
// loop over ideal lattice positions
int i, k, idx[3];
for (idx[0] = -max_co; idx[0] <= max_co; ++idx[0]) {
for (idx[1] = -max_co; idx[1] <= max_co; ++idx[1]) {
for (idx[2] = -max_co; idx[2] <= max_co; ++idx[2]) {
// distance of atoms
for (i = 0; i < 3; ++i) {
delta[i] = dir_vec[0][i] * idx[0] + dir_vec[1][i] * idx[1] + dir_vec[2][i] * idx[2];
}
squared_distance = delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2];
// check if atom is within cutoff region
if ((squared_distance != 0.0) and (squared_distance < squared_cutoff)) {
++neigh;
squared_distance *= inv_squared_cutoff;
// weight
weight = squared_distance * (squared_distance - 2.0) + 1.0;
wsum += weight;
// three reciprocal directions
for (k = 0; k < 3; ++k) {
scalar_product = reciprocal_vectors[1][k][0] * delta[0] + reciprocal_vectors[1][k][1] * delta[1] + reciprocal_vectors[1][k][2] * delta[2];
reesum[k] += weight * cos(scalar_product);
imesum[k] -= weight * sin(scalar_product);
}
}
}
}
}
// compute normalization
norm_fac = 3.0 * wsum * wsum;
for (k = 0; k < 3; ++k) {
norm_fac -= reesum[k] * reesum[k] + imesum[k] * imesum[k];
}
return neigh;
}