-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy pathRads2Ioda.h
141 lines (111 loc) · 5.1 KB
/
Rads2Ioda.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#pragma once
#include <iostream>
#include <map>
#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 Rads2Ioda : public NetCDFToIodaConverter {
public:
explicit Rads2Ioda(const eckit::Configuration & fullConfig, const eckit::mpi::Comm & comm)
: NetCDFToIodaConverter(fullConfig, comm) {
variable_ = "absoluteDynamicTopography";
}
// Read netcdf file and populate iodaVars
gdasapp::obsproc::iodavars::IodaVars providerToIodaVars(const std::string fileName) final {
oops::Log::info() << "Processing files provided by the RADS" << std::endl;
// Open the NetCDF file in read-only mode
netCDF::NcFile ncFile(fileName, netCDF::NcFile::read);
// Get the number of obs in the file
int nobs = ncFile.getDim("time").getSize();
// Set the int metadata names
std::vector<std::string> intMetadataNames = {"pass", "cycle", "mission", "oceanBasin"};
// Set the float metadata name
std::vector<std::string> floatMetadataNames = {"mdt"};
// Create instance of iodaVars object
gdasapp::obsproc::iodavars::IodaVars iodaVars(nobs, floatMetadataNames, intMetadataNames);
// Read non-optional metadata: datetime, longitude and latitude
int lat[iodaVars.location_]; // NOLINT
ncFile.getVar("lat").getVar(lat);
int lon[iodaVars.location_]; // NOLINT
ncFile.getVar("lon").getVar(lon);
float geoscaleFactor;
ncFile.getVar("lon").getAtt("scale_factor").getValues(&geoscaleFactor);
float datetime[iodaVars.location_]; // NOLINT
ncFile.getVar("time_mjd").getVar(datetime);
iodaVars.referenceDate_ = "seconds since 1858-11-17T00:00:00Z";
std::map<std::string, int> altimeterMap;
// TODO(All): This is incomplete, add missions to the list below
altimeterMap["SNTNL-3A"] = 1;
altimeterMap["SNTNL-3B"] = 2;
altimeterMap["JASON-1"] = 3;
altimeterMap["JASON-2"] = 4;
altimeterMap["JASON-3"] = 5;
altimeterMap["CRYOSAT2"] = 6;
altimeterMap["SARAL"] = 7;
// Read optional integer metadata "mission"
std::string mission_name;
ncFile.getAtt("mission_name").getValues(mission_name);
int mission_index = altimeterMap[mission_name]; // mission name mapped to integer
// convert mission map to string to add to global attributes
std::stringstream mapStr;
for (const auto& mapEntry : altimeterMap) {
mapStr << mapEntry.first << " = " << mapEntry.second << " ";
}
iodaVars.strGlobalAttr_["mission_index"] = mapStr.str();
std::string references;
ncFile.getAtt("references").getValues(references);
iodaVars.strGlobalAttr_["references"] = references;
// Read optional integer metadata "pass" and "cycle"
int pass[iodaVars.location_]; // NOLINT
ncFile.getVar("pass").getVar(pass);
int cycle[iodaVars.location_]; // NOLINT
ncFile.getVar("cycle").getVar(cycle);
// Store optional metadata, set ocean basins to -999 for now
for (int i = 0; i < iodaVars.location_; i++) {
iodaVars.intMetadata_.row(i) << pass[i], cycle[i], mission_index, -999;
}
// Get adt_egm2008 obs values and attributes
int adt[iodaVars.location_]; // NOLINT
ncFile.getVar("adt_egm2008").getVar(adt);
float scaleFactor;
ncFile.getVar("adt_egm2008").getAtt("scale_factor").getValues(&scaleFactor);
// Read sla
short sla[iodaVars.location_]; // NOLINT
ncFile.getVar("sla").getVar(sla);
// Update non-optional Eigen arrays
for (int i = 0; i < iodaVars.location_; i++) {
iodaVars.longitude_(i) = static_cast<float>(lon[i])*geoscaleFactor;
iodaVars.latitude_(i) = static_cast<float>(lat[i])*geoscaleFactor;
iodaVars.datetime_(i) = static_cast<int64_t>(datetime[i]*86400.0f);
iodaVars.obsVal_(i) = static_cast<float>(adt[i])*scaleFactor;
iodaVars.obsError_(i) = 0.1; // Do something for obs error
iodaVars.preQc_(i) = 0;
// Save MDT in optional floatMetadata
iodaVars.floatMetadata_.row(i) << iodaVars.obsVal_(i) -
static_cast<float>(sla[i])*scaleFactor;
}
// Basic QC
Eigen::Array<bool, Eigen::Dynamic, 1> boundsCheck =
(iodaVars.obsVal_ > -4.0 && iodaVars.obsVal_ < 4.0);
iodaVars.trim(boundsCheck);
// get ocean basin masks if asked in the config
obsproc::oceanmask::OceanMask* oceanMask = nullptr;
if (fullConfig_.has("ocean basin")) {
std::string fileName;
fullConfig_.get("ocean basin", fileName);
oceanMask = new obsproc::oceanmask::OceanMask(fileName);
for (int i = 0; i < iodaVars.location_; i++) {
iodaVars.intMetadata_.coeffRef(i, 3) =
oceanMask->getOceanMask(iodaVars.longitude_[i], iodaVars.latitude_[i]);
}
}
return iodaVars;
};
}; // class Rads2Ioda
} // namespace gdasapp