MMSP User's Guide

The page is a meant to provide both a tutorial-style introduction to MMSP as well as a reasonably complete programming reference.

1 Introduction to MMSP


2 Using MMSP components

This section is a guide to using MMSP to write simple microstructure evolution codes. We assume that the user will be using only existing components; later on we'll describe how to add new components to the package.

2.1 Programming interface

Let's first look at possibly the simplest example of a simulation code using the MMSP. Suppose we want to perform a 3D simulation of isotropic grain growth using the phase field method, and we already have a valid grid input file named file1 (see below for file formats). The file PFgrid.graingrowth.h, included with the distribution, contains the functions we need. We create a new source file, PF.graingrowth.cpp, and enter

// PF.graingrowth.cpp
// A minimal phase field code using MMSP
#include"PFgrid.graingrowth.h"
using namespace PF;

int main(int argc, char* argv[])
{
    PFgrid3D grid("file1");
    grid.update(100);
    grid.output("file2");
}


PF.graingrowth.h defines the update() function used in this program and also includes the headers that declare the PFgrid3D class. For now, just note that this file determines what kind of simulation will be performed. The first line in the main() function declares the object grid of type PFgrid3D and reads the data contained in file1 into the empty grid data structure. All grid objects in MMSP have similar names and can be declared with the same syntax. The update() function performs 100 phase field time steps, and the output() function writes the new, updated grid to file2.

Note that there is one line of code for each operation on the grid. This is a typical feature of the MMSP design. In any case, we now have a 3D phase field code that will read a grid from file1, perform 100 time steps, and write the result to file2. All that's left is to compile and run the program.

2.2 Problem dimensionality

All grid objects within MMSP are written so that changing problem dimensionality is trivial. In the example given above, we declared an object of type PFgrid3D. As you might guess, the way to recast this program for a 2D grid is to change the object declaration to PFgrid2D. Remember, though, that when the program is compiled and run, the actual grid that is input from the file must be a valid 2D or 3D grid, depending on the object declaration. Such I/O errors are easy to catch, as they terminate the program and write a message to stderr.

Simulation method

We've shown one example of using the MMSP to write a simple grain growth program using the phase field method. Now suppose we want to use a different computational technique, say the Monte Carlo method. In this case, we only need to replace the PFgrid3D declaration with MCgrid3D, the standard grid object for conventional Monte Carlo simulations, and choose the appropriate header file to include (MC.graingrowth.h). Obviously, the input file must now be a valid Monte Carlo grid or an I/O error occurs.

Physical process

In MMSP, the update() function is the one and only function that determines how the microstructure evolves. This is a member function of each grid object, but its implementation is defined in a separate header file. In our example, this file was PF.graingrowth.h. Several such header files are included in the distribution, and the user can easily create new headers as described in a later section. One and only one header file should (or even can) be included in the source file containing the main function. This was intentional; the point is that (1) different physics can be plugged-in to the simulation in a simple manner, (2) the function call remains the same in all simulations, and (3) the fact that only one update function can be defined forces the user to write a smaller, more managable program.

Parallel implementation

One of the real benefits of using MMSP is how simple it makes writing parallel simulation codes. MMSP uses the MPI standard for distributed computing. However, the grid object classes are endowed with member functions that hide most of the program's interaction with MPI. There are only a few significant changes that must be made to the minimal single processor program described above to turn it into a parallel program, provided that the update() function has been parallelized. The simplest parallel program is

// PF.graingrowth.parallel.cpp
// A minimal phase field code (parallel implementation)
#include"PFgrid.graingrowth.h"
using namespace PF;

int main(int argc, char* argv[])
{
    MPI::Init(argc,argv);
    int id = MPI::COMM_WORLD.Get_rank();
    int np = MPI::COMM_WORLD.Get_size();

    PFgrid3D grid("file1",id,np);
    grid.update(100,id,np);
    grid.output("file2",id,np);

    MPI::Finalize();
}


The four lines

    MPI::Init(argc,argv);
    int id = MPI::COMM_WORLD.Get_rank();
    int np = MPI::COMM_WORLD.Get_size();

    ...

    MPI::Finalize();


are required of any MPI program (or at least something like them). The other changes are, of course, that the PFgrid3D constructor, output(), and update() member functions now require the processor rank and the total number of processors as additional parameters. These are overloaded versions of the single processor functions that are smart enough to subdivide the domain and perform the necessary interprocess communication during the simulation. Thus no external codes to decompose or recombine the grid are necessary, and only the minimum number of MPI subroutine calls must be made by the user.

Microstructure generation

File formats

Extending the package

Writing the update() function

New grid classes