1073 lines
33 KiB
C++
1073 lines
33 KiB
C++
// clang-format off
|
|
/* ----------------------------------------------------------------------
|
|
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
|
|
https://www.lammps.org/, 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 author: Aidan Thompson (SNL)
|
|
improved CG and backtrack ls, added quadratic ls
|
|
Sources: Numerical Recipes frprmn routine
|
|
"Conjugate Gradient Method Without the Agonizing Pain" by
|
|
JR Shewchuk, https://www.cs.cmu.edu/~quake-papers/painless-conjugate-gradient.pdf
|
|
------------------------------------------------------------------------- */
|
|
|
|
#include "min.h"
|
|
|
|
#include "angle.h"
|
|
#include "atom.h"
|
|
#include "atom_vec.h"
|
|
#include "bond.h"
|
|
#include "comm.h"
|
|
#include "compute.h"
|
|
#include "dihedral.h"
|
|
#include "domain.h"
|
|
#include "error.h"
|
|
#include "fix_minimize.h"
|
|
#include "force.h"
|
|
#include "improper.h"
|
|
#include "kspace.h"
|
|
#include "math_const.h"
|
|
#include "memory.h"
|
|
#include "modify.h"
|
|
#include "neighbor.h"
|
|
#include "output.h"
|
|
#include "pair.h"
|
|
#include "thermo.h"
|
|
#include "timer.h"
|
|
#include "update.h"
|
|
|
|
#include <cmath>
|
|
#include <cstring>
|
|
|
|
using namespace LAMMPS_NS;
|
|
using namespace MathConst;
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
Min::Min(LAMMPS *lmp) : Pointers(lmp)
|
|
{
|
|
dmax = 0.1;
|
|
searchflag = 0;
|
|
linestyle = 1;
|
|
normstyle = TWO;
|
|
|
|
delaystep = 20;
|
|
dtgrow = 1.1;
|
|
dtshrink = 0.5;
|
|
alpha0 = 0.25;
|
|
alphashrink = 0.99;
|
|
tmax = 10.0;
|
|
tmin = 0.02;
|
|
integrator = 0;
|
|
halfstepback_flag = 1;
|
|
delaystep_start_flag = 1;
|
|
max_vdotf_negatif = 2000;
|
|
alpha_final = 0.0;
|
|
|
|
elist_global = elist_atom = nullptr;
|
|
vlist_global = vlist_atom = cvlist_atom = nullptr;
|
|
|
|
nextra_global = 0;
|
|
fextra = nullptr;
|
|
|
|
nextra_atom = 0;
|
|
xextra_atom = fextra_atom = nullptr;
|
|
extra_peratom = extra_nlen = nullptr;
|
|
extra_max = nullptr;
|
|
requestor = nullptr;
|
|
|
|
external_force_clear = 0;
|
|
|
|
kokkosable = 0;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
Min::~Min()
|
|
{
|
|
delete [] elist_global;
|
|
delete [] elist_atom;
|
|
delete [] vlist_global;
|
|
delete [] vlist_atom;
|
|
delete [] cvlist_atom;
|
|
|
|
delete [] fextra;
|
|
|
|
memory->sfree(xextra_atom);
|
|
memory->sfree(fextra_atom);
|
|
memory->destroy(extra_peratom);
|
|
memory->destroy(extra_nlen);
|
|
memory->destroy(extra_max);
|
|
memory->sfree(requestor);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
void Min::init()
|
|
{
|
|
if (lmp->kokkos && !kokkosable)
|
|
error->all(FLERR,"Must use a Kokkos-enabled min style "
|
|
"(e.g. min_style cg/kk) with Kokkos minimize");
|
|
|
|
// create fix needed for storing atom-based quantities
|
|
// will delete it at end of run
|
|
|
|
modify->add_fix("MINIMIZE all MINIMIZE");
|
|
fix_minimize = (FixMinimize *) modify->fix[modify->nfix-1];
|
|
|
|
// clear out extra global and per-atom dof
|
|
// will receive requests for new per-atom dof during pair init()
|
|
// can then add vectors to fix_minimize in setup()
|
|
|
|
nextra_global = 0;
|
|
delete [] fextra;
|
|
fextra = nullptr;
|
|
|
|
nextra_atom = 0;
|
|
memory->sfree(xextra_atom);
|
|
memory->sfree(fextra_atom);
|
|
memory->destroy(extra_peratom);
|
|
memory->destroy(extra_nlen);
|
|
memory->destroy(extra_max);
|
|
memory->sfree(requestor);
|
|
xextra_atom = fextra_atom = nullptr;
|
|
extra_peratom = extra_nlen = nullptr;
|
|
extra_max = nullptr;
|
|
requestor = nullptr;
|
|
|
|
// virial_style:
|
|
// VIRIAL_PAIR if computed explicitly in pair via sum over pair interactions
|
|
// VIRIAL_FDOTR if computed implicitly in pair by
|
|
// virial_fdotr_compute() via sum over ghosts
|
|
|
|
if (force->newton_pair) virial_style = VIRIAL_FDOTR;
|
|
else virial_style = VIRIAL_PAIR;
|
|
|
|
// setup lists of computes for global and per-atom PE and pressure
|
|
|
|
ev_setup();
|
|
|
|
// detect if fix omp is present for clearing force arrays
|
|
|
|
int ifix = modify->find_fix("package_omp");
|
|
if (ifix >= 0) external_force_clear = 1;
|
|
|
|
// set flags for arrays to clear in force_clear()
|
|
|
|
torqueflag = extraflag = 0;
|
|
if (atom->torque_flag) torqueflag = 1;
|
|
if (atom->avec->forceclearflag) extraflag = 1;
|
|
|
|
// allow pair and Kspace compute() to be turned off via modify flags
|
|
|
|
if (force->pair && force->pair->compute_flag) pair_compute_flag = 1;
|
|
else pair_compute_flag = 0;
|
|
if (force->kspace && force->kspace->compute_flag) kspace_compute_flag = 1;
|
|
else kspace_compute_flag = 0;
|
|
|
|
// orthogonal vs triclinic simulation box
|
|
|
|
triclinic = domain->triclinic;
|
|
|
|
// reset reneighboring criteria if necessary
|
|
|
|
neigh_every = neighbor->every;
|
|
neigh_delay = neighbor->delay;
|
|
neigh_dist_check = neighbor->dist_check;
|
|
|
|
if (neigh_every != 1 || neigh_delay != 0 || neigh_dist_check != 1) {
|
|
if (comm->me == 0)
|
|
error->warning(FLERR, "Using 'neigh_modify every 1 delay 0 check"
|
|
" yes' setting during minimization");
|
|
}
|
|
|
|
neighbor->every = 1;
|
|
neighbor->delay = 0;
|
|
neighbor->dist_check = 1;
|
|
|
|
niter = neval = 0;
|
|
|
|
// store timestep size (important for variable timestep minimizer)
|
|
|
|
dtinit = update->dt;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
setup before run
|
|
------------------------------------------------------------------------- */
|
|
|
|
void Min::setup(int flag)
|
|
{
|
|
if (comm->me == 0 && screen) {
|
|
fmt::print(screen,"Setting up {} style minimization ...\n",
|
|
update->minimize_style);
|
|
if (flag) {
|
|
fmt::print(screen," Unit style : {}\n", update->unit_style);
|
|
fmt::print(screen," Current step : {}\n", update->ntimestep);
|
|
timer->print_timeout(screen);
|
|
}
|
|
}
|
|
update->setupflag = 1;
|
|
|
|
// setup extra global dof due to fixes
|
|
// cannot be done in init() b/c update init() is before modify init()
|
|
|
|
nextra_global = modify->min_dof();
|
|
if (nextra_global) {
|
|
fextra = new double[nextra_global];
|
|
if (comm->me == 0 && screen)
|
|
fprintf(screen,"WARNING: Energy due to %d extra global DOFs will"
|
|
" be included in minimizer energies\n",nextra_global);
|
|
}
|
|
|
|
// compute for potential energy
|
|
|
|
int id = modify->find_compute("thermo_pe");
|
|
if (id < 0) error->all(FLERR,"Minimization could not find thermo_pe compute");
|
|
pe_compute = modify->compute[id];
|
|
|
|
// style-specific setup does two tasks
|
|
// setup extra global dof vectors
|
|
// setup extra per-atom dof vectors due to requests from Pair classes
|
|
// cannot be done in init() b/c update init() is before modify/pair init()
|
|
|
|
setup_style();
|
|
|
|
// ndoftotal = total dof for entire minimization problem
|
|
// dof for atoms, extra per-atom, extra global
|
|
|
|
bigint ndofme = 3 * static_cast<bigint>(atom->nlocal);
|
|
for (int m = 0; m < nextra_atom; m++)
|
|
ndofme += extra_peratom[m]*static_cast<bigint>(atom->nlocal);
|
|
MPI_Allreduce(&ndofme,&ndoftotal,1,MPI_LMP_BIGINT,MPI_SUM,world);
|
|
ndoftotal += nextra_global;
|
|
|
|
// setup domain, communication and neighboring
|
|
// acquire ghosts
|
|
// build neighbor lists
|
|
|
|
atom->setup();
|
|
modify->setup_pre_exchange();
|
|
if (triclinic) domain->x2lamda(atom->nlocal);
|
|
domain->pbc();
|
|
domain->reset_box();
|
|
comm->setup();
|
|
if (neighbor->style) neighbor->setup_bins();
|
|
comm->exchange();
|
|
if (atom->sortfreq > 0) atom->sort();
|
|
comm->borders();
|
|
if (triclinic) domain->lamda2x(atom->nlocal+atom->nghost);
|
|
domain->image_check();
|
|
domain->box_too_small_check();
|
|
modify->setup_pre_neighbor();
|
|
neighbor->build(1);
|
|
modify->setup_post_neighbor();
|
|
neighbor->ncalls = 0;
|
|
|
|
// remove these restriction eventually
|
|
|
|
if (searchflag == 0) {
|
|
if (nextra_global)
|
|
error->all(FLERR,
|
|
"Cannot use a damped dynamics min style with fix box/relax");
|
|
if (nextra_atom)
|
|
error->all(FLERR,
|
|
"Cannot use a damped dynamics min style with per-atom DOF");
|
|
}
|
|
|
|
if (strcmp(update->minimize_style,"hftn") == 0) {
|
|
if (nextra_global)
|
|
error->all(FLERR, "Cannot use hftn min style with fix box/relax");
|
|
if (nextra_atom)
|
|
error->all(FLERR, "Cannot use hftn min style with per-atom DOF");
|
|
}
|
|
|
|
// atoms may have migrated in comm->exchange()
|
|
|
|
reset_vectors();
|
|
|
|
// compute all forces
|
|
|
|
force->setup();
|
|
ev_set(update->ntimestep);
|
|
force_clear();
|
|
modify->setup_pre_force(vflag);
|
|
|
|
if (pair_compute_flag) force->pair->compute(eflag,vflag);
|
|
else if (force->pair) force->pair->compute_dummy(eflag,vflag);
|
|
|
|
if (atom->molecular != Atom::ATOMIC) {
|
|
if (force->bond) force->bond->compute(eflag,vflag);
|
|
if (force->angle) force->angle->compute(eflag,vflag);
|
|
if (force->dihedral) force->dihedral->compute(eflag,vflag);
|
|
if (force->improper) force->improper->compute(eflag,vflag);
|
|
}
|
|
|
|
if (force->kspace) {
|
|
force->kspace->setup();
|
|
if (kspace_compute_flag) force->kspace->compute(eflag,vflag);
|
|
else force->kspace->compute_dummy(eflag,vflag);
|
|
}
|
|
|
|
modify->setup_pre_reverse(eflag,vflag);
|
|
if (force->newton) comm->reverse_comm();
|
|
|
|
// update per-atom minimization variables stored by pair styles
|
|
|
|
if (nextra_atom)
|
|
for (int m = 0; m < nextra_atom; m++)
|
|
requestor[m]->min_xf_get(m);
|
|
|
|
modify->setup(vflag);
|
|
output->setup(flag);
|
|
update->setupflag = 0;
|
|
|
|
// stats for initial thermo output
|
|
|
|
ecurrent = pe_compute->compute_scalar();
|
|
if (nextra_global) ecurrent += modify->min_energy(fextra);
|
|
if (output->thermo->normflag) ecurrent /= atom->natoms;
|
|
|
|
einitial = ecurrent;
|
|
fnorm2_init = sqrt(fnorm_sqr());
|
|
fnorminf_init = sqrt(fnorm_inf());
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
setup without output or one-time post-init setup
|
|
flag = 0 = just force calculation
|
|
flag = 1 = reneighbor and force calculation
|
|
------------------------------------------------------------------------- */
|
|
|
|
void Min::setup_minimal(int flag)
|
|
{
|
|
update->setupflag = 1;
|
|
|
|
// setup domain, communication and neighboring
|
|
// acquire ghosts
|
|
// build neighbor lists
|
|
|
|
if (flag) {
|
|
modify->setup_pre_exchange();
|
|
if (triclinic) domain->x2lamda(atom->nlocal);
|
|
domain->pbc();
|
|
domain->reset_box();
|
|
comm->setup();
|
|
if (neighbor->style) neighbor->setup_bins();
|
|
comm->exchange();
|
|
comm->borders();
|
|
if (triclinic) domain->lamda2x(atom->nlocal+atom->nghost);
|
|
domain->image_check();
|
|
domain->box_too_small_check();
|
|
modify->setup_pre_neighbor();
|
|
neighbor->build(1);
|
|
modify->setup_post_neighbor();
|
|
neighbor->ncalls = 0;
|
|
}
|
|
|
|
// atoms may have migrated in comm->exchange()
|
|
|
|
reset_vectors();
|
|
|
|
// compute all forces
|
|
|
|
ev_set(update->ntimestep);
|
|
force_clear();
|
|
modify->setup_pre_force(vflag);
|
|
|
|
if (pair_compute_flag) force->pair->compute(eflag,vflag);
|
|
else if (force->pair) force->pair->compute_dummy(eflag,vflag);
|
|
|
|
if (atom->molecular != Atom::ATOMIC) {
|
|
if (force->bond) force->bond->compute(eflag,vflag);
|
|
if (force->angle) force->angle->compute(eflag,vflag);
|
|
if (force->dihedral) force->dihedral->compute(eflag,vflag);
|
|
if (force->improper) force->improper->compute(eflag,vflag);
|
|
}
|
|
|
|
if (force->kspace) {
|
|
force->kspace->setup();
|
|
if (kspace_compute_flag) force->kspace->compute(eflag,vflag);
|
|
else force->kspace->compute_dummy(eflag,vflag);
|
|
}
|
|
|
|
modify->setup_pre_reverse(eflag,vflag);
|
|
if (force->newton) comm->reverse_comm();
|
|
|
|
// update per-atom minimization variables stored by pair styles
|
|
|
|
if (nextra_atom)
|
|
for (int m = 0; m < nextra_atom; m++)
|
|
requestor[m]->min_xf_get(m);
|
|
|
|
modify->setup(vflag);
|
|
update->setupflag = 0;
|
|
|
|
// stats for Finish to print
|
|
|
|
ecurrent = pe_compute->compute_scalar();
|
|
if (nextra_global) ecurrent += modify->min_energy(fextra);
|
|
if (output->thermo->normflag) ecurrent /= atom->natoms;
|
|
|
|
einitial = ecurrent;
|
|
fnorm2_init = sqrt(fnorm_sqr());
|
|
fnorminf_init = sqrt(fnorm_inf());
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
perform minimization, calling iterate() for N steps
|
|
------------------------------------------------------------------------- */
|
|
|
|
void Min::run(int n)
|
|
{
|
|
// minimizer iterations
|
|
|
|
stop_condition = iterate(n);
|
|
stopstr = stopstrings(stop_condition);
|
|
|
|
// if early exit from iterate loop:
|
|
// set update->nsteps to niter for Finish stats to print
|
|
// set output->next values to this timestep
|
|
// call energy_force() to insure vflag is set when forces computed
|
|
// output->write does final output for thermo, dump, restart files
|
|
// add ntimestep to all computes that store invocation times
|
|
// since are hardwiring call to thermo/dumps and computes may not be ready
|
|
|
|
if (stop_condition != MAXITER) {
|
|
update->nsteps = niter;
|
|
|
|
if (update->restrict_output == 0) {
|
|
for (int idump = 0; idump < output->ndump; idump++)
|
|
output->next_dump[idump] = update->ntimestep;
|
|
output->next_dump_any = update->ntimestep;
|
|
if (output->restart_flag) {
|
|
output->next_restart = update->ntimestep;
|
|
if (output->restart_every_single)
|
|
output->next_restart_single = update->ntimestep;
|
|
if (output->restart_every_double)
|
|
output->next_restart_double = update->ntimestep;
|
|
}
|
|
}
|
|
output->next_thermo = update->ntimestep;
|
|
|
|
modify->addstep_compute_all(update->ntimestep);
|
|
ecurrent = energy_force(0);
|
|
output->write(update->ntimestep);
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
void Min::cleanup()
|
|
{
|
|
modify->post_run();
|
|
|
|
// stats for Finish to print
|
|
|
|
efinal = ecurrent;
|
|
fnorm2_final = sqrt(fnorm_sqr());
|
|
fnorminf_final = sqrt(fnorm_inf());
|
|
|
|
// reset reneighboring criteria
|
|
|
|
neighbor->every = neigh_every;
|
|
neighbor->delay = neigh_delay;
|
|
neighbor->dist_check = neigh_dist_check;
|
|
|
|
// delete fix at end of run, so its atom arrays won't persist
|
|
|
|
modify->delete_fix("MINIMIZE");
|
|
domain->box_too_small_check();
|
|
|
|
// reset timestep size (important for variable timestep minimizer)
|
|
|
|
update->dt = dtinit;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
evaluate potential energy and forces
|
|
may migrate atoms due to reneighboring
|
|
return new energy, which should include nextra_global dof
|
|
return negative gradient stored in atom->f
|
|
return negative gradient for nextra_global dof in fextra
|
|
------------------------------------------------------------------------- */
|
|
|
|
double Min::energy_force(int resetflag)
|
|
{
|
|
// check for reneighboring
|
|
// always communicate since minimizer moved atoms
|
|
|
|
int nflag = neighbor->decide();
|
|
|
|
if (nflag == 0) {
|
|
timer->stamp();
|
|
comm->forward_comm();
|
|
timer->stamp(Timer::COMM);
|
|
} else {
|
|
if (modify->n_min_pre_exchange) {
|
|
timer->stamp();
|
|
modify->min_pre_exchange();
|
|
timer->stamp(Timer::MODIFY);
|
|
}
|
|
if (triclinic) domain->x2lamda(atom->nlocal);
|
|
domain->pbc();
|
|
if (domain->box_change) {
|
|
domain->reset_box();
|
|
comm->setup();
|
|
if (neighbor->style) neighbor->setup_bins();
|
|
}
|
|
timer->stamp();
|
|
comm->exchange();
|
|
if (atom->sortfreq > 0 &&
|
|
update->ntimestep >= atom->nextsort) atom->sort();
|
|
comm->borders();
|
|
if (triclinic) domain->lamda2x(atom->nlocal+atom->nghost);
|
|
timer->stamp(Timer::COMM);
|
|
if (modify->n_min_pre_neighbor) {
|
|
modify->min_pre_neighbor();
|
|
timer->stamp(Timer::MODIFY);
|
|
}
|
|
neighbor->build(1);
|
|
timer->stamp(Timer::NEIGH);
|
|
if (modify->n_min_post_neighbor) {
|
|
modify->min_post_neighbor();
|
|
timer->stamp(Timer::MODIFY);
|
|
}
|
|
}
|
|
|
|
ev_set(update->ntimestep);
|
|
force_clear();
|
|
|
|
timer->stamp();
|
|
|
|
if (modify->n_min_pre_force) {
|
|
modify->min_pre_force(vflag);
|
|
timer->stamp(Timer::MODIFY);
|
|
}
|
|
|
|
if (pair_compute_flag) {
|
|
force->pair->compute(eflag,vflag);
|
|
timer->stamp(Timer::PAIR);
|
|
}
|
|
|
|
if (atom->molecular != Atom::ATOMIC) {
|
|
if (force->bond) force->bond->compute(eflag,vflag);
|
|
if (force->angle) force->angle->compute(eflag,vflag);
|
|
if (force->dihedral) force->dihedral->compute(eflag,vflag);
|
|
if (force->improper) force->improper->compute(eflag,vflag);
|
|
timer->stamp(Timer::BOND);
|
|
}
|
|
|
|
if (kspace_compute_flag) {
|
|
force->kspace->compute(eflag,vflag);
|
|
timer->stamp(Timer::KSPACE);
|
|
}
|
|
|
|
if (modify->n_min_pre_reverse) {
|
|
modify->min_pre_reverse(eflag,vflag);
|
|
timer->stamp(Timer::MODIFY);
|
|
}
|
|
|
|
if (force->newton) {
|
|
comm->reverse_comm();
|
|
timer->stamp(Timer::COMM);
|
|
}
|
|
|
|
// update per-atom minimization variables stored by pair styles
|
|
|
|
if (nextra_atom)
|
|
for (int m = 0; m < nextra_atom; m++)
|
|
requestor[m]->min_xf_get(m);
|
|
|
|
// fixes that affect minimization
|
|
|
|
if (modify->n_min_post_force) {
|
|
timer->stamp();
|
|
modify->min_post_force(vflag);
|
|
timer->stamp(Timer::MODIFY);
|
|
}
|
|
|
|
// compute potential energy of system
|
|
// normalize if thermo PE does
|
|
|
|
double energy = pe_compute->compute_scalar();
|
|
if (nextra_global) energy += modify->min_energy(fextra);
|
|
if (output->thermo->normflag) energy /= atom->natoms;
|
|
|
|
// if reneighbored, atoms migrated
|
|
// if resetflag = 1, update x0 of atoms crossing PBC
|
|
// reset vectors used by lo-level minimizer
|
|
|
|
if (nflag) {
|
|
if (resetflag) fix_minimize->reset_coords();
|
|
reset_vectors();
|
|
}
|
|
|
|
return energy;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
clear force on own & ghost atoms
|
|
clear other arrays as needed
|
|
------------------------------------------------------------------------- */
|
|
|
|
void Min::force_clear()
|
|
{
|
|
if (external_force_clear) return;
|
|
|
|
// clear global force array
|
|
// if either newton flag is set, also include ghosts
|
|
|
|
size_t nbytes = sizeof(double) * atom->nlocal;
|
|
if (force->newton) nbytes += sizeof(double) * atom->nghost;
|
|
|
|
if (nbytes) {
|
|
memset(&atom->f[0][0],0,3*nbytes);
|
|
if (torqueflag) memset(&atom->torque[0][0],0,3*nbytes);
|
|
if (extraflag) atom->avec->force_clear(0,nbytes);
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
pair style makes request to add a per-atom variables to minimization
|
|
requestor stores callback to pair class to invoke during min
|
|
to get current variable and forces on it and to update the variable
|
|
return flag that pair can use if it registers multiple variables
|
|
------------------------------------------------------------------------- */
|
|
|
|
int Min::request(Pair *pair, int peratom, double maxvalue)
|
|
{
|
|
int n = nextra_atom + 1;
|
|
xextra_atom = (double **) memory->srealloc(xextra_atom,n*sizeof(double *),
|
|
"min:xextra_atom");
|
|
fextra_atom = (double **) memory->srealloc(fextra_atom,n*sizeof(double *),
|
|
"min:fextra_atom");
|
|
memory->grow(extra_peratom,n,"min:extra_peratom");
|
|
memory->grow(extra_nlen,n,"min:extra_nlen");
|
|
memory->grow(extra_max,n,"min:extra_max");
|
|
requestor = (Pair **) memory->srealloc(requestor,n*sizeof(Pair *),
|
|
"min:requestor");
|
|
|
|
requestor[nextra_atom] = pair;
|
|
extra_peratom[nextra_atom] = peratom;
|
|
extra_max[nextra_atom] = maxvalue;
|
|
nextra_atom++;
|
|
return nextra_atom-1;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
void Min::modify_params(int narg, char **arg)
|
|
{
|
|
if (narg == 0) error->all(FLERR,"Illegal min_modify command");
|
|
|
|
int iarg = 0;
|
|
while (iarg < narg) {
|
|
if (strcmp(arg[iarg],"dmax") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
|
|
dmax = utils::numeric(FLERR,arg[iarg+1],false,lmp);
|
|
iarg += 2;
|
|
} else if (strcmp(arg[iarg],"delaystep") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
|
|
delaystep = utils::numeric(FLERR,arg[iarg+1],false,lmp);
|
|
iarg += 2;
|
|
} else if (strcmp(arg[iarg],"dtgrow") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
|
|
dtgrow = utils::numeric(FLERR,arg[iarg+1],false,lmp);
|
|
iarg += 2;
|
|
} else if (strcmp(arg[iarg],"dtshrink") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
|
|
dtshrink = utils::numeric(FLERR,arg[iarg+1],false,lmp);
|
|
iarg += 2;
|
|
} else if (strcmp(arg[iarg],"alpha0") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
|
|
alpha0 = utils::numeric(FLERR,arg[iarg+1],false,lmp);
|
|
iarg += 2;
|
|
} else if (strcmp(arg[iarg],"alphashrink") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
|
|
alphashrink = utils::numeric(FLERR,arg[iarg+1],false,lmp);
|
|
iarg += 2;
|
|
} else if (strcmp(arg[iarg],"tmax") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
|
|
tmax = utils::numeric(FLERR,arg[iarg+1],false,lmp);
|
|
iarg += 2;
|
|
} else if (strcmp(arg[iarg],"tmin") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
|
|
tmin = utils::numeric(FLERR,arg[iarg+1],false,lmp);
|
|
iarg += 2;
|
|
} else if (strcmp(arg[iarg],"halfstepback") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
|
|
if (strcmp(arg[iarg+1],"yes") == 0) halfstepback_flag = 1;
|
|
else if (strcmp(arg[iarg+1],"no") == 0) halfstepback_flag = 0;
|
|
else error->all(FLERR,"Illegal min_modify command");
|
|
iarg += 2;
|
|
} else if (strcmp(arg[iarg],"initialdelay") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
|
|
if (strcmp(arg[iarg+1],"yes") == 0) delaystep_start_flag = 1;
|
|
else if (strcmp(arg[iarg+1],"no") == 0) delaystep_start_flag = 0;
|
|
else error->all(FLERR,"Illegal min_modify command");
|
|
iarg += 2;
|
|
} else if (strcmp(arg[iarg],"vdfmax") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
|
|
max_vdotf_negatif = utils::numeric(FLERR,arg[iarg+1],false,lmp);
|
|
iarg += 2;
|
|
} else if (strcmp(arg[iarg],"integrator") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
|
|
if (strcmp(arg[iarg+1],"eulerimplicit") == 0) integrator = 0;
|
|
else if (strcmp(arg[iarg+1],"verlet") == 0) integrator = 1;
|
|
else if (strcmp(arg[iarg+1],"leapfrog") == 0) integrator = 2;
|
|
else if (strcmp(arg[iarg+1],"eulerexplicit") == 0) integrator = 3;
|
|
else error->all(FLERR,"Illegal min_modify command");
|
|
iarg += 2;
|
|
} else if (strcmp(arg[iarg],"line") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
|
|
if (strcmp(arg[iarg+1],"backtrack") == 0) linestyle = 0;
|
|
else if (strcmp(arg[iarg+1],"quadratic") == 0) linestyle = 1;
|
|
else if (strcmp(arg[iarg+1],"forcezero") == 0) linestyle = 2;
|
|
else if (strcmp(arg[iarg+1],"spin_cubic") == 0) linestyle = 3;
|
|
else if (strcmp(arg[iarg+1],"spin_none") == 0) linestyle = 4;
|
|
else error->all(FLERR,"Illegal min_modify command");
|
|
iarg += 2;
|
|
} else if (strcmp(arg[iarg],"norm") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
|
|
if (strcmp(arg[iarg+1],"two") == 0) normstyle = TWO;
|
|
else if (strcmp(arg[iarg+1],"max") == 0) normstyle = MAX;
|
|
else if (strcmp(arg[iarg+1],"inf") == 0) normstyle = INF;
|
|
else error->all(FLERR,"Illegal min_modify command");
|
|
iarg += 2;
|
|
} else {
|
|
int n = modify_param(narg-iarg,&arg[iarg]);
|
|
if (n == 0) error->all(FLERR,"Illegal fix_modify command");
|
|
iarg += n;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
setup lists of computes for global and per-atom PE and pressure
|
|
------------------------------------------------------------------------- */
|
|
|
|
void Min::ev_setup()
|
|
{
|
|
delete [] elist_global;
|
|
delete [] elist_atom;
|
|
delete [] vlist_global;
|
|
delete [] vlist_atom;
|
|
delete [] cvlist_atom;
|
|
elist_global = elist_atom = nullptr;
|
|
vlist_global = vlist_atom = cvlist_atom = nullptr;
|
|
|
|
nelist_global = nelist_atom = 0;
|
|
nvlist_global = nvlist_atom = ncvlist_atom = 0;
|
|
for (int i = 0; i < modify->ncompute; i++) {
|
|
if (modify->compute[i]->peflag) nelist_global++;
|
|
if (modify->compute[i]->peatomflag) nelist_atom++;
|
|
if (modify->compute[i]->pressflag) nvlist_global++;
|
|
if (modify->compute[i]->pressatomflag & 1) nvlist_atom++;
|
|
if (modify->compute[i]->pressatomflag & 2) ncvlist_atom++;
|
|
}
|
|
|
|
if (nelist_global) elist_global = new Compute*[nelist_global];
|
|
if (nelist_atom) elist_atom = new Compute*[nelist_atom];
|
|
if (nvlist_global) vlist_global = new Compute*[nvlist_global];
|
|
if (nvlist_atom) vlist_atom = new Compute*[nvlist_atom];
|
|
if (ncvlist_atom) cvlist_atom = new Compute*[ncvlist_atom];
|
|
|
|
nelist_global = nelist_atom = 0;
|
|
nvlist_global = nvlist_atom = ncvlist_atom = 0;
|
|
for (int i = 0; i < modify->ncompute; i++) {
|
|
if (modify->compute[i]->peflag)
|
|
elist_global[nelist_global++] = modify->compute[i];
|
|
if (modify->compute[i]->peatomflag)
|
|
elist_atom[nelist_atom++] = modify->compute[i];
|
|
if (modify->compute[i]->pressflag)
|
|
vlist_global[nvlist_global++] = modify->compute[i];
|
|
if (modify->compute[i]->pressatomflag & 1)
|
|
vlist_atom[nvlist_atom++] = modify->compute[i];
|
|
if (modify->compute[i]->pressatomflag & 2)
|
|
cvlist_atom[ncvlist_atom++] = modify->compute[i];
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
set eflag,vflag for current iteration
|
|
invoke matchstep() on all timestep-dependent computes to clear their arrays
|
|
eflag/vflag based on computes that need info on this ntimestep
|
|
always set eflag_global = 1, since need energy every iteration
|
|
eflag: set any or no bits
|
|
ENERGY_GLOBAL bit for global energy
|
|
ENERGY_ATOM bit for per-atom energy
|
|
vflag: set any or no bits, but GLOBAL/FDOTR bit cannot both be set
|
|
VIRIAL_PAIR bit for global virial as sum of pairwise terms
|
|
VIRIAL_FDOTR bit for global virial via F dot r
|
|
VIRIAL_ATOM bit for per-atom virial
|
|
VIRIAL_CENTROID bit for per-atom centroid virial
|
|
all force components (pair,bond,angle,...,kspace) use eflag/vflag
|
|
in their ev_setup() method to set local energy/virial flags
|
|
------------------------------------------------------------------------- */
|
|
|
|
void Min::ev_set(bigint ntimestep)
|
|
{
|
|
int i,flag;
|
|
|
|
int eflag_global = 1;
|
|
for (i = 0; i < nelist_global; i++)
|
|
elist_global[i]->matchstep(ntimestep);
|
|
|
|
flag = 0;
|
|
int eflag_atom = 0;
|
|
for (i = 0; i < nelist_atom; i++)
|
|
if (elist_atom[i]->matchstep(ntimestep)) flag = 1;
|
|
if (flag) eflag_atom = ENERGY_ATOM;
|
|
|
|
if (eflag_global) update->eflag_global = update->ntimestep;
|
|
if (eflag_atom) update->eflag_atom = update->ntimestep;
|
|
eflag = eflag_global + eflag_atom;
|
|
|
|
flag = 0;
|
|
int vflag_global = 0;
|
|
for (i = 0; i < nvlist_global; i++)
|
|
if (vlist_global[i]->matchstep(ntimestep)) flag = 1;
|
|
if (flag) vflag_global = virial_style;
|
|
|
|
flag = 0;
|
|
int vflag_atom = 0;
|
|
for (i = 0; i < nvlist_atom; i++)
|
|
if (vlist_atom[i]->matchstep(ntimestep)) flag = 1;
|
|
if (flag) vflag_atom = VIRIAL_ATOM;
|
|
|
|
flag = 0;
|
|
int cvflag_atom = 0;
|
|
for (i = 0; i < ncvlist_atom; i++)
|
|
if (cvlist_atom[i]->matchstep(ntimestep)) flag = 1;
|
|
if (flag) cvflag_atom = VIRIAL_CENTROID;
|
|
|
|
if (vflag_global) update->vflag_global = update->ntimestep;
|
|
if (vflag_atom || cvflag_atom) update->vflag_atom = update->ntimestep;
|
|
vflag = vflag_global + vflag_atom + cvflag_atom;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
compute and return ||force||_2^2
|
|
------------------------------------------------------------------------- */
|
|
|
|
double Min::fnorm_sqr()
|
|
{
|
|
int i,n;
|
|
double *fatom;
|
|
|
|
double local_norm2_sqr = 0.0;
|
|
for (i = 0; i < nvec; i++) local_norm2_sqr += fvec[i]*fvec[i];
|
|
if (nextra_atom) {
|
|
for (int m = 0; m < nextra_atom; m++) {
|
|
fatom = fextra_atom[m];
|
|
n = extra_nlen[m];
|
|
for (i = 0; i < n; i++)
|
|
local_norm2_sqr += fatom[i]*fatom[i];
|
|
}
|
|
}
|
|
|
|
double norm2_sqr = 0.0;
|
|
MPI_Allreduce(&local_norm2_sqr,&norm2_sqr,1,MPI_DOUBLE,MPI_SUM,world);
|
|
|
|
if (nextra_global)
|
|
for (i = 0; i < nextra_global; i++)
|
|
norm2_sqr += fextra[i]*fextra[i];
|
|
|
|
return norm2_sqr;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
compute and return ||force||_inf
|
|
------------------------------------------------------------------------- */
|
|
|
|
double Min::fnorm_inf()
|
|
{
|
|
int i,n;
|
|
double *fatom;
|
|
|
|
double local_norm_inf = 0.0;
|
|
for (i = 0; i < nvec; i++)
|
|
local_norm_inf = MAX(fvec[i]*fvec[i],local_norm_inf);
|
|
if (nextra_atom) {
|
|
for (int m = 0; m < nextra_atom; m++) {
|
|
fatom = fextra_atom[m];
|
|
n = extra_nlen[m];
|
|
for (i = 0; i < n; i++)
|
|
local_norm_inf = MAX(fatom[i]*fatom[i],local_norm_inf);
|
|
}
|
|
}
|
|
|
|
double norm_inf = 0.0;
|
|
MPI_Allreduce(&local_norm_inf,&norm_inf,1,MPI_DOUBLE,MPI_MAX,world);
|
|
|
|
if (nextra_global)
|
|
for (i = 0; i < nextra_global; i++)
|
|
norm_inf = MAX(fextra[i]*fextra[i],norm_inf);
|
|
|
|
return norm_inf;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
compute and return ||force||_max (inf norm per-vector)
|
|
------------------------------------------------------------------------- */
|
|
|
|
double Min::fnorm_max()
|
|
{
|
|
int i,n;
|
|
double fdotf,*fatom;
|
|
|
|
double local_norm_max = 0.0;
|
|
for (i = 0; i < nvec; i+=3) {
|
|
fdotf = fvec[i]*fvec[i]+fvec[i+1]*fvec[i+1]+fvec[i+2]*fvec[i+2];
|
|
local_norm_max = MAX(fdotf,local_norm_max);
|
|
}
|
|
if (nextra_atom) {
|
|
for (int m = 0; m < nextra_atom; m++) {
|
|
fatom = fextra_atom[m];
|
|
n = extra_nlen[m];
|
|
for (i = 0; i < n; i+=3) {
|
|
fdotf = fatom[i]*fatom[i]+fatom[i+1]*fatom[i+1]+fatom[i+2]*fatom[i+2];
|
|
local_norm_max = MAX(fdotf,local_norm_max);
|
|
}
|
|
}
|
|
}
|
|
|
|
double norm_max = 0.0;
|
|
MPI_Allreduce(&local_norm_max,&norm_max,1,MPI_DOUBLE,MPI_MAX,world);
|
|
|
|
if (nextra_global) {
|
|
for (i = 0; i < nextra_global; i+=3) {
|
|
fdotf = fextra[i]*fextra[i];
|
|
norm_max = MAX(fdotf,norm_max);
|
|
}
|
|
}
|
|
return norm_max;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
compute and return sum_i||mag. torque_i||_2 (in eV)
|
|
------------------------------------------------------------------------- */
|
|
|
|
double Min::total_torque()
|
|
{
|
|
double fmsq,ftotsqone,ftotsqall;
|
|
int nlocal = atom->nlocal;
|
|
double hbar = force->hplanck/MY_2PI;
|
|
double tx,ty,tz;
|
|
double **sp = atom->sp;
|
|
double **fm = atom->fm;
|
|
|
|
fmsq = ftotsqone = ftotsqall = 0.0;
|
|
for (int i = 0; i < nlocal; i++) {
|
|
tx = fm[i][1]*sp[i][2] - fm[i][2]*sp[i][1];
|
|
ty = fm[i][2]*sp[i][0] - fm[i][0]*sp[i][2];
|
|
tz = fm[i][0]*sp[i][1] - fm[i][1]*sp[i][0];
|
|
fmsq = tx*tx + ty*ty + tz*tz;
|
|
ftotsqone += fmsq;
|
|
}
|
|
|
|
// summing all fmsqtot on this replica
|
|
|
|
MPI_Allreduce(&ftotsqone,&ftotsqall,1,MPI_DOUBLE,MPI_SUM,world);
|
|
|
|
// multiply it by hbar so that units are in eV
|
|
|
|
return sqrt(ftotsqall) * hbar;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
compute and return max_i ||mag. torque components|| (in eV)
|
|
------------------------------------------------------------------------- */
|
|
|
|
double Min::inf_torque()
|
|
{
|
|
double fmaxsqone,fmaxsqall;
|
|
int nlocal = atom->nlocal;
|
|
double hbar = force->hplanck/MY_2PI;
|
|
double tx,ty,tz;
|
|
double **sp = atom->sp;
|
|
double **fm = atom->fm;
|
|
|
|
fmaxsqone = fmaxsqall = 0.0;
|
|
for (int i = 0; i < nlocal; i++) {
|
|
tx = fm[i][1]*sp[i][2] - fm[i][2]*sp[i][1];
|
|
ty = fm[i][2]*sp[i][0] - fm[i][0]*sp[i][2];
|
|
tz = fm[i][0]*sp[i][1] - fm[i][1]*sp[i][0];
|
|
fmaxsqone = MAX(fmaxsqone,tx*tx);
|
|
fmaxsqone = MAX(fmaxsqone,ty*ty);
|
|
fmaxsqone = MAX(fmaxsqone,tz*tz);
|
|
}
|
|
|
|
// finding max fm on this replica
|
|
|
|
fmaxsqall = fmaxsqone;
|
|
MPI_Allreduce(&fmaxsqone,&fmaxsqall,1,MPI_DOUBLE,MPI_MAX,world);
|
|
|
|
// multiply it by hbar so that units are in eV
|
|
|
|
return sqrt(fmaxsqall) * hbar;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
compute and return max_i ||mag. torque_i|| (in eV)
|
|
------------------------------------------------------------------------- */
|
|
|
|
double Min::max_torque()
|
|
{
|
|
double fmsq,fmaxsqone,fmaxsqall;
|
|
int nlocal = atom->nlocal;
|
|
double hbar = force->hplanck/MY_2PI;
|
|
double tx,ty,tz;
|
|
double **sp = atom->sp;
|
|
double **fm = atom->fm;
|
|
|
|
fmsq = fmaxsqone = fmaxsqall = 0.0;
|
|
for (int i = 0; i < nlocal; i++) {
|
|
tx = fm[i][1]*sp[i][2] - fm[i][2]*sp[i][1];
|
|
ty = fm[i][2]*sp[i][0] - fm[i][0]*sp[i][2];
|
|
tz = fm[i][0]*sp[i][1] - fm[i][1]*sp[i][0];
|
|
fmsq = tx*tx + ty*ty + tz*tz;
|
|
fmaxsqone = MAX(fmaxsqone,fmsq);
|
|
}
|
|
|
|
// finding max fm on this replica
|
|
|
|
fmaxsqall = fmaxsqone;
|
|
MPI_Allreduce(&fmaxsqone,&fmaxsqall,1,MPI_DOUBLE,MPI_MAX,world);
|
|
|
|
// multiply it by hbar so that units are in eV
|
|
|
|
return sqrt(fmaxsqall) * hbar;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
possible stop conditions
|
|
------------------------------------------------------------------------- */
|
|
|
|
char *Min::stopstrings(int n)
|
|
{
|
|
const char *strings[] = {"max iterations",
|
|
"max force evaluations",
|
|
"energy tolerance",
|
|
"force tolerance",
|
|
"search direction is not downhill",
|
|
"linesearch alpha is zero",
|
|
"forces are zero",
|
|
"quadratic factors are zero",
|
|
"trust region too small",
|
|
"HFTN minimizer error",
|
|
"walltime limit reached",
|
|
"max iterations with v.f negative"};
|
|
return (char *) strings[n];
|
|
}
|