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

Add JPSSRR sea-ice product to ioda converter #1259

Merged
merged 6 commits into from
Aug 27, 2024
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
125 changes: 125 additions & 0 deletions utils/obsproc/IcecJpssrr2Ioda.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#pragma once

#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <netcdf> // NOLINT (using C API)
#include <sstream>
#include <string>
#include <vector>

#include "eckit/config/LocalConfiguration.h"

#include <Eigen/Dense> // NOLINT

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

#include "oops/util/dateFunctions.h"

#include "NetCDFToIodaConverter.h"

namespace gdasapp {

class IcecJpssrr2Ioda : public NetCDFToIodaConverter {
public:
explicit IcecJpssrr2Ioda(const eckit::Configuration & fullConfig, const eckit::mpi::Comm & comm)
: NetCDFToIodaConverter(fullConfig, comm) {
variable_ = "seaIceFraction";
}

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

// Open the NetCDF file in read-only mode
netCDF::NcFile ncFile(fileName, netCDF::NcFile::read);
oops::Log::info() << "Reading... " << fileName << std::endl;

// Get the number of obs in the file
int dimxSize = ncFile.getDim("Columns").getSize();
int dimySize = ncFile.getDim("Rows").getSize();
int nobs = dimxSize * dimySize;

// Set the int metadata names
std::vector<std::string> intMetadataNames = {"oceanBasin"};
guillaumevernieres marked this conversation as resolved.
Show resolved Hide resolved

// Set the float metadata name
std::vector<std::string> floatMetadataNames = {};

// Create instance of iodaVars object
gdasapp::obsproc::iodavars::IodaVars iodaVars(nobs, floatMetadataNames, intMetadataNames);

oops::Log::debug() << "--- iodaVars.location_: " << iodaVars.location_ << std::endl;

// Read non-optional metadata: longitude and latitude
std::vector<float> oneDimLatVal(iodaVars.location_);
ncFile.getVar("Latitude").getVar(oneDimLatVal.data());

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

// Create a vector to hold the Summary Qc variable
// User-level summary QC: 0=Normal, 1=Uncertain
std::vector<signed char> oneDimFlagsVal(iodaVars.location_);
ncFile.getVar("SummaryQC_Ice_Concentration").getVar(oneDimFlagsVal.data());

// Get Ice_Concentration obs values
std::vector<float> oneDimObsVal(iodaVars.location_);
ncFile.getVar("IceConc").getVar(oneDimObsVal.data());

// Read and process the starting and ending of dateTime
auto timeStartAttr = ncFile.getAtt("time_coverage_start");
auto timeEndAttr = ncFile.getAtt("time_coverage_end");

// Extract attribute values as strings
std::string timeStartStr, timeEndStr;
timeStartAttr.getValues(timeStartStr);
timeEndAttr.getValues(timeEndStr);

// Convert the extracted strings to util::DateTime objects
util::DateTime timeStartDtime(timeStartStr);
util::DateTime timeEndDtime(timeEndStr);

// Create vectors of util::DateTime
std::vector<util::DateTime> timeStartVector = {timeStartDtime};
std::vector<util::DateTime> timeEndVector = {timeEndDtime};

// Set epoch time for JPSSRR ICEC
util::DateTime epochDtime("1970-01-01T00:00:00Z");

// Convert Obs DateTime objects to epoch time offsets in seconds
int64_t timeStartOffsets
= ioda::convertDtimeToTimeOffsets(epochDtime, timeStartVector)[0];
int64_t timeEndOffsets
= ioda::convertDtimeToTimeOffsets(epochDtime, timeEndVector)[0];

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

// Update non-optional Eigen arrays
for (int i = 0; i < iodaVars.location_; i++) {
iodaVars.longitude_(i) = oneDimLonVal[i];
iodaVars.latitude_(i) = oneDimLatVal[i];
iodaVars.obsVal_(i) = static_cast<float>(oneDimObsVal[i]*0.01);
iodaVars.obsError_(i) = 0.1; // Do something for obs error
guillaumevernieres marked this conversation as resolved.
Show resolved Hide resolved
iodaVars.preQc_(i) = oneDimFlagsVal[i];
iodaVars.datetime_(i) =
timeStartOffsets + (timeEndOffsets - timeStartOffsets) * 0.5;
// Store optional metadata, set ocean basins to -999 for now
iodaVars.intMetadata_.row(i) << -999;
}

// basic test for iodaVars.trim
Eigen::Array<bool, Eigen::Dynamic, 1> mask =
((iodaVars.obsVal_ >= 0.0 && iodaVars.obsVal_ <= 1.0) &&
iodaVars.datetime_ > 0.0 &&
(iodaVars.latitude_ <= -40.0 || iodaVars.latitude_ >= 40.0));
iodaVars.trim(mask);

return iodaVars;
};
}; // class IcecMirs2Ioda
} // 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 @@ -8,6 +8,7 @@

#include "../Ghrsst2Ioda.h"
#include "../IcecAmsr2Ioda.h"
#include "../IcecJpssrr2Ioda.h"
#include "../IcecMirs2Ioda.h"
#include "../Rads2Ioda.h"
#include "../RTOFSSalinity.h"
Expand Down Expand Up @@ -54,6 +55,9 @@ namespace gdasapp {
} else if (provider == "MIRS") {
IcecMirs2Ioda conv2ioda(fullConfig, this->getComm());
conv2ioda.writeToIoda();
} else if (provider == "JPSSRR") {
IcecJpssrr2Ioda conv2ioda(fullConfig, this->getComm());
conv2ioda.writeToIoda();
} else if (provider == "VIIRSAOD") {
Viirsaod2Ioda conv2ioda(fullConfig, this->getComm());
conv2ioda.writeToIoda();
Expand Down
9 changes: 9 additions & 0 deletions utils/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ list( APPEND utils_test_input
testinput/gdas_smos2ioda.yaml
testinput/gdas_icecamsr2ioda.yaml
testinput/gdas_icecmirs2ioda.yaml
testinput/gdas_icecjpssrr2ioda.yaml
testinput/gdas_viirsaod2ioda.yaml
)

Expand All @@ -21,6 +22,7 @@ set( gdas_utils_test_ref
testref/smos2ioda.test
testref/icecamsr2ioda.test
testref/icecmirs2ioda.test
testref/icecjpssrr2ioda.test
testref/viirsaod2ioda.test
)

Expand Down Expand Up @@ -154,3 +156,10 @@ ecbuild_add_test( TARGET test_gdasapp_util_icecmirs2ioda
ARGS "../testinput/gdas_icecmirs2ioda.yaml"
LIBS gdas-utils
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/obsproc)

# Test the JPSSRR to IODA converter
ecbuild_add_test( TARGET test_gdasapp_util_icecjpssrr2ioda
COMMAND ${CMAKE_BINARY_DIR}/bin/gdas_obsprovider2ioda.x
ARGS "../testinput/gdas_icecjpssrr2ioda.yaml"
LIBS gdas-utils
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/obsproc)
2 changes: 2 additions & 0 deletions utils/test/prepdata.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ cdl2nc4 icec_amsr2_south_1.nc4 ${project_source_dir}/testdata/icec_amsr2_south_1
cdl2nc4 icec_amsr2_south_2.nc4 ${project_source_dir}/testdata/icec_amsr2_south_2.cdl
cdl2nc4 icec_mirs_snpp_1.nc4 ${project_source_dir}/testdata/icec_mirs_snpp_1.cdl
cdl2nc4 icec_mirs_snpp_2.nc4 ${project_source_dir}/testdata/icec_mirs_snpp_2.cdl
cdl2nc4 icec_jrr_n20_1.nc4 ${project_source_dir}/testdata/icec_jrr_n20_1.cdl
cdl2nc4 icec_jrr_n20_2.nc4 ${project_source_dir}/testdata/icec_jrr_n20_2.cdl
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
Expand Down
Loading
Loading