MADNESS
0.10.1
|
Additional documentation and instructions exist in several places:
For basic computing with MADNESS functions, the only header file that you should need to include is <mra/mra.h>
. Advanced programs may need a few others. You should include the directory in the include path to avoid possible conflicts with similarly named files from other packages. In the following documentation trunk
will denote the top-level directory of the MADNESS distribution.
A perhaps more up to date version of this example is here.
This section shows you how to use MADNESS and evaluate a simple mathematical expression, notably this one dimensional integral.
The corresponding code is in trunk/src/examples/sininteg.cc
, and we will go through this first example in gory detail. The main steps will be
MADNESS generates a numerical representation of a function ( ) by projecting into its internal, orthonormal basis, , see Mra for more detail. This involves computing many integrals of the form
using adaptive numerical quadrature. Thus, MADNESS has to be able to evaluate your function at an arbitrary point, and so you must provide it with a C++ implementation of your function using a standard interface. MADNESS passes to your function the coordinates (an array of the appropriate dimension) and you return the value. For simple functions, a C++ function suffices whereas more complicated stuff might require a C++ class (see a subsequent example).
For this example, we wish to evaluate the function where is a 1-D coordinate. The example code looks like
If your function were in 3-D it would be passed a coord_3d
that would have 3 elements (0, 1, and 2) that you might interpret as or or something else as defined by your problem. A very useful capability of both Maple and Mathematica is to generate C from expressions or functions, though the resulting code often requires a little cleanup.
save
) or global (Fortran common blocks or modules) data; reading from such locations is fine. Note that this constraint applies to all code invoked by your function, including math, BLAS, and linear algebra libraries and for some machines/compilers it requires special options be used (the MADNESS makefiles look after this). If you suspect a thread problem, try running with the environment variable MAD_NUM_THREADS
set to one.OMP_NUM_THREADS
(or set it to one).MADNESS has its own parallel runtime environment that is fully compatible with the Message Passing Interface (MPI). Just as for MPI, the MADNESS runtime must be initialized at the start and cleaned up at the end. Here is the simplest parallel program using MADNESS.
The World
object in MADNESS is similar to an MPI communicator (each World
is associated with a communicator) but contains all of the information necessary to provide the rich functionality of the MADNESS runtime. Instead of calling MPI::Init()
and MPI::Finalize()
, a MADNESS program invokes madness::initialize()
and madness::finalize()
(call madness::initialized()
to check that MADNESS had been initialized). The routine madness::startup()
initializes the MADNESS numerical environment.
We implemented the C++ function above (myf
) and the domain is specified by the problem as . Presently, all functions of a given dimension being used by a program are assumed to have the same, default domain. We will use the default precision (1e-6 in the L2-norm – most errors in MADNESS use this norm).
The first line sets the domain and the second makes a numerical representation of your function. The slightly unusual form of the constructor for a MADNESS function is called the "named parameter idiom." The first version of MADNESS was written in Python, which provides named parameters that can have default values. To override the default for some parameter only required specifying its name. However, C++ does not provide named parameters and the closest we can get to them is the above idiom. To make a Function
what you are actually doing is making a FunctionFactory
associated with a World
, overriding defaults inside the factory, and then constructing a function from the factory. The default function is zero, so to make a zero 1D function we would just type
In the second line, to generate a MADNESS Function
from your C++ function we override the default with the desired function pointer.
The integral of a function over the whole domain is computed with the trace()
function, c.f.,
All_reduce()
). Therefore, every process associated with the World
must execute this statement; otherwise your program will hang if it is running in parallel using MPI.If you only want to run sequentially, just print your data in any of the myriad ways possible using C++. However, if you ever want to run in parallel across multiple nodes using MPI you should from the outset get in the habit of writing the following instead
The critical ingredient is the if-test that ensures only one process actually does the printing (in this case process zero, but that choice is arbitrary). If you don't do this, your output will be unexpectedly verbose. The templated routine print()
is just a convenience wrapper around cout <<
that automatically inserts spaces between elements and a newline at the end.
f.trace()
to a variable because trace
is a collective, but printing is serial. Anecdotally, at least 90% of hanging parallel programs are due to getting this wrong.Your complete program, trunk/src/apps/sininteg.cc
, looks like this
Yes, this is rather verbose for integrating in 1D, but you should already be able to see that most of it is boiler plate. You could be integrating a much more complicated function in 4-D with only little more personal effort (the computer might have to work a lot harder though).
Previous: Getting started with MADNESS; Next: Compiling and running MADNESS programs