fix for ScaFaCoS library

This commit is contained in:
Rene Halver 2018-07-09 13:00:52 -06:00
parent 510834cb7b
commit b259de95d2
6 changed files with 776 additions and 3 deletions

View File

@ -119,7 +119,7 @@ option(ENABLE_ALL "Build all default packages" OFF)
set(DEFAULT_PACKAGES ASPHERE BODY CLASS2 COLLOID COMPRESS CORESHELL DIPOLE GRANULAR
KSPACE MANYBODY MC MEAM MISC MOLECULE PERI QEQ
REAX REPLICA RIGID SHOCK SNAP SRD)
set(OTHER_PACKAGES KIM PYTHON MSCG MPIIO VORONOI POEMS LATTE
set(OTHER_PACKAGES KIM PYTHON MSCG MPIIO VORONOI POEMS LATTE SCAFACOS
USER-ATC USER-AWPMD USER-BOCS USER-CGDNA USER-MESO
USER-CGSDK USER-COLVARS USER-DIFFRACTION USER-DPD USER-DRUDE USER-EFF
USER-FEP USER-H5MD USER-LB USER-MANIFOLD USER-MEAMC USER-MGPT USER-MISC
@ -150,11 +150,11 @@ pkg_depends(CORESHELL KSPACE)
######################################################
# packages with special compiler needs or external libs
######################################################
if(PKG_REAX OR PKG_MEAM OR PKG_USER-QUIP OR PKG_USER-QMMM OR PKG_LATTE)
if(PKG_REAX OR PKG_MEAM OR PKG_USER-QUIP OR PKG_USER-QMMM OR PKG_LATTE OR PKG_SCAFACOS)
enable_language(Fortran)
endif()
if(PKG_MEAM OR PKG_USER-H5MD OR PKG_USER-QMMM)
if(PKG_MEAM OR PKG_USER-H5MD OR PKG_USER-QMMM OR PKG_SCAFACOS)
enable_language(C)
endif()
@ -313,6 +313,13 @@ if(PKG_LATTE)
list(APPEND LAMMPS_LINK_LIBS ${LATTE_LIBRARIES} ${LAPACK_LIBRARIES})
endif()
if(PKG_SCAFACOS)
FIND_PACKAGE(PkgConfig)
PKG_CHECK_MODULES(SCAFACOS scafacos)
include_directories(${SCAFACOS_INCLUDE_DIRS})
list(APPEND LAMMPS_LINK_LIBS ${SCAFACOS_LDFLAGS})
endif()
if(PKG_USER-MOLFILE)
add_library(molfile INTERFACE)
target_include_directories(molfile INTERFACE ${LAMMPS_LIB_SOURCE_DIR}/molfile)

View File

@ -0,0 +1,25 @@
LAMMPS Description
8 atoms
2 atom types
0.0000000000000000 2.000000000000000 xlo xhi
0.0000000000000000 2.000000000000000 ylo yhi
0.0000000000000000 2.000000000000000 zlo zhi
Masses
1 22.98976928
2 35.45
Atoms
1 1 1 1.0 0.0 0.0 0.0
2 1 2 -1.0 1.0 0.0 0.0
3 1 2 -1.0 0.0 1.0 0.0
4 1 1 1.0 1.0 1.0 0.0
5 1 2 -1.0 0.0 0.0 1.0
6 1 1 1.0 1.0 0.0 1.0
7 1 1 1.0 0.0 1.0 1.0
8 1 2 -1.0 1.0 1.0 1.0

View File

@ -0,0 +1,22 @@
# Point dipoles in a 2d box
units lj
atom_style full
read_data data.NaCl
# need both mass settings due to hybrid atom style
velocity all set 0.0 0.0 0.0 mom no
pair_style zero 1.0
pair_coeff * *
neighbor 1.0 bin
neigh_modify delay 0
fix 1 all scafacos fmm
timestep 0.005
run 100

67
src/SCAFACOS/Install.sh Executable file
View File

@ -0,0 +1,67 @@
# Install/unInstall package files in LAMMPS
# mode = 0/1/2 for uninstall/install/update
mode=$1
# enforce using portable C locale
LC_ALL=C
export LC_ALL
# arg1 = file, arg2 = file it depends on
action () {
if (test $mode = 0) then
rm -f ../$1
elif (! cmp -s $1 ../$1) then
if (test -z "$2" || test -e ../$2) then
cp $1 ..
if (test $mode = 2) then
echo " updating src/$1"
fi
fi
elif (test -n "$2") then
if (test ! -e ../$2) then
rm -f ../$1
fi
fi
}
# all package files with no dependencies
for file in *.cpp *.h; do
test -f ${file} && action $file
done
# edit 2 Makefile.package files to include/exclude package info
if (test $1 = 1) then
if (test -e ../Makefile.package) then
sed -i -e 's/[^ \t]*scafacos[^ \t]* //' ../Makefile.package
sed -i -e 's|^PKG_INC =[ \t]*|&-I../../lib/scafacos/includelink |' ../Makefile.package
sed -i -e 's|^PKG_PATH =[ \t]*|&-L../../lib/scafacos/liblink |' ../Makefile.package
#sed -i -e 's|^PKG_LIB =[ \t]*|&-lscafacos |' ../Makefile.package
sed -i -e 's|^PKG_SYSINC =[ \t]*|&$(scafacos_SYSINC) |' ../Makefile.package
sed -i -e 's|^PKG_SYSLIB =[ \t]*|&$(scafacos_SYSLIB) |' ../Makefile.package
sed -i -e 's|^PKG_SYSPATH =[ \t]*|&$(scafacos_SYSPATH) |' ../Makefile.package
fi
if (test -e ../Makefile.package.settings) then
sed -i -e '/^include.*scafacos.*$/d' ../Makefile.package.settings
# multiline form needed for BSD sed on Macs
sed -i -e '4 i \
include ..\/..\/lib\/scafacos\/Makefile.lammps
' ../Makefile.package.settings
fi
elif (test $1 = 0) then
if (test -e ../Makefile.package) then
sed -i -e 's/[^ \t]*scafacos[^ \t]* //' ../Makefile.package
fi
if (test -e ../Makefile.package.settings) then
sed -i -e '/^include.*scafacos.*$/d' ../Makefile.package.settings
fi
fi

View File

@ -0,0 +1,488 @@
/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
http://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 author: Rene Halver (JSC)
------------------------------------------------------------------------- */
#include <cstdio>
#include <cstring>
#include "fix_scafacos.h"
#include "atom.h"
#include "comm.h"
#include "update.h"
#include "neighbor.h"
#include "domain.h"
#include "force.h"
#include "pair.h"
#include "modify.h"
#include "compute.h"
#include "memory.h"
#include "error.h"
// ScaFaCoS library
#include <iostream>
#include <sstream>
#include <string>
#include "fcs.h"
#include "universe.h"
using namespace LAMMPS_NS;
using namespace FixConst;
#define INVOKED_PERATOM 8
/* ---------------------------------------------------------------------- */
FixScafacos::FixScafacos(LAMMPS *lmp, int narg, char **arg) :
Fix(lmp, narg, arg)
{
// form of fix string:
// fix <#> <group> scafacos <method> [ tolerance <type> <value> ]
if (narg < 4) error->all(FLERR,"Illegal fix scafacos command");
// read method from fix input
method = arg[3];
// check if fmm method was chosen
fmm_chosen = !strcmp(method.c_str(),"fmm");
int arg_index = 4;
tolerance_set = false;
while(arg_index < narg)
{
// check if tolerance option is set
if (strcmp(arg[arg_index],"tolerance") == 0)
{
tolerance_set = true;
++arg_index;
if (strcmp(arg[arg_index],"energy") == 0)
tolerance_type = FCS_TOLERANCE_TYPE_ENERGY;
else if (strcmp(arg[arg_index],"energy_rel") == 0)
tolerance_type = FCS_TOLERANCE_TYPE_ENERGY_REL;
else if (strcmp(arg[arg_index],"field") == 0)
tolerance_type = FCS_TOLERANCE_TYPE_FIELD;
else if (strcmp(arg[arg_index],"field_rel") == 0)
tolerance_type = FCS_TOLERANCE_TYPE_FIELD_REL;
else if (strcmp(arg[arg_index],"potential") == 0)
tolerance_type = FCS_TOLERANCE_TYPE_POTENTIAL;
else if (strcmp(arg[arg_index],"potential_rel") == 0)
tolerance_type = FCS_TOLERANCE_TYPE_POTENTIAL_REL;
else
error->all(FLERR,"Illegal fix scafacos command");
++arg_index;
tolerance_value = atof(arg[arg_index]);
++arg_index;
}
else
error->all(FLERR,"Illegal fix scafacos command");
}
}
/* ---------------------------------------------------------------------- */
FixScafacos::~FixScafacos()
{
memory->destroy(pot);
memory->destroy(field);
}
/* ---------------------------------------------------------------------- */
int FixScafacos::setmask()
{
int mask = 0;
//mask |= INITIAL_INTEGRATE;
//mask |= FINAL_INTEGRATE;
mask |= PRE_REVERSE;
mask |= POST_FORCE;
mask |= MIN_POST_FORCE;
mask |= THERMO_ENERGY;
return mask;
}
/* ---------------------------------------------------------------------- */
void FixScafacos::init()
{
// error checks
if (domain->dimension == 2)
error->all(FLERR,"Fix scafacos requires 3d problem");
rank = comm->me;
int nlocal = 0;
int proc_grid[3] = {comm->procgrid[0], comm->procgrid[1], comm->procgrid[2]};
int myloc[3] = {comm->myloc[0], comm->myloc[1], comm->myloc[2]};
// call ScaFaCoS init
// TODO: check if universe->uworld is a good idea for computations
result = fcs_init(&fcs,method.c_str(),universe->uworld);
if (!check_result(result, rank)) return;
setup_handle();
nlocal = atom -> nlocal;
// get particle data
x = &atom->x[0][0];
q = atom->q;
if (tolerance_set)
{
result = fcs_set_tolerance(fcs,tolerance_type,tolerance_value);
if (!check_result(result, rank)) return;
}
// print out parameters within handle
if (rank == 0) fcs_print_parameters(fcs);
// call the tuning routine (needs to be redone, if critical system parameters should change)
result = fcs_tune(fcs,nlocal,x,q);
if (!check_result(result, rank)) return;
// allocate arrays larger in order to avoid fast reallocation due to fluctuations
local_array_size = (int)(1.25 * (double)nlocal);
// allocate new result arrays
memory->create(pot,local_array_size,"scafacos:potential");
memory->create(field,local_array_size*3,"scafacos:field");
// set result vectors to zero
for ( int i = 0; i < local_array_size; ++i)
{
pot[i] = 0.0;
field[3*i] = field[3*i+1] = field[3*i+2] = 0.0;
}
}
/* ---------------------------------------------------------------------- */
void FixScafacos::init_list(int id, NeighList *ptr)
{
}
/* ---------------------------------------------------------------------- */
void FixScafacos::setup(int vflag)
{
post_force(vflag);
}
/* ---------------------------------------------------------------------- */
void FixScafacos::min_setup(int vflag)
{
post_force(vflag);
}
/* ---------------------------------------------------------------------- */
void FixScafacos::setup_pre_reverse(int eflag, int vflag)
{
pre_reverse(eflag,vflag);
}
/* ----------------------------------------------------------------------
integrate electronic degrees of freedom
------------------------------------------------------------------------- */
void FixScafacos::initial_integrate(int vflag) {}
/* ----------------------------------------------------------------------
store eflag, so can use it in post_force to tally per-atom energies
------------------------------------------------------------------------- */
void FixScafacos::pre_reverse(int eflag, int vflag)
{
int eflag_caller = eflag;
}
/* ---------------------------------------------------------------------- */
void FixScafacos::post_force(int vflag)
{
int nlocal;
nlocal = atom->nlocal;
x = &atom->x[0][0];
q = atom->q;
// check if box has changed since the last call of fcs_tune, if yes, call fcs_tune
if (box_has_changed())
{
setup_handle();
// print out parameters within handle
if (rank == 0)
{
std::cout << " updated ScaFaCoS handle: " << std::endl;
fcs_print_parameters(fcs);
}
// call the tuning routine (needs to be redone, if critical system parameters should change)
result = fcs_tune(fcs,nlocal,x,q);
if (!check_result(result, rank)) return;
}
/*
if (fmm_chosen && phantom_particle)
for (int i = 0; i < nlocal; ++i)
{
std::cout << rank << " " << i << " " << x[3*i] << " " << x[3*i+1] << " " << x[3*i+2] << " " << q[i] << std::endl;
}
*/
// check if arrays for potentials and field are still large enough for the number of particles on process
if (nlocal > local_array_size)
{
// allocate arrays larger in order to avoid fast reallocation due to fluctuations
local_array_size = (int)(1.25 * (double)nlocal);
// destroy old result arrays
memory->destroy(pot);
memory->destroy(field);
// allocate result arrays
memory->create(pot,local_array_size,"scafacos:potential");
memory->create(field,3*local_array_size,"scafacos:field");
}
// set result vectors to zero
for ( int i = 0; i < nlocal; ++i)
{
pot[i] = 0.0;
field[3*i] = field[3*i+1] = field[3*i+2] = 0.0;
}
// compute Coulomb
fcs_run(fcs, nlocal, x, q, field, pot);
if(!check_result(result,rank)) return;
double E_coul_loc = 0.0;
// no reduction required (?)
//double E_coul = 0.0;
for (int i = 0; i < atom->nlocal; ++i)
{
//std::cout << atom->f[i][0] << " " << field[3*i] << " " << q[i] << std::endl;
atom->f[i][0] += field[3*i] * q[i];
atom->f[i][1] += field[3*i+1] * q[i];
atom->f[i][2] += field[3*i+2] * q[i];
E_coul_loc += 0.5 * q[i] * pot[i];
}
// not required (?)
// MPI_ALLREDUCE(&E_coul_loc, &E_coul, 1, MPI_DOUBLE, MPI_SUM, universe->uworld);
force->pair->eng_coul += E_coul_loc;
}
/* ---------------------------------------------------------------------- */
void FixScafacos::min_post_force(int vflag)
{
post_force(vflag);
}
/* ----------------------------------------------------------------------
integrate electronic degrees of freedom
------------------------------------------------------------------------- */
void FixScafacos::final_integrate() {}
/* ---------------------------------------------------------------------- */
void FixScafacos::reset_dt()
{
//dtv = update->dt;
//dtf = 0.5 * update->dt * force->ftm2v;
}
/* ----------------------------------------------------------------------
DFTB energy from LATTE
------------------------------------------------------------------------- */
double FixScafacos::compute_scalar()
{
}
/* ----------------------------------------------------------------------
memory usage of local arrays
------------------------------------------------------------------------- */
double FixScafacos::memory_usage()
{
double bytes = 0.0;
bytes += local_array_size * sizeof(double);
bytes += local_array_size * 3 * sizeof(double);
return bytes;
}
/* ----------------------------------------------------------------------
setup of ScaFaCoS handle with common parameters
------------------------------------------------------------------------- */
void FixScafacos::setup_handle()
{
// store periodicity
periodicity[0] = domain->xperiodic;
periodicity[1] = domain->yperiodic;
periodicity[2] = domain->zperiodic;
// store offset of the system
offset[0] = domain->boundary[0][0];
offset[1] = domain->boundary[1][0];
offset[2] = domain->boundary[2][0];
// calculate box vectors
box_x[0] = domain->prd[0];
box_x[1] = box_x[2] = 0.0;
box_y[1] = domain->prd[1];
box_y[0] = box_y[2] = 0.0;
box_z[2] = domain->prd[2];
box_z[1] = box_z[0] = 0.0;
total_particles = atom->natoms;
// TODO: for now disable short-range calculations within LAMMPS
near_field_flag = 0;
// enter all parameters required to ScaFaCoS handle
result = fcs_set_box_a(fcs, box_x);
if (!check_result(result, rank)) return;
result = fcs_set_box_b(fcs, box_y);
if (!check_result(result, rank)) return;
result = fcs_set_box_c(fcs, box_z);
if (!check_result(result, rank)) return;
result = fcs_set_box_origin(fcs, offset);
if (!check_result(result, rank)) return;
result = fcs_set_periodicity(fcs, periodicity);
if (!check_result(result, rank)) return;
result = fcs_set_near_field_flag(fcs, near_field_flag);
if (!check_result(result, rank)) return;
result = fcs_set_total_particles(fcs, atom->natoms);
if (!check_result(result, rank)) return;
}
/* ----------------------------------------------------------------------
check if box parameters changed, requiring a new call to fcs_tune
------------------------------------------------------------------------- */
bool FixScafacos::box_has_changed()
{
bool changed = false;
double n_periodicity[3];
double n_offset[3];
double n_box_x[3];
double n_box_y[3];
double n_box_z[3];
int n_total_particles;
// store periodicity
n_periodicity[0] = domain->xperiodic;
n_periodicity[1] = domain->yperiodic;
n_periodicity[2] = domain->zperiodic;
// store offset of the system
n_offset[0] = domain->boundary[0][0];
n_offset[1] = domain->boundary[1][0];
n_offset[2] = domain->boundary[2][0];
// calculate box vectors
n_box_x[0] = domain->prd[0];
n_box_x[1] = n_box_x[2] = 0.0;
n_box_y[1] = domain->prd[1];
n_box_y[0] = n_box_y[2] = 0.0;
n_box_z[2] = domain->prd[2];
n_box_z[1] = n_box_z[0] = 0.0;
n_total_particles = atom->natoms;
changed = changed ||
( n_periodicity[0] != periodicity[0] ) ||
( n_periodicity[1] != periodicity[1] ) ||
( n_periodicity[2] != periodicity[2] ) ||
( n_offset[0] != offset[0] ) ||
( n_offset[1] != offset[1] ) ||
( n_offset[2] != offset[2] ) ||
( n_box_x[0] != box_x[0] ) ||
( n_box_x[1] != box_x[1] ) ||
( n_box_x[2] != box_x[2] ) ||
( n_box_y[0] != box_y[0] ) ||
( n_box_y[1] != box_y[1] ) ||
( n_box_y[2] != box_y[2] ) ||
( n_box_z[0] != box_z[0] ) ||
( n_box_z[1] != box_z[1] ) ||
( n_box_z[2] != box_z[2] ) ||
( n_total_particles != total_particles );
/*
if (changed)
{
std::cout << rank << " " << "n_per_x : per_x" << n_periodicity[0] << " " << periodicity[0] << std::endl;
std::cout << rank << " " << "n_per_y : per_y" << n_periodicity[1] << " " << periodicity[1] << std::endl;
std::cout << rank << " " << "n_per_z : per_z" << n_periodicity[2] << " " << periodicity[2] << std::endl;
std::cout << rank << " " << "n_off_x : off_x" << n_offset[0] << " " << offset[0] << std::endl;
std::cout << rank << " " << "n_off_y : off_y" << n_offset[1] << " " << offset[1] << std::endl;
std::cout << rank << " " << "n_off_z : off_z" << n_offset[2] << " " << offset[2] << std::endl;
std::cout << rank << " " << "n_bx_x : bx_x" << n_box_x[0] << " " << box_x[0] << std::endl;
std::cout << rank << " " << "n_bx_y : bx_y" << n_box_x[1] << " " << box_x[1] << std::endl;
std::cout << rank << " " << "n_bx_z : bx_z" << n_box_x[2] << " " << box_x[2] << std::endl;
std::cout << rank << " " << "n_by_x : by_x" << n_box_y[0] << " " << box_y[0] << std::endl;
std::cout << rank << " " << "n_by_y : by_y" << n_box_y[1] << " " << box_y[1] << std::endl;
std::cout << rank << " " << "n_by_z : by_z" << n_box_y[2] << " " << box_y[2] << std::endl;
std::cout << rank << " " << "n_bz_x : bz_x" << n_box_z[0] << " " << box_z[0] << std::endl;
std::cout << rank << " " << "n_bz_y : bz_y" << n_box_z[1] << " " << box_z[1] << std::endl;
std::cout << rank << " " << "n_bz_z : bz_z" << n_box_z[2] << " " << box_z[2] << std::endl;
std::cout << rank << " " << "n_total : total" << n_total_particles << " " << total_particles << std::endl;
}
*/
return changed;
}
/* ----------------------------------------------------------------------
check of ScaFaCoS result
------------------------------------------------------------------------- */
bool FixScafacos::check_result(FCSResult result, int comm_rank)
{
if (result)
{
std::cout << "ScaFaCoS ERROR: Caught error on task " << comm_rank << "!" << std::endl;
std::string err_msg;
std::stringstream ss;
ss << fcs_result_get_function(result) << "\n" << fcs_result_get_message(result) << "\n";
err_msg = ss.str();
error -> all(FLERR, err_msg.c_str());
fcs_result_destroy(result);
}
return true;
}

164
src/SCAFACOS/fix_scafacos.h Normal file
View File

@ -0,0 +1,164 @@
/* -*- c++ -*- ----------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
http://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.
------------------------------------------------------------------------- */
#ifdef FIX_CLASS
FixStyle(scafacos,FixScafacos)
#else
#ifndef LMP_FIX_SCAFACOS_H
#define LMP_FIX_SCAFACOS_H
#include "fix.h"
#include "fcs.h"
#include <string>
namespace LAMMPS_NS {
class FixScafacos : public Fix {
public:
FixScafacos(class LAMMPS *, int, char **);
virtual ~FixScafacos();
int setmask();
void init();
void init_list(int, NeighList*);
void setup(int);
void min_setup(int);
void setup_pre_reverse(int, int);
void initial_integrate(int);
void pre_reverse(int, int);
void post_force(int);
void min_post_force(int);
void final_integrate();
void reset_dt();
double compute_scalar();
double memory_usage();
protected:
std::string method;
// MPI rank
int rank;
// source arrays for positions and charges
double *x, *q;
// result arrays for potentials and field
double *pot, *field;
// box vectors for each dimension
fcs_float box_x[3], box_y[3], box_z[3];
// offset of the box from the origin
fcs_float offset[3];
// periodicity of the system
fcs_int periodicity[3];
// ScaFaCoS handle
FCS fcs;
// ScaFaCoS result variable
FCSResult result;
// function to check results
bool check_result(FCSResult, int);
// function to set up handle with common parameters
void setup_handle();
// function to check if the box parameters changed, so that a new tuning step is required
bool box_has_changed();
// store total number of particles (to check if tune needs to be called again)
fcs_int total_particles;
// store number of local particles (to be able to readjust the size of result arrays, when needed)
int local_array_size;
// should the near field calculations be computed by LAMMPS?
fcs_int near_field_flag;
// type of accuracy chosen (if customized)
fcs_int tolerance_type;
// value of tolerance
fcs_float tolerance_value;
// is tolerance set?
bool tolerance_set;
// check if fmm is chosen (ghost particles, since the implementation needs at least 1 particle on each process!)
bool fmm_chosen;
// FMM: fmm particle array size
int fmm_array_size;
};
}
#endif
#endif
/* ERROR/WARNING messages:
E: Must use units metal with fix latte command
UNDOCUMENTED
E: Fix latte currently runs only in serial
UNDOCUMENTED
E: LAMMPS is linked against incompatible LATTE library
UNDOCUMENTED
E: Illegal ... command
Self-explanatory. Check the input script syntax and compare to the
documentation for the command. You can use -echo screen as a
command-line option when running LAMMPS to see the offending line.
E: Fix latte does not yet support a LAMMPS calculation of a Coulomb potential
UNDOCUMENTED
E: Could not find fix latte compute ID
UNDOCUMENTED
E: Fix latte compute ID does not compute pe/atom
UNDOCUMENTED
E: Fix latte requires 3d problem
UNDOCUMENTED
E: Fix latte cannot compute Coulomb potential
UNDOCUMENTED
E: Fix latte requires 3d simulation
UNDOCUMENTED
W: Fix latte should come after all other integration fixes
UNDOCUMENTED
E: Internal LATTE problem
UNDOCUMENTED
*/