Merge branch 'master' into programmer-guide-updates

# Conflicts:
#	doc/lammps.1
This commit is contained in:
Axel Kohlmeyer
2021-09-09 23:34:46 -04:00
37 changed files with 358 additions and 287 deletions

View File

@ -36,7 +36,11 @@ find_package(Git)
# by default, install into $HOME/.local (not /usr/local), so that no root access (and sudo!!) is needed
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "$ENV{HOME}/.local" CACHE PATH "Default install path" FORCE)
if((CMAKE_SYSTEM_NAME STREQUAL "Windows") AND (NOT CMAKE_CROSSCOMPILING))
set(CMAKE_INSTALL_PREFIX "$ENV{USERPROFILE}/LAMMPS" CACHE PATH "Default install path" FORCE)
else()
set(CMAKE_INSTALL_PREFIX "$ENV{HOME}/.local" CACHE PATH "Default install path" FORCE)
endif()
endif()
# If enabled, no need to use LD_LIBRARY_PATH / DYLD_LIBRARY_PATH when installed
@ -90,6 +94,10 @@ endif()
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF CACHE BOOL "Use compiler extensions")
# ugly hack for MSVC which by default always reports an old C++ standard in the __cplusplus macro
if(MSVC)
add_compile_options(/Zc:__cplusplus)
endif()
# export all symbols when building a .dll file on windows
if((CMAKE_SYSTEM_NAME STREQUAL "Windows") AND BUILD_SHARED_LIBS)

View File

@ -141,7 +141,7 @@ to the
code that were used during the run. Available keywords
for styles are "both", "none", "screen", or "log". Any other keyword
will be considered a file name to write the detailed citation info to
instead of logfile or screen. Default is the "log" style where there
instead of logfile or screen. Default is the "log" style where there
is a short summary in the screen output and detailed citations
in BibTeX format in the logfile. The option "both" selects the detailed
output for both, "none", the short output for both, and "screen" will
@ -234,7 +234,7 @@ the standard output. If <file name> is "none", (most) screen
output will be suppressed. In multi-partition mode only
some high-level all-partition information is written to the
screen or "<file name>" file, the remainder is written in a
per-partition file "screen.N" or "<file name>.N"
per-partition file "screen.N" or "<file name>.N"
with "N" being the respective partition number, and unless
overridden by the \-pscreen flag (see above).
.TP

View File

@ -206,6 +206,9 @@ Convenience functions
Customized standard functions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. doxygenfunction:: binary_search
:project: progguide
.. doxygenfunction:: merge_sort
:project: progguide

View File

@ -508,11 +508,6 @@ class PyLammps(object):
:py:attr:`PyLammps.last_run`.
"""
output = self.__getattr__('run')(*args, **kwargs)
comm = self.lmp.get_mpi_comm()
if comm:
output = self.lmp.comm.bcast(output, root=0)
self.runs += get_thermo_data(output)
return output
@ -643,7 +638,7 @@ class PyLammps(object):
return [x.strip() for x in value.split('=')]
def _parse_info_system(self, output):
lines = output[6:-2]
lines = output[5:-2]
system = {}
for line in lines:
@ -704,7 +699,7 @@ class PyLammps(object):
return system
def _parse_info_communication(self, output):
lines = output[6:-3]
lines = output[5:-3]
comm = {}
for line in lines:
@ -725,7 +720,7 @@ class PyLammps(object):
return comm
def _parse_element_list(self, output):
lines = output[6:-3]
lines = output[5:-3]
elements = []
for line in lines:
@ -737,7 +732,7 @@ class PyLammps(object):
return elements
def _parse_groups(self, output):
lines = output[6:-3]
lines = output[5:-3]
groups = []
group_pattern = re.compile(r"(?P<name>.+) \((?P<type>.+)\)")
@ -784,6 +779,10 @@ class PyLammps(object):
self.command(cmd)
output = capture.output
comm = self.lmp.get_mpi_comm()
if comm:
output = self.lmp.comm.bcast(output, root=0)
if 'verbose' in kwargs and kwargs['verbose']:
print(output)

View File

@ -163,8 +163,8 @@ action fix_dpd_energy_kokkos.cpp fix_dpd_energy.cpp
action fix_dpd_energy_kokkos.h fix_dpd_energy.h
action fix_rx_kokkos.cpp fix_rx.cpp
action fix_rx_kokkos.h fix_rx.h
action gridcomm_kokkos.cpp gridcomm.cpp
action gridcomm_kokkos.h gridcomm.h
action gridcomm_kokkos.cpp fft3d.h
action gridcomm_kokkos.h fft3d.h
action improper_class2_kokkos.cpp improper_class2.cpp
action improper_class2_kokkos.h improper_class2.h
action improper_harmonic_kokkos.cpp improper_harmonic.cpp

View File

@ -51,6 +51,7 @@ FixBondCreate::FixBondCreate(LAMMPS *lmp, int narg, char **arg) :
nevery = utils::inumeric(FLERR,arg[3],false,lmp);
if (nevery <= 0) error->all(FLERR,"Illegal fix bond/create command");
dynamic_group_allow = 1;
force_reneighbor = 1;
next_reneighbor = -1;
vector_flag = 1;

View File

@ -47,13 +47,18 @@ using namespace FixConst;
// socket interface
#ifndef _WIN32
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <netdb.h>
#else
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <io.h>
#include <windows.h>
#endif
#define MSGLEN 12

View File

@ -1029,12 +1029,12 @@ void Balance::tally(int dim, int n, double *split)
if (wtflag) {
weight = fixstore->vstore;
for (int i = 0; i < nlocal; i++) {
index = binary(x[i][dim],n,split);
index = utils::binary_search(x[i][dim],n,split);
onecost[index] += weight[i];
}
} else {
for (int i = 0; i < nlocal; i++) {
index = binary(x[i][dim],n,split);
index = utils::binary_search(x[i][dim],n,split);
onecost[index] += 1.0;
}
}
@ -1131,16 +1131,16 @@ double Balance::imbalance_splits()
if (wtflag) {
weight = fixstore->vstore;
for (int i = 0; i < nlocal; i++) {
ix = binary(x[i][0],nx,xsplit);
iy = binary(x[i][1],ny,ysplit);
iz = binary(x[i][2],nz,zsplit);
ix = utils::binary_search(x[i][0],nx,xsplit);
iy = utils::binary_search(x[i][1],ny,ysplit);
iz = utils::binary_search(x[i][2],nz,zsplit);
proccost[iz*nx*ny + iy*nx + ix] += weight[i];
}
} else {
for (int i = 0; i < nlocal; i++) {
ix = binary(x[i][0],nx,xsplit);
iy = binary(x[i][1],ny,ysplit);
iz = binary(x[i][2],nz,zsplit);
ix = utils::binary_search(x[i][0],nx,xsplit);
iy = utils::binary_search(x[i][1],ny,ysplit);
iz = utils::binary_search(x[i][2],nz,zsplit);
proccost[iz*nx*ny + iy*nx + ix] += 1.0;
}
}
@ -1161,40 +1161,6 @@ double Balance::imbalance_splits()
return imbalance;
}
/* ----------------------------------------------------------------------
binary search for where value falls in N-length vec
note that vec actually has N+1 values, but ignore last one
values in vec are monotonically increasing, but adjacent values can be ties
value may be outside range of vec limits
always return index from 0 to N-1 inclusive
return 0 if value < vec[0]
reutrn N-1 if value >= vec[N-1]
return index = 1 to N-2 inclusive if vec[index] <= value < vec[index+1]
note that for adjacent tie values, index of lower tie is not returned
since never satisfies 2nd condition that value < vec[index+1]
------------------------------------------------------------------------- */
int Balance::binary(double value, int n, double *vec)
{
int lo = 0;
int hi = n-1;
if (value < vec[lo]) return lo;
if (value >= vec[hi]) return hi;
// insure vec[lo] <= value < vec[hi] at every iteration
// done when lo,hi are adjacent
int index = (lo+hi)/2;
while (lo < hi-1) {
if (value < vec[index]) hi = index;
else if (value >= vec[index]) lo = index;
index = (lo+hi)/2;
}
return index;
}
/* ----------------------------------------------------------------------
write dump snapshot of line segments in Pizza.py mdump mesh format
write xy lines around each proc's sub-domain for 2d

View File

@ -84,7 +84,6 @@ class Balance : public Command {
void shift_setup_static(char *);
void tally(int, int, double *);
int adjust(int, double *);
int binary(double, int, double *);
#ifdef BALANCE_DEBUG
void debug_shift_output(int, int, int, double *);
#endif

View File

@ -782,13 +782,13 @@ int Comm::coord2proc(double *x, int &igx, int &igy, int &igz)
} else if (layout == Comm::LAYOUT_NONUNIFORM) {
if (triclinic == 0) {
igx = binary((x[0]-boxlo[0])/prd[0],procgrid[0],xsplit);
igy = binary((x[1]-boxlo[1])/prd[1],procgrid[1],ysplit);
igz = binary((x[2]-boxlo[2])/prd[2],procgrid[2],zsplit);
igx = utils::binary_search((x[0]-boxlo[0])/prd[0],procgrid[0],xsplit);
igy = utils::binary_search((x[1]-boxlo[1])/prd[1],procgrid[1],ysplit);
igz = utils::binary_search((x[2]-boxlo[2])/prd[2],procgrid[2],zsplit);
} else {
igx = binary(x[0],procgrid[0],xsplit);
igy = binary(x[1],procgrid[1],ysplit);
igz = binary(x[2],procgrid[2],zsplit);
igx = utils::binary_search(x[0],procgrid[0],xsplit);
igy = utils::binary_search(x[1],procgrid[1],ysplit);
igz = utils::binary_search(x[2],procgrid[2],zsplit);
}
}
@ -802,36 +802,6 @@ int Comm::coord2proc(double *x, int &igx, int &igy, int &igz)
return grid2proc[igx][igy][igz];
}
/* ----------------------------------------------------------------------
binary search for value in N-length ascending vec
value may be outside range of vec limits
always return index from 0 to N-1 inclusive
return 0 if value < vec[0]
reutrn N-1 if value >= vec[N-1]
return index = 1 to N-2 if vec[index] <= value < vec[index+1]
------------------------------------------------------------------------- */
int Comm::binary(double value, int n, double *vec)
{
int lo = 0;
int hi = n-1;
if (value < vec[lo]) return lo;
if (value >= vec[hi]) return hi;
// insure vec[lo] <= value < vec[hi] at every iteration
// done when lo,hi are adjacent
int index = (lo+hi)/2;
while (lo < hi-1) {
if (value < vec[index]) hi = index;
else if (value >= vec[index]) lo = index;
index = (lo+hi)/2;
}
return index;
}
/* ----------------------------------------------------------------------
partition a global regular grid into one brick-shaped sub-grid per proc
if grid point is inside my sub-domain I own it,

View File

@ -98,7 +98,6 @@ class Comm : protected Pointers {
virtual void forward_comm_array(int, double **) = 0;
virtual int exchange_variable(int, double *, double *&) = 0;
int binary(double, int, double *);
// map a point to a processor, based on current decomposition

View File

@ -64,7 +64,7 @@ vstore(nullptr), astore(nullptr), rbuf(nullptr)
if (flavor == PERATOM) {
restart_peratom = utils::inumeric(FLERR,arg[4],false,lmp);
nvalues = utils::inumeric(FLERR,arg[5],false,lmp);
if (restart_peratom < 0 or restart_peratom > 1 || nvalues <= 0)
if ((restart_peratom < 0) || (restart_peratom > 1) || (nvalues <= 0))
error->all(FLERR,"Illegal fix store command");
vecflag = 0;
if (nvalues == 1) vecflag = 1;

View File

@ -51,6 +51,9 @@
#include <map>
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#define PSAPI_VERSION 1
#include <windows.h>
#include <cstdint>

View File

@ -55,7 +55,9 @@
#include <cstring>
#include <map>
#if !defined(_WIN32)
#if defined(_WIN32)
#include <io.h>
#else
#include <unistd.h> // for isatty()
#endif

View File

@ -288,12 +288,6 @@ union ubuf {
#define _noopt
#endif
// settings to enable LAMMPS to build under Windows
#ifdef _WIN32
#include "lmpwindows.h"
#endif
// suppress unused parameter warning
#define LMP_UNUSED_PARAM(x) (void) (x)

View File

@ -11,17 +11,8 @@
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
#include <ciso646>
#if !defined(__MINGW32__)
#include "erf.h"
#endif
#include <cmath>
#include <cstring>
#include <direct.h>
// LAMMPS uses usleep with 100 ms arguments, no microsecond precision needed
#if !defined(__MINGW32__)
#include "sleep.h"
#endif
// some symbols have different names in Windows

View File

@ -1,4 +1,3 @@
// clang-format off
/* -*- c++ -*- ----------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
@ -14,8 +13,6 @@
#include "my_page.h"
#include <cstdlib>
#if defined(LMP_INTEL) && !defined(LAMMPS_MEMALIGN) && !defined(_WIN32)
#define LAMMPS_MEMALIGN 64
#endif
@ -60,12 +57,12 @@ using namespace LAMMPS_NS;
* Need to call init() before use to define allocation settings */
template <class T>
MyPage<T>::MyPage() : ndatum(0), nchunk(0), pages(nullptr), page(nullptr),
npage(0), ipage(-1), index(-1), maxchunk(-1),
pagesize(-1), pagedelta(1), errorflag(0) {};
MyPage<T>::MyPage() :
ndatum(0), nchunk(0), pages(nullptr), page(nullptr), npage(0), ipage(-1), index(-1),
maxchunk(-1), pagesize(-1), pagedelta(1), errorflag(0){};
template <class T>
MyPage<T>::~MyPage() {
template <class T> MyPage<T>::~MyPage()
{
deallocate();
}
@ -79,27 +76,26 @@ MyPage<T>::~MyPage() {
* \param user_pagedelta Number of pages to allocate with one malloc
* \return 1 if there were invalid parameters, 2 if there was an allocation error or 0 if successful */
template<class T>
int MyPage<T>::init(int user_maxchunk, int user_pagesize,
int user_pagedelta) {
maxchunk = user_maxchunk;
pagesize = user_pagesize;
pagedelta = user_pagedelta;
template <class T> int MyPage<T>::init(int user_maxchunk, int user_pagesize, int user_pagedelta)
{
maxchunk = user_maxchunk;
pagesize = user_pagesize;
pagedelta = user_pagedelta;
if (maxchunk <= 0 || pagesize <= 0 || pagedelta <= 0) return 1;
if (maxchunk > pagesize) return 1;
if (maxchunk <= 0 || pagesize <= 0 || pagedelta <= 0) return 1;
if (maxchunk > pagesize) return 1;
// free storage if re-initialized
// free storage if re-initialized
deallocate();
deallocate();
// initial page allocation
// initial page allocation
allocate();
if (errorflag) return 2;
reset();
return 0;
}
allocate();
if (errorflag) return 2;
reset();
return 0;
}
/** Pointer to location that can store N items.
*
@ -110,8 +106,8 @@ int MyPage<T>::init(int user_maxchunk, int user_pagesize,
* \param n number of items for which storage is requested
* \return memory location or null pointer, if error or allocation failed */
template <class T>
T *MyPage<T>::get(int n) {
template <class T> T *MyPage<T>::get(int n)
{
if (n > maxchunk) {
errorflag = 1;
return nullptr;
@ -120,7 +116,7 @@ T *MyPage<T>::get(int n) {
nchunk++;
// return pointer from current page
if (index+n <= pagesize) {
if (index + n <= pagesize) {
int start = index;
index += n;
return &page[start];
@ -137,11 +133,10 @@ T *MyPage<T>::get(int n) {
return &page[0];
}
/** Reset state of memory pool without freeing any memory */
template <class T>
void MyPage<T>::reset() {
template <class T> void MyPage<T>::reset()
{
ndatum = nchunk = 0;
index = ipage = 0;
page = (pages != nullptr) ? pages[ipage] : nullptr;
@ -150,23 +145,22 @@ void MyPage<T>::reset() {
/* ---------------------------------------------------------------------- */
template <class T>
void MyPage<T>::allocate() {
template <class T> void MyPage<T>::allocate()
{
npage += pagedelta;
pages = (T **) realloc(pages,npage*sizeof(T *));
pages = (T **) realloc(pages, npage * sizeof(T *));
if (!pages) {
errorflag = 2;
return;
}
for (int i = npage-pagedelta; i < npage; i++) {
for (int i = npage - pagedelta; i < npage; i++) {
#if defined(LAMMPS_MEMALIGN)
void *ptr;
if (posix_memalign(&ptr, LAMMPS_MEMALIGN, pagesize*sizeof(T)))
errorflag = 2;
if (posix_memalign(&ptr, LAMMPS_MEMALIGN, pagesize * sizeof(T))) errorflag = 2;
pages[i] = (T *) ptr;
#else
pages[i] = (T *) malloc(pagesize*sizeof(T));
pages[i] = (T *) malloc(pagesize * sizeof(T));
if (!pages[i]) errorflag = 2;
#endif
}
@ -174,8 +168,8 @@ void MyPage<T>::allocate() {
/** Free all allocated pages of this class instance */
template <class T>
void MyPage<T>::deallocate() {
template <class T> void MyPage<T>::deallocate()
{
reset();
for (int i = 0; i < npage; i++) free(pages[i]);
free(pages);
@ -186,9 +180,9 @@ void MyPage<T>::deallocate() {
// explicit instantiations
namespace LAMMPS_NS {
template class MyPage<int>;
template class MyPage<long>;
template class MyPage<long long>;
template class MyPage<double>;
template class MyPage<HyperOneCoeff>;
}
template class MyPage<int>;
template class MyPage<long>;
template class MyPage<long long>;
template class MyPage<double>;
template class MyPage<HyperOneCoeff>;
} // namespace LAMMPS_NS

View File

@ -1,4 +1,3 @@
// clang-format off
/* -*- c++ -*- ----------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
@ -50,7 +49,8 @@ using namespace LAMMPS_NS;
template <class T>
MyPoolChunk<T>::MyPoolChunk(int user_minchunk, int user_maxchunk, int user_nbin,
int user_chunkperpage, int user_pagedelta) {
int user_chunkperpage, int user_pagedelta)
{
minchunk = user_minchunk;
maxchunk = user_maxchunk;
nbin = user_nbin;
@ -68,13 +68,13 @@ MyPoolChunk<T>::MyPoolChunk(int user_minchunk, int user_maxchunk, int user_nbin,
// insure nbin*binsize spans minchunk to maxchunk inclusive
binsize = (maxchunk-minchunk+1) / nbin;
if (minchunk + nbin*binsize <= maxchunk) binsize++;
binsize = (maxchunk - minchunk + 1) / nbin;
if (minchunk + nbin * binsize <= maxchunk) binsize++;
freelist = nullptr;
for (int ibin = 0; ibin < nbin; ibin++) {
freehead[ibin] = -1;
chunksize[ibin] = minchunk + (ibin+1)*binsize - 1;
chunksize[ibin] = minchunk + (ibin + 1) * binsize - 1;
if (chunksize[ibin] > maxchunk) chunksize[ibin] = maxchunk;
}
@ -85,10 +85,10 @@ MyPoolChunk<T>::MyPoolChunk(int user_minchunk, int user_maxchunk, int user_nbin,
}
/** Destroy class instance and free all allocated memory */
template <class T>
MyPoolChunk<T>::~MyPoolChunk() {
delete [] freehead;
delete [] chunksize;
template <class T> MyPoolChunk<T>::~MyPoolChunk()
{
delete[] freehead;
delete[] chunksize;
if (npage) {
free(freelist);
for (int i = 0; i < npage; i++) free(pages[i]);
@ -102,9 +102,9 @@ MyPoolChunk<T>::~MyPoolChunk() {
* \param index Index of chunk in memory pool
* \return Pointer to requested chunk of storage */
template <class T>
T *MyPoolChunk<T>::get(int &index) {
int ibin = nbin-1;
template <class T> T *MyPoolChunk<T>::get(int &index)
{
int ibin = nbin - 1;
if (freehead[ibin] < 0) {
allocate(ibin);
if (errorflag) {
@ -116,10 +116,10 @@ T *MyPoolChunk<T>::get(int &index) {
ndatum += maxchunk;
nchunk++;
index = freehead[ibin];
int ipage = index/chunkperpage;
int ipage = index / chunkperpage;
int ientry = index % chunkperpage;
freehead[ibin] = freelist[index];
return &pages[ipage][ientry*chunksize[ibin]];
return &pages[ipage][ientry * chunksize[ibin]];
}
/** Return pointer/index of unused chunk of size N
@ -128,15 +128,15 @@ T *MyPoolChunk<T>::get(int &index) {
* \param index Index of chunk in memory pool
* \return Pointer to requested chunk of storage */
template <class T>
T *MyPoolChunk<T>::get(int n, int &index) {
template <class T> T *MyPoolChunk<T>::get(int n, int &index)
{
if (n < minchunk || n > maxchunk) {
errorflag = 3;
index = -1;
return nullptr;
}
int ibin = (n-minchunk) / binsize;
int ibin = (n - minchunk) / binsize;
if (freehead[ibin] < 0) {
allocate(ibin);
if (errorflag) {
@ -148,35 +148,34 @@ T *MyPoolChunk<T>::get(int n, int &index) {
ndatum += n;
nchunk++;
index = freehead[ibin];
int ipage = index/chunkperpage;
int ipage = index / chunkperpage;
int ientry = index % chunkperpage;
freehead[ibin] = freelist[index];
return &pages[ipage][ientry*chunksize[ibin]];
return &pages[ipage][ientry * chunksize[ibin]];
}
/** Put indexed chunk back into memory pool via free list
*
* \param index Memory chunk index returned by call to get() */
template <class T>
void MyPoolChunk<T>::put(int index) {
if (index < 0) return;
int ipage = index/chunkperpage;
int ibin = whichbin[ipage];
nchunk--;
ndatum -= chunksize[ibin];
freelist[index] = freehead[ibin];
freehead[ibin] = index;
}
template <class T> void MyPoolChunk<T>::put(int index)
{
if (index < 0) return;
int ipage = index / chunkperpage;
int ibin = whichbin[ipage];
nchunk--;
ndatum -= chunksize[ibin];
freelist[index] = freehead[ibin];
freehead[ibin] = index;
}
template <class T>
void MyPoolChunk<T>::allocate(int ibin) {
template <class T> void MyPoolChunk<T>::allocate(int ibin)
{
int oldpage = npage;
npage += pagedelta;
freelist = (int *) realloc(freelist,sizeof(int)*npage*chunkperpage);
pages = (T **) realloc(pages,sizeof(T *)*npage);
whichbin = (int *) realloc(whichbin,sizeof(int)*npage);
freelist = (int *) realloc(freelist, sizeof(int) * npage * chunkperpage);
pages = (T **) realloc(pages, sizeof(T *) * npage);
whichbin = (int *) realloc(whichbin, sizeof(int) * npage);
if (!freelist || !pages) {
errorflag = 2;
return;
@ -188,34 +187,32 @@ void MyPoolChunk<T>::allocate(int ibin) {
whichbin[i] = ibin;
#if defined(LAMMPS_MEMALIGN)
void *ptr;
if (posix_memalign(&ptr, LAMMPS_MEMALIGN,
sizeof(T)*chunkperpage*chunksize[ibin]))
if (posix_memalign(&ptr, LAMMPS_MEMALIGN, sizeof(T) * chunkperpage * chunksize[ibin]))
errorflag = 2;
pages[i] = (T *) ptr;
#else
pages[i] = (T *) malloc(sizeof(T)*chunkperpage*chunksize[ibin]);
pages[i] = (T *) malloc(sizeof(T) * chunkperpage * chunksize[ibin]);
if (!pages[i]) errorflag = 2;
#endif
}
// reset free list for unused chunks on new pages
freehead[ibin] = oldpage*chunkperpage;
for (int i = freehead[ibin]; i < npage*chunkperpage; i++) freelist[i] = i+1;
freelist[npage*chunkperpage-1] = -1;
freehead[ibin] = oldpage * chunkperpage;
for (int i = freehead[ibin]; i < npage * chunkperpage; i++) freelist[i] = i + 1;
freelist[npage * chunkperpage - 1] = -1;
}
/** Return total size of allocated pages
*
* \return total storage used in bytes */
template <class T>
double MyPoolChunk<T>::size() const {
double bytes = (double)npage*chunkperpage*sizeof(int);
bytes += (double)npage*sizeof(T *);
bytes += (double)npage*sizeof(int);
for (int i=0; i < npage; ++i)
bytes += (double)chunkperpage*chunksize[i]*sizeof(T);
template <class T> double MyPoolChunk<T>::size() const
{
double bytes = (double) npage * chunkperpage * sizeof(int);
bytes += (double) npage * sizeof(T *);
bytes += (double) npage * sizeof(int);
for (int i = 0; i < npage; ++i) bytes += (double) chunkperpage * chunksize[i] * sizeof(T);
return bytes;
}
@ -223,6 +220,6 @@ double MyPoolChunk<T>::size() const {
// explicit instantiations
namespace LAMMPS_NS {
template class MyPoolChunk<int>;
template class MyPoolChunk<double>;
}
template class MyPoolChunk<int>;
template class MyPoolChunk<double>;
} // namespace LAMMPS_NS

View File

@ -365,10 +365,10 @@ void Neighbor::init()
int icollection, jcollection;
// If collections not yet defined, create default map using types
if (not custom_collection_flag) {
if (!custom_collection_flag) {
ncollections = n;
interval_collection_flag = 0;
if (not type2collection)
if (!type2collection)
memory->create(type2collection,n+1,"neigh:type2collection");
for (i = 1; i <= n; i++)
type2collection[i] = i-1;
@ -390,7 +390,7 @@ void Neighbor::init()
for (j = 0; j < ncollections; j++)
cutcollectionsq[i][j] = 0.0;
if (not interval_collection_flag) {
if (!interval_collection_flag) {
finite_cut_flag = 0;
for (i = 1; i <= n; i++){
icollection = type2collection[i];
@ -419,7 +419,7 @@ void Neighbor::init()
finite_cut_flag = 0;
// Map types to collections
if (not type2collection)
if (!type2collection)
memory->create(type2collection,n+1,"neigh:type2collection");
for (i = 1; i <= n; i++)
@ -2557,7 +2557,7 @@ void Neighbor::modify_params(int narg, char **arg)
comm->ncollections_cutoff = 0;
interval_collection_flag = 0;
custom_collection_flag = 1;
if (not type2collection)
if (!type2collection)
memory->create(type2collection,ntypes+1,"neigh:type2collection");
// Erase previous mapping

View File

@ -173,7 +173,7 @@ void TextFileReader::next_dvector(double *list, int n)
}
ValueTokenizer values(line);
while (values.has_next()) { list[i++] = values.next_double(); }
while (values.has_next() && i < n) { list[i++] = values.next_double(); }
}
}

View File

@ -415,14 +415,14 @@ bigint Thermo::lost_check()
error->all(FLERR,"Too many total atoms");
// print notification, if future warnings will be ignored
int maxwarn = error->get_maxwarn();
bigint maxwarn = error->get_maxwarn();
if ((maxwarn > 0) && (warnbefore == 0) && (ntotal[1] > maxwarn)) {
warnbefore = 1;
if (comm->me == 0)
error->message(FLERR,"WARNING: Too many warnings: {} vs {}. All "
"future warnings will be suppressed",ntotal[1],maxwarn);
}
error->set_allwarn(ntotal[1]);
error->set_allwarn(MIN(MAXSMALLINT,ntotal[1]));
// no lost atoms, nothing else to do.
if (ntotal[0] == atom->natoms) return ntotal[0];

View File

@ -21,6 +21,9 @@
#include <cstring>
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <cstdint>
#else

View File

@ -32,6 +32,21 @@
#include <unistd.h> // for readlink
#endif
#if defined(__APPLE__)
#include <fcntl.h> // for fcntl
#include <sys/syslimits.h>
#endif
#if defined(_WIN32)
// target Windows version is Windows 7 and later
#if defined(_WIN32_WINNT)
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT _WIN32_WINNT_WIN7
#include <io.h>
#include <windows.h>
#endif
/*! \file utils.cpp */
/*
@ -149,6 +164,9 @@ std::string utils::getsyserror()
/** On Linux the folder /proc/self/fd holds symbolic links to the actual
* pathnames associated with each open file descriptor of the current process.
* On macOS the same kind of information can be obtained using ``fcntl(fd,F_GETPATH,buf)``.
* On Windows we use ``GetFinalPathNameByHandleA()`` which is available with
* Windows Vista and later.
*
* This function is used to provide a filename with error messages in functions
* where the filename is not passed as an argument, but the FILE * pointer.
@ -162,6 +180,20 @@ const char *utils::guesspath(char *buf, int len, FILE *fp)
// get pathname from /proc or copy (unknown)
if (readlink(fmt::format("/proc/self/fd/{}", fd).c_str(), buf, len - 1) <= 0)
strncpy(buf, "(unknown)", len - 1);
#elif defined(__APPLE__)
int fd = fileno(fp);
char filepath[PATH_MAX];
if (fcntl(fd, F_GETPATH, filepath) != -1)
strncpy(buf, filepath, len - 1);
else
strncpy(buf, "(unknown)", len - 1);
#elif defined(_WIN32)
char filepath[MAX_PATH];
HANDLE h = (HANDLE) _get_osfhandle(_fileno(fp));
if (GetFinalPathNameByHandleA(h, filepath, PATH_MAX, FILE_NAME_NORMALIZED) > 0)
strncpy(buf, filepath, len - 1);
else
strncpy(buf, "(unknown)", len - 1);
#else
strncpy(buf, "(unknown)", len - 1);
#endif
@ -1284,6 +1316,30 @@ int utils::date2num(const std::string &date)
return num;
}
/* binary search in vector of ascending doubles */
int utils::binary_search(const double needle, const int n, const double *haystack)
{
int lo = 0;
int hi = n - 1;
if (needle < haystack[lo]) return lo;
if (needle >= haystack[hi]) return hi;
// insure haystack[lo] <= needle < haystack[hi] at every iteration
// done when lo,hi are adjacent
int index = (lo + hi) / 2;
while (lo < hi - 1) {
if (needle < haystack[index])
hi = index;
else if (needle >= haystack[index])
lo = index;
index = (lo + hi) / 2;
}
return index;
}
/* ----------------------------------------------------------------------
* Merge sort part 1: Loop over sublists doubling in size with each iteration.
* Pre-sort small sublists with insertion sort for better overall performance.
@ -1412,26 +1468,26 @@ static int re_matchp(const char *text, re_t pattern, int *matchlen);
#define MAX_CHAR_CLASS_LEN 40 /* Max length of character-class buffer in. */
enum {
UNUSED,
DOT,
BEGIN,
END,
QUESTIONMARK,
STAR,
PLUS,
CHAR,
CHAR_CLASS,
INV_CHAR_CLASS,
DIGIT,
NOT_DIGIT,
INTEGER,
NOT_INTEGER,
FLOAT,
NOT_FLOAT,
ALPHA,
NOT_ALPHA,
WHITESPACE,
NOT_WHITESPACE /*, BRANCH */
RX_UNUSED,
RX_DOT,
RX_BEGIN,
RX_END,
RX_QUESTIONMARK,
RX_STAR,
RX_PLUS,
RX_CHAR,
RX_CHAR_CLASS,
RX_INV_CHAR_CLASS,
RX_DIGIT,
RX_NOT_DIGIT,
RX_INTEGER,
RX_NOT_INTEGER,
RX_FLOAT,
RX_NOT_FLOAT,
RX_ALPHA,
RX_NOT_ALPHA,
RX_WHITESPACE,
RX_NOT_WHITESPACE /*, BRANCH */
};
typedef struct regex_t {
@ -1483,7 +1539,7 @@ int re_matchp(const char *text, re_t pattern, int *matchlen)
{
*matchlen = 0;
if (pattern != 0) {
if (pattern[0].type == BEGIN) {
if (pattern[0].type == RX_BEGIN) {
return ((matchpattern(&pattern[1], text, matchlen)) ? 0 : -1);
} else {
int idx = -1;
@ -1518,22 +1574,22 @@ re_t re_compile(re_ctx_t context, const char *pattern)
switch (c) {
/* Meta-characters: */
case '^': {
re_compiled[j].type = BEGIN;
re_compiled[j].type = RX_BEGIN;
} break;
case '$': {
re_compiled[j].type = END;
re_compiled[j].type = RX_END;
} break;
case '.': {
re_compiled[j].type = DOT;
re_compiled[j].type = RX_DOT;
} break;
case '*': {
re_compiled[j].type = STAR;
re_compiled[j].type = RX_STAR;
} break;
case '+': {
re_compiled[j].type = PLUS;
re_compiled[j].type = RX_PLUS;
} break;
case '?': {
re_compiled[j].type = QUESTIONMARK;
re_compiled[j].type = RX_QUESTIONMARK;
} break;
/* Escaped character-classes (\s \w ...): */
@ -1545,39 +1601,39 @@ re_t re_compile(re_ctx_t context, const char *pattern)
switch (pattern[i]) {
/* Meta-character: */
case 'd': {
re_compiled[j].type = DIGIT;
re_compiled[j].type = RX_DIGIT;
} break;
case 'D': {
re_compiled[j].type = NOT_DIGIT;
re_compiled[j].type = RX_NOT_DIGIT;
} break;
case 'i': {
re_compiled[j].type = INTEGER;
re_compiled[j].type = RX_INTEGER;
} break;
case 'I': {
re_compiled[j].type = NOT_INTEGER;
re_compiled[j].type = RX_NOT_INTEGER;
} break;
case 'f': {
re_compiled[j].type = FLOAT;
re_compiled[j].type = RX_FLOAT;
} break;
case 'F': {
re_compiled[j].type = NOT_FLOAT;
re_compiled[j].type = RX_NOT_FLOAT;
} break;
case 'w': {
re_compiled[j].type = ALPHA;
re_compiled[j].type = RX_ALPHA;
} break;
case 'W': {
re_compiled[j].type = NOT_ALPHA;
re_compiled[j].type = RX_NOT_ALPHA;
} break;
case 's': {
re_compiled[j].type = WHITESPACE;
re_compiled[j].type = RX_WHITESPACE;
} break;
case 'S': {
re_compiled[j].type = NOT_WHITESPACE;
re_compiled[j].type = RX_NOT_WHITESPACE;
} break;
/* Escaped character, e.g. '.' or '$' */
default: {
re_compiled[j].type = CHAR;
re_compiled[j].type = RX_CHAR;
re_compiled[j].u.ch = pattern[i];
} break;
}
@ -1592,14 +1648,14 @@ re_t re_compile(re_ctx_t context, const char *pattern)
/* Look-ahead to determine if negated */
if (pattern[i + 1] == '^') {
re_compiled[j].type = INV_CHAR_CLASS;
re_compiled[j].type = RX_INV_CHAR_CLASS;
i += 1; /* Increment i to avoid including '^' in the char-buffer */
if (pattern[i + 1] == 0) /* incomplete pattern, missing non-zero char after '^' */
{
return 0;
}
} else {
re_compiled[j].type = CHAR_CLASS;
re_compiled[j].type = RX_CHAR_CLASS;
}
/* Copy characters inside [..] to buffer */
@ -1628,7 +1684,7 @@ re_t re_compile(re_ctx_t context, const char *pattern)
/* Other characters: */
default: {
re_compiled[j].type = CHAR;
re_compiled[j].type = RX_CHAR;
re_compiled[j].u.ch = c;
} break;
}
@ -1639,8 +1695,8 @@ re_t re_compile(re_ctx_t context, const char *pattern)
i += 1;
j += 1;
}
/* 'UNUSED' is a sentinel used to indicate end-of-pattern */
re_compiled[j].type = UNUSED;
/* 'RX_UNUSED' is a sentinel used to indicate end-of-pattern */
re_compiled[j].type = RX_UNUSED;
return (re_t) re_compiled;
}
@ -1753,31 +1809,31 @@ static int matchcharclass(char c, const char *str)
static int matchone(regex_t p, char c)
{
switch (p.type) {
case DOT:
case RX_DOT:
return matchdot(c);
case CHAR_CLASS:
case RX_CHAR_CLASS:
return matchcharclass(c, (const char *) p.u.ccl);
case INV_CHAR_CLASS:
case RX_INV_CHAR_CLASS:
return !matchcharclass(c, (const char *) p.u.ccl);
case DIGIT:
case RX_DIGIT:
return matchdigit(c);
case NOT_DIGIT:
case RX_NOT_DIGIT:
return !matchdigit(c);
case INTEGER:
case RX_INTEGER:
return matchint(c);
case NOT_INTEGER:
case RX_NOT_INTEGER:
return !matchint(c);
case FLOAT:
case RX_FLOAT:
return matchfloat(c);
case NOT_FLOAT:
case RX_NOT_FLOAT:
return !matchfloat(c);
case ALPHA:
case RX_ALPHA:
return matchalphanum(c);
case NOT_ALPHA:
case RX_NOT_ALPHA:
return !matchalphanum(c);
case WHITESPACE:
case RX_WHITESPACE:
return matchwhitespace(c);
case NOT_WHITESPACE:
case RX_NOT_WHITESPACE:
return !matchwhitespace(c);
default:
return (p.u.ch == c);
@ -1817,7 +1873,7 @@ static int matchplus(regex_t p, regex_t *pattern, const char *text, int *matchle
static int matchquestion(regex_t p, regex_t *pattern, const char *text, int *matchlen)
{
if (p.type == UNUSED) return 1;
if (p.type == RX_UNUSED) return 1;
if (matchpattern(pattern, text, matchlen)) return 1;
if (*text && matchone(p, *text++)) {
if (matchpattern(pattern, text, matchlen)) {
@ -1833,13 +1889,13 @@ static int matchpattern(regex_t *pattern, const char *text, int *matchlen)
{
int pre = *matchlen;
do {
if ((pattern[0].type == UNUSED) || (pattern[1].type == QUESTIONMARK)) {
if ((pattern[0].type == RX_UNUSED) || (pattern[1].type == RX_QUESTIONMARK)) {
return matchquestion(pattern[0], &pattern[2], text, matchlen);
} else if (pattern[1].type == STAR) {
} else if (pattern[1].type == RX_STAR) {
return matchstar(pattern[0], &pattern[2], text, matchlen);
} else if (pattern[1].type == PLUS) {
} else if (pattern[1].type == RX_PLUS) {
return matchplus(pattern[0], &pattern[2], text, matchlen);
} else if ((pattern[0].type == END) && pattern[1].type == UNUSED) {
} else if ((pattern[0].type == RX_END) && pattern[1].type == RX_UNUSED) {
return (text[0] == '\0');
}
(*matchlen)++;

View File

@ -408,7 +408,7 @@ namespace utils {
/*! Try to detect pathname from FILE pointer.
*
* Currently only supported on Linux, otherwise will report "(unknown)".
* Currently supported on Linux, macOS, and Windows, otherwise will report "(unknown)".
*
* \param buf storage buffer for pathname. output will be truncated if not large enough
* \param len size of storage buffer. output will be truncated to this length - 1
@ -541,6 +541,23 @@ namespace utils {
int date2num(const std::string &date);
/*! Binary search in a vector of ascending doubles of length N
*
* If the value is smaller than the smallest value in the vector, 0 is returned.
* If the value is larger or equal than the largest value in the vector, N-1 is returned.
* Otherwise the index that satisfies the condition
*
* haystack[index] <= value < haystack[index+1]
*
* is returned, i.e. a value from 1 to N-2. Note that if there are tied values in the
* haystack, always the larger index is returned as only that satisfied the condition.
*
* \param needle search value for which are are looking for the closest index
* \param n size of the haystack array
* \param haystack array with data in ascending order.
* \return index of value in the haystack array smaller or equal to needle */
int binary_search(const double needle, const int n, const double *haystack);
/*! Custom merge sort implementation
*
* This function provides a custom upward hybrid merge sort

View File

@ -22,6 +22,7 @@
#if !defined(WIN32_LEAN_AND_MEAN)
#define WIN32_LEAN_AND_MEAN
#endif
#include <direct.h>
#include <io.h>
#include <windows.h>
#define chdir(x) _chdir(x)

View File

@ -65,7 +65,7 @@ TEST_F(LibraryProperties, memory_usage)
#if defined(__linux__) || defined(_WIN32)
EXPECT_GE(meminfo[1], 0.0);
#endif
#if !defined(__INTEL_LLVM_COMPILER)
#if (defined(__linux__) || defined(__APPLE__) || defined(_WIN32)) && !defined(__INTEL_LLVM_COMPILER)
EXPECT_GT(meminfo[2], 0.0);
#endif
};

View File

@ -117,6 +117,9 @@ endforeach()
# tester for timestepping fixes
add_executable(test_fix_timestep test_fix_timestep.cpp)
if(NOT BUILD_SHARED_LIBS)
target_compile_definitions(test_fix_timestep PRIVATE USING_STATIC_LIBS=1)
endif()
target_link_libraries(test_fix_timestep PRIVATE lammps style_tests)
# tests for timestep related fixes (time integration, thermostat, force manipulation, constraints/restraints)

View File

@ -255,6 +255,9 @@ TEST(FixTimestep, plain)
{
if (!LAMMPS::is_installed_pkg("MOLECULE")) GTEST_SKIP();
if (test_config.skip_tests.count(test_info_->name())) GTEST_SKIP();
#if defined(USING_STATIC_LIBS)
if (test_config.skip_tests.count("static")) GTEST_SKIP();
#endif
const char *args[] = {"FixTimestep", "-log", "none", "-echo", "screen", "-nocite"};
@ -703,6 +706,9 @@ TEST(FixTimestep, omp)
if (!LAMMPS::is_installed_pkg("OPENMP")) GTEST_SKIP();
if (!LAMMPS::is_installed_pkg("MOLECULE")) GTEST_SKIP();
if (test_config.skip_tests.count(test_info_->name())) GTEST_SKIP();
#if defined(USING_STATIC_LIBS)
if (test_config.skip_tests.count("static")) GTEST_SKIP();
#endif
const char *args[] = {"FixTimestep", "-log", "none", "-echo", "screen", "-nocite",
"-pk", "omp", "4", "-sf", "omp"};

View File

@ -1,7 +1,7 @@
---
lammps_version: 30 Jul 2021
date_generated: Mon Aug 23 20:32:03 2021
epsilon: 2e-11
epsilon: 2e-10
skip_tests:
prerequisites: ! |
pair reaxff

View File

@ -1,7 +1,7 @@
---
lammps_version: 30 Jul 2021
date_generated: Mon Aug 23 20:32:03 2021
epsilon: 3e-12
epsilon: 4e-12
skip_tests:
prerequisites: ! |
pair reaxff

View File

@ -1,6 +1,7 @@
---
lammps_version: 10 Mar 2021
date_generated: Wed Mar 24 18:57:26 202
date_generated: Wed Mar 24 18:57:26 2021
skip_tests: static
epsilon: 9e-12
prerequisites: ! |
atom full

View File

@ -1,7 +1,7 @@
---
lammps_version: 30 Jul 2021
date_generated: Tue Aug 24 15:36:39 2021
epsilon: 2e-13
epsilon: 7.5e-11
skip_tests: single
prerequisites: ! |
pair drip

View File

@ -1,7 +1,7 @@
---
lammps_version: 30 Jul 2021
date_generated: Tue Aug 24 15:36:41 2021
epsilon: 5e-13
epsilon: 2e-10
skip_tests: single
prerequisites: ! |
pair drip

View File

@ -1,7 +1,7 @@
---
lammps_version: 30 Jul 2021
date_generated: Wed Aug 25 07:37:07 2021
epsilon: 1e-13
epsilon: 2e-12
skip_tests: single
prerequisites: ! |
pair lebedeva/z

View File

@ -26,7 +26,7 @@ target_compile_definitions(test_python_package PRIVATE -DTEST_INPUT_FOLDER=${TES
# this requires CMake 3.12. don't care to add backward compatibility for this.
if(Python3_Development_FOUND)
target_compile_definitions(test_python_package PRIVATE -DTEST_HAVE_PYTHON_DEVELOPMENT=1)
target_link_libraries(test_python_package PRIVATE Python::Python)
target_link_libraries(test_python_package PRIVATE Python3::Python)
endif()
add_test(NAME PythonPackage COMMAND test_python_package WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set_tests_properties(PythonPackage PROPERTIES ENVIRONMENT "LAMMPS_POTENTIALS=${LAMMPS_POTENTIALS_DIR};PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}:${LAMMPS_PYTHON_DIR}:$ENV{PYTHONPATH};PYTHONUNBUFFERED=1")

View File

@ -84,5 +84,40 @@ class PythonPyLammps(unittest.TestCase):
self.assertEqual(len(self.pylmp.last_run.thermo.TotEng), 2)
self.assertEqual(len(self.pylmp.last_run.thermo.Press), 2)
def test_info_queries(self):
self.pylmp.lattice("fcc", 0.8442),
self.pylmp.region("box block", 0, 4, 0, 4, 0, 4)
self.pylmp.create_box(1, "box")
self.pylmp.variable("a equal 10.0")
self.pylmp.variable("b string value")
self.assertEqual(self.pylmp.variables['a'].value, 10.0)
self.assertEqual(self.pylmp.variables['b'].value, 'value')
self.assertEqual(len(self.pylmp.variables),2)
self.assertEqual(self.pylmp.system.units,'lj')
self.assertEqual(self.pylmp.system.atom_style,'atomic')
self.assertEqual(self.pylmp.system.ntypes,1)
self.assertEqual(self.pylmp.system.natoms,0)
self.assertEqual(self.pylmp.communication.comm_style,'brick')
self.assertEqual(self.pylmp.communication.comm_layout,'uniform')
self.assertEqual(self.pylmp.communication.nprocs,1)
self.assertEqual(len(self.pylmp.computes),3)
self.assertEqual(self.pylmp.computes[0]['name'], 'thermo_temp')
self.assertEqual(self.pylmp.computes[0]['style'], 'temp')
self.assertEqual(self.pylmp.computes[0]['group'], 'all')
self.assertEqual(self.pylmp.computes[1]['name'], 'thermo_press')
self.assertEqual(self.pylmp.computes[1]['style'], 'pressure')
self.assertEqual(self.pylmp.computes[1]['group'], 'all')
self.assertEqual(self.pylmp.computes[2]['name'], 'thermo_pe')
self.assertEqual(self.pylmp.computes[2]['style'], 'pe')
self.assertEqual(self.pylmp.computes[2]['group'], 'all')
self.assertEqual(len(self.pylmp.dumps),0)
self.pylmp.fix('one','all','nve')
self.assertEqual(len(self.pylmp.fixes),1)
self.assertEqual(self.pylmp.fixes[0]['name'], 'one')
self.assertEqual(self.pylmp.fixes[0]['style'], 'nve')
self.assertEqual(self.pylmp.fixes[0]['group'], 'all')
self.pylmp.group('none','empty')
self.assertEqual(len(self.pylmp.groups),2)
if __name__ == "__main__":
unittest.main()

View File

@ -722,7 +722,7 @@ TEST(Utils, guesspath)
{
char buf[256];
FILE *fp = fopen("test_guesspath.txt", "w");
#if defined(__linux__)
#if defined(__linux__) || defined(__APPLE__) || defined(_WIN32)
const char *path = utils::guesspath(buf, sizeof(buf), fp);
ASSERT_THAT(path, EndsWith("test_guesspath.txt"));
#else
@ -875,6 +875,24 @@ TEST(Utils, date2num)
ASSERT_EQ(utils::date2num("31December100"), 1001231);
}
TEST(Utils, binary_search)
{
double data[] = {-2.0, -1.8, -1.0, -1.0, -1.0, -0.5, -0.2, 0.0, 0.1, 0.1,
0.2, 0.3, 0.5, 0.5, 0.6, 0.7, 1.0, 1.2, 1.5, 2.0};
const int n = sizeof(data) / sizeof(double);
ASSERT_EQ(utils::binary_search(-5.0, n, data), 0);
ASSERT_EQ(utils::binary_search(-2.0, n, data), 0);
ASSERT_EQ(utils::binary_search(-1.9, n, data), 0);
ASSERT_EQ(utils::binary_search(-1.0, n, data), 4);
ASSERT_EQ(utils::binary_search(0.0, n, data), 7);
ASSERT_EQ(utils::binary_search(0.1, n, data), 9);
ASSERT_EQ(utils::binary_search(0.4, n, data), 11);
ASSERT_EQ(utils::binary_search(1.1, n, data), 16);
ASSERT_EQ(utils::binary_search(1.5, n, data), 18);
ASSERT_EQ(utils::binary_search(2.0, n, data), 19);
ASSERT_EQ(utils::binary_search(2.5, n, data), 19);
}
static int compare(int a, int b, void *)
{
if (a < b)