Numerical library¶
MADNESS provides a high-level environment for the solution of integral and differential equations in many dimensions using adaptive, fast methods with guaranteed precision based on multi-resolution analysis and novel separated representations.
Useful examples can be found in the src/examples
directory, where the use of the numerical library can be
practiced by example.
sininteg.cc
: Create a function and integratehatom_energy.cc
: Compute the energy of the hydrogen atomheat.cc
: apply the Greens function to the heat equation3dharmonic.cc
: solve the 3D harmonic oscillatorh2.cc
: solve the Hartree-Fock equations for the H2 moleculenonlinschro.cc
: use the KAIN solver for accelerating the solution of a system of non-linear equationshedft.cc
: use density functional theory
Some (dated) documentation for the examples and the API is here.
Example for MADNESS as an external library¶
To use MADNESS as an external library in a code we recommend cmake. Build and install MADNESS to an install directory. Set
export MADNESS_DIR=/path/to/madness/install/directory/
In file CMakeLists.txt
:
cmake_minimum_required(VERSION 3.22)
project(yourbinary)
set(CMAKE_CXX_STANDARD 17)
find_package(MADNESS CONFIG REQUIRED)
add_executable(yourbinary main.cc)
target_link_libraries(yourbinary madness)
In file main.cc
:
#include <madness.h>
using namespace madness;
int main(int argc, char* argv[]) {
World& world=initialize(argc,argv);
startup(world,argc,argv,true);
FunctionDefaults<1>::set_cubic_cell(-10,10);
FunctionDefaults<1>::set_k(8);
FunctionDefaults<1>::set_thresh(1.e-6);
try {
auto gaussian = [](const Vector<double,1>& r){return std::exp(-r[0]*r[0]);};
Function<double,1> f = FunctionFactory<double,1>(world).f(gaussian);
double I = f.trace();
double value = f(0.7);
if (world.rank() == 0) {
print("trace(exp(-r^2)",I,"error",I-std::sqrt(M_PI));
print("f(0.7)",value,"error",value-exp(-0.7*0.7));
}
} catch (...) {
std::cout << "caught an error " << std::endl;
}
finalize();
return 0;
}
}
Going through the code line by line:
#include <madness.h>
Includes the MADNESS library.
using namespace madness;
int main(int argc, char* argv[]) {
World& world=initialize(argc,argv)
World contains the MPI communicator and must always be set, even if the code will run only on one node.
startup(world,argc,argv,true)
Reads all necessary numerical data (e.g. the wavelet twoscale coefficients) and prints out compiler flags.
FunctionDefaults<1>::set_cubic_cell(-10,10)
Defines the computation cell/intervall, here in 1 dimension. MADNESS is templated with respect to the dimensions.
FunctionDefaults<1>::set_k(8);
Sets the wavelet order to 8. Anything between 2 and 30 will work, the optimal choice depends on the specific problem, 8 is usually a good guess.
FunctionDefaults<1>::set_thresh(1.e-7);
Sets the precision threshold to \(\epsilon=10^{-6}\) in the \(L^2\) norm.
Unless the function is singular or has other pathological features the threshold will be met. Up to the limits of numerical precision,
it is possible to tighten the threshold to secure more digits. There are also different truncation modes. The default is mode=0
which is designed to yield accurate function values and norms, whereas mode=1
aims to also yield accurate derivatives.
try {
auto functor=[](const Vector<double,1>& r){return std::exp(-r[0]*r[0]);}
Defines the function to be represented in MRA. Vector
is a MADNESS class defining coordinates.
Function<double,1> f=FunctionFactory<double,1>(world).f(functor);
Projects the function \(e^{-r^2}\) onto the MRA representation. A FunctionFactory
is used to
define various properties of the Function
, it requires world
as input, a function (or functor)
to compute the value. Additional optional arguments can be provided using the named-parameter idiom.
double I=f.trace();
Integrates the function \(\int_{-10}^{-10} e^{-r^2}\mathrm dx\). It is a collective operation.
double value = f(0.7);
evalues the numerical function at point `x=0.7. This is executed by a single process but might involve remote communication. If you wish to evaluate at many points (e.g., a line or a cube, there are more efficient interfaces).
if (world.rank() == 0) {
print("trace(exp(-r^2)",I,"error",I-std::sqrt(M_PI));
print("f(0.7)",value,"error",value-exp(-0.7*0.7));
}
Prints out the values using the MADNESS Python-like print
function. Be sure to add the if
block to avoid verbose output if
run on many MPI ranks. Also make sure that the trace
or any other collective operation is not called inside this
if
block. A collective operation called by only one rank will cause the program to hang. This is a common error.
} catch (...) {
std::cout << "caught an error " << std::endl;
}
finalize();
Finalizes the communicator.
It is important that all MRA objects (e.g. Function<double,1>
) are destructed before
finalize()
is called, otherwise segmentation faults might occur since the destructor for these objects will erroneously be called at the very end of the program after the runtime has been dismantled.
Thus, for simple programs enclose all MRA code after initialize
in a sub-scope (e.g., using braces or inside a try/catch
block), or after obtaining World
pass it (by reference) into another procedure
that contains your MRA code.
return 0;
}