diff --git a/CMakeLists.txt b/CMakeLists.txt index 408631984..b7ca95fe0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,7 @@ option(BUILD_TRACE_PLUGIN "Build Trace plugin" ON) option(BUILD_USER_CODE_PLUGIN "Build User-code plugin" ON) option(BUILD_JSON_PLUGIN "Build JSON plugin" OFF) option(ENABLE_BENCHMARKING "Activate benchmarks in the test suite" OFF) - +option(BUILD_DAMARIS_PLUGIN "Build Damaris plug-in" OFF) ### Default build type @@ -122,7 +122,9 @@ if("${BUILD_DECL_HDF5_PLUGIN}") endif() set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" CACHE STRING "" FORCE) - +if("${BUILD_DAMARIS_PLUGIN}") + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/plugins/damaris/cmake") +endif() ### Sanity check @@ -513,6 +515,12 @@ sbuild_add_module(USER_CODE_PLUGIN SUBSTEPS test ) +sbuild_add_module(DAMARIS_PLUGIN + ENABLE_BUILD_FLAG BUILD_DAMARIS_PLUGIN + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/plugins/damaris" + DEPENDS PDI +) + sbuild_add_module(PDI_EXAMPLE ENABLE_BUILD_FLAG BUILD_TESTING SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/example" diff --git a/plugins/damaris/AUTHORS b/plugins/damaris/AUTHORS new file mode 100644 index 000000000..f58f6f3a2 --- /dev/null +++ b/plugins/damaris/AUTHORS @@ -0,0 +1,18 @@ +Multiple people have contributed to PDI Damaris plugin. To show our +appreciation for their public spirit, we list here in alphabetical order a +condensed list of their contributions. + + +Josh Bowden - Inria (joshua-charles.bowden@inria.fr) +* Maintainer (Apr. 2024 - Aug. 2024) +* Design and initial implementation + +Etienne Ndamlabin - Inria (jean-etienne.ndamlabin-mboula@inria.fr) +* Maintainer (Oct. 2024 - ...) +* (Re-)Design and initial implementation + +Jacques Morice - CEA (jacques.morice@cea.fr) +* Developper + +Yushan Wang - CEA (yushan.wang@cea.fr) +* Developper \ No newline at end of file diff --git a/plugins/damaris/CMakeLists.txt b/plugins/damaris/CMakeLists.txt new file mode 100644 index 000000000..c1e6de2de --- /dev/null +++ b/plugins/damaris/CMakeLists.txt @@ -0,0 +1,94 @@ +#============================================================================= +# Copyright (C) 2015-2024 Commissariat a l'energie atomique et aux energies alternatives (CEA) +# Copyright (C) 2024 Institut national de recherche en informatique et en automatique (Inria) +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the names of CEA, nor the names of the contributors may be used to +# endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +cmake_minimum_required(VERSION 3.16...3.29) +project(pdi_damaris_plugin LANGUAGES C CXX) +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") + +# TODO: +include(CTest) + +include(GNUInstallDirs) + +#Damaris +#set(Damaris_DEPS damaris MPI::MPI_C MPI::MPI_CXX) +set(Damaris_DEPS MPI::MPI_C MPI::MPI_CXX) +find_package(Damaris 1.12.0 REQUIRED) +if (Damaris_FOUND) + message(STATUS "The Damaris library was found (${Damaris_LIBRARIES_PATH}/libdamaris.so)") + # Adds the libraries used - Damaris and its dependencies to the link line + # target_link_libraries(mysim PUBLIC ${Damaris_LIBRARIES}) + # This creates a definition of a pre-processor variable (PROG_HAS_DAMARIS) + # that can be used as a guard in the C/C++ code: + # target_compile_definitions(mysim PRIVATE PROG_HAS_DAMARIS) + list(APPEND Damaris_DEPS damaris) + #list(APPEND Damaris_DEPS ${Damaris_LIBRARIES}) + include_directories(${Damaris_INCLUDE_DIRS} ${Damaris_INCLUDE_DIRS}/damaris) + link_directories(${Damaris_LIBRARIES_PATH}) + + + message(STATUS "Embedding RPATH for Damaris: ${Damaris_LIBRARIES_PATH}") + set(CMAKE_INSTALL_RPATH "${Damaris_LIBRARIES_PATH}") + set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) +else() + message(STATUS "The Damaris library was NOT found") +endif() + +# MPI +find_package(MPI REQUIRED COMPONENTS CXX C) + +# PDI +find_package(PDI 1.9.2 REQUIRED COMPONENTS plugins) + +# The plugin +add_library(pdi_damaris_plugin MODULE + damaris.cxx + damaris_cfg.cxx + damaris_wrapper.cxx + damaris_api_call_handler.cxx) +target_link_libraries(pdi_damaris_plugin PUBLIC PDI::PDI_plugins ${Damaris_DEPS}) +set_target_properties(pdi_damaris_plugin PROPERTIES CXX_VISIBILITY_PRESET hidden) + +# installation +set(INSTALL_PDIPLUGINDIR "${PDI_DEFAULT_PLUGINDIR}" CACHE PATH "PDI plugins (${PDI_DEFAULT_PLUGINDIR})") +install(TARGETS pdi_damaris_plugin + LIBRARY DESTINATION "${INSTALL_PDIPLUGINDIR}" +) + +#[===[ TODO +# Tests +if("${BUILD_TESTING}") + add_subdirectory(tests/) +endif() +]===] + +if("${DAMARIS_BUILD_EXAMPLES}") + add_subdirectory(example/) +endif() diff --git a/plugins/damaris/README.md b/plugins/damaris/README.md new file mode 100644 index 000000000..b9816bacc --- /dev/null +++ b/plugins/damaris/README.md @@ -0,0 +1,154 @@ +# The Damaris plugin {#damaris_plugin} + +**WARNING** This documentation is a work in progress and does not reflect the +full potential of this plugin. + +**Add description of damaris plugin** +* For detailed information about the Damaris library, please refer to https://project.inria.fr/damaris/ + + +## Configuration grammar + +**WARNING** The following grammar will be changed in the next version of this plugin (see https://github.com/jmorice91/pdi/issues/42). For this reason, we won't go into the details. + +* `communicator`: (currently not used) A $-expression referencing an MPI communicator. + +* `architecture`: a key-value map. + * `sim_name`(string): the name of the simulation, also used as the name of output files. + * `domains`(integer, default: 1) : number of blocks by sub domain + * `dedicated`: describes what resources will be used by Damaris. Notes: for the momenet, the plugin works with either `only dedicated cores`, or `only dedicated nodes`. In the future, the mix of two modes will be supported. + * `core`: an integer value (default 0). Number of cores per node that will be used for Damaris + * `node`: an integer value (default 0). Number of nodes that will be used for Damaris. + +* `client_comm_get`(string) identifier of the communicator which includes all Damaris client processes +* `get_is_client` (string) identifier of data which differentiates the Damaris client processes from Damaris server processes (only needed with explicite use of Damaris client, c.f. section below) + +* `datasets`: list of `DATASET_DESC` + * `dataset`: a key value map that represent a datset that will be shared with damaris. It is composed with + * `name`(string): name of the dataset + * `layout`(string): name of the layout of this dataset + * `storage`(string): name of the storage of this dataset + +* `layouts`: list of `LAYOUT_DESC`. + * `layout:` a key value map that represent a layout defined in a `dataset`. It is composed with + * `name` (string) name of the layout + * `type` (string) type of an element of the dataset. e.g. `double`, `int`, `float`, etc + * `global`: Global size of the layout. + * `dimensions`: Local (subdomain) size of the layout with ghosts layers. + * `ghosts`: Number of ghost layers for each dimension. "," separates two dimensions and ":" separates left and right layers in each dimension + * `depends_on`: List of data used to update the layout attributes. This is necessary because Damaris servers need this data to correctly set the `global` and `dimensions` values, on its side. + +* `storages`: list of `STORAGE_DESC`. + * `storage:` a key value map that represent the storage used to save a`dataset`. It is composed with + * `name` (string) Name of the storage + * `type` Type of the output file. Currently only HDF5 is supported. + * `file_mode` (Collective or FilePerCore) + * `files_path` Path for the output files + +* `write`: list of data that will be write on the disk by damaris. Each data is composed with + * `dataset`: The dataset in which the data will be written. + * `position`: The starting position of the data (for each client process) with repect to the dataset. + +* `log`: a key value map that specifies the logger information of Damaris. This feature is optional. + * `file_name`(string) The beginning of the log filename. By default is the value of damaris/architecture/sim_name and the default folder is "where_you_launch_the_script/log". The suffix of the filename is `_P#proc_#iter.log` where #proc represents the MPI rank of the Damaris server process. #iter is the number of iterations. + * `rotation_size` (integer): 5 + * `log_level`(string): level of the logger. The value is one of: trace, debug, info, warning, error and fatal + * `flush`(true or false): Forces the log file to be flushed if set to true . + +**Question: what is the other type for a storage ( hdf5, ...)** + +* the keyword `when:` can be used in `write` to define the frequency? +* For the layout structure, we can use the same definition as decl_hdf5 plugin to be homogenous ==> modularity between plugin !! +* In decl_hdf5 plugin in some sense, layout and write can be "fusioné"? + +All this points can be adressed in a new version in damaris plugin. + +## How to use the Damaris plugin with a simulation? + +There are two ways to initialize Damaris client from the simualtion. + +### Implicit use of Damaris client +```c++ + +MPI_Init(...); +PDI_init(...); + +PDI_expose("comm", &comm, PDI_INOUT); // <-- allow plugin to set, and return Damaris client communicator +// all simulation codes follow here +// use comm as the default communicator for the rest of the simulation +// only the Damaris client processes are available here +PDI_finalize(); +MPI_Finalize(); // Damaris server processes will be finalized by the end of the damaris_plugin +``` + +### Explicit use of Damaris client +```c++ + +MPI_Init(...); +PDI_init(...); +int is_client; +PDI_expose("is_client", &is_client, PDI_INOUT); // Only Damaris client will have is_client=1 +PDI_expose("comm", &comm, PDI_INOUT); // <-- allow plugin to set, and return Damaris client communicator +if(is_client) { + // use comm as the default communicator for the rest of the simulation + // all simulation codes should be inside this block +} +// all MPI processes are available here (both Damaris clients and servers) +PDI_finalize(); +MPI_Finalize(); +``` + +With explicit use of Damaris client, one has to add a few lines in the yaml file: +```yaml +metadata: + is_client: int +damaris: + get_is_client: is_client # identifier of data which differentiates the Damaris client processes from Damaris server processes +``` + +* [to be modified] the communicator will be split by Damaris in a communicator for the simulation code (client) +and for damaris execution (client). +* `get_is_client`(string): name of `is_client` in PDI data store (optional). It is requested only with `is_client` is used. +* `client_comm_get` (string): name of mpi communicator of simulation code (client) in PDI data store (requested). This communicator is defined by Damaris after the split of the communicator defined in `communicator` between client and server. + + +## full configuration example + +```yaml +damaris: + architecture: + sim_name: example + domains: 1 + dedicated: + core: 1 + client_comm_get: comm + datasets: + - dataset: + name: main_field + layout: main_field_layout + storage: hdf5_example + layouts: + - layout: + name: main_field_layout + type: double + global: ['$psize[0]*($dsize[0]-2),$psize[1]*($dsize[1]-2)'] + dimensions: [ '$dsize[0]', '$dsize[1]' ] + ghosts: '1:1,1:1' + depends_on: [dsize, psize] + storages: + - storage: + name: hdf5_example + type: HDF5 + file_mode: Collective + files_path: ./HDF5_files_damaris/ + write: + main_field: + dataset: main_field + when: '$iter<10' + position: ['($dsize[0]-2)*$pcoord[0]', '($dsize[1]-2)*$pcoord[1]'] + log: + file_name: example + rotation_size: 5 + log_level: info + flush: true +``` diff --git a/plugins/damaris/cmake/DamarisPluginUtils.cmake b/plugins/damaris/cmake/DamarisPluginUtils.cmake new file mode 100644 index 000000000..b0b9fb1c2 --- /dev/null +++ b/plugins/damaris/cmake/DamarisPluginUtils.cmake @@ -0,0 +1,37 @@ +# Function to compare two versions +# Returns 1 if version1 >= version2, 0 otherwise +function(version_greater_equal version1 version2 result) + string(REGEX MATCHALL "[0-9]+" v1_parts "${version1}") + string(REGEX MATCHALL "[0-9]+" v2_parts "${version2}") + + # pad missing parts with 0 + list(LENGTH v1_parts len1) + list(LENGTH v2_parts len2) + if(len1 LESS 3) + math(EXPR pad "3 - ${len1}") + foreach(_i RANGE ${pad}) + list(APPEND v1_parts 0) + endforeach() + endif() + if(len2 LESS 3) + math(EXPR pad "3 - ${len2}") + foreach(_i RANGE ${pad}) + list(APPEND v2_parts 0) + endforeach() + endif() + + set(_result 1) + foreach(i RANGE 0 2) + list(GET v1_parts ${i} v1i) + list(GET v2_parts ${i} v2i) + if(v1i GREATER v2i) + set(_result 1) + break() + elseif(v1i LESS v2i) + set(_result 0) + break() + endif() + endforeach() + + set(${result} ${_result} PARENT_SCOPE) +endfunction() diff --git a/plugins/damaris/cmake/FindDamaris.cmake b/plugins/damaris/cmake/FindDamaris.cmake new file mode 100644 index 000000000..85155fc0d --- /dev/null +++ b/plugins/damaris/cmake/FindDamaris.cmake @@ -0,0 +1,92 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FindDamaris +-------- + +Find Damaris, a library for data managmement and visualisation that provides +asynchronous, in situ processing capabilities to MPI based simulation codes. +#]=======================================================================] + +#include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations.cmake) +#include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) +include(FindPackageHandleStandardArgs) +include(DamarisPluginUtils) + +set(Damaris_BASE_DIR /usr/local/lib/damaris /usr/lib/damaris /opt/damaris) +set(Damaris_VERSIONS 1.12.0) +set(MIN_REQUIRED_VERSIONS "1.12.0") + +#Find Damaris base install dir +set(Damaris_CANDIDATES ${Damaris_ROOT}) +foreach(base_dir ${Damaris_BASE_DIR}) + if(EXISTS "${base_dir}" AND IS_DIRECTORY "${base_dir}") + list(APPEND Damaris_CANDIDATES ${base_dir}) #In case no version dir exists + + if(DEFINED Damaris_VERSION) + message(STATUS "Provided damaris version: ${Damaris_VERSION}") + string(REGEX MATCH "^[0-9]+\\.[0-9]+\\.[0-9]+$" IS_VERSION "${Damaris_VERSION}") + + if(IS_VERSION AND (EXISTS "${base_dir}/${Damaris_VERSION}" AND IS_DIRECTORY "${base_dir}/${Damaris_VERSION}")) + version_greater_equal(${Damaris_VERSION} ${MIN_REQUIRED_VERSIONS} IS_GE) + + if(IS_GE) + list(APPEND Damaris_CANDIDATES ${base_dir}/${FOLDER_NAME}) + else() + message(STATUS "damaris_plugin requires at least version \"1.12.0\", provided: ${Damaris_VERSION}") + endif() + elseif(IS_VERSION) + message(STATUS "No Damaris installation with version \"${Damaris_VERSION}\" folder!") + endif() + else() + # Get all subdirectories + file(GLOB SUBFOLDERS LIST_DIRECTORIES true "${base_dir}/*") + + foreach(SUB "${SUBFOLDERS}") + # Get the folder name only (without path) + get_filename_component(FOLDER_NAME "${SUB}" NAME) + + # Check if the folder name matches x.y.z + string(REGEX MATCH "^[0-9]+\\.[0-9]+\\.[0-9]+$" IS_VERSION "${FOLDER_NAME}") + + if(IS_VERSION) + version_greater_equal(${FOLDER_NAME} ${MIN_REQUIRED_VERSIONS} IS_GE) + + if(IS_GE) + list(APPEND Damaris_CANDIDATES ${base_dir}/${FOLDER_NAME}) + #else() + endif() + endif() + endforeach() + endif() + + #foreach(version ${Damaris_VERSIONS}) + #list(APPEND Damaris_CANDIDATES ${base_dir}/${version}) + #endforeach() + endif() +endforeach() + +set(Damaris_INC_SUFFIXES) +set(Damaris_LIB_SUFFIXES) +foreach(version ${Damaris_VERSIONS}) + list(APPEND Damaris_INC_SUFFIXES include) + list(APPEND Damaris_LIB_SUFFIXES lib) +endforeach() + +find_path(Damaris_INCLUDE_DIRS Damaris.h + HINTS ${Damaris_CANDIDATES} + PATH_SUFFIXES ${Damaris_INC_SUFFIXES} + ENV CPLUS_INCLUDE_PATH) +find_library(Damaris_LIBRARIES damaris + HINTS ${Damaris_CANDIDATES} + PATH_SUFFIXES ${Damaris_LIB_SUFFIXES} + ENV LIBRARY_PATH LD_LIBRARY_PATH) + +find_package_handle_standard_args(Damaris DEFAULT_MSG Damaris_INCLUDE_DIRS + Damaris_LIBRARIES) +mark_as_advanced(Damaris_INCLUDE_DIRS Damaris_LIBRARIES) +if(Damaris_INCLUDE_DIRS) + set(Damaris_FOUND TRUE) + get_filename_component(Damaris_LIBRARIES_PATH ${Damaris_LIBRARIES} DIRECTORY) +endif(Damaris_INCLUDE_DIRS) \ No newline at end of file diff --git a/plugins/damaris/cmake/FindPackageHandleStandardArgs.cmake b/plugins/damaris/cmake/FindPackageHandleStandardArgs.cmake new file mode 100644 index 000000000..4fb08259a --- /dev/null +++ b/plugins/damaris/cmake/FindPackageHandleStandardArgs.cmake @@ -0,0 +1,466 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FindPackageHandleStandardArgs +----------------------------- + +This module provides a function intended to be used in :ref:`Find Modules` +implementing :command:`find_package()` calls. It handles the +``REQUIRED``, ``QUIET`` and version-related arguments of ``find_package``. +It also sets the ``_FOUND`` variable. The package is +considered found if all variables listed contain valid results, e.g. +valid filepaths. + +.. command:: find_package_handle_standard_args + + There are two signatures:: + + find_package_handle_standard_args( + (DEFAULT_MSG|) + ... + ) + + find_package_handle_standard_args( + [FOUND_VAR ] + [REQUIRED_VARS ...] + [VERSION_VAR ] + [HANDLE_COMPONENTS] + [CONFIG_MODE] + [NAME_MISMATCHED] + [REASON_FAILURE_MESSAGE ] + [FAIL_MESSAGE ] + ) + + The ``_FOUND`` variable will be set to ``TRUE`` if all + the variables ``...`` are valid and any optional + constraints are satisfied, and ``FALSE`` otherwise. A success or + failure message may be displayed based on the results and on + whether the ``REQUIRED`` and/or ``QUIET`` option was given to + the :command:`find_package` call. + + The options are: + + ``(DEFAULT_MSG|)`` + In the simple signature this specifies the failure message. + Use ``DEFAULT_MSG`` to ask for a default message to be computed + (recommended). Not valid in the full signature. + + ``FOUND_VAR `` + Obsolete. Specifies either ``_FOUND`` or + ``_FOUND`` as the result variable. This exists only + for compatibility with older versions of CMake and is now ignored. + Result variables of both names are always set for compatibility. + + ``REQUIRED_VARS ...`` + Specify the variables which are required for this package. + These may be named in the generated failure message asking the + user to set the missing variable values. Therefore these should + typically be cache entries such as ``FOO_LIBRARY`` and not output + variables like ``FOO_LIBRARIES``. This option is mandatory if + ``HANDLE_COMPONENTS`` is not specified. + + ``VERSION_VAR `` + Specify the name of a variable that holds the version of the package + that has been found. This version will be checked against the + (potentially) specified required version given to the + :command:`find_package` call, including its ``EXACT`` option. + The default messages include information about the required + version and the version which has been actually found, both + if the version is ok or not. + + ``HANDLE_COMPONENTS`` + Enable handling of package components. In this case, the command + will report which components have been found and which are missing, + and the ``_FOUND`` variable will be set to ``FALSE`` + if any of the required components (i.e. not the ones listed after + the ``OPTIONAL_COMPONENTS`` option of :command:`find_package`) are + missing. + + ``CONFIG_MODE`` + Specify that the calling find module is a wrapper around a + call to ``find_package( NO_MODULE)``. This implies + a ``VERSION_VAR`` value of ``_VERSION``. The command + will automatically check whether the package configuration file + was found. + + ``REASON_FAILURE_MESSAGE `` + Specify a custom message of the reason for the failure which will be + appended to the default generated message. + + ``FAIL_MESSAGE `` + Specify a custom failure message instead of using the default + generated message. Not recommended. + + ``NAME_MISMATCHED`` + Indicate that the ```` does not match + ``${CMAKE_FIND_PACKAGE_NAME}``. This is usually a mistake and raises a + warning, but it may be intentional for usage of the command for components + of a larger package. + +Example for the simple signature: + +.. code-block:: cmake + + find_package_handle_standard_args(LibXml2 DEFAULT_MSG + LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR) + +The ``LibXml2`` package is considered to be found if both +``LIBXML2_LIBRARY`` and ``LIBXML2_INCLUDE_DIR`` are valid. +Then also ``LibXml2_FOUND`` is set to ``TRUE``. If it is not found +and ``REQUIRED`` was used, it fails with a +:command:`message(FATAL_ERROR)`, independent whether ``QUIET`` was +used or not. If it is found, success will be reported, including +the content of the first ````. On repeated CMake runs, +the same message will not be printed again. + +.. note:: + + If ```` does not match ``CMAKE_FIND_PACKAGE_NAME`` for the + calling module, a warning that there is a mismatch is given. The + ``FPHSA_NAME_MISMATCHED`` variable may be set to bypass the warning if using + the old signature and the ``NAME_MISMATCHED`` argument using the new + signature. To avoid forcing the caller to require newer versions of CMake for + usage, the variable's value will be used if defined when the + ``NAME_MISMATCHED`` argument is not passed for the new signature (but using + both is an error).. + +Example for the full signature: + +.. code-block:: cmake + + find_package_handle_standard_args(LibArchive + REQUIRED_VARS LibArchive_LIBRARY LibArchive_INCLUDE_DIR + VERSION_VAR LibArchive_VERSION) + +In this case, the ``LibArchive`` package is considered to be found if +both ``LibArchive_LIBRARY`` and ``LibArchive_INCLUDE_DIR`` are valid. +Also the version of ``LibArchive`` will be checked by using the version +contained in ``LibArchive_VERSION``. Since no ``FAIL_MESSAGE`` is given, +the default messages will be printed. + +Another example for the full signature: + +.. code-block:: cmake + + find_package(Automoc4 QUIET NO_MODULE HINTS /opt/automoc4) + find_package_handle_standard_args(Automoc4 CONFIG_MODE) + +In this case, a ``FindAutmoc4.cmake`` module wraps a call to +``find_package(Automoc4 NO_MODULE)`` and adds an additional search +directory for ``automoc4``. Then the call to +``find_package_handle_standard_args`` produces a proper success/failure +message. +#]=======================================================================] + +include(${CMAKE_CURRENT_LIST_DIR}/FindPackageMessage.cmake) + +# internal helper macro +macro(_FPHSA_FAILURE_MESSAGE _msg) + set (__msg "${_msg}") + if (FPHSA_REASON_FAILURE_MESSAGE) + string(APPEND __msg "\n Reason given by package: ${FPHSA_REASON_FAILURE_MESSAGE}\n") + endif() + if (${_NAME}_FIND_REQUIRED) + message(FATAL_ERROR "${__msg}") + else () + if (NOT ${_NAME}_FIND_QUIETLY) + message(STATUS "${__msg}") + endif () + endif () +endmacro() + + +# internal helper macro to generate the failure message when used in CONFIG_MODE: +macro(_FPHSA_HANDLE_FAILURE_CONFIG_MODE) + # _CONFIG is set, but FOUND is false, this means that some other of the REQUIRED_VARS was not found: + if(${_NAME}_CONFIG) + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: missing:${MISSING_VARS} (found ${${_NAME}_CONFIG} ${VERSION_MSG})") + else() + # If _CONSIDERED_CONFIGS is set, the config-file has been found, but no suitable version. + # List them all in the error message: + if(${_NAME}_CONSIDERED_CONFIGS) + set(configsText "") + list(LENGTH ${_NAME}_CONSIDERED_CONFIGS configsCount) + math(EXPR configsCount "${configsCount} - 1") + foreach(currentConfigIndex RANGE ${configsCount}) + list(GET ${_NAME}_CONSIDERED_CONFIGS ${currentConfigIndex} filename) + list(GET ${_NAME}_CONSIDERED_VERSIONS ${currentConfigIndex} version) + string(APPEND configsText "\n ${filename} (version ${version})") + endforeach() + if (${_NAME}_NOT_FOUND_MESSAGE) + if (FPHSA_REASON_FAILURE_MESSAGE) + string(PREPEND FPHSA_REASON_FAILURE_MESSAGE "${${_NAME}_NOT_FOUND_MESSAGE}\n ") + else() + set(FPHSA_REASON_FAILURE_MESSAGE "${${_NAME}_NOT_FOUND_MESSAGE}") + endif() + else() + string(APPEND configsText "\n") + endif() + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:${configsText}") + + else() + # Simple case: No Config-file was found at all: + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: found neither ${_NAME}Config.cmake nor ${_NAME_LOWER}-config.cmake ${VERSION_MSG}") + endif() + endif() +endmacro() + + +function(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG) + + # Set up the arguments for `cmake_parse_arguments`. + set(options CONFIG_MODE HANDLE_COMPONENTS NAME_MISMATCHED) + set(oneValueArgs FAIL_MESSAGE REASON_FAILURE_MESSAGE VERSION_VAR FOUND_VAR) + set(multiValueArgs REQUIRED_VARS) + + # Check whether we are in 'simple' or 'extended' mode: + set(_KEYWORDS_FOR_EXTENDED_MODE ${options} ${oneValueArgs} ${multiValueArgs} ) + list(FIND _KEYWORDS_FOR_EXTENDED_MODE "${_FIRST_ARG}" INDEX) + + unset(FPHSA_NAME_MISMATCHED_override) + if (DEFINED FPHSA_NAME_MISMATCHED) + # If the variable NAME_MISMATCHED variable is set, error if it is passed as + # an argument. The former is for old signatures, the latter is for new + # signatures. + list(FIND ARGN "NAME_MISMATCHED" name_mismatched_idx) + if (NOT name_mismatched_idx EQUAL "-1") + message(FATAL_ERROR + "The `NAME_MISMATCHED` argument may only be specified by the argument or " + "the variable, not both.") + endif () + + # But use the variable if it is not an argument to avoid forcing minimum + # CMake version bumps for calling modules. + set(FPHSA_NAME_MISMATCHED_override "${FPHSA_NAME_MISMATCHED}") + endif () + + if(${INDEX} EQUAL -1) + set(FPHSA_FAIL_MESSAGE ${_FIRST_ARG}) + set(FPHSA_REQUIRED_VARS ${ARGN}) + set(FPHSA_VERSION_VAR) + else() + cmake_parse_arguments(FPHSA "${options}" "${oneValueArgs}" "${multiValueArgs}" ${_FIRST_ARG} ${ARGN}) + + if(FPHSA_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \"${FPHSA_UNPARSED_ARGUMENTS}\"") + endif() + + if(NOT FPHSA_FAIL_MESSAGE) + set(FPHSA_FAIL_MESSAGE "DEFAULT_MSG") + endif() + + # In config-mode, we rely on the variable _CONFIG, which is set by find_package() + # when it successfully found the config-file, including version checking: + if(FPHSA_CONFIG_MODE) + list(INSERT FPHSA_REQUIRED_VARS 0 ${_NAME}_CONFIG) + list(REMOVE_DUPLICATES FPHSA_REQUIRED_VARS) + set(FPHSA_VERSION_VAR ${_NAME}_VERSION) + endif() + + if(NOT FPHSA_REQUIRED_VARS AND NOT FPHSA_HANDLE_COMPONENTS) + message(FATAL_ERROR "No REQUIRED_VARS specified for FIND_PACKAGE_HANDLE_STANDARD_ARGS()") + endif() + endif() + + if (DEFINED FPHSA_NAME_MISMATCHED_override) + set(FPHSA_NAME_MISMATCHED "${FPHSA_NAME_MISMATCHED_override}") + endif () + + if (DEFINED CMAKE_FIND_PACKAGE_NAME + AND NOT FPHSA_NAME_MISMATCHED + AND NOT _NAME STREQUAL CMAKE_FIND_PACKAGE_NAME) + message(AUTHOR_WARNING + "The package name passed to `find_package_handle_standard_args` " + "(${_NAME}) does not match the name of the calling package " + "(${CMAKE_FIND_PACKAGE_NAME}). This can lead to problems in calling " + "code that expects `find_package` result variables (e.g., `_FOUND`) " + "to follow a certain pattern.") + endif () + +# now that we collected all arguments, process them + + if("x${FPHSA_FAIL_MESSAGE}" STREQUAL "xDEFAULT_MSG") + set(FPHSA_FAIL_MESSAGE "Could NOT find ${_NAME}") + endif() + + if (FPHSA_REQUIRED_VARS) + list(GET FPHSA_REQUIRED_VARS 0 _FIRST_REQUIRED_VAR) + endif() + + string(TOUPPER ${_NAME} _NAME_UPPER) + string(TOLOWER ${_NAME} _NAME_LOWER) + + if(FPHSA_FOUND_VAR) + set(_FOUND_VAR_UPPER ${_NAME_UPPER}_FOUND) + set(_FOUND_VAR_MIXED ${_NAME}_FOUND) + if(FPHSA_FOUND_VAR STREQUAL _FOUND_VAR_MIXED OR FPHSA_FOUND_VAR STREQUAL _FOUND_VAR_UPPER) + set(_FOUND_VAR ${FPHSA_FOUND_VAR}) + else() + message(FATAL_ERROR "The argument for FOUND_VAR is \"${FPHSA_FOUND_VAR}\", but only \"${_FOUND_VAR_MIXED}\" and \"${_FOUND_VAR_UPPER}\" are valid names.") + endif() + else() + set(_FOUND_VAR ${_NAME_UPPER}_FOUND) + endif() + + # collect all variables which were not found, so they can be printed, so the + # user knows better what went wrong (#6375) + set(MISSING_VARS "") + set(DETAILS "") + # check if all passed variables are valid + set(FPHSA_FOUND_${_NAME} TRUE) + foreach(_CURRENT_VAR ${FPHSA_REQUIRED_VARS}) + if(NOT ${_CURRENT_VAR}) + set(FPHSA_FOUND_${_NAME} FALSE) + string(APPEND MISSING_VARS " ${_CURRENT_VAR}") + else() + string(APPEND DETAILS "[${${_CURRENT_VAR}}]") + endif() + endforeach() + if(FPHSA_FOUND_${_NAME}) + set(${_NAME}_FOUND TRUE) + set(${_NAME_UPPER}_FOUND TRUE) + else() + set(${_NAME}_FOUND FALSE) + set(${_NAME_UPPER}_FOUND FALSE) + endif() + + # component handling + unset(FOUND_COMPONENTS_MSG) + unset(MISSING_COMPONENTS_MSG) + + if(FPHSA_HANDLE_COMPONENTS) + foreach(comp ${${_NAME}_FIND_COMPONENTS}) + if(${_NAME}_${comp}_FOUND) + + if(NOT DEFINED FOUND_COMPONENTS_MSG) + set(FOUND_COMPONENTS_MSG "found components:") + endif() + string(APPEND FOUND_COMPONENTS_MSG " ${comp}") + + else() + + if(NOT DEFINED MISSING_COMPONENTS_MSG) + set(MISSING_COMPONENTS_MSG "missing components:") + endif() + string(APPEND MISSING_COMPONENTS_MSG " ${comp}") + + if(${_NAME}_FIND_REQUIRED_${comp}) + set(${_NAME}_FOUND FALSE) + string(APPEND MISSING_VARS " ${comp}") + endif() + + endif() + endforeach() + set(COMPONENT_MSG "${FOUND_COMPONENTS_MSG} ${MISSING_COMPONENTS_MSG}") + string(APPEND DETAILS "[c${COMPONENT_MSG}]") + endif() + + # version handling: + set(VERSION_MSG "") + set(VERSION_OK TRUE) + + # check with DEFINED here as the requested or found version may be "0" + if (DEFINED ${_NAME}_FIND_VERSION) + if(DEFINED ${FPHSA_VERSION_VAR}) + set(_FOUND_VERSION ${${FPHSA_VERSION_VAR}}) + + if(${_NAME}_FIND_VERSION_EXACT) # exact version required + # count the dots in the version string + string(REGEX REPLACE "[^.]" "" _VERSION_DOTS "${_FOUND_VERSION}") + # add one dot because there is one dot more than there are components + string(LENGTH "${_VERSION_DOTS}." _VERSION_DOTS) + if (_VERSION_DOTS GREATER ${_NAME}_FIND_VERSION_COUNT) + # Because of the C++ implementation of find_package() ${_NAME}_FIND_VERSION_COUNT + # is at most 4 here. Therefore a simple lookup table is used. + if (${_NAME}_FIND_VERSION_COUNT EQUAL 1) + set(_VERSION_REGEX "[^.]*") + elseif (${_NAME}_FIND_VERSION_COUNT EQUAL 2) + set(_VERSION_REGEX "[^.]*\\.[^.]*") + elseif (${_NAME}_FIND_VERSION_COUNT EQUAL 3) + set(_VERSION_REGEX "[^.]*\\.[^.]*\\.[^.]*") + else () + set(_VERSION_REGEX "[^.]*\\.[^.]*\\.[^.]*\\.[^.]*") + endif () + string(REGEX REPLACE "^(${_VERSION_REGEX})\\..*" "\\1" _VERSION_HEAD "${_FOUND_VERSION}") + unset(_VERSION_REGEX) + if (NOT ${_NAME}_FIND_VERSION VERSION_EQUAL _VERSION_HEAD) + set(VERSION_MSG "Found unsuitable version \"${_FOUND_VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"") + set(VERSION_OK FALSE) + else () + set(VERSION_MSG "(found suitable exact version \"${_FOUND_VERSION}\")") + endif () + unset(_VERSION_HEAD) + else () + if (NOT ${_NAME}_FIND_VERSION VERSION_EQUAL _FOUND_VERSION) + set(VERSION_MSG "Found unsuitable version \"${_FOUND_VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"") + set(VERSION_OK FALSE) + else () + set(VERSION_MSG "(found suitable exact version \"${_FOUND_VERSION}\")") + endif () + endif () + unset(_VERSION_DOTS) + + else() # minimum version specified: + if (${_NAME}_FIND_VERSION VERSION_GREATER _FOUND_VERSION) + set(VERSION_MSG "Found unsuitable version \"${_FOUND_VERSION}\", but required is at least \"${${_NAME}_FIND_VERSION}\"") + set(VERSION_OK FALSE) + else () + set(VERSION_MSG "(found suitable version \"${_FOUND_VERSION}\", minimum required is \"${${_NAME}_FIND_VERSION}\")") + endif () + endif() + + else() + + # if the package was not found, but a version was given, add that to the output: + if(${_NAME}_FIND_VERSION_EXACT) + set(VERSION_MSG "(Required is exact version \"${${_NAME}_FIND_VERSION}\")") + else() + set(VERSION_MSG "(Required is at least version \"${${_NAME}_FIND_VERSION}\")") + endif() + + endif() + else () + # Check with DEFINED as the found version may be 0. + if(DEFINED ${FPHSA_VERSION_VAR}) + set(VERSION_MSG "(found version \"${${FPHSA_VERSION_VAR}}\")") + endif() + endif () + + if(VERSION_OK) + string(APPEND DETAILS "[v${${FPHSA_VERSION_VAR}}(${${_NAME}_FIND_VERSION})]") + else() + set(${_NAME}_FOUND FALSE) + endif() + + + # print the result: + if (${_NAME}_FOUND) + FIND_PACKAGE_MESSAGE(${_NAME} "Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG} ${COMPONENT_MSG}" "${DETAILS}") + else () + + if(FPHSA_CONFIG_MODE) + _FPHSA_HANDLE_FAILURE_CONFIG_MODE() + else() + if(NOT VERSION_OK) + set(RESULT_MSG) + if (_FIRST_REQUIRED_VAR) + string (APPEND RESULT_MSG "found ${${_FIRST_REQUIRED_VAR}}") + endif() + if (COMPONENT_MSG) + if (RESULT_MSG) + string (APPEND RESULT_MSG ", ") + endif() + string (APPEND RESULT_MSG "${FOUND_COMPONENTS_MSG}") + endif() + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: ${VERSION_MSG} (${RESULT_MSG})") + else() + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} (missing:${MISSING_VARS}) ${VERSION_MSG}") + endif() + endif() + + endif () + + set(${_NAME}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE) + set(${_NAME_UPPER}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE) +endfunction() diff --git a/plugins/damaris/cmake/FindPackageMessage.cmake b/plugins/damaris/cmake/FindPackageMessage.cmake new file mode 100644 index 000000000..0628b9816 --- /dev/null +++ b/plugins/damaris/cmake/FindPackageMessage.cmake @@ -0,0 +1,48 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FindPackageMessage +------------------ + +.. code-block:: cmake + + find_package_message( "message for user" "find result details") + +This function is intended to be used in FindXXX.cmake modules files. +It will print a message once for each unique find result. This is +useful for telling the user where a package was found. The first +argument specifies the name (XXX) of the package. The second argument +specifies the message to display. The third argument lists details +about the find result so that if they change the message will be +displayed again. The macro also obeys the QUIET argument to the +find_package command. + +Example: + +.. code-block:: cmake + + if(X11_FOUND) + find_package_message(X11 "Found X11: ${X11_X11_LIB}" + "[${X11_X11_LIB}][${X11_INCLUDE_DIR}]") + else() + ... + endif() +#]=======================================================================] + +function(find_package_message pkg msg details) + # Avoid printing a message repeatedly for the same find result. + if(NOT ${pkg}_FIND_QUIETLY) + string(REPLACE "\n" "" details "${details}") + set(DETAILS_VAR FIND_PACKAGE_MESSAGE_DETAILS_${pkg}) + if(NOT "${details}" STREQUAL "${${DETAILS_VAR}}") + # The message has not yet been printed. + message(STATUS "${msg}") + + # Save the find details in the cache to avoid printing the same + # message again. + set("${DETAILS_VAR}" "${details}" + CACHE INTERNAL "Details about finding ${pkg}") + endif() + endif() +endfunction() diff --git a/plugins/damaris/cmake/SelectLibraryConfigurations.cmake b/plugins/damaris/cmake/SelectLibraryConfigurations.cmake new file mode 100644 index 000000000..4c0e9a8c0 --- /dev/null +++ b/plugins/damaris/cmake/SelectLibraryConfigurations.cmake @@ -0,0 +1,80 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +SelectLibraryConfigurations +--------------------------- + +.. code-block:: cmake + + select_library_configurations(basename) + +This macro takes a library base name as an argument, and will choose +good values for the variables + +:: + + basename_LIBRARY + basename_LIBRARIES + basename_LIBRARY_DEBUG + basename_LIBRARY_RELEASE + +depending on what has been found and set. + +If only ``basename_LIBRARY_RELEASE`` is defined, ``basename_LIBRARY`` will +be set to the release value, and ``basename_LIBRARY_DEBUG`` will be set +to ``basename_LIBRARY_DEBUG-NOTFOUND``. If only ``basename_LIBRARY_DEBUG`` +is defined, then ``basename_LIBRARY`` will take the debug value, and +``basename_LIBRARY_RELEASE`` will be set to ``basename_LIBRARY_RELEASE-NOTFOUND``. + +If the generator supports configuration types, then ``basename_LIBRARY`` +and ``basename_LIBRARIES`` will be set with debug and optimized flags +specifying the library to be used for the given configuration. If no +build type has been set or the generator in use does not support +configuration types, then ``basename_LIBRARY`` and ``basename_LIBRARIES`` +will take only the release value, or the debug value if the release one +is not set. +#]=======================================================================] + +# This macro was adapted from the FindQt4 CMake module and is maintained by Will +# Dicharry . + +macro(select_library_configurations basename) + if(NOT ${basename}_LIBRARY_RELEASE) + set(${basename}_LIBRARY_RELEASE "${basename}_LIBRARY_RELEASE-NOTFOUND" CACHE FILEPATH "Path to a library.") + endif() + if(NOT ${basename}_LIBRARY_DEBUG) + set(${basename}_LIBRARY_DEBUG "${basename}_LIBRARY_DEBUG-NOTFOUND" CACHE FILEPATH "Path to a library.") + endif() + + get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if( ${basename}_LIBRARY_DEBUG AND ${basename}_LIBRARY_RELEASE AND + NOT ${basename}_LIBRARY_DEBUG STREQUAL ${basename}_LIBRARY_RELEASE AND + ( _isMultiConfig OR CMAKE_BUILD_TYPE ) ) + # if the generator is multi-config or if CMAKE_BUILD_TYPE is set for + # single-config generators, set optimized and debug libraries + set( ${basename}_LIBRARY "" ) + foreach( _libname IN LISTS ${basename}_LIBRARY_RELEASE ) + list( APPEND ${basename}_LIBRARY optimized "${_libname}" ) + endforeach() + foreach( _libname IN LISTS ${basename}_LIBRARY_DEBUG ) + list( APPEND ${basename}_LIBRARY debug "${_libname}" ) + endforeach() + elseif( ${basename}_LIBRARY_RELEASE ) + set( ${basename}_LIBRARY ${${basename}_LIBRARY_RELEASE} ) + elseif( ${basename}_LIBRARY_DEBUG ) + set( ${basename}_LIBRARY ${${basename}_LIBRARY_DEBUG} ) + else() + set( ${basename}_LIBRARY "${basename}_LIBRARY-NOTFOUND") + endif() + + set( ${basename}_LIBRARIES "${${basename}_LIBRARY}" ) + + if( ${basename}_LIBRARY ) + set( ${basename}_FOUND TRUE ) + endif() + + mark_as_advanced( ${basename}_LIBRARY_RELEASE + ${basename}_LIBRARY_DEBUG + ) +endmacro() diff --git a/plugins/damaris/damaris.cxx b/plugins/damaris/damaris.cxx new file mode 100644 index 000000000..65e6df38a --- /dev/null +++ b/plugins/damaris/damaris.cxx @@ -0,0 +1,399 @@ +/******************************************************************************* + * Copyright (C) 2015-2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2024 National Institute for Research in Digital Science and Technology (Inria) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of CEA nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "damaris_api_call_handler.h" +#include "damaris_cfg.h" +#include "damaris_wrapper.h" + +namespace { + +using PDI::Context; +using PDI::Datatype_sptr; +using PDI::Error; +using PDI::Plugin; +using PDI::Ref; +using PDI::Ref_r; +using PDI::Ref_w; +using PDI::to_string; + +using std::dynamic_pointer_cast; +using std::get; +using std::list; +using std::pair; +using std::string; +using std::unique_ptr; +using std::unordered_map; +using std::unordered_set; + +using namespace PDI; +using namespace damaris_pdi; + +class damaris_plugin: public Plugin +{ + Damaris_cfg m_config; + Damaris_api_call_handler m_event_handler; + + unique_ptr m_damaris; + + static pair, unordered_set> dependencies() { return {{"mpi"}, {"mpi"}}; } + + list multi_expose_transaction_dataname; + //list multi_expose_transaction_dataref; + + std::string int_numbers_types[3] = {"short", "int", "integer"}; + std::string real_numbers_types[3] = {"float", "real", "double"}; + + int iteration = 0; //for debugging + int datasets_to_write_count = 0; //The number of data already written in the current iteration + +public: + damaris_plugin(Context& ctx, PC_tree_t config) + : Plugin{ctx} + , m_config{ctx, config} + , m_event_handler{ + m_config.xml_config_object(), + m_config.communicator(), + m_config.init_on_event(), + m_config.start_on_event(), + m_config.stop_on_event() + } + { + std::string data_cb_concat = ""; + for (auto&& desc: m_config.descs()) { //add data callback only for awaited data + ctx.callbacks().add_data_callback([this](const std::string& name, Ref ref) { this->data(name, ref); }, desc.first); + data_cb_concat.append(desc.first + ", "); + } + context().logger().info("Data for callback : {}", data_cb_concat); + + //Sim configured event names + for (auto&& event: m_config.events()) { + ctx.callbacks().add_event_callback([this](const std::string& name) { this->event(name); }, event.first); + } + + //Default event names, maight be called internally + for (auto&& ev_name: event_names) { + //Only if the key if not yet used by an event + if (m_config.events().find(ev_name.second) == m_config.events().end()) + ctx.callbacks().add_event_callback([this](const std::string& name) { this->event(name); }, ev_name.second); + } + + ctx.logger().info("Plugin loaded successfully"); + } + + void data(const std::string& name, Ref ref) + { + ensure_damaris_is_initialized(""); + + //Update damaris parameters, which depend on PDI metadatas + if (m_config.is_needed_metadata(name)) { + std::unordered_map> updatable_parameters = m_config.get_updatable_parameters(context()); + + std::string prm_name_concat = ""; + for (auto prm_update_info: updatable_parameters) { + auto prm_name = prm_update_info.first; + auto update_info = prm_update_info.second; + { + std::string prm_value = update_info.first; + std::string prm_type = update_info.second; + + void* prm_value_buffer; + size_t prm_buffer_size; + if (std::find(std::begin(int_numbers_types), std::end(int_numbers_types), prm_type) != std::end(int_numbers_types)) { + int prm_long_value = std::atoi(prm_value.c_str()); + prm_value_buffer = &prm_long_value; + prm_buffer_size = sizeof(int); + + int msg_err = m_damaris->damaris_pdi_parameter_set(prm_name.c_str(), prm_value_buffer, prm_buffer_size); + } else if (std::find(std::begin(real_numbers_types), std::end(real_numbers_types), prm_type) != std::end(real_numbers_types)) { + std::cout << "real_numbers_types contains " << prm_type << '\n'; + double prm_dbl_value = std::atof(prm_value.c_str()); + prm_value_buffer = &prm_dbl_value; + prm_buffer_size = sizeof(double); + + int msg_err = m_damaris->damaris_pdi_parameter_set(prm_name.c_str(), prm_value_buffer, prm_buffer_size); + } else { + std::cout << "String === " << prm_type << '\n'; + std::string prm_string_value = prm_value; + prm_value_buffer = &prm_string_value; + prm_buffer_size = sizeof(std::string); + + int msg_err = m_damaris->damaris_pdi_parameter_set(prm_name.c_str(), prm_value_buffer, prm_buffer_size); + } + } + + prm_name_concat.append(prm_name + ", "); + m_config.reset_parameter_depends_on(prm_name); + } + if (updatable_parameters.size() > 0) { + prm_name_concat.pop_back(); + prm_name_concat.pop_back(); + context().logger().info("data `{}' Is a needed metadata for the evaluation of parameters {}", name, prm_name_concat); + } + } else if (m_config.is_dataset_to_write(name)) { + if (Ref_r rref = ref) { + Dataset_Write_Info ds_write_info = m_config.get_dataset_write_info(name); + + //Only write when autorized! + if (ds_write_info.when.to_long(context())) { + context().logger().info("data `{}' will be written when = '{}'", name, ds_write_info.when.to_long(context())); + + int32_t block = ds_write_info.block.to_long(context()); + context().logger().info("data `{}' will be written in block = '{}'", name, block); + int64_t position[3] + = {ds_write_info.position[0].to_long(context()), + ds_write_info.position[1].to_long(context()), + ds_write_info.position[2].to_long(context())}; + + const void* data = static_cast(rref.get()); + + if (block > 0) { + context().logger().info( + "data `{}' will be written at: block '{}' and position '{}:{}:{}', when = '{}'", + name, + block, + position[0], + position[1], + position[2], + ds_write_info.when.to_long(context()) + ); + + std::string set_block_pos_event_name = m_event_handler.get_event_name(Event_type::DAMARIS_SET_BLOCK_POSITION); + m_event_handler.damaris_api_call_event( + context(), + m_damaris, + set_block_pos_event_name, + multi_expose_transaction_dataname, + name.c_str(), + block, + position + ); + + std::string write_block_event_name = m_event_handler.get_event_name(Event_type::DAMARIS_WRITE_BLOCK); + m_event_handler.damaris_api_call_event( + context(), + m_damaris, + write_block_event_name, + multi_expose_transaction_dataname, + name.c_str(), + block, + data + ); + } else { + std::string set_pos_event_name = m_event_handler.get_event_name(Event_type::DAMARIS_SET_POSITION); + m_event_handler.damaris_api_call_event( + context(), + m_damaris, + set_pos_event_name, + multi_expose_transaction_dataname, + name.c_str(), + position + ); + + std::string write_event_name = m_event_handler.get_event_name(Event_type::DAMARIS_WRITE); + m_event_handler + .damaris_api_call_event(context(), m_damaris, write_event_name, multi_expose_transaction_dataname, name.c_str(), data); + } + + datasets_to_write_count++; + //Wait until all datasets are written before launching end of iteration operations + if (m_config.is_there_after_write_events() && datasets_to_write_count == m_config.datasets_to_write().size()) { + list after_write_events = m_config.get_after_write_events(); + for (auto it = after_write_events.begin(); it != after_write_events.end(); it++) { + std::string aw_event = it->c_str(); + if (m_event_handler.is_damaris_api_call_event(aw_event)) { + context().logger().info("event `{}' has been triggered", aw_event); + + context().logger().info("is_damaris_api_call_event ( `{}' ) = TRUE", aw_event); + m_event_handler.damaris_api_call_event(context(), m_damaris, aw_event, {}); + } else { //Non Damaris call event + } + } + datasets_to_write_count = 0; + } + } + } else { + context().logger().error("The Damaris need write access over the data (`{}')", name); + } + } else if (m_config.is_parameter_to_update(name)) { + context().logger().info("m_config.is_parameter_to_update('{}') = `{}'", name, m_config.is_parameter_to_update(name)); + std::pair prm_to_update_info = m_config.get_parameter_to_update_info(name); + std::string prm_name = prm_to_update_info.first; + size_t size; + + damaris::model::DamarisParameterXML prmxml = m_config.get_parameter_xml(prm_name); + + if (std::find(std::begin(int_numbers_types), std::end(int_numbers_types), prmxml.param_datatype_) != std::end(int_numbers_types)) { + size = sizeof(int); + } else if (std::find(std::begin(real_numbers_types), std::end(real_numbers_types), prmxml.param_datatype_) + != std::end(real_numbers_types)) + { + size = sizeof(double); + } else { + size = sizeof(std::string); + } + + if (prm_to_update_info.second == Desc_type::PRM_TO_SET) { + Ref_r rref = ref; + + int msg_err = m_damaris->damaris_pdi_parameter_set(prm_name.c_str(), static_cast(rref.get()), size); + } else if (prm_to_update_info.second == Desc_type::PRM_TO_GET) { + Ref_w wref = ref; + + int msg_err = m_damaris->damaris_pdi_parameter_get(prm_name.c_str(), static_cast(wref.get()), size); + } else { + //Error handling! + } + } + //is_client_get !? + else if (name == m_config.is_client_dataset_name()) + { + context().logger().info("'{}' == m_config.is_client_dataset_name() = '{}'", name, (name == m_config.is_client_dataset_name())); + + if (Ref_w wref = ref) { + context().logger().info( + ":) D) '{}' == m_config.is_client_dataset_name() = '{}' | m_damaris->get_is_client() = '{}'", + name, + (name == m_config.is_client_dataset_name()), + m_damaris->get_is_client() + ); + *static_cast(wref.get()) = m_damaris->get_is_client(); + context().logger().info("------------------- CALLED is_client_dataset_name Return is_client = '{}')", m_damaris->get_is_client()); + } else { + //MayBe a PDI_multi_expose is under traitement + multi_expose_transaction_dataname.emplace_back(name); + } + } + //client_comm_get !? + else if (name == m_config.client_comm_get_dataset_name()) + { + if (Ref_w wref = ref) { + MPI_Comm client_comm; + int err = m_damaris->damaris_pdi_client_comm_get(&client_comm); + + *static_cast(wref.get()) = client_comm; + context().logger().info("------------------- CALLED is_client_dataset_name Return client_comm SETED)"); + } else { + //MayBe a PDI_multi_expose is under traitement + multi_expose_transaction_dataname.emplace_back(name); + } + } else { //Handle other situations... + multi_expose_transaction_dataname.emplace_back(name); + //multi_expose_transaction_dataref.emplace_back(ref); + } + } + + void event(const std::string& event_name) + { + ensure_damaris_is_initialized(event_name); + + //If it a sim configured event + if (m_config.events().find(event_name) != m_config.events().end()) { + if (event_name == m_config.init_on_event()) { + std::string init_event_name = m_event_handler.get_event_name(Event_type::DAMARIS_INITIALIZE); + m_event_handler.damaris_api_call_event(context(), m_damaris, init_event_name, multi_expose_transaction_dataname); + } else if (event_name == m_config.start_on_event()) { + std::string start_event_name = m_event_handler.get_event_name(Event_type::DAMARIS_START); + m_event_handler.damaris_api_call_event(context(), m_damaris, start_event_name, multi_expose_transaction_dataname); + } else if (event_name == m_config.end_iteration_on_event()) { + std::string end_it_event_name = m_event_handler.get_event_name(Event_type::DAMARIS_END_ITERATION); + m_event_handler.damaris_api_call_event(context(), m_damaris, end_it_event_name, multi_expose_transaction_dataname); + } else if (event_name == m_config.finalize_on_event()) { + std::string finalize_event_name = m_event_handler.get_event_name(Event_type::DAMARIS_FINALIZE); + m_event_handler.damaris_api_call_event(context(), m_damaris, finalize_event_name, multi_expose_transaction_dataname); + } + } else if (m_event_handler.is_damaris_api_call_event(event_name)) { + context().logger().info("event `{}' has been triggered", event_name); + + context().logger().info("is_damaris_api_call_event ( `{}' ) = TRUE", event_name); + m_event_handler.damaris_api_call_event(context(), m_damaris, event_name, multi_expose_transaction_dataname); + + multi_expose_transaction_dataname.clear(); + //multi_expose_transaction_dataref.clear(); + } else { //Non Damaris call event + } + } + + void ensure_damaris_is_initialized(const std::string& event_name) + { + if (!m_damaris) { + if (m_config.events().find(event_name) != m_config.events().end()) { + //Means the first action received by the plugin wasn't fir initialization... + if (!m_config.init_on_event().empty() && event_name != m_config.init_on_event()) { + context().logger().error("Trying to use {} plugin before the initialization of the Damaris library!", pretty_name()); + } else if (event_name == m_config.init_on_event()) + return; //The init will follows + } + this->damaris_init(); + } + } + + void damaris_init() + { + context().logger().info("In damaris_init()"); + std::string init_event_name = m_event_handler.get_event_name(Event_type::DAMARIS_INITIALIZE); + m_event_handler.damaris_api_call_event(context(), m_damaris, init_event_name, multi_expose_transaction_dataname); + } + + ~damaris_plugin() + { + if (m_config.finalize_on_event().empty() && m_damaris) { + context().logger().info("Calling DAMARIS_FINALIZE in ~damaris_plugin()"); + std::string finalize_event_name = m_event_handler.get_event_name(Event_type::DAMARIS_FINALIZE); + m_event_handler.damaris_api_call_event(context(), m_damaris, finalize_event_name, multi_expose_transaction_dataname); + } + + context().logger().info("Closing plugin"); + } + + /** Pretty name for the plugin that will be shown in the logger + * + * \return pretty name of the plugin + */ + static std::string pretty_name() { return "Damaris"; } + +}; // class damaris_plugin + +} // namespace + +PDI_PLUGIN(damaris) diff --git a/plugins/damaris/damaris_api_call_handler.cxx b/plugins/damaris/damaris_api_call_handler.cxx new file mode 100644 index 000000000..e2cb77947 --- /dev/null +++ b/plugins/damaris/damaris_api_call_handler.cxx @@ -0,0 +1,592 @@ +/******************************************************************************* + * Copyright (C) 2025-2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2024-2026 National Institute for Research in Digital Science and Technology (Inria) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of CEA nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include +#include "damaris_api_call_handler.h" +#include "damaris_cfg.h" + +using PDI::Context; +using PDI::Ref; +using PDI::Ref_r; +using PDI::Ref_w; +using std::list; +using std::map; +using std::string; +using std::unique_ptr; +using std::unordered_map; +using std::unordered_set; + +namespace damaris_pdi { + +Damaris_api_call_handler::Damaris_api_call_handler(std::string cfg_object) +{ + xml_config_object = cfg_object; +} + +Damaris_api_call_handler::Damaris_api_call_handler(std::string cfg_object, PDI::Expression comm) +{ + xml_config_object = cfg_object; + m_communicator = comm; +} + +Damaris_api_call_handler::Damaris_api_call_handler( + std::string cfg_object, + PDI::Expression comm, + std::string init_on_event, + std::string start_on_event, + std::string stop_on_event +) +{ + xml_config_object = cfg_object; + m_communicator = comm; + m_init_on_event = init_on_event; + m_start_on_event = start_on_event; + m_stop_on_event = stop_on_event; +} + +std::string Damaris_api_call_handler::get_event_name(Event_type event_type) +{ + return event_names.at(event_type); +} + +bool Damaris_api_call_handler::is_damaris_api_call_event(std::string event_name) +{ + for (auto event: event_names) { + if (event_name == event.second) return true; + } + return false; +} + +void Damaris_api_call_handler::damaris_api_call_event( + Context& ctx, + unique_ptr& m_damaris, + std::string event_name, + list expose_dataname, + ... +) +{ + //************************************************************ */ + //Events : Damaris Initialize and Damaris Start + //************************************************************ */ + if (event_name == event_names.at(Event_type::DAMARIS_INITIALIZE)) { + damaris_pdi_init(ctx, m_damaris, xml_config_object.c_str()); + + if (m_start_on_event.empty()) { + ctx.logger().info("Plugin sent damaris_start() to Damaris, in initialize"); + + std::string start_event_name = this->get_event_name(Event_type::DAMARIS_START); + PDI_status_t status = PDI_event(start_event_name.c_str()); + } + } else if (event_name == event_names.at(Event_type::DAMARIS_START)) { + // DAMARIS_START + // The following call starts the servers. Servers will run inside this + // function until they are asked to stop by clients. On clients, + // is_client will be set to 1 (0 on servers). + if (!m_damaris) { + ctx.logger().warn("Trying to call damaris_start() before plugin initialization (`{}')", event_name); + return; + } + std::string arg1_name; + int is_client; + int err = m_damaris->damaris_pdi_start(&is_client); + m_damaris->set_is_client(is_client); + //ctx.logger().info("------------------- CALLED damaris_pdi_start Return IS_CLIENT = '{}')", is_client); + + int arg_pos = 0; + int nb_awaited_args = 1; + int transaction_data_size = expose_dataname.size(); + auto it = expose_dataname.begin(); + //Position to the first needed parameter + advance(it, (transaction_data_size - nb_awaited_args)); + //for (auto it = expose_dataname.rbegin(); it != expose_dataname.rend(); ++it) { + //while (arg_pos < nb_awaited_args) + for (; it != expose_dataname.end(); it++) { + ctx.logger().info("Multi expose: Reclaiming `{}' ({}/{})", it->c_str(), ++arg_pos, expose_dataname.size()); + + if (arg_pos == 1 && strcmp("is_client", it->c_str()) == 0) { + ctx.logger().info("------------------- CALLED damaris_pdi_start {} = '{}')", arg1_name, is_client); + + int* inout_is_client; + PDI_access(it->c_str(), (void**)&inout_is_client, PDI_INOUT); + *inout_is_client = is_client; + arg1_name = it->c_str(); + PDI_release(it->c_str()); + } else + //if(nb_awaited_args <= arg_pos) + break; + } + + //-----------------------Workaround to hide is_client from user----------------------- + //If it is a server and no `if(is_client) {}` closure: + // - all its clients have ended + // - we can stop it from here, since there is no more if(is_client) {} closure + if (!is_client && Damaris_cfg::is_client_dataset_name().empty()) { + //TODO: consider (in the future) a user code to execute if defined (dc_ending_operations) + if (!Damaris_cfg::client_comm_get_dataset_name().empty()) { + //release the Damaris clients mpi comm + PDI_release(Damaris_cfg::client_comm_get_dataset_name().c_str()); + } + + // Get the pointer of PC_tree_t as a (void*) + uintptr_t* conf; + PDI_access("conf_yaml", (void**)&conf, PDI_IN); + PDI_release("conf_yaml"); + + // Get the pointer of PC_tree_t as is object type (need to be done before PDI_finalize) + uintptr_t cc_conf = *conf; + PC_tree_t* vv_conf = reinterpret_cast(cc_conf); + + PDI_finalize(); + + if (PC_tree_destroy(vv_conf)) { + printf("Damaris server: Error in PC_tree_destroy\n"); + } + vv_conf = NULL; + conf = NULL; + + MPI_Finalize(); + exit(0); + } + } + + //************************************************************ */ + //Events that rely on Multi expose + //************************************************************ */ + // DAMARIS_PARAMETER_GET + else if (event_name == event_names.at(Event_type::DAMARIS_PARAMETER_GET)) + { + if (!m_damaris) { + ctx.logger().warn("Trying to call damaris_parameter_get() before plugin initialization (`{}')", event_name); + return; + } + ctx.logger().info("------------------- INNNNN DAMARIS_PARAMETER_GET Event..."); + + char* var_name; + std::string arg1_name = "prm_name"; + void* buffer; + std::string arg2_name = "prm_buffer"; + unsigned int* size; + std::string arg3_name = "prm_size"; + + va_list extra_args; + va_start(extra_args, expose_dataname); + string data_name = va_arg(extra_args, const char*); + if (!data_name.empty()) { + var_name = (char*)data_name.c_str(); + buffer = va_arg(extra_args, void*); + ctx.logger().info("------------------- INNNNN DAMARIS_PARAMETER_GET Event.. EXTRA ARGS.... AFTER buffer"); + *size = va_arg(extra_args, int); + ctx.logger().info("------------------- INNNNN DAMARIS_PARAMETER_GET Event.. EXTRA ARGS.... AFTER size..."); + + + ctx.logger().info( + "------------------- CALLING damaris_pdi_parameter_get arg_pos({}==='{}', {}==='{}', {}==='{}')", + arg1_name, + var_name, + arg2_name, + *(int*)buffer, + arg3_name, + *(int*)size + ); + int err = m_damaris->damaris_pdi_parameter_get((const char*)var_name, (void*)buffer, *(int*)size); + } + + } + // DAMARIS_PARAMETER_SET + else if (event_name == event_names.at(Event_type::DAMARIS_PARAMETER_SET)) + { + if (!m_damaris) { + ctx.logger().warn("Trying to call damaris_parameter_set() before plugin initialization (`{}')", event_name); + return; + } + + char* var_name; + std::string arg1_name = "prm_name"; + void* buffer; + std::string arg2_name = "prm_buffer"; + unsigned int* size; + std::string arg3_name = "prm_size"; + + va_list extra_args; + va_start(extra_args, expose_dataname); + string data_name = va_arg(extra_args, const char*); + if (!data_name.empty()) { + var_name = (char*)data_name.c_str(); + buffer = va_arg(extra_args, void*); + *size = va_arg(extra_args, size_t); + + ctx.logger().info( + "------------------- CALLING damaris_pdi_parameter_set arg_pos({}==='{}', {}==='{}', {}==='{}')", + arg1_name, + var_name, + arg2_name, + *(int*)buffer, + arg3_name, + *(int*)size + ); + int err = m_damaris->damaris_pdi_parameter_set((const char*)var_name, (const void*)buffer, *(int*)size); + } + } + // DAMARIS_CLIENT_COMM_GET + else if (event_name == event_names.at(Event_type::DAMARIS_CLIENT_COMM_GET)) + { + MPI_Comm client_comm; + std::string arg1_name; + + int err = m_damaris->damaris_pdi_client_comm_get(&client_comm); + + int arg_pos = 0; + int nb_awaited_args = 1; + int transaction_data_size = expose_dataname.size(); + auto it = expose_dataname.begin(); + //Position to the first needed parameter + advance(it, (transaction_data_size - nb_awaited_args)); + //for (auto it = expose_dataname.rbegin(); it != expose_dataname.rend(); ++it) { + //while (arg_pos < nb_awaited_args) + for (; it != expose_dataname.end(); it++) { + ctx.logger().info("Multi expose: Reclaiming `{}' ({}/{})", it->c_str(), ++arg_pos, expose_dataname.size()); + + if (arg_pos == 1) { + static MPI_Comm* comm; + PDI_access(it->c_str(), (void**)&comm, PDI_INOUT); + *comm = client_comm; //Update the value + arg1_name = it->c_str(); + PDI_release(it->c_str()); + } else + //if(nb_awaited_args <= arg_pos) + break; + } + } + // DAMARIS_SET_POSITION + else if (event_name == event_names.at(Event_type::DAMARIS_SET_POSITION)) + { + if (!m_damaris) { + ctx.logger().warn("Trying to call damaris_set_position() before plugin initialization (`{}')", event_name); + return; + } + + char* var_name; + std::string arg1_name = "pos_var_name"; + int64_t* position; + std::string arg2_name = "position"; + + va_list extra_args; + va_start(extra_args, expose_dataname); + string data_name = va_arg(extra_args, const char*); + if (!data_name.empty()) { + var_name = (char*)data_name.c_str(); + position = va_arg(extra_args, int64_t*); + } else { + //Retrive parameters! sent via Multi expose, the two last sent data + char* var_name; + std::string arg1_name; + int64_t* position; + std::string arg2_name; + int arg_pos = 0; + int nb_awaited_args = 2; + int transaction_data_size = expose_dataname.size(); + auto it = expose_dataname.begin(); + //Position to the first needed parameter + advance(it, (transaction_data_size - nb_awaited_args)); + //for (auto it = expose_dataname.rbegin(); it != expose_dataname.rend(); ++it) { + //while (arg_pos < nb_awaited_args) + for (; it != expose_dataname.end(); it++) { + ctx.logger().info("Multi expose: Reclaiming `{}' ({}/{})", it->c_str(), ++arg_pos, expose_dataname.size()); + + if (arg_pos == 1) { + PDI_access(it->c_str(), (void**)&var_name, PDI_IN); + arg1_name = it->c_str(); + PDI_release(it->c_str()); + } else if (arg_pos == 2) { + PDI_access(it->c_str(), (void**)&position, PDI_IN); + arg2_name = it->c_str(); + PDI_release(it->c_str()); + } else + //if(nb_awaited_args <= arg_pos) + break; + } + } + + ctx.logger().info( + "------------------- CALLING damaris_pdi_set_position arg_pos({}==='{}', {}==='{}')", + arg1_name, + var_name, + arg2_name, + *(int64_t*)position + ); + int err = m_damaris->damaris_pdi_set_position((const char*)var_name, (const int64_t*)position); + } + // DAMARIS_WRITE + else if (event_name == event_names.at(Event_type::DAMARIS_WRITE)) + { + if (!m_damaris) { + ctx.logger().warn("Trying to call damaris_write() before plugin initialization (`{}')", event_name); + return; + } + char* var_name; + std::string arg1_name = "w_var_name"; + void* data; + std::string arg2_name = "data"; + + va_list extra_args; + va_start(extra_args, expose_dataname); + //if(string data_name = va_arg(extra_args, string)) { + string data_name = va_arg(extra_args, const char*); + if (!data_name.empty()) { + var_name = (char*)data_name.c_str(); + data = va_arg(extra_args, void*); //const void* + } else { + //Retrive parameters! sent via Multi expose, the two last sent data + char* var_name; + std::string arg1_name; + void* data; + std::string arg2_name; + int arg_pos = 0; + int nb_awaited_args = 2; + int transaction_data_size = expose_dataname.size(); + auto it = expose_dataname.begin(); + //Position to the first needed parameter + advance(it, (transaction_data_size - nb_awaited_args)); + //for (auto it = expose_dataname.rbegin(); it != expose_dataname.rend(); ++it) { + //while (arg_pos < nb_awaited_args) + for (; it != expose_dataname.end(); it++) { + ctx.logger().info("Multi expose: Reclaiming `{}' ({}/{})", it->c_str(), ++arg_pos, expose_dataname.size()); + + if (arg_pos == 1) { + PDI_access(it->c_str(), (void**)&var_name, PDI_IN); + arg1_name = it->c_str(); + PDI_release(it->c_str()); + } else if (arg_pos == 2) { + PDI_access(it->c_str(), (void**)&data, PDI_IN); + arg2_name = it->c_str(); + PDI_release(it->c_str()); + } else + //if(nb_awaited_args <= arg_pos) + break; + } + } + + ctx.logger().info("------------------- CALLING damaris_pdi_write arg_pos({}==='{}', {}==={})", arg1_name, var_name, arg2_name, *(int*)data); + int err = m_damaris->damaris_pdi_write((const char*)var_name, (void*)data); + } + // DAMARIS_SET_BLOCK_POSITION + else if (event_name == event_names.at(Event_type::DAMARIS_SET_BLOCK_POSITION)) + { + if (!m_damaris) { + ctx.logger().warn("Trying to call damaris_set_position() before plugin initialization (`{}')", event_name); + return; + } + + char* var_name; + std::string arg1_name = "bpos_var_name"; + int32_t* block; + std::string arg2_name = "dom"; + int64_t* position; + std::string arg3_name = "position"; + + va_list extra_args; + va_start(extra_args, expose_dataname); + string data_name = va_arg(extra_args, const char*); + if (!data_name.empty()) { + var_name = (char*)data_name.c_str(); + *block = va_arg(extra_args, int32_t); + position = va_arg(extra_args, int64_t*); + } else { + //Retrive parameters! sent via Multi expose, the three last sent data + int arg_pos = 0; + int nb_awaited_args = 3; + int transaction_data_size = expose_dataname.size(); + auto it = expose_dataname.begin(); + //Position to the first needed parameter + advance(it, (transaction_data_size - nb_awaited_args)); + //for (auto it = expose_dataname.rbegin(); it != expose_dataname.rend(); ++it) { + //while (arg_pos < nb_awaited_args) + for (; it != expose_dataname.end(); it++) { + ctx.logger().info("Multi expose: Reclaiming `{}' ({}/{})", it->c_str(), ++arg_pos, expose_dataname.size()); + + if (arg_pos == 1) { + PDI_access(it->c_str(), (void**)&var_name, PDI_IN); + arg1_name = it->c_str(); + PDI_release(it->c_str()); + } else if (arg_pos == 2) { + PDI_access(it->c_str(), (void**)&block, PDI_IN); + arg2_name = it->c_str(); + PDI_release(it->c_str()); + } else if (arg_pos == 3) { + PDI_access(it->c_str(), (void**)&position, PDI_IN); + arg3_name = it->c_str(); + PDI_release(it->c_str()); + } else + //if(nb_awaited_args <= arg_pos) + break; + } + } + + ctx.logger().info( + "------------------- CALLING damaris_pdi_set_block_position arg_pos({}==='{}', {}==='{}', {}==='{}')", + arg1_name, + var_name, + arg2_name, + (int32_t)*block, + arg3_name, + *(int64_t*)position + ); + int err = m_damaris->damaris_pdi_set_block_position((const char*)var_name, (int32_t)*block, (const int64_t*)position); + } + // DAMARIS_WRITE_BLOCK + else if (event_name == event_names.at(Event_type::DAMARIS_WRITE_BLOCK)) + { + if (!m_damaris) { + ctx.logger().warn("Trying to call damaris_write() before plugin initialization (`{}')", event_name); + return; + } + char* var_name; + std::string arg1_name = "wb_var_name"; + int32_t* block; + std::string arg2_name = "dom"; + void* data; + std::string arg3_name = "data"; + + va_list extra_args; + va_start(extra_args, expose_dataname); + //if(string data_name = va_arg(extra_args, string)) { + string data_name = va_arg(extra_args, const char*); + if (!data_name.empty()) { + var_name = (char*)data_name.c_str(); + *block = va_arg(extra_args, int32_t); + data = va_arg(extra_args, void*); //const void* + } else { + //Retrive parameters! sent via Multi expose, the three last sent data + int arg_pos = 0; + int nb_awaited_args = 3; + int transaction_data_size = expose_dataname.size(); + auto it = expose_dataname.begin(); + //Position to the first needed parameter + advance(it, (transaction_data_size - nb_awaited_args)); + //for (auto it = expose_dataname.rbegin(); it != expose_dataname.rend(); ++it) { + //while (arg_pos < nb_awaited_args) + for (; it != expose_dataname.end(); it++) { + ctx.logger().info("Multi expose: Reclaiming `{}' ({}/{})", it->c_str(), ++arg_pos, expose_dataname.size()); + + if (arg_pos == 1) { + PDI_access(it->c_str(), (void**)&var_name, PDI_IN); + arg1_name = it->c_str(); + PDI_release(it->c_str()); + } else if (arg_pos == 2) { + PDI_access(it->c_str(), (void**)&block, PDI_IN); + arg2_name = it->c_str(); + PDI_release(it->c_str()); + } else if (arg_pos == 3) { + PDI_access(it->c_str(), (void**)&data, PDI_IN); + arg3_name = it->c_str(); + PDI_release(it->c_str()); + } else + //if(nb_awaited_args <= arg_pos) + break; + } + } + + ctx.logger().info( + "------------------- CALLING damaris_pdi_write_block arg_pos({}==='{}', {}==='{}', {}==='{}')", + arg1_name, + var_name, + arg2_name, + (int32_t)*block, + arg3_name, + *(int*)data + ); + int err = m_damaris->damaris_pdi_write_block((const char*)var_name, (int32_t)*block, (void*)data); + } + + //************************************************************ */ + //Events : End Iteration / Damaris Stop and Damaris Finalize + //************************************************************ */ + else if (event_name == event_names.at(Event_type::DAMARIS_END_ITERATION)) + { + if (m_damaris) { + int err = m_damaris->damaris_pdi_end_iteration(); + + ctx.logger().info("Plugin sent damaris_end_iteration() to Damaris"); + } else { + ctx.logger().warn("Trying to call damaris_end_iteration() before plugin initialization (`{}')", event_name); + } + } else if (event_name == event_names.at(Event_type::DAMARIS_STOP)) { + // DAMARIS_STOP is called and the Daamris server processes will return from the damaris_start() call + if (!m_damaris) { + ctx.logger().warn("Trying to call damaris_strop() before plugin initialization (`{}')", event_name); + return; + } + + int err = m_damaris->damaris_pdi_stop(); + } else if (event_name == event_names.at(Event_type::DAMARIS_FINALIZE)) { + if (!m_damaris) { + ctx.logger().warn("Trying to call damaris_strop() before plugin initialization (`{}')", event_name); + return; + } + + try { + if (m_stop_on_event.empty() && m_damaris->get_is_client()) { + ctx.logger().info("Plugin sent damaris_stop() to Damaris, in finalization"); + + //std::string stop_event_name = this->get_event_name(Event_type::DAMARIS_STOP); + //PDI_status_t status = PDI_event(stop_event_name.c_str()); + + int stop_err = m_damaris->damaris_pdi_stop(); + } + + ctx.logger().info("Plugin sent damaris_finalize() to Damaris"); + int err = m_damaris->damaris_pdi_finalize(); + if (err == DAMARIS_OK) { + ctx.logger().info("Damaris finalized successfully!"); + } + } catch (const std::exception& e) { + ctx.logger().error("Issue when finalizing Damaris: {}", e.what()); + } catch (...) { + ctx.logger().error("An issue occurred when finalizing Damaris."); + } + } else { + assert(false && "Unexpected damaris event type"); + } +} + +void Damaris_api_call_handler::damaris_pdi_init(Context& ctx, unique_ptr& m_damaris, const char* damaris_xml_object) +{ + if (!m_damaris) { + MPI_Comm comm = MPI_COMM_WORLD; + if (m_communicator) { + //TODO: could be replaced by a communicator passed from the simulation! + } + + // This creator method calls damaris_initialize(), passin in an XML file, so if we want to + // pre-fill our xml file, we need to do this somehow before: + // PDI_expose("mpi_comm", &main_comm, PDI_INOUT); + + m_damaris.reset(new Damaris_wrapper{ctx, damaris_xml_object, comm}); + + ctx.logger().info("Plugin initialized successfully"); + } +} + +} // namespace damaris_pdi diff --git a/plugins/damaris/damaris_api_call_handler.h b/plugins/damaris/damaris_api_call_handler.h new file mode 100644 index 000000000..b88f398bf --- /dev/null +++ b/plugins/damaris/damaris_api_call_handler.h @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (C) 2015-2024 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2024 National Institute for Research in Digital Science and Technology (Inria) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of CEA nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#ifndef DAMARIS_API_CALL_HANDLER_H_ +#define DAMARIS_API_CALL_HANDLER_H_ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "damaris_cfg.h" +#include "damaris_wrapper.h" + +using PDI::Context; +using std::list; +using std::string; +using std::unique_ptr; + +namespace damaris_pdi { + +class Damaris_api_call_handler +{ + std::string xml_config_object; + PDI::Expression m_communicator; + std::string m_init_on_event = ""; + std::string m_start_on_event = ""; + std::string m_stop_on_event = ""; + std::string m_finalize_on_event = ""; + +public: + Damaris_api_call_handler( + std::string cfg_object, + PDI::Expression comm, + std::string init_on_event, + std::string start_on_event, + std::string stop_on_event + ); + Damaris_api_call_handler(std::string cfg_object, PDI::Expression comm); + Damaris_api_call_handler(std::string cfg_object); + + std::string get_event_name(Event_type event_type); + + bool is_damaris_api_call_event(std::string event_name); + + void damaris_api_call_event(Context& ctx, unique_ptr& m_damaris, std::string event_name, list expose_dataname, ...); + +private: + void damaris_pdi_init(Context& ctx, unique_ptr& m_damaris, const char* damaris_xml_object); +}; // class Damaris_api_call_handler + +} // namespace damaris_pdi + +#endif // DAMARIS_API_CALL_HANDLER_H_ diff --git a/plugins/damaris/damaris_async_gather.h b/plugins/damaris/damaris_async_gather.h new file mode 100644 index 000000000..6112602f4 --- /dev/null +++ b/plugins/damaris/damaris_async_gather.h @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (C) 2015-2024 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2024 National Institute for Research in Digital Science and Technology (Inria) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of CEA nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#ifndef Damaris_async_gather_H_ +#define Damaris_async_gather_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// Definitions of the Damaris XML tag generators +#include +#include +#include + +#include "damaris_wrapper.h" + +using PDI::Context; +using std::list; +using std::string; +using std::unique_ptr; + +namespace damaris_pdi { + +class Damaris_async_gather +{ +public: + Damaris_async_gather(PDI::Context& ctx, PC_tree_t tree); + +}; // class Damaris_async_gather + +} // namespace damaris_pdi + +#endif // Damaris_async_gather_H_ diff --git a/plugins/damaris/damaris_cfg.cxx b/plugins/damaris/damaris_cfg.cxx new file mode 100644 index 000000000..1380a913c --- /dev/null +++ b/plugins/damaris/damaris_cfg.cxx @@ -0,0 +1,1081 @@ +/******************************************************************************* + * Copyright (C) 2015-2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2024 National Institute for Research in Digital Science and Technology (Inria) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of CEA nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "damaris_cfg.h" + +using PDI::Context; +using PDI::each; +using PDI::Expression; +using PDI::Impl_error; +using PDI::len; +using PDI::opt_each; +using PDI::Ref; +using PDI::Ref_r; +using PDI::Ref_w; +using PDI::Spectree_error; +using PDI::to_double; +using PDI::to_long; +using PDI::to_string; +using PDI::Value_error; +using std::function; +using std::list; +using std::map; +using std::string; +using std::unique_ptr; +using std::unordered_map; + +namespace damaris_pdi { + +namespace { + +bool load_desc(unordered_map& descs, Context& ctx, const string& name, Desc_type desc_type) +{ + auto&& result = descs.emplace(name, desc_type); + if (!result.second) { + ctx.logger().warn("Duplicate use of a descriptor `{}')", name); + } + return result.second; +} + +bool load_event(unordered_map& events, Context& ctx, const string& name, Event_type event_type) +{ + auto&& result = events.emplace(name, event_type); + if (!result.second) { + ctx.logger().warn("Duplicate use of a descriptor `{}')", name); + } + return result.second; +} + +} // namespace + +/** + * This variable contains Damaris xml config nested groups, with dataset elements to display in the XML config + * It is possible to have nested groups, but in most of the cases, + * we just have root groups containing directly the dataset elements + */ +std::vector root_groups_xml; + +// Array to store the nested groups names +const unsigned max_nested_groups = 5; +std::string nested_groups_names[max_nested_groups]; +char ds_elt_full_name_delimiter = '/'; + +void retrive_nested_groups(std::string& dataset_elt_full_name, char delimiter, std::string nested_groups_names[], unsigned& index); +template +void insert_dataset_elts_to_group(DS_TYPE varxml, std::string nested_groups_names[], unsigned index); +//void insert_dataset_elts_to_group(damaris::model::DamarisVarXML varxml, std::string nested_groups_names[], unsigned index); + +std::string numbers_types[] = {"short", "int", "integer", "float", "real", "double"}; +std::string int_numbers_types[3] = {"short", "int", "integer"}; +std::string real_numbers_types[3] = {"float", "real", "double"}; + +// This constructor is called on the construction of the damaris_plugin struct/object +// This code is a mapping of the Damaris src/model/Model.xsd schema to PDI YAML +// The mapping should allow us to recreate a Damaris XML object from what is present +// in the YML file +Damaris_cfg::Damaris_cfg(Context& ctx, PC_tree_t tree) + : m_communicator{"MPI_COMM_WORLD"} +{ + m_is_client_dataset_name = ""; + init_xml_config_object(); + + each(tree, [&](PC_tree_t key_tree, PC_tree_t value) { + string key = to_string(key_tree); + if (key == "architecture") { + //default_when = to_string(value); + parse_architecture_tree(ctx, value); + } else if (key == "communicator") { + m_communicator = to_string(value); + if (!m_communicator) { + throw Spectree_error{key_tree, "no MPI communicator setted `{}'", key}; + } + } else if (key == "init_on_event" || key == "on_init") { + m_init_on_event = to_string(value); + load_event(m_events, ctx, m_init_on_event, Event_type::DAMARIS_INITIALIZE); + } else if (key == "finalize_on_event" || key == "on_finalize") { + m_finalize_on_event = to_string(value); + load_event(m_events, ctx, m_finalize_on_event, Event_type::DAMARIS_FINALIZE); + } else if (key == "start_on_event") { + m_start_on_event = to_string(value); + load_event(m_events, ctx, m_start_on_event, Event_type::DAMARIS_START); + } else if (key == "stop_on_event") { + m_stop_on_event = to_string(value); + load_event(m_events, ctx, m_stop_on_event, Event_type::DAMARIS_STOP); + } else if (key == "end_iteration_on_event") { + m_end_iteration_on_event = to_string(value); + load_event(m_events, ctx, m_end_iteration_on_event, Event_type::DAMARIS_END_ITERATION); + } else if (key == "parameters") { + parse_parameters_tree(ctx, value); + } else if (key == "datasets") { + parse_datasets_tree(ctx, value); + } else if (key == "layouts") { + parse_layouts_tree(ctx, value); + } else if (key == "storages") { + parse_storages_tree(ctx, value); + } + + else if (key == "write") + { + parse_write_tree(ctx, value); + } + + else if (key == "parameter_get") + { + parse_parameter_to_update_tree(ctx, value, Desc_type::PRM_TO_GET); + } + + else if (key == "parameter_set") + { + parse_parameter_to_update_tree(ctx, value, Desc_type::PRM_TO_SET); + } + + else if (key == "after_write") + { + if (!PC_status(value)) { + if (!PC_status(PC_get(value, "[0]"))) { //Array [ev0,ev1,ev3] + each(value, [&](PC_tree_t event_name) { m_after_write_events.emplace_back(to_string(event_name)); }); + } else { //ev0 + m_after_write_events.emplace_back(to_string(value)); + } + } + } + + else if (key == "start" || key == "get_is_client" || key == "is_client_get") + { + if (!PC_status(value)) { + m_is_client_dataset_name = to_string(value); + load_desc(m_descs, ctx, m_is_client_dataset_name, Desc_type::IS_CLIENT_GET); + } + } + + else if (key == "client_comm_get") + { + if (!PC_status(value)) { + m_client_comm_get_dataset_name = to_string(value); + load_desc(m_descs, ctx, m_client_comm_get_dataset_name, Desc_type::CLIENT_COMM_GET); + } + } + }); + + std::string end_it_event_name = event_names.at(Event_type::DAMARIS_END_ITERATION); + //Add only if it does not exist yet + if (std::find(m_after_write_events.begin(), m_after_write_events.end(), end_it_event_name) == m_after_write_events.end() + && m_end_iteration_on_event.empty()) + { + m_after_write_events.emplace_back(end_it_event_name); + } + + parse_log_tree(ctx, tree); + + //Insert grouped dataset elements + for (int root_group_id = 0; root_group_id < root_groups_xml.size(); root_group_id++) { + damaris::model::DamarisGroupXML root_gp_xml = root_groups_xml[root_group_id]; + + m_groups.emplace(root_gp_xml.get_name(), root_gp_xml); + std::map find_replace_map + = {{"_DATASET_ELEMENT_REGEX_", root_gp_xml.ReturnXMLForGroup() + "\n_DATASET_ELEMENT_REGEX_"}}; + damarisXMLModifyModel.RepalceWithRegEx(find_replace_map); + } + + //CleanUp XML OBJECT + std::map find_replace_map + = {{"_DATASET_ELEMENT_REGEX_", ""}, {"_STORAGE_ELEMENT_REGEX_", ""}, {"_PLUGINS_REGEX_", ""}, {"_SCRIPTS_REGEX_", ""}}; + damarisXMLModifyModel.RepalceWithRegEx(find_replace_map); + + m_xml_config_object = damarisXMLModifyModel.GetConfigString(); + + //printf("-------------------------------------------------------XML OBJECT MODIFIED----------------------------------------------\n%s", damarisXMLModifyModel.GetConfigString().c_str()); + //exit(0); +} + +void Damaris_cfg::parse_architecture_tree(Context& ctx, PC_tree_t arch_tree) +{ + std::map find_replace_map = {}; + + //simulation name & Buffer + std::string sim_name = "damaris_pdi_simu"; + PC_tree_t sim_name_tree = PC_get(arch_tree, ".sim_name"); + if (!PC_status(sim_name_tree)) { + sim_name = to_string(sim_name_tree); + } + //Buffer + std::string buffer_name = sim_name + "_buffer"; + long buffer_size = 67108864; + PC_tree_t buffer_size_tree = PC_get(arch_tree, ".buffer_size"); + if (!PC_status(buffer_size_tree)) { + buffer_size = to_long(buffer_size_tree); + } + find_replace_map.insert({{"_SIM_NAME_", sim_name}, {"_SHMEM_BUFFER_BYTES_REGEX_", std::to_string(buffer_size)}, {"_SHMEM_NAME_", buffer_name}}); + + //domains + int m_arch_domains = 1; + PC_tree_t domains_tree = PC_get(arch_tree, ".domains"); + if (!PC_status(domains_tree)) { + m_arch_domains = to_long(domains_tree); + } + find_replace_map.insert({"_DOMAINS_REGEX_", std::to_string(m_arch_domains)}); + + //dedicated + int m_dc_cores_pernode = 0; + int m_dc_nodes = 0; + PC_tree_t arch_dedicated_tree = PC_get(arch_tree, ".dedicated"); + if (!PC_status(arch_dedicated_tree)) { + int nb_subkey_dc = len(arch_dedicated_tree); + + for (int subkey_dc_id = 0; subkey_dc_id < nb_subkey_dc; subkey_dc_id++) { + string key_str = to_string(PC_get(arch_dedicated_tree, "{%d}", subkey_dc_id)); + + if (key_str == "core") { + m_dc_cores_pernode = to_long(PC_get(arch_dedicated_tree, ".core")); + } else if (key_str == "node") { + m_dc_nodes = to_long(PC_get(arch_dedicated_tree, ".node")); + } + } + } + find_replace_map.insert({{"_DC_REGEX_", std::to_string(m_dc_cores_pernode)}, {"_DN_REGEX_", std::to_string(m_dc_nodes)}}); + + //placement not yet used + PC_tree_t arch_placement_tree = PC_get(arch_tree, ".placement"); + if (!PC_status(arch_placement_tree)) { + int nb_subkey_dc = len(arch_placement_tree); + + for (int subkey_pl_id = 0; subkey_pl_id < nb_subkey_dc; subkey_pl_id++) { + string key_str = to_string(PC_get(arch_placement_tree, "{%d}", subkey_pl_id)); + + if (key_str == "start") { + m_placement_start = to_long(PC_get(arch_placement_tree, ".start")); + } else if (key_str == "stride") { + m_placement_stride = to_long(PC_get(arch_placement_tree, ".stride")); + } else if (key_str == "blocksize") { + m_placement_blocksize = to_long(PC_get(arch_placement_tree, ".blocksize")); + } else if (key_str == "mask") { + m_placement_mask_str = to_string(PC_get(arch_placement_tree, ".mask")); + } + } + //TODO: find_replace_map.insert( ); + } + + //Update the xml config + damarisXMLModifyModel.RepalceWithRegEx(find_replace_map); +} + +void Damaris_cfg::parse_parameters_tree(Context& ctx, PC_tree_t parameters_tree_list) +{ + opt_each(parameters_tree_list, [&](PC_tree_t parameters_tree) { //each parameters (list of parameter) + each(parameters_tree, [&](PC_tree_t prm_tree_key, PC_tree_t parameter_tree) { //each parameter + damaris::model::DamarisParameterXML prmxml{}; + std::map find_replace_map = {}; + std::unordered_map depends_on_metadata; + bool is_dependent = false; + + each(parameter_tree, [&](PC_tree_t prm_key, PC_tree_t value) { //parameter info + std::string key = to_string(prm_key); + + if (key == "name") { + prmxml.param_name_ = to_string(value); + } else if (key == "type") { + prmxml.param_datatype_ = to_string(value); + } else if (key == "value") { + prmxml.param_value_ = to_string(value); + m_parameter_expression.emplace(prmxml.param_name_, to_string(value)); + } else if (key == "depends_on") { + is_dependent = true; + if (!PC_status(PC_get(value, "[0]"))) { //Array //[d1,d2,d3] for instance, each di an expreession of of Damaris Parameter + //int idx = 0; + each(value, [&](PC_tree_t metadata_name_tree) { + std::string metadata_name = to_string(metadata_name_tree); + + depends_on_metadata.insert({metadata_name, false}); + + load_desc(m_descs, ctx, metadata_name, Desc_type::PRM_REQUIRED_METADATA); + }); + } else { //d1 + std::string metadata_name = to_string(value); + //depends_on_metadata[metadata_name] = false; + depends_on_metadata.insert({metadata_name, false}); + + load_desc(m_descs, ctx, metadata_name, Desc_type::PRM_REQUIRED_METADATA); + } + + + } else { + std::cerr << "ERROR: damaris_cfg unrecogognized parameter map string: " << key << std::endl; + } + }); + //m_parameter_expression.emplace(prmxml.param_name_, prmxml.param_value_); + m_parameter_depends_on.emplace(prmxml.param_name_, depends_on_metadata); + //set default value if depend on metadata + if (is_dependent && std::find(std::begin(numbers_types), std::end(numbers_types), prmxml.param_datatype_) != std::end(numbers_types)) { + if (std::find(std::begin(int_numbers_types), std::end(int_numbers_types), prmxml.param_datatype_) != std::end(int_numbers_types)) + prmxml.param_value_ = "1"; //"0" + else + prmxml.param_value_ = "1.0"; //"0" + } + + m_parameters.emplace(prmxml.param_name_, prmxml); + find_replace_map.insert({"_DATASET_ELEMENT_REGEX_", prmxml.ReturnXMLForParameter() + "\n_DATASET_ELEMENT_REGEX_"}); + + //Update the xml config + damarisXMLModifyModel.RepalceWithRegEx(find_replace_map); + }); + }); +} + +void Damaris_cfg::parse_datasets_tree(Context& ctx, PC_tree_t datasets_tree_list) +{ + opt_each(datasets_tree_list, [&](PC_tree_t datasets_tree) { //each datasets (list of dataset) + each(datasets_tree, [&](PC_tree_t ds_tree_key, PC_tree_t dataset_tree) { //each dataset + damaris::model::DamarisVarXML vxml{}; + std::map find_replace_map = {}; + unsigned name_index = 0; + std::string dataset_elt_full_name; + + each(dataset_tree, [&](PC_tree_t ds_key, PC_tree_t value) { //dataset info + std::string key = to_string(ds_key); + + int tf; // 0 is false, anything else is true + if (key == "name") { + dataset_elt_full_name = to_string(value); + retrive_nested_groups(dataset_elt_full_name, ds_elt_full_name_delimiter, nested_groups_names, name_index); + + vxml.var_name_ = nested_groups_names[name_index - 1]; //to_string(value); + } else if (key == "layout") { + vxml.layout_name_ = to_string(value); + } else if (key == "mesh") { + vxml.mesh_ = to_string(value); + } else if (key == "centering") { + std::string t_centering = to_string(value); + vxml.set_centering(t_centering); + } else if (key == "storage") { + vxml.store_ = to_string(value); + } else if (key == "script") { + vxml.script_ = to_string(value); + } else if (key == "unit") { + vxml.unit_ = to_string(value); + } else if (key == "select_mem") { + vxml.select_mem_ = to_string(value); + } else if (key == "select_file") { + vxml.select_file_ = to_string(value); + } else if (key == "select_subset") { + vxml.select_subset_ = to_string(value); + } else if (key == "ref") { + vxml.ref_ = to_string(value); + } else if (key == "type") { + std::string in_type = to_string(value); + vxml.set_type(in_type); + } else if (key == "comment") { + vxml.comment_ = to_string(value); + } else if (key == "visualizable") { + PC_bool(value, &tf); + vxml.visualizable_ = (tf == 0) ? false : true; + } else if (key == "time_varying") { + PC_bool(value, &tf); + vxml.time_varying_ = (tf == 0) ? false : true; + } else if (key == "enabled") { + PC_bool(value, &tf); + vxml.enabled_ = (tf == 0) ? false : true; + } else { + throw Value_error{"ERROR: damaris_cfg unrecogognized variable map string: `{}'", key}; + } + }); + + if (dataset_elt_full_name.empty()) throw Value_error{"ERROR: damaris_cfg variable name must not be empty"}; + + //m_datasets.emplace(vxml.var_name_ , vxml) ; + m_datasets.emplace(dataset_elt_full_name, vxml); + + if (name_index == 1) { + find_replace_map.insert({"_DATASET_ELEMENT_REGEX_", vxml.ReturnXMLForVariable() + "\n_DATASET_ELEMENT_REGEX_"}); + + //Update the xml config + damarisXMLModifyModel.RepalceWithRegEx(find_replace_map); + } else { //In nested groups + insert_dataset_elts_to_group(vxml, nested_groups_names, name_index); + } + }); + }); +} + +void Damaris_cfg::parse_layouts_tree(Context& ctx, PC_tree_t layouts_tree_list) +{ + opt_each(layouts_tree_list, [&](PC_tree_t layouts_tree) { //each layouts (list of layout) + each(layouts_tree, [&](PC_tree_t lts_key, PC_tree_t layout_tree) { //each layout + damaris::model::DamarisLayoutXML layoutxml{}; + std::map find_replace_map = {}; + unsigned name_index = 0; + std::string dataset_elt_full_name; + std::unordered_map depends_on_metadata; + bool is_dependent = false; + std::string depends_on_str = ""; + std::string in_datatype; + int nb_dims; + + each(layout_tree, [&](PC_tree_t lt_key, PC_tree_t value) { //layout info + std::string key = to_string(lt_key); + + int intb; // 0 is false, anything else is true + if (key == "name") { + dataset_elt_full_name = to_string(value); + retrive_nested_groups(dataset_elt_full_name, ds_elt_full_name_delimiter, nested_groups_names, name_index); + + layoutxml.layout_name_ = nested_groups_names[name_index - 1]; //to_string(value); + } else if (key == "type") { + in_datatype = to_string(value); + layoutxml.set_datatype(in_datatype); + } else if (key == "dimensions") { + //Is there a way to determine if an expression is ready to be evaluated? ei, all the conponent have a value + if (!PC_status(PC_get(value, "[0]"))) { //Array //[d1,d2,d3] for instance, each di an expreession of of Damaris Parameter + int nb_layout_dims2; + PC_len(value, &nb_layout_dims2); + + std::string dims_list = ""; + each(value, [&](PC_tree_t dim) { dims_list += to_string(dim) + ","; }); + dims_list.pop_back(); + layoutxml.layout_dimensions_ = dims_list; + } else { //"d1,d2,d3" for instance, each di an expreession of of Damaris Parameter + layoutxml.layout_dimensions_ = to_string(value); + } + + nb_dims = count(layoutxml.layout_dimensions_.begin(), layoutxml.layout_dimensions_.end(), ',') + 1; + } else if (key == "global") { + //Is there a way to determine if an expression is ready to be evaluated? ei, all the conponent have a value + if (!PC_status(PC_get(value, "[0]"))) { //Array //[dg1,dg2,dg3] for instance, each di an expreession of of Damaris Parameter + + std::string dims_global_list = ""; + each(value, [&](PC_tree_t dim) { dims_global_list += to_string(dim) + ","; }); + dims_global_list.pop_back(); + layoutxml.layout_dims_global_ = dims_global_list; + } else { //"dg1,dg2,dg3" for instance, each di an expreession of of Damaris Parameter + layoutxml.layout_dims_global_ = to_string(value); + } + } else if (key == "ghosts") { + //Is there a way to determine if an expression is ready to be evaluated? ei, all the conponent have a value + if (!PC_status(PC_get(value, "[0]"))) + { //Array //['g11:g12','g21:g22','g31:g32'] for instance, each di an expreession of of Damaris Parameter + + std::string ghosts_list = ""; + each(value, [&](PC_tree_t dim) { ghosts_list += to_string(dim) + ","; }); + ghosts_list.pop_back(); + layoutxml.layout_ghosts_ = ghosts_list; + } else { //"g11:g12,g21:g22,g31:g32" for instance, each di an expreession of of Damaris Parameter + layoutxml.layout_ghosts_ = to_string(value); + } + } else if (key == "language") { + std::string in_language = to_string(value); + layoutxml.set_language(in_language); + } else if (key == "visualizable") { + PC_bool(value, &intb); + bool in_visualizable = (intb == 0) ? false : true; + layoutxml.layout_visualizable_ = in_visualizable; + } else if (key == "comment") { + layoutxml.layout_comment_ = to_string(value); + } else if (key == "depends_on") { + is_dependent = true; + if (!PC_status(PC_get(value, "[0]"))) { //Array //[d1,d2,d3] for instance, each di an expreession of of Damaris Parameter + //int idx = 0; + each(value, [&](PC_tree_t metadata_name_tree) { + std::string metadata_name = to_string(metadata_name_tree); + depends_on_str += metadata_name + ", "; + + depends_on_metadata.insert({metadata_name, false}); + }); + depends_on_str.pop_back(); //' ' + depends_on_str.pop_back(); //',' + } else { //d1 + std::string metadata_name = to_string(value); + depends_on_str = metadata_name; + //depends_on_metadata[metadata_name] = false; + depends_on_metadata.insert({metadata_name, false}); + } + depends_on_str = "[" + depends_on_str + "]"; + + } else { + throw Value_error{"ERROR: damaris_cfg unrecogognized layout map string: `{}'", key}; + } + }); + m_layout_depends_on.emplace(layoutxml.layout_name_, depends_on_metadata); + //set default value if depend on metadata + //Construct Damaris parameters behind the scene + if (is_dependent) { + std::stringstream ss_dims(layoutxml.layout_dimensions_), ss_globals(layoutxml.layout_dims_global_); + std::string tmp; + std::vector dim_list, global_list; + + while (std::getline(ss_dims, tmp, ',')) { + dim_list.push_back(tmp); + } + while (std::getline(ss_globals, tmp, ',')) { + global_list.push_back(tmp); + } + + std::string prm_config_yaml = ""; + + layoutxml.layout_dimensions_ = ""; + std::string new_globals = ""; + //TODO: get the type of the metadata to which the layout depends, to apply it to the parameters + std::string metadatatype = "int"; + + for (int i = 0; i < nb_dims; i++) { + string dim_name = layoutxml.layout_name_ + "_dim" + std::to_string(i); + prm_config_yaml += "- parameter: \n"; + prm_config_yaml += " name: " + dim_name + " \n"; + prm_config_yaml += " type: " + metadatatype + " \n"; + prm_config_yaml += " value: '" + dim_list[i] + "' \n"; + prm_config_yaml += " depends_on: " + depends_on_str + " \n"; + + layoutxml.layout_dimensions_ += dim_name + ","; + + if (layoutxml.layout_dims_global_.length() > 1) { + string global_name = layoutxml.layout_name_ + "_global" + std::to_string(i); + prm_config_yaml += "- parameter: \n"; + prm_config_yaml += " name: " + global_name + " \n"; + prm_config_yaml += " type: " + metadatatype + " \n"; + prm_config_yaml += " value: '" + global_list[i] + "' \n"; + prm_config_yaml += " depends_on: " + depends_on_str + " \n"; + + new_globals += global_name + ","; + } + } + layoutxml.layout_dimensions_.pop_back(); + //the layout_dims_global_ attribute being optional with default value '#', we need to ensure it has been set before modification. + //if(layoutxml.layout_dims_global_ != '#') { + if (layoutxml.layout_dims_global_.length() > 1) { + new_globals.pop_back(); + layoutxml.layout_dims_global_ = new_globals; + } + + //Background creation of parameter + PC_tree_t parameters_conf = PC_parse_string(prm_config_yaml.c_str()); + parse_parameters_tree(ctx, parameters_conf); + } + + if (dataset_elt_full_name.empty()) throw Value_error{"ERROR: damaris_cfg layout name must not be empty"}; + + m_layouts.emplace(dataset_elt_full_name, layoutxml); + + if (name_index == 1) { + find_replace_map.insert({"_DATASET_ELEMENT_REGEX_", layoutxml.ReturnXMLForLayout() + "\n_DATASET_ELEMENT_REGEX_"}); + + //Update the xml config + damarisXMLModifyModel.RepalceWithRegEx(find_replace_map); + } else { //In nested groups + insert_dataset_elts_to_group(layoutxml, nested_groups_names, name_index); + } + }); + }); +} + +void Damaris_cfg::parse_storages_tree(Context& ctx, PC_tree_t storages_tree_list) +{ + opt_each(storages_tree_list, [&](PC_tree_t storages_tree) { //each storages (list of storage) + each(storages_tree, [&](PC_tree_t storagest_key, PC_tree_t storage_tree) { //each storage + damaris::model::DamarisStoreXML store{}; + std::map find_replace_map = {}; + + each(storage_tree, [&](PC_tree_t st_key, PC_tree_t value) { //storage info + std::string key = to_string(st_key); + + int tf; // 0 is false, anything else is true + if (key == "name") { + store.store_name_ = to_string(value); + } else if (key == "type") { + store.store_type_ = to_string(value); + } else if (key == "file_mode") { + store.store_opt_FileMode_ = to_string(value); + } else if (key == "files_path") { + store.store_opt_FilesPath_ = to_string(value); + //Ensure the folder exists + struct stat st; + if (stat(store.store_opt_FilesPath_.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) { + } else { + if (mkdir(store.store_opt_FilesPath_.c_str(), 0775) == -1) { + exit(1); + } else { + //ctx.logger().info("HDF5 files_path created successfully: '{}'", to_string(value)); + } + } + } else { + std::cerr << "ERROR: damaris_cfg unrecogognized storage map string: " << key << std::endl; + } + }); + m_storages.emplace(store.store_name_, store); + + find_replace_map.insert({"_STORAGE_ELEMENT_REGEX_", store.ReturnXMLForStore() + "\n_STORAGE_ELEMENT_REGEX_"}); + + //Update the xml config + damarisXMLModifyModel.RepalceWithRegEx(find_replace_map); + }); + }); +} + +void Damaris_cfg::parse_write_tree(Context& ctx, PC_tree_t write_tree_list) +{ + each(write_tree_list, [&](PC_tree_t writet_key, PC_tree_t write_ds_tree) { //each dataset to write + std::string ds_name = to_string(writet_key); //the name of the data to write, if dataset not specified afterward! + Dataset_Write_Info ds_write_info; + + //dataset + PC_tree_t ds_name_tree = PC_get(write_ds_tree, ".dataset"); + if (!PC_status(ds_name_tree)) { + ds_name = to_string(ds_name_tree); + } + //when + PC_tree_t ds_when_tree = PC_get(write_ds_tree, ".when"); + if (!PC_status(ds_when_tree)) { + ds_write_info.when = to_string(ds_when_tree); + } + //position + PC_tree_t ds_position_tree = PC_get(write_ds_tree, ".position"); + if (!PC_status(ds_position_tree)) { + if (!PC_status(PC_get(ds_position_tree, "[0]"))) { //Array [p0,p1,p3] (1 to 3 elements) + int position_dim; + PC_len(ds_position_tree, &position_dim); + + int pos_idx = 0; + each(ds_position_tree, [&](PC_tree_t dim) { + ds_write_info.position[pos_idx] = to_string(dim); + pos_idx++; + }); + } else { //p0 + ds_write_info.position[0] = to_string(ds_position_tree); + } + } + //block + PC_tree_t ds_block_tree = PC_get(write_ds_tree, ".block"); + if (!PC_status(ds_block_tree)) { + ds_write_info.block = to_string(ds_block_tree); + } + + m_datasets_to_write.emplace(ds_name, ds_write_info); + + load_desc(m_descs, ctx, ds_name, Desc_type::DATA_TO_WRITE_WITH_BLOCK); + }); +} + +void Damaris_cfg::parse_parameter_to_update_tree(Context& ctx, PC_tree_t ptu_tree, Desc_type op_type) +{ + if (!PC_status(PC_get(ptu_tree, "[0]"))) { //Array [prm0,prm1,prm3,...] / it's a list of names only + each(ptu_tree, [&](PC_tree_t prm_name_t) { + std::pair prm_to_update_info; + std::string metadata = to_string(prm_name_t); + std::string prm_name = to_string(prm_name_t); + + prm_to_update_info.first = prm_name; + prm_to_update_info.second = op_type; + m_parameter_to_update.emplace(metadata, prm_to_update_info); + load_desc(m_descs, ctx, metadata, op_type); + }); + } else if (!PC_status(ptu_tree)) { // it's a name:{config...} mapping + each(ptu_tree, [&](PC_tree_t ptu_metadata, PC_tree_t ptu_prmname) { + std::string metadata = to_string(ptu_metadata); + std::string prm_name = to_string(ptu_metadata); + std::pair prm_to_update_info; + + if (!PC_status(ptu_prmname)) { + if (to_string(ptu_prmname) != "") prm_name = to_string(ptu_prmname); + } + + prm_to_update_info.first = prm_name; + prm_to_update_info.second = op_type; + m_parameter_to_update.emplace(metadata, prm_to_update_info); + load_desc(m_descs, ctx, metadata, op_type); + }); + } +} + +void Damaris_cfg::parse_log_tree(Context& ctx, PC_tree_t config) +{ + std::map find_replace_map = {}; + + //Log File Name + std::string log_file_name = "damaris_pdi_simu"; + PC_tree_t log_file_name_tree = PC_get(config, ".log.file_name"); + if (!PC_status(log_file_name_tree)) { + log_file_name = to_string(log_file_name_tree); + } else { + PC_tree_t sim_name_tree = PC_get(config, ".architecture.sim_name"); + if (!PC_status(sim_name_tree)) { + log_file_name = to_string(sim_name_tree); + } + } + + std::string log_rotation_size = "5"; + PC_tree_t log_rotation_size_tree = PC_get(config, ".log.rotation_size"); + if (!PC_status(log_rotation_size_tree)) { + log_rotation_size = to_string(log_rotation_size_tree); + } + + std::string log_level = "info"; + PC_tree_t log_level_tree = PC_get(config, ".log.log_level"); + if (!PC_status(log_level_tree)) { + log_level = to_string(log_level_tree); + } + + std::string log_flush = "true"; + PC_tree_t log_flush_tree = PC_get(config, ".log.flush"); + if (!PC_status(log_flush_tree)) { + log_flush = to_string(log_flush_tree); + } + + find_replace_map.insert( + {{"_SIM_LOG_NAME_", log_file_name}, {"_LOG_ROTATION_SIZE_", log_rotation_size}, {"_LOG_FLUSH_", log_flush}, {"_LOG_LEVEL_", log_level}} + ); + + //Update the xml config + damarisXMLModifyModel.RepalceWithRegEx(find_replace_map); +} + +bool Damaris_cfg::is_dataset_to_write(std::string data_name) +{ + bool is_dataset_to_write = false; + for (auto& datasets_to_write: m_datasets_to_write) { + if (data_name == datasets_to_write.first) { + is_dataset_to_write = true; + break; + } + } + + return is_dataset_to_write; +} + +bool Damaris_cfg::is_parameter_to_update(std::string data_name) +{ + bool is_parameter_to_update = false; + for (auto& parameter_to_update: m_parameter_to_update) { + if (data_name == parameter_to_update.first) { + is_parameter_to_update = true; + break; + } + } + + return is_parameter_to_update; +} + +bool Damaris_cfg::is_needed_metadata(std::string data_name) +{ + bool is_needed_metadata = false; + for (auto& prm_depends_on: m_parameter_depends_on) { + auto& prm_name = prm_depends_on.first; + auto& prm_depends_on_data = prm_depends_on.second; + for (auto& depends_on_data: prm_depends_on_data) { + //auto &depends_on_data_name = depends_on_data.first; + //auto &depends_on_data_state = depends_on_data.second; + + if (data_name == depends_on_data.first && depends_on_data.second == false) { + depends_on_data.second = true; + is_needed_metadata = true; + } + } + } + + return is_needed_metadata; +} + +std::unordered_map> Damaris_cfg::get_updatable_parameters(Context& ctx) +{ + std::unordered_map> updatable_parameters; + for (auto prm_depends_on: m_parameter_depends_on) { + auto prm_name = prm_depends_on.first; + auto prm_depends_on_data = prm_depends_on.second; + bool to_be_updated = (prm_depends_on_data.size() > 0); //true; + for (auto depends_on_data: prm_depends_on_data) { + auto depends_on_data_name = depends_on_data.first; + auto depends_on_data_state = depends_on_data.second; + + if (!depends_on_data_state) { + to_be_updated = false; + break; + } + } + if (to_be_updated) { //Update the parameter + PDI::Expression prm_value = m_parameter_expression.at(prm_name); + damaris::model::DamarisParameterXML prmxml = m_parameters.at(prm_name); + prmxml.param_value_ = prm_value.to_string(ctx); + std::pair update_info; + update_info.first = prm_value.to_string(ctx); + update_info.second = prmxml.param_datatype_; + + updatable_parameters.emplace(prm_name, update_info); + } + } + + return updatable_parameters; +} + +void Damaris_cfg::reset_parameter_depends_on(std::string prm_name) +{ + if (m_parameter_depends_on.find(prm_name) == m_parameter_depends_on.end()) { + // not found + // handle the error + } else { + std::unordered_map prm_depends_on_data = m_parameter_depends_on.at(prm_name); + for (auto depends_on_data: prm_depends_on_data) { + depends_on_data.second = false; + } + } +} + +void Damaris_cfg::reset_parameter_depends_on(std::vector prm_list) +{ + for (auto prm_name: prm_list) { + reset_parameter_depends_on(prm_name); + } +} + +void Damaris_cfg::reset_all_parameters_depends_on() +{ + for (auto prm_depends_on: m_parameter_depends_on) { + auto prm_name = prm_depends_on.first; + auto prm_depends_on_data = prm_depends_on.second; + + for (auto depends_on_data: prm_depends_on_data) { + depends_on_data.second = false; + } + } +} + +// Retrive nested groups names from a dataset_elt_full_name (gp1/gp2/.../dataset_elt_name) +void retrive_nested_groups(std::string& dataset_elt_full_name, char delimiter, std::string nested_groups_names[], unsigned& index) +{ + // Creating an input string stream from the dataset_elt_full_name + std::istringstream namestream(dataset_elt_full_name); + + // Tmp string + string gp_name; + + // Retrive names from the string stream separated by the delimiter + while (getline(namestream, gp_name, delimiter)) { + if (index == max_nested_groups) { + throw Value_error{"Damaris variable/layout/mesh can be nested in more than `{}' groups", max_nested_groups}; + } + // Add the gp_name to the array + nested_groups_names[index++] = gp_name; + } + //If index==1, there is no group, ie: nested_groups_names[index-1] = dataset_elt_full_name +} + +template +void insert_dataset_elts_to_group(DS_TYPE ds_elt_xml, std::string nested_groups_names[], unsigned index) +{ + std::string nearest_group_name = nested_groups_names[index - 2]; + bool root_group_exists = false; + damaris::model::DamarisGroupXML* nearest_parent_group = NULL; + for (int root_group_id = 0; root_group_id < root_groups_xml.size(); root_group_id++) { + damaris::model::DamarisGroupXML* root_gp_xml = &root_groups_xml[root_group_id]; + + if (nested_groups_names[0] == root_gp_xml->get_name()) { + if (nearest_group_name == root_gp_xml->get_name()) { + root_gp_xml->add_ds_element(ds_elt_xml); + + return; + } + nearest_parent_group = root_gp_xml; + root_group_exists = true; + break; + } + } + + //Insertion in an existing nested group + bool insertion_group_found = false; + int group_id = 1; + if (root_group_exists) { + while (group_id <= (index - 2) && !insertion_group_found) { + std::string group_name = nested_groups_names[group_id]; + + insertion_group_found = true; + std::vector sub_groups = nearest_parent_group->get_sub_groups(); + for (int group_id = 0; group_id < sub_groups.size(); group_id++) { + if (group_name == sub_groups[group_id].get_name()) { + if (nearest_group_name == sub_groups[group_id].get_name()) { + //(&sub_groups[group_id])->add_variable(ds_elt_xml); + (&sub_groups[group_id])->add_ds_element(ds_elt_xml); + + return; + } + insertion_group_found = false; + nearest_parent_group = &sub_groups[group_id++]; + break; + } + } + } + } + //The nested groups has to be created + else + { + damaris::model::DamarisGroupXML root_gp_xml{nested_groups_names[0]}; + + if (nearest_group_name == root_gp_xml.get_name()) { + //root_gp_xml.add_variable(ds_elt_xml); + root_gp_xml.add_ds_element(ds_elt_xml); + root_groups_xml.emplace_back(root_gp_xml); + + return; + } + + root_groups_xml.emplace_back(root_gp_xml); + + nearest_parent_group = &root_gp_xml; + insertion_group_found = true; + } + + //Insertion point found + if (insertion_group_found) { + damaris::model::DamarisGroupXML linked_parent_group{nearest_group_name}; + for (unsigned insertion_group_id = (index - 2); insertion_group_id >= group_id; insertion_group_id--) { + std::string group_name = nested_groups_names[insertion_group_id]; + damaris::model::DamarisGroupXML sub_gp_xml{group_name}; + if (nearest_group_name == sub_gp_xml.get_name()) { + sub_gp_xml.add_ds_element(ds_elt_xml); + } else { + sub_gp_xml.add_sub_group(linked_parent_group); + } + linked_parent_group = sub_gp_xml; + } + nearest_parent_group->add_sub_group(linked_parent_group); + } +} + +const string& Damaris_cfg::xml_config_object(void) +{ + m_xml_config_object = damarisXMLModifyModel.GetConfigString(); + return m_xml_config_object; +} + +//const PDI::Expression& Damaris_cfg::communicator() const +PDI::Expression Damaris_cfg::communicator() const +{ + return m_communicator; +} + +const unordered_map& Damaris_cfg::datasets() const +{ + return m_datasets; +} + +const unordered_map& Damaris_cfg::layouts() const +{ + return m_layouts; +} + +const unordered_map& Damaris_cfg::parameters() const +{ + return m_parameters; +} + +const damaris::model::DamarisParameterXML Damaris_cfg::get_parameter_xml(string prm_name) const +{ + return m_parameters.at(prm_name); +} + +const std::unordered_map& Damaris_cfg::storages() const +{ + return m_storages; +} + +const std::unordered_map& Damaris_cfg::groups() const +{ + return m_groups; +} + +const unordered_map& Damaris_cfg::descs() const +{ + return m_descs; +} + +const unordered_map& Damaris_cfg::events() const +{ + return m_events; +} + +const std::unordered_map& Damaris_cfg::datasets_to_write() const +{ + return m_datasets_to_write; +} + +const std::unordered_map>& Damaris_cfg::parameter_to_update() const +{ + return m_parameter_to_update; +} + +Dataset_Write_Info Damaris_cfg::get_dataset_write_info(std::string data_name) const +{ + try { + return m_datasets_to_write.at(data_name); + } catch (...) { + assert(false && "Trying to get inexistant damaris awaited dataset!"); + } +} + +std::pair Damaris_cfg::get_parameter_to_update_info(std::string data_name) const +{ + try { + return m_parameter_to_update.at(data_name); + } catch (...) { + assert(false && "Trying to get inexistant damaris awaited dataset!"); + } +} + +std::string Damaris_cfg::init_on_event() const +{ + return m_init_on_event; +} + +std::string Damaris_cfg::finalize_on_event() const +{ + return m_finalize_on_event; +} + +std::string Damaris_cfg::start_on_event() const +{ + return m_start_on_event; +} + +std::string Damaris_cfg::stop_on_event() const +{ + return m_stop_on_event; +} + +std::string Damaris_cfg::end_iteration_on_event() const +{ + return m_end_iteration_on_event; +} + +std::string Damaris_cfg::m_is_client_dataset_name = ""; +std::string Damaris_cfg::m_client_comm_get_dataset_name = ""; + +} // namespace damaris_pdi diff --git a/plugins/damaris/damaris_cfg.h b/plugins/damaris/damaris_cfg.h new file mode 100644 index 000000000..cab48b62b --- /dev/null +++ b/plugins/damaris/damaris_cfg.h @@ -0,0 +1,288 @@ +/******************************************************************************* + * Copyright (C) 2015-2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2024 National Institute for Research in Digital Science and Technology (Inria) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of CEA nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#ifndef DAMARIS_CFG_H_ +#define DAMARIS_CFG_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// Definitions of the Damaris XML tag generators +#include +#include +#include + +#include "damaris_wrapper.h" + +using PDI::Context; +using std::list; +using std::string; +using std::unique_ptr; + +namespace damaris_pdi { + +typedef struct placement { + int start; + int stride; + int blocksize; + std::vector mask{}; //std::vector mask {} ??? +} placement; + +typedef struct dedicated { + int core; + int node; +} dedicated; + +struct Architecture_type { + int domain; + placement arch_placement; + dedicated arch_cores_or_nodes; +}; + +enum class Desc_type { + DATA_TO_WRITE, + DATA_TO_WRITE_WITH_BLOCK + //,DATA_TO_READ + , + PRM_REQUIRED_METADATA, + PRM_TO_GET, + PRM_TO_SET, + IS_CLIENT_GET, + CLIENT_COMM_GET +}; + +enum class Event_type { + DAMARIS_INITIALIZE = 0, + DAMARIS_START = 1, + DAMARIS_SET_POSITION = 2, + DAMARIS_SET_BLOCK_POSITION = 3, + DAMARIS_WRITE = 4, + DAMARIS_WRITE_BLOCK = 5, + DAMARIS_CLIENT_COMM_GET = 6, + DAMARIS_PARAMETER_SET = 7, + DAMARIS_PARAMETER_GET = 8, + DAMARIS_END_ITERATION = 9, + DAMARIS_GET_ITERATION = 10, + DAMARIS_SIGNAL = 11, + DAMARIS_BIND = 12, + DAMARIS_STOP = 13, + DAMARIS_FINALIZE = 14 +}; + +/** These default event names are for internal use. If a configured name is given, these ones will be surcharged */ +const std::unordered_map event_names + = {{Event_type::DAMARIS_INITIALIZE, "initialize"}, + {Event_type::DAMARIS_START, "damaris_start"}, + {Event_type::DAMARIS_SET_POSITION, "damaris_set_position"}, + {Event_type::DAMARIS_SET_BLOCK_POSITION, "damaris_set_block_position"}, + {Event_type::DAMARIS_WRITE, "damaris_write"}, + {Event_type::DAMARIS_WRITE_BLOCK, "damaris_write_block"}, + {Event_type::DAMARIS_CLIENT_COMM_GET, "damaris_client_comm_get"}, + {Event_type::DAMARIS_PARAMETER_SET, "damaris_parameter_set"}, + {Event_type::DAMARIS_PARAMETER_GET, "damaris_parameter_get"}, + {Event_type::DAMARIS_END_ITERATION, "damaris_end_iteration"}, + {Event_type::DAMARIS_GET_ITERATION, "damaris_get_iteration"}, + {Event_type::DAMARIS_SIGNAL, "damaris_signal"}, + {Event_type::DAMARIS_BIND, "damaris_bind"}, + {Event_type::DAMARIS_STOP, "damaris_stop"}, + {Event_type::DAMARIS_FINALIZE, "finalize"}}; + +struct Dataset_Write_Info { + PDI::Expression when = "1"; //By default, always write as long as there are iteration going on + /*int64_t* */ PDI::Expression position[3] = {"0", "0", "0"}; //Max Dim is 3 + /*int32_t */ PDI::Expression block = "0"; //when domain = 1, which is the default behaviour +}; + +struct DamarisXMLGenerators { + damaris::model::DamarisParameterXML params_; // Layouts are typically descrived using parameters. Parameters can be shared between layouts. + damaris::model::DamarisVarXML variable_; // A variable + damaris::model::DamarisLayoutXML layout_; // each variable has one layout (layouts can be sahred) + damaris::model::DamarisMeshXML mesh_; // Contains extra elements that need to be part of a Damaris for visualization + damaris::model::DamarisStoreXML store_; // Contains the extra elements that need to be part of a Damaris (for HDF5 file w/r) +}; + +class Damaris_cfg +{ + std::string m_xml_config_object; + damaris::model::ModifyModel damarisXMLModifyModel; + + std::string m_init_on_event = ""; + std::string m_start_on_event = ""; + std::string m_stop_on_event = ""; + std::string m_end_iteration_on_event = ""; + std::string m_finalize_on_event = ""; + + + int m_arch_domains; + int m_dc_cores_pernode; + int m_dc_nodes; + + + int m_placement_start; + int m_placement_stride; + int m_placement_blocksize; + std::string m_placement_mask_str; + + PDI::Expression m_communicator; + + std::unordered_map m_datasets; + std::unordered_map m_layouts; + std::unordered_map m_parameters; + std::unordered_map m_storages; + std::unordered_map m_groups; + + std::unordered_map m_descs; + std::unordered_map m_events; + std::unordered_map m_datasets_to_write; + list m_after_write_events; + //> + std::unordered_map> m_parameter_to_update; + + //In damaris parameter play similar role than metadata in PDI, in that other variable can be expressed by them + // Here we express parameter in terms of PDI metadata, and when metadata are expose, + // we need to be awards in order to update parameter value for Damaris + // This variable is intended for that + std::unordered_map> m_parameter_depends_on; + std::unordered_map m_parameter_expression; + std::unordered_map> m_layout_depends_on; + std::unordered_map m_layout_expression; + + static std::string m_is_client_dataset_name; + static std::string m_client_comm_get_dataset_name; + + + const std::string XML_CONFIG_TEMPLATE = R"V0G0N( + + + + + + + + + + + _DATASET_ELEMENT_REGEX_ + + + + _STORAGE_ELEMENT_REGEX_ + + + _PLUGINS_REGEX_ + + + + + + _SCRIPTS_REGEX_ + + + + + )V0G0N"; + + +protected: + void parse_architecture_tree(Context& ctx, PC_tree_t arch_tree); + void parse_parameters_tree(Context& ctx, PC_tree_t parameters_tree_list); + void parse_datasets_tree(Context& ctx, PC_tree_t datasets_tree_list); + void parse_layouts_tree(Context& ctx, PC_tree_t layouts_tree_list); + void parse_storages_tree(Context& ctx, PC_tree_t storages_tree_list); + void parse_write_tree(Context& ctx, PC_tree_t write_tree_list); + void parse_parameter_to_update_tree(Context& ctx, PC_tree_t ptu_tree_list, Desc_type op_type); + void parse_log_tree(Context& ctx, PC_tree_t config); + + void init_xml_config_object() + { + m_xml_config_object = XML_CONFIG_TEMPLATE; + damarisXMLModifyModel = damaris::model::ModifyModel(m_xml_config_object); + } + + +public: + Damaris_cfg(PDI::Context& ctx, PC_tree_t tree); + + const std::string& xml_config_object(void); + + PDI::Expression communicator() const; + + const std::unordered_map& datasets() const; + const std::unordered_map& layouts() const; + const std::unordered_map& parameters() const; + const damaris::model::DamarisParameterXML get_parameter_xml(string prm_name) const; + const std::unordered_map& storages() const; + const std::unordered_map& groups() const; + + const std::unordered_map& descs() const; + const std::unordered_map& events() const; + const std::unordered_map& datasets_to_write() const; + const std::unordered_map>& parameter_to_update() const; + + Dataset_Write_Info get_dataset_write_info(std::string data_name) const; + std::pair get_parameter_to_update_info(std::string data_name) const; + + list get_after_write_events() const { return m_after_write_events; } + + bool is_there_after_write_events() const { return m_after_write_events.size(); } + + static std::string is_client_dataset_name() { return m_is_client_dataset_name; } + + static std::string client_comm_get_dataset_name() { return m_client_comm_get_dataset_name; } + + std::string init_on_event() const; + std::string start_on_event() const; + std::string stop_on_event() const; + std::string end_iteration_on_event() const; + std::string finalize_on_event() const; + + const std::unordered_map>& recover_var() const; + + const std::unordered_map>>& send_file() const; + + + bool is_dataset_to_write(std::string data_name); + bool is_parameter_to_update(std::string data_name); + bool is_needed_metadata(std::string data_name); + + std::unordered_map> get_updatable_parameters(Context& ctx); + + void reset_parameter_depends_on(std::string prm_name); + void reset_parameter_depends_on(std::vector prm_list); + + void reset_all_parameters_depends_on(); +}; // class Damaris_cfg + +} // namespace damaris_pdi + +#endif // DAMARIS_CFG_H_ diff --git a/plugins/damaris/damaris_wrapper.cxx b/plugins/damaris/damaris_wrapper.cxx new file mode 100644 index 000000000..964ae8693 --- /dev/null +++ b/plugins/damaris/damaris_wrapper.cxx @@ -0,0 +1,226 @@ +/******************************************************************************* + * Copyright (C) 2015-2024 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2024 National Institute for Research in Digital Science and Technology (Inria) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of CEA nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + + +#include +#include +#include +#include +#include +#include + +#include + +#include "damaris_wrapper.h" + +using PDI::Context; +using PDI::Context_proxy; +using PDI::Data_descriptor; +using PDI::Datatype_sptr; +using PDI::Impl_error; +using PDI::Plugin_error; +using PDI::Scalar_datatype; +using PDI::Scalar_kind; +using PDI::to_string; + +using std::string; + +namespace { + +void add_predefined(Context& ctx, const std::string& name, void* data, Datatype_sptr type) +{ + Data_descriptor& predef_desc = ctx.desc(name); + if (!predef_desc.empty()) { + throw Impl_error{"Predefined descriptor already defined `%s'", name.c_str()}; + } + + predef_desc.metadata(true); + // share a RO reference on comm_self with no memory destruction function (local variable) + predef_desc.share({data, nullptr, move(type), true, false}, true, false); + predef_desc.reclaim(); // reclaim the reference and let PDI keep a copy (metadata) +} + +} // namespace + +namespace damaris_pdi { + +// Constructer calls damaris_init() +Damaris_wrapper::Damaris_wrapper(Context& ctx, const char* xmlConfigObject, MPI_Comm comm) +{ + ctx.logger().info("Damaris lib initialization starts..."); + int status = damaris_pdi_initialize(xmlConfigObject, comm); + if (status != DAMARIS_OK) { + throw Plugin_error{"Cannot initialize Damaris library"}; + } else + ctx.logger().info("Damaris lib initialization Done!"); +} + +int Damaris_wrapper::damaris_pdi_initialize(const char* configfile, MPI_Comm comm) +{ + return damaris_initialize(configfile, comm); +} + +int Damaris_wrapper::damaris_pdi_finalize(void) +{ + return damaris_finalize(); +} + +int Damaris_wrapper::damaris_pdi_start(int* is_client) +{ + return damaris_start(is_client); +} + +int Damaris_wrapper::damaris_pdi_stop(void) +{ + return damaris_stop(); +} + +int Damaris_wrapper::damaris_pdi_write(const char* varname, const void* data) +{ + return damaris_pdi_write_block(varname, 0, data); +} + +int Damaris_wrapper::damaris_pdi_write(std::string varname, const void* data) +{ + return damaris_pdi_write_block(varname, 0, data); +} + +bool Damaris_wrapper::damaris_pdi_write_block(const char* varname, int32_t block, const void* data) +{ + return damaris_write_block(varname, block, data); +} + +bool Damaris_wrapper::damaris_pdi_write_block(std::string varname, int32_t block, const void* data) +{ + return damaris_write_block(varname.c_str(), block, data); +} + +int Damaris_wrapper::damaris_pdi_get_type(const char* variable_name, DAMARIS_TYPE_STR* vartype) +{ + return damaris_get_type(variable_name, vartype); +} + +int Damaris_wrapper::damaris_pdi_has_plugin(DAMARIS_PLUGIN_TYPE plugin) +{ + return damaris_has_plugin(plugin); +} + +int Damaris_wrapper::damaris_pdi_alloc(const char* varname, void** ptr) +{ + return damaris_alloc(varname, ptr); +} + +int Damaris_wrapper::damaris_pdi_alloc_block(const char* varname, int32_t block, void** ptr) +{ + return damaris_alloc_block(varname, block, ptr); +} + +int Damaris_wrapper::damaris_pdi_commit(const char* varname) +{ + return damaris_commit(varname); +} + +int Damaris_wrapper::damaris_pdi_commit_iteration(const char* varname, int32_t iteration) +{ + return damaris_commit_iteration(varname, iteration); +} + +int Damaris_wrapper::damaris_pdi_commit_block_iteration(const char* varname, int32_t block, int32_t iteration) +{ + return damaris_commit_block_iteration(varname, block, iteration); +} + +int Damaris_wrapper::damaris_pdi_clear(const char* varname) +{ + return damaris_clear(varname); +} + +int Damaris_wrapper::damaris_pdi_clear_block(const char* varname, int32_t block) +{ + return damaris_clear_block(varname, block); +} + +int Damaris_wrapper::damaris_pdi_clear_iteration(const char* varname, int32_t iteration) +{ + return damaris_clear_iteration(varname, iteration); +} + +int Damaris_wrapper::damaris_pdi_clear_block_iteration(const char* varname, int32_t block, int32_t iteration) +{ + return damaris_clear_block_iteration(varname, block, iteration); +} + +int Damaris_wrapper::damaris_pdi_signal(const char* signal_name) +{ + return damaris_signal(signal_name); +} + +int Damaris_wrapper::damaris_pdi_bind(const char* signal_name, signal_t sig) +{ + return damaris_bind(signal_name, sig); +} + +int Damaris_wrapper::damaris_pdi_parameter_get(const char* param_name, void* buffer, unsigned int size) +{ + return damaris_parameter_get(param_name, buffer, size); +} + +int Damaris_wrapper::damaris_pdi_parameter_set(const char* param_name, const void* buffer, unsigned int size) +{ + return damaris_parameter_set(param_name, buffer, size); +} + +int Damaris_wrapper::damaris_pdi_set_position(const char* var_name, const int64_t* position) +{ + return damaris_set_position(var_name, position); +} + +int Damaris_wrapper::damaris_pdi_set_block_position(const char* var_name, int32_t block, const int64_t* position) +{ + return damaris_set_block_position(var_name, block, position); +} + +int Damaris_wrapper::damaris_pdi_client_comm_get(MPI_Comm* comm) +{ + return damaris_client_comm_get(comm); +} + +int Damaris_wrapper::damaris_pdi_end_iteration(void) +{ + return damaris_end_iteration(); +} + +int Damaris_wrapper::damaris_pdi_get_iteration(int* iteration) +{ + return damaris_get_iteration(iteration); +} + +Damaris_wrapper::~Damaris_wrapper() +{ + // Call before MPI_Finalize() + //damaris_finalize();//Commented to avoid double finalize bug, Thank you Jacques :) +} + +} // namespace damaris_pdi diff --git a/plugins/damaris/damaris_wrapper.h b/plugins/damaris/damaris_wrapper.h new file mode 100644 index 000000000..b623a0c6c --- /dev/null +++ b/plugins/damaris/damaris_wrapper.h @@ -0,0 +1,387 @@ +/******************************************************************************* + * Copyright (C) 2015-2024 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2024 National Institute for Research in Digital Science and Technology (Inria) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of CEA nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + + +#ifndef DAMARIS_WRAPPER_H_ +#define DAMARIS_WRAPPER_H_ + +#include +#include + +#include + +//#include "damaris_cfg.h" + +namespace damaris_pdi { + +class Damaris_wrapper +{ + Damaris_wrapper(const Damaris_wrapper&) = delete; + + Damaris_wrapper& operator= (const Damaris_wrapper&) = delete; + + int m_is_client; // if != 0 (In this case == 1) then the process is a Damaris client (i.e. a simulation rank) + +public: + Damaris_wrapper(PDI::Context& ctx, const char* xmlConfigObject, MPI_Comm comm); + + // Damaris_wrapper(PDI::Context& ctx, const Damaris_cfg& config, MPI_Comm comm, PC_tree_t logging_tree); + //Damaris_wrapper(PDI::Context& ctx, Damaris_cfg::Damaris_cfg& config, MPI_Comm comm); + + // void set_is_client(int is_client) ; + // int get_is_client( void ) ; + + + void set_is_client(int is_client) { m_is_client = is_client; } + + int get_is_client(void) { return m_is_client; } + +private: + /** + * Initializes Damaris, should be called after MPI_Init. + * + * \param[in] configfile : name of the XML configuration file. + * \param[in] comm : MPI communicator gathering all the nodes. + * + * \return DAMARIS_OK on success, other error codes on failures. + * + */ + int damaris_pdi_initialize(const char* configfile, MPI_Comm comm); + +public: + /** + * Finalize Damaris. Should be called before MPI_Finalize. If Damaris was + * started (damaris_start()), it should be stopped (using damaris_stop()) before this call. + * + * \return DAMARIS_OK on success, other error codes on failures. + * + */ + int damaris_pdi_finalize(void); + + /** + * Starts the server(s). Sets is_client to 1 if this core is a client. + * Otherwise, this function starts the server and blocks until clients call + * damaris_stop, and is_client is set to 0. + * + * \param[out] is_client : indicates if this core is a client. + * + * \return DAMARIS_OK on success, other error codes on failures. + * + */ + int damaris_pdi_start(int* is_client); + + /** + * Stops the server. This function should only be called by client processes + * and these processes must have called damaris_start to start the servers + * before. When all client processes have called damaris_stop, server processes + * blocked on damaris_start will return (with is_client set to 0). + * + * \return DAMARIS_OK on success, other error codes on failures. + * + */ + int damaris_pdi_stop(void); + + + /** + * Writes a variable (similar to damaris_write_block(varnale,0,data)). + * + * \param[in] varname : name of the variable to write. + * \param[in] data : pointer to the data to write. + * + * \return DAMARIS_OK on success, other error codes on failures. + */ + int damaris_pdi_write(const char* varname, const void* data); + /** + * Writes a variable (similar to damaris_write_block(varnale,0,data)). + * + * \param[in] varname : name of the variable to write. + * \param[in] data : pointer to the data to write. + * + * \return DAMARIS_OK on success, other error codes on failures. + */ + int damaris_pdi_write(std::string varname, const void* data); + + + /** + * Writes a block of a variable. The variable name should be the full name + * of a variable defined in the configuration file. The block id should be + * between 0 and the number of domains per client - 1, as defined in the + * configuration file. + * + * \param[in] varname : name of the variable to write. + * \param[in] block : id of the block to write. + * \param[in] data : pointer to the data to write. + * + * \return DAMARIS_OK on success, other error codes on failures. + */ + bool damaris_pdi_write_block(const char* varname, int32_t block, const void* data); + /** + * Writes a block of a variable. The variable name should be the full name + * of a variable defined in the configuration file. The block id should be + * between 0 and the number of domains per client - 1, as defined in the + * configuration file. + * + * \param[in] varname : name of the variable to write. + * \param[in] block : id of the block to write. + * \param[in] data : pointer to the data to write. + * + * \return DAMARIS_OK on success, other error codes on failures. + */ + bool damaris_pdi_write_block(std::string varname, int32_t block, const void* data); + + /** + * Checks what a variables layout type is and returns an enumerated constant + * indicating the type + * + * \param[in] varname : name of the variable to check the type of. + * \param[out] vartype : enumerated constant indicating what data type the + * Variable data is defined with. + * + * \return DAMARIS_OK on success, other error codes on failures. + */ + int damaris_pdi_get_type(const char* variable_name, DAMARIS_TYPE_STR* vartype); + + + /** + * Checks if a particular Damaris plugin is available. + * + * \param[out] vartype : enumerated constant indicating what data type the + * Variable data is defined with. + * + * \return 1 on plugin being available, 0 otherwise + */ + int damaris_pdi_has_plugin(DAMARIS_PLUGIN_TYPE plugin); + + /** + * Allocates the data required for a variable to be entirely written in memory. + * Similar to damaris_alloc_block(varname,0,ptr). + * + * \param[in] varname : name of the variable to write. + * \param[out] ptr : pointer to a pointer to the allocated memory. + * + * \return DAMARIS_OK on success, other error codes on failure. + */ + int damaris_pdi_alloc(const char* varname, void** ptr); + + /** + * Allocates the data required for a block of a variable to be written + * in memory. + * + * \param[in] varname : name of the variable to write. + * \param[in] block : block id for which to allocate memory. + * \param[out] ptr : pointer to a pointer to the allocated memory. + */ + int damaris_pdi_alloc_block(const char* varname, int32_t block, void** ptr); + + /** + * Commits data associated to the variable on current iteration. This function + * is equivalent to calling damaris_commit_block(varname,0). + * + * \param[in] varname : name of the variable to commit. + * + * \return DAMARIS_OK on success, other error codes on failures. + * + */ + int damaris_pdi_commit(const char* varname); + + /** + * Commits variable from the given iteration. This call is equivalent to + * damaris_commit_block_iteration(varname,0,iteration). + * + * \param[in] varname : name of the variable to commit. + * \param[in] iteration : iteration to commit. + * + * \return DAMARIS_OK on success, other error codes on failures. + * + */ + int damaris_pdi_commit_iteration(const char* varname, int32_t iteration); + + /** + * Commits a specific block of variable from the given iteration. The variable + * should be defined in the configuration file and the block id should be + * within the range defined by the number of domains per client. + * + * \param[in] varname : name of the variable to commit. + * \param[in] block : block id. + * \param[in] iteration : iteration to commit. + * + * \return DAMARIS_OK on success, other error codes on failures. + * + */ + int damaris_pdi_commit_block_iteration(const char* varname, int32_t block, int32_t iteration); + + /** + * Clears the specified variable. This call is equivalent to + * damaris_clear_block(varname,0). Will transfer the responsibility of the data + * to the dedicated cores. + * + * \param[in] varname : name of the variable to clear. + * + * \return DAMARIS_OK on success, other error codes on failures. + * + */ + int damaris_pdi_clear(const char* varname); + + /** + * Clears the block for the specified variable. + * + * \param[in] varname : name of the variable to clear. + * \param[in] block : block id. + * + * \return DAMARIS_OK on success, other error codes on failures. + * + */ + int damaris_pdi_clear_block(const char* varname, int32_t block); + + /** + * Clears the specified variable at a specified iteration. + * + * \param[in] varname : name of the variable to clear. + * \param[in] iteration : iteration to clear. + * + * \return DAMARIS_OK on success, other error codes on failures. + * + */ + int damaris_pdi_clear_iteration(const char* varname, int32_t iteration); + + /** + * Clears the block for the specified variable at a specified iteration. + * + * \param[in] varname : name of the variable to clear. + * \param[in] iteration : iteration number. + * \param[in] block : block id. + * + * \return DAMARIS_OK on success, other error codes on failures. + * + */ + int damaris_pdi_clear_block_iteration(const char* varname, int32_t block, int32_t iteration); + + /** + * Sends a signal to the closest dedicated core. + * + * \param[in] signal_name : name of the signal to send, must correspond to an + * event or a script in the configuration file. + * + * \return DAMARIS_OK on success, other error codes on failures. + * + */ + int damaris_pdi_signal(const char* signal_name); + + /** + * Associates a signal name to a function from the executable. + * + * \param[in] signal_name : name of the signal. + * \param[in] sig : function to bind. + * + * \return DAMARIS_OK on success, other error codes on failures. + * + */ + int damaris_pdi_bind(const char* signal_name, signal_t sig); + + /** + * Get the current value of a parameter. + * + * \param[in] param_name : name of the parameter to read. + * \param[out] buffer : buffer in which to put the value. + * \param[in] size : maximum size (in bytes) in the buffer. + * + * \return DAMARIS_OK on success, other error codes on failures. + * + */ + int damaris_pdi_parameter_get(const char* param_name, void* buffer, unsigned int size); + + /** + * Set the value of a parameter. + * + * \param[in] param_name : name of the parameter. + * \param[in] buffer : buffer from which to take the value. + * \param[in] size : size of the buffer. + * + * \return DAMARIS_OK on success, other error codes on failures. + * + */ + int damaris_pdi_parameter_set(const char* param_name, const void* buffer, unsigned int size); + + /** + * Changes the position of the variable. Sets the meta-data that represents + * the start of position within the variable of which the block stores the data + * of. + * Equivalent to a call to + * damaris_set_block_position(var_name,0,position). + * + * \param[in] var_name : name of the variable to move. + * \param[in] position : array of new coordinates. + * + * \return DAMARIS_OK on success, other error codes on failures. + * + */ + int damaris_pdi_set_position(const char* var_name, const int64_t* position); + + /** + * Changes the position of a block of the variable. + * + * \param[in] var_name : name of the variable to move. + * \param[in] block : block id. + * \param[in] position : array of new coordinates. + * + * \return DAMARIS_OK on success, other error codes on failures. + * + */ + int damaris_pdi_set_block_position(const char* var_name, int32_t block, const int64_t* position); + + /** + * Gets the communicator that the clients must use. + * + * \param[out] comm : communicator gathering clients. + * + * \return DAMARIS_OK on success, other error codes on failures. + * + */ + int damaris_pdi_client_comm_get(MPI_Comm* comm); + + /** + * Ends the current iteration. + * + * \return DAMARIS_OK on success, other error codes on failures. + * + */ + int damaris_pdi_end_iteration(void); + + /** + * Gets the current iteration number. + * + * \param[out] iteration : current iteration number. + * + * \return DAMARIS_OK on success, other error codes on failures. + * + */ + int damaris_pdi_get_iteration(int* iteration); + + + ~Damaris_wrapper(); +}; // class Damaris_wrapper + +} // namespace damaris_pdi +#endif // DAMARIS_WRAPPER_H_ diff --git a/plugins/damaris/example/CMakeLists.txt b/plugins/damaris/example/CMakeLists.txt new file mode 100644 index 000000000..3e91bf627 --- /dev/null +++ b/plugins/damaris/example/CMakeLists.txt @@ -0,0 +1,44 @@ +#============================================================================= +# Copyright (C) 2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) +# Copyright (C) 2026 Institut national de recherche en informatique et en automatique (Inria) +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the names of CEA, nor the names of the contributors may be used to +# endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +# copy yaml file, to binary folder, for example_damaris_plugin.c +configure_file(example_damaris_plugin.yml example_damaris_plugin.yml COPYONLY) +add_executable (example_damaris_plugin example_damaris_plugin.c) +target_link_libraries (example_damaris_plugin PDI::PDI_C MPI::MPI_CXX) + +# copy yaml file, to binary folder, for example_damaris_plugin_with_is_client.c +configure_file(example_damaris_plugin_with_is_client.yml example_damaris_plugin_with_is_client.yml COPYONLY) +add_executable (example_damaris_plugin_with_is_client example_damaris_plugin_with_is_client.c) +target_link_libraries (example_damaris_plugin_with_is_client PDI::PDI_C MPI::MPI_CXX) + +# copy xml file for damaris, to binary folder, for example_damaris_only.c +configure_file(example_damaris_api.xml example_damaris_api.xml COPYONLY) +add_executable(example_damaris_only example_damaris_only.c) +target_link_libraries(example_damaris_only ${Damaris_DEPS}) diff --git a/plugins/damaris/example/README.md b/plugins/damaris/example/README.md new file mode 100644 index 000000000..7419c45c5 --- /dev/null +++ b/plugins/damaris/example/README.md @@ -0,0 +1,9 @@ +# description of the examples + +All the examples solved the heat equation in 2D. + +* `example_damaris_only.c`: In this code, we use only damaris library. The input file is `example_damaris_api.xml`. + +* `example_damaris_plugin.c`: Here, pdi is used to call the damaris library with the plugin dedicated to this library. Moreover, the finalization of the server process (damaris) is done inside the damaris plugin in calling (PDI_finalize, MPI_finalize). The input file is `example_damaris_plugin.yml`. + +* `example_damaris_plugin_with_is_client.c`: In this case, pdi is used to call the damaris library with the plugin dedicated to this library. Moreover, the finalization of the server process (damaris) is done in the simulation code. The input file is `example_damaris_plugin_with_is_client.yml`. \ No newline at end of file diff --git a/plugins/damaris/example/example_damaris_api.xml b/plugins/damaris/example/example_damaris_api.xml new file mode 100644 index 000000000..7c8912991 --- /dev/null +++ b/plugins/damaris/example/example_damaris_api.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/damaris/example/example_damaris_only.c b/plugins/damaris/example/example_damaris_only.c new file mode 100644 index 000000000..56462a55b --- /dev/null +++ b/plugins/damaris/example/example_damaris_only.c @@ -0,0 +1,258 @@ +/******************************************************************************* + * Copyright (C) 2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2026 National Institute for Research in Digital Science and Technology (Inria) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of CEA nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "Damaris.h" + +void init(int dsize[2], int pcoord[2], double dat[dsize[0]][dsize[1]]) +{ + for (int yy = 0; yy < dsize[0]; ++yy) { + for (int xx = 0; xx < dsize[1]; ++xx) { + dat[yy][xx] = 0; + } + } + if (pcoord[1] == 0) { + for (int yy = 0; yy < dsize[0]; ++yy) { + dat[yy][0] = 1000000; + } + } +} + +void iter(int dsize[2], double cur[dsize[0]][dsize[1]], double next[dsize[0]][dsize[1]]) +{ + int xx, yy; + for (xx = 0; xx < dsize[1]; ++xx) { + next[0][xx] = cur[0][xx]; + } + for (yy = 1; yy < dsize[0] - 1; ++yy) { + next[yy][0] = cur[yy][0]; + for (xx = 1; xx < dsize[1] - 1; ++xx) { + next[yy][xx] + = (cur[yy][xx] * .5) + (cur[yy][xx - 1] * .125) + (cur[yy][xx + 1] * .125) + (cur[yy - 1][xx] * .125) + (cur[yy + 1][xx] * .125); + } + next[yy][dsize[1] - 1] = cur[yy][dsize[1] - 1]; + } + for (xx = 0; xx < dsize[1]; ++xx) { + next[dsize[0] - 1][xx] = cur[dsize[0] - 1][xx]; + } +} + +void exchange(MPI_Comm cart_com, int dsize[2], double cur[dsize[0]][dsize[1]]) +{ + MPI_Status status; + int rank_source, rank_dest; + static MPI_Datatype column, row; + static int initialized = 0; + + if (!initialized) { + MPI_Type_vector(dsize[0] - 2, 1, dsize[1], MPI_DOUBLE, &column); + MPI_Type_commit(&column); + MPI_Type_contiguous(dsize[1] - 2, MPI_DOUBLE, &row); + MPI_Type_commit(&row); + initialized = 1; + } + + /* send down */ + MPI_Cart_shift(cart_com, 0, 1, &rank_source, &rank_dest); + MPI_Sendrecv( + &cur[dsize[0] - 2][1], + 1, + row, + rank_dest, + 100, /* send row before ghost */ + &cur[0][1], + 1, + row, + rank_source, + 100, /* receive 1st row (ghost) */ + cart_com, + &status + ); + + /* send up */ + MPI_Cart_shift(cart_com, 0, -1, &rank_source, &rank_dest); + MPI_Sendrecv( + &cur[1][1], + 1, + row, + rank_dest, + 100, /* send column after ghost */ + &cur[dsize[0] - 1][1], + 1, + row, + rank_source, + 100, /* receive last column (ghost) */ + cart_com, + &status + ); + + /* send to the right */ + MPI_Cart_shift(cart_com, 1, 1, &rank_source, &rank_dest); + MPI_Sendrecv( + &cur[1][dsize[1] - 2], + 1, + column, + rank_dest, + 100, /* send column before ghost */ + &cur[1][0], + 1, + column, + rank_source, + 100, /* receive 1st column (ghost) */ + cart_com, + &status + ); + + /* send to the left */ + MPI_Cart_shift(cart_com, 1, -1, &rank_source, &rank_dest); + MPI_Sendrecv( + &cur[1][1], + 1, + column, + rank_dest, + 100, /* send column after ghost */ + &cur[1][dsize[1] - 1], + 1, + column, + rank_source, + 100, /* receive last column (ghost) */ + cart_com, + &status + ); +} + +int main(int argc, char* argv[]) +{ + MPI_Init(&argc, &argv); + + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + exit(1); + } + + // All processes must initialize Damaris with the XML configuration + // - client process = heat simulation process + // - server process = damaris process for writting hdf5 file. + damaris_initialize(argv[1], MPI_COMM_WORLD); + + int is_client; + + int err = damaris_start(&is_client); + printf("-------------------------------------------------------------------------------------D: :) ;) is_client = %d\n", is_client); + + MPI_Comm main_comm = MPI_COMM_WORLD; + damaris_client_comm_get(&main_comm); // <-- returns Damaris client comm + + // Again, we check that servers have been started properly and that + // this process is a client. + if ((err == DAMARIS_OK || err == DAMARIS_NO_SERVER) && is_client) { + int psize_1d; + MPI_Comm_size(main_comm, &psize_1d); + int pcoord_1d; + MPI_Comm_rank(main_comm, &pcoord_1d); + + long longval; + + int dsize[2]; + damaris_parameter_get("global_height", &longval, sizeof(int)); + dsize[0] = longval; + damaris_parameter_get("global_width", &longval, sizeof(int)); + dsize[1] = longval; + + int psize[2]; + damaris_parameter_get("parallelism_h", &longval, sizeof(int)); + psize[0] = longval; + damaris_parameter_get("parallelism_w", &longval, sizeof(int)); + psize[1] = longval; + + // get local & add ghosts to sizes + assert(dsize[0] % psize[0] == 0); + dsize[0] = dsize[0] / psize[0] + 2; + assert(dsize[1] % psize[1] == 0); + dsize[1] = dsize[1] / psize[1] + 2; + + printf("(psize[1] * psize[0] == psize_1d) === (%d * %d == %d) \n", psize[1], psize[0], psize_1d); + assert(psize[1] * psize[0] == psize_1d); + + int cart_period[2] = {0, 0}; + MPI_Comm cart_com; + MPI_Cart_create(main_comm, 2, psize, cart_period, 1, &cart_com); + int pcoord[2]; + MPI_Cart_coords(cart_com, pcoord_1d, 2, pcoord); + + int ii = 0; + int main_field_layout_global0 = psize[0] * (dsize[0] - 2); + int main_field_layout_global1 = psize[1] * (dsize[1] - 2); + damaris_parameter_set("main_field_layout_global0", &main_field_layout_global0, sizeof(int)); + damaris_parameter_set("main_field_layout_global1", &main_field_layout_global1, sizeof(int)); + damaris_parameter_set("main_field_layout_dim0", &dsize[0], sizeof(int)); + damaris_parameter_set("main_field_layout_dim1", &dsize[1], sizeof(int)); + + + double(*cur)[dsize[1]] = malloc(sizeof(double) * dsize[1] * dsize[0]); + double(*next)[dsize[1]] = malloc(sizeof(double) * dsize[1] * dsize[0]); + + init(dsize, pcoord, cur); + + int64_t position_main_field[2]; + + position_main_field[0] = (dsize[0] - 2) * pcoord[0]; + position_main_field[1] = (dsize[1] - 2) * pcoord[1]; + + double start = MPI_Wtime(); + int next_reduce = 0; + for (ii = 0; ii < 10; ++ii) { + //Iteration write + damaris_set_position("main_field", position_main_field); + damaris_write("main_field", cur); + damaris_end_iteration(); + + iter(dsize, cur, next); + exchange(cart_com, dsize, next); + double(*tmp)[dsize[1]] = cur; + cur = next; + next = tmp; + } + //Last write + damaris_set_position("main_field", position_main_field); + damaris_write("main_field", cur); + damaris_end_iteration(); + + damaris_stop(); + free(cur); + free(next); + } + + damaris_finalize(); + + MPI_Finalize(); + return 0; +} diff --git a/plugins/damaris/example/example_damaris_plugin.c b/plugins/damaris/example/example_damaris_plugin.c new file mode 100644 index 000000000..d53445250 --- /dev/null +++ b/plugins/damaris/example/example_damaris_plugin.c @@ -0,0 +1,267 @@ +/******************************************************************************* + * Copyright (C) 2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2025-2026 National Institute for Research in Digital Science and Technology (Inria) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of CEA nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pdi.h" + +void init(int dsize[2], int pcoord[2], double dat[dsize[0]][dsize[1]]) +{ + for (int yy = 0; yy < dsize[0]; ++yy) { + for (int xx = 0; xx < dsize[1]; ++xx) { + dat[yy][xx] = 0; + } + } + if (pcoord[1] == 0) { + for (int yy = 0; yy < dsize[0]; ++yy) { + dat[yy][0] = 1000000; + } + } +} + +void iter(int dsize[2], double cur[dsize[0]][dsize[1]], double next[dsize[0]][dsize[1]]) +{ + int xx, yy; + for (xx = 0; xx < dsize[1]; ++xx) { + next[0][xx] = cur[0][xx]; + } + for (yy = 1; yy < dsize[0] - 1; ++yy) { + next[yy][0] = cur[yy][0]; + for (xx = 1; xx < dsize[1] - 1; ++xx) { + next[yy][xx] + = (cur[yy][xx] * .5) + (cur[yy][xx - 1] * .125) + (cur[yy][xx + 1] * .125) + (cur[yy - 1][xx] * .125) + (cur[yy + 1][xx] * .125); + } + next[yy][dsize[1] - 1] = cur[yy][dsize[1] - 1]; + } + for (xx = 0; xx < dsize[1]; ++xx) { + next[dsize[0] - 1][xx] = cur[dsize[0] - 1][xx]; + } +} + +void exchange(MPI_Comm cart_com, int dsize[2], double cur[dsize[0]][dsize[1]]) +{ + MPI_Status status; + int rank_source, rank_dest; + static MPI_Datatype column, row; + static int initialized = 0; + + if (!initialized) { + MPI_Type_vector(dsize[0] - 2, 1, dsize[1], MPI_DOUBLE, &column); + MPI_Type_commit(&column); + MPI_Type_contiguous(dsize[1] - 2, MPI_DOUBLE, &row); + MPI_Type_commit(&row); + initialized = 1; + } + + /* send down */ + MPI_Cart_shift(cart_com, 0, 1, &rank_source, &rank_dest); + MPI_Sendrecv( + &cur[dsize[0] - 2][1], + 1, + row, + rank_dest, + 100, /* send row before ghost */ + &cur[0][1], + 1, + row, + rank_source, + 100, /* receive 1st row (ghost) */ + cart_com, + &status + ); + + /* send up */ + MPI_Cart_shift(cart_com, 0, -1, &rank_source, &rank_dest); + MPI_Sendrecv( + &cur[1][1], + 1, + row, + rank_dest, + 100, /* send column after ghost */ + &cur[dsize[0] - 1][1], + 1, + row, + rank_source, + 100, /* receive last column (ghost) */ + cart_com, + &status + ); + + /* send to the right */ + MPI_Cart_shift(cart_com, 1, 1, &rank_source, &rank_dest); + MPI_Sendrecv( + &cur[1][dsize[1] - 2], + 1, + column, + rank_dest, + 100, /* send column before ghost */ + &cur[1][0], + 1, + column, + rank_source, + 100, /* receive 1st column (ghost) */ + cart_com, + &status + ); + + /* send to the left */ + MPI_Cart_shift(cart_com, 1, -1, &rank_source, &rank_dest); + MPI_Sendrecv( + &cur[1][1], + 1, + column, + rank_dest, + 100, /* send column after ghost */ + &cur[1][dsize[1] - 1], + 1, + column, + rank_source, + 100, /* receive last column (ghost) */ + cart_com, + &status + ); +} + +int main(int argc, char* argv[]) +{ + MPI_Init(&argc, &argv); + + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + exit(1); + } + + PC_tree_t conf = PC_parse_path(argv[1]); + + MPI_Comm main_comm = MPI_COMM_WORLD; + int world_size; + MPI_Comm_size(MPI_COMM_WORLD, &world_size); + if (world_size != 4) { + fprintf(stderr, "Please use at least 4 mpi processes\n"); + exit(1); + } + PDI_init(PC_get(conf, ".pdi")); + + // expose the pointer of conf + uintptr_t pconf = (uintptr_t)&conf; + PDI_expose("conf_yaml", &pconf, PDI_OUT); + + PDI_expose("mpi_comm", &main_comm, PDI_INOUT); // <-- allow plugin to set, returns Damaris client comm + + int psize_1d; + MPI_Comm_size(main_comm, &psize_1d); + int pcoord_1d; + MPI_Comm_rank(main_comm, &pcoord_1d); + + PDI_expose("mpi_rank", &pcoord_1d, PDI_OUT); + PDI_expose("mpi_size", &psize_1d, PDI_OUT); + + long longval; + + int dsize[2]; + PC_int(PC_get(conf, ".datasize[0]"), &longval); + dsize[0] = longval; + PC_int(PC_get(conf, ".datasize[1]"), &longval); + dsize[1] = longval; + + int psize[2]; + PC_int(PC_get(conf, ".parallelism.height"), &longval); + psize[0] = longval; + PC_int(PC_get(conf, ".parallelism.width"), &longval); + psize[1] = longval; + + double duration; + PC_double(PC_get(conf, ".duration"), &duration); + + // get local & add ghosts to sizes + assert(dsize[0] % psize[0] == 0); + dsize[0] = dsize[0] / psize[0] + 2; + assert(dsize[1] % psize[1] == 0); + dsize[1] = dsize[1] / psize[1] + 2; + + assert(psize[1] * psize[0] == psize_1d); + + int cart_period[2] = {0, 0}; + MPI_Comm cart_com; + MPI_Cart_create(main_comm, 2, psize, cart_period, 1, &cart_com); + int pcoord[2]; + MPI_Cart_coords(cart_com, pcoord_1d, 2, pcoord); + + int ii = 0; + PDI_expose("iter", &ii, PDI_OUT); + PDI_expose("dsize", dsize, PDI_OUT); + PDI_expose("psize", psize, PDI_OUT); + PDI_expose("pcoord", pcoord, PDI_OUT); + + double(*cur)[dsize[1]] = malloc(sizeof(double) * dsize[1] * dsize[0]); + double(*next)[dsize[1]] = malloc(sizeof(double) * dsize[1] * dsize[0]); + + init(dsize, pcoord, cur); + + PDI_event("main_loop"); + double start = MPI_Wtime(); + int next_reduce = 0; + for (ii = 0; ii < 10; ++ii) { + PDI_multi_expose("newiter", "iter", &ii, PDI_INOUT, "main_field", cur, PDI_INOUT, NULL); + + iter(dsize, cur, next); + exchange(cart_com, dsize, next); + double(*tmp)[dsize[1]] = cur; + cur = next; + next = tmp; + + // if (ii >= next_reduce) { + // double local_time, global_time; + // local_time = MPI_Wtime() - start; + // MPI_Allreduce(&local_time, &global_time, 1, MPI_DOUBLE, MPI_MAX, main_comm); + // if (global_time >= duration) { + // if (0 == pcoord_1d) printf("iter=%7d; time=%7.3f; STOP!!!\n", ii, global_time); + // break; + // } + // int rem_iter = .9 * (duration - global_time) * (ii + 1) / (global_time + 0.1); + // if (rem_iter < 1) rem_iter = 1; + // next_reduce = ii + rem_iter; + // if (0 == pcoord_1d) printf("iter=%7d; time=%7.3f; next_reduce=%7d\n", ii, global_time, next_reduce); + // } + } + PDI_expose("iter", &ii, PDI_OUT); + PDI_expose("main_field", cur, PDI_OUT); + + free(cur); + free(next); + + PDI_finalize(); + + PC_tree_destroy(&conf); + + MPI_Finalize(); +} diff --git a/plugins/damaris/example/example_damaris_plugin.yml b/plugins/damaris/example/example_damaris_plugin.yml new file mode 100644 index 000000000..408471a5c --- /dev/null +++ b/plugins/damaris/example/example_damaris_plugin.yml @@ -0,0 +1,59 @@ +# duration in seconds +duration: 0.75 +# global [height, width] (excluding boundary conditions or ghosts) +datasize: [60, 12] +# degree of parallelism +parallelism: { height: 3, width: 1 } + +pdi: + metadata: + iter: int # current iteration id + dsize: { size: 2, type: array, subtype: int } # local data size including ghosts/boundary + psize: { size: 2, type: array, subtype: int } # number of processes in each dimension + pcoord: { size: 2, type: array, subtype: int } # coordinate of the process + mpi_comm: MPI_Comm + conf_yaml: uintptr_t + data: + main_field: { size: [ '$dsize[0]', '$dsize[1]' ], type: array, subtype: double } + + plugins: + mpi: + damaris: + architecture: + sim_name: example + domains: 1 + dedicated: + core: 1 + node: 0 + client_comm_get: mpi_comm # Needed to return the Damaris client comm + datasets: + - dataset: + name: main_field + layout: main_field_layout + storage: hdf5_example + layouts: + - layout: + name: main_field_layout + type: double + global: ['$psize[0]*($dsize[0]-2)', '$psize[1]*($dsize[1]-2)'] + dimensions: [ '$dsize[0]', '$dsize[1]' ] # process dim, with ghosts/boundaries + ghosts: '1:1,1:1' + depends_on: [dsize, psize] # This will help ensure an update of the layout attributes value to Damaris lib once the metadata are exposed (using Damaris Parameters in the background)! + storages: + - storage: + name: hdf5_example + type: HDF5 + file_mode: Collective # or FilePerCore + files_path: ./HDF5_files_without_is_client/ # Where to save files + + write: + main_field: # the name of the data to write, if dataset not specified afterward! ~meme notion de dataset hdf5 + dataset: main_field + when: '$iter<10' # do only write the first 10 iterations (0...9), Default at every iteration. + position: ['($dsize[0]-2)*$pcoord[0]', '($dsize[1]-2)*$pcoord[1]'] # ~start de dataset_selection par iteration + + log: + file_name: example_without_is_client + rotation_size: 5 + log_level: info + flush: true \ No newline at end of file diff --git a/plugins/damaris/example/example_damaris_plugin_with_is_client.c b/plugins/damaris/example/example_damaris_plugin_with_is_client.c new file mode 100644 index 000000000..891367552 --- /dev/null +++ b/plugins/damaris/example/example_damaris_plugin_with_is_client.c @@ -0,0 +1,275 @@ +/******************************************************************************* + * Copyright (C) 2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2026 National Institute for Research in Digital Science and Technology (Inria) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of CEA nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pdi.h" + +void init(int dsize[2], int pcoord[2], double dat[dsize[0]][dsize[1]]) +{ + for (int yy = 0; yy < dsize[0]; ++yy) { + for (int xx = 0; xx < dsize[1]; ++xx) { + dat[yy][xx] = 0; + } + } + if (pcoord[1] == 0) { + for (int yy = 0; yy < dsize[0]; ++yy) { + dat[yy][0] = 1000000; + } + } +} + +void iter(int dsize[2], double cur[dsize[0]][dsize[1]], double next[dsize[0]][dsize[1]]) +{ + int xx, yy; + for (xx = 0; xx < dsize[1]; ++xx) { + next[0][xx] = cur[0][xx]; + } + for (yy = 1; yy < dsize[0] - 1; ++yy) { + next[yy][0] = cur[yy][0]; + for (xx = 1; xx < dsize[1] - 1; ++xx) { + next[yy][xx] + = (cur[yy][xx] * .5) + (cur[yy][xx - 1] * .125) + (cur[yy][xx + 1] * .125) + (cur[yy - 1][xx] * .125) + (cur[yy + 1][xx] * .125); + } + next[yy][dsize[1] - 1] = cur[yy][dsize[1] - 1]; + } + for (xx = 0; xx < dsize[1]; ++xx) { + next[dsize[0] - 1][xx] = cur[dsize[0] - 1][xx]; + } +} + +void exchange(MPI_Comm cart_com, int dsize[2], double cur[dsize[0]][dsize[1]]) +{ + MPI_Status status; + int rank_source, rank_dest; + static MPI_Datatype column, row; + static int initialized = 0; + + if (!initialized) { + MPI_Type_vector(dsize[0] - 2, 1, dsize[1], MPI_DOUBLE, &column); + MPI_Type_commit(&column); + MPI_Type_contiguous(dsize[1] - 2, MPI_DOUBLE, &row); + MPI_Type_commit(&row); + initialized = 1; + } + + /* send down */ + MPI_Cart_shift(cart_com, 0, 1, &rank_source, &rank_dest); + MPI_Sendrecv( + &cur[dsize[0] - 2][1], + 1, + row, + rank_dest, + 100, /* send row before ghost */ + &cur[0][1], + 1, + row, + rank_source, + 100, /* receive 1st row (ghost) */ + cart_com, + &status + ); + + /* send up */ + MPI_Cart_shift(cart_com, 0, -1, &rank_source, &rank_dest); + MPI_Sendrecv( + &cur[1][1], + 1, + row, + rank_dest, + 100, /* send column after ghost */ + &cur[dsize[0] - 1][1], + 1, + row, + rank_source, + 100, /* receive last column (ghost) */ + cart_com, + &status + ); + + /* send to the right */ + MPI_Cart_shift(cart_com, 1, 1, &rank_source, &rank_dest); + MPI_Sendrecv( + &cur[1][dsize[1] - 2], + 1, + column, + rank_dest, + 100, /* send column before ghost */ + &cur[1][0], + 1, + column, + rank_source, + 100, /* receive 1st column (ghost) */ + cart_com, + &status + ); + + /* send to the left */ + MPI_Cart_shift(cart_com, 1, -1, &rank_source, &rank_dest); + MPI_Sendrecv( + &cur[1][1], + 1, + column, + rank_dest, + 100, /* send column after ghost */ + &cur[1][dsize[1] - 1], + 1, + column, + rank_source, + 100, /* receive last column (ghost) */ + cart_com, + &status + ); +} + +int main(int argc, char* argv[]) +{ + MPI_Init(&argc, &argv); + + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + exit(1); + } + + PC_tree_t conf = PC_parse_path(argv[1]); + + MPI_Comm main_comm = MPI_COMM_WORLD; + int world_size; + MPI_Comm_size(MPI_COMM_WORLD, &world_size); + if (world_size != 4) { + fprintf(stderr, "Please use at least 4 mpi processes\n"); + exit(1); + } + PDI_init(PC_get(conf, ".pdi")); + + // expose the pointer of conf + uintptr_t pconf = (uintptr_t)&conf; + PDI_expose("conf_yaml", &pconf, PDI_OUT); + + // All processes must initialize Damaris with the XML configuration + // - client process = heat simulation process + // - server process = damaris process for writting hdf5 file. + + int is_client=0; + PDI_expose("is_client", &is_client, PDI_INOUT); // The order doesn't care + PDI_expose("mpi_comm", &main_comm, PDI_INOUT); // <-- allow plugin to set, returns Damaris client comm + + if (is_client) { + int psize_1d; + MPI_Comm_size(main_comm, &psize_1d); + int pcoord_1d; + MPI_Comm_rank(main_comm, &pcoord_1d); + + PDI_expose("mpi_rank", &pcoord_1d, PDI_OUT); + PDI_expose("mpi_size", &psize_1d, PDI_OUT); + + long longval; + + int dsize[2]; + PC_int(PC_get(conf, ".datasize[0]"), &longval); + dsize[0] = longval; + PC_int(PC_get(conf, ".datasize[1]"), &longval); + dsize[1] = longval; + + int psize[2]; + PC_int(PC_get(conf, ".parallelism.height"), &longval); + psize[0] = longval; + PC_int(PC_get(conf, ".parallelism.width"), &longval); + psize[1] = longval; + + double duration; + PC_double(PC_get(conf, ".duration"), &duration); + + // get local & add ghosts to sizes + assert(dsize[0] % psize[0] == 0); + dsize[0] = dsize[0] / psize[0] + 2; + assert(dsize[1] % psize[1] == 0); + dsize[1] = dsize[1] / psize[1] + 2; + + assert(psize[1] * psize[0] == psize_1d); + + int cart_period[2] = {0, 0}; + MPI_Comm cart_com; + MPI_Cart_create(main_comm, 2, psize, cart_period, 1, &cart_com); + int pcoord[2]; + MPI_Cart_coords(cart_com, pcoord_1d, 2, pcoord); + + int ii = 0; + PDI_expose("iter", &ii, PDI_OUT); + PDI_expose("dsize", dsize, PDI_OUT); + PDI_expose("psize", psize, PDI_OUT); + PDI_expose("pcoord", pcoord, PDI_OUT); + + double(*cur)[dsize[1]] = malloc(sizeof(double) * dsize[1] * dsize[0]); + double(*next)[dsize[1]] = malloc(sizeof(double) * dsize[1] * dsize[0]); + + init(dsize, pcoord, cur); + + PDI_event("main_loop"); + double start = MPI_Wtime(); + int next_reduce = 0; + for (ii = 0; ii < 10; ++ii) { + PDI_multi_expose("newiter", "iter", &ii, PDI_INOUT, "main_field", cur, PDI_INOUT, NULL); + + iter(dsize, cur, next); + exchange(cart_com, dsize, next); + double(*tmp)[dsize[1]] = cur; + cur = next; + next = tmp; + + // if (ii >= next_reduce) { + // double local_time, global_time; + // local_time = MPI_Wtime() - start; + // MPI_Allreduce(&local_time, &global_time, 1, MPI_DOUBLE, MPI_MAX, main_comm); + // if (global_time >= duration) { + // if (0 == pcoord_1d) printf("iter=%7d; time=%7.3f; STOP!!!\n", ii, global_time); + // break; + // } + // int rem_iter = .9 * (duration - global_time) * (ii + 1) / (global_time + 0.1); + // if (rem_iter < 1) rem_iter = 1; + // next_reduce = ii + rem_iter; + // if (0 == pcoord_1d) printf("iter=%7d; time=%7.3f; next_reduce=%7d\n", ii, global_time, next_reduce); + // } + } + PDI_expose("iter", &ii, PDI_OUT); + PDI_expose("main_field", cur, PDI_OUT); + + free(cur); + free(next); + } + + PDI_finalize(); + + PC_tree_destroy(&conf); + + MPI_Finalize(); +} diff --git a/plugins/damaris/example/example_damaris_plugin_with_is_client.yml b/plugins/damaris/example/example_damaris_plugin_with_is_client.yml new file mode 100644 index 000000000..339d91271 --- /dev/null +++ b/plugins/damaris/example/example_damaris_plugin_with_is_client.yml @@ -0,0 +1,61 @@ +# duration in seconds +duration: 0.75 +# global [height, width] (excluding boundary conditions or ghosts) +datasize: [60, 12] +# degree of parallelism +parallelism: { height: 3, width: 1 } + +pdi: + metadata: + iter: int # current iteration id + dsize: { size: 2, type: array, subtype: int } # local data size including ghosts/boundary + psize: { size: 2, type: array, subtype: int } # number of processes in each dimension + pcoord: { size: 2, type: array, subtype: int } # coordinate of the process + mpi_comm: MPI_Comm + conf_yaml: uintptr_t + is_client: int + data: + main_field: { size: [ '$dsize[0]', '$dsize[1]' ], type: array, subtype: double } + + plugins: + mpi: + damaris: + architecture: + sim_name: example + domains: 1 + dedicated: + core: 1 + node: 0 + client_comm_get: mpi_comm # Needed to return the Damaris client comm + get_is_client: is_client #Needed only when is_client is used + datasets: + - dataset: + name: main_field + layout: main_field_layout + storage: hdf5_example + layouts: + - layout: + name: main_field_layout + type: double + global: ['$psize[0]*($dsize[0]-2)', '$psize[1]*($dsize[1]-2)'] + dimensions: [ '$dsize[0]', '$dsize[1]' ] # process dim, with ghosts/boundaries + ghosts: '1:1,1:1' + depends_on: [dsize, psize] # This will help ensure an update of the layout attributes value to Damaris lib once the metadata are exposed (using Damaris Parameters in the background)! + storages: + - storage: + name: hdf5_example + type: HDF5 + file_mode: Collective # or FilePerCore + files_path: ./HDF5_files_with_is_client/ # Where to save files + + write: + main_field: # the name of the data to write, if dataset not specified afterward! ~meme notion de dataset hdf5 + dataset: main_field + when: '$iter<10' # do only write the first 10 iterations (0...9), Default at every iteration. + position: ['($dsize[0]-2)*$pcoord[0]', '($dsize[1]-2)*$pcoord[1]'] # ~start de dataset_selection par iteration + + log: + file_name: example_with_is_client + rotation_size: 5 + log_level: info + flush: true \ No newline at end of file diff --git a/plugins/damaris/tests/CMakeLists.txt b/plugins/damaris/tests/CMakeLists.txt new file mode 100644 index 000000000..ad760def6 --- /dev/null +++ b/plugins/damaris/tests/CMakeLists.txt @@ -0,0 +1,53 @@ +#============================================================================= +# Copyright (C) 2015-2024 Commissariat a l'energie atomique et aux energies alternatives (CEA) +# Copyright (C) 2021 Institute of Bioorganic Chemistry Polish Academy of Science (PSNC) +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the names of CEA, nor the names of the contributors may be used to +# endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +cmake_minimum_required(VERSION 3.16...3.29) + +set(RUNTEST_DIR "${CMAKE_CURRENT_LIST_DIR}/../cmake/runtest-dir") + +# Add the plugin path to PDI_PLUGIN_PATH +set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" PROPERTY TEST_INCLUDE_FILE "${CMAKE_CURRENT_BINARY_DIR}/TestPath.cmake") +file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TestPath.cmake" + CONTENT " +set(PDI_PLUGIN_PATH \"\$ENV{PDI_PLUGIN_PATH}\")\n +if(\"x\${PDI_PLUGIN_PATH}x\" STREQUAL xx)\n +set(ENV{PDI_PLUGIN_PATH} \"\$\")\n +else()\n +set(ENV{PDI_PLUGIN_PATH} \"\$:\${PDI_PLUGIN_PATH}\")\n +endif() +" +) + +# Damaris plugin test +add_executable(damaris_mpi_test_01 damaris_mpi_test_01.c) +target_link_libraries(damaris_mpi_test_01 damaris MPI::MPI_C MPI::MPI_CXX) +add_test(NAME damaris_mpi_test_01 COMMAND "${RUNTEST_DIR}" "${MPIEXEC}" "${MPIEXEC_NUMPROC_FLAG}" 2 ${MPIEXEC_PREFLAGS} "$" ${MPIEXEC_POSTFLAGS} "${CMAKE_CURRENT_SOURCE_DIR}/damaris_mpi_test_01.yml") +set_property(TEST damaris_mpi_test_01 PROPERTY PROCESSORS 1) + diff --git a/plugins/damaris/tests/damaris_mpi_test_01.c b/plugins/damaris/tests/damaris_mpi_test_01.c new file mode 100644 index 000000000..6d68f91b2 --- /dev/null +++ b/plugins/damaris/tests/damaris_mpi_test_01.c @@ -0,0 +1,120 @@ +/******************************************************************************* + * Copyright (C) 2015-2019 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2021 Institute of Bioorganic Chemistry Polish Academy of Science (PSNC) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of CEA nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include +#include +#include +#include +#include + +#define IMX 10 +#define JMX 5 + +int main(int argc, char* argv[]) +{ + int values[JMX][IMX], cp_values[JMX][IMX]; + double reals[JMX][IMX], cp_reals[JMX][IMX]; + int i, j, input; + int ni = IMX, nj = JMX; // PDI only + + MPI_Init(&argc, &argv); + assert(argc == 2 && "Needs 1 single arg: config file"); + + PC_tree_t conf = PC_parse_path(argv[1]); + MPI_Comm world = MPI_COMM_WORLD; + PDI_init(conf); + int rank; + MPI_Comm_rank(world, &rank); + + char filename[4096]; + snprintf(filename, 4096, "decl_hdf5_mpi_test_01_C_r%d.h5", rank); + remove(filename); + + // Fill arrays + for (j = 0; j < JMX; ++j) { + for (i = 0; i < IMX; ++i) { + values[j][i] = i; + reals[j][i] = (double)(i) + 0.1 * (i % 10); + cp_values[j][i] = -1; + cp_reals[j][i] = -1.0; + } + } + + input = 0; + PDI_expose("rank", &rank, PDI_OUT); + PDI_expose("input", &input, PDI_OUT); + // Set size for PDI + PDI_expose("ni", &ni, PDI_OUT); + PDI_expose("nj", &nj, PDI_OUT); + + // Test that export/exchange works + PDI_expose("input", &input, PDI_OUT); + PDI_expose("reals", &reals, PDI_OUT); // output real + PDI_expose("values", &values, PDI_INOUT); // output integers + + input = 1; + // Import should also work + PDI_expose("input", &input, PDI_OUT); // update metadata => HDF5 now import only + PDI_expose("reals", &cp_reals, PDI_IN); // input real + PDI_expose("values", &cp_values, PDI_INOUT); // input integers + + // So the data should be the same + fprintf(stderr, "Data exported | Data imported\n"); + for (j = 0; j < JMX; ++j) { + for (i = 0; i < IMX; ++i) { + fprintf(stderr, "%10d %4d\n", values[j][i], cp_values[j][i]); + fprintf(stderr, "%10.2f %2.2f\n", reals[j][i], cp_reals[j][i]); + if (values[j][i] != cp_values[j][i] || reals[j][i] != cp_reals[j][i]) { + fprintf( + stderr, + "[%d]: values[%d][%d] = %10d should be equal to cp_values[%d][%d] = %4d\n", + rank, + j, + i, + values[j][i], + j, + i, + cp_values[j][i] + ); + fprintf( + stderr, + "[%d]: reals[%d][%d] = %11.2f should be equal to cp_reals[%d][%d] = %5.2f\n", + rank, + j, + i, + reals[j][i], + j, + i, + cp_reals[j][i] + ); + MPI_Abort(MPI_COMM_WORLD, -1); + } + } + } + + PDI_finalize(); + PC_tree_destroy(&conf); + MPI_Finalize(); +} diff --git a/plugins/damaris/tests/damaris_mpi_test_01.yml b/plugins/damaris/tests/damaris_mpi_test_01.yml new file mode 100644 index 000000000..c4d1f8adc --- /dev/null +++ b/plugins/damaris/tests/damaris_mpi_test_01.yml @@ -0,0 +1,68 @@ +# duration in seconds +duration: 0.75 +# global [height, width] (excluding boundary conditions or ghosts) +datasize: [60, 12] +# degree of parallelism +parallelism: { height: 3, width: 1 } + +# only the following config is passed to PDI +pdi: + metadata: # type of small values for which PDI keeps a copy + iter: int # current iteration id + dsize: { size: 2, type: array, subtype: int } # local data size including ghosts/boundary + psize: { size: 2, type: array, subtype: int } # number of processes in each dimension + pcoord: { size: 2, type: array, subtype: int } # coordinate of the process + data: + main_field: { size: [ '$dsize[0]', '$dsize[1]' ], type: array, subtype: double } + + plugins: + mpi: + damaris: + + on_init: init #or init_on_event + #start_on_event: damaris_start + #end_iteration_on_event: damaris_end_iteration + on_finalize: finalization #Or finalize_on_event + communicator: $MPI_COMM_WORLD # pas utilisé actuellement + architecture: + sim_name: example + domains: 1 # => nb de block par sous domain + # damaris divise le sous domain par block equitablement + dedicated: + core: 1 + node: 0 + datasets: + - dataset: + name: main_field + layout: main_field_layout + mesh: mesh2d # pour la visualisation + centering: zonal + storage: hdf5_example + script: + visualizable: true + time_varying: true + #comment: This is the zonal pressure from our test simulation + layouts: # on ne peut pas modifier la valuer de layout une fois initialisé, travail en cours pour la modification dynamique + - layout: + name: main_field_layout # ~hdf5 dataset_selection + type: double + global: '$psize[0]*($dsize[0]-2),$psize[1]*($dsize[1]-2)' + dimensions: [ '$dsize[0]', '$dsize[1]' ] # process dim, with ghosts/boundaries + ghosts: '1:1,1:1' # 1 ghost à gauche de dim1, 1 ghost à droit de dim1 , 1 ghost à gauche de dim2, 1 ghost à droit de dim2 + depends_on: [dsize, psize] # This will help ensure an update of the layout attributes value to Damaris lib once the metadata are exposed (using Damaris Parameters in the background)! + storages: + - storage: + name: hdf5_example + type: HDF5 + file_mode: Collective # or FilePerCore + files_path: ./HDF5_files/ # Where to save files + # hdf5 file name defined by damaris + #frequency: 1 + + #Optional config, has a default behavior + log: + #file_name: example # default = $sim_name + rotation_size: 5 + log_level: info + flush: true +