Skip to content

Commit

Permalink
Add SMOS SSS to IODA converter with test data (NOAA-EMC#653)
Browse files Browse the repository at this point in the history
* working stub

* seems to work with the current develop

* rewrote smos ioda converter to match new parent class

* added datetime to smos ioda file

* tweaked cdl files to make it past netcdf converter

* clean up todos

* clean up todos

* removed variable from ioda conversion yaml

* addition iodaVars.referenceDate

* from arrays to vectors

* note for Eigen Maps
  • Loading branch information
AndrewEichmann-NOAA authored Oct 3, 2023
1 parent b97bce8 commit 61e1da6
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 1,118 deletions.
87 changes: 87 additions & 0 deletions utils/obsproc/Smos2Ioda.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#pragma once

#include <iostream>
#include <netcdf> // NOLINT (using C API)
#include <string>
#include <vector>

#include "eckit/config/LocalConfiguration.h"

#include <Eigen/Dense> // NOLINT

#include "ioda/Group.h"
#include "ioda/ObsGroup.h"

#include "NetCDFToIodaConverter.h"

namespace gdasapp {

class Smos2Ioda : public NetCDFToIodaConverter {
public:
explicit Smos2Ioda(const eckit::Configuration & fullConfig)
: NetCDFToIodaConverter(fullConfig) {
variable_ = "Salinity";
}

// Read netcdf file and populate iodaVars
gdasapp::IodaVars providerToIodaVars(const std::string fileName) final {
oops::Log::info() << "Processing files provided by SMOS" << std::endl;

// Open the NetCDF file in read-only mode
netCDF::NcFile ncFile(fileName, netCDF::NcFile::read);

// Get number of obs
int nobs = ncFile.getDim("n_grid_points").getSize();

// Set the int metadata names
// TODO(AFE): add other metadata in form of
// std::vector<std::string> intMetadataNames = {"pass", "cycle", "mission"};
std::vector<std::string> intMetadataNames = {};

// Set the float metadata name
// TODO(AFE): add other metadata in form of
// std::vector<std::string> floatMetadataNames = {"mdt"};
std::vector<std::string> floatMetadataNames = {};
// Create instance of iodaVars object
gdasapp::IodaVars iodaVars(nobs, floatMetadataNames, intMetadataNames);

std::vector<float> lat(iodaVars.location);
ncFile.getVar("Latitude").getVar(lat.data());

std::vector<float> lon(iodaVars.location);
ncFile.getVar("Longitude").getVar(lon.data());

std::vector<float> sss(iodaVars.location);
ncFile.getVar("SSS_corr").getVar(sss.data());

std::vector<float> sss_error(iodaVars.location);
ncFile.getVar("Sigma_SSS_corr").getVar(sss_error.data());

std::vector< uint16_t > sss_qc(iodaVars.location);
ncFile.getVar("Dg_quality_SSS_corr").getVar(sss_qc.data());

// according to https://earth.esa.int/eogateway/documents/20142/0/SMOS-L2-Aux-Data-Product-Specification.pdf,
// this is UTC decimal days after MJD2000 which is
// Jan 01 2000 00:00:00 GMT+0000
std::vector<float> datetime(iodaVars.location);
ncFile.getVar("Mean_acq_time").getVar(datetime.data());

iodaVars.referenceDate = "seconds since 1970-01-01T00:00:00Z";

// unix epoch (seconds after iodaVars.referenceDate) at
// Jan 01 2000 00:00:00 GMT+0000
const int mjd2000 = 946684800;

// TODO(AFE) maybe use Eigen Maps here
for (int i = 0; i < iodaVars.location; i++) {
iodaVars.longitude(i) = lon[i];
iodaVars.latitude(i) = lat[i];
iodaVars.obsVal(i) = sss[i];
iodaVars.obsError(i) = sss_error[i];
iodaVars.preQc(i) = sss_qc[i];
iodaVars.datetime(i) = static_cast<int64_t>(datetime[i]*86400.0f) + mjd2000;
}
return iodaVars;
};
}; // class Smos2Ioda
} // namespace gdasapp
4 changes: 4 additions & 0 deletions utils/obsproc/applications/gdas_obsprovider2ioda.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "oops/runs/Application.h"

#include "../Rads2Ioda.h"
#include "../Smos2Ioda.h"

namespace gdasapp {
class ObsProvider2IodaApp : public oops::Application {
Expand All @@ -25,6 +26,9 @@ namespace gdasapp {
conv2ioda.writeToIoda();
} else if (provider == "GHRSST") {
oops::Log::info() << "Comming soon!" << std::endl;
} else if (provider == "SMOS") {
Smos2Ioda conv2ioda(fullConfig);
conv2ioda.writeToIoda();
} else {
oops::Log::info() << "Provider not implemented" << std::endl;
return 1;
Expand Down
8 changes: 8 additions & 0 deletions utils/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
list( APPEND utils_test_input
testinput/gdas_meanioda.yaml
testinput/gdas_rads2ioda.yaml
testinput/gdas_smos2ioda.yaml
)

file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/testinput)
Expand Down Expand Up @@ -37,3 +38,10 @@ ecbuild_add_test( TARGET test_gdasapp_util_rads2ioda
ARGS "../testinput/gdas_rads2ioda.yaml"
LIBS gdas-utils
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/obsproc)

# Test the SMOS to IODA converter
ecbuild_add_test( TARGET test_gdasapp_util_smos2ioda
COMMAND ${CMAKE_BINARY_DIR}/bin/gdas_obsprovider2ioda.x
ARGS "../testinput/gdas_smos2ioda.yaml"
LIBS gdas-utils
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/obsproc)
4 changes: 2 additions & 2 deletions utils/test/prepdata.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ cdl2nc4 icec_amsr2_south_2.nc4 ${project_source_dir}/testdata/icec_amsr2_south_2
# TODO(Andy): Fix the corrupted cdl files below
#cdl2nc4 sss_smap_1.nc4 ${project_source_dir}/testdata/sss_smap_1.cdl
#cdl2nc4 sss_smap_2.nc4 ${project_source_dir}/testdata/sss_smap_2.cdl
#cdl2nc4 sss_smos_1.nc4 ${project_source_dir}/testdata/sss_smos_1.cdl
#cdl2nc4 sss_smos_2.nc4 ${project_source_dir}/testdata/sss_smos_2.cdl
cdl2nc4 sss_smos_1.nc4 ${project_source_dir}/testdata/sss_smos_1.cdl
cdl2nc4 sss_smos_2.nc4 ${project_source_dir}/testdata/sss_smos_2.cdl
cdl2nc4 ghrsst_sst_mb_202107010000.nc4 ${project_source_dir}/testdata/ghrsst_sst_mb_202107010000.cdl
cdl2nc4 ghrsst_sst_mb_202107010100.nc4 ${project_source_dir}/testdata/ghrsst_sst_mb_202107010100.cdl
Loading

0 comments on commit 61e1da6

Please sign in to comment.