Using SLUG as a Library

In addition to running as a standalone program, SLUG can be compiled as a library that can be called by external programs. This is useful for including stellar population synthesis calculations within some larger code, e.g., a galaxy simulation code in which star particles represent individual star clusters, where the stars in them are treated stochastically.

Compiling in Library Mode

To compile in library mode, simply do:

make lib

in the main directory. This will cause a dynamically linked library file libslug.x to be created in the src directory, where x is whatever the standard extension for dynamically linked libraries on your system is (.so for unix-like systems, .dylib for MacOS).

Alternately, if you prefer a statically-linked version, you can do:

make libstatic

and a statically-linked archive libslug.y will be created instead, where y is the standard statically-linked library extension on your system (generally .a).

In addition to lib and libstatic, the makefile supports lib-debug and libstatic-debug as targets as well. These compile the same libraries, but with optimization disabled and debugging symbols enabled.

Finally, if you want MPI functionality, you can compile with:

make lib MPI=ENABLE_MPI

See Compiling for more on compiling with MPI enabled.

Predefined Objects

In order to make it more convenient to use slug as a library, the library pre-defines some of the most commonly-used classes, in order to save users the need to construct them. These predefined objects can be accessed by including the file slug_predefined.H in your source file. This function defines the class slug_predef, which pre-defines all the IMFs, evolutionary tracks, spectral synthesizers, and yields that ship with slug, without forcing the user to interact with the parameter parsing structure.

The slug_predef class provides the methods imf, tracks, specsyn, and yields. These methods take as arguments a string specifying one of the predefined names of an IMF, set of tracks, or spectral synthesizer, and return an object of that class that can then be passed to slug_cluster to produce a cluster object. For example, the following sytax creates a slug_cluster with ID number 1, a mass of 100 solar masses, age 0, a Chabrier IMF, Padova solar metallicity tracks, starburst99-style spectral synthesis, and slug’s default nuclear yields:

#include "slug_predefined.H"
#include "slug_cluster.H"

slug_cluster *cluster =
   new slug_cluster(1, 100.0, 0.0, slug_predef.imf("chabrier"),
                    slug_predef.tracks("modp020.dat"),
                    slug_predef.specsyn("sb99"),
                    nullptr, nullptr, nullptr,
                    slug_predef.yields());

Using SLUG as a Library with MPI-Enabled Codes

In large codes where one might wish to use slug for subgrid stellar models, it is often necessary to pass information between processors using MPI. Since slug’s representation of stellar populations is complex, and much information is shared between particles rather than specific to individual particles (e.g., tables of yields and evolutionary tracks), passing slug information between processors is non-trivial.

To facilitate parallel implementations, slug provides routines that wrap the base MPI routines and allow seamless and efficient exchange of the slug_cluster class (which slug uses to represent simple stellar populations) between processors. The prototypes for these functions are found in the src/slug_MPI.H header file, and the functions are available if the library was compiled with MPI support enabled (see Compiling in Library Mode).

Here is an example of MPI usage, in which one processor creates a cluster and then sends it to another one:

#include "slug_cluster.H"
#include "slug_MPI.H"
#include "mpi.h"
#include <vector>
#include <cstdio>

int main(int argc, char *argv[]) {

  // Start MPI
  MPI_Init(&argc, &argv);

  // Get rank
  int rank;
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);

  // Rank 0 creates a cluster and prints out the masses of the stars
  slug_cluster *cluster;
  if (rank == 0) {
    cluster =
       new slug_cluster(1, 100.0, 0.0, slug_predef.imf("chabrier"),
                        slug_predef.tracks("modp020.dat"),
                        slug_predef.specsyn("sb99"),
                        nullptr, nullptr, nullptr,
                        slug_predef.yields());
    const std::vector<double> stars = cluster->get_stars();
    for (int j=0; j<stars.size(); j++)
      std::cout << "rank 0, star " << j
                << ": " << stars[j] << std::endl;
  }

  // Barrier to make sure rank 0 outputs come first
  MPI_Barrier(MPI_COMM_WORLD);

  // Rank 0 sends cluster, rank 1 receives it
  if (rank == 0) {
    MPI_send_slug_cluster(*cluster, 1, 0, MPI_COMM_WORLD);
  } else if (rank == 1) {
    cluster = MPI_recv_slug_cluster(0, 1, MPI_COMM_WORLD,
                                    slug_predef.imf("chabrier"),
                                    slug_predef.tracks("modp020.dat"),
                                    slug_predef.specsyn("sb99"),
                                    nullptr, nullptr, nullptr,
                                    slug_predef.yields());
  }

  // Rank 1 prints the masses of the stars; the resulting masses
  // should be identical to that produced on rank 0
  if (rank == 1) {
    const std::vector<double> stars = cluster->get_stars();
    for (int j=0; j<stars.size(); j++)
      std::cout << "rank 1, star " << j
                << ": " << stars[j] << std::endl;
  }
}