#include "fix_drude.h"
#include <mpi.h>
#include <cstring>
#include <map>
#include "atom.h"
#include "comm.h"
#include "modify.h"
#include "error.h"
#include "memory.h"
#include "molecule.h"
#include "atom_vec.h"
#include <set>
#include <vector>
using namespace LAMMPS_NS;
using namespace FixConst;
FixDrude::FixDrude(LAMMPS *lmp, int narg, char **arg) :
Fix(lmp, narg, arg)
{
if (narg != 3 + atom->ntypes) error->all(FLERR,"Illegal fix drude command");
comm_border = 1; special_alter_flag = 1;
create_attribute = 1;
is_reduced = false;
memory->create(drudetype, atom->ntypes+1, "fix_drude::drudetype");
for (int i=3; i<narg; i++) {
if (arg[i][0] == 'n' || arg[i][0] == 'N' || arg[i][0] == '0')
drudetype[i-2] = NOPOL_TYPE;
else if (arg[i][0] == 'c' || arg[i][0] == 'C' || arg[i][0] == '1')
drudetype[i-2] = CORE_TYPE;
else if (arg[i][0] == 'd' || arg[i][0] == 'D' || arg[i][0] == '2')
drudetype[i-2] = DRUDE_TYPE;
else
error->all(FLERR, "Illegal fix drude command");
}
drudeid = NULL;
grow_arrays(atom->nmax);
atom->add_callback(0);
atom->add_callback(1);
atom->add_callback(2);
build_drudeid();
rebuildflag = 0;
}
FixDrude::~FixDrude()
{
atom->delete_callback(id,2);
atom->delete_callback(id,1);
atom->delete_callback(id,0);
memory->destroy(drudetype);
memory->destroy(drudeid);
}
void FixDrude::init()
{
int count = 0;
for (int i = 0; i < modify->nfix; i++)
if (strcmp(modify->fix[i]->style,"drude") == 0) count++;
if (count > 1) error->all(FLERR,"More than one fix drude");
if (!rebuildflag) rebuild_special();
}
int FixDrude::setmask()
{
int mask = 0;
return mask;
}
void FixDrude::build_drudeid(){
int nlocal = atom->nlocal;
int *type = atom->type;
std::vector<tagint> drude_vec; std::vector<tagint> core_drude_vec;
partner_set = new std::set<tagint>[nlocal];
if (atom->molecular == 1)
{
for (int i=0; i<nlocal; i++){
if (drudetype[type[i]] == NOPOL_TYPE) continue;
drudeid[i] = 0;
for (int k=0; k<atom->num_bond[i]; k++){
core_drude_vec.push_back(atom->tag[i]);
core_drude_vec.push_back(atom->bond_atom[i][k]);
}
}
}
else
{
class Molecule **atommols;
atommols = atom->avec->onemols;
for (int i=0; i<nlocal; i++){
int imol = atom->molindex[i];
int iatom = atom->molatom[i];
tagint *batom = atommols[imol]->bond_atom[iatom];
tagint tagprev = atom->tag[i] - iatom - 1;
int nbonds = atommols[imol]->num_bond[iatom];
if (drudetype[type[i]] == NOPOL_TYPE) continue;
drudeid[i] = 0;
for (int k=0; k<nbonds; k++){
core_drude_vec.push_back(atom->tag[i]);
core_drude_vec.push_back(batom[k]+tagprev);
}
}
}
comm->ring(core_drude_vec.size(), sizeof(tagint),
(char *) core_drude_vec.data(),
4, ring_build_partner, NULL, (void *)this, 1);
for (int i=0; i<nlocal; i++){
if (drudetype[type[i]] == DRUDE_TYPE){
drude_vec.push_back(atom->tag[i]);
drudeid[i] = *partner_set[i].begin(); }
}
comm->ring(drude_vec.size(), sizeof(tagint),
(char *) drude_vec.data(),
3, ring_search_drudeid, NULL, (void *)this, 1);
delete [] partner_set;
}
void FixDrude::ring_search_drudeid(int size, char *cbuf, void *ptr){
FixDrude *fdptr = (FixDrude *) ptr;
Atom *atom = fdptr->atom;
int nlocal = atom->nlocal;
int *type = atom->type;
std::set<tagint> *partner_set = fdptr->partner_set;
tagint *drudeid = fdptr->drudeid;
int *drudetype = fdptr->drudetype;
tagint *first = (tagint *) cbuf;
tagint *last = first + size;
std::set<tagint> drude_set(first, last);
std::set<tagint>::iterator it;
for (int i=0; i<nlocal; i++) {
if (drudetype[type[i]] != CORE_TYPE || drudeid[i] > 0) continue;
for (it = partner_set[i].begin(); it != partner_set[i].end(); it++) { if (drude_set.count(*it) > 0){
drudeid[i] = *it;
break;
}
}
}
}
void FixDrude::ring_build_partner(int size, char *cbuf, void *ptr){
FixDrude *fdptr = (FixDrude *) ptr;
Atom *atom = fdptr->atom;
int nlocal = atom->nlocal;
std::set<tagint> *partner_set = fdptr->partner_set;
tagint *it = (tagint *) cbuf;
tagint *last = it + size;
while (it < last) {
int j = atom->map(*it);
if (j >= 0 && j < nlocal)
partner_set[j].insert(*(it+1));
j = atom->map(*(it+1));
if (j >= 0 && j < nlocal)
partner_set[j].insert(*it);
it += 2;
}
}
void FixDrude::grow_arrays(int nmax)
{
memory->grow(drudeid,nmax,"fix_drude:drudeid");
}
void FixDrude::copy_arrays(int i, int j, int )
{
drudeid[j] = drudeid[i];
}
int FixDrude::pack_exchange(int i, double *buf)
{
int m = 0;
buf[m++] = ubuf(drudeid[i]).d;
return m;
}
int FixDrude::unpack_exchange(int nlocal, double *buf)
{
int m = 0;
drudeid[nlocal] = (tagint) ubuf(buf[m++]).i;
return m;
}
int FixDrude::pack_border(int n, int *list, double *buf)
{
int m = 0;
for (int i=0; i<n; i++){
int j = list[i];
buf[m++] = ubuf(drudeid[j]).d;
}
return m;
}
int FixDrude::unpack_border(int n, int first, double *buf)
{
int m = 0;
for (int i=first; i<first+n; i++){
drudeid[i] = (tagint) ubuf(buf[m++]).i;
}
return m;
}
void FixDrude::rebuild_special(){
rebuildflag = 1;
int nlocal = atom->nlocal;
int **nspecial = atom->nspecial;
tagint **special = atom->special;
int *type = atom->type;
if (atom->molecular != 1)
return;
if (comm->me == 0) {
if (screen) fprintf(screen, "Rebuild special list taking Drude particles into account\n");
if (logfile) fprintf(logfile, "Rebuild special list taking Drude particles into account\n");
}
int nspecmax, nspecmax_old, nspecmax_loc;
nspecmax_loc = 0;
for (int i=0; i<nlocal; i++) {
if (nspecmax_loc < nspecial[i][2]) nspecmax_loc = nspecial[i][2];
}
MPI_Allreduce(&nspecmax_loc, &nspecmax_old, 1, MPI_INT, MPI_MAX, world);
if (comm->me == 0) {
if (screen) fprintf(screen, "Old max number of 1-2 to 1-4 neighbors: %d\n", nspecmax_old);
if (logfile) fprintf(logfile, "Old max number of 1-2 to 1-4 neighbors: %d\n", nspecmax_old);
}
std::vector<tagint> drude_vec, core_drude_vec, core_special_vec;
for (int i=0; i<nlocal; i++) {
if (drudetype[type[i]] == DRUDE_TYPE) {
drude_vec.push_back(atom->tag[i]);
} else if (drudetype[type[i]] == CORE_TYPE){
core_drude_vec.push_back(atom->tag[i]);
core_drude_vec.push_back(drudeid[i]);
}
}
comm->ring(drude_vec.size(), sizeof(tagint),
(char *) drude_vec.data(),
9, ring_remove_drude, NULL, (void *)this, 1);
comm->ring(core_drude_vec.size(), sizeof(tagint),
(char *) core_drude_vec.data(),
10, ring_add_drude, NULL, (void *)this, 1);
nspecmax_loc = 0;
for (int i=0; i<nlocal; i++) {
if (nspecmax_loc < nspecial[i][2]) nspecmax_loc = nspecial[i][2];
}
MPI_Allreduce(&nspecmax_loc, &nspecmax, 1, MPI_INT, MPI_MAX, world);
if (comm->me == 0) {
if (screen) fprintf(screen, "New max number of 1-2 to 1-4 neighbors: %d (+%d)\n", nspecmax, nspecmax - nspecmax_old);
if (logfile) fprintf(logfile, "New max number of 1-2 to 1-4 neighbors: %d (+%d)\n", nspecmax, nspecmax - nspecmax_old);
}
if (atom->maxspecial < nspecmax) {
char str[1024];
sprintf(str, "Not enough space in special: extra/special/per/atom should be at least %d", nspecmax - nspecmax_old);
error->all(FLERR, str);
}
for (int i=0; i<nlocal; i++) {
if (drudetype[type[i]] != CORE_TYPE) continue;
core_special_vec.push_back(atom->tag[i]);
core_special_vec.push_back((tagint) nspecial[i][0]);
core_special_vec.push_back((tagint) nspecial[i][1]);
core_special_vec.push_back((tagint) nspecial[i][2]);
for (int j=1; j<nspecial[i][2]; j++)
core_special_vec.push_back(special[i][j]);
}
comm->ring(core_special_vec.size(), sizeof(tagint),
(char *) core_special_vec.data(),
11, ring_copy_drude, NULL, (void *)this, 1);
}
void FixDrude::ring_remove_drude(int size, char *cbuf, void *ptr){
FixDrude *fdptr = (FixDrude *) ptr;
Atom *atom = fdptr->atom;
int nlocal = atom->nlocal;
int **nspecial = atom->nspecial;
tagint **special = atom->special;
int *type = atom->type;
tagint *first = (tagint *) cbuf;
tagint *last = first + size;
std::set<tagint> drude_set(first, last);
int *drudetype = fdptr->drudetype;
for (int i=0; i<nlocal; i++) {
if (drudetype[type[i]] == DRUDE_TYPE) continue;
for (int j=0; j<nspecial[i][2]; j++) {
if (drude_set.count(special[i][j]) > 0) { nspecial[i][2]--;
for (int k=j; k<nspecial[i][2]; k++)
special[i][k] = special[i][k+1];
if (j < nspecial[i][1]) {
nspecial[i][1]--;
if (j < nspecial[i][0]) nspecial[i][0]--;
}
j--;
}
}
}
}
void FixDrude::ring_add_drude(int size, char *cbuf, void *ptr){
FixDrude *fdptr = (FixDrude *) ptr;
Atom *atom = fdptr->atom;
int nlocal = atom->nlocal;
int **nspecial = atom->nspecial;
tagint **special = atom->special;
int *type = atom->type;
tagint *drudeid = fdptr->drudeid;
int *drudetype = fdptr->drudetype;
tagint *first = (tagint *) cbuf;
tagint *last = first + size;
std::map<tagint, tagint> core_drude_map;
tagint *it = first;
while (it < last) {
tagint core_tag = *it;
it++;
core_drude_map[core_tag] = *it;
it++;
}
for (int i=0; i<nlocal; i++) {
if (drudetype[type[i]] == DRUDE_TYPE) continue;
if (core_drude_map.count(atom->tag[i]) > 0) { for (int k=nspecial[i][2]-1; k>=0; k--)
special[i][k+1] = special[i][k];
special[i][0] = drudeid[i];
nspecial[i][0]++;
nspecial[i][1]++;
nspecial[i][2]++;
}
for (int j=0; j<nspecial[i][2]; j++) {
if (core_drude_map.count(special[i][j]) > 0) { for (int k=nspecial[i][2]-1; k>j; k--)
special[i][k+1] = special[i][k];
special[i][j+1] = core_drude_map[special[i][j]];
nspecial[i][2]++;
if (j < nspecial[i][1]) {
nspecial[i][1]++;
if (j < nspecial[i][0]) nspecial[i][0]++;
}
j++;
}
}
}
}
void FixDrude::ring_copy_drude(int size, char *cbuf, void *ptr){
FixDrude *fdptr = (FixDrude *) ptr;
Atom *atom = fdptr->atom;
int nlocal = atom->nlocal;
int **nspecial = atom->nspecial;
tagint **special = atom->special;
int *type = atom->type;
tagint *drudeid = fdptr->drudeid;
int *drudetype = fdptr->drudetype;
tagint *first = (tagint *) cbuf;
tagint *last = first + size;
std::map<tagint, tagint*> core_special_map;
tagint *it = first;
while (it < last) {
tagint core_tag = *it;
it++;
core_special_map[core_tag] = it;
it += 2;
it += (int) *it;
}
for (int i=0; i<nlocal; i++) {
if (drudetype[type[i]] != DRUDE_TYPE) continue;
if (core_special_map.count(drudeid[i]) > 0) { it = core_special_map[drudeid[i]];
nspecial[i][0] = (int) *it;
it++;
nspecial[i][1] = (int) *it;
it++;
nspecial[i][2] = (int) *it;
it++;
special[i][0] = drudeid[i];
for (int k=1; k<nspecial[i][2]; k++) {
special[i][k] = *it;
it++;
}
}
}
}
void FixDrude::set_arrays(int i){
if (drudetype[atom->type[i]] != NOPOL_TYPE){
if (atom->nspecial[i] ==0) error->all(FLERR, "Polarizable atoms cannot be inserted with special lists info from the molecule template");
drudeid[i] = atom->special[i][0]; } else {
drudeid[i] = 0;
}
}