Skip to content

Commit be6e5f1

Browse files
committed
First step towards CF compliant netcdf files. dynamically located lat, long, datetime coordinates and dimensions using standard long names
1 parent f98f9e4 commit be6e5f1

File tree

2 files changed

+197
-122
lines changed

2 files changed

+197
-122
lines changed

src/timeseries/netcdf.cpp

Lines changed: 110 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
netcdf::netcdf()
2929
{
3030
_is_open = false;
31+
_datetime_field="";
3132
}
3233
netcdf::~netcdf()
3334
{
@@ -100,55 +101,95 @@ void netcdf::open(const std::string &file)
100101
{
101102
_data.open(file.c_str(), netCDF::NcFile::read);
102103
}
104+
105+
std::string netcdf::find_coord_by_standard_name(const std::string& standard_name) const
106+
{
107+
for (auto& itr : _data.getCoordVars())
108+
{
109+
auto var = _data.getVar(itr.first);
110+
auto standardNameAtt = var.getAtt("standard_name");
111+
if(standardNameAtt.isNull())
112+
continue;
113+
114+
std::string std_name;
115+
standardNameAtt.getValues(std_name);
116+
if(std_name == standard_name)
117+
{
118+
return itr.first;
119+
}
120+
}
121+
122+
// if we get here, we didn't find what we were looking for
123+
CHM_THROW_EXCEPTION(forcing_error, "Could not find coordinate variable for " + standard_name);
124+
125+
}
126+
127+
std::string netcdf::find_dim_by_standard_name(const std::string& standard_name) const
128+
{
129+
for (auto& itr : _data.getDims())
130+
{
131+
auto var = _data.getVar(itr.first);
132+
auto standardNameAtt = var.getAtt("standard_name");
133+
if(standardNameAtt.isNull())
134+
continue;
135+
136+
std::string std_name;
137+
standardNameAtt.getValues(std_name);
138+
if(std_name == standard_name)
139+
{
140+
return itr.first;
141+
}
142+
}
143+
144+
// if we get here, we didn't find what we were looking for
145+
CHM_THROW_EXCEPTION(forcing_error, "Could not find coordinate variable for " + standard_name);
146+
147+
}
148+
103149
void netcdf::open_GEM(const std::string &file)
104150
{
105151
_data.open(file.c_str(), netCDF::NcFile::read);
106152

107153
// gem netcdf files have 1 coordinate, datetime
108154

109-
// for (auto& itr : _data.getVars())
110-
// {
111-
// LOG_DEBUG << itr.first;
112-
// }
113-
//
114-
// SPDLOG_DEBUG("-----");
115-
// for (auto& itr : _data.getCoordVars())
116-
// {
117-
// LOG_DEBUG << itr.first;
118-
// }
119-
// SPDLOG_DEBUG("-----");
120-
// for (auto& itr : _data.getDims())
121-
// {
122-
// LOG_DEBUG << itr.first;
123-
// }
155+
SPDLOG_DEBUG("NC getVars:");
156+
for (auto& itr : _data.getVars())
157+
{
158+
SPDLOG_DEBUG("\t"+itr.first);
159+
}
160+
161+
SPDLOG_DEBUG("NC getCoordVars");
162+
for (auto& itr : _data.getCoordVars())
163+
{
164+
SPDLOG_DEBUG("\t"+itr.first);
165+
}
166+
SPDLOG_DEBUG("Nc getDims");
167+
for (auto& itr : _data.getDims())
168+
{
169+
SPDLOG_DEBUG("\t"+itr.first);
170+
}
171+
124172
auto coord_vars = _data.getCoordVars();
125173

126174
// a few NC have time as a variable and not a coordinate variable so look for time/datetime there
127175
if(coord_vars.size() == 0)
128176
{
129177
CHM_THROW_EXCEPTION(forcing_error,"Netcdf file does not have a coordinate variable defined.");
130178
}
131-
_datetime_field = "datetime";
179+
180+
_datetime_field = find_coord_by_standard_name("time");
181+
SPDLOG_DEBUG("Found time coordinate variable: " + _datetime_field);
132182

133183
try
134184
{
135185
_datetime_length = coord_vars[_datetime_field].getDim(_datetime_field).getSize();
136186
}
137187
catch (netCDF::exceptions::NcNullGrp& e)
138188
{
139-
_datetime_field = "time";
140-
try {
141-
_datetime_length = coord_vars[_datetime_field].getDim(_datetime_field).getSize();
142-
}
143-
catch (netCDF::exceptions::NcNullGrp& e)
144-
{
145-
SPDLOG_ERROR("Tried datetime and time, coord not found");
146-
throw e;
147-
}
148-
189+
SPDLOG_ERROR("Could not find datetime coordinate");
190+
throw;
149191
}
150192

151-
152193
// if we don't have at least two timesteps, we can't figure out the model internal timestep length (dt)
153194
if(_datetime_length == 1)
154195
{
@@ -168,22 +209,47 @@ void netcdf::open_GEM(const std::string &file)
168209

169210
netCDF::NcVar times = _data.getVar(_datetime_field);
170211

171-
if(times.getType().getName() != "int64")
212+
//load in the time offsets
213+
auto* dt = new int64_t[_datetime_length];
214+
215+
if(times.getType().getName() == "int64")
172216
{
173-
CHM_THROW_EXCEPTION(forcing_error,"Datetime dimension not in int64 format");
217+
times.getVar(dt);
174218
}
219+
else if(times.getType().getName() == "double")
220+
{
221+
auto* tmp_dt = new double[_datetime_length];
222+
times.getVar(tmp_dt);
175223

224+
for(int i = 0; i < _datetime_length; i++)
225+
{
226+
dt[i] = static_cast<int64_t>(tmp_dt[i]);
227+
}
176228

177-
//load in the time offsets
178-
int64_t* dt = new int64_t[_datetime_length];
179-
times.getVar(dt);
229+
delete[] tmp_dt;
230+
}
231+
else
232+
{
233+
CHM_THROW_EXCEPTION(forcing_error, "Datetime dimension not in int64 or double type. Type is: " + times.getType().getName());
234+
}
180235

181236
//figure out what the epoch is
182237
// we are expecting the units attribute data to look like
183238
// hours since 2018-01-05 01:00:00
184239
std::string epoch;
185240
auto a = times.getAtt("units");
186-
a.getValues(epoch);
241+
242+
try
243+
{
244+
// SPDLOG_DEBUG(times.getAtt("units").getType().getName());
245+
a.getValues(epoch);
246+
}
247+
catch(...)
248+
{
249+
SPDLOG_ERROR("Datetime attributes are likely string attributes. Did you write this netcdf with xarray with engine=h5netcdf? Try engine=netcdf4");
250+
CHM_THROW_EXCEPTION(forcing_error, "Datetime string attributes not supported");
251+
}
252+
187253

188254

189255
if( epoch.find("hours") != std::string::npos )
@@ -206,15 +272,14 @@ void netcdf::open_GEM(const std::string &file)
206272

207273
} else
208274
{
209-
CHM_THROW_EXCEPTION(forcing_error, "Unknown datetime epoch offset unit.");
275+
CHM_THROW_EXCEPTION(forcing_error, "Unknown datetime epoch offset unit: " + epoch);
210276
}
211277

212278
std::vector<std::string> strs;
213279
boost::split(strs, epoch, boost::is_any_of(" "));
214280

215281
if(strs.size() != 4)
216282
{
217-
218283
//might be in iso format (2017-08-13T01:00:00)
219284
if(strs.size() != 3)
220285
{
@@ -245,8 +310,6 @@ void netcdf::open_GEM(const std::string &file)
245310
{
246311
CHM_THROW_EXCEPTION(forcing_error, "Unable to parse netcdf epoch time " + s);
247312
}
248-
249-
250313
}
251314
else
252315
{
@@ -294,17 +357,13 @@ void netcdf::open_GEM(const std::string &file)
294357
SPDLOG_DEBUG("NetCDF end is {}",boost::posix_time::to_simple_string(_end));
295358
SPDLOG_DEBUG("NetCDF timestep is {}", boost::posix_time::to_simple_string(_timestep));
296359

297-
auto dims = _data.getDims();
298-
for(auto itr : dims)
299-
{
300-
if(itr.first == "xgrid_0")
301-
xgrid = itr.second.getSize();
302-
else if(itr.first == "ygrid_0")
303-
ygrid = itr.second.getSize();
304-
}
360+
_lat_field = find_dim_by_standard_name("latitude");
361+
_lon_field = find_dim_by_standard_name("longitude");
305362

306-
SPDLOG_DEBUG("NetCDF grid is {} (x) by {} (y)", xgrid, ygrid);
363+
xgrid = _data.getDim(_lon_field).getSize();
364+
ygrid = _data.getDim(_lat_field).getSize();
307365

366+
SPDLOG_DEBUG("NetCDF grid is {} (x) by {} (y)", xgrid, ygrid);
308367

309368
}
310369

@@ -338,7 +397,7 @@ std::set<std::string> netcdf::get_variable_names()
338397
{
339398
auto vars = _data.getVars();
340399

341-
std::vector<std::string> exclude = {"datetime","leadtime", "reftime", "HGT_P0_L1_GST", "gridlat_0", "gridlon_0", "xgrid_0", "ygrid_0"};
400+
std::vector<std::string> exclude = {"datetime","leadtime", "reftime", "HGT_P0_L1_GST", _lat_field, _lon_field, "xgrid_0", "ygrid_0"};
342401

343402
for (auto itr: vars)
344403
{
@@ -464,11 +523,11 @@ double netcdf::get_var2D(std::string var, size_t x, size_t y)
464523

465524
netcdf::data netcdf::get_lat()
466525
{
467-
return get_var2D("gridlat_0");
526+
return get_var2D(_lat_field);
468527
}
469528
netcdf::data netcdf::get_lon()
470529
{
471-
return get_var2D("gridlon_0");
530+
return get_var2D(_lon_field);
472531
}
473532

474533
size_t netcdf::get_xsize()
@@ -482,11 +541,11 @@ size_t netcdf::get_ysize()
482541

483542
double netcdf::get_lat(size_t x, size_t y)
484543
{
485-
return get_var2D("gridlat_0",x,y);
544+
return get_var2D(_lat_field,x,y);
486545
}
487546
double netcdf::get_lon(size_t x, size_t y)
488547
{
489-
return get_var2D("gridlon_0",x,y);
548+
return get_var2D(_lon_field,x,y);
490549
}
491550

492551
double netcdf::get_z(size_t x, size_t y)

0 commit comments

Comments
 (0)