/* ---------------------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator https://lammps.sandia.gov/, Sandia National Laboratories Steve Plimpton, sjplimp@sandia.gov 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: Yair Augusto Gutiérrez Fosado Davide Michieletto ------------------------------------------------------------------------- */ #include "fix_topo2.h" //The atom class provides access to various atom properties, including the atom type, molecular ID, position, velocity, and force #include "atom.h" //Came by default in fix_swap_atom #include <cmath> #include <cctype> #include <cfloat> #include <cstring> #include "atom.h" #include "update.h" #include "modify.h" #include "fix.h" #include "comm.h" #include "compute.h" #include "group.h" #include "domain.h" #include "region.h" #include "random_park.h" #include "force.h" #include "pair.h" #include "bond.h" #include "angle.h" #include "dihedral.h" #include "improper.h" #include "kspace.h" #include "memory.h" #include "error.h" #include "neighbor.h" //Davide include them (with more) #include "math.h" #include "stdlib.h" #include "string.h" #include "neigh_list.h" #include "neigh_request.h" #include "random_mars.h" #include "citeme.h" #include <iostream> #include<stdlib.h> #include <time.h> /* Important for random number generator */ #include <sstream> // std::stringstream #include<fstream> //ofstream #include "input.h" #include "variable.h" using namespace std; using namespace LAMMPS_NS; using namespace FixConst; static const char cite_fix_topo2[] = "Non-equilibrium Topo2 Strand Crossing:\n\n" "@Article{Michieletto2018,\n" " author = {Michieletto et al},\n" " title = {xx},\n" " journal = {xx},\n" " year = 2018,\n" " volume = xx,\n" " pages = {xx}\n" "}\n\n"; /* ---------------------------------------------------------------------- */ //Constructor: The name of the class is FixTopo2 and the fix-style-name to //be used in the lammps script is fix_topo2. FixTopo2::FixTopo2(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg), alist(nullptr), list(nullptr), random_equal(nullptr), random_unequal(nullptr), random(nullptr) { //Cite if (lmp->citeme) lmp->citeme->add(cite_fix_topo2); //Check that the atom_style is molecular if (atom->molecular != 1) error->all(FLERR,"Cannot use fix_topo2 with non-molecular systems"); //Number of arguments for the fix. The first three arguments are parsed by Fix base class constructor. //The rest are specific to this fix. From 4 to 8 are mandatory for all cases. //4.- flagtopo: Static or randjump or jump2maxdens or jum2marcurv //5.- ntopo: the number of topo2 in the system. //6.- ltopo: the length of each topo2 in the system. //7.- topotype: the topoII particle type. //8.- seed: random seed //If Static we need one more argument //9.-tsteptopo: at which timestep we want to introduce the topo2 //If randjump or jump2maxdens or jum2marcurv, we need three arguments //9.- nevery: how often the fix is called. //10.- prob: probability that the topo2 changes position each nevery timesteps. //11.- intertype: intermediate type assigned to atoms from a section where topo 2 is jumping from. //commands use in fix_bond_swap //force_reneighbor = -1; next_reneighbor = -1; //next_reneighbor = 1; vector_flag = 1; size_vector = 2; global_freq = 1; extvector = 0; //Required arguments: common to all cases flagtopo=0; if (strcmp(arg[3],"static") == 0) {flagtopo=1;} else if (strcmp(arg[3],"randjump") == 0) {flagtopo=2;} else if (strcmp(arg[3],"jump2maxdens") == 0) {flagtopo=3;} else if (strcmp(arg[3],"jump2maxcurv") == 0) {flagtopo=4;} if( flagtopo==0 ){error->all(FLERR,"Illegal fix topo2 command");} //Check that the number of arguments is the correct one if( flagtopo==1){ if(narg!=9){error->all(FLERR,"Illegal fix topo2 command, a different number of arguments is required for static");} } if( flagtopo==2){ if(narg!=11){error->all(FLERR,"Illegal fix topo2 command, a different number of arguments is required for randjump");} } if( flagtopo==3){ if(narg!=11){error->all(FLERR,"Illegal fix topo2 command, a different number of arguments is required for jump2maxdens");} } if( flagtopo==4){ if(narg!=11){error->all(FLERR,"Illegal fix topo2 command, a different number of arguments is required for jump2maxcurv");} } ntopo = utils::inumeric(FLERR,arg[4],false,lmp); if (ntopo < 0 || ntopo > atom->natoms) error->all(FLERR,"Illegal fix topo2 command: in ntopo"); ltopo = utils::inumeric(FLERR,arg[5],false,lmp); int length = ntopo*ltopo; if (ltopo < 0 || length > atom->natoms) error->all(FLERR,"Illegal fix topo2 command: in ltopo"); topotype = utils::inumeric(FLERR,arg[6],false,lmp); int ilo,ihi,jlo,jhi; utils::bounds(FLERR,arg[6],1,atom->ntypes,ilo,ihi,error); seed = utils::inumeric(FLERR,arg[7],false,lmp); if (seed <= 0) error->all(FLERR,"Illegal fix topo2 command"); //In case static, we have to set the timestep at which we want to introduce the topo2 if(flagtopo==1){ tsteptopo = utils::inumeric(FLERR,arg[8],false,lmp); if (tsteptopo < 0) error->all(FLERR,"Illegal fix topo2 command"); if (tsteptopo < update->beginstep) error->all(FLERR,"Illegal fix topo2 command"); } //In case of the last three cases additional arguments needed: if(flagtopo>=2){ nevery = utils::inumeric(FLERR,arg[8],false,lmp); if (nevery <= 0) error->all(FLERR,"Illegal fix topo2 command"); prob = utils::numeric(FLERR,arg[9],false,lmp); if (prob<0.0 || prob>1.0) error->all(FLERR,"Illegal fix topo2 command"); intertype = utils::inumeric(FLERR,arg[10],false,lmp); utils::bounds(FLERR,arg[10],1,atom->ntypes,ilo,ihi,error); } // random number generator, same for all procs random_equal = new RanPark(lmp,seed); // random number generator, not the same for all procs random_unequal = new RanPark(lmp,seed); // initialize Marsaglia RNG with processor-unique seed random = new RanMars(lmp,seed + comm->me); //To get a different random number every time the program is executed srand(time(NULL)*seed); //Decleare selection list select = nullptr; beads = nullptr; //Declare atom list alist = nullptr; } /****************/ /* Destructor */ /****************/ FixTopo2::~FixTopo2() { delete random_equal; delete random_unequal; delete random; memory->destroy(select); memory->destroy(beads); memory->destroy(alist); } /**************/ /* Set mask */ /**************/ //It determines the stage in the timestep at which the fix will be executed. //The post integrate method is set for operations that need to happen immediately after updates by for example fix nve which performs the start-of-timestep velocity-Verlet integration operations to update velocities by a half-step, and coordinates by a full step. Only a few fixes use this, e.g. to reflect particles off box boundaries. int FixTopo2::setmask() { int mask = 0; mask |= POST_INTEGRATE; return mask; } /***********************/ /* The init() method */ /***********************/ //This is called at the beginning of each run, simply sets some internal flags void FixTopo2::init() { // require an atom style with molecule IDs if (atom->molecule == NULL) error->all(FLERR, "Must use atom style with molecule IDs with fix topo2"); // pair and bonds must be defined if (force->pair == NULL || force->bond == NULL) error->all(FLERR,"Fix topo2 requires pair and bond styles"); // angles must be defined if (force->angle == NULL && atom->nangles > 0 && comm->me == 0) error->warning(FLERR,"Fix topo2 will ignore defined angles"); if (force->pair->single_enable == 0) error->all(FLERR,"Pair style does not support fix topo2"); // no dihedral or improper potentials allowed if (force->dihedral || force->improper) error->all(FLERR,"Fix topo2 cannot use dihedral or improper styles"); // special bonds must be 0 1 1 if (force->special_lj[1] != 0.0 || force->special_lj[2] != 1.0 || force->special_lj[3] != 1.0) error->all(FLERR,"Fix topo2 requires special_bonds = 0,1,1"); /* // need a full neighbor list, built every Nevery steps 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; //neighbor->requests[irequest]->occasional = 1; */ } //Then, an init_list() method needs to be introduced to get hold of the neighbor list pointer, with a placeholder named list //void FixTopo2::init_list(int /*id*/, NeighList *ptr) //{ // list = ptr; //} /************************************************/ /* The action of this fix is implemented here */ /************************************************/ void FixTopo2::post_integrate() { //Print flag (1) yes print, (0) no int printflag=1; stringstream writeFile; ofstream write; //Rankid int myrank; MPI_Comm_rank(MPI_COMM_WORLD, &myrank); //Static: TopoII chooses a random position and it stays there until the end of the simulation if(flagtopo==1){ if(tsteptopo != update->ntimestep ) return; //This is to print to a file with the selected ramdom id (for static). if(printflag==1 && myrank==0){ writeFile <<"static_fromLAMMPS.dat"; write.open(writeFile.str().c_str(), std::ios_base::app); } //Selects a random atom id int rng = randomid(); if(printflag==1 && myrank==0){write << update->ntimestep << " " << rng << " " << topotype << endl;} //Change the bead-type of the particle with the previous id (from 1 to topotype) //and also to the (ltopo-1) particles that are behind. placetopo(rng); } //Random jump: with a certain frequency (nevery) the topoII can jump to a random position. //The decision of jumping is done with a certain probability. if(flagtopo==2){ if (update->ntimestep % nevery) return; //This is to print to a file with the selected ramdom id (for randjump) if(printflag==1 && myrank==0){ writeFile <<"randjump_fromLAMMPS.dat"; write.open(writeFile.str().c_str(), std::ios_base::app); } //The first time the programm is called it introduces a topoII with probability=1. if (update->ntimestep == nevery){ int rng = randomid(); if(printflag==1 && myrank==0){write << update->ntimestep << " " << rng << " " << topotype << endl;} placetopo(rng); } //The next times it is called if (update->ntimestep > nevery){ //Probability of moving double pmv = pmove(); if(pmv<=prob){ int rng = randomid(); if(printflag==1 && myrank==0){write << update->ntimestep << " " << rng << " " << topotype << endl;} changetype(); placetopo(rng); } } } //Jump to maximum density: with a certain frequency (nevery) the topoII can jump to the position of maximum bead density. //The decision of jumping is done with a certain probability. if(flagtopo==3){ if (update->ntimestep % nevery) return; //This is to print to a file with the bead-id with maximum density if(printflag==1 && myrank==0){ writeFile <<"jump2maxdens_fromLAMMPS.dat"; write.open(writeFile.str().c_str(), std::ios_base::app); } //Window size (in beads) within which it is reasonable to accommodate a knot. Smaller values will not be informative int w=50; //The first time the programm is called it introduces a topoII with probability=1. if (update->ntimestep == nevery){ int idmd = maxdensid(w); if(printflag==1 && myrank==0){write << update->ntimestep << " " << idmd << " " << topotype << endl;} placetopo(idmd); } //The next times it is called if (update->ntimestep > nevery){ //Probability of moving double pmv = pmove(); if(pmv<=prob){ int idmd = maxdensid(w); if(printflag==1 && myrank==0){write << update->ntimestep << " " << idmd << " " << topotype << endl;} changetype(); placetopo(idmd); } } } //Jump to maximum curvature: with a certain frequency (nevery) the topoII can jump to the position of maximum curvature. //The decision of jumping is done with a certain probability. if(flagtopo==4){ if (update->ntimestep % nevery) return; //This is to print to a file with the bead-id with maximum curvature if(printflag==1 && myrank==0){ writeFile <<"jump2maxcurv_fromLAMMPS.dat"; write.open(writeFile.str().c_str(), std::ios_base::app); } //Window size (in beads) within which the local curvature will be computed and integrated int w=50; //The first time the programm is called it introduces a topoII with probability=1. if (update->ntimestep == nevery){ int idmc = maxcurvid(w); if(printflag==1 && myrank==0){write << update->ntimestep << " " << idmc << " " << topotype << endl;} placetopo(idmc); } //The next times it is called if (update->ntimestep > nevery){ //Probability of moving double pmv = pmove(); if(pmv<=prob){ int idmc = maxcurvid(w); if(printflag==1 && myrank==0){write << update->ntimestep << " " << idmc << " " << topotype << endl;} changetype(); placetopo(idmc); } } } next_reneighbor = update->ntimestep; } /* ----------------------- */ /* Choose randomly atom id */ /* ----------------------- */ int FixTopo2::randomid() { //The total number of atoms in the system bigint natoms = atom->natoms; //Choose Randomly a bead [1, natoms] int rng; rng = 1 + (rand() % natoms); return rng; } /* -----------------------------------------------------*/ /* Find the bead with maximum density (if flagtopo==3) */ /* -----------------------------------------------------*/ int FixTopo2::maxdensid(int w) { /* Gather data from all processors into one global array */ /***********************************************************/ //We will use MPI_Gatherv for this purpose, so here is a description: //int MPI_Gatherv(const void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, const int *recvcounts, const int *displs, MPI_Datatype recvtype, int root, MPI_Comm comm) //sendbuf = **x: starting address of send buffer (choice) //sendcount = nlocal: number of elements in send buffer (integer) //sendtype = MPI_DOUBLE: data type of send buffer elements (handle) //recvbuf = xpos: address of receive buffer (choice, significant only at root) //recvcounts = nlarray[mysize]: integer array (of length group size) containing the number of elements that are received from each process (significant only at root) //displs = disp[mysize]: integer array (of length group size). Entry i specifies the displacement relative to recvbuf at which to place the incoming data from process i //recvtype = MPI_DOUBLE: data type of recv buffer elements (significant only at root) (handle) //root = myrank==0: rank of receiving process (integer) //comm: communicator (handle) //Declaration of arrays used in this section int *nlarray = nullptr; int *disp = nullptr; int *indice = nullptr; double *xpos = nullptr; double *ypos = nullptr; double *zpos = nullptr; double **globalpos = nullptr; //Number of ranks used int mysize; MPI_Comm_size(MPI_COMM_WORLD, &mysize); //Rankid int myrank; MPI_Comm_rank(MPI_COMM_WORLD, &myrank); bigint natoms = atom->natoms; int nlocal = atom->nlocal; int nghost = atom->nghost; int m = nlocal+nghost; //Define in rank0 an array which entries are the number (nlocal), of atoms in each processor if (myrank == 0) { memory->destroy(nlarray); memory->create(nlarray,mysize, "FixTopo2::maxdensid()"); } MPI_Gather(&nlocal, 1, MPI_INT, nlarray, 1, MPI_INT, 0, MPI_COMM_WORLD); /* //Check fprintf(screen,"mysize=%d, myrank=%d, nlocal=%d \n",mysize,myrank,nlocal); if (myrank == 0) { for(int i=0; i<mysize; i++){ fprintf(screen,"i=%d, nlarray[i]=%d \n",i, nlarray[i]); } } */ int totlen = 0; //Space between two consecutive entries int space =0; //Define in rank0 an array which contains the displacement between entries, when using MPI_Gatherv. if (myrank == 0) { memory->destroy(disp); memory->create(disp,mysize, "FixTopo2::maxdensid()"); disp[0]=0; totlen += nlarray[0]; for (int i=1; i<mysize; i++) { totlen += nlarray[i]; disp[i] = disp[i-1] + nlarray[i-1] + space; } // allocate memory for atom with positions memory->destroy(xpos); memory->destroy(ypos); memory->destroy(zpos); memory->destroy(indice); memory->create(xpos,natoms,"FixTopo2::maxdensid()"); memory->create(ypos,natoms,"FixTopo2::maxdensid()"); memory->create(zpos,natoms,"FixTopo2::maxdensid()"); memory->create(indice,natoms,"FixTopo2::maxdensid()"); } //We are going to gather the global indices that come from local in each rank tagint *tag = atom->tag; //Also we are going to gather the x,y,z position from each rank double **x = atom->x; double xtemp[nlocal]; double ytemp[nlocal]; double ztemp[nlocal]; for(int l=0; l<nlocal; l++) { xtemp[l]=x[l][0]; ytemp[l]=x[l][1]; ztemp[l]=x[l][2]; } MPI_Gatherv(tag, nlocal, MPI_INT, indice, nlarray, disp, MPI_INT, 0, MPI_COMM_WORLD); MPI_Gatherv(xtemp, nlocal, MPI_DOUBLE, xpos, nlarray, disp, MPI_DOUBLE, 0, MPI_COMM_WORLD); MPI_Gatherv(ytemp, nlocal, MPI_DOUBLE, ypos, nlarray, disp, MPI_DOUBLE, 0, MPI_COMM_WORLD); MPI_Gatherv(ztemp, nlocal, MPI_DOUBLE, zpos, nlarray, disp, MPI_DOUBLE, 0, MPI_COMM_WORLD); if (myrank == 0) { //Initialize array to store all the atom positions: memory->destroy(globalpos); memory->create(globalpos,natoms,3, "FixTopo2::maxdensid()"); for(int i=0; i<natoms; i++){ int g = indice[i]; globalpos[g-1][0] = xpos[i]; globalpos[g-1][1] = ypos[i]; globalpos[g-1][2] = zpos[i]; //fprintf(screen,"i=%d, x=%f, y=%f, z=%f \n", indice[i], xpos[i], ypos[i], zpos[i]); } //for(int i=0; i<natoms; i++){ // fprintf(screen,"i=%d, x=%f, y=%f, z=%f \n", i+1, globalpos[i][0], globalpos[i][1], globalpos[i][2]); //} } /* Local density is computed within a sphere of radius R */ /*********************************************************/ int *idmdarray = nullptr; int idmaxdens; if (myrank == 0) { memory->destroy(idmdarray); memory->create(idmdarray,mysize,"FixTopo2::maxdensid()"); double R=pow(w,0.588); double Rsqd=R*R; int maxdens=0; int md; for(int g=0; g<natoms;g++){ int localdens=0; double xtmp = globalpos[g][0]; double ytmp = globalpos[g][1]; double ztmp = globalpos[g][2]; for (int j=0;j<natoms;j++){ // calculate distance-squared between g,j atom-types double delx = xtmp - globalpos[j][0]; double dely = ytmp - globalpos[j][1]; double delz = ztmp - globalpos[j][2]; double rsq = delx*delx + dely*dely + delz*delz; if (rsq < Rsqd) { localdens += 1;} } if(localdens>maxdens){ maxdens=fabs(localdens); md=g+1; } } //We store a copy of the particle id with maximum density into this array //that is only defined in rank0 for(int s=0; s<mysize; s++){ idmdarray[s] = md; } } //Send the id with maximum density to all ranks MPI_Scatter(idmdarray, 1, MPI_INT, &idmaxdens, 1, MPI_INT, 0, MPI_COMM_WORLD); return idmaxdens; } /* -----------------------------------------------------*/ /* Find the bead with maximum curvature (if flagtopo==4) */ /* -----------------------------------------------------*/ int FixTopo2::maxcurvid(int w) { /* Gather data from all processors into one global array */ /***********************************************************/ //We will use MPI_Gatherv for this purpose, so here is a description: //int MPI_Gatherv(const void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, const int *recvcounts, const int *displs, MPI_Datatype recvtype, int root, MPI_Comm comm) //sendbuf = **x: starting address of send buffer (choice) //sendcount = nlocal: number of elements in send buffer (integer) //sendtype = MPI_DOUBLE: data type of send buffer elements (handle) //recvbuf = xpos: address of receive buffer (choice, significant only at root) //recvcounts = nlarray[mysize]: integer array (of length group size) containing the number of elements that are received from each process (significant only at root) //displs = disp[mysize]: integer array (of length group size). Entry i specifies the displacement relative to recvbuf at which to place the incoming data from process i //recvtype = MPI_DOUBLE: data type of recv buffer elements (significant only at root) (handle) //root = myrank==0: rank of receiving process (integer) //comm: communicator (handle) //Declaration of arrays used in this section int *nlarray = nullptr; int *disp = nullptr; int *indice = nullptr; double *xpos = nullptr; double *ypos = nullptr; double *zpos = nullptr; double **globalpos = nullptr; //Number of ranks used int mysize; MPI_Comm_size(MPI_COMM_WORLD, &mysize); //Rankid int myrank; MPI_Comm_rank(MPI_COMM_WORLD, &myrank); bigint natoms = atom->natoms; int nlocal = atom->nlocal; int nghost = atom->nghost; int m = nlocal+nghost; //Define in rank0 an array which entries are the number (nlocal), of atoms in each processor if (myrank == 0) { memory->destroy(nlarray); memory->create(nlarray,mysize, "FixTopo2::maxcurvid()"); } MPI_Gather(&nlocal, 1, MPI_INT, nlarray, 1, MPI_INT, 0, MPI_COMM_WORLD); /* //Check fprintf(screen,"mysize=%d, myrank=%d, nlocal=%d \n",mysize,myrank,nlocal); if (myrank == 0) { for(int i=0; i<mysize; i++){ fprintf(screen,"i=%d, nlarray[i]=%d \n",i, nlarray[i]); } } */ int totlen = 0; //Space between two consecutive entries int space =0; //Define in rank0 an array which contains the displacement between entries, when using MPI_Gatherv. if (myrank == 0) { memory->destroy(disp); memory->create(disp,mysize, "FixTopo2::maxcurvid()"); disp[0]=0; totlen += nlarray[0]; for (int i=1; i<mysize; i++) { totlen += nlarray[i]; disp[i] = disp[i-1] + nlarray[i-1] + space; } // allocate memory for atom with positions memory->destroy(xpos); memory->destroy(ypos); memory->destroy(zpos); memory->destroy(indice); memory->create(xpos,natoms,"FixTopo2::maxcurvid()"); memory->create(ypos,natoms,"FixTopo2::maxcurvid()"); memory->create(zpos,natoms,"FixTopo2::maxcurvid()"); memory->create(indice,natoms,"FixTopo2::maxcurvid()"); } //We are going to gather the global indices that come from local in each rank tagint *tag = atom->tag; //Also we are going to gather the x,y,z position from each rank the unwrapped coordinates double **x = atom->x; double xtemp[nlocal]; double ytemp[nlocal]; double ztemp[nlocal]; imageint *image = atom->image; double unwrap[3]; for(int l=0; l<nlocal; l++) { domain->unmap(x[l],image[l],unwrap); xtemp[l]=unwrap[0]; ytemp[l]=unwrap[1]; ztemp[l]=unwrap[2]; } MPI_Gatherv(tag, nlocal, MPI_INT, indice, nlarray, disp, MPI_INT, 0, MPI_COMM_WORLD); MPI_Gatherv(xtemp, nlocal, MPI_DOUBLE, xpos, nlarray, disp, MPI_DOUBLE, 0, MPI_COMM_WORLD); MPI_Gatherv(ytemp, nlocal, MPI_DOUBLE, ypos, nlarray, disp, MPI_DOUBLE, 0, MPI_COMM_WORLD); MPI_Gatherv(ztemp, nlocal, MPI_DOUBLE, zpos, nlarray, disp, MPI_DOUBLE, 0, MPI_COMM_WORLD); if (myrank == 0) { //Initialize array to store all the atom positions: memory->destroy(globalpos); memory->create(globalpos,natoms,3, "FixTopo2::maxcurvid()"); for(int i=0; i<natoms; i++){ int g = indice[i]; globalpos[g-1][0] = xpos[i]; globalpos[g-1][1] = ypos[i]; globalpos[g-1][2] = zpos[i]; //fprintf(screen,"i=%d, x=%f, y=%f, z=%f \n", indice[i], xpos[i], ypos[i], zpos[i]); } //for(int i=0; i<natoms; i++){ // fprintf(screen,"i=%d, x=%f, y=%f, z=%f \n", i+1, globalpos[i][0], globalpos[i][1], globalpos[i][2]); //} } /* Local curvature computed within a window */ /********************************************/ int *idmcarray = nullptr; int idmaxcurv; if (myrank == 0) { memory->destroy(idmcarray); memory->create(idmcarray,mysize,"FixTopo2::maxcurvid()"); double maxcurv=0; double localcurv[natoms]; int mc; for(int g=0; g<natoms;g++){ localcurv[g]=0; //loop over window centered at g for (int j=0; j<=w;j++) { double tang1[3]; double tang2[3]; //Three consecutive beads int b1=int(g+j-w/2.)-1; int b2=int(g+j-w/2.); int b3=int(g+j-w/2.)+1; if(b1<0)b1=natoms+b1; // [0:natoms-1] if(b2<0)b2=natoms+b2; // [0:natoms-1] if(b3<0)b3=natoms+b3; // [0:natoms-1] if(b1>=natoms)b1=b1%natoms; // [0:natoms-1] if(b2>=natoms)b2=b2%natoms; // [0:natoms-1] if(b3>=natoms)b3=b3%natoms; // [0:natoms-1] //Two consecutive tangents for(int d=0; d<3; d++){ tang1[d] = globalpos[b2][d]-globalpos[b1][d]; tang2[d] = globalpos[b3][d]-globalpos[b2][d]; } double norm1=sqrt(tang1[0]*tang1[0]+tang1[1]*tang1[1]+tang1[2]*tang1[2]); double norm2=sqrt(tang2[0]*tang2[0]+tang2[1]*tang2[1]+tang2[2]*tang2[2]); double costheta=(tang1[0]*tang2[0]+tang1[1]*tang2[1]+tang1[2]*tang2[2])/(norm1*norm2); //integrate over window //divide by w to get the "average" localcurv[g]+=(1.0-costheta)*1.0/w; } if(localcurv[g]>maxcurv){ maxcurv=localcurv[g]; mc=g+1; } } //We store a copy of the particle id with maximum curvature into this array //that is only defined in rank0 for(int s=0; s<mysize; s++){ idmcarray[s] = mc; } } //Send the id with maximum density to all ranks MPI_Scatter(idmcarray, 1, MPI_INT, &idmaxcurv, 1, MPI_INT, 0, MPI_COMM_WORLD); return idmaxcurv; } /* ------------------ */ /* Moving probability */ /* ------------------ */ double FixTopo2::pmove() { //This is a number between 0 and 1. double pmv=rand()*1.0/RAND_MAX; return pmv; } /* ---------- */ /* Place topo */ /* ---------- */ void FixTopo2::placetopo(int id) { //The total number of atoms in the system bigint natoms = atom->natoms; //The number of owned atoms belonging to the core that executes this line int nlocal = atom->nlocal; //The number of ghost atoms int nghost = atom->nghost; //Both owned and ghost atoms int n = nlocal+nghost; delete [] select; select = new int[n]; //useful for converting from local to global tagint *tag = atom->tag; //Define the global id of the ltopo-consecutive beads that are going to change type. delete [] beads; beads = new int[ltopo]; for (int i=0;i<ltopo;i++){ int g=(id-ltopo+i); if(g<0)g+=natoms; beads[i]=g+1; //fprintf(screen,"Global id that will change type: %d \n",beads[i]); } //Firts select the local atoms that are going to change type according to the previously global selected ids for (int l = 0; l < nlocal; l++){ select[l] = 0; for (int i=0; i<ltopo; i++){ int g=beads[i]; if (tag[l] == g) { select[l] = 1; //fprintf(screen," l=%d, tag[l]=%d, g=%d \n",l,tag[l], g); } } } //Change the type of previously selected atoms for (int l = 0; l < nlocal; l++) { if (!select[l]) continue; atom->type[l] = topotype; } } /* ------------------------ */ /* Change type intermediate */ /* ------------------------ */ void FixTopo2::changetype() { //The number of owned atoms belonging to the core that executes this line int nlocal = atom->nlocal; //The number of ghost atoms int nghost = atom->nghost; //Both owned and ghost atoms int n = nlocal+nghost; delete [] select; select = new int[n]; //useful for converting from local to global tagint *tag = atom->tag; //Change the type of atoms from (topotype+1) to 1 for (int l = 0; l < nlocal; l++) { if(atom->type[l] == topotype+1) atom->type[l] = 1; } //Change the type of atoms from (topotype) to (intertype) for (int l = 0; l < nlocal; l++) { if(atom->type[l] == topotype) atom->type[l] = intertype; } } /***********************************************************************/ /* Needed to write a restart file that can continue with the simulation*/ /***********************************************************************/ void FixTopo2::write_restart(FILE *fp) { int n = 0; double list[4]; list[n++] = random_equal->state(); list[n++] = random_unequal->state(); list[n++] = ubuf(next_reneighbor).d; list[n++] = ubuf(update->ntimestep).d; if (comm->me == 0) { int size = n * sizeof(double); fwrite(&size,sizeof(int),1,fp); fwrite(list,sizeof(double),n,fp); } } /* ---------------------------------------------------------------------- use state info from restart file to restart the Fix ------------------------------------------------------------------------- */ void FixTopo2::restart(char *buf) { int n = 0; double *list = (double *) buf; seed = static_cast<int> (list[n++]); random_equal->reset(seed); seed = static_cast<int> (list[n++]); random_unequal->reset(seed); next_reneighbor = (bigint) ubuf(list[n++]).i; bigint ntimestep_restart = (bigint) ubuf(list[n++]).i; if (ntimestep_restart != update->ntimestep) error->all(FLERR,"Must not reset timestep when restarting fix topo2"); }