

{"id":209,"date":"2017-10-23T09:51:30","date_gmt":"2017-10-23T07:51:30","guid":{"rendered":"https:\/\/project.inria.fr\/damaris\/?page_id=209"},"modified":"2024-06-07T13:06:44","modified_gmt":"2024-06-07T11:06:44","slug":"instrumenting-a-simulation","status":"publish","type":"page","link":"https:\/\/project.inria.fr\/damaris\/instrumenting-a-simulation\/","title":{"rendered":"Instrumenting a Simulation"},"content":{"rendered":"<p class=\"p1\">This part of the documentation will show you how to instrument a simulation in order to use Damaris. Damaris&nbsp;requires the simulation to be based on MPI.<\/p>\n<h4 class=\"p2\">Minimal Configuration File<\/h4>\n<p class=\"p1\">Before actually instrumenting your code, an XML configuration file has to be created. This configuration file will contain some information about the simulation, the system, the data and the plug-ins&nbsp;that you want to use with Damaris. In the below sample, you can find a minimal configuration file.<\/p>\n<pre lang=\"xml\" line=\"1\" escaped=\"true\">&lt;?xml version =\"1.0\"?&gt;\n&lt;simulation name=\"mysimulation\" language=\"fortran\" xmlns=\"http:\/\/damaris.gforge.inria.fr\/damaris\/model\"&gt;\n    &lt;architecture&gt;\n        &lt;domains count=\"1\"\/&gt;\n        &lt;dedicated cores=\"1\" nodes=\"0\"\/&gt;\n        &lt;buffer name=\"buffer\" size=\"67108864\" \/&gt;\n        &lt;placement \/&gt;\n        &lt;queue name=\"queue\" size=\"100\" \/&gt;\n    &lt;\/architecture&gt;\n&lt;data&gt;\n&lt;\/data&gt;\n&lt;actions&gt;\n&lt;\/actions&gt;\n&lt;\/simulation&gt;\n<\/pre>\n<p>The <code>&lt;simulation&gt;<\/code> name is used in different backends, for example to name trace files or to be&nbsp;added on figures when using in situ visualization. The language (&#8220;c&#8221;, &#8220;cpp&#8221;, or &#8220;fortran&#8221;) is&nbsp;optional (default is &#8220;c&#8221;), it indicates in which language the simulation is written, so that fortran array&nbsp;layouts can be converted into C layouts. The <code>&lt;domain&gt;<\/code> section provides the number of domains that&nbsp;a single process will handle. Some simulations indeed split their variables into blocks (or domains)&nbsp;and distribute several blocks per process. If the number of blocks is not the same on every process, it&nbsp;should correspond to the maximum number of blocks that a single process may have to handle. The <code>&lt;dedicated&gt;<\/code> section provides the number of dedicated cores in a node, and the number of dedicated&nbsp;nodes. These different configurations are explained in more details later.<\/p>\n<p>The <code>&lt;buffer&gt;<\/code> section provides the name and the size (in bytes) of the buffer that will be used for&nbsp;the simulation processes to communicate with the dedicated cores or dedicated nodes. The <code>&lt;buffer name=&gt;<\/code> is important, as it will be used to name the shared memory file. The name must be unique if multiple simulations are running on the same machine or node. It can take an&nbsp;additional <em>type<\/em> attribute which can be either &#8220;<em>posix<\/em>&#8221; (by default) or &#8220;<em>sysv<\/em>&#8220;, representing the type&nbsp;of share memory to be used. POSIX shared memory will use the <em>shm_open<\/em> function, while SYS-V&nbsp;shared memory will use <em>shmget<\/em>. Some HPC operating systems indeed don&#8217;t have one or the other&nbsp;type available. Finally, an additional blocks attribute can be added to the buffer, taking an integer&nbsp;(e.g. blocks=&#8221;3&#8243;) to indicate that several blocks of shared memory must be opened of the given size.&nbsp;This feature is useful in order to open a shared memory buffer with a size bigger than the maximum&nbsp;allowed by the operating system (most operating systems limit this size to 1 or 2 GB). However, a&nbsp;variable can never have a size bigger than the block size (since separate blocks may not be contiguous). This being said, larger shared memory segments are possible on modern HPC operating systems, and the value for size is stored as a 64 bit integer so can cope with larger allocations if applicable.<\/p>\n<p>The <code>&lt;queue&gt;<\/code> section provides the name and size (in number of messages) of the message queues&nbsp;used to communicate between clients and dedicated resources. This is the remnant of older versions&nbsp;of Damaris in which the queue was handled in shared memory as well. Simply keeping the default&nbsp;name and size works just fine.&nbsp;Finally the data and actions sections will be described later.<\/p>\n<p>The <code>&lt;placement \/&gt;<\/code> section is a placeholder for future capabilities of setting where the Damaris core will be within a multicore architecture.<\/p>\n<h4 class=\"p1\">Instrumenting a simulation<\/h4>\n<p class=\"p2\">Now that a minimal configuration file has been provided, let&#8217;s instrument our code.<\/p>\n<h5 class=\"p2\">Writing the code<\/h5>\n<p class=\"p2\">The below listings present the minimal code instrumentation for a C and a Fortran code respectively.&nbsp;All Damaris function calls must be placed after a first call to the <em>damaris_initialize<\/em> function (resp.&nbsp;<em>damaris_initialize_f<\/em> in Fortran). This function takes as parameter an XML configuration file and&nbsp;a MPI communicator (generally MPI_COMM_WORLD). The call to <em>damaris_initialize<\/em> should be&nbsp;placed after the MPI initialization, as it uses MPI functions. <em>damaris_initialize<\/em> involves collective&nbsp;MPI communications: process 0 in the provided communicator will load the XML file and broadcast&nbsp;its content to other processes in the communicator, thus all the processes in the involved communicator&nbsp;should call this function.<\/p>\n<p class=\"p2\">Symmetrically a call to <em>damaris_finalize<\/em> (resp. <em>damaris_finalize_f <\/em>in fortran) frees the resources used&nbsp;by Damaris before the simulation finishes. It should be called before finalizing MPI.<\/p>\n<p class=\"p2\">These functions only prepare <em>Damaris<\/em> but do not start the dedicated cores or nodes. To start&nbsp;the dedicated resources, a call to <em>damaris_start<\/em> (resp. <em>damaris_start_f<\/em>) is necessary. This function&nbsp;takes a pointer to an integer as parameter. <em>Damaris<\/em> will automatically analyze the platform on&nbsp;which the simulation runs and selects cores to be either clients or servers. On client processes, this&nbsp;function returns and the integer is set to a positive value. On server processes, this function blocks&nbsp;and runs the server loop. It will only return when the clients have all called <em>damaris_stop<\/em> (resp.&nbsp;<em>damaris_stop_f<\/em>), and the integer will be set to 0. This integer can thus be used to select whether or&nbsp;not to run the simulation loop on a particular process.<\/p>\n<p class=\"p2\">Inside the simulation&#8217;s main loop, a call to <em>damaris_client_comm_get<\/em> gives a communicator that&nbsp;the clients can use to communicate with one another (i.e., a replacement for the MPI_COMM_WORLD&nbsp;that now includes dedicated resources). It is crucial that the simulation only uses this communicator and not MPI_COMM_WORLD, as global communicator.<\/p>\n<p class=\"p2\"><em>damaris_end_iteration<\/em> (resp. <em>damaris_end_iteration_f<\/em>) informs the dedicated cores that the&nbsp;current iteration has finished. Damaris keeps track of the iteration number internally: this number&nbsp;is equal to 0 before the first call to <em>damaris_end_iteration<\/em>. A call to <em>damaris_end_iteration<\/em> will&nbsp;update potentially connected backends such as VisIt. As it involves collective communications, all the&nbsp;clients have to call this function. These main functions are summarized in tables below.<\/p>\n<table style=\"border: 3px solid #cccccc;\">\n<tbody>\n<tr style=\"border: 3px solid #cccccc;\">\n<td><strong>Ref.<\/strong><\/td>\n<td><strong>Function name (C,C++)<\/strong><\/td>\n<td><strong>Parameters<\/strong><\/td>\n<\/tr>\n<tr>\n<td>1.<\/td>\n<td>damaris_initialize<\/td>\n<td>const char* configfile, MPI Comm comm<\/td>\n<\/tr>\n<tr>\n<td>2.<\/td>\n<td>damaris_start<\/td>\n<td>int* is_client<\/td>\n<\/tr>\n<tr>\n<td>3.<\/td>\n<td>damaris_client_comm_get<\/td>\n<td>MPI_Comm* comm<\/td>\n<\/tr>\n<tr>\n<td>4.<\/td>\n<td>damaris_end_iteration<\/td>\n<td>&#8212;<\/td>\n<\/tr>\n<tr>\n<td>5.<\/td>\n<td>damaris_stop<\/td>\n<td>&#8212;<\/td>\n<\/tr>\n<tr>\n<td>6.<\/td>\n<td>damaris_finalize<\/td>\n<td>&#8212;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<table style=\"border: 3px solid #cccccc;\">\n<tbody>\n<tr style=\"border: 3px solid #cccccc;\">\n<td><strong>Ref.<\/strong><\/td>\n<td><strong>Function name (Fortran)<\/strong><\/td>\n<td><strong>Parameters<\/strong><\/td>\n<\/tr>\n<tr>\n<td>1.<\/td>\n<td>damaris_initialize_f<\/td>\n<td>const char* configfile, MPI Comm comm, integer ierr<\/td>\n<\/tr>\n<tr>\n<td>2.<\/td>\n<td>damaris_start_f<\/td>\n<td>int* is_client, integer ierr<\/td>\n<\/tr>\n<tr>\n<td>3.<\/td>\n<td>damaris_client_comm_get_f<\/td>\n<td>MPI_Comm* comm<\/td>\n<\/tr>\n<tr>\n<td>4.<\/td>\n<td>damaris_end_iteration_f<\/td>\n<td>integer ierr<\/td>\n<\/tr>\n<tr>\n<td>5.<\/td>\n<td>damaris_stop_f<\/td>\n<td>integer ierr<\/td>\n<\/tr>\n<tr>\n<td>6.<\/td>\n<td>damaris_finalize_f<\/td>\n<td>integer ierr<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<pre lang=\"c\" line=\"1\" escaped=\"true\">#include \"Damaris. h\"\n\n\nvoid sim_main_loop (MPI Comm comm)\n{\n    int i ;\n    for ( i =0; i &lt; 100; i++) {\n        \/\/ do something using comm as global communicator\n        damaris_end_iteration( ) ;\n    }\n}\n\nint main ( int argc , char argv )\n{\n    MPI_Init (&amp;argc , &amp;argv ) ;\n    int id = 0 ;\n    int err, is_client;\n    MPI_Comm global ;\n    err = damaris_initialize(\"config.xml\" , MPI_COMM_WORLD) ;\n    damaris_start(&amp;is_client) ;\n    if ( is_client) {\n        damaris_client_comm_get (&amp;global) ;\n        sim_main_loop(global) ;\n        damaris_stop();\n    }\n    damaris_finalize() ;\n    MPI_Finalize();\n    return 0;\n}\n<\/pre>\n<pre lang=\"fortran\" line=\"1\">program sim\ninclude 'mpi f.h'\n\n    integer :: ierr , is_client , comm\n\n    call MPI_ INIT(ierr )\n    call damaris_initialize_f (\"config.xml\" ,MPI_COMM_WORLD, ierr )\n    call damaris_start_f ( is_client , ierr )\n\n    if ( is_client.gt.0 ) then\n        call damaris_client_comm_get (comm, ierr )\n        call sim_main_loop(comm)\n        call damaris_stop_f (ierr)\n    endif\n\n    call damaris_finalize_f (ierr)\n    call MPI_FINALIZE( ierr )\nend program sim\n\nsubroutine sim_main_loop (comm)\n    integer comm\n    integer i , ierr\n    do i = 1 , 100\n        ! do something\n        call damaris_end_iteration_f (ierr )\n    end do\nend subroutine\n<\/pre>\n<p><strong>Note:<\/strong> <em>Damaris<\/em> is not thread-safe yet. If your application uses an hybrid model where different&nbsp;threads run on the same node, only one thread should call damaris initialize (and other Damaris&nbsp;functions), and all the threads on a node should act as one single client.<\/p>\n<h4>Compiling<\/h4>\n<p>An instrumented software must be able to compile and link with the Damaris library. There are various options for this &#8211;<\/p>\n<ol>\n<li>Command line compilation<\/li>\n<li>Integrate with Makefile project<\/li>\n<li>Integrate with CMake project<\/li>\n<li>Integrate with Autotools project<\/li>\n<\/ol>\n<ol>\n<li>Command line compilation<br \/>\nIn order to compile your instrumented simulation, the Damaris header files must be provided, as&nbsp;well as the dependencies&#8217; headers. The following options must be passed to the compiler:<\/p>\n<pre lang=\"bash\" line=\"1\">-I\/damaris\/install\/prefix\/include \n-I\/path\/to\/dependencies\/include\n<\/pre>\n<p>If all dependencies and Damaris have been installed in $HOME\/local, for example, it becomes<\/p>\n<pre lang=\"bash\" line=\"1\">-I$HOME\/local\/include\n<\/pre>\n<p>Fortran programs may look for damaris.mod, which is also in the include directory where Damaris<br \/>\nhas been installed.&nbsp;The linker will need the Damaris library le <em>libdamaris.a<\/em> as well as other dependent libraries. The following provides the necessary options for the linker:<\/p>\n<pre lang=\"bash\" line=\"1\">-rdynamic\n-L\/damaris\/install\/prefix\/lib -ldamaris -L\/path\/to\/dependencies\/lib \\\n-Wl,--whole-archive,-ldamaris,--no-whole-archive \\\n-lxerces-c \\\n-lboost_filesystem -lboost_system -lboost_date_time \\\n-lstdc++ -lrt -ldl\n<\/pre>\n<p>The <em>-rdynamic<\/em> option asks the linker to add all symbols, not only used ones, to the dynamic<br \/>\nsymbol table. This option is needed for some uses of dlopen in Damaris. It is possible that the<br \/>\noption differs with some linkers.<\/p>\n<h5>Running<\/h5>\n<p>The deployment of a Damaris-enabled simulation is that of a normal simulation:<\/p>\n<pre lang=\"bash\" line=\"1\">mpirun -np X -machinefile FILE .\/sim\n<\/pre>\n<p>Where X is the number of processes (in total), and FILE lists the hostnames on which to start the<br \/>\nprogram.<\/li>\n<li>Integrate with Makefile project<br \/>\nHard coding the above in a Makefile is possible, however Damaris provides a damaris.pc file for use with the <em>pkg-config utility<\/em>.<br \/>\nN.B. The damaris.pc file has caused problems, in particular when Damaris is compiled with ParaView Catalyst support.<\/li>\n<li>Integrate with CMake project\n<pre lang=\"bash\" line=\"1\" escaped=\"true\"># Add a CMAke configure time switch to turn on and off use of Damaris\noption(USE_DAMARIS_LIB \"Use the Damaris library for asynchronous I\/O?\" OFF)\n...\n\n# If the switch is ON\nif (USE_DAMARIS_LIB)\n  # Use the CMake find_package() directive to search for Damaris.\n  # The CMake system is looking for the DamarisConfig.cmake file and\n  # may require a hint as to where to find it if it is not in a standard system path. \n  # Use the following on the command line: -DDamaris_ROOT=\/path\/to\/damaris\/\n  # The <em>\/path\/to\/damaris\/<\/em> is the parent directory to the <em>lib<\/em> \n  # and the <em>share<\/em> directories, where Damaris has been installed\n  find_package(Damaris 1.8)\n  if (Damaris_FOUND)\n    include_directories(${Damaris_INCLUDE_DIRS})\n  else()\n    message(STATUS \"The Damaris library was NOT found\")\n  endif()\nendif()\n\n...\nif (USE_DAMARIS_LIB)\n    # Adds the libraries used - Damaris and its dependencies to the link line\n    target_link_libraries(mysim PUBLIC ${Damaris_LIBRARIES})\n    # This creates a definition of a pre-processor variable (PROG_HAS_DAMARIS)\n    # that can be used as a guard in the C\/C++ code:\n    target_compile_definitions(mysim PRIVATE PROG_HAS_DAMARIS)\nendif()\n\n<\/pre>\n<\/li>\n<li>Integrate with Autotools project<br \/>\nAn example of using Autotools is available via the Code_Saturne integration code, which is available through github (account jcbowden) and also available internally in <a href=\"https:\/\/gitlab.inria.fr\/Damaris\/Simulations\/code-saturne-damaris-integration\">https:\/\/gitlab.inria.fr\/Damaris\/Simulations<\/a>. Look for the <code>m4\/cs_damaris.m4<\/code> file in the cs_with_damaris branch. The m4 file tests if Damaris is present on a system and then defines variables: DAMARIS_LDFLAGS DAMARIS_LIBS DAMARISRUNPATH, which are subsequently used in Makefile.am files of source code subdirectories that use the Damaris API.<\/li>\n<\/ol>\n<p><strong>Note:<\/strong> Damaris doesn&#8217;t use any a priori knowledge about rank-to-cores binding and is able to start the proper number of servers at the right location even if process ranks are not consecutive in the nodes. It leverages the processor&#8217;s name to group processes, or platform specific information such as the host&#8217;s personality on BG\/P. Yet, it is important that the same number of cores are used in each node.<\/p>\n<p>Next: <a href=\"https:\/\/project.inria.fr\/damaris\/data-description\/\">Data Description<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This part of the documentation will show you how to instrument a simulation in order to use Damaris. Damaris&nbsp;requires the simulation to be based on MPI. Minimal Configuration File Before actually instrumenting your code, an XML configuration file has to be created. This configuration file will contain some information about&#8230;<\/p>\n<p> <a class=\"continue-reading-link\" href=\"https:\/\/project.inria.fr\/damaris\/instrumenting-a-simulation\/\"><span>Continue reading<\/span><i class=\"crycon-right-dir\"><\/i><\/a> <\/p>\n","protected":false},"author":1249,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-209","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/project.inria.fr\/damaris\/wp-json\/wp\/v2\/pages\/209","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/project.inria.fr\/damaris\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/project.inria.fr\/damaris\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/project.inria.fr\/damaris\/wp-json\/wp\/v2\/users\/1249"}],"replies":[{"embeddable":true,"href":"https:\/\/project.inria.fr\/damaris\/wp-json\/wp\/v2\/comments?post=209"}],"version-history":[{"count":56,"href":"https:\/\/project.inria.fr\/damaris\/wp-json\/wp\/v2\/pages\/209\/revisions"}],"predecessor-version":[{"id":1392,"href":"https:\/\/project.inria.fr\/damaris\/wp-json\/wp\/v2\/pages\/209\/revisions\/1392"}],"wp:attachment":[{"href":"https:\/\/project.inria.fr\/damaris\/wp-json\/wp\/v2\/media?parent=209"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}