Files
lammps/src/ELECTRODE/slab_dipole.cpp
2022-12-12 01:07:46 -05:00

149 lines
5.3 KiB
C++

/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
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: Ludwig Ahrens-Iwers (TUHH), Shern Tee (UQ), Robert Meißner (TUHH)
------------------------------------------------------------------------- */
#include "slab_dipole.h"
#include "atom.h"
#include "domain.h"
#include "force.h"
#include "kspace.h"
#include "math_const.h"
#include <cmath>
using namespace LAMMPS_NS;
using namespace MathConst;
#define SMALL 0.00001
/* ----------------------------------------------------------------------
Slab-geometry correction term to dampen inter-slab interactions between
periodically repeating slabs. Yields good approximation to 2D Ewald if
adequate empty space is left between repeating slabs (J. Chem. Phys.
111, 3155). Slabs defined here to be parallel to the xy plane. Also
extended to non-neutral systems (J. Chem. Phys. 131, 094107).
-------------------------------------------------------------------------
*/
SlabDipole::SlabDipole(LAMMPS *lmp) : BoundaryCorrection(lmp){};
void SlabDipole::compute_corr(double qsum, int eflag_atom, int eflag_global, double &energy,
double *eatom)
{
// compute local contribution to global dipole moment
double const volume = get_volume();
double *q = atom->q;
double **x = atom->x;
double zprd_slab = domain->zprd * force->kspace->slab_volfactor;
int nlocal = atom->nlocal;
double dipole = 0.0;
for (int i = 0; i < nlocal; i++) dipole += q[i] * x[i][2];
// sum local contributions to get global dipole moment
double dipole_all;
MPI_Allreduce(&dipole, &dipole_all, 1, MPI_DOUBLE, MPI_SUM, world);
// need to make non-neutral systems and/or
// per-atom energy translationally invariant
double dipole_r2 = 0.0;
if (eflag_atom || fabs(qsum) > SMALL) {
for (int i = 0; i < nlocal; i++) dipole_r2 += q[i] * x[i][2] * x[i][2];
// sum local contributions
double tmp;
MPI_Allreduce(&dipole_r2, &tmp, 1, MPI_DOUBLE, MPI_SUM, world);
dipole_r2 = tmp;
}
// compute corrections
double const e_slabcorr = MY_2PI *
(dipole_all * dipole_all - qsum * dipole_r2 - qsum * qsum * zprd_slab * zprd_slab / 12.0) /
volume;
double const scale = 1.0;
double const qscale = force->qqrd2e * scale;
if (eflag_global) energy += qscale * e_slabcorr;
// per-atom energy
if (eflag_atom) {
double efact = qscale * MY_2PI / volume;
for (int i = 0; i < nlocal; i++)
eatom[i] += efact * q[i] *
(x[i][2] * dipole_all - 0.5 * (dipole_r2 + qsum * x[i][2] * x[i][2]) -
qsum * zprd_slab * zprd_slab / 12.0);
}
// add on force corrections
double const ffact = qscale * (-4.0 * MY_PI / volume);
double **f = atom->f;
for (int i = 0; i < nlocal; i++) f[i][2] += ffact * q[i] * (dipole_all - qsum * x[i][2]);
}
void SlabDipole::vector_corr(double *vec, int sensor_grpbit, int source_grpbit, bool invert_source)
{
double const volume = get_volume();
int const nlocal = atom->nlocal;
double **x = atom->x;
double *q = atom->q;
int *mask = atom->mask;
double dipole = 0.;
for (int i = 0; i < nlocal; i++) {
if (!!(mask[i] & source_grpbit) != invert_source) dipole += q[i] * x[i][2];
}
MPI_Allreduce(MPI_IN_PLACE, &dipole, 1, MPI_DOUBLE, MPI_SUM, world);
dipole *= 4.0 * MY_PI / volume;
for (int i = 0; i < nlocal; i++) {
if (mask[i] & sensor_grpbit) vec[i] += x[i][2] * dipole;
}
}
void SlabDipole::matrix_corr(bigint *imat, double **matrix)
{
double const volume = get_volume();
int nlocal = atom->nlocal;
double **x = atom->x;
// how many local and total group atoms?
int ngrouplocal = 0;
for (int i = 0; i < nlocal; i++)
if (imat[i] > -1) ngrouplocal++;
bigint ngroup = 0;
MPI_Allreduce(&ngrouplocal, &ngroup, 1, MPI_INT, MPI_SUM, world);
std::vector<double> nprd_local = std::vector<double>(ngrouplocal);
for (int i = 0, n = 0; i < nlocal; i++) {
if (imat[i] < 0) continue;
nprd_local[n++] = x[i][2];
}
// gather subsets nprd positions
std::vector<int> recvcounts = gather_recvcounts(ngrouplocal);
std::vector<int> displs = gather_displs(recvcounts);
std::vector<double> nprd_all = std::vector<double>(ngroup);
MPI_Allgatherv(&nprd_local.front(), ngrouplocal, MPI_DOUBLE, &nprd_all.front(),
&recvcounts.front(), &displs.front(), MPI_DOUBLE, world);
std::vector<bigint> jmat = gather_jmat(imat);
const double prefac = MY_4PI / volume;
for (int i = 0; i < nlocal; i++) {
if (imat[i] < 0) continue;
for (bigint j = 0; j < ngroup; j++) {
if (jmat[j] > imat[i]) continue; // matrix is symmetric
double aij = prefac * x[i][2] * nprd_all[j];
matrix[imat[i]][jmat[j]] += aij;
if (imat[i] != jmat[j]) matrix[jmat[j]][imat[i]] += aij;
}
}
}