This program is designed to find the volume of a nanoparticle using Monte Carlo integration. The volume of your nanoparticle is a nice thing to know in certain cases, because you can use it to
- apply a size-correction factor to your dielectric constant to account for quantum-size effects
- determine the absorption/scattering/extinction cross-section of your nanoparticle
- determine the density of your nanoparticle
I'm sure there are many other wonderful uses, but these are why I am interested in the volume. Please see the Theory section at the bottom for an explaination of how this code works.
NPVol should be as easy as running
Make in the
If you wish to install the binary to your PATH, then you can also run
install. There are a few customizations that I will go over for you.
I have deliberately avoided a configure script or any other heavy duty build
system in favor of a simple Makefile for this project. The reason is that
there are no external dependencies (except for MPI if you choose to use it) so
there is no need to use excessive force. As such, I have added in some
customizations to the
Makefile itself to simulate on a small scale how these
configure systems work. Feel free to change what needs changing, or better yet
improve and generalize what is there.
NPVol is aware of the compilers
pgf90, and thus has compilation flags for these compilers
pre-programmed. The default compiler is
gfortran (since this is what most
people have), but I have found the
ifort gives far superior performance, so
use that if you have access (
g95 is particulary bad, so I don't recommed).
To choose the compiler you want, use
where in this case it is assumed you wanted
By default a serial version of
NPVol is compiled. This is fine if your
nanoparticles are less than about 2000 atoms, but for particles larger than
this you will want to use parallelized code.
NPVol has been written to
support either OpenMP or MPI, but not both.
Which method should I choose?
In all cases, I recommend OpenMP. It is built into the compiler, so you don't need to install special libraries to use it. Also, you execute the program the same way as the serial version.
The only time I recommend MPI is if you are on a distributed memory system
such as a supercomputer or using
g95, since neither will support OpenMP
(OpenMP is shared memory only, and
g95 hasn't implemented it yet).
Compiling a parallel method
To compile with parallelization support, you must set the
MPI comes in many flavors (implementations), such as OpenMPI (not to
be confused with OpenMP), MPICH, etc.
Makefile is aware of
OpenMPI and MPICH, as these are probably the two most common free
MPI flavors. You should determine which flavor you have installed on your
machine before you compile
NPVol. By default, the
Makefile assumes you
have OpenMPI installed. To change this, use the
make PARALLEL=mpi FLAVOR=mpich
If you have a different flavor, please add it to the
If you don't want your binary to be in the extracted folder, you may
the binary to your hard drive:
PREFIX value is
/usr/local/, so by default this will install
NPVol binary to
/usr/local/bin. If you don't have write permissions to
this directory, you may have to use
sudo make install
If you do not have
sudo access, the I recommend installing to the
directory in your
make install PREFIX=$HOME
~/bin) is not in your
$PATH, then you can add it by adding
this line to your
.profile on Mac OS X):
NPVol, you simply must give it an .xyz file as input
where it is assumed that you have
NPVol to your
What is the format of an XYZ file?
An XYZ file simply gives the atomic name and coordinates of each atom in your system, with the number of atoms total on the first line and a comment on the second:
3 This is a comment Au 0.0 0.0 0.0 Ag 3.0 0.0 0.0 Ag -3.0 0.0 0.0
Note that the coordinates are Cartesian coordinates and by default are assumed
to be in Angstroms, although this may be overridden. For
additional restriction is added in that the comment line must give the radii of
each atom type in the system:
3 Ag=1.4445 Au=1.4445 Au 0.0 0.0 0.0 Ag 3.0 0.0 0.0 Ag -3.0 0.0 0.0
Note that you must have an equals between the atom name and the radius and there can be no spaces.
NPVol has a few options
-hDisplays a help message and quits
-?Displays a help message and quits
-bReads in coordinates and radii in Bohr instead of Angstroms
-s NSAMPLESOverride the default number of sample points to a chosen value. The default number of samples is given by # Samples = MAX(# Atoms * 50, 10^6).
-oOptimize the radius multiplier. This is explained below.
-t THRESHOLDThreshold for optimization. This is explained below.
Radius multiplier optimization
Because the number of sample points must increase as the system size grows, the
NPVol scales poorly with system size (in fact is N^2). This is fine
for a stand-alone code such as
NPVol, since the idea is that you may use
NPVol to calculate the volume of your nanoparticle and then use that value as
input in other codes that are aimed at calculating other properties but need
For most systems this poor scaling is barely noticable, since the overhead to
NPVol is quite low. The problem is that when the number of atoms becomes
very large then
NPVol takes a very long time. For example, for 1070000
atoms, it takes
NPVol 8 hours to caluculate the volume using the default
settings and running on 8 processors; that would be 64 hours if run in serial!
You can always use more processors (
NPVol is embarassingly parallel,
meaning that it scales perfectly with # processors) but not everybody has
access to a large number of processors.
To circumvent this, some codes that need the volume give the option of simply
summing up the volumes of the atoms assuming they are spheres with the given
radii. This method is much faster than Monte Carlo, but it will
underestimate the volume since it does not take into account the space between
atoms (which is accounted for in
NPVol). To compensate, the radii may be
multiplied by some factor. Using the
-o option will cause
calculate the optimal multiplier that you may give so that any nanoparticle's
volume with similar shape, structure and composition can be calculated quickly.
This optimization is done by actually calculating the volume using the
multiplier and then determining the error between the two methods. The default
threshold for the error is 1%, but this can be edited using the
Running in parallel
As mentioned previously, if you compiled with OpenMP then there is no need to execute this code any differently than you would if it were serial:
However, if you wish to use less processors than the maximum on your machine,
you may set the
OMP_NUM_THREADS environment variable. Let's say you only
wanted to run on 2 processors:
OMP_NUM_THREADS=2 NPVol nanoparticle.xyz
Running with MPI requires that you use MPI's executable to set up the parallel environment. Let's say you wanted to run on 4 processors:
mpirun -n4 NPVol nanoparticle.xyz
You must give the number of processors. In somecases,
mpirun is not
mpiexec is an equivalent routine:
mpiexec -n4 NPVol nanoparticle.xyz
Imagine that you want to know the value of pi, and all you have to determine this is a circular dartboard inside a square frame and an infinite number of darts. It turns out that this is all we need to determine pi (honest).
We know that the area of the square frame that the dartboard is in is
A^square = (2r)^2 = 4r^2
where r is half the side length of the frame (you'll see why it was defined that way in a moment). We also know that the area of the circular dartboard is
A^circle = πr^2
Now, if there was some way to find the ratio (R) of the areas, then we could find pi, like this:
R = A^circle / A^square = ( πr^2 ) / ( 4r^2 ) = π / 4
π = 4R
This means that π is just 4 times the ratio of the two areas!
…But wait a minute. What does that have to do with my dartboard? How can I find the area of the circle without pi? This is where the Monte Carlo technique comes in. In Monte Carlo, we use random numbers to compare something known (or easily determined) to something unknown (or less easily determined).
Remember that our circular dartboard is enclosed by a square frame, with the side of the frame being exactly equal to the diameter of the dartboard. If you are a terrible dart player, and every dart you throw has a random chance of hitting anywhere in the frame, be it on the dartboard or not, then the ratio of darts that are in the frame to darts that are only within the dartboard is directly proportional to the ratio of the areas, and therefore is equal to the R defined above!.
I'll let that soak in a bit. Try drawing a circle enclosed by a square on a piece of paper, then putting random dots in the square, as if the circle were not there. This is exactly what I am talking about. The more points you put (darts you throw), the closer your ratio is to exactly the ratio of the areas. If you were to use an infinite number of points/darts, then you would get the exact answer.
So, how does this relate to nanoparticles? In this case, the dartboard is the nanoparticle, and the frame is a box that completely surrounds the nanoparticle. The volume of the box is easy to calculate, but obviously the volume of an arbitrarily shaped nanoparticle is not. All we know about our nanoparticle is the atomic positions in space and the radii of each of these atoms. If we take random sample points inside the box, and take the ratio of sample points that were in the box to those only in the nanoparticle, then we can find the volume of the nanoparticle:
R = points^np / points^box
V^np = R * V^box
Thus, we can easily find the volume of our arbitrarily shaped nanoparticle!