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

Added example utility using OOPS and IODA #553

Merged
merged 16 commits into from
Aug 9, 2023
Merged
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
24 changes: 13 additions & 11 deletions utils/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
project(gdas-utils LANGUAGES C CXX )

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
set(CMAKE_FORTRAN_STANDARD 08)
set(CMAKE_FORTRAN_STANDARD_REQUIRED ON)
set(CMAKE_FORTRAN_EXTENSIONS OFF)

find_package(NetCDF REQUIRED COMPONENTS CXX)
find_package(oops REQUIRED)
find_package(atlas REQUIRED)
find_package(soca REQUIRED)

# Increment post processing
ecbuild_add_executable( TARGET gdas_incr_handler.x
SOURCES gdas_incr_handler.cc gdas_postprocincr.h)
target_compile_features( gdas_incr_handler.x PUBLIC cxx_std_17)
target_link_libraries( gdas_incr_handler.x PUBLIC NetCDF::NetCDF_CXX oops atlas soca)

# Hybrid-Weight
ecbuild_add_executable( TARGET gdas_socahybridweights.x
SOURCES gdas_socahybridweights.cc )
target_compile_features( gdas_socahybridweights.x PUBLIC cxx_std_17)
target_link_libraries( gdas_socahybridweights.x PUBLIC NetCDF::NetCDF_CXX oops atlas soca)
add_subdirectory(soca)
add_subdirectory(ioda_example)
add_subdirectory(test)
6 changes: 6 additions & 0 deletions utils/ioda_example/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
ecbuild_add_executable( TARGET gdas_meanioda.x
SOURCES gdas_meanioda.cc )

target_compile_features( gdas_meanioda.x PUBLIC cxx_std_17)
target_link_libraries( gdas_meanioda.x PUBLIC oops ioda)

14 changes: 14 additions & 0 deletions utils/ioda_example/gdas_meanioda.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#include "gdas_meanioda.h"
#include "oops/runs/Run.h"

// this is an example application that
// will use IODA to read a file and print something
// it is intended to be very bare bones
// you will note the .cc file is very empty
// the .h file is where the action is!

int main(int argc, char ** argv) {
oops::Run run(argc, argv);
gdasapp::IodaExample iodaexample;
return run.execute(iodaexample);
}
86 changes: 86 additions & 0 deletions utils/ioda_example/gdas_meanioda.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#include <iostream>
#include <numeric>
#include "eckit/config/LocalConfiguration.h"
#include "ioda/Group.h"
#include "ioda/ObsSpace.h"
#include "ioda/ObsVector.h"
#include "oops/base/PostProcessor.h"
#include "oops/mpi/mpi.h"
#include "oops/runs/Application.h"
#include "oops/util/DateTime.h"
#include "oops/util/Duration.h"
#include "oops/util/Logger.h"

namespace gdasapp {
// this is an example of how one can use OOPS and IODA to do something
// in this code, we will read in configuration from YAML
// and then use that configuration to read in a IODA formatted file,
// read one group/variable (with optional channel)
// and compute and print out the mean of the variable.
// Nothing fancy, but you can see how this could be expanded!

class IodaExample : public oops::Application {
public:
explicit IodaExample(const eckit::mpi::Comm & comm = oops::mpi::world())
: Application(comm) {}
static const std::string classname() {return "gdasapp::IodaExample";}

int execute(const eckit::Configuration & fullConfig, bool /*validate*/) const {

// get the obs space configuration
const eckit::LocalConfiguration obsConfig(fullConfig, "obs space");
ioda::ObsTopLevelParameters obsparams;
obsparams.validateAndDeserialize(obsConfig); // TODO CRM, can I remove this and then the simulated vars junk??
oops::Log::info() << "obs space: " << std::endl << obsConfig << std::endl;

// time window stuff
CoryMartin-NOAA marked this conversation as resolved.
Show resolved Hide resolved
std::string winbegin;
std::string winend;
fullConfig.get("window begin", winbegin);
fullConfig.get("window end", winend);

// what variable to get the mean of
std::string group;
std::string variable;
fullConfig.get("group", group);
fullConfig.get("variable", variable);
int chan = 0;
if (fullConfig.has("channel")) {
fullConfig.get("channel", chan);
}

// read the obs space
// Note, the below line does a lot of heavy lifting
// we can probably go to a lower level function (and more of them) to accomplish the same thing
ioda::ObsSpace ospace(obsparams, oops::mpi::world(), util::DateTime(winbegin), util::DateTime(winend), oops::mpi::myself());
const size_t nlocs = ospace.nlocs();
oops::Log::info() << "nlocs =" << nlocs << std::endl;
std::vector<float> buffer(nlocs);

// below is grabbing from the IODA obs space the specified group/variable and putting it into the buffer
if (chan == 0) {
// no channel is selected
ospace.get_db(group, variable, buffer);
} else {
// give it the channel as a single item list
ospace.get_db(group, variable, buffer, {chan});
}

// the below line computes the mean, aka sum divided by count
const float mean = std::accumulate(buffer.begin(), buffer.end(), 0) / float(nlocs);

// write the mean out to the stdout
oops::Log::info() << "mean value for " << group << "/" << variable << "=" << mean << std::endl;

// a better program should return a real exit code depending on result, but this is just an example!
return 0;
}
// -----------------------------------------------------------------------------
private:
std::string appname() const {
return "gdasapp::IodaExample";
}
// -----------------------------------------------------------------------------
};

} // namespace gdasapp
11 changes: 11 additions & 0 deletions utils/soca/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Increment post processing
ecbuild_add_executable( TARGET gdas_incr_handler.x
SOURCES gdas_incr_handler.cc gdas_postprocincr.h)
target_compile_features( gdas_incr_handler.x PUBLIC cxx_std_17)
target_link_libraries( gdas_incr_handler.x PUBLIC NetCDF::NetCDF_CXX oops atlas soca)

# Hybrid-Weight
ecbuild_add_executable( TARGET gdas_socahybridweights.x
SOURCES gdas_socahybridweights.cc )
target_compile_features( gdas_socahybridweights.x PUBLIC cxx_std_17)
target_link_libraries( gdas_socahybridweights.x PUBLIC NetCDF::NetCDF_CXX oops atlas soca)
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
13 changes: 13 additions & 0 deletions utils/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Create Data directory for test input config and symlink all files
list( APPEND utils_test_input
testinput/gdas_meanioda.yaml
)

file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/testinput)
CREATE_SYMLINK( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${utils_test_input} )

# Test example IODA utility that computes the mean of a variable
ecbuild_add_test( TARGET test_gdasapp_util_ioda_example
COMMAND ${CMAKE_BINARY_DIR}/bin/gdas_meanioda.x
ARGS "testinput/gdas_meanioda.yaml"
LIBS gdas-utils)
16 changes: 16 additions & 0 deletions utils/test/testinput/gdas_meanioda.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# the window is 30 years long to capture anything we can throw at it in this input file
window begin: 2000-11-01T09:00:00Z
window end: 2030-11-01T15:00:00Z
obs space:
name: gmi_gpm_test_mean
obsdatain:
engine:
type: H5File
obsfile: ../../../soca/test/Data/obs/gmi_gpm_obs.nc
# the below 2 lines are not used but needed by the IODA obsspace it seems...
simulated variables: [brightnessTemperature]
observed variables: [brightnessTemperature]
group: ObsValue
variable: brightnessTemperature
# channel is optional, depends on what variable you are reading
channel: 6
Loading