/* ---------------------------------------------------------------------- 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 dir1ectory. ------------------------------------------------------------------------- */ #include "compute_stress_spherical.h" #include "atom.h" #include "citeme.h" #include "comm.h" #include "domain.h" #include "error.h" #include "force.h" #include "math_const.h" #include "math_special.h" #include "memory.h" #include "neigh_list.h" #include "neighbor.h" #include "pair.h" #include "update.h" #include using namespace LAMMPS_NS; using namespace MathConst; using MathSpecial::cube; using MathSpecial::square; static constexpr double SMALL = 1.0e-10; /*----------------------------------------------------------------------------------- Contributing author: Olav Galteland (Norwegian University of Science and Technology) olav.galteland@ntnu.no ------------------------------------------------------------------------------------*/ static const char cite_compute_stress_sphere[] = "compute stress/spherical: doi:10.48550/arXiv.2201.13060\n\n" "@article{galteland2022defining,\n" "title={Defining the Pressures of a Fluid in a Nanoporous, Heterogeneous Medium},\n" "author={Galteland, Olav and Rauter, Michael T and Varughese, Kevin K and Bedeaux, Dick and " " Kjelstrup, Signe},\n" "journal={arXiv preprint arXiv:2201.13060},\n" "year={2022}\n" "}\n\n"; /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ ComputeStressSpherical::ComputeStressSpherical(LAMMPS *lmp, int narg, char **arg) : Compute(lmp, narg, arg), dens(nullptr), pkrr(nullptr), pktt(nullptr), pkpp(nullptr), pcrr(nullptr), pctt(nullptr), pcpp(nullptr), tdens(nullptr), tpkrr(nullptr), tpktt(nullptr), tpkpp(nullptr), tpcrr(nullptr), tpctt(nullptr), tpcpp(nullptr), list(nullptr) { if (lmp->citeme) lmp->citeme->add(cite_compute_stress_sphere); if (narg != 8) error->all(FLERR, "Illegal compute stress/spherical command. Illegal number of arguments."); x0 = utils::numeric(FLERR, arg[3], false, lmp); y0 = utils::numeric(FLERR, arg[4], false, lmp); z0 = utils::numeric(FLERR, arg[5], false, lmp); bin_width = utils::numeric(FLERR, arg[6], false, lmp); Rmax = utils::numeric(FLERR, arg[7], false, lmp); nbins = (int) (Rmax / bin_width) + 1; double tmp_width = Rmax / nbins; if ((fabs(bin_width - tmp_width) > SMALL) && (comm->me == 0)) utils::logmesg(lmp, "Adjusting bin width for compute {} from {:.6f} to {:.6f}\n", style, bin_width, tmp_width); bin_width = tmp_width; if (bin_width <= 0.0) error->all(FLERR, "Illegal compute stress/spherical command. Bin width must be > 0"); array_flag = 1; vector_flag = 0; extarray = 0; size_array_cols = 8; // r, dens, pkrr, pktt, pkpp, pcrr, pctt, pcpp size_array_rows = nbins; memory->create(invV, nbins, "compute/stress/spherical:invV"); memory->create(dens, nbins, "compute/stress/spherical:dens"); memory->create(pkrr, nbins, "compute/stress/spherical:pkrr"); memory->create(pktt, nbins, "compute/stress/spherical:pktt"); memory->create(pkpp, nbins, "compute/stress/spherical:pkpp"); memory->create(pcrr, nbins, "compute/stress/spherical:pcrr"); memory->create(pctt, nbins, "compute/stress/spherical:pctt"); memory->create(pcpp, nbins, "compute/stress/spherical:pcpp"); memory->create(tdens, nbins, "compute/stress/spherical:tdens"); memory->create(tpkrr, nbins, "compute/stress/spherical:tpkrr"); memory->create(tpktt, nbins, "compute/stress/spherical:tpktt"); memory->create(tpkpp, nbins, "compute/stress/spherical:tpkpp"); memory->create(tpcrr, nbins, "compute/stress/spherical:tpcrr"); memory->create(tpctt, nbins, "compute/stress/spherical:tpctt"); memory->create(tpcpp, nbins, "compute/stress/spherical:tpcpp"); memory->create(array, size_array_rows, size_array_cols, "compute/stress/spherical:array"); } /* ---------------------------------------------------------------------- */ ComputeStressSpherical::~ComputeStressSpherical() { memory->destroy(invV); memory->destroy(dens); memory->destroy(pkrr); memory->destroy(pktt); memory->destroy(pkpp); memory->destroy(pcrr); memory->destroy(pctt); memory->destroy(pcpp); memory->destroy(tdens); memory->destroy(tpkrr); memory->destroy(tpktt); memory->destroy(tpkpp); memory->destroy(tpcrr); memory->destroy(tpctt); memory->destroy(tpcpp); memory->destroy(array); } /* ---------------------------------------------------------------------- */ void ComputeStressSpherical::init() { if (force->pair == nullptr) error->all(FLERR, "No pair style is defined for compute stress/spherical"); if (force->pair->single_enable == 0) error->all(FLERR, "Pair style does not support compute stress/spherical"); // Inverse volume of each spherical shell (bin) for (int bin = 0; bin < nbins; bin++) invV[bin] = 0.75 / (MY_PI * (cube((bin + 1) * bin_width) - cube(bin * bin_width))); neighbor->add_request(this, NeighConst::REQ_OCCASIONAL); } /* ---------------------------------------------------------------------- */ void ComputeStressSpherical::init_list(int /* id */, NeighList *ptr) { list = ptr; } /* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- count pairs and compute pair info on this proc only count pair once if newton_pair is off both atom I,J must be in group if flag is set, compute requested info about pair ------------------------------------------------------------------------- */ void ComputeStressSpherical::compute_array() { invoked_array = update->ntimestep; int bin; // Zero arrays for (int bin = 0; bin < nbins; bin++) { tdens[bin] = 0.0; tpkrr[bin] = 0.0; tpktt[bin] = 0.0; tpkpp[bin] = 0.0; tpcrr[bin] = 0.0; tpctt[bin] = 0.0; tpcpp[bin] = 0.0; } int i, j, ii, jj, inum, jnum, itype, jtype; tagint itag, jtag; double ri[3], xtmp, ytmp, ztmp, tmp; double rsq, fpair, factor_coul, factor_lj; int *ilist, *jlist, *numneigh, **firstneigh; double r, vr, vt, vp, theta; double **x = atom->x; double **v = atom->v; tagint *tag = atom->tag; int *type = atom->type; int *mask = atom->mask; int nlocal = atom->nlocal; double *special_coul = force->special_coul; double *special_lj = force->special_lj; int newton_pair = force->newton_pair; // invoke half neighbor list (will copy or build if necessary) neighbor->build_one(list); inum = list->inum; ilist = list->ilist; numneigh = list->numneigh; firstneigh = list->firstneigh; // calculate number density and kinetic contribution to pressure for (i = 0; i < nlocal; i++) { ri[0] = x[i][0] - x0; ri[1] = x[i][1] - y0; ri[2] = x[i][2] - z0; for (j = 0; j < 3; j++) { tmp = domain->boxhi[j] - domain->boxlo[j]; if (ri[j] > 0.5 * tmp) ri[j] -= tmp; else if (ri[j] < -0.5 * tmp) ri[j] += tmp; } r = sqrt(ri[0] * ri[0] + ri[1] * ri[1] + ri[2] * ri[2]); if (r >= Rmax) continue; bin = (int) (r / bin_width); // Avoiding division by zero if ((r != 0.0) && (ri[0] != 0.0)) { theta = acos(ri[2] / r); tdens[bin] += 1.0; vr = (ri[0] * v[i][0] + ri[1] * v[i][1] + ri[2] * v[i][2]) / r; vt = r * sin(theta) / (square(ri[1] / ri[0]) + 1.0) * ((ri[0] * v[i][1] - ri[1] * v[i][0]) / (ri[0] * ri[0])); vp = (ri[2] * vr - r * v[i][2]) / (r * sqrt(1.0 - square(ri[2] / r))); tpkrr[bin] += vr * vr; tpktt[bin] += vt * vt; tpkpp[bin] += vp * vp; } } // loop over neighbors of my atoms // skip if I or J are not in group // for newton = 0 and J = ghost atom, // need to ensure I,J pair is only output by one proc // use same itag,jtag logic as in Neighbor::neigh_half_nsq() // for flag = 0, just count pair interactions within force cutoff // for flag = 1, calculate requested output fields Pair *pair = force->pair; double **cutsq = force->pair->cutsq; double qi[3], l1, l2, l3, l4, R1, R2, Fa, Fb, l_sum; double rij, f, ririj, sqr, la, lb, sql0, lambda0; double rsqxy, ririjxy, sqrixy, sqlxy0, A, B, C; int end_bin; for (ii = 0; ii < inum; ii++) { i = ilist[ii]; if (!(mask[i] & groupbit)) continue; xtmp = x[i][0]; ytmp = x[i][1]; ztmp = x[i][2]; itag = tag[i]; itype = type[i]; jlist = firstneigh[i]; jnum = numneigh[i]; for (jj = 0; jj < jnum; jj++) { j = jlist[jj]; factor_lj = special_lj[sbmask(j)]; factor_coul = special_coul[sbmask(j)]; j &= NEIGHMASK; if (!(mask[j] & groupbit)) continue; // itag = jtag is possible for long cutoffs that include images of self // do calculation only on appropriate processor if (newton_pair == 0 && j >= nlocal) { jtag = tag[j]; if (itag > jtag) { if ((itag + jtag) % 2 == 0) continue; } else if (itag < jtag) { if ((itag + jtag) % 2 == 1) continue; } else { if (x[j][2] < ztmp) continue; if (x[j][2] == ztmp) { if (x[j][1] < ytmp) continue; if (x[j][1] == ytmp && x[j][0] < xtmp) continue; } } } ri[0] = x[j][0] - xtmp; ri[1] = x[j][1] - ytmp; ri[2] = x[j][2] - ztmp; rsq = ri[0] * ri[0] + ri[1] * ri[1] + ri[2] * ri[2]; jtype = type[j]; // Check if inside cut-off if (rsq >= cutsq[itype][jtype]) continue; pair->single(i, j, itype, jtype, rsq, factor_coul, factor_lj, fpair); qi[0] = xtmp - x0; qi[1] = ytmp - y0; qi[2] = ztmp - z0; for (int k = 0; k < 3; k++) { tmp = domain->boxhi[k] - domain->boxlo[k]; if (qi[k] > 0.5 * tmp) qi[k] -= tmp; else if (qi[k] < -0.5 * tmp) qi[k] += tmp; } l_sum = 0.0; rij = sqrt(rsq); f = -rij * fpair; ririj = qi[0] * ri[0] + qi[1] * ri[1] + qi[2] * ri[2]; sqr = qi[0] * qi[0] + qi[1] * qi[1] + qi[2] * qi[2]; la = 0.0; lb = 0.0; sql0 = sqr - ririj * ririj / rsq; lambda0 = -ririj / rsq; rsqxy = ri[0] * ri[0] + ri[1] * ri[1]; ririjxy = qi[0] * ri[0] + qi[1] * ri[1]; sqrixy = qi[0] * qi[0] + qi[1] * qi[1]; sqlxy0 = (rsqxy != 0.0) ? sqrixy - ririjxy * ririjxy / rsqxy : 0.0; A = square(qi[0] * ri[1] - qi[1] * ri[0]); if (sqlxy0 > SMALL) C = sqrt(rsqxy * sqrixy - ririjxy * ririjxy); if (sql0 > SMALL) B = sqrt(rsq * sqr - ririj * ririj); end_bin = (int) (sqrt(rsq + 2.0 * ririj + sqr) / bin_width); while (lb < 1.0) { l1 = lb + SMALL; bin = (int) floor(sqrt(rsq * l1 * l1 + 2.0 * ririj * l1 + sqr) / bin_width); R1 = bin * bin_width; R2 = (bin + 1) * bin_width; // we must not take the square root of a negative number or divide by zero. if ((R1 * R1 < sql0) || (R2 * R2 < sql0) || (rsq <= 0.0)) { lb = 1.0; } else { l1 = lambda0 + sqrt((R1 * R1 - sql0) / rsq); l2 = lambda0 - sqrt((R1 * R1 - sql0) / rsq); l3 = lambda0 + sqrt((R2 * R2 - sql0) / rsq); l4 = lambda0 - sqrt((R2 * R2 - sql0) / rsq); if (l4 >= 0.0 && l4 <= 1.0 && l4 > la) lb = l4; else if (l3 >= 0.0 && l3 <= 1.0 && l3 > la) lb = l3; else if (l2 >= 0.0 && l2 <= 1.0 && l2 > la) lb = l2; else if (l1 >= 0.0 && l1 <= 1.0 && l1 > la) lb = l1; else lb = 1.0; } if (bin == end_bin) lb = 1.0; if (la > lb) error->all(FLERR, "Error: la > lb\n"); if (bin >= 0 && bin < nbins) { if (sql0 > SMALL) { Fa = -B * atan2(rsq * la + ririj, B); Fb = -B * atan2(rsq * lb + ririj, B); tpcrr[bin] -= (f / rij) * (rsq * (lb - la) + Fb - Fa); tpcpp[bin] -= (f / rij) * (Fa - Fb) / 2.0; } else tpcrr[bin] -= f * rij * (lb - la); if (sqlxy0 > SMALL) tpctt[bin] -= (f / rij) * A / C * (atan2(rsqxy * lb + ririjxy, C) - atan2(rsqxy * la + ririjxy, C)); } l_sum += lb - la; la = lb; } // Error check if (fabs(l_sum - 1.0) > SMALL) error->all(FLERR, "ERROR: The sum of the fractional line segments is not 1.0"); } } // normalize pressure for (bin = 0; bin < nbins; bin++) { tdens[bin] *= invV[bin]; tpkrr[bin] *= invV[bin]; tpktt[bin] *= invV[bin]; tpkpp[bin] *= invV[bin]; tpcrr[bin] *= invV[bin]; tpctt[bin] *= invV[bin]; tpcpp[bin] *= invV[bin]; } // communicate across processors MPI_Allreduce(tdens, dens, nbins, MPI_DOUBLE, MPI_SUM, world); MPI_Allreduce(tpkrr, pkrr, nbins, MPI_DOUBLE, MPI_SUM, world); MPI_Allreduce(tpktt, pktt, nbins, MPI_DOUBLE, MPI_SUM, world); MPI_Allreduce(tpkpp, pkpp, nbins, MPI_DOUBLE, MPI_SUM, world); MPI_Allreduce(tpcrr, pcrr, nbins, MPI_DOUBLE, MPI_SUM, world); MPI_Allreduce(tpctt, pctt, nbins, MPI_DOUBLE, MPI_SUM, world); MPI_Allreduce(tpcpp, pcpp, nbins, MPI_DOUBLE, MPI_SUM, world); // populate array to output. for (bin = 0; bin < nbins; bin++) { array[bin][0] = (bin + 0.5) * bin_width; array[bin][1] = dens[bin]; array[bin][2] = pkrr[bin]; array[bin][3] = pktt[bin]; array[bin][4] = pkpp[bin]; array[bin][5] = pcrr[bin]; array[bin][6] = pctt[bin]; array[bin][7] = pcpp[bin]; } } double ComputeStressSpherical::memory_usage() { return 15.0 * (double) (nbins + size_array_rows * size_array_cols) * sizeof(double); }