diff --git a/.jenkins/mpi_compiler_checks.gvy b/.jenkins/mpi_compiler_checks.gvy index 6e618c718..ad1a04e97 100644 --- a/.jenkins/mpi_compiler_checks.gvy +++ b/.jenkins/mpi_compiler_checks.gvy @@ -97,6 +97,20 @@ pipeline { echo ' Build completed ' } } + stage (' Test '){ + steps { + echo ' Running MPI tests with ctest ' + sh """ + module load cmake/3.30.8 + module load boost/1.85.0 + module load ${GNU_COMPILER_MODULE} + module load ${MPI_MODULE} + cd build_mpi_${GNU_COMPILER_NAME}_${MPI_NAME}_${CMAKE_HOST_NAME}_${SIMD_NAME}_${env.BUILD_TAG} + ctest -VV + """ + echo ' Testing completed ' + } + } } post { always { diff --git a/CMakePresets.json b/CMakePresets.json index 6f58ba421..87fd5316d 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -170,10 +170,40 @@ "SPECFEM_BENCHMARKS_BUILD_DIR": "${sourceDir}/benchmarks/build/debug-nosimd" } }, + { + "name": "debug-mpi", + "displayName": "MPI Debug -- EXPERIMENTAL", + "binaryDir": "build/debug-mpi", + "environment": { + "CC": "mpicc", + "CXX": "mpicxx", + "FC": "mpifort" + }, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_INSTALL_PREFIX": "${sourceDir}/bin/debug-mpi", + "Kokkos_ARCH_NATIVE": "ON", + "Kokkos_ENABLE_AGGRESSIVE_VECTORIZATION": "OFF", + "Kokkos_ENABLE_ATOMICS_BYPASS": "OFF", + "Kokkos_ENABLE_DEBUG": "ON", + "Kokkos_ENABLE_DEBUG_BOUNDS_CHECK": "ON", + "SPECFEM_INSTALL": "ON", + "SPECFEM_ENABLE_MPI": "ON", + "SPECFEM_BUILD_TESTS": "ON", + "SPECFEM_ENABLE_SIMD": "ON", + "SPECFEM_BUILD_BENCHMARKS": "ON", + "SPECFEM_BENCHMARKS_BUILD_DIR": "${sourceDir}/benchmarks/build/debug-mpi" + } + }, { "name": "release-mpi", "displayName": "MPI Release -- EXPERIMENTAL", "binaryDir": "build/release-mpi", + "environment": { + "CC": "mpicc", + "CXX": "mpicxx", + "FC": "mpifort" + }, "cacheVariables": { "CMAKE_BUILD_TYPE": "Release", "CMAKE_INSTALL_PREFIX": "${sourceDir}/bin/release-mpi", @@ -272,6 +302,13 @@ "all" ] }, + { + "name": "debug-mpi", + "configurePreset": "debug-mpi", + "targets": [ + "all" + ] + }, { "name": "release-mpi", "configurePreset": "release-mpi", diff --git a/core/specfem.cpp b/core/specfem.cpp index 02a30a4bc..9f664e09c 100644 --- a/core/specfem.cpp +++ b/core/specfem.cpp @@ -61,41 +61,58 @@ int run_simulation(const std::string &dimension, int argc, char **argv, try { specfem::program::Context context(argc, argv); - const YAML::Node parameter_dict = YAML::LoadFile(opts.parameters_file); - - // Build LoggerOptions from CLI values - std::optional log_file_opt; - std::optional per_rank_opt; - std::optional auto_flush_opt; - std::optional log_level_opt; - - if (flags.log_file_set) - log_file_opt = opts.log_file; - if (flags.per_rank_set) - per_rank_opt = opts.log_per_rank; - if (flags.auto_flush_set) - auto_flush_opt = opts.log_auto_flush; - if (flags.log_level_set) - log_level_opt = opts.log_level; - - auto logger_options = specfem::logger::LoggerOptions::from_values( - std::move(log_file_opt), per_rank_opt, auto_flush_opt, - std::move(log_level_opt)); - specfem::Logger::apply_options(logger_options); - - // Set log file if specified in parameters and not already set by CLI - if (parameter_dict["parameters"]["log-file"]) { - const std::string log_file = - parameter_dict["parameters"]["log-file"].as(); - specfem::Logger::set_log_file(log_file); + // Only run simulation if current rank is part of the subset communicator + // (i.e. not excluded by user-defined nprocs) + // For simulations that use the entire world communicator, this check will + // pass for all ranks. + if (specfem::MPI::communicator() != MPI_COMM_NULL) { + + const YAML::Node parameter_dict = YAML::LoadFile(opts.parameters_file); + + // Build LoggerOptions from CLI values + std::optional log_file_opt; + std::optional per_rank_opt; + std::optional auto_flush_opt; + std::optional log_level_opt; + + if (flags.log_file_set) + log_file_opt = opts.log_file; + if (flags.per_rank_set) + per_rank_opt = opts.log_per_rank; + if (flags.auto_flush_set) + auto_flush_opt = opts.log_auto_flush; + if (flags.log_level_set) + log_level_opt = opts.log_level; + + auto logger_options = specfem::logger::LoggerOptions::from_values( + std::move(log_file_opt), per_rank_opt, auto_flush_opt, + std::move(log_level_opt)); + specfem::Logger::apply_options(logger_options); + + // Set log file if specified in parameters and not already set by CLI + if (parameter_dict["parameters"]["log-file"]) { + const std::string log_file = + parameter_dict["parameters"]["log-file"].as(); + specfem::Logger::set_log_file(log_file); + } + + const auto success = specfem::program::execute(dimension, parameter_dict); + + if (!success) { + std::cerr << "Execution failed" << std::endl; + result = 1; + } + } else { + result = 0; } - const auto success = specfem::program::execute(dimension, parameter_dict); + SPECFEM_MPI_SAFECALL(MPI_Barrier(MPI_COMM_WORLD)); - if (!success) { - std::cerr << "Execution failed" << std::endl; - result = 1; - } + // Ensure all ranks have reached this point, reduce the result across ranks + int result_reduced = result; + SPECFEM_MPI_SAFECALL(MPI_Allreduce(&result, &result_reduced, 1, MPI_INT, + MPI_MAX, MPI_COMM_WORLD)); + result = result_reduced; } catch (const std::exception &e) { std::cerr << "Error during execution: " << e.what() << std::endl; @@ -137,15 +154,32 @@ int run_qplots(int argc, char **argv, const Qoptions &opts) { try { specfem::program::Context context(argc, argv); - const auto success = specfem::program::qplots( - opts.Q, opts.minfreq, opts.maxfreq, opts.min_plot_freq, - opts.max_plot_freq, opts.output_dir); - - if (!success) { - std::cerr << "Q plot generation failed" << std::endl; - result = 1; + // Only run Q plot generation if current rank is part of the subset + // communicator (i.e. not excluded by user-defined nprocs). For simulations + // that use the entire world communicator, this check will pass for all + // ranks. + if (specfem::MPI::communicator() != MPI_COMM_NULL) { + + const auto success = specfem::program::qplots( + opts.Q, opts.minfreq, opts.maxfreq, opts.min_plot_freq, + opts.max_plot_freq, opts.output_dir); + + if (!success) { + std::cerr << "Q plot generation failed" << std::endl; + result = 1; + } + } else { + result = 0; } + SPECFEM_MPI_SAFECALL(MPI_Barrier(MPI_COMM_WORLD)); + + // Ensure all ranks have reached this point, reduce the result across ranks + int result_reduced = result; + SPECFEM_MPI_SAFECALL(MPI_Allreduce(&result, &result_reduced, 1, MPI_INT, + MPI_MAX, MPI_COMM_WORLD)); + result = result_reduced; + } catch (const std::exception &e) { std::cerr << "Error during Q plot generation: " << e.what() << std::endl; result = 1; diff --git a/core/specfem/io/mesh/impl/fortran/dim2/mesh.cpp b/core/specfem/io/mesh/impl/fortran/dim2/mesh.cpp index 082699ebb..93a42d57c 100644 --- a/core/specfem/io/mesh/impl/fortran/dim2/mesh.cpp +++ b/core/specfem/io/mesh/impl/fortran/dim2/mesh.cpp @@ -77,15 +77,16 @@ specfem::io::read_2d_mesh( int nspec_all = mesh.parameters.nspec; SPECFEM_MPI_SAFECALL(MPI_Reduce(&mesh.parameters.nspec, &nspec_all, 1, - MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD)); + MPI_INT, MPI_SUM, 0, + specfem::MPI::communicator())); int nelem_acforcing_all = mesh.parameters.nelem_acforcing; SPECFEM_MPI_SAFECALL(MPI_Reduce(&mesh.parameters.nelem_acforcing, &nelem_acforcing_all, 1, MPI_INT, MPI_SUM, 0, - MPI_COMM_WORLD)); + specfem::MPI::communicator())); int nelem_acoustic_surface_all = mesh.parameters.nelem_acoustic_surface; SPECFEM_MPI_SAFECALL(MPI_Reduce(&mesh.parameters.nelem_acoustic_surface, &nelem_acoustic_surface_all, 1, MPI_INT, - MPI_SUM, 0, MPI_COMM_WORLD)); + MPI_SUM, 0, specfem::MPI::communicator())); try { auto [n_sls, attenuation_f0_reference, read_velocities_at_f0] = diff --git a/core/specfem/io/mesh/impl/fortran/dim3/mesh.cpp b/core/specfem/io/mesh/impl/fortran/dim3/mesh.cpp index c85c6a56c..d33961b36 100644 --- a/core/specfem/io/mesh/impl/fortran/dim3/mesh.cpp +++ b/core/specfem/io/mesh/impl/fortran/dim3/mesh.cpp @@ -5,7 +5,6 @@ #include "specfem/io/mesh/impl/fortran/dim3/read_boundaries.hpp" #include "specfem/io/mesh/impl/fortran/dim3/read_control_nodes.hpp" #include "specfem/io/mesh/impl/fortran/dim3/read_materials.hpp" -#include "specfem/io/mesh/impl/fortran/dim3/read_mpi_interfaces.hpp" #include "specfem/io/mesh/impl/fortran/dim3/read_pml_boundaries.hpp" #include #include @@ -53,10 +52,6 @@ specfem::io::read_3d_mesh(const std::string &database_file) { // TODO (Rohit: PML_BOUNDARIES): Add support for PML boundaries specfem::io::mesh::impl::fortran::dim3::read_pml_boundaries(param_stream); - // MPI interfaces are not supported yet - // TODO (Rohit: MPI_INTERFACES): Add support for MPI interfaces - specfem::io::mesh::impl::fortran::dim3::read_mpi_interfaces(param_stream); - // Read adjacency information mesh.adjacency_graph = specfem::io::mesh::impl::fortran::dim3::read_adjacency_graph(param_stream, diff --git a/core/specfem/io/mesh/impl/fortran/dim3/read_mpi_interfaces.cpp b/core/specfem/io/mesh/impl/fortran/dim3/read_mpi_interfaces.cpp deleted file mode 100644 index 4381cdd20..000000000 --- a/core/specfem/io/mesh/impl/fortran/dim3/read_mpi_interfaces.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "specfem/io/mesh/impl/fortran/dim3/read_mpi_interfaces.hpp" -#include "specfem/io/fortranio/interface.hpp" -#include - -void specfem::io::mesh::impl::fortran::dim3::read_mpi_interfaces( - std::ifstream &stream) { - - int num_interfaces, max_elements_per_interface; - - specfem::io::fortran_read_line(stream, &num_interfaces, - &max_elements_per_interface); - - // TODO (Rohit: MPI_INTERFACES): Add support for MPI interfaces - if (num_interfaces != 0 && max_elements_per_interface != 0) { - throw std::runtime_error( - "MPI interfaces are not supported yet for 3D simulations."); - } -} diff --git a/core/specfem/io/mesh/impl/fortran/dim3/read_mpi_interfaces.hpp b/core/specfem/io/mesh/impl/fortran/dim3/read_mpi_interfaces.hpp deleted file mode 100644 index 2e82b78ae..000000000 --- a/core/specfem/io/mesh/impl/fortran/dim3/read_mpi_interfaces.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include - -namespace specfem::io::mesh::impl::fortran::dim3 { - -/** - * @brief Read MPI interface data from MESHFEM3D database file - * - * Reads MPI interface information from a binary MESHFEM3D database file - * for parallel domain decomposition in 3D spectral element simulations. - * MPI interfaces define the communication patterns between neighboring - * MPI processes in distributed memory parallel computations. - * - * @param stream Input file stream positioned at the MPI interfaces section of - * the MESHFEM3D database - * - * @throws std::ios_base::failure If stream operations encounter errors - * - * @note **Current Status**: This function is a placeholder implementation that - * validates the presence of MPI interface data but does not process it. Full - * MPI interface support for 3D simulations is planned for future development. - * - * @warning This function will throw a runtime error if the database contains - * non-zero MPI interface data, indicating unsupported functionality. - * - * @code - * // Example usage in MESHFEM3D database reading - * std::ifstream database_stream("proc000000_mesh.bin", std::ios::binary); - * - * // Read other mesh components first... - * - * // Read MPI interfaces (currently validates only) - * specfem::io::mesh::impl::fortran::dim3::read_mpi_interfaces(database_stream); - * @endcode - * - * @see specfem::io::mesh::impl::fortran::dim3::mesh - * @todo Implement full MPI interface processing for parallel 3D simulations - */ -void read_mpi_interfaces(std::ifstream &stream); - -} // namespace specfem::io::mesh::impl::fortran::dim3 diff --git a/core/specfem/mpi/mpi.cpp b/core/specfem/mpi/mpi.cpp index 12c3c16ff..a18383243 100644 --- a/core/specfem/mpi/mpi.cpp +++ b/core/specfem/mpi/mpi.cpp @@ -5,6 +5,7 @@ namespace specfem { // Static storage for MPI rank and size (-1 indicates uninitialized) int MPI::rank_ = -1; int MPI::size_ = -1; +MPI_Comm MPI::comm_ = MPI_COMM_WORLD; void MPI::initialize(int *argc, char ***argv) { #ifdef SPECFEM_ENABLE_MPI @@ -15,12 +16,59 @@ void MPI::initialize(int *argc, char ***argv) { MPI_Init(argc, argv); } - MPI_Comm_size(MPI_COMM_WORLD, &size_); - MPI_Comm_rank(MPI_COMM_WORLD, &rank_); + comm_ = MPI_COMM_WORLD; + + MPI_Comm_size(comm_, &size_); + MPI_Comm_rank(comm_, &rank_); +#else + // Non-MPI build: single process + rank_ = 0; + size_ = 1; + comm_ = MPI_COMM_WORLD; // Dummy value for non-MPI build +#endif +} + +void MPI::initialize(int *argc, char ***argv, int nprocs) { +#ifdef SPECFEM_ENABLE_MPI + int initialized; + MPI_Initialized(&initialized); + + if (!initialized) { + MPI_Init(argc, argv); + // Check that requested nprocs does not exceed world size + int world_size; + MPI_Comm_size(MPI_COMM_WORLD, &world_size); + if (nprocs > world_size) { + std::cerr << "Error: Requested nprocs (" << nprocs + << ") exceeds available world size (" << world_size << ")." + << std::endl; + MPI_Abort(MPI_COMM_WORLD, 1); + } + + // Get rank from world communicator before using in split + int world_rank; + MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); + + // Only use the first nprocs ranks in the new communicator + int color = (world_rank < nprocs) ? 1 : MPI_UNDEFINED; + MPI_Comm_split(MPI_COMM_WORLD, color, world_rank, &comm_); + } + + // Handle ranks excluded by subset communicator + if (comm_ == MPI_COMM_NULL) { + // Rank is not part of the subset communicator + rank_ = -1; + size_ = -1; + return; + } + + MPI_Comm_size(comm_, &size_); + MPI_Comm_rank(comm_, &rank_); #else // Non-MPI build: single process rank_ = 0; size_ = 1; + comm_ = MPI_COMM_WORLD; // Dummy value for non-MPI build #endif } @@ -31,6 +79,11 @@ void MPI::finalize() { // Only finalize if not already finalized externally if (!finalized) { + // Free custom communicator if it was created and is valid + if (comm_ != MPI_COMM_WORLD && comm_ != MPI_COMM_NULL) { + MPI_Comm_free(&comm_); + comm_ = MPI_COMM_NULL; + } MPI_Finalize(); } #endif diff --git a/core/specfem/mpi/mpi.hpp b/core/specfem/mpi/mpi.hpp index 7c22b21c1..7849e3018 100644 --- a/core/specfem/mpi/mpi.hpp +++ b/core/specfem/mpi/mpi.hpp @@ -12,6 +12,13 @@ #include #endif +// Non-MPI shims: define MPI_Comm as a simple type at global scope +#ifndef SPECFEM_ENABLE_MPI +using MPI_Comm = int; +constexpr MPI_Comm MPI_COMM_WORLD = 0; +constexpr MPI_Comm MPI_COMM_NULL = -1; +#endif + namespace specfem { // Forward declaration @@ -51,6 +58,7 @@ class MPI { private: static int rank_; ///< Current MPI rank (-1 if not initialized) static int size_; ///< Total number of MPI processes (-1 if not initialized) + static MPI_Comm comm_; ///< MPI communicator (MPI_COMM_WORLD or user-defined) public: /** @@ -61,7 +69,7 @@ class MPI { static void sync() { check_context(); #ifdef SPECFEM_ENABLE_MPI - MPI_Barrier(MPI_COMM_WORLD); + MPI_Barrier(comm_); #endif } @@ -94,6 +102,21 @@ class MPI { return size_; } + /** + * @brief Get active communicator for simulations + * + * Returns the active communicator, which may be a subset communicator + * (if initialized with nprocs) or MPI_COMM_WORLD (default initialization). + * For excluded ranks in subset initialization, returns MPI_COMM_NULL. + * + * @return MPI_Comm The active communicator for simulations + * @throws Exits with error code 1 if called outside Context scope + */ + static MPI_Comm communicator() { + check_context(); + return comm_; + } + /** * @brief Check if current process is the main process (rank 0) * @@ -173,6 +196,21 @@ class MPI { */ static void initialize(int *argc, char ***argv); + /** + * @brief Initialize MPI with user-defined processes within world communicator + * + * Creates a subset MPI communicator containing only the first nprocs ranks. + * Ranks >= nprocs receive MPI_COMM_NULL and are excluded from simulation. + * + * @param argc Pointer to argument count + * @param argv Pointer to argument vector + * @param nprocs Number of processes to include in the new communicator (must + * be + * <= world size) + * @throws Exits with error code 1 if nprocs > world size + */ + static void initialize(int *argc, char ***argv, int nprocs); + /** * @brief Finalize MPI and reset rank/size to -1 * diff --git a/core/specfem/program/context.cpp b/core/specfem/program/context.cpp index b8388072d..05012f4b6 100644 --- a/core/specfem/program/context.cpp +++ b/core/specfem/program/context.cpp @@ -15,6 +15,14 @@ Context::Context(int argc, char *argv[]) specfem::Logger::initialize(this); } +Context::Context(int argc, char *argv[], int nprocs) + : kokkos_guard_(std::make_unique(argc, argv)) { + // Initialize static MPI wrapper with user-defined processes + specfem::MPI::initialize(&argc, &argv, nprocs); + // Initialize Logger + specfem::Logger::initialize(this); +} + Context::Context(const std::vector &args) { int argc; char **argv; diff --git a/core/specfem/program/context.hpp b/core/specfem/program/context.hpp index 984c1cb9d..0eb57d72d 100644 --- a/core/specfem/program/context.hpp +++ b/core/specfem/program/context.hpp @@ -36,6 +36,14 @@ class Context { */ Context(int argc, char *argv[]); + /** + * @brief Initialize context with subset of MPI processes + * @param argc Argument count + * @param argv Argument vector + * @param nprocs Number of processes to use (creates subset communicator) + */ + Context(int argc, char *argv[], int nprocs); + /** * @brief Initialize context from argument vector * @param args Vector of arguments diff --git a/fortran/meshfem3d/meshfem3D/save_databases.F90 b/fortran/meshfem3d/meshfem3D/save_databases.F90 index f5c33e8f2..c5b5e6cce 100644 --- a/fortran/meshfem3d/meshfem3D/save_databases.F90 +++ b/fortran/meshfem3d/meshfem3D/save_databases.F90 @@ -25,11 +25,11 @@ ! !===================================================================== - subroutine save_databases(nspec,nglob, & - iMPIcut_xi,iMPIcut_eta, & - nodes_coords,ispec_material_id, & - nspec2D_xmin,nspec2D_xmax,nspec2D_ymin,nspec2D_ymax, & - ibelm_xmin,ibelm_xmax,ibelm_ymin,ibelm_ymax,ibelm_bottom,ibelm_top) +subroutine save_databases(nspec,nglob, & + iMPIcut_xi,iMPIcut_eta, & + nodes_coords,ispec_material_id, & + nspec2D_xmin,nspec2D_xmax,nspec2D_ymin,nspec2D_ymax, & + ibelm_xmin,ibelm_xmax,ibelm_ymin,ibelm_ymax,ibelm_bottom,ibelm_top) use constants, only: MAX_STRING_LEN,IDOMAIN_ACOUSTIC,IDOMAIN_ELASTIC,IDOMAIN_POROELASTIC, & NDIM,IMAIN,IIN_DB,myrank,NGLLZ,NGLLY,NGLLX @@ -176,7 +176,7 @@ subroutine save_databases(nspec,nglob, & ! opens database file open(unit=IIN_database,file=trim(database_file), & - status='unknown',action='write',form='unformatted',iostat=ier) + status='unknown',action='write',form='unformatted',iostat=ier) if (ier /= 0) then print *,'Error opening Database file: ',trim(database_file) stop 'error opening Database file' @@ -205,7 +205,7 @@ subroutine save_databases(nspec,nglob, & ! pad dummy zeros to fill up 17 entries matpropl(:) = 0.d0 select case(domain_id) - case (IDOMAIN_ACOUSTIC,IDOMAIN_ELASTIC) + case (IDOMAIN_ACOUSTIC,IDOMAIN_ELASTIC) ! material properties format: !#(1)rho #(2)vp #(3)vs #(4)Q_Kappa #(5)Q_mu #(6)anisotropy_flag #(7)domain_id #(8)mat_id ! @@ -214,7 +214,7 @@ subroutine save_databases(nspec,nglob, & ! ! skipping mat_id, not needed matpropl(1:7) = material_properties(i,1:7) - case (IDOMAIN_POROELASTIC) + case (IDOMAIN_POROELASTIC) ! material properties format: !#(1)rho_s #(2)rho_f #(3)phi #(4)tort #(5)eta #(6)0 #(7)domain_id #(8)mat_id ! .. #(9)kxx #(10)kxy #(11)kxz #(12)kyy #(13)kyz #(14)kzz #(15)kappa_s #(16)kappa_f #(17)kappa_fr #(18)mu_fr @@ -247,11 +247,11 @@ subroutine save_databases(nspec,nglob, & undef_mat_prop(4,1) = material_properties_undef(i,3) ! tomo-filename: tomography_model**.xyz ! checks consistency between domain-name and domain_id select case (domain_id) - case (IDOMAIN_ACOUSTIC) + case (IDOMAIN_ACOUSTIC) if (trim(undef_mat_prop(3,1)) /= 'acoustic') stop 'Error in undef_mat_prop acoustic domain' - case (IDOMAIN_ELASTIC) + case (IDOMAIN_ELASTIC) if (trim(undef_mat_prop(3,1)) /= 'elastic') stop 'Error in undef_mat_prop elastic domain' - case (IDOMAIN_POROELASTIC) + case (IDOMAIN_POROELASTIC) if (trim(undef_mat_prop(3,1)) /= 'poroelastic') stop 'Error in undef_mat_prop poroelastic domain' end select ! default name if none given @@ -430,177 +430,14 @@ subroutine save_databases(nspec,nglob, & write(IIN_database) nspec_CPML_total if (nspec_CPML_total > 0) then - write(IIN_database) nspec_CPML - - do ispec_CPML = 1,nspec_CPML - write(IIN_database) CPML_to_spec(ispec_CPML), CPML_regions(ispec_CPML) - enddo - do ispec = 1,nspec - write(IIN_database) is_CPML(ispec) - enddo - endif - - ! MPI Interfaces - ! - ! note: check with routine write_interfaces_database() to produce identical output - if (NPROC_XI > 1 .or. NPROC_ETA > 1) then - ! determines number of MPI interfaces for each slice - nb_interfaces = 4 - interfaces(W:N) = .true. - interfaces(NW:SW) = .false. - - ! slices at model boundaries - if (iproc_xi_current == 0) then - nb_interfaces = nb_interfaces -1 - interfaces(W) = .false. - endif - if (iproc_xi_current == NPROC_XI-1) then - nb_interfaces = nb_interfaces -1 - interfaces(E) = .false. - endif - if (iproc_eta_current == 0) then - nb_interfaces = nb_interfaces -1 - interfaces(S) = .false. - endif - if (iproc_eta_current == NPROC_ETA-1) then - nb_interfaces = nb_interfaces -1 - interfaces(N) = .false. - endif - - ! slices in middle of model - if ((interfaces(W) .eqv. .true.) .and. (interfaces(N) .eqv. .true.)) then - interfaces(NW) = .true. - nb_interfaces = nb_interfaces +1 - endif - if ((interfaces(N) .eqv. .true.) .and. (interfaces(E) .eqv. .true.)) then - interfaces(NE) = .true. - nb_interfaces = nb_interfaces +1 - endif - if ((interfaces(E) .eqv. .true.) .and. (interfaces(S) .eqv. .true.)) then - interfaces(SE) = .true. - nb_interfaces = nb_interfaces +1 - endif - if ((interfaces(W) .eqv. .true.) .and. (interfaces(S) .eqv. .true.)) then - interfaces(SW) = .true. - nb_interfaces = nb_interfaces +1 - endif - - nspec_interface(:) = 0 - if (interfaces(W)) nspec_interface(W) = count(iMPIcut_xi(1,:) .eqv. .true.) - if (interfaces(E)) nspec_interface(E) = count(iMPIcut_xi(2,:) .eqv. .true.) - if (interfaces(S)) nspec_interface(S) = count(iMPIcut_eta(1,:) .eqv. .true.) - if (interfaces(N)) nspec_interface(N) = count(iMPIcut_eta(2,:) .eqv. .true.) - if (interfaces(NW)) nspec_interface(NW) = count((iMPIcut_xi(1,:) .eqv. .true.) .and. (iMPIcut_eta(2,:) .eqv. .true.)) - if (interfaces(NE)) nspec_interface(NE) = count((iMPIcut_xi(2,:) .eqv. .true.) .and. (iMPIcut_eta(2,:) .eqv. .true.)) - if (interfaces(SE)) nspec_interface(SE) = count((iMPIcut_xi(2,:) .eqv. .true.) .and. (iMPIcut_eta(1,:) .eqv. .true.)) - if (interfaces(SW)) nspec_interface(SW) = count((iMPIcut_xi(1,:) .eqv. .true.) .and. (iMPIcut_eta(1,:) .eqv. .true.)) - - nspec_interfaces_max = maxval(nspec_interface) - - ! format: #number_of_MPI_interfaces #maximum_number_of_elements_on_each_interface - write(IIN_database) nb_interfaces,nspec_interfaces_max - - ! face elements - if (interfaces(W)) then - ! format: #process_interface_id #number_of_elements_on_interface - write(IIN_database) addressing(iproc_xi_current-1,iproc_eta_current),nspec_interface(W) - do ispec = 1,nspec - if (iMPIcut_xi(1,ispec)) then - ! format: #(1)spectral_element_id #(2)interface_type #(3)node_id1 #(4)node_id2 #(5).. #(6).. - ! note: face outputs 4 corner points - write(IIN_database) ispec,4,ibool(1,1,1,ispec),ibool(1,NGLLY_M,1,ispec), & - ibool(1,1,NGLLZ_M,ispec),ibool(1,NGLLY_M,NGLLZ_M,ispec) - endif - enddo - endif + write(IIN_database) nspec_CPML - if (interfaces(E)) then - ! format: #process_interface_id #number_of_elements_on_interface - write(IIN_database) addressing(iproc_xi_current+1,iproc_eta_current),nspec_interface(E) - do ispec = 1,nspec - if (iMPIcut_xi(2,ispec)) then - ! format: #(1)spectral_element_id #(2)interface_type #(3)node_id1 #(4)node_id2 #(5).. #(6).. - write(IIN_database) ispec,4,ibool(NGLLX_M,1,1,ispec),ibool(NGLLX_M,NGLLY_M,1,ispec), & - ibool(NGLLX_M,1,NGLLZ_M,ispec),ibool(NGLLX_M,NGLLY_M,NGLLZ_M,ispec) - endif - enddo - endif - - if (interfaces(S)) then - ! format: #process_interface_id #number_of_elements_on_interface - write(IIN_database) addressing(iproc_xi_current,iproc_eta_current-1),nspec_interface(S) - do ispec = 1,nspec - if (iMPIcut_eta(1,ispec)) then - ! format: #(1)spectral_element_id #(2)interface_type #(3)node_id1 #(4)node_id2 #(5).. #(6).. - write(IIN_database) ispec,4,ibool(1,1,1,ispec),ibool(NGLLX_M,1,1,ispec), & - ibool(1,1,NGLLZ_M,ispec),ibool(NGLLX_M,1,NGLLZ_M,ispec) - endif - enddo - endif - - if (interfaces(N)) then - ! format: #process_interface_id #number_of_elements_on_interface - write(IIN_database) addressing(iproc_xi_current,iproc_eta_current+1),nspec_interface(N) - do ispec = 1,nspec - if (iMPIcut_eta(2,ispec)) then - ! format: #(1)spectral_element_id #(2)interface_type #(3)node_id1 #(4)node_id2 #(5).. #(6).. - write(IIN_database) ispec,4,ibool(NGLLX_M,NGLLY_M,1,ispec),ibool(1,NGLLY_M,1,ispec), & - ibool(NGLLX_M,NGLLY_M,NGLLZ_M,ispec),ibool(1,NGLLY_M,NGLLZ_M,ispec) - endif - enddo - endif - - ! edge elements - if (interfaces(NW)) then - ! format: #process_interface_id #number_of_elements_on_interface - write(IIN_database) addressing(iproc_xi_current-1,iproc_eta_current+1),nspec_interface(NW) - do ispec = 1,nspec - if ((iMPIcut_xi(1,ispec) .eqv. .true.) .and. (iMPIcut_eta(2,ispec) .eqv. .true.)) then - ! note: edge elements output 2 corners and 2 dummy values - ! format: #(1)spectral_element_id #(2)interface_type #(3)node_id1 #(4)node_id2 #(5).. #(6).. - write(IIN_database) ispec,2,ibool(1,NGLLY_M,1,ispec),ibool(1,NGLLY_M,NGLLZ_M,ispec),-1,-1 - endif - enddo - endif - - if (interfaces(NE)) then - ! format: #process_interface_id #number_of_elements_on_interface - write(IIN_database) addressing(iproc_xi_current+1,iproc_eta_current+1),nspec_interface(NE) - do ispec = 1,nspec - if ((iMPIcut_xi(2,ispec) .eqv. .true.) .and. (iMPIcut_eta(2,ispec) .eqv. .true.)) then - ! format: #(1)spectral_element_id #(2)interface_type #(3)node_id1 #(4)node_id2 #(5).. #(6).. - write(IIN_database) ispec,2,ibool(NGLLX_M,NGLLY_M,1,ispec),ibool(NGLLX_M,NGLLY_M,NGLLZ_M,ispec),-1,-1 - endif - enddo - endif - - if (interfaces(SE)) then - ! format: #process_interface_id #number_of_elements_on_interface - write(IIN_database) addressing(iproc_xi_current+1,iproc_eta_current-1),nspec_interface(SE) - do ispec = 1,nspec - if ((iMPIcut_xi(2,ispec) .eqv. .true.) .and. (iMPIcut_eta(1,ispec) .eqv. .true.)) then - ! format: #(1)spectral_element_id #(2)interface_type #(3)node_id1 #(4)node_id2 #(5).. #(6).. - write(IIN_database) ispec,2,ibool(NGLLX_M,1,1,ispec),ibool(NGLLX_M,1,NGLLZ_M,ispec),-1,-1 - endif - enddo - endif - - if (interfaces(SW)) then - ! format: #process_interface_id #number_of_elements_on_interface - write(IIN_database) addressing(iproc_xi_current-1,iproc_eta_current-1),nspec_interface(SW) - do ispec = 1,nspec - if ((iMPIcut_xi(1,ispec) .eqv. .true.) .and. (iMPIcut_eta(1,ispec) .eqv. .true.)) then - ! format: #(1)spectral_element_id #(2)interface_type #(3)node_id1 #(4)node_id2 #(5).. #(6).. - write(IIN_database) ispec,2,ibool(1,1,1,ispec),ibool(1,1,NGLLZ_M,ispec),-1,-1 - endif - enddo - endif - else - ! single process execution, no MPI boundaries - nb_interfaces = 0 - nspec_interfaces_max = 0 - ! format: #number_of_MPI_interfaces #maximum_number_of_elements_on_each_interface - write(IIN_database) nb_interfaces,nspec_interfaces_max + do ispec_CPML = 1,nspec_CPML + write(IIN_database) CPML_to_spec(ispec_CPML), CPML_regions(ispec_CPML) + enddo + do ispec = 1,nspec + write(IIN_database) is_CPML(ispec) + enddo endif total_nadj_element = 0 @@ -650,12 +487,11 @@ subroutine save_databases(nspec,nglob, & ! matched to anchor_local via coordinates ! These anchor points remove rotational ambiguity on MPI surfaces by ! establishing a canonical reference frame through corner correspondences. - write(IIN_database) num_mpi_adjacencies do i = 1, num_mpi_adjacencies write(IIN_database) mpi_adjacency(i, 1), mpi_adjacency(i, 2), & - mpi_adjacency(i, 3), mpi_adjacency(i, 4), & - mpi_adjacency(i, 5), mpi_adjacency(i, 6), & - mpi_adjacency(i, 7) + mpi_adjacency(i, 3), mpi_adjacency(i, 4), & + mpi_adjacency(i, 5), mpi_adjacency(i, 6), & + mpi_adjacency(i, 7) index = index + 1 enddo @@ -678,17 +514,17 @@ subroutine save_databases(nspec,nglob, & !! VM VM add outputs as CUBIT call save_output_mesh_files_as_cubit(nspec,nglob, & - nodes_coords, ispec_material_id, & - nspec2D_xmin,nspec2D_xmax,nspec2D_ymin,nspec2D_ymax, & - ibelm_xmin,ibelm_xmax,ibelm_ymin,ibelm_ymax,ibelm_bottom,ibelm_top, & - nspec_CPML_total) + nodes_coords, ispec_material_id, & + nspec2D_xmin,nspec2D_xmax,nspec2D_ymin,nspec2D_ymax, & + ibelm_xmin,ibelm_xmax,ibelm_ymin,ibelm_ymax,ibelm_bottom,ibelm_top, & + nspec_CPML_total) ! output for AxiSEM coupling if (COUPLE_WITH_INJECTION_TECHNIQUE) then call save_output_mesh_files_for_coupled_model(nspec, & - nspec2D_xmin,nspec2D_xmax,nspec2D_ymin,nspec2D_ymax, & - ibelm_xmin,ibelm_xmax,ibelm_ymin,ibelm_ymax,ibelm_bottom,ibelm_top, & - xstore,ystore,zstore) + nspec2D_xmin,nspec2D_xmax,nspec2D_ymin,nspec2D_ymax, & + ibelm_xmin,ibelm_xmax,ibelm_ymin,ibelm_ymax,ibelm_bottom,ibelm_top, & + xstore,ystore,zstore) endif endif @@ -705,16 +541,16 @@ subroutine save_databases(nspec,nglob, & call flush_IMAIN() endif - end subroutine save_databases +end subroutine save_databases !--------------------------------------------------------------------------------------------------------------- - !! VM VM add subroutine to save meshes in case of a single MPI process - subroutine save_output_mesh_files_as_cubit(nspec,nglob, & - nodes_coords, ispec_material_id, & - nspec2D_xmin,nspec2D_xmax,nspec2D_ymin,nspec2D_ymax, & - ibelm_xmin,ibelm_xmax,ibelm_ymin,ibelm_ymax,ibelm_bottom,ibelm_top, & - nspec_CPML_total) + !! VM VM add subroutine to save meshes in case of a single MPI process +subroutine save_output_mesh_files_as_cubit(nspec,nglob, & + nodes_coords, ispec_material_id, & + nspec2D_xmin,nspec2D_xmax,nspec2D_ymin,nspec2D_ymax, & + ibelm_xmin,ibelm_xmax,ibelm_ymin,ibelm_ymax,ibelm_bottom,ibelm_top, & + nspec_CPML_total) use constants, only: NDIM,IMAIN,myrank,IIN_DB use constants_meshfem, only: NGLLX_M,NGLLY_M,NGLLZ_M @@ -859,9 +695,9 @@ subroutine save_output_mesh_files_as_cubit(nspec,nglob, & if (mask_iglob(iglob)) then ! format: #iglob #x #y #z(corrected by z_bottom) write(IIN_database,'(i14,3x,3(f20.5,1x))') iglob_to_nodeid(iglob), & - nodes_coords(iglob,1), & - nodes_coords(iglob,2), & - nodes_coords(iglob,3)-z_bottom + nodes_coords(iglob,1), & + nodes_coords(iglob,2), & + nodes_coords(iglob,3)-z_bottom endif enddo close(IIN_database) @@ -872,14 +708,14 @@ subroutine save_output_mesh_files_as_cubit(nspec,nglob, & ! corner points ! format: #ispec #iglob1 #iglob2 #iglob3 #iglob4 #iglob5 #iglob6 #iglob7 #iglob8 write(IIN_database,'(9i15)') ispec, & - iglob_to_nodeid(ibool(1,1,1,ispec)), & - iglob_to_nodeid(ibool(NGLLZ_M,1,1,ispec)), & - iglob_to_nodeid(ibool(NGLLZ_M,NGLLZ_M,1,ispec)), & - iglob_to_nodeid(ibool(1,NGLLZ_M,1,ispec)), & - iglob_to_nodeid(ibool(1,1,NGLLZ_M,ispec)), & - iglob_to_nodeid(ibool(NGLLZ_M,1,NGLLZ_M,ispec)), & - iglob_to_nodeid(ibool(NGLLZ_M,NGLLZ_M,NGLLZ_M,ispec)), & - iglob_to_nodeid(ibool(1,NGLLZ_M,NGLLZ_M,ispec)) + iglob_to_nodeid(ibool(1,1,1,ispec)), & + iglob_to_nodeid(ibool(NGLLZ_M,1,1,ispec)), & + iglob_to_nodeid(ibool(NGLLZ_M,NGLLZ_M,1,ispec)), & + iglob_to_nodeid(ibool(1,NGLLZ_M,1,ispec)), & + iglob_to_nodeid(ibool(1,1,NGLLZ_M,ispec)), & + iglob_to_nodeid(ibool(NGLLZ_M,1,NGLLZ_M,ispec)), & + iglob_to_nodeid(ibool(NGLLZ_M,NGLLZ_M,NGLLZ_M,ispec)), & + iglob_to_nodeid(ibool(1,NGLLZ_M,NGLLZ_M,ispec)) enddo close(IIN_database) @@ -892,10 +728,10 @@ subroutine save_output_mesh_files_as_cubit(nspec,nglob, & do i = 1,nspec2D_xmin ! format: #boundary_face_id #iglob1 #iglob2 #iglob3 #iglob4 write(IIN_database,'(5(i10,1x))') ibelm_xmin(i), & - iglob_to_nodeid(ibool(1,1,1,ibelm_xmin(i))), & - iglob_to_nodeid(ibool(1,NGLLY_M,1,ibelm_xmin(i))), & - iglob_to_nodeid(ibool(1,1,NGLLZ_M,ibelm_xmin(i))), & - iglob_to_nodeid(ibool(1,NGLLY_M,NGLLZ_M,ibelm_xmin(i))) + iglob_to_nodeid(ibool(1,1,1,ibelm_xmin(i))), & + iglob_to_nodeid(ibool(1,NGLLY_M,1,ibelm_xmin(i))), & + iglob_to_nodeid(ibool(1,1,NGLLZ_M,ibelm_xmin(i))), & + iglob_to_nodeid(ibool(1,NGLLY_M,NGLLZ_M,ibelm_xmin(i))) enddo close(IIN_database) @@ -904,10 +740,10 @@ subroutine save_output_mesh_files_as_cubit(nspec,nglob, & do i = 1,nspec2D_xmax ! format: #boundary_face_id #iglob1 #iglob2 #iglob3 #iglob4 write(IIN_database,'(5(i10,1x))') ibelm_xmax(i), & - iglob_to_nodeid(ibool(NGLLX_M,1,1,ibelm_xmax(i))), & - iglob_to_nodeid(ibool(NGLLX_M,NGLLY_M,1,ibelm_xmax(i))), & - iglob_to_nodeid(ibool(NGLLX_M,1,NGLLZ_M,ibelm_xmax(i))), & - iglob_to_nodeid(ibool(NGLLX_M,NGLLY_M,NGLLZ_M,ibelm_xmax(i))) + iglob_to_nodeid(ibool(NGLLX_M,1,1,ibelm_xmax(i))), & + iglob_to_nodeid(ibool(NGLLX_M,NGLLY_M,1,ibelm_xmax(i))), & + iglob_to_nodeid(ibool(NGLLX_M,1,NGLLZ_M,ibelm_xmax(i))), & + iglob_to_nodeid(ibool(NGLLX_M,NGLLY_M,NGLLZ_M,ibelm_xmax(i))) enddo close(IIN_database) @@ -916,10 +752,10 @@ subroutine save_output_mesh_files_as_cubit(nspec,nglob, & do i = 1,nspec2D_ymin ! format: #boundary_face_id #iglob1 #iglob2 #iglob3 #iglob4 write(IIN_database,'(5(i10,1x))') ibelm_ymin(i), & - iglob_to_nodeid(ibool(1,1,1,ibelm_ymin(i))), & - iglob_to_nodeid(ibool(NGLLX_M,1,1,ibelm_ymin(i))), & - iglob_to_nodeid(ibool(1,1,NGLLZ_M,ibelm_ymin(i))), & - iglob_to_nodeid(ibool(NGLLX_M,1,NGLLZ_M,ibelm_ymin(i))) + iglob_to_nodeid(ibool(1,1,1,ibelm_ymin(i))), & + iglob_to_nodeid(ibool(NGLLX_M,1,1,ibelm_ymin(i))), & + iglob_to_nodeid(ibool(1,1,NGLLZ_M,ibelm_ymin(i))), & + iglob_to_nodeid(ibool(NGLLX_M,1,NGLLZ_M,ibelm_ymin(i))) enddo close(IIN_database) @@ -928,10 +764,10 @@ subroutine save_output_mesh_files_as_cubit(nspec,nglob, & do i = 1,nspec2D_ymax ! format: #boundary_face_id #iglob1 #iglob2 #iglob3 #iglob4 write(IIN_database,'(5(i10,1x))') ibelm_ymax(i), & - iglob_to_nodeid(ibool(NGLLX_M,NGLLY_M,1,ibelm_ymax(i))), & - iglob_to_nodeid(ibool(1,NGLLY_M,1,ibelm_ymax(i))), & - iglob_to_nodeid(ibool(NGLLX_M,NGLLY_M,NGLLZ_M,ibelm_ymax(i))), & - iglob_to_nodeid(ibool(1,NGLLY_M,NGLLZ_M,ibelm_ymax(i))) + iglob_to_nodeid(ibool(NGLLX_M,NGLLY_M,1,ibelm_ymax(i))), & + iglob_to_nodeid(ibool(1,NGLLY_M,1,ibelm_ymax(i))), & + iglob_to_nodeid(ibool(NGLLX_M,NGLLY_M,NGLLZ_M,ibelm_ymax(i))), & + iglob_to_nodeid(ibool(1,NGLLY_M,NGLLZ_M,ibelm_ymax(i))) enddo @@ -940,10 +776,10 @@ subroutine save_output_mesh_files_as_cubit(nspec,nglob, & do i = 1,NSPEC2D_BOTTOM ! format: #boundary_face_id #iglob1 #iglob2 #iglob3 #iglob4 write(IIN_database,'(5(i10,1x))') ibelm_bottom(i), & - iglob_to_nodeid(ibool(1,1,1,ibelm_bottom(i))), & - iglob_to_nodeid(ibool(NGLLX_M,1,1,ibelm_bottom(i))), & - iglob_to_nodeid(ibool(NGLLX_M,NGLLY_M,1,ibelm_bottom(i))), & - iglob_to_nodeid(ibool(1,NGLLY_M,1,ibelm_bottom(i))) + iglob_to_nodeid(ibool(1,1,1,ibelm_bottom(i))), & + iglob_to_nodeid(ibool(NGLLX_M,1,1,ibelm_bottom(i))), & + iglob_to_nodeid(ibool(NGLLX_M,NGLLY_M,1,ibelm_bottom(i))), & + iglob_to_nodeid(ibool(1,NGLLY_M,1,ibelm_bottom(i))) enddo close(IIN_database) @@ -952,10 +788,10 @@ subroutine save_output_mesh_files_as_cubit(nspec,nglob, & do i = 1,NSPEC2D_TOP ! format: #boundary_face_id #iglob1 #iglob2 #iglob3 #iglob4 write(IIN_database,'(5(i10,1x))') ibelm_top(i), & - iglob_to_nodeid(ibool(1,1,NGLLZ_M,ibelm_top(i))), & - iglob_to_nodeid(ibool(NGLLX_M,1,NGLLZ_M,ibelm_top(i))), & - iglob_to_nodeid(ibool(NGLLX_M,NGLLY_M,NGLLZ_M,ibelm_top(i))), & - iglob_to_nodeid(ibool(1,NGLLY_M,NGLLZ_M,ibelm_top(i))) + iglob_to_nodeid(ibool(1,1,NGLLZ_M,ibelm_top(i))), & + iglob_to_nodeid(ibool(NGLLX_M,1,NGLLZ_M,ibelm_top(i))), & + iglob_to_nodeid(ibool(NGLLX_M,NGLLY_M,NGLLZ_M,ibelm_top(i))), & + iglob_to_nodeid(ibool(1,NGLLY_M,NGLLZ_M,ibelm_top(i))) enddo close(IIN_database) @@ -971,17 +807,17 @@ subroutine save_output_mesh_files_as_cubit(nspec,nglob, & deallocate(mask_iglob,iglob_to_nodeid) - end subroutine save_output_mesh_files_as_cubit +end subroutine save_output_mesh_files_as_cubit !--------------------------------------------------------------------------------------------------------------- - !! VM VM add subroutine to save meshes in case of a single MPI process - subroutine save_output_mesh_files_for_coupled_model(nspec, & - nspec2D_xmin,nspec2D_xmax,nspec2D_ymin,nspec2D_ymax, & - ibelm_xmin,ibelm_xmax,ibelm_ymin,ibelm_ymax,ibelm_bottom,ibelm_top, & - xgrid,ygrid,zgrid) + !! VM VM add subroutine to save meshes in case of a single MPI process +subroutine save_output_mesh_files_for_coupled_model(nspec, & + nspec2D_xmin,nspec2D_xmax,nspec2D_ymin,nspec2D_ymax, & + ibelm_xmin,ibelm_xmax,ibelm_ymin,ibelm_ymax,ibelm_bottom,ibelm_top, & + xgrid,ygrid,zgrid) use constants, only: NGLLX, NGLLY, NGLLZ, NDIM, ZERO, IMAIN, myrank, & INJECTION_TECHNIQUE_IS_AXISEM @@ -1028,7 +864,7 @@ subroutine save_output_mesh_files_for_coupled_model(nspec, & !! GLL points double precision, dimension(NGLLX,NGLLY,NGLLZ) :: longitud, latitud, radius double precision, dimension(NGLLX,NGLLY,NGLLZ) :: xstore, ystore, zstore - !! Element control points + !! Element control points double precision, dimension(NGNOD) :: xelm, yelm, zelm double precision, dimension(NGLLZ) :: radius_Z ! for temporary copies @@ -1247,7 +1083,7 @@ subroutine save_output_mesh_files_for_coupled_model(nspec, & do j = jmin,jmax do i = imin,imax write(92,'(3f25.10,i10,6i3)') xstore(i,j,k),ystore(i,j,k),zstore(i,j,k),ispec,i,j,k,1, & - ilayer,updown(k) + ilayer,updown(k) write(91,1000) radius(i,j,k), latitud(i,j,k), longitud(i,j,k) ! point counter icounter_pts = icounter_pts + 1 @@ -1313,7 +1149,7 @@ subroutine save_output_mesh_files_for_coupled_model(nspec, & do j = jmin,jmax do i = imin,imax write(92,'(3f25.10,i10,6i3)') xstore(i,j,k),ystore(i,j,k),zstore(i,j,k),ispec,i,j,k,2, & - ilayer,updown(k) + ilayer,updown(k) write(91,1000) radius(i,j,k), latitud(i,j,k), longitud(i,j,k) ! point counter icounter_pts = icounter_pts + 1 @@ -1379,7 +1215,7 @@ subroutine save_output_mesh_files_for_coupled_model(nspec, & do j = jmin,jmax do i = imin,imax write(92,'(3f25.10,i10,6i3)') xstore(i,j,k),ystore(i,j,k),zstore(i,j,k),ispec,i,j,k,3, & - ilayer,updown(k) + ilayer,updown(k) write(91,1000) radius(i,j,k), latitud(i,j,k), longitud(i,j,k) ! point counter icounter_pts = icounter_pts + 1 @@ -1445,7 +1281,7 @@ subroutine save_output_mesh_files_for_coupled_model(nspec, & do j = jmin,jmax do i = imin,imax write(92,'(3f25.10,i10,6i3)') xstore(i,j,k),ystore(i,j,k),zstore(i,j,k),ispec,i,j,k,4, & - ilayer,updown(k) + ilayer,updown(k) write(91,1000) radius(i,j,k), latitud(i,j,k), longitud(i,j,k) ! point counter icounter_pts = icounter_pts + 1 @@ -1511,7 +1347,7 @@ subroutine save_output_mesh_files_for_coupled_model(nspec, & do j = jmin,jmax do i = imin,imax write(92,'(3f25.10,i10,6i3)') xstore(i,j,k),ystore(i,j,k),zstore(i,j,k),ispec,i,j,k,5, & - ilayer,updown(k) + ilayer,updown(k) write(91,1000) radius(i,j,k), latitud(i,j,k), longitud(i,j,k) ! point counter icounter_pts = icounter_pts + 1 @@ -1578,7 +1414,7 @@ subroutine save_output_mesh_files_for_coupled_model(nspec, & do j = jmin,jmax do i = imin,imax write(92,'(3f25.10,i10,6i3)') xstore(i,j,k),ystore(i,j,k),zstore(i,j,k),ispec,i,j,k,6, & - ilayer,updown(k) + ilayer,updown(k) write(91,1000) radius(i,j,k), latitud(i,j,k), longitud(i,j,k) ! point counter icounter_pts = icounter_pts + 1 @@ -1600,4 +1436,4 @@ subroutine save_output_mesh_files_for_coupled_model(nspec, & call flush_IMAIN() endif - end subroutine save_output_mesh_files_for_coupled_model +end subroutine save_output_mesh_files_for_coupled_model diff --git a/tests/unit-tests/CMakeLists.txt b/tests/unit-tests/CMakeLists.txt index 98061b291..a42d82906 100644 --- a/tests/unit-tests/CMakeLists.txt +++ b/tests/unit-tests/CMakeLists.txt @@ -1,1084 +1,17 @@ cmake_minimum_required(VERSION 3.17.5) -# GoogleTest requires at least C++17 -set(CMAKE_CXX_STANDARD 17) - -# Include the GoogleTest framework -include("${CMAKE_SOURCE_DIR}/cmake/googletest.cmake") - -# Explicitly set binary output directory for tests -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/tests/unit-tests) - -include_directories(.) - -set(TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}) - -# Some of the writing test need to write somewhere and we don't want that -# to be in the source directory -set(TEST_OUTPUT_DIR ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) - -enable_testing() - -add_library( - specfem_environment - SPECFEM_Environment.cpp -) - -target_link_libraries( - specfem_environment - gtest_main - specfem_program -) - -add_library( - mesh_utilities_mapping - mesh_utilities/mapping.cpp -) - -target_link_libraries( - mesh_utilities_mapping - Kokkos::kokkos -) - -add_executable( - test_mesh_utilities_mapping_2d - mesh_utilities/test_mapping_2d.cpp -) - -target_link_libraries( - test_mesh_utilities_mapping_2d - mesh_utilities_mapping - specfem::utilities - gtest_main -) - -add_executable( - test_mesh_utilities_mapping_3d - mesh_utilities/test_mapping_3d.cpp -) - -target_link_libraries( - test_mesh_utilities_mapping_3d - mesh_utilities_mapping - specfem::utilities - gtest_main -) - -add_executable( - io_framework_tests - io/io_framework_tests.cpp -) - -target_link_libraries(io_framework_tests PRIVATE - specfem::io - ${BOOST_LIBS} - gtest_main - Kokkos::kokkos - specfem_environment - $<$:zlib> - $<$:hdf5> - $<$:adios2> -) - -target_compile_definitions( - io_framework_tests - PUBLIC - $<$>:-DNO_NPZ> - $<$>:-DNO_HDF5> - $<$>:-DNO_ADIOS2> -) - -add_executable( - abort_tests - program/abort_tests.cpp -) - -target_link_libraries( - abort_tests - specfem::program - gtest_main - Kokkos::kokkos -) - -add_executable( - is_close_tests - utilities/is_close_tests.cpp -) - -target_link_libraries( - is_close_tests - specfem::utilities - gtest_main - Kokkos::kokkos -) - -add_executable( - logspace_tests - utilities/logspace_tests.cpp - utilities/logarithmic_center_tests.cpp - utilities/band_tests.cpp -) - -target_link_libraries( - logspace_tests - specfem::utilities - specfem_environment - gtest_main - Kokkos::kokkos -) -add_executable( - units_tests - units/quantity_tests.cpp - units/unit_cast_tests.cpp -) - -target_link_libraries( - units_tests - specfem::utilities - gtest_main - Kokkos::kokkos -) - -add_executable( - attenuation_tests - attenuation/runner.cpp - attenuation/compute_band_tests.cpp - attenuation/compute_tau_sigma_tests.cpp - attenuation/maxwell_tests.cpp - attenuation/compute_tau_eps_tests.cpp - attenuation/compute_factors_tests.cpp - attenuation/compute_integration_factors_tests.cpp -) - -target_link_libraries( - attenuation_tests - gtest_main - specfem::attenuation - Kokkos::kokkos -) - -add_executable( - optimization_tests - optimization/runner.cpp - optimization/neldermead_tests.cpp - optimization/steepestdescent_tests.cpp -) - -target_link_libraries( - optimization_tests - gtest_main - Kokkos::kokkos -) - -add_executable( - gll_tests - gll/gll_tests.cpp -) - -target_link_libraries( - gll_tests - gtest_main - specfem::quadrature - specfem_environment - point - -lpthread -lm -) - -add_executable( - lagrange_tests - lagrange/Lagrange_tests.cpp -) - -target_link_libraries( - lagrange_tests - gtest_main - specfem::quadrature - specfem_environment - -lpthread -lm -) - -add_executable( - jacobian_tests - jacobian/jacobian_tests.cpp - jacobian/dim2/shape_functions_tests.cpp - jacobian/dim2/compute_locations_tests.cpp - jacobian/dim2/compute_jacobian_tests.cpp - jacobian/dim3/shape_functions_tests.cpp - jacobian/dim3/compute_locations_tests.cpp - jacobian/dim3/compute_jacobian_tests.cpp -) - -target_link_libraries( - jacobian_tests - jacobian - specfem_environment - gtest_main - -lpthread -lm -) - -add_executable( - enumerations_tests - enumerations/dim2/mesh_entity.cpp - enumerations/dim2/connections.cpp - enumerations/dim3/mesh_entity.cpp - enumerations/dim3/connections.cpp - enumerations/runner.cpp -) - -target_link_libraries( - enumerations_tests - specfem::enums - specfem::quadrature - shape_functions - gtest_main - specfem_environment - specfem::utilities - -lpthread -lm -) - -set_target_properties(enumerations_tests PROPERTIES UNITY_BUILD OFF) - -add_executable( - simd_tests - datatype/simd_tests.cpp -) -target_link_libraries( - simd_tests - gtest_main - gmock_main - Kokkos::kokkos - -lpthread -lm -) - -add_executable( - fortranio_test - fortran_io/fortranio_tests.cpp -) - -target_link_libraries( - fortranio_test - gtest_main - gmock_main - specfem::io - -lpthread -lm -) - - -add_executable( - point_tests - point/index_tests.cpp - point/coordinates_tests.cpp - point/boundary_tests.cpp - point/jacobian_matrix_tests.cpp - point/attenuation_tests.cpp - point/source_tests.cpp - point/stress_integrand_tests.cpp - point/stress_tests.cpp - # Kernels - # Dim 2 - point/kernels/dim2/acoustic_isotropic.cpp - point/kernels/dim2/elastic_isotropic.cpp - point/kernels/dim2/elastic_anisotropic.cpp - # point/kernels/dim2/poroelastic_isotropic.cpp - # Dim 3 - point/kernels/dim3/elastic_isotropic.cpp - # Properties - # Dim 2 - point/properties/dim2/elastic_isotropic.cpp - point/properties/dim2/elastic_anisotropic.cpp - point/properties/dim2/acoustic_isotropic.cpp - point/properties/dim2/elastic_isotropic_cosserat.cpp - point/properties/dim2/electromagnetic_isotropic.cpp - point/properties/dim2/poroelastic_isotropic.cpp - # Dim 3 - point/properties/dim3/elastic_isotropic.cpp - point/properties/dim3/elastic_isotropic_cosserat.cpp - -) - -target_link_libraries( - point_tests - point - specfem_environment - gtest_main - gmock_main -) - -add_executable( - receivers_tests - receivers/dim2/receiver_tests.cpp - receivers/dim3/receiver_tests.cpp -) - -target_link_libraries( - receivers_tests - gtest_main - specfem::receivers - specfem_environment - yaml-cpp - ${BOOST_LIBS} - -lpthread -lm -) - -add_executable( - mesh_dim2_tests - mesh/dim2/test_fixture/test_fixture.cpp - mesh/dim2/materials/materials.cpp - mesh/dim2/materials/properties.cpp - mesh/dim2/runner.cpp - mesh/dim2/adjacency_graph/adjacency_graph.cpp - mesh/dim2/adjacency_graph/adjacency_graph_regular_mesh.cpp - mesh/dim2/adjacency_graph/adjacency_graph_irregular_mesh.cpp -) - -target_link_libraries( - mesh_dim2_tests - gtest_main - specfem::mesh - specfem_environment - yaml-cpp - specfem::io - specfem::utilities - - # material_class - -lpthread -lm -) - -add_executable( - mesh_dim3_tests - mesh/dim3/mesh.cpp - mesh/dim3/control_nodes.cpp - mesh/dim3/materials.cpp - mesh/dim3/boundaries.cpp - mesh/dim3/adjacency_graph.cpp - mesh/dim3/tags.cpp - mesh/dim3/test.cpp -) - -target_link_libraries( - mesh_dim3_tests - gtest_main - specfem::mesh - specfem_environment - specfem::io - specfem::utilities - - -lpthread -lm -) - -add_executable( - nonconforming_tests - nonconforming/reparameterizations/compute_intersection_test.cpp - nonconforming/reparameterizations/set_transfer_functions_test.cpp - nonconforming/runner.cpp -) - -target_link_libraries( - nonconforming_tests - specfem::mesh - specfem::assembly - specfem::quadrature - specfem_environment - yaml-cpp - specfem::utilities - ${BOOST_LIBS} - -lpthread -lm - gtest_main -) - -# add_executable( -# compute_jacobian_matrix_tests -# assembly_mesh/jacobian_matrix/compute_jacobian_matrix_tests.cpp -# ) - -# target_link_libraries( -# compute_jacobian_matrix_tests -# mesh -# assembly -# quadrature -# yaml-cpp -# compare_arrays -# boost -# specfem_environment -# # material_class -# -lpthread -lm -# ) - -# add_executable( -# compute_elastic_tests -# assembly_mesh/elastic/compute_properties_tests.cpp -# ) - -# target_link_libraries( -# compute_elastic_tests -# mesh -# assembly -# quadrature -# specfem_environment -# -# yaml-cpp -# compare_arrays -# boost -# # material_class -# -lpthread -lm -# ) - -# add_executable( -# compute_acoustic_tests -# assembly_mesh/acoustic/compute_properties_tests.cpp -# ) - -# target_link_libraries( -# compute_acoustic_tests -# mesh -# assembly -# quadrature -# specfem_environment -# yaml-cpp -# compare_arrays -# # material_class -# -lpthread -lm -# ) - -# add_executable( -# compute_tests -# assembly_mesh/index/compute_tests.cpp -# ) - -# target_link_libraries( -# compute_tests -# mesh -# assembly -# quadrature -# specfem_environment -# yaml-cpp -# compare_arrays -# io -# boost -# # material_class -# -lpthread -lm -# ) - -# target_compile_definitions( -# compute_tests -# PUBLIC -# $<$:-DSPECFEMPP_APPLE_BUILD> -# $<$:-DSPECFEMPP_GNU_BUILD> -# $<$:-DSPECFEMPP_MSVC_BUILD> -# $<$:-DSPECFEMPP_INTEL_LLVM_BUILD> -# ) - -add_executable( - nonconforming_assembly_tests - assembly/runner.cpp - assembly/nonconforming_interfaces/container_init_test.cpp -) -target_link_libraries( - nonconforming_assembly_tests - specfem::io - specfem::mesh - specfem::assembly - specfem::quadrature - specfem_environment - yaml-cpp - specfem::utilities - ${BOOST_LIBS} - - -lpthread -lm - gtest_main -) - - -add_executable( - assembly_tests - assembly/test_fixture/test_fixture.cpp - assembly/runner.cpp - assembly/kernels/kernels.cpp - assembly/properties/properties.cpp - assembly/compute_wavefield/compute_wavefield.cpp - assembly/sources/sources.cpp - assembly/check_jacobian/dim2/check_jacobian.cpp - assembly/locate/locate_point.cpp - assembly/locate/locate_point_on_edge.cpp - assembly/mesh/utilities.cpp - assembly/sources/locate_sources.cpp - assembly/compute_source_array/dim2/compute_source_array_from_vector.cpp - assembly/compute_source_array/dim2/compute_source_array_from_tensor.cpp - assembly/compute_source_array/dim3/compute_source_array_from_vector.cpp - assembly/compute_source_array/dim3/compute_source_array_from_tensor.cpp - assembly/info/compute_tests.cpp - assembly/info/scatter_minmax_tests.cpp - assembly/element_intersections/edge_types_tests.cpp - assembly/element_intersections/face_types_tests.cpp - - # New dim3 tests - assembly/dim3/mesh/shape_functions.cpp - assembly/dim3/mesh/points.cpp - assembly/dim3/mesh/control_nodes.cpp - assembly/dim3/jacobian_matrix/jacobian_matrix.cpp - assembly/dim3/properties/properties.cpp -) - -#Set unity build off -target_compile_definitions(assembly_tests PRIVATE TEST_OUTPUT_DIR=${TEST_OUTPUT_DIR}) -set_target_properties(assembly_tests PROPERTIES UNITY_BUILD OFF) - -target_link_libraries( - assembly_tests - specfem::mesh - specfem::assembly - specfem::quadrature - specfem_environment - specfem::io - - yaml-cpp - specfem::utilities - specfem::enums - specfem::element - shape_functions - specfem::quadrature - ${BOOST_LIBS} - - -lpthread -lm - gtest_main - ${BOOST_LIBS} -) - -add_executable( - assembly_receivers_tests - assembly/receivers/receivers_tests.cpp - assembly/receivers/dim2/receivers_tests.cpp - assembly/receivers/impl/receiver_iterator_tests.cpp - assembly/receivers/impl/dim2/seismogram_iterator_tests.cpp -) - -target_link_libraries( - assembly_receivers_tests - specfem::mesh - specfem::assembly - specfem::quadrature - specfem_environment - yaml-cpp - specfem::io - ${BOOST_LIBS} - - gtest - gtest_main - -lpthread -lm -) - -add_executable( - io_tests - io/sources/test_read_sources_file.cpp - io/sources/test_read_sources_yaml.cpp - io/sources/test_source_solutions.cpp - io/receivers/test_receiver_solutions.cpp - io/receivers/test_read_stations_file.cpp - io/receivers/test_read_yaml.cpp -) - -target_link_libraries( - io_tests - specfem::io - gtest_main - yaml-cpp - specfem::enums - ${BOOST_LIBS} -) - -add_executable( - interpolate_function - algorithms/interpolate_function/dim2/interpolate_function.cpp - algorithms/interpolate_function/dim3/interpolate_function.cpp - algorithms/interpolate_function/runner.cpp -) - -target_link_libraries( - interpolate_function - specfem::quadrature - specfem_environment - specfem::algorithms - specfem::io - ${BOOST_LIBS} - point -) - -add_executable( - locate_point_fixture_2d - algorithms/dim2/locate_point_fixture.cpp - algorithms/dim2/locate_point_test.cpp - algorithms/dim2/locate_point_on_edge_test.cpp -) - -target_link_libraries( - locate_point_fixture_2d - mesh_utilities_mapping - specfem::algorithms - jacobian - point - specfem::utilities - specfem_environment - gtest_main -) - - -add_executable( - locate_point_fixture_3d - algorithms/dim3/locate_point_fixture.cpp -) - -target_link_libraries( - locate_point_fixture_3d - mesh_utilities_mapping - specfem::algorithms - jacobian - point - specfem::utilities - specfem_environment - gtest_main -) - -add_executable( - gradient_tests - algorithms/dim2/gradient.cpp - algorithms/dim3/gradient.cpp -) - -target_link_libraries( - gradient_tests - specfem::quadrature - specfem::algorithms - gtest_main - Kokkos::kokkos - specfem_environment - specfem::assembly - specfem::utilities - -lpthread -lm -) - -# Turn unity build off for gradient_tests -set_target_properties(gradient_tests PROPERTIES UNITY_BUILD OFF) - -add_executable( - transfer_tests - algorithms/dim2/transfer.cpp -) - -target_link_libraries( - transfer_tests - specfem::algorithms - specfem::enums - gtest_main - Kokkos::kokkos - specfem_environment - specfem::utilities - -lpthread -lm -) - -add_executable( - coupling_integral_tests - algorithms/dim2/coupling_integral/dshape.cpp - algorithms/dim2/coupling_integral/timesshape.cpp -) - -target_link_libraries( - coupling_integral_tests - specfem::algorithms - specfem::element_connections - specfem::mesh - assembly - gtest_main - Kokkos::kokkos - specfem_environment - specfem::utilities - - specfem::quadrature - yaml-cpp - -lpthread -lm -) - -add_executable( - policies_tests - policies/policies.cpp -) - -target_link_libraries( - policies_tests - specfem::mesh - source_class - specfem::receivers - specfem_environment - yaml-cpp - ${BOOST_LIBS} - -lpthread -lm -) - -add_executable( - chunked_edge_tests - policies/chunked_edge.cpp -) - -target_link_libraries( - chunked_edge_tests - gtest_main - Kokkos::kokkos - specfem_environment - specfem::assembly - specfem::enums - ${BOOST_LIBS} - -lpthread -lm -) - -add_executable( - chunked_face_tests - policies/chunked_face.cpp -) - -target_link_libraries( - chunked_face_tests - gtest_main - Kokkos::kokkos - specfem_environment - specfem::enums - point - -lpthread -lm -) - -add_executable( - chunked_face_intersection_tests - policies/chunked_face_intersection.cpp -) - -target_link_libraries( - chunked_face_intersection_tests - gtest_main - Kokkos::kokkos - specfem_environment - specfem::enums - point - -lpthread -lm -) - -add_executable( - mass_matrix_tests - medium/mass_matrix/main.cpp - # 2D mass matrix tests - medium/mass_matrix/dim2/elastic_isotropic.cpp - medium/mass_matrix/dim2/elastic_anisotropic.cpp - medium/mass_matrix/dim2/acoustic.cpp - medium/mass_matrix/dim2/poroelastic.cpp - # 3D mass matrix tests - medium/mass_matrix/dim3/elastic_isotropic.cpp - medium/mass_matrix/dim3/acoustic.cpp -) - -target_link_libraries( - mass_matrix_tests - point - gtest_main -) - -add_executable( - stress_tests - medium/stress/main.cpp - # 2D stress tests - medium/stress/dim2/acoustic.cpp - medium/stress/dim2/elastic_isotropic.cpp - medium/stress/dim2/elastic_anisotropic.cpp - medium/stress/dim2/elastic_isotropic_cosserat.cpp - medium/stress/dim2/poroelastic_isotropic.cpp - # 3D stress tests - medium/stress/dim3/elastic_isotropic.cpp - medium/stress/dim3/acoustic.cpp -) - -target_link_libraries( - stress_tests - point - gtest_main -) - -add_executable( - strain_tests - medium/strain/main.cpp - medium/strain/dim2/elastic_isotropic.cpp - medium/strain/dim3/elastic_isotropic.cpp -) - -target_link_libraries( - strain_tests - point - gtest_main -) - -add_executable( - compute_coupling_tests - compute_coupling/acoustic_elastic.cpp - compute_coupling/elastic_acoustic.cpp - compute_coupling/nonconforming/acoustic_elastic.cpp - compute_coupling/nonconforming/elastic_acoustic.cpp - compute_coupling/runner.cpp -) - -target_link_libraries( - compute_coupling_tests - point - gtest - specfem_environment - Kokkos::kokkos -) - -add_executable( - source_tests - medium/source/main.cpp - # 2D source tests - medium/source/dim2/acoustic.cpp - medium/source/dim2/elastic_isotropic.cpp - medium/source/dim2/elastic_anisotropic.cpp - medium/source/dim2/elastic_isotropic_cosserat.cpp - medium/source/dim2/poroelastic.cpp - # 3D source tests - medium/source/dim3/elastic_isotropic.cpp - medium/source/dim3/acoustic.cpp -) - -target_link_libraries( - source_tests - point - gtest_main -) - -add_executable( - source_class_tests - source/source.cpp - source/base_source_tests.cpp - source/typed_source_tests.cpp - # source/dim2/vector_sources/force_source.cpp - # source/dim2/vector_sources/cosserat_force_source.cpp - # source/dim2/vector_sources/adjoint_source.cpp - # source/dim2/vector_sources/external_source.cpp - # source/dim2/tensor_sources/moment_tensor_source.cpp - # source/dim3/vector_sources/force_source.cpp - # source/dim3/tensor_sources/moment_tensor_source.cpp -) -target_link_libraries( - source_class_tests - source_class - source_time_functions - specfem_environment - -lpthread -lm -) - -add_executable( - source_time_function_tests - source_time_function/source_time_function_tests.cpp -) - -target_link_libraries( - source_time_function_tests - source_time_functions - Kokkos::kokkos - yaml-cpp - gtest_main -) - - -add_executable( - displacement_newmark_2d_tests - displacement_tests/Newmark/dim2/newmark_tests.cpp -) - -target_link_libraries( - displacement_newmark_2d_tests - specfem::quadrature - specfem::mesh - # material_class - yaml-cpp - specfem_environment - specfem::assembly - specfem::runtime_configuration - timescheme - point - specfem::algorithms - specfem::solver - specfem::periodic_tasks - ${BOOST_LIBS} - -lpthread -lm -) - - -add_executable( - displacement_newmark_3d_tests - displacement_tests/Newmark/dim3/newmark_tests.cpp -) - -target_link_libraries( - displacement_newmark_3d_tests - specfem::quadrature - specfem::mesh - # material_class - yaml-cpp - specfem_environment - specfem::assembly - specfem::runtime_configuration - timescheme - point - specfem::algorithms - specfem::solver - specfem::periodic_tasks - ${BOOST_LIBS} - -lpthread -lm -) - -# add_executable( -# seismogram_elastic_tests -# seismogram/elastic/seismogram_tests.cpp -# ) - -# target_link_libraries( -# seismogram_elastic_tests -# quadrature -# mesh -# yaml-cpp -# specfem_environment -# assembly -# specfem::runtime_configuration -# writer -# periodic_tasks -# domain -# coupled_interface -# solver -# -lpthread -lm -# ) - -# add_executable( -# seismogram_acoustic_tests -# seismogram/acoustic/seismogram_tests.cpp -# ) - -# target_link_libraries( -# seismogram_acoustic_tests -# quadrature -# mesh -# yaml-cpp -# specfem_environment -# assembly -# specfem::runtime_configuration -# writer -# periodic_tasks -# domain -# coupled_interface -# solver -# -lpthread -lm -# ) - -# add_executable( -# compute_coupled_interfaces_tests -# assembly_mesh/coupled_interfaces/coupled_interfaces_tests.cpp -# ) - -# target_link_libraries( -# compute_coupled_interfaces_tests -# quadrature -# mesh -# yaml-cpp -# specfem_environment -# assembly -# compare_arrays -# point -# edge -# boost -# -lpthread -lm -# ) - - -# Link to gtest only if MPI is disabled -if(NOT SPECFEM_ENABLE_MPI) - include(GoogleTest) - set(TEST_TARGETS - abort_tests - assembly_receivers_tests - assembly_tests - attenuation_tests - chunked_edge_tests - chunked_face_tests - chunked_face_intersection_tests - compute_coupling_tests - # compute_acoustic_tests - # compute_coupled_interfaces_tests - # compute_elastic_tests - # compute_jacobian_matrix_tests - # compute_tests - displacement_newmark_2d_tests - displacement_newmark_3d_tests - fortranio_test - enumerations_tests - gll_tests - interpolate_function - io_framework_tests - io_tests - is_close_tests - logspace_tests - jacobian_tests - lagrange_tests - locate_point_fixture_2d - locate_point_fixture_3d - gradient_tests - transfer_tests - mass_matrix_tests - mesh_dim2_tests - mesh_dim3_tests - nonconforming_tests - point_tests - policies_tests - optimization_tests - receivers_tests - simd_tests - source_class_tests - source_tests - source_time_function_tests - strain_tests - stress_tests - test_mesh_utilities_mapping_2d - test_mesh_utilities_mapping_3d - units_tests - ) - foreach(test_target IN LISTS TEST_TARGETS) - gtest_discover_tests(${test_target} - DISCOVERY_MODE POST_BUILD - DISCOVERY_TIMEOUT 300 - ) - endforeach() - - # For tests that require data files from the tests directory, - # create symbolic links to the test data directory so that - # the tests can access them from the build folder. - - set(LINK_DIRS - algorithms - assembly - assembly_mesh - data - displacement_tests - domain - fortran_io - io - mesh - policies - ) - - foreach(dir_name IN LISTS LINK_DIRS) - file(CREATE_LINK - ${CMAKE_CURRENT_SOURCE_DIR}/${dir_name} - ${CMAKE_CURRENT_BINARY_DIR}/${dir_name} - SYMBOLIC) - endforeach() - - - # gtest_discover_tests(seismogram_elastic_tests) - # gtest_discover_tests(seismogram_acoustic_tests) +# Include common test configuration and utilities +# This handles C++ standard, GoogleTest setup, and shared test libraries +include(${CMAKE_CURRENT_SOURCE_DIR}/common.cmake) + +# Include test definitions based on build configuration +if(SPECFEM_ENABLE_MPI) + # Build MPI-only tests + include(${CMAKE_CURRENT_SOURCE_DIR}/mpi.cmake) endif() +include(${CMAKE_CURRENT_SOURCE_DIR}/serial.cmake) +# Data subdirectories for test provenance add_subdirectory(data/dim2/ncmarked_conforming_grid_curved/provenance) add_subdirectory(data/dim2/ncmarked_conforming_grid_flat/provenance) diff --git a/tests/unit-tests/SPECFEM_Environment.cpp b/tests/unit-tests/SPECFEM_Environment.cpp index 17efc734c..8242c9a07 100644 --- a/tests/unit-tests/SPECFEM_Environment.cpp +++ b/tests/unit-tests/SPECFEM_Environment.cpp @@ -1,14 +1,34 @@ #include "SPECFEM_Environment.hpp" +#include "specfem/mpi.hpp" #include "specfem/program/context.hpp" #include +#include std::shared_ptr SPECFEMEnvironment::context_ = nullptr; +size_t SPECFEMEnvironment::required_mpi_size_ = 1; +bool SPECFEMEnvironment::mpi_size_valid_ = true; +std::string SPECFEMEnvironment::mpi_size_error_ = ""; + +SPECFEMEnvironment::SPECFEMEnvironment(const size_t nprocs) { + required_mpi_size_ = nprocs; +} void SPECFEMEnvironment::SetUp() { char **argv = nullptr; int argc = 0; - context_ = std::make_shared(argc, argv); + context_ = std::make_shared(argc, argv, + required_mpi_size_); + + // Check if actual MPI size meets the required size + const int actual_mpi_size = specfem::MPI::get_size(); + if (actual_mpi_size < static_cast(required_mpi_size_)) { + mpi_size_valid_ = false; + std::ostringstream msg; + msg << "MPI size requirement not met. Required: " << required_mpi_size_ + << ", Actual: " << actual_mpi_size; + mpi_size_error_ = msg.str(); + } } void SPECFEMEnvironment::TearDown() { context_.reset(); } diff --git a/tests/unit-tests/SPECFEM_Environment.hpp b/tests/unit-tests/SPECFEM_Environment.hpp index e5d31334a..18bda62cf 100644 --- a/tests/unit-tests/SPECFEM_Environment.hpp +++ b/tests/unit-tests/SPECFEM_Environment.hpp @@ -7,9 +7,20 @@ class SPECFEMEnvironment : public ::testing::Environment { public: + SPECFEMEnvironment() = default; + ~SPECFEMEnvironment() override = default; + + SPECFEMEnvironment(const size_t nprocs); + void SetUp() override; void TearDown() override; + static bool IsMPISizeValid() { return mpi_size_valid_; } + static std::string GetMPISizeError() { return mpi_size_error_; } + private: static std::shared_ptr context_; + static size_t required_mpi_size_; + static bool mpi_size_valid_; + static std::string mpi_size_error_; }; diff --git a/tests/unit-tests/common.cmake b/tests/unit-tests/common.cmake new file mode 100644 index 000000000..61cc9486e --- /dev/null +++ b/tests/unit-tests/common.cmake @@ -0,0 +1,45 @@ +# Common CMake configuration for unit tests +# This file contains shared setup, libraries, and utilities used by both +# serial (non-MPI) and MPI test builds. + +# GoogleTest requires at least C++17 +set(CMAKE_CXX_STANDARD 17) + +# Include the GoogleTest framework +include("${CMAKE_SOURCE_DIR}/cmake/googletest.cmake") + +# Explicitly set binary output directory for tests +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/tests/unit-tests) + +include_directories(.) + +set(TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +# Some of the writing tests need to write somewhere and we don't want that +# to be in the source directory +set(TEST_OUTPUT_DIR ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + +enable_testing() + +# Common library: Test environment setup +add_library( + specfem_environment + SPECFEM_Environment.cpp +) + +target_link_libraries( + specfem_environment + gtest_main + specfem_program +) + +# Common library: Mesh utilities mapping +add_library( + mesh_utilities_mapping + mesh_utilities/mapping.cpp +) + +target_link_libraries( + mesh_utilities_mapping + Kokkos::kokkos +) diff --git a/tests/unit-tests/data/mpi/dim3/HomogeneousHalfSpace/Database/proc_0.bin b/tests/unit-tests/data/mpi/dim3/HomogeneousHalfSpace/Database/proc_0.bin deleted file mode 100644 index b9f810b4b..000000000 Binary files a/tests/unit-tests/data/mpi/dim3/HomogeneousHalfSpace/Database/proc_0.bin and /dev/null differ diff --git a/tests/unit-tests/data/mpi/dim3/HomogeneousHalfSpace/Database/proc_1.bin b/tests/unit-tests/data/mpi/dim3/HomogeneousHalfSpace/Database/proc_1.bin deleted file mode 100644 index ca423c2b1..000000000 Binary files a/tests/unit-tests/data/mpi/dim3/HomogeneousHalfSpace/Database/proc_1.bin and /dev/null differ diff --git a/tests/unit-tests/data/mpi/dim3/HomogeneousHalfSpace/Database/proc_2.bin b/tests/unit-tests/data/mpi/dim3/HomogeneousHalfSpace/Database/proc_2.bin deleted file mode 100644 index 905d79e16..000000000 Binary files a/tests/unit-tests/data/mpi/dim3/HomogeneousHalfSpace/Database/proc_2.bin and /dev/null differ diff --git a/tests/unit-tests/data/mpi/dim3/HomogeneousHalfSpace/Database/proc_3.bin b/tests/unit-tests/data/mpi/dim3/HomogeneousHalfSpace/Database/proc_3.bin deleted file mode 100644 index a3f2ae4dc..000000000 Binary files a/tests/unit-tests/data/mpi/dim3/HomogeneousHalfSpace/Database/proc_3.bin and /dev/null differ diff --git a/tests/unit-tests/data/mpi/dim3/HomogeneousHalfSpace/.gitignore b/tests/unit-tests/data/mpi/dim3/HomogeneousMediumMPI4x4/.gitignore similarity index 100% rename from tests/unit-tests/data/mpi/dim3/HomogeneousHalfSpace/.gitignore rename to tests/unit-tests/data/mpi/dim3/HomogeneousMediumMPI4x4/.gitignore diff --git a/tests/unit-tests/data/mpi/dim3/HomogeneousMediumMPI4x4/Database/proc_0.bin b/tests/unit-tests/data/mpi/dim3/HomogeneousMediumMPI4x4/Database/proc_0.bin new file mode 100644 index 000000000..b27ea93b2 Binary files /dev/null and b/tests/unit-tests/data/mpi/dim3/HomogeneousMediumMPI4x4/Database/proc_0.bin differ diff --git a/tests/unit-tests/data/mpi/dim3/HomogeneousMediumMPI4x4/Database/proc_1.bin b/tests/unit-tests/data/mpi/dim3/HomogeneousMediumMPI4x4/Database/proc_1.bin new file mode 100644 index 000000000..7ee37d154 Binary files /dev/null and b/tests/unit-tests/data/mpi/dim3/HomogeneousMediumMPI4x4/Database/proc_1.bin differ diff --git a/tests/unit-tests/data/mpi/dim3/HomogeneousMediumMPI4x4/Database/proc_2.bin b/tests/unit-tests/data/mpi/dim3/HomogeneousMediumMPI4x4/Database/proc_2.bin new file mode 100644 index 000000000..f65dbb701 Binary files /dev/null and b/tests/unit-tests/data/mpi/dim3/HomogeneousMediumMPI4x4/Database/proc_2.bin differ diff --git a/tests/unit-tests/data/mpi/dim3/HomogeneousMediumMPI4x4/Database/proc_3.bin b/tests/unit-tests/data/mpi/dim3/HomogeneousMediumMPI4x4/Database/proc_3.bin new file mode 100644 index 000000000..68e0f0035 Binary files /dev/null and b/tests/unit-tests/data/mpi/dim3/HomogeneousMediumMPI4x4/Database/proc_3.bin differ diff --git a/tests/unit-tests/data/mpi/dim3/HomogeneousHalfSpace/Snakefile b/tests/unit-tests/data/mpi/dim3/HomogeneousMediumMPI4x4/Snakefile similarity index 100% rename from tests/unit-tests/data/mpi/dim3/HomogeneousHalfSpace/Snakefile rename to tests/unit-tests/data/mpi/dim3/HomogeneousMediumMPI4x4/Snakefile diff --git a/tests/unit-tests/data/mpi/dim3/HomogeneousHalfSpace/provenance/DATA/meshfem3D_files/Mesh_Par_file b/tests/unit-tests/data/mpi/dim3/HomogeneousMediumMPI4x4/provenance/DATA/meshfem3D_files/Mesh_Par_file similarity index 100% rename from tests/unit-tests/data/mpi/dim3/HomogeneousHalfSpace/provenance/DATA/meshfem3D_files/Mesh_Par_file rename to tests/unit-tests/data/mpi/dim3/HomogeneousMediumMPI4x4/provenance/DATA/meshfem3D_files/Mesh_Par_file diff --git a/tests/unit-tests/data/mpi/dim3/HomogeneousHalfSpace/provenance/DATA/meshfem3D_files/interface1.txt b/tests/unit-tests/data/mpi/dim3/HomogeneousMediumMPI4x4/provenance/DATA/meshfem3D_files/interface1.txt similarity index 100% rename from tests/unit-tests/data/mpi/dim3/HomogeneousHalfSpace/provenance/DATA/meshfem3D_files/interface1.txt rename to tests/unit-tests/data/mpi/dim3/HomogeneousMediumMPI4x4/provenance/DATA/meshfem3D_files/interface1.txt diff --git a/tests/unit-tests/data/mpi/dim3/HomogeneousHalfSpace/provenance/DATA/meshfem3D_files/interfaces.txt b/tests/unit-tests/data/mpi/dim3/HomogeneousMediumMPI4x4/provenance/DATA/meshfem3D_files/interfaces.txt similarity index 100% rename from tests/unit-tests/data/mpi/dim3/HomogeneousHalfSpace/provenance/DATA/meshfem3D_files/interfaces.txt rename to tests/unit-tests/data/mpi/dim3/HomogeneousMediumMPI4x4/provenance/DATA/meshfem3D_files/interfaces.txt diff --git a/tests/unit-tests/io/mesh/read_mesh.cpp b/tests/unit-tests/io/mesh/read_mesh.cpp new file mode 100644 index 000000000..da5dc30b6 --- /dev/null +++ b/tests/unit-tests/io/mesh/read_mesh.cpp @@ -0,0 +1,51 @@ +#include "SPECFEM_Environment.hpp" + +#include "read_mesh_test_fixture.hpp" +#include "specfem/logger.hpp" +#include "specfem/mpi.hpp" +#include +#include +#include + +using namespace specfem::test_configuration; + +std::unordered_map expected_nelements = { + { "HomogeneousMediumMPI4x4", 16 } +}; /// 4x4x1 mesh with 16 elements total + +TEST_P(Read3DMeshMPITest, SuccessfulExecution) { + const auto ¶m_name = GetParam(); + + // Get the mesh that was loaded in SetUp() + const auto &mesh = getMesh(); + + const auto expected_num_elements = expected_nelements.at(param_name); + + // Synchronize all ranks after mesh loading + SPECFEM_MPI_SAFECALL(MPI_Barrier(specfem::MPI::world_communicator())); + + // Get local element count and perform MPI reduction to sum across all ranks + const auto local_num_elements = mesh.nspec; // Each rank's local element count + int global_num_elements = 0; + + SPECFEM_MPI_SAFECALL(MPI_Reduce(&local_num_elements, &global_num_elements, 1, + MPI_INT, MPI_SUM, 0, + specfem::MPI::world_communicator())); + + // On rank 0, verify the mesh was loaded successfully + SPECFEM_MPI_ON_ROOT({ + ASSERT_EQ(global_num_elements, expected_num_elements) + << "Total number of elements across all ranks should match expected"; + + specfem::Logger::info("io::read_3d_mesh test passed for dataset: " + + param_name); + specfem::Logger::info("Expected total elements: " + + std::to_string(expected_num_elements)); + specfem::Logger::info("Total elements across " + + std::to_string(specfem::MPI::get_size()) + + " processes: " + std::to_string(global_num_elements)); + }); + + // Final barrier to ensure all ranks complete the test + SPECFEM_MPI_SAFECALL(MPI_Barrier(specfem::MPI::world_communicator())); +} diff --git a/tests/unit-tests/io/mesh/read_mesh_test_fixture.hpp b/tests/unit-tests/io/mesh/read_mesh_test_fixture.hpp new file mode 100644 index 000000000..4c017e213 --- /dev/null +++ b/tests/unit-tests/io/mesh/read_mesh_test_fixture.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include "SPECFEM_Environment.hpp" +#include "specfem/enums.hpp" +#include "specfem/io.hpp" +#include "specfem/mesh.hpp" +#include "specfem/mpi.hpp" +#include +#include + +namespace specfem::test_configuration { +struct Read3DMesh { + constexpr static specfem::element::dimension_tag dimension = + specfem::element::dimension_tag::dim3; + specfem::mesh::mesh mesh; + + Read3DMesh() = default; + + Read3DMesh(const std::string &database) { + mesh = specfem::io::read_3d_mesh(database); + } +}; +} // namespace specfem::test_configuration + +// Setup a fixture for parameterized tests +class Read3DMeshMPITest : public ::testing::TestWithParam { +protected: + constexpr static specfem::element::dimension_tag dimension = + specfem::element::dimension_tag::dim3; + + specfem::test_configuration::Read3DMesh mesh; + + Read3DMeshMPITest() = default; + + void SetUp() override { + // Check if MPI size requirement was met during environment setup + if (!SPECFEMEnvironment::IsMPISizeValid()) { + GTEST_SKIP() << SPECFEMEnvironment::GetMPISizeError(); + } + + // Check if the current + if (specfem::MPI::world_communicator() == MPI_COMM_NULL) { + GTEST_SKIP() << "Test designed for 4 processes. Rank " + << specfem::MPI::get_rank() + << " is outside the participating range [0-3]."; + } + + const auto &folder = GetParam(); + const std::string database = "data/mpi/dim3/" + folder + "/Database.bin"; + const auto mpi_database = specfem::MPI::format_proc_filename(database); + mesh = specfem::test_configuration::Read3DMesh(mpi_database); + } + + void TearDown() override { + // Any cleanup needed for each test + } + + ~Read3DMeshMPITest() override = default; + + // Accessor for the mesh + const auto &getMesh() const { return mesh.mesh; } +}; diff --git a/tests/unit-tests/io/mesh/runner.cpp b/tests/unit-tests/io/mesh/runner.cpp new file mode 100644 index 000000000..78925ea19 --- /dev/null +++ b/tests/unit-tests/io/mesh/runner.cpp @@ -0,0 +1,14 @@ + +#include "SPECFEM_Environment.hpp" + +#include "read_mesh_test_fixture.hpp" +#include + +INSTANTIATE_TEST_SUITE_P(Read3DMeshMPITests, Read3DMeshMPITest, + ::testing::Values("HomogeneousMediumMPI4x4")); + +int main(int argc, char *argv[]) { + ::testing::InitGoogleTest(&argc, argv); + ::testing::AddGlobalTestEnvironment(new SPECFEMEnvironment(4)); + return RUN_ALL_TESTS(); +} diff --git a/tests/unit-tests/mesh/dim3/adjacency_graph.cpp b/tests/unit-tests/mesh/dim3/adjacency_graph.cpp index ca0b3b3f6..3bf45be72 100644 --- a/tests/unit-tests/mesh/dim3/adjacency_graph.cpp +++ b/tests/unit-tests/mesh/dim3/adjacency_graph.cpp @@ -134,212 +134,3 @@ TEST_P(Mesh3DTest, AdjacencyGraph) { const auto &expected = expected_adjacency_map.at(param_name); expected.check(adjacency_graph); } - -namespace specfem::test_configuration { - -struct MPIConnection { - - constexpr static specfem::element::dimension_tag dimension = - specfem::element::dimension_tag::dim3; - struct EdgeProperties { - size_t rank; ///< MPI rank of the edge - size_t index; ///< Local element index associated with this edge - specfem::mesh_entity::dim3::type connection_id; - specfem::mesh_entity::dim3::type anchor; - - EdgeProperties(size_t rank, size_t index, - specfem::mesh_entity::dim3::type connection_id, - specfem::mesh_entity::dim3::type anchor) - : rank(rank), index(index), connection_id(connection_id), - anchor(anchor) {} - EdgeProperties( - const std::tuple &properties) - : EdgeProperties(std::get<0>(properties), std::get<1>(properties), - std::get<2>(properties), std::get<3>(properties)) {} - }; - - EdgeProperties edge1; ///< Properties of edge 1 in MPI connection - EdgeProperties edge2; ///< Properties of edge 2 in MPI connection - - MPIConnection(const EdgeProperties &edge1, const EdgeProperties &edge2) - : edge1(edge1), edge2(edge2) {} - - MPIConnection( - const std::tuple &edge1_properties, - const std::tuple &edge2_properties) - : edge1(edge1_properties), edge2(edge2_properties) {} - - bool expect_in(const std::vector::MPIEdgeProperties> &mpi_connections) const { - - const auto my_rank = static_cast(specfem::MPI::get_rank()); - if (my_rank != edge1.rank && my_rank != edge2.rank) { - return false; // This connection was not expected on this rank - } - - /// Check if the expected MPI connection exists in the list of MPI - /// connections - bool found = false; - for (const auto &mpi_edge : mpi_connections) { - if (my_rank == edge1.rank) { - // We are edge1, look for matching edge2 - if (mpi_edge.neighbor_partition == edge2.rank && - mpi_edge.orientation == edge1.connection_id && - mpi_edge.neighbor_orientation == edge2.connection_id && - mpi_edge.local_index == edge1.index && - mpi_edge.neighbor_local_index == edge2.index && - mpi_edge.local_anchor_point == edge1.anchor && - mpi_edge.neighbor_anchor_point == edge2.anchor) { - found = true; - break; - } - } else { - // We are edge2, look for matching edge1 - if (mpi_edge.neighbor_partition == edge1.rank && - mpi_edge.orientation == edge2.connection_id && - mpi_edge.neighbor_orientation == edge1.connection_id && - mpi_edge.local_index == edge2.index && - mpi_edge.neighbor_local_index == edge1.index && - mpi_edge.local_anchor_point == edge2.anchor && - mpi_edge.neighbor_anchor_point == edge1.anchor) { - found = true; - break; - } - } - } - - return found; - } -}; - -struct ExpectedMPIAdjacency3D { - constexpr static specfem::element::dimension_tag dimension = - specfem::element::dimension_tag::dim3; - - std::vector mpi_connections; ///< List of expected MPI - ///< connections - - ExpectedMPIAdjacency3D( - const std::initializer_list &mpi_connections) - : mpi_connections(mpi_connections) {} - - void check( - const specfem::mesh::adjacency_graph &adjacency_graph) const { - const auto &mpi_conns = adjacency_graph.mpi_connections(); - for (const auto &expected_mpi_conn : mpi_connections) { - auto local_found = expected_mpi_conn.expect_in(mpi_conns); - int local_found_int = local_found ? 1 : 0; - int found_int = 0; - SPECFEM_MPI_SAFECALL(MPI_Reduce(&local_found_int, &found_int, 1, MPI_INT, - MPI_LOR, 0, MPI_COMM_WORLD)); - bool found = (found_int != 0); - - if (!found) { - SPECFEM_MPI_ON_ROOT({ - std::ostringstream msg; - msg << "Failed expected MPI adjacency between rank " - << expected_mpi_conn.edge1.rank << " (element " - << expected_mpi_conn.edge1.index << ", connection ID " - << static_cast(expected_mpi_conn.edge1.connection_id) - << ", anchor " << static_cast(expected_mpi_conn.edge1.anchor) - << ") and rank " << expected_mpi_conn.edge2.rank << " (element " - << expected_mpi_conn.edge2.index << ", connection ID " - << static_cast(expected_mpi_conn.edge2.connection_id) - << ", anchor " << static_cast(expected_mpi_conn.edge2.anchor) - << ")\n"; - msg << " No matching MPI connection found in adjacency graph.\n"; - ADD_FAILURE() << msg.str(); - }); - } - - SPECFEM_MPI_SAFECALL(MPI_Barrier(MPI_COMM_WORLD)); - } - SPECFEM_MPI_ON_ROOT({ - SUCCEED() << "All expected MPI connections are present and correct." - << std::endl; - }); - } -}; - -} // namespace specfem::test_configuration - -using namespace specfem::test_configuration; - -static const std::unordered_map - expected_mpi_adjacency_map = { - { "HomogeneousMediumMPI4x4", - ExpectedMPIAdjacency3D( - { // Left - Right connections - MPIConnection( - std::make_tuple( - 0, 1, specfem::mesh_entity::dim3::type::right, - specfem::mesh_entity::dim3::type::bottom_front_right), - std::make_tuple( - 1, 2, specfem::mesh_entity::dim3::type::left, - specfem::mesh_entity::dim3::type::bottom_front_left)), - MPIConnection( - std::make_tuple( - 2, 1, specfem::mesh_entity::dim3::type::right, - specfem::mesh_entity::dim3::type::bottom_front_right), - std::make_tuple( - 3, 2, specfem::mesh_entity::dim3::type::left, - specfem::mesh_entity::dim3::type::bottom_front_left)), - // Front - Back connections - MPIConnection( - std::make_tuple( - 0, 2, specfem::mesh_entity::dim3::type::back, - specfem::mesh_entity::dim3::type::bottom_back_left), - std::make_tuple( - 2, 0, specfem::mesh_entity::dim3::type::front, - specfem::mesh_entity::dim3::type::bottom_front_left)), - MPIConnection( - std::make_tuple( - 1, 3, specfem::mesh_entity::dim3::type::back, - specfem::mesh_entity::dim3::type::bottom_back_right), - std::make_tuple( - 3, 1, specfem::mesh_entity::dim3::type::front, - specfem::mesh_entity::dim3::type::bottom_front_right)), - // Diagonal Edge connections - MPIConnection( - std::make_tuple( - 0, 3, specfem::mesh_entity::dim3::type::back_right, - specfem::mesh_entity::dim3::type::bottom_back_right), - std::make_tuple( - 3, 0, specfem::mesh_entity::dim3::type::front_left, - specfem::mesh_entity::dim3::type::bottom_front_left)), - MPIConnection( - std::make_tuple( - 1, 2, specfem::mesh_entity::dim3::type::back_left, - specfem::mesh_entity::dim3::type::bottom_back_left), - std::make_tuple(2, 1, - specfem::mesh_entity::dim3::type::front_right, - specfem::mesh_entity::dim3::type:: - bottom_front_right)) }) } - }; - -TEST_P(Mesh3DTest, MPIAdjacencyGraph) { - const auto ¶m_name = GetParam(); - - // Skip cleanly if there is no MPI ground truth for this parameter. - const auto it = expected_mpi_adjacency_map.find(param_name); - if (it == expected_mpi_adjacency_map.end()) { - GTEST_SKIP() << "No MPI adjacency ground truth defined for test case: " - << param_name << std::endl; - return; - } - - // Treat MPI size requirement as an environmental constraint. - if (specfem::MPI::get_size() < 4) { - GTEST_SKIP() << "Test requires at least 4 MPI ranks. Current size: " - << specfem::MPI::get_size() << std::endl; - return; - } - - const auto &mesh = getMesh(); - const auto &adjacency_graph = mesh.adjacency_graph; - const auto &expected = it->second; - expected.check(adjacency_graph); -} diff --git a/tests/unit-tests/mesh/mpi/dim3/.gitignore b/tests/unit-tests/mesh/mpi/dim3/.gitignore new file mode 100644 index 000000000..46464549f --- /dev/null +++ b/tests/unit-tests/mesh/mpi/dim3/.gitignore @@ -0,0 +1 @@ +!test_fixture.hpp diff --git a/tests/unit-tests/mesh/mpi/dim3/adjacency_graph.cpp b/tests/unit-tests/mesh/mpi/dim3/adjacency_graph.cpp new file mode 100644 index 000000000..3759fc2b6 --- /dev/null +++ b/tests/unit-tests/mesh/mpi/dim3/adjacency_graph.cpp @@ -0,0 +1,215 @@ + +#include +#include +#include +#include +#include +#include + +#include "specfem/enums.hpp" +#include "specfem/mesh.hpp" +#include "specfem/mpi.hpp" +#include "test_fixture.hpp" + +namespace specfem::test_configuration { + +struct MPIConnection { + + constexpr static specfem::element::dimension_tag dimension = + specfem::element::dimension_tag::dim3; + struct EdgeProperties { + size_t rank; ///< MPI rank of the edge + size_t index; ///< Local element index associated with this edge + specfem::mesh_entity::dim3::type connection_id; + specfem::mesh_entity::dim3::type anchor; + + EdgeProperties(size_t rank, size_t index, + specfem::mesh_entity::dim3::type connection_id, + specfem::mesh_entity::dim3::type anchor) + : rank(rank), index(index), connection_id(connection_id), + anchor(anchor) {} + EdgeProperties( + const std::tuple &properties) + : EdgeProperties(std::get<0>(properties), std::get<1>(properties), + std::get<2>(properties), std::get<3>(properties)) {} + }; + + EdgeProperties edge1; ///< Properties of edge 1 in MPI connection + EdgeProperties edge2; ///< Properties of edge 2 in MPI connection + + MPIConnection(const EdgeProperties &edge1, const EdgeProperties &edge2) + : edge1(edge1), edge2(edge2) {} + + MPIConnection( + const std::tuple &edge1_properties, + const std::tuple &edge2_properties) + : edge1(edge1_properties), edge2(edge2_properties) {} + + bool expect_in(const std::vector::MPIEdgeProperties> &mpi_connections) const { + + const auto my_rank = static_cast(specfem::MPI::get_rank()); + if (my_rank != edge1.rank && my_rank != edge2.rank) { + return false; // This connection was not expected on this rank + } + + /// Check if the expected MPI connection exists in the list of MPI + /// connections + bool found = false; + for (const auto &mpi_edge : mpi_connections) { + if (my_rank == edge1.rank) { + // We are edge1, look for matching edge2 + if (mpi_edge.neighbor_partition == edge2.rank && + mpi_edge.orientation == edge1.connection_id && + mpi_edge.neighbor_orientation == edge2.connection_id && + mpi_edge.local_index == edge1.index && + mpi_edge.neighbor_local_index == edge2.index && + mpi_edge.local_anchor_point == edge1.anchor && + mpi_edge.neighbor_anchor_point == edge2.anchor) { + found = true; + break; + } + } else if (my_rank == edge2.rank) { + // We are edge2, look for matching edge1 + if (mpi_edge.neighbor_partition == edge1.rank && + mpi_edge.orientation == edge2.connection_id && + mpi_edge.neighbor_orientation == edge1.connection_id && + mpi_edge.local_index == edge2.index && + mpi_edge.neighbor_local_index == edge1.index && + mpi_edge.local_anchor_point == edge2.anchor && + mpi_edge.neighbor_anchor_point == edge1.anchor) { + found = true; + break; + } + } + } + + return found; + } +}; + +struct ExpectedMPIAdjacency3D { + constexpr static specfem::element::dimension_tag dimension = + specfem::element::dimension_tag::dim3; + + std::vector mpi_connections; ///< List of expected MPI + ///< connections + + ExpectedMPIAdjacency3D( + const std::initializer_list &mpi_connections) + : mpi_connections(mpi_connections) {} + + void check( + const specfem::mesh::adjacency_graph &adjacency_graph) const { + const auto &mpi_conns = adjacency_graph.mpi_connections(); + for (const auto &expected_mpi_conn : mpi_connections) { + auto local_found = expected_mpi_conn.expect_in(mpi_conns); + int local_found_int = local_found ? 1 : 0; + int found_int = 0; + SPECFEM_MPI_SAFECALL(MPI_Reduce(&local_found_int, &found_int, 1, MPI_INT, + MPI_LOR, 0, + specfem::MPI::communicator())); + bool found = (found_int != 0); + + if (!found) { + SPECFEM_MPI_ON_ROOT({ + std::ostringstream msg; + msg << "Failed expected MPI adjacency between rank " + << expected_mpi_conn.edge1.rank << " (element " + << expected_mpi_conn.edge1.index << ", connection ID " + << static_cast(expected_mpi_conn.edge1.connection_id) + << ", anchor " << static_cast(expected_mpi_conn.edge1.anchor) + << ") and rank " << expected_mpi_conn.edge2.rank << " (element " + << expected_mpi_conn.edge2.index << ", connection ID " + << static_cast(expected_mpi_conn.edge2.connection_id) + << ", anchor " << static_cast(expected_mpi_conn.edge2.anchor) + << ")\n"; + msg << " No matching MPI connection found in adjacency graph.\n"; + ADD_FAILURE() << msg.str(); + }); + } + + SPECFEM_MPI_SAFECALL(MPI_Barrier(specfem::MPI::communicator())); + } + SPECFEM_MPI_ON_ROOT({ + SUCCEED() << "All expected MPI connections are present and correct." + << std::endl; + }); + } +}; + +} // namespace specfem::test_configuration + +using namespace specfem::test_configuration; + +static const std::unordered_map + expected_mpi_adjacency_map = { + { "HomogeneousMediumMPI4x4", + ExpectedMPIAdjacency3D( + { // Left - Right connections + MPIConnection( + std::make_tuple( + 0, 1, specfem::mesh_entity::dim3::type::right, + specfem::mesh_entity::dim3::type::bottom_front_right), + std::make_tuple( + 1, 0, specfem::mesh_entity::dim3::type::left, + specfem::mesh_entity::dim3::type::bottom_front_left)), + MPIConnection( + std::make_tuple( + 2, 1, specfem::mesh_entity::dim3::type::right, + specfem::mesh_entity::dim3::type::bottom_front_right), + std::make_tuple( + 3, 0, specfem::mesh_entity::dim3::type::left, + specfem::mesh_entity::dim3::type::bottom_front_left)), + // Front - Back connections + MPIConnection( + std::make_tuple( + 0, 2, specfem::mesh_entity::dim3::type::back, + specfem::mesh_entity::dim3::type::bottom_back_left), + std::make_tuple( + 2, 0, specfem::mesh_entity::dim3::type::front, + specfem::mesh_entity::dim3::type::bottom_front_left)), + MPIConnection( + std::make_tuple( + 1, 3, specfem::mesh_entity::dim3::type::back, + specfem::mesh_entity::dim3::type::bottom_back_left), + std::make_tuple( + 3, 1, specfem::mesh_entity::dim3::type::front, + specfem::mesh_entity::dim3::type::bottom_front_left)), + // Diagonal Edge connections + MPIConnection( + std::make_tuple( + 0, 3, specfem::mesh_entity::dim3::type::back_right, + specfem::mesh_entity::dim3::type::bottom_back_right), + std::make_tuple( + 3, 0, specfem::mesh_entity::dim3::type::front_left, + specfem::mesh_entity::dim3::type::bottom_front_left)), + MPIConnection( + std::make_tuple( + 1, 2, specfem::mesh_entity::dim3::type::back_left, + specfem::mesh_entity::dim3::type::bottom_back_left), + std::make_tuple(2, 1, + specfem::mesh_entity::dim3::type::front_right, + specfem::mesh_entity::dim3::type:: + bottom_front_right)) }) } + }; + +TEST_P(MPIMesh3DTest, MPIAdjacencyGraph) { + const auto ¶m_name = GetParam(); + + // Skip cleanly if there is no MPI ground truth for this parameter. + const auto it = expected_mpi_adjacency_map.find(param_name); + if (it == expected_mpi_adjacency_map.end()) { + GTEST_SKIP() << "No MPI adjacency ground truth defined for test case: " + << param_name << std::endl; + return; + } + + const auto &mesh = getMesh(); + const auto &adjacency_graph = mesh.adjacency_graph; + const auto &expected = it->second; + expected.check(adjacency_graph); +} diff --git a/tests/unit-tests/mesh/mpi/dim3/runner.cpp b/tests/unit-tests/mesh/mpi/dim3/runner.cpp new file mode 100644 index 000000000..e024c5c19 --- /dev/null +++ b/tests/unit-tests/mesh/mpi/dim3/runner.cpp @@ -0,0 +1,14 @@ +#include "../../../SPECFEM_Environment.hpp" + +#include "test_fixture.hpp" +#include +#include + +INSTANTIATE_TEST_SUITE_P(MPIMesh3DTests, MPIMesh3DTest, + ::testing::Values("HomogeneousMediumMPI4x4")); + +int main(int argc, char *argv[]) { + ::testing::InitGoogleTest(&argc, argv); + ::testing::AddGlobalTestEnvironment(new SPECFEMEnvironment(4)); + return RUN_ALL_TESTS(); +} diff --git a/tests/unit-tests/mesh/mpi/dim3/test_fixture.hpp b/tests/unit-tests/mesh/mpi/dim3/test_fixture.hpp new file mode 100644 index 000000000..9b9a9fb83 --- /dev/null +++ b/tests/unit-tests/mesh/mpi/dim3/test_fixture.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include "SPECFEM_Environment.hpp" +#include "specfem/enums.hpp" +#include "specfem/io.hpp" +#include "specfem/mesh.hpp" +#include "specfem/mpi.hpp" +#include +#include + +namespace specfem::test_configuration { +struct ActualMesh3D { + constexpr static specfem::element::dimension_tag dimension = + specfem::element::dimension_tag::dim3; + specfem::mesh::mesh mesh; + + ActualMesh3D() = default; + + ActualMesh3D(const std::string &database) { + mesh = specfem::io::read_3d_mesh(database); + } +}; +} // namespace specfem::test_configuration + +// Setup a fixture for parameterized tests +class MPIMesh3DTest : public ::testing::TestWithParam { +protected: + constexpr static specfem::element::dimension_tag dimension = + specfem::element::dimension_tag::dim3; + + specfem::test_configuration::ActualMesh3D mesh; + + MPIMesh3DTest() = default; + + void SetUp() override { + // Check if MPI size requirement was met during environment setup + if (!SPECFEMEnvironment::IsMPISizeValid()) { + GTEST_SKIP() << SPECFEMEnvironment::GetMPISizeError(); + } + + // Check if the current rank is within the participating range for this test + if (specfem::MPI::world_communicator() == MPI_COMM_NULL) { + GTEST_SKIP() << "Test designed for 4 processes. Rank " + << specfem::MPI::get_rank() + << " is outside the participating range [0-3]."; + } + const auto &folder = GetParam(); + const std::string database = "data/mpi/dim3/" + folder + "/Database.bin"; + const auto mpi_database = specfem::MPI::format_proc_filename(database); + mesh = specfem::test_configuration::ActualMesh3D(mpi_database); + } + void TearDown() override { + // Any cleanup needed for each test + } + + ~MPIMesh3DTest() override = default; + + // Accessor for the mesh + const auto &getMesh() const { return mesh.mesh; } +}; diff --git a/tests/unit-tests/mpi.cmake b/tests/unit-tests/mpi.cmake new file mode 100644 index 000000000..6959979e8 --- /dev/null +++ b/tests/unit-tests/mpi.cmake @@ -0,0 +1,81 @@ +# ============================================================================== +# Helper function to register an MPI test with configurable processor count +# and test properties +# ============================================================================== +# Usage: add_mpi_test(TEST_NAME NUM_PROCESSES) +# Example: add_mpi_test(mesh_mpi_dim3_tests 4) +function(add_mpi_test TEST_NAME NUM_PROCESSES) + message(STATUS "Registering MPI test: ${TEST_NAME} with ${NUM_PROCESSES} processes") + + # Create test command with MPI launcher + add_test( + NAME ${TEST_NAME} + COMMAND ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} ${NUM_PROCESSES} + ${CMAKE_BINARY_DIR}/tests/unit-tests/${TEST_NAME} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/tests/unit-tests + ) + + # Set test properties for resource management and parallelization + set_tests_properties(${TEST_NAME} PROPERTIES + # Number of processors reserved for this test + PROCESSORS ${NUM_PROCESSES} + # Timeout (in seconds) - adjust as needed + TIMEOUT 300 + # Run serially if multiple processes to avoid compute conflicts + RUN_SERIAL ON + ) +endfunction() + +# MPI-only test executables + +add_executable( + mesh_mpi_dim3_tests + mesh/mpi/dim3/adjacency_graph.cpp + mesh/mpi/dim3/runner.cpp +) + +target_link_libraries( + mesh_mpi_dim3_tests + specfem::mesh + specfem_environment + specfem::io + specfem::utilities + MPI::MPI_CXX + -lpthread -lm +) + +add_executable( + io_mesh_mpi_tests + io/mesh/read_mesh.cpp + io/mesh/runner.cpp +) + +target_link_libraries( + io_mesh_mpi_tests + specfem::io + specfem::mesh + specfem_environment + specfem::utilities + MPI::MPI_CXX + -lpthread -lm +) + +# Register MPI tests using helper function +# Format: add_mpi_test(test_name num_processes) +enable_testing() + +add_mpi_test(mesh_mpi_dim3_tests 4) +add_mpi_test(io_mesh_mpi_tests 4) + +# Link test data directories for MPI tests +set(MPI_LINK_DIRS + data + mesh +) + +foreach(dir_name IN LISTS MPI_LINK_DIRS) + file(CREATE_LINK + ${CMAKE_CURRENT_SOURCE_DIR}/${dir_name} + ${CMAKE_CURRENT_BINARY_DIR}/${dir_name} + SYMBOLIC) +endforeach() diff --git a/tests/unit-tests/serial.cmake b/tests/unit-tests/serial.cmake new file mode 100644 index 000000000..a571f2a81 --- /dev/null +++ b/tests/unit-tests/serial.cmake @@ -0,0 +1,858 @@ +# Serial (non-MPI) test definitions and setup +# This file contains all non-MPI test executables and their test discovery registration. + +# Test framework setup for serial tests +include(GoogleTest) + +# Non-MPI test executables + +add_executable( + test_mesh_utilities_mapping_2d + mesh_utilities/test_mapping_2d.cpp +) + +target_link_libraries( + test_mesh_utilities_mapping_2d + mesh_utilities_mapping + specfem::utilities + gtest_main +) + +add_executable( + test_mesh_utilities_mapping_3d + mesh_utilities/test_mapping_3d.cpp +) + +target_link_libraries( + test_mesh_utilities_mapping_3d + mesh_utilities_mapping + specfem::utilities + gtest_main +) + +add_executable( + io_framework_tests + io/io_framework_tests.cpp +) + +target_link_libraries(io_framework_tests PRIVATE + specfem::io + ${BOOST_LIBS} + gtest_main + Kokkos::kokkos + specfem_environment + $<$:zlib> + $<$:hdf5> + $<$:adios2> +) + +target_compile_definitions( + io_framework_tests + PUBLIC + $<$>:-DNO_NPZ> + $<$>:-DNO_HDF5> + $<$>:-DNO_ADIOS2> +) + +add_executable( + abort_tests + program/abort_tests.cpp +) + +target_link_libraries( + abort_tests + specfem::program + gtest_main + Kokkos::kokkos +) + +add_executable( + is_close_tests + utilities/is_close_tests.cpp +) + +target_link_libraries( + is_close_tests + specfem::utilities + gtest_main + Kokkos::kokkos +) + +add_executable( + logspace_tests + utilities/logspace_tests.cpp + utilities/logarithmic_center_tests.cpp + utilities/band_tests.cpp +) + +target_link_libraries( + logspace_tests + specfem::utilities + specfem_environment + gtest_main + Kokkos::kokkos +) + +add_executable( + units_tests + units/quantity_tests.cpp + units/unit_cast_tests.cpp +) + +target_link_libraries( + units_tests + specfem::utilities + gtest_main + Kokkos::kokkos +) + +add_executable( + attenuation_tests + attenuation/runner.cpp + attenuation/compute_band_tests.cpp + attenuation/compute_tau_sigma_tests.cpp + attenuation/maxwell_tests.cpp + attenuation/compute_tau_eps_tests.cpp + attenuation/compute_factors_tests.cpp + attenuation/compute_integration_factors_tests.cpp +) + +target_link_libraries( + attenuation_tests + gtest_main + specfem::attenuation + Kokkos::kokkos +) + +add_executable( + optimization_tests + optimization/runner.cpp + optimization/neldermead_tests.cpp + optimization/steepestdescent_tests.cpp +) + +target_link_libraries( + optimization_tests + gtest_main + Kokkos::kokkos +) + +add_executable( + gll_tests + gll/gll_tests.cpp +) + +target_link_libraries( + gll_tests + gtest_main + specfem::quadrature + specfem_environment + point + -lpthread -lm +) + +add_executable( + lagrange_tests + lagrange/Lagrange_tests.cpp +) + +target_link_libraries( + lagrange_tests + gtest_main + specfem::quadrature + specfem_environment + -lpthread -lm +) + +add_executable( + jacobian_tests + jacobian/jacobian_tests.cpp + jacobian/dim2/shape_functions_tests.cpp + jacobian/dim2/compute_locations_tests.cpp + jacobian/dim2/compute_jacobian_tests.cpp + jacobian/dim3/shape_functions_tests.cpp + jacobian/dim3/compute_locations_tests.cpp + jacobian/dim3/compute_jacobian_tests.cpp +) + +target_link_libraries( + jacobian_tests + jacobian + specfem_environment + gtest_main + -lpthread -lm +) + +add_executable( + enumerations_tests + enumerations/dim2/mesh_entity.cpp + enumerations/dim2/connections.cpp + enumerations/dim3/mesh_entity.cpp + enumerations/dim3/connections.cpp + enumerations/runner.cpp +) + +target_link_libraries( + enumerations_tests + specfem::enums + specfem::quadrature + shape_functions + gtest_main + specfem_environment + specfem::utilities + -lpthread -lm +) + +set_target_properties(enumerations_tests PROPERTIES UNITY_BUILD OFF) + +add_executable( + simd_tests + datatype/simd_tests.cpp +) + +target_link_libraries( + simd_tests + gtest_main + gmock_main + Kokkos::kokkos + -lpthread -lm +) + +add_executable( + fortranio_test + fortran_io/fortranio_tests.cpp +) + +target_link_libraries( + fortranio_test + gtest_main + gmock_main + specfem::io + -lpthread -lm +) + +add_executable( + point_tests + point/index_tests.cpp + point/coordinates_tests.cpp + point/boundary_tests.cpp + point/jacobian_matrix_tests.cpp + point/attenuation_tests.cpp + point/source_tests.cpp + point/stress_integrand_tests.cpp + point/stress_tests.cpp + # Kernels + # Dim 2 + point/kernels/dim2/acoustic_isotropic.cpp + point/kernels/dim2/elastic_isotropic.cpp + point/kernels/dim2/elastic_anisotropic.cpp + # point/kernels/dim2/poroelastic_isotropic.cpp + # Dim 3 + point/kernels/dim3/elastic_isotropic.cpp + # Properties + # Dim 2 + point/properties/dim2/elastic_isotropic.cpp + point/properties/dim2/elastic_anisotropic.cpp + point/properties/dim2/acoustic_isotropic.cpp + point/properties/dim2/elastic_isotropic_cosserat.cpp + point/properties/dim2/electromagnetic_isotropic.cpp + point/properties/dim2/poroelastic_isotropic.cpp + # Dim 3 + point/properties/dim3/elastic_isotropic.cpp + point/properties/dim3/elastic_isotropic_cosserat.cpp +) + +target_link_libraries( + point_tests + point + specfem_environment + gtest_main + gmock_main +) + +add_executable( + receivers_tests + receivers/dim2/receiver_tests.cpp + receivers/dim3/receiver_tests.cpp +) + +target_link_libraries( + receivers_tests + gtest_main + specfem::receivers + specfem_environment + yaml-cpp + ${BOOST_LIBS} + -lpthread -lm +) + +add_executable( + mesh_dim2_tests + mesh/dim2/test_fixture/test_fixture.cpp + mesh/dim2/materials/materials.cpp + mesh/dim2/materials/properties.cpp + mesh/dim2/runner.cpp + mesh/dim2/adjacency_graph/adjacency_graph.cpp + mesh/dim2/adjacency_graph/adjacency_graph_regular_mesh.cpp + mesh/dim2/adjacency_graph/adjacency_graph_irregular_mesh.cpp +) + +target_link_libraries( + mesh_dim2_tests + gtest_main + specfem::mesh + specfem_environment + yaml-cpp + specfem::io + specfem::utilities + -lpthread -lm +) + +add_executable( + mesh_dim3_tests + mesh/dim3/mesh.cpp + mesh/dim3/control_nodes.cpp + mesh/dim3/materials.cpp + mesh/dim3/boundaries.cpp + mesh/dim3/adjacency_graph.cpp + mesh/dim3/tags.cpp + mesh/dim3/test.cpp +) + +target_link_libraries( + mesh_dim3_tests + gtest_main + specfem::mesh + specfem_environment + specfem::io + specfem::utilities + -lpthread -lm +) + +add_executable( + nonconforming_tests + nonconforming/reparameterizations/compute_intersection_test.cpp + nonconforming/reparameterizations/set_transfer_functions_test.cpp + nonconforming/runner.cpp +) + +target_link_libraries( + nonconforming_tests + specfem::mesh + specfem::assembly + specfem::quadrature + specfem_environment + yaml-cpp + specfem::utilities + ${BOOST_LIBS} + -lpthread -lm + gtest_main +) + +add_executable( + nonconforming_assembly_tests + assembly/runner.cpp + assembly/nonconforming_interfaces/container_init_test.cpp +) + +target_link_libraries( + nonconforming_assembly_tests + specfem::io + specfem::mesh + specfem::assembly + specfem::quadrature + specfem_environment + yaml-cpp + specfem::utilities + ${BOOST_LIBS} + -lpthread -lm + gtest_main +) + +add_executable( + assembly_tests + assembly/test_fixture/test_fixture.cpp + assembly/runner.cpp + assembly/kernels/kernels.cpp + assembly/properties/properties.cpp + assembly/compute_wavefield/compute_wavefield.cpp + assembly/sources/sources.cpp + assembly/check_jacobian/dim2/check_jacobian.cpp + assembly/locate/locate_point.cpp + assembly/locate/locate_point_on_edge.cpp + assembly/mesh/utilities.cpp + assembly/sources/locate_sources.cpp + assembly/compute_source_array/dim2/compute_source_array_from_vector.cpp + assembly/compute_source_array/dim2/compute_source_array_from_tensor.cpp + assembly/compute_source_array/dim3/compute_source_array_from_vector.cpp + assembly/compute_source_array/dim3/compute_source_array_from_tensor.cpp + assembly/info/compute_tests.cpp + assembly/info/scatter_minmax_tests.cpp + assembly/element_intersections/edge_types_tests.cpp + assembly/element_intersections/face_types_tests.cpp + assembly/dim3/mesh/shape_functions.cpp + assembly/dim3/mesh/points.cpp + assembly/dim3/mesh/control_nodes.cpp + assembly/dim3/jacobian_matrix/jacobian_matrix.cpp + assembly/dim3/properties/properties.cpp +) + +target_compile_definitions(assembly_tests PRIVATE TEST_OUTPUT_DIR=${TEST_OUTPUT_DIR}) +set_target_properties(assembly_tests PROPERTIES UNITY_BUILD OFF) + +target_link_libraries( + assembly_tests + specfem::mesh + specfem::assembly + specfem::quadrature + specfem_environment + specfem::io + yaml-cpp + specfem::utilities + specfem::enums + specfem::element + shape_functions + specfem::quadrature + ${BOOST_LIBS} + -lpthread -lm + gtest_main +) + +add_executable( + assembly_receivers_tests + assembly/receivers/receivers_tests.cpp + assembly/receivers/dim2/receivers_tests.cpp + assembly/receivers/impl/receiver_iterator_tests.cpp + assembly/receivers/impl/dim2/seismogram_iterator_tests.cpp +) + +target_link_libraries( + assembly_receivers_tests + specfem::mesh + specfem::assembly + specfem::quadrature + specfem_environment + yaml-cpp + specfem::io + ${BOOST_LIBS} + gtest + gtest_main + -lpthread -lm +) + +add_executable( + io_tests + io/sources/test_read_sources_file.cpp + io/sources/test_read_sources_yaml.cpp + io/sources/test_source_solutions.cpp + io/receivers/test_receiver_solutions.cpp + io/receivers/test_read_stations_file.cpp + io/receivers/test_read_yaml.cpp +) + +target_link_libraries( + io_tests + specfem::io + gtest_main + yaml-cpp + specfem::enums + ${BOOST_LIBS} +) + +add_executable( + interpolate_function + algorithms/interpolate_function/dim2/interpolate_function.cpp + algorithms/interpolate_function/dim3/interpolate_function.cpp + algorithms/interpolate_function/runner.cpp +) + +target_link_libraries( + interpolate_function + specfem::quadrature + specfem_environment + specfem::algorithms + specfem::io + ${BOOST_LIBS} + point +) + +add_executable( + locate_point_fixture_2d + algorithms/dim2/locate_point_fixture.cpp + algorithms/dim2/locate_point_test.cpp + algorithms/dim2/locate_point_on_edge_test.cpp +) + +target_link_libraries( + locate_point_fixture_2d + mesh_utilities_mapping + specfem::algorithms + jacobian + point + specfem::utilities + specfem_environment + gtest_main +) + +add_executable( + locate_point_fixture_3d + algorithms/dim3/locate_point_fixture.cpp +) + +target_link_libraries( + locate_point_fixture_3d + mesh_utilities_mapping + specfem::algorithms + jacobian + point + specfem::utilities + specfem_environment + gtest_main +) + +add_executable( + gradient_tests + algorithms/dim2/gradient.cpp + algorithms/dim3/gradient.cpp +) + +target_link_libraries( + gradient_tests + specfem::quadrature + specfem::algorithms + gtest_main + Kokkos::kokkos + specfem_environment + specfem::assembly + specfem::utilities + -lpthread -lm +) + +set_target_properties(gradient_tests PROPERTIES UNITY_BUILD OFF) + +add_executable( + transfer_tests + algorithms/dim2/transfer.cpp +) + +target_link_libraries( + transfer_tests + specfem::algorithms + specfem::enums + gtest_main + Kokkos::kokkos + specfem_environment + specfem::utilities + -lpthread -lm +) + +add_executable( + coupling_integral_tests + algorithms/dim2/coupling_integral/dshape.cpp + algorithms/dim2/coupling_integral/timesshape.cpp +) + +target_link_libraries( + coupling_integral_tests + specfem::algorithms + specfem::element_connections + specfem::mesh + assembly + gtest_main + Kokkos::kokkos + specfem_environment + specfem::utilities + specfem::quadrature + yaml-cpp + -lpthread -lm +) + +add_executable( + policies_tests + policies/policies.cpp +) + +target_link_libraries( + policies_tests + specfem::mesh + source_class + specfem::receivers + specfem_environment + yaml-cpp + ${BOOST_LIBS} + -lpthread -lm +) + +add_executable( + chunked_edge_tests + policies/chunked_edge.cpp +) + +target_link_libraries( + chunked_edge_tests + gtest_main + Kokkos::kokkos + specfem_environment + specfem::assembly + specfem::enums + ${BOOST_LIBS} + -lpthread -lm +) + +add_executable( + chunked_face_tests + policies/chunked_face.cpp +) + +target_link_libraries( + chunked_face_tests + gtest_main + Kokkos::kokkos + specfem_environment + specfem::enums + point + -lpthread -lm +) + +add_executable( + chunked_face_intersection_tests + policies/chunked_face_intersection.cpp +) + +target_link_libraries( + chunked_face_intersection_tests + gtest_main + Kokkos::kokkos + specfem_environment + specfem::enums + point + -lpthread -lm +) + +add_executable( + mass_matrix_tests + medium/mass_matrix/main.cpp + medium/mass_matrix/dim2/elastic_isotropic.cpp + medium/mass_matrix/dim2/elastic_anisotropic.cpp + medium/mass_matrix/dim2/acoustic.cpp + medium/mass_matrix/dim2/poroelastic.cpp + medium/mass_matrix/dim3/elastic_isotropic.cpp + medium/mass_matrix/dim3/acoustic.cpp +) + +target_link_libraries( + mass_matrix_tests + point + gtest_main +) + +add_executable( + stress_tests + medium/stress/main.cpp + medium/stress/dim2/acoustic.cpp + medium/stress/dim2/elastic_isotropic.cpp + medium/stress/dim2/elastic_anisotropic.cpp + medium/stress/dim2/elastic_isotropic_cosserat.cpp + medium/stress/dim2/poroelastic_isotropic.cpp + medium/stress/dim3/elastic_isotropic.cpp + medium/stress/dim3/acoustic.cpp +) + +target_link_libraries( + stress_tests + point + gtest_main +) + +add_executable( + strain_tests + medium/strain/main.cpp + medium/strain/dim2/elastic_isotropic.cpp + medium/strain/dim3/elastic_isotropic.cpp +) + +target_link_libraries( + strain_tests + point + gtest_main +) + +add_executable( + compute_coupling_tests + compute_coupling/acoustic_elastic.cpp + compute_coupling/elastic_acoustic.cpp + compute_coupling/nonconforming/acoustic_elastic.cpp + compute_coupling/nonconforming/elastic_acoustic.cpp + compute_coupling/runner.cpp +) + +target_link_libraries( + compute_coupling_tests + point + gtest + specfem_environment + Kokkos::kokkos +) + +add_executable( + source_tests + medium/source/main.cpp + medium/source/dim2/acoustic.cpp + medium/source/dim2/elastic_isotropic.cpp + medium/source/dim2/elastic_anisotropic.cpp + medium/source/dim2/elastic_isotropic_cosserat.cpp + medium/source/dim2/poroelastic.cpp + medium/source/dim3/elastic_isotropic.cpp + medium/source/dim3/acoustic.cpp +) + +target_link_libraries( + source_tests + point + gtest_main +) + +add_executable( + source_class_tests + source/source.cpp + source/base_source_tests.cpp + source/typed_source_tests.cpp +) + +target_link_libraries( + source_class_tests + source_class + source_time_functions + specfem_environment + -lpthread -lm +) + +add_executable( + source_time_function_tests + source_time_function/source_time_function_tests.cpp +) + +target_link_libraries( + source_time_function_tests + source_time_functions + Kokkos::kokkos + yaml-cpp + gtest_main +) + +add_executable( + displacement_newmark_2d_tests + displacement_tests/Newmark/dim2/newmark_tests.cpp +) + +target_link_libraries( + displacement_newmark_2d_tests + specfem::quadrature + specfem::mesh + yaml-cpp + specfem_environment + specfem::assembly + specfem::runtime_configuration + timescheme + point + specfem::algorithms + specfem::solver + specfem::periodic_tasks + ${BOOST_LIBS} + -lpthread -lm +) + +add_executable( + displacement_newmark_3d_tests + displacement_tests/Newmark/dim3/newmark_tests.cpp +) + +target_link_libraries( + displacement_newmark_3d_tests + specfem::quadrature + specfem::mesh + yaml-cpp + specfem_environment + specfem::assembly + specfem::runtime_configuration + timescheme + point + specfem::algorithms + specfem::solver + specfem::periodic_tasks + ${BOOST_LIBS} + -lpthread -lm +) + +# Register serial tests for discovery +set(SERIAL_TEST_TARGETS + abort_tests + assembly_receivers_tests + assembly_tests + attenuation_tests + chunked_edge_tests + chunked_face_tests + chunked_face_intersection_tests + compute_coupling_tests + displacement_newmark_2d_tests + displacement_newmark_3d_tests + fortranio_test + enumerations_tests + gll_tests + interpolate_function + io_framework_tests + io_tests + is_close_tests + logspace_tests + jacobian_tests + lagrange_tests + locate_point_fixture_2d + locate_point_fixture_3d + gradient_tests + transfer_tests + mass_matrix_tests + mesh_dim2_tests + mesh_dim3_tests + nonconforming_tests + point_tests + policies_tests + optimization_tests + receivers_tests + simd_tests + source_class_tests + source_tests + source_time_function_tests + strain_tests + stress_tests + test_mesh_utilities_mapping_2d + test_mesh_utilities_mapping_3d + units_tests +) + +foreach(test_target IN LISTS SERIAL_TEST_TARGETS) + gtest_discover_tests(${test_target} + DISCOVERY_MODE POST_BUILD + DISCOVERY_TIMEOUT 300 + ) +endforeach() + +# Link test data directories for serial tests +set(SERIAL_LINK_DIRS + algorithms + assembly + assembly_mesh + data + displacement_tests + domain + fortran_io + io + mesh + policies +) + +foreach(dir_name IN LISTS SERIAL_LINK_DIRS) + file(CREATE_LINK + ${CMAKE_CURRENT_SOURCE_DIR}/${dir_name} + ${CMAKE_CURRENT_BINARY_DIR}/${dir_name} + SYMBOLIC) +endforeach()