6. Using software in a hpc environment#
http://hpctoolkit.org/software-instructions.html https://apptainer.org/docs/user/main/index.html
6.1. Spack: automatic load for programs and containers#
Spack was designed by llnl and is targeted to simplify the installation and managment of HPC programs with many possible versions and dependencies. Check the docs at https://spack.readthedocs.io/en/latest/tutorial.html or https://bluewaters.ncsa.illinois.edu/webinars/software-ecosystems/spack . For now let’s just install it, configure it, and install some simple tools.
6.1.1. Installation#
Following the official docs, just clone the repository
git clone https://github.com/spack/spack.git # clones the repo
cd spack
#git checkout releases/v0.17 # Checkout the latest stable release
source share/spack/setup-env.sh # Setup the environment , this command should go in ~/.bashrc
Now you can check what can be installed
spack list
To be able to use spack easily in the future, it is recommended to add
the source command to your ~/.bashrc
, so just add the following at the
end of the file
source $HOME/PATHTOREPO/share/spack/setup-env.sh
and then close and open the terminal.
In our computer room there has been a problem with spack and openssl. It
is better to instruct spack to use the local openssl version instead of
building one. To do so, add the following to your spack package config
file, $SPACK_REPO/etc/spack/packages.yaml
:
packages:
openssl:
externals:
- spec: openssl@1.1.1m
prefix: /usr
buildable: False
You can check the correct version with \(openssl version\). Furthermore,
to be able to run your installed programs on several computers with
different processors, use the flag target=x86_64
.
6.1.2. Installing some tools with spack#
Now let’s just install the gsl
scientific library and some eigen
alternative versions:
spack info gsl # Get some information about versions, deps, etc
spack install gsl@2.5
spack install gsl@2.4
module avail
To check the installed software, you can use the module
command as
(installed when you used bootstrap
)
module avail
Now you will see that you have two versions of the gsl. If you want to
use one of them, you will load it with spack. The check the change in
environment, first check the PATH
, then load, then compare
echo $PATH
echo $C_INCLUDE_PATH
echo $LD_LIBRARY_PATH
Now load the gsl version 2.5,
spack load gsl@2.5
and check the new paths
echo $PATH
echo $C_INCLUDE_PATH
echo $LD_LIBRARY_PATH
If you unload the gsl 2.5, everything goes back to normal,
spack unload gsl@2.5
echo $PATH
echo $C_INCLUDE_PATH
echo $LD_LIBRARY_PATH
To learn more about spack, check the official docs and tutorials. In the following we will use it to play with several packages in parallel programming. Is voro++ available? what about eigen?
6.2. How to install programs from source#
In this workshop we will learn how to install a program or library from source to the home directory of the user. This is useful when you need a programa but it is not available in the system you are working on. Also, It could be that you actually need a newer version than the installed one. We will use the following programs, some of them already available at the computer room:
Name |
installed version |
latest version |
---|---|---|
3.3.4 |
3.3.8 |
|
3.2.7 |
3.3.7 |
|
Not installed |
0.4.6 |
|
Not installed |
9.2 |
We will learn how to do it by compiling a package directly, something very useful to know, and also using a new tool aimed for supercomputers, called spack , that simplifies the installation of software and also allows to have many versions of the same library, something not easy done manually.
6.2.1. Preliminary concepts#
It is important for you to know a little how your operating system and
the shell look for commands. In general, the PATH
variable stores the
directories where the shell interpreter will look for a given command.
To check its contents, you can run the following,
If, for instance, you want to add another directory to the PATH
, like
the directory $HOME/local/bin
, you can run the following
export PATH=$PATH:$HOME/bin
This appends the special directory to the old content of the PATH
.
When you want to compile a given program that uses some other libraries,
you must specify any extra folder to look for include files, done with
the -I
flag, and for libraries, done with the -L
and -l
flags. For
instance, let’s assume that you installed some programs in your
HOME/local
, the include files inside $HOME/local/include
, and the
libraries inside $HOME/local/lib
. If you want to tell the compiler to
use those, you must compile as
Finally, whenever you are installing a program from source you must be aware that this reduces to basically the following steps:
Download and untar the file. Enter the unpacked directory.
Read the README/INSTALL files for any important info.
If the program uses cmake, create a build dir and then use cmake to generate the Makefiles:
mkdir build cd build cmake ../ -DCMAKE_INSTALL_PREFIX=$HOME/local
On the other hand, if the program uses
configure
, then configure the system to install on the required path./configure --prefix=$HOME/local
Compile and install the program, maybe using many threads
make -j 4 # uses for threads to compile make install
Done. The program is installed.
Finally, when compiling, do not forget to use the flags
-L
and-I
appropriately.
Setting all the flags and making sure to use the right version is
sometimes difficult, so tools like spack
aim to manage and simplify
this.
6.2.2. Checking the version for already installed programs#
If you are used to apt-get
or something related, you can use the
package manager to check. But, in general, you can check the versions by
looking at the appropriate places on your system. Typically, if you are
looking for a library, they are installed under /usr/lib
or
/usr/local/lib
, while include files are installed under /usr/include
or /usr/local/include
. For instance, if you are looking for library
foo
, then you should look for the file libfoo.a
or libfoo.so
. One
useful utility for this is the command locate
or find
.
locate libfoo
find /usr/lib -iname "*libfoo*"
Execute these commands to check the actual versions for fftw, eigen, and git. What versions do you have? If you a re looking for a program, or an specific version of program, you must check if the program exists by executing it. For command line programs you usually can check the version by using the following
where programname
is the name of the command.
6.2.3. Preparing the local places to install the utilities#
In this case we will install everything under the $HOME/local
subdirectory inside your home, so please create it. Remember that the
symbol $HOME
means your home directory. The utilies will then create
the appropriate folders there. NOTE: Better use the $HOME
var
instead of ~
, which is another way to state the name of your home.
6.2.4. Typical installation algorithm#
Download the source from the project page. This normally implies downloading a tarball (file ending with
.tar.gz
or.tar.bz2
) .Un-compress the downloaded file. For a tarball, the command will be
tar xf filename.tar.gz
Enter to the newly uncompressed folder (almost always usually
cd filename
).READ the
README
and/or theINSTALL
file to check for important info regarding the program. SOmetimes these files tell you that the installation is special and that you are required to follow some special steps (that will happen withvoro++
)CONFIGURATION: You have two options, each one independent on the other:
If the program has a configure script, then just run
./configure --help
to check all the available options. Since we want to install on the
$HOME/local
directory, then we need to run./configure --prefix=$HOME/local
If you don’t specify the prefix, then the program will be installed on the
/usr/bin
or/usr/local/bin
directories, whatever is the default. If these commands ends successfully, it will print some info to the screen and will tell you to go to the next step. Otherwise you will need to read the log and fix the errors (like installing a dependency).If the program uses
cmake
, a makefile generator and
configurator, then you need to do the following:
mkdir build # creates a build directory to put there the temporary built files cd build cmake ../ -DCMAKE_INSTALL_PREFIX:PATH=$HOME/local # configure the building process for the source code located on the parent directory
COMPILATION: Now that you have configured your installation, you need to compile by using the GNU make utility (Note: All this build utilities come from the gnu organization and are free software as in freedom). If you have several cores, you can use them in parallel, assuming the that the Makefile and your make versions supports it:
make -j 3 # for three cores, but, if you are unsure, just use one core.
Any errors in this stage should be fixed before going to the next one.
INSTALLATION After successful compilation, you can install by using
make install
This will install the program (libraries, binaries, include files, manual files, etc) onto the
prefix
directory. If you want to instll system-wide (you did not set theprefix
), then you need to usesudo make install
. In this case you don’t needsudo
since you are installing on your own home.TESTING In this case use a program to test your installation. When you compile your program and you want to use the version that you installed, you need to tell the compiler where to find the libraries/includes, so you need something like
g++ -L $HOME/local/lib -I $HOME/local/include programname.cpp -llibname
-L $HOME/local/lib
tells the compiler to look for libraries on
the
$HOME/local/lib
directory.-I $HOME/local/include
tells the compiler to look for include
files on the
$HOME/local/include
directory.-llibname
tells the compiler to link with the given
library. Sometimes is not needed. Sometimes is crucial. Be careful, if your library is called libfftw, you need to write
-lfftw
, not-llibfftw
.
6.2.5. Workshop#
For each of the proposed utilities written at the beginning, follow the next procedure:
Check the installed version number and compare with the latest one.
Install the latest version on your home directory by following the procedure stated above.
Run each of the following example program but make sure you a re using you installed version. Show to the instructor the compilation line.
Important NOTE: for g++
, use the prefix -9
in the configure line
to put that as suffix to the commands and avoid collisions with the
compiler already installed in the system. This can be done by adding the
flag --program-suffix=-9
to the configure
command.
6.2.6. Test Programs#
6.2.6.1. fftw#
This is a c code. Save it as testfftw.c and compile with gcc
instead
of g++
.
// From : https://github.com/undees/fftw-example
// This ia a c code (save it as testfftw.c)
/* Start reading here */
#include <fftw3.h>
#define NUM_POINTS 128
/* Never mind this bit */
#include <stdio.h>
#include <math.h>
#define REAL 0
#define IMAG 1
void acquire_from_somewhere(fftw_complex* signal) {
/* Generate two sine waves of different frequencies and
* amplitudes.
*/
int i;
for (i = 0; i < NUM_POINTS; ++i) {
double theta = (double)i / (double)NUM_POINTS * M_PI;
signal[i][REAL] = 1.0 * cos(10.0 * theta) +
0.5 * cos(25.0 * theta);
signal[i][IMAG] = 1.0 * sin(10.0 * theta) +
0.5 * sin(25.0 * theta);
}
}
void do_something_with(fftw_complex* result) {
int i;
for (i = 0; i < NUM_POINTS; ++i) {
double mag = sqrt(result[i][REAL] * result[i][REAL] +
result[i][IMAG] * result[i][IMAG]);
printf("%g\n", mag);
}
}
/* Resume reading here */
int main() {
fftw_complex signal[NUM_POINTS];
fftw_complex result[NUM_POINTS];
fftw_plan plan = fftw_plan_dft_1d(NUM_POINTS,
signal,
result,
FFTW_FORWARD,
FFTW_ESTIMATE);
acquire_from_somewhere(signal);
fftw_execute(plan);
do_something_with(result);
fftw_destroy_plan(plan);
return 0;
}
6.2.6.2. Eigen C++#
These are C++
codes. Save them, compile, run and explain what they do.
#include <iostream>
#include <Eigen/Dense>
#include <Eigen/Core>
using Eigen::MatrixXd;
int main()
{
//std::cout << EIGEN_MAYOR_VERSION << std::endl;
std::cout << EIGEN_MINOR_VERSION << std::endl;
MatrixXd m(2,2);
m(0,0) = 3;
m(1,0) = 2.5;
m(0,1) = -1;
m(1,1) = m(1,0) + m(0,1);
std::cout << m << std::endl;
}
#include <iostream>
#include <Eigen/Dense>
using namespace Eigen;
int main()
{
Matrix2d a;
a << 1, 2,
3, 4;
MatrixXd b(2,2);
b << 2, 3,
1, 4;
std::cout << "a + b =\n" << a + b << std::endl;
std::cout << "a - b =\n" << a - b << std::endl;
std::cout << "Doing a += b;" << std::endl;
a += b;
std::cout << "Now a =\n" << a << std::endl;
Vector3d v(1,2,3);
Vector3d w(1,0,0);
std::cout << "-v + w - v =\n" << -v + w - v << std::endl;
}
#include <iostream>
#include <Eigen/Dense>
using namespace std;
using namespace Eigen;
int main()
{
Matrix3f A;
Vector3f b;
A << 1,2,3, 4,5,6, 7,8,10;
b << 3, 3, 4;
cout << "Here is the matrix A:\n" << A << endl;
cout << "Here is the vector b:\n" << b << endl;
Vector3f x = A.colPivHouseholderQr().solve(b);
cout << "The solution is:\n" << x << endl;
}
#include <iostream>
#include <Eigen/Dense>
using namespace std;
using namespace Eigen;
int main()
{
Matrix2f A;
A << 1, 2, 2, 3;
cout << "Here is the matrix A:\n" << A << endl;
SelfAdjointEigenSolver<Matrix2f> eigensolver(A);
if (eigensolver.info() != Success) abort();
cout << "The eigenvalues of A are:\n" << eigensolver.eigenvalues() << endl;
cout << "Here's a matrix whose columns are eigenvectors of A \n"
<< "corresponding to these eigenvalues:\n"
<< eigensolver.eigenvectors() << endl;
}
6.2.7. Voro++#
Use the example http://math.lbl.gov/voro++/examples/random_points/
// Voronoi calculation example code
//
// Author : Chris H. Rycroft (LBL / UC Berkeley)
// Email : chr@alum.mit.edu
// Date : August 30th 2011
#include "voro++.hh"
using namespace voro;
// Set up constants for the container geometry
const double x_min=-1,x_max=1;
const double y_min=-1,y_max=1;
const double z_min=-1,z_max=1;
const double cvol=(x_max-x_min)*(y_max-y_min)*(x_max-x_min);
// Set up the number of blocks that the container is divided into
const int n_x=6,n_y=6,n_z=6;
// Set the number of particles that are going to be randomly introduced
const int particles=20;
// This function returns a random double between 0 and 1
double rnd() {return double(rand())/RAND_MAX;}
int main() {
int i;
double x,y,z;
// Create a container with the geometry given above, and make it
// non-periodic in each of the three coordinates. Allocate space for
// eight particles within each computational block
container con(x_min,x_max,y_min,y_max,z_min,z_max,n_x,n_y,n_z,
false,false,false,8);
// Randomly add particles into the container
for(i=0;i<particles;i++) {
x=x_min+rnd()*(x_max-x_min);
y=y_min+rnd()*(y_max-y_min);
z=z_min+rnd()*(z_max-z_min);
con.put(i,x,y,z);
}
// Sum up the volumes, and check that this matches the container volume
double vvol=con.sum_cell_volumes();
printf("Container volume : %g\n"
"Voronoi volume : %g\n"
"Difference : %g\n",cvol,vvol,vvol-cvol);
// Output the particle positions in gnuplot format
con.draw_particles("random_points_p.gnu");
// Output the Voronoi cells in gnuplot format
con.draw_cells_gnuplot("random_points_v.gnu");
}
On gnuplot do the following:
splot "random_points_p.gnu" u 2:3:4, "random_points_v.gnu" with lines
6.2.8. g++ 9.2#
Just run the command and check the version,
Now run any of the special functions examples that required -std=c++17
.