Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#737: SCHISM Coastal Formulation #810

Draft
wants to merge 51 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
c0294b2
#737: Initial draft of Coastal Formulation interface, SCHISM implemen…
PhilMiller May 3, 2024
a57659f
Clean up compiler issues
PhilMiller Jun 24, 2024
4d43b38
Another compilation fix
PhilMiller Jun 24, 2024
af3166f
More work toward Schism Formulation implementation
PhilMiller Jun 26, 2024
d07ebf8
Reword
PhilMiller Jun 26, 2024
17a8bfd
Adjust for refactor
PhilMiller Jun 26, 2024
28809e0
Typo fix
PhilMiller Jul 1, 2024
f345e6d
Parameterize SCHISM library path
PhilMiller Jul 8, 2024
25209b0
Update to match const-qualification changes
PhilMiller Aug 8, 2024
e003e76
Match added argument
PhilMiller Aug 8, 2024
7478cd2
Start outlining main implementation
PhilMiller Aug 8, 2024
68dc991
Finalize forcings/boundary providers
PhilMiller Aug 8, 2024
6fa3c18
List variables to set up before Update
PhilMiller Aug 8, 2024
04c9cf5
Stylize argument-naming comment
PhilMiller Aug 8, 2024
4d55b6c
Tweak definition of all_points to clarify call site usage
PhilMiller Aug 8, 2024
67e17c8
Conditionalize on Fortran support
PhilMiller Aug 16, 2024
c366014
Start handling input variable setup
PhilMiller Aug 16, 2024
706046b
Sketch time keeping members
PhilMiller Aug 16, 2024
1751125
Call BMI Init function
PhilMiller Aug 16, 2024
2dba565
Move input variable setup from ctor to Initialize
PhilMiller Aug 16, 2024
d687db0
Match SCHISM BMI's registration/instantiaion function name
PhilMiller Aug 19, 2024
b7d1eba
Fix declaration of all_points to avoid linking errors on duplicate de…
PhilMiller Aug 23, 2024
9b5716e
Conditionalize SCHISM code on Fortran and MPI support
PhilMiller Aug 23, 2024
fa6c223
Square away virtual method implementation
PhilMiller Aug 23, 2024
56783a3
Typo fix in variable name
PhilMiller Aug 23, 2024
23725d1
Bmi_Fortran_Adapter: Add MPI-specific constructor
PhilMiller Aug 23, 2024
d618d80
SchismFormulation: Match usage to Bmi_Fortran_Adapter constructor tak…
PhilMiller Aug 23, 2024
c7eaf3e
Require MPI for the test
PhilMiller Aug 26, 2024
8b4d612
Enumerate output variables
PhilMiller Aug 26, 2024
90b33d9
Implement mesh_size method
PhilMiller Aug 26, 2024
9d74eb2
Implement get_values
PhilMiller Aug 26, 2024
b9c1237
Generalize output string
PhilMiller Aug 26, 2024
ffb7057
Position comments better
PhilMiller Aug 26, 2024
5835aac
Square up time types
PhilMiller Aug 26, 2024
686a706
Implement much of forcings
PhilMiller Aug 26, 2024
bbe5c5f
Split parallel_utils.h to header/source, and make bits usable from mu…
PhilMiller Aug 27, 2024
b850742
Initialize input variables
PhilMiller Aug 27, 2024
0b0a237
Finalize input providers since mock is now set
PhilMiller Aug 27, 2024
d9bcb52
Single-step test with mocked inputs, works for mpiexec -np 1
PhilMiller Aug 27, 2024
b190797
Annotate override
PhilMiller Sep 17, 2024
ebaa00d
Firm up MeshPointsSelector
PhilMiller Sep 17, 2024
eaac987
Mostly implemented NetCDF unstructured mesh reader for coastal formul…
PhilMiller Sep 17, 2024
aab3cdc
Drop unused member variable
PhilMiller Sep 17, 2024
9fd9510
Remove unused instance and value caching from NetCDF mesh provider
PhilMiller Sep 17, 2024
5ed7fd5
Clear out some residual cruft
PhilMiller Sep 17, 2024
852c31c
DataProvider: Add nicer interface to base class
PhilMiller Sep 17, 2024
5d2ed33
NetCDF mesh data provider: fix up time keeping
PhilMiller Sep 17, 2024
1b29181
Adjust Schism formulation and test to exploit new NetCDF mesh data pr…
PhilMiller Sep 18, 2024
6a2a3dd
Time logic cleanup
PhilMiller Sep 18, 2024
b22f210
SCHISM Formulation: Pass MPI communicator as constructor argument
PhilMiller Sep 18, 2024
d680b07
Fix missing headers and conditional return in non-Python path
PhilMiller Sep 30, 2024
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
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ add_subdirectory("src/core")
add_subdirectory("src/geojson")
add_subdirectory("src/bmi")
add_subdirectory("src/realizations/catchment")
add_subdirectory("src/realizations/coastal")
add_subdirectory("src/forcing")
add_subdirectory("src/utilities/mdarray")
add_subdirectory("src/utilities/mdframe")
Expand Down
44 changes: 44 additions & 0 deletions include/bmi/Bmi_Fortran_Adapter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
#include "State_Exception.hpp"
#include "utilities/ExternalIntegrationException.hpp"

#if NGEN_WITH_MPI
#include <mpi.h>
#endif // NGEN_WITH_MPI

// Forward declaration to provide access to protected items in testing
class Bmi_Fortran_Adapter_Test;

Expand Down Expand Up @@ -76,6 +80,46 @@ namespace models {
}
}

#if NGEN_WITH_MPI
// Special case constructor for formulation modules that
// implement the NGen BMI MPI protocol, in which the MPI
// Communicator is set through SetValue() between
// construction and Initialize()
Bmi_Fortran_Adapter(const std::string &type_name,
std::string library_file_path,
std::string bmi_init_config,
bool has_fixed_time_step,
std::string registration_func,
MPI_Comm comm)
: AbstractCLibBmiAdapter(type_name,
library_file_path,
bmi_init_config,
has_fixed_time_step,
registration_func
)
{
try {
bmi_model = std::make_unique<Bmi_Fortran_Handle_Wrapper>(Bmi_Fortran_Handle_Wrapper());
dynamic_library_load();
execModuleRegistration();

MPI_Fint comm_fortran = MPI_Comm_c2f(comm);
inner_set_value_int("bmi_mpi_comm_handle", &comm_fortran);

int init_result = initialize(&bmi_model->handle, bmi_init_config.c_str());
if (init_result != BMI_SUCCESS) {
init_exception_msg = "Failure when attempting to initialize " + model_name;
throw models::external::State_Exception(init_exception_msg);
}
model_initialized = true;
}
catch (...) {
model_initialized = true;
throw;
}
}
#endif // NGEN_WITH_MPI

Bmi_Fortran_Adapter(Bmi_Fortran_Adapter const&) = delete;
Bmi_Fortran_Adapter(Bmi_Fortran_Adapter&&) = delete;

Expand Down
2 changes: 2 additions & 0 deletions include/forcing/DataProvider.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ namespace data_access
*/
virtual std::vector<data_type> get_values(const selection_type& selector, ReSampleMethod m=SUM) = 0;

virtual void get_values(const selection_type& selector, boost::span<double> data) { throw std::runtime_error("DP::get_values(span) Unimplemented"); }

virtual bool is_property_sum_over_time_step(const std::string& name) const {return false; }

private:
Expand Down
2 changes: 2 additions & 0 deletions include/forcing/GenericDataProvider.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@

#include "DataProvider.hpp"
#include "DataProviderSelectors.hpp"
#include "MeshPointsSelectors.hpp"

namespace data_access
{
using GenericDataProvider = DataProvider<double, CatchmentAggrDataSelector>;
using MeshPointsDataProvider = DataProvider<double, MeshPointsSelector>;
}

#endif
17 changes: 17 additions & 0 deletions include/forcing/MeshPointsSelectors.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#pragma once

#include <chrono>
#include <string>
#include <boost/variant.hpp>

struct AllPoints {};
static AllPoints all_points;

struct MeshPointsSelector
{
std::string variable_name;
std::chrono::time_point<std::chrono::system_clock> init_time;
std::chrono::seconds duration;
std::string output_units;
boost::variant<AllPoints, std::vector<int>> points;
};
104 changes: 104 additions & 0 deletions include/forcing/NetCDFMeshPointsDataProvider.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#pragma once

#include <NGenConfig.h>

#if NGEN_WITH_NETCDF

#include "GenericDataProvider.hpp"
#include "DataProviderSelectors.hpp"

#include <string>
#include <algorithm>
#include <map>
#include <memory>
#include <string>
#include <exception>

namespace netCDF {
class NcVar;
class NcFile;
}

namespace data_access
{
class NetCDFMeshPointsDataProvider : public MeshPointsDataProvider
{
public:

using time_point_type = std::chrono::time_point<std::chrono::system_clock>;

NetCDFMeshPointsDataProvider(std::string input_path,
time_point_type sim_start,
time_point_type sim_end);

// Default implementation defined in the .cpp file so that
// client code doesn't need to have the full definition of
// NcFile visible for the compiler to implicitly generate
// ~NetCDFMeshPointsDataProvider() = default;
// for every file that uses this class
~NetCDFMeshPointsDataProvider();

void finalize() override;

/** Return the variables that are accessible by this data provider */
boost::span<const std::string> get_available_variable_names() const override;

/** Return the first valid time for which data from the requested variable can be requested */
long get_data_start_time() const override;

/** Return the last valid time for which data from the requested variable can be requested */
long get_data_stop_time() const override;

long record_duration() const override;

/**
* Get the index of the data time step that contains the given point in time.
*
* An @ref std::out_of_range exception should be thrown if the time is not in any time step.
*
* @param epoch_time The point in time, as a seconds-based epoch time.
* @return The index of the forcing time step that contains the given point in time.
* @throws std::out_of_range If the given point is not in any time step.
*/
size_t get_ts_index_for_time(const time_t &epoch_time) const override;

/**
* Get the value of a forcing property for an arbitrary time period, converting units if needed.
*
* An @ref std::out_of_range exception should be thrown if the data for the time period is not available.
*
* @param selector Data required to establish what subset of the stored data should be accessed
* @param m How data is to be resampled if there is a mismatch in data alignment or repeat rate
* @return The value of the forcing property for the described time period, with units converted if needed.
* @throws std::out_of_range If data for the time period is not available.
*/
double get_value(const selection_type& selector, ReSampleMethod m) override;

void get_values(const selection_type& selector, boost::span<double> data) override;

// And an implementation of the usual version using it
std::vector<data_type> get_values(const selection_type& selector, data_access::ReSampleMethod) override
{
throw std::runtime_error("Unimplemented");
}

private:

void cache_variable(std::string const& var_name);

time_point_type sim_start_date_time_epoch;
time_point_type sim_end_date_time_epoch;

std::vector<std::string> variable_names;
std::vector<time_point_type> time_vals;
std::chrono::seconds time_stride; // the amount of time between stored time values

std::shared_ptr<netCDF::NcFile> nc_file;

struct metadata_cache_entry;
std::map<std::string, metadata_cache_entry> ncvar_cache;
};
}


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

#include <forcing/DataProvider.hpp>
#include <forcing/MeshPointsSelectors.hpp>

#include <string>
#include <boost/core/span.hpp>

class CoastalFormulation : public data_access::DataProvider<double, MeshPointsSelector>
{
public:
CoastalFormulation(std::string id)
: id_(std::move(id))
{ }

virtual ~CoastalFormulation() = default;

std::string get_id() const {
return this->id_;
}

virtual void initialize() = 0;
void finalize() override = 0;
virtual void update() = 0;

void get_values(const selection_type& selector, boost::span<double> data) override = 0;

// And an implementation of the usual version using it
std::vector<data_type> get_values(const selection_type& selector, data_access::ReSampleMethod) override
{
std::vector<data_type> output(selected_points_count(selector));
get_values(selector, output);
return output;
}

protected:
using selection_type = MeshPointsSelector;
using data_type = double;

virtual size_t mesh_size(std::string const& variable_name) = 0;

size_t selected_points_count(const selection_type& selector)
{
auto* points = boost::get<std::vector<int>>(&selector.points);
size_t size = points ? points->size() : this->mesh_size(selector.variable_name);
return size;
}

private:
const std::string id_;
};
82 changes: 82 additions & 0 deletions include/realizations/coastal/SchismFormulation.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#pragma once

#include <NGenConfig.h>

#if NGEN_WITH_BMI_FORTRAN && NGEN_WITH_MPI

#include <realizations/coastal/CoastalFormulation.hpp>
#include <bmi/Bmi_Fortran_Adapter.hpp>
#include <memory>
#include <map>
#include <vector>

class SchismFormulation final : public CoastalFormulation
{
public:
using MeshPointsDataProvider = data_access::DataProvider<double, MeshPointsSelector>;
SchismFormulation(
std::string const& id
, std::string const& library_path
, std::string const& init_config_path
, MPI_Comm mpi_comm
, std::shared_ptr<MeshPointsDataProvider> met_forcings
, std::shared_ptr<MeshPointsDataProvider> offshore_boundary
, std::shared_ptr<MeshPointsDataProvider> inflow_boundary
);

~SchismFormulation();

// Implementation of DataProvider
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;
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) override;

// Implementation of CoastalFormulation
void initialize() override;
void finalize() override;
void update() override;

void get_values(const selection_type& selector, boost::span<double> data) override;

protected:
size_t mesh_size(std::string const& variable_name) override;

private:
std::unique_ptr<models::bmi::Bmi_Fortran_Adapter> bmi_;

enum ForcingSelector { METEO, OFFSHORE, INFLOW };
struct InputMapping { ForcingSelector selector; std::string name; };
static std::map<std::string, InputMapping> expected_input_variables_;
std::map<std::string, std::string> input_variable_units_;
std::map<std::string, std::string> input_variable_type_;
std::map<std::string, size_t> input_variable_count_;

static std::vector<std::string> exported_output_variable_names_;
std::map<std::string, std::string> output_variable_units_;
std::map<std::string, std::string> output_variable_type_;
std::map<std::string, size_t> output_variable_count_;

std::chrono::time_point<std::chrono::system_clock> current_time_;
std::chrono::seconds time_step_length_;

// TODO: Some of these maybe should be members of
// CoastalFormulation

// TODO: We really want something that we can ask for
// area-averaged RAINRATE over elements, but we're going to make
// do with point values at the element centroids

using ProviderType = data_access::DataProvider<double, MeshPointsSelector>;
std::shared_ptr<ProviderType> meteorological_forcings_provider_;
std::shared_ptr<ProviderType> offshore_boundary_provider_;
std::shared_ptr<ProviderType> inflows_boundary_provider_;

void set_inputs();
};

#endif // NGEN_WITH_BMI_FORTRAN && NGEN_WITH_MPI
Loading
Loading