Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions build_test_sfincs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
cmake -B cmake_build -S . \
-DNGEN_WITH_BMI_FORTRAN=ON \
-DNGEN_BUILD_COASTAL_TESTS=ON \
-DNGEN_ENABLE_SCHISM=OFF \
-DSFINCS_BMI_LIBRARY=/home/mohammed.karim/Calibration/ngen/extern/SFINCS/source/src/build/libsfincs_bmi.so \
-DSFINCS_INIT_CONFIG=/home/mohammed.karim/Calibration/ngen/extern/SFINCS/source/src/build/sfincs_config.txt
cmake --build cmake_build -j
ctest --test-dir cmake_build -N | grep -i sfincs
ctest --test-dir cmake_build -R sfincs -V

21 changes: 21 additions & 0 deletions include/realizations/coastal/SfincsCreator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef SFINCS_CREATOR_HEADER
#define SFINCS_CREATOR_HEADER

#include "realizations/coastal/ModelCreator.h"
#include "realizations/coastal/Coastal_Config_Params.h"

class SfincsCreator : public ModelCreator {
public:
std::unique_ptr<CoastalFormulation>
createCoastalFormulation(coastal_config_params const&,
Simulation_Time const&) const override;

SfincsCreator* clone() const override;

private:
void writeInitConfig(coastal_config_params const&,
Simulation_Time const&) const;
};

#endif // SFINCS_CREATOR_HEADER

94 changes: 94 additions & 0 deletions include/realizations/coastal/SfincsFormulation.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#pragma once

#include <string>
#include <vector>
#include <memory>

#include <boost/core/span.hpp>

#include "NGenConfig.h"

#include "realizations/coastal/CoastalFormulation.hpp"

// BMI Fortran adapter (correct include + namespace)
#if NGEN_WITH_BMI_FORTRAN
#include "bmi/Bmi_Fortran_Adapter.hpp"
#endif

/**
* SfincsFormulation
*
* Mirrors the structure of SchismFormulation:
* - derives from CoastalFormulation (which derives MeshPointsDataProvider)
* - provides required DataProvider<> pure virtuals
* - owns a BMI adapter instance
* - optionally consumes met/offshore/channel boundary providers
*/
class SfincsFormulation final : public CoastalFormulation
{
public:
// Match SchismFormulation style typedefs
using ProviderType = data_access::MeshPointsDataProvider;
using ProviderPtr = std::shared_ptr<ProviderType>;

SfincsFormulation(std::string model_id,
std::string library_file,
std::string init_config,
ProviderPtr met_provider,
ProviderPtr offshore_provider,
ProviderPtr channel_provider);

~SfincsFormulation() override;

// --- BMI lifecycle ---
void initialize() override;
void finalize() override;
void update() override;
void update_until(double const& t) override;

// --- Time ---
double get_current_time() override;
double get_start_time() override;
double get_end_time() override;
double get_time_step() override;

// --- MeshPointsDataProvider interface (from MeshPointsDataProvider) ---
// Required by CoastalFormulation (pure virtual)
void get_values(const selection_type& selector, boost::span<double> data) override;

// Optional convenience overload (NOT override)
void get_values(const selection_type& selector, std::vector<double>& out);

std::size_t mesh_size(const std::string& mesh_name) override;

// --- DataProvider<double, MeshPointsSelector> pure virtuals ---
boost::span<const std::string> get_available_variable_names() const override;
long get_data_start_time() const override;
long get_data_stop_time() const override;
long record_duration() const override;
std::size_t get_ts_index_for_time(const time_t& epoch_time) const override;
data_type get_value(const selection_type& selector, data_access::ReSampleMethod m=data_access::SUM) override;

private:
void create_formulation_();
void destroy_formulation_();

// Optional: push forcing into BMI vars (keep minimal for now)
void set_inputs_();

private:
std::string model_id_;
std::string library_file_;
std::string init_config_;

ProviderPtr met_provider_;
ProviderPtr offshore_provider_;
ProviderPtr channel_provider_;

std::vector<std::string> available_vars_;

#if NGEN_WITH_BMI_FORTRAN
std::unique_ptr<models::bmi::Bmi_Fortran_Adapter> bmi_;
#endif
};

26 changes: 18 additions & 8 deletions src/NGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@
#include <Catchment_Formulation.hpp>
#include <HY_Features.hpp>
#include "realizations/coastal/ModelCreatorRegistry.h"

#if NGEN_ENABLE_SCHISM
#include "realizations/coastal/SchismCreator.h"
#endif

#include "realizations/coastal/SfincsCreator.h"

#if NGEN_WITH_SQLITE3
#include <geopackage.hpp>
Expand Down Expand Up @@ -51,7 +56,7 @@ bool is_subdivided_hydrofabric_wanted = false;
#include "core/Partition_Parser.hpp"
#include <HY_Features_MPI.hpp>

#include "core/Partition_One.hpp"
#include "core/Partition_One.hpp>"

std::string PARTITION_PATH = "";
#endif // NGEN_WITH_MPI
Expand Down Expand Up @@ -608,25 +613,30 @@ int main(int argc, char *argv[]) {

// create the factory registry
ModelCreatorRegistry &registry = ModelCreatorRegistry::getInstance();
// add the Schism factory to the registry

// register all supported coastal models
#if NGEN_ENABLE_SCHISM
registry.registerCreator(ModelType::SCHISM, std::make_unique<SchismCreator>());
// add more factories for coastal models, e.g. sfincs
//....

// retrieve the creator for the model selected
#endif

registry.registerCreator(ModelType::SFINCS, std::make_unique<SfincsCreator>()); //

// retrieve the creator for the model selected in the config
std::unique_ptr<ModelCreator> coastal_creator =
registry.getCreator(coastal_conf->getModelType());
// now run the schism model

// execute the selected coastal model (SCHISM or SFINCS)
coastal_creator->executeModel( *coastal_conf,
*(manager->Simulation_Time_Object) );

}

manager->finalize();
manager->finalize();

#if NGEN_WITH_MPI
MPI_Finalize();
#endif

return 0;
}

200 changes: 182 additions & 18 deletions src/realizations/coastal/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,23 +1,187 @@
include(${PROJECT_SOURCE_DIR}/cmake/dynamic_sourced_library.cmake)
dynamic_sourced_cxx_library(realizations_coastal "${CMAKE_CURRENT_SOURCE_DIR}")
cmake_minimum_required(VERSION 3.19)

# If we are configured standalone in this directory, define a minimal project so CMake is happy.
if(NOT DEFINED PROJECT_NAME)
project(ngen_coastal LANGUAGES C CXX)
endif()

option(NGEN_ENABLE_SCHISM "Enable SCHISM coastal model support" OFF)

if (NGEN_ENABLE_SCHISM)
add_compile_definitions(NGEN_ENABLE_SCHISM=1)
else()
add_compile_definitions(NGEN_ENABLE_SCHISM=0)
endif()


# Try to infer the repo root when configured from src/realizations/coastal
# ngen root is 3 levels up from here: ngen/src/realizations/coastal
set(NGEN_TOP "${CMAKE_CURRENT_LIST_DIR}/../../.." CACHE PATH "Path to ngen repository root")

# ---------------------------------------------------------------------------
# Bring in the helper that defines dynamic_sourced_cxx_library, if available
# When configuring from the repo root, PROJECT_SOURCE_DIR will already be the top.
# Here we use NGEN_TOP so this file also works standalone.
# ---------------------------------------------------------------------------
set(_DYNAMIC_LIB_HELPER "${NGEN_TOP}/cmake/dynamic_sourced_library.cmake")
if(EXISTS "${_DYNAMIC_LIB_HELPER}")
include("${_DYNAMIC_LIB_HELPER}")
else()
message(STATUS "dynamic_sourced_library.cmake not found at: ${_DYNAMIC_LIB_HELPER}")
message(STATUS "This coastal CMakeLists is meant to be driven from the repo root.")
message(STATUS "Continuing, but you must provide equivalent targets manually or configure from ${NGEN_TOP}")
endif()

# ---------------------------------------------------------------------------
# Library: realizations_coastal
# ---------------------------------------------------------------------------

# Only create the library via the helper if it exists
if(COMMAND dynamic_sourced_cxx_library)
include("${NGEN_TOP}/cmake/dynamic_sourced_library.cmake") # ensure scope
dynamic_sourced_cxx_library(realizations_coastal "${CMAKE_CURRENT_SOURCE_DIR}")
else()
# Fallback: collect all .cpp files in this directory tree and build a static lib
file(GLOB_RECURSE REALIZATIONS_COASTAL_SRC CONFIGURE_DEPENDS
"${CMAKE_CURRENT_LIST_DIR}/*.cpp"
)
add_library(realizations_coastal STATIC ${REALIZATIONS_COASTAL_SRC})
endif()

add_library(NGen::realizations_coastal ALIAS realizations_coastal)

# ---------------------------------------------------------------
# Toggle SCHISM coastal integration (DEFAULT: ON)
# Disable with: -DNGEN_ENABLE_SCHISM=OFF
# ---------------------------------------------------------------
option(NGEN_ENABLE_SCHISM "Build SCHISM coastal integration" ON)

if(NOT NGEN_ENABLE_SCHISM)
# Mark SCHISM sources as header-only so they are not compiled/linked
set(_schism_srcs
${CMAKE_CURRENT_LIST_DIR}/SchismCreator.cpp
${CMAKE_CURRENT_LIST_DIR}/SchismFormulation.cpp
)
foreach(_s IN LISTS _schism_srcs)
if(EXISTS "${_s}")
set_source_files_properties(${_s} PROPERTIES HEADER_FILE_ONLY TRUE)
message(STATUS "Coastal: excluding SCHISM source from build: ${_s}")
endif()
endforeach()

# Also exclude the SCHISM coastal test if present in the repo
set(_schism_test "${NGEN_TOP}/test/coastal/SchismFormulation_Test.cpp")
if(EXISTS "${_schism_test}")
set_source_files_properties("${_schism_test}" PROPERTIES HEADER_FILE_ONLY TRUE)
message(STATUS "Tests: excluding SCHISM coastal test: ${_schism_test}")
endif()
endif()

# Public include paths
target_include_directories(realizations_coastal PUBLIC
${PROJECT_SOURCE_DIR}/include/core
${PROJECT_SOURCE_DIR}/include/realizations/coastal
${PROJECT_SOURCE_DIR}/include/forcing
${PROJECT_SOURCE_DIR}/include/simulation_time
${PROJECT_SOURCE_DIR}/include/utilities
${PROJECT_SOURCE_DIR}/include/coastal
${PROJECT_SOURCE_DIR}/include/bmi
)

target_link_libraries(realizations_coastal PUBLIC
${CMAKE_DL_LIBS}
NGen::config_header
NGen::geojson
NGen::logging
NGen::ngen_bmi
)
${NGEN_TOP}/include/core
${NGEN_TOP}/include/realizations/coastal
${NGEN_TOP}/include/forcing
${NGEN_TOP}/include/simulation_time
${NGEN_TOP}/include/utilities
${NGEN_TOP}/include/coastal
${NGEN_TOP}/include/bmi
)

# Link to imported targets if they exist; otherwise, warn clearly when standalone
set(_missing_imports "")
foreach(_tgt NGen::config_header NGen::geojson NGen::logging NGen::ngen_bmi)
if(NOT TARGET ${_tgt})
list(APPEND _missing_imports ${_tgt})
endif()
endforeach()

if(_missing_imports)
message(STATUS "The following imported targets are missing: ${_missing_imports}")
message(STATUS "You are likely configuring this subfolder standalone.")
message(STATUS "For a full build, configure from the repo root: ${NGEN_TOP}")
endif()

# Always link libdl if available; conditionally link NGen targets if present
target_link_libraries(realizations_coastal PUBLIC ${CMAKE_DL_LIBS})
if(TARGET NGen::config_header)
target_link_libraries(realizations_coastal PUBLIC NGen::config_header)
endif()
if(TARGET NGen::geojson)
target_link_libraries(realizations_coastal PUBLIC NGen::geojson)
endif()
if(TARGET NGen::logging)
target_link_libraries(realizations_coastal PUBLIC NGen::logging)
endif()
if(TARGET NGen::ngen_bmi)
target_link_libraries(realizations_coastal PUBLIC NGen::ngen_bmi)
endif()

# ---------------------------------------------------------------------------
# Optional SFINCS tests (no MPI)
# Build only if we have Fortran BMI enabled at configure time and NGen targets exist
# ---------------------------------------------------------------------------
option(NGEN_BUILD_COASTAL_TESTS "Build coastal realization tests" ON)
option(NGEN_WITH_BMI_FORTRAN "Enable BMI Fortran adapter in NGen" OFF) # default OFF if standalone

if(NGEN_BUILD_COASTAL_TESTS AND NGEN_WITH_BMI_FORTRAN)
include(CTest)

# Provide convenient cache vars for the SFINCS BMI lib and init file
set(SFINCS_BMI_LIBRARY "${NGEN_TOP}/extern/SFINCS/source/src/build/libsfincs_bmi.so"
CACHE FILEPATH "Path to libsfincs_bmi shared library")
set(SFINCS_INIT_CONFIG "${NGEN_TOP}/extern/SFINCS/source/src/build/sfincs_config.txt"
CACHE FILEPATH "Path to SFINCS BMI initialize() config file")

# -------- test_sfincs_formulation_smoke (only if the test source exists) --------
set(_sfincs_smoke_src "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_sfincs_formulation_smoke.cpp")
if(EXISTS "${_sfincs_smoke_src}")
add_executable(test_sfincs_formulation_smoke "${_sfincs_smoke_src}")
target_include_directories(test_sfincs_formulation_smoke PRIVATE
${NGEN_TOP}/include
)
target_link_libraries(test_sfincs_formulation_smoke PRIVATE
realizations_coastal
)
if(TARGET NGen::ngen_bmi)
target_link_libraries(test_sfincs_formulation_smoke PRIVATE NGen::ngen_bmi)
endif()
if(TARGET NGen::logging)
target_link_libraries(test_sfincs_formulation_smoke PRIVATE NGen::logging)
endif()

add_test(NAME test_sfincs_formulation_smoke
COMMAND test_sfincs_formulation_smoke
${SFINCS_BMI_LIBRARY}
${SFINCS_INIT_CONFIG})
else()
message(STATUS "Skipping test_sfincs_formulation_smoke: file not found at ${_sfincs_smoke_src}")
endif()

# -------- test_sfincs_creator_smoke (optional helper) --------
set(_sfincs_creator_src "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_sfincs_creator_smoke.cpp")
if(EXISTS "${_sfincs_creator_src}")
add_executable(test_sfincs_creator_smoke "${_sfincs_creator_src}")
target_include_directories(test_sfincs_creator_smoke PRIVATE
${NGEN_TOP}/include
)
target_link_libraries(test_sfincs_creator_smoke PRIVATE
realizations_coastal
)
if(TARGET NGen::ngen_bmi)
target_link_libraries(test_sfincs_creator_smoke PRIVATE NGen::ngen_bmi)
endif()
if(TARGET NGen::logging)
target_link_libraries(test_sfincs_creator_smoke PRIVATE NGen::logging)
endif()

add_test(NAME test_sfincs_creator_smoke
COMMAND test_sfincs_creator_smoke
${SFINCS_BMI_LIBRARY}
${SFINCS_INIT_CONFIG})
else()
message(STATUS "Skipping test_sfincs_creator_smoke: file not found at ${_sfincs_creator_src}")
endif()

endif()

Loading