00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include <cstring>
00020 #include "../udunits/udunits.h"
00021 #include "pism_const.hh"
00022 #include "nc_util.hh"
00023
00024 int nc_check(int stat) {
00025 if (stat)
00026 SETERRQ1(1, "NC_ERR: %s\n", nc_strerror(stat));
00027 return 0;
00028 }
00029
00030 int check_err(const int stat, const int line, const char *file) {
00031 if (stat != NC_NOERR) {
00032 (void) fprintf(stderr, "line %d of %s: %s\n", line, file, nc_strerror(stat));
00033 SETERRQ1(1, "NC_ERR: %s\n", nc_strerror(stat));
00034
00035 }
00036 return 0;
00037 }
00038
00039 NCTool::NCTool(MPI_Comm c, PetscMPIInt r)
00040 : com(c), rank(r) {
00041 ncid = -1;
00042
00043
00044 if (utIsInit() == 0) {
00045 if (utInit(NULL) != 0) {
00046 PetscPrintf(com, "PISM ERROR: UDUNITS initialization failed.\n");
00047 PetscEnd();
00048 }
00049 }
00050 }
00051
00052 NCTool::~NCTool() {
00053 if (ncid >= 0) {
00054 PetscPrintf(com, "NCTool::~NCTool() ncid >= 0.\n");
00055 PetscEnd();
00056 }
00057 }
00058
00060 int NCTool::get_ncid() const {
00061 return ncid;
00062 }
00063
00064 PetscErrorCode NCTool::find_dimension(const char short_name[], int *dimid, bool &exists) const {
00065 PetscErrorCode ierr;
00066 int stat, found = 0, my_dimid;
00067 if (rank == 0) {
00068 stat = nc_inq_dimid(ncid, short_name, &my_dimid);
00069 if (stat == NC_NOERR)
00070 found = 1;
00071 }
00072 ierr = MPI_Bcast(&found, 1, MPI_INT, 0, com); CHKERRQ(ierr);
00073 ierr = MPI_Bcast(&my_dimid, 1, MPI_INT, 0, com); CHKERRQ(ierr);
00074
00075
00076 if (found) {
00077 exists = true;
00078 if (dimid != NULL)
00079 *dimid = my_dimid;
00080 } else {
00081 exists = false;
00082
00083 }
00084
00085 return 0;
00086 }
00087
00089
00105 PetscErrorCode NCTool::find_variable(string short_name, string standard_name,
00106 int *varidp, bool &exists,
00107 bool &found_by_standard_name) const {
00108 int ierr;
00109 int stat, found = 0, my_varid = -1, nvars;
00110 bool standard_name_match = false;
00111
00112 if (standard_name != "") {
00113 if (rank == 0) {
00114 ierr = nc_inq_nvars(ncid, &nvars); CHKERRQ(check_err(ierr,__LINE__,__FILE__));
00115 }
00116 ierr = MPI_Bcast(&nvars, 1, MPI_INT, 0, com); CHKERRQ(ierr);
00117
00118 string attribute;
00119 for (int j = 0; j < nvars; j++) {
00120 ierr = get_att_text(j, "standard_name", attribute); CHKERRQ(ierr);
00121 if (attribute == "")
00122 continue;
00123
00124 if (attribute == standard_name) {
00125 if (!found) {
00126 found = true;
00127 standard_name_match = true;
00128 my_varid = j;
00129 } else {
00130 ierr = PetscPrintf(com,
00131 "PISM ERROR: Inconsistency in the input file: "
00132 "Variables #%d and #%d have the same standard_name ('%s').\n",
00133 my_varid, j, attribute.c_str());
00134 CHKERRQ(ierr);
00135 PetscEnd();
00136 }
00137 }
00138 }
00139 }
00140
00141
00142 if (!found) {
00143 if (rank == 0) {
00144 stat = nc_inq_varid(ncid, short_name.c_str(), &my_varid);
00145 if (stat == NC_NOERR)
00146 found = true;
00147 }
00148 ierr = MPI_Bcast(&found, 1, MPI_INT, 0, com); CHKERRQ(ierr);
00149 ierr = MPI_Bcast(&my_varid, 1, MPI_INT, 0, com); CHKERRQ(ierr);
00150 }
00151
00152 if (found) {
00153 exists = true;
00154 found_by_standard_name = standard_name_match;
00155 if (varidp != NULL)
00156 *varidp = my_varid;
00157 } else {
00158 exists = false;
00159
00160 }
00161
00162 return 0;
00163 }
00164
00165 PetscErrorCode NCTool::find_variable(string short_name, string standard_name,
00166 int *varidp, bool &exists) const {
00167 bool dummy;
00168 PetscErrorCode ierr = find_variable(short_name, standard_name, varidp, exists, dummy);
00169 CHKERRQ(ierr);
00170 return 0;
00171 }
00172
00173
00174 PetscErrorCode NCTool::find_variable(string short_name, int *varid, bool &exists) const {
00175 return find_variable(short_name, "", varid, exists);
00176 }
00177
00179 PetscErrorCode NCTool::get_last_time(double *time) const {
00180 PetscErrorCode ierr;
00181 ierr = get_dim_limits("t", NULL, time); CHKERRQ(ierr);
00182 return 0;
00183 }
00184
00185
00187
00191 PetscErrorCode NCTool::get_vertical_dims(double* &z_levels, double* &zb_levels) const {
00192 int stat;
00193 int z_id, zb_id, z_len, zb_len;
00194 size_t zero = 0, nc_z_len, nc_zb_len;
00195
00196 stat = get_dim_length("z", &z_len); CHKERRQ(stat);
00197 stat = get_dim_length("zb", &zb_len); CHKERRQ(stat);
00198
00199 z_levels = new double[z_len];
00200 zb_levels = new double[zb_len];
00201
00202 nc_z_len = (size_t) z_len;
00203 nc_zb_len = (size_t) zb_len;
00204
00205 if (rank == 0) {
00206 stat = nc_inq_varid(ncid, "z", &z_id);
00207 if (stat != NC_NOERR) {
00208 stat = PetscPrintf(com, "PISM ERROR: Can't find the 'z' coordinate variable.\n"); CHKERRQ(stat);
00209 PetscEnd();
00210 }
00211
00212 stat = nc_inq_varid(ncid, "zb", &zb_id);
00213 if (stat != NC_NOERR) {
00214 stat = PetscPrintf(com, "PISM ERROR: Can't find the 'zb' coordinate variable.\n"); CHKERRQ(stat);
00215 PetscEnd();
00216 }
00217
00218 stat = nc_get_vara_double(ncid, z_id, &zero, &nc_z_len, z_levels);
00219 CHKERRQ(check_err(stat,__LINE__,__FILE__));
00220 stat = nc_get_vara_double(ncid, zb_id, &zero, &nc_zb_len, zb_levels);
00221 CHKERRQ(check_err(stat,__LINE__,__FILE__));
00222 }
00223
00224 MPI_Bcast(z_levels, z_len, MPI_DOUBLE, 0, com);
00225 MPI_Bcast(zb_levels, zb_len, MPI_DOUBLE, 0, com);
00226 return 0;
00227 }
00228
00229
00231
00234 PetscErrorCode NCTool::put_dimension_regular(int varid, int len, double start, double end) const {
00235 PetscErrorCode ierr;
00236 int stat;
00237 double *v, delta;
00238
00239 if (end <= start)
00240 SETERRQ2(1, "Can't write dimension: start = %f, end = %f",
00241 start, end);
00242
00243 delta = (end - start) / (len - 1);
00244
00245 ierr = PetscMalloc(len * sizeof(double), &v); CHKERRQ(ierr);
00246 for (int i = 0; i < len; i++)
00247 v[i] = start + i * delta;
00248
00249
00250
00251
00252 if (v[len - 1] > end) v[len - 1] = end;
00253
00254 stat = nc_put_var_double(ncid, varid, v); CHKERRQ(check_err(stat,__LINE__,__FILE__));
00255 ierr = PetscFree(v); CHKERRQ(ierr);
00256
00257 return 0;
00258 }
00259
00260
00262 PetscErrorCode NCTool::put_dimension(int varid, int len, PetscScalar *vals) const {
00263 PetscErrorCode ierr;
00264 int stat;
00265 double *v;
00266
00267 ierr = PetscMalloc(len * sizeof(double), &v); CHKERRQ(ierr);
00268 for (int i = 0; i < len; i++) {
00269 v[i] = (double)vals[i];
00270 }
00271 stat = nc_put_var_double(ncid, varid, v); CHKERRQ(check_err(stat,__LINE__,__FILE__));
00272 ierr = PetscFree(v); CHKERRQ(ierr);
00273 return 0;
00274 }
00275
00277
00280 PetscErrorCode NCTool::write_history(const char history[], bool overwrite) const {
00281 int stat;
00282 string old_history, new_history;
00283
00284
00285 stat = get_att_text(NC_GLOBAL, "history", old_history); CHKERRQ(stat);
00286
00287 if (overwrite) {
00288 new_history = history;
00289 } else {
00290 new_history = history + old_history;
00291 }
00292
00293
00294 if (rank == 0) {
00295 stat = nc_redef(ncid); CHKERRQ(check_err(stat,__LINE__,__FILE__));
00296
00297 stat = nc_put_att_text(ncid, NC_GLOBAL, "history",
00298 new_history.size(), new_history.c_str());
00299 CHKERRQ(check_err(stat,__LINE__,__FILE__));
00300
00301 stat = nc_enddef(ncid); CHKERRQ(check_err(stat,__LINE__,__FILE__));
00302 }
00303
00304 return 0;
00305 }
00306
00308
00314 bool NCTool::check_dimension(const char name[], const int len) const {
00315 int stat, dimid;
00316 size_t dimlen;
00317
00318 if (rank == 0) {
00319 stat = nc_inq_dimid(ncid, name, &dimid);
00320 if (stat == NC_NOERR) {
00321 if (len < 0)
00322 return true;
00323
00324 stat = nc_inq_dimlen(ncid, dimid, &dimlen);
00325 if (stat != NC_NOERR)
00326 return false;
00327
00328 if ((int)dimlen == len)
00329 return true;
00330 }
00331
00332 return false;
00333 }
00334
00335 return true;
00336 }
00337
00338
00340 PetscErrorCode NCTool::append_time(PetscReal time) const {
00341 int stat, t_id;
00342
00343
00344 if (rank == 0) {
00345 size_t t_len;
00346 stat = nc_inq_dimid(ncid, "t", &t_id); CHKERRQ(check_err(stat,__LINE__,__FILE__));
00347 stat = nc_inq_dimlen(ncid, t_id, &t_len); CHKERRQ(check_err(stat,__LINE__,__FILE__));
00348
00349 stat = nc_inq_varid(ncid, "t", &t_id); CHKERRQ(check_err(stat,__LINE__,__FILE__));
00350
00351 stat = nc_put_var1_double(ncid, t_id, &t_len, &time); CHKERRQ(check_err(stat,__LINE__,__FILE__));
00352 }
00353
00354 return 0;
00355 }
00356
00358
00361 PetscErrorCode NCTool::open_for_reading(const char filename[]) {
00362 PetscErrorCode ierr;
00363 int stat = 0;
00364
00365 if (ncid >= 0) SETERRQ(1, "NCTool::open_for_reading(): ncid >= 0 at the beginning of the call");
00366
00367 if (rank == 0) {
00368 stat = nc_open(filename, NC_NOWRITE, &ncid);
00369 }
00370 ierr = MPI_Bcast(&stat, 1, MPI_INT, 0, com); CHKERRQ(ierr);
00371 ierr = MPI_Bcast(&ncid, 1, MPI_INT, 0, com); CHKERRQ(ierr);
00372
00373 if (stat != NC_NOERR) {
00374 ierr = PetscPrintf(com, "ERROR: Can't open file '%s'!\n",
00375 filename); CHKERRQ(ierr);
00376 PetscEnd();
00377 }
00378
00379 return 0;
00380 }
00381
00383 PetscErrorCode NCTool::close() {
00384 PetscErrorCode ierr;
00385 if (rank == 0) {
00386 ierr = nc_close(ncid); CHKERRQ(check_err(ierr,__LINE__,__FILE__));
00387 }
00388 ncid = -1;
00389 return 0;
00390 }
00391
00393 PetscErrorCode NCTool::open_for_writing(const char filename[]) {
00394 int stat;
00395
00396 if (ncid >= 0) SETERRQ(1, "NCTool::open_for_writing(): ncid >= 0 at the beginning of the call");
00397
00398 if (rank == 0) {
00399 bool file_exists = false;
00400
00401
00402 if (FILE *f = fopen(filename, "r")) {
00403 file_exists = true;
00404 fclose(f);
00405 } else {
00406 file_exists = false;
00407 }
00408
00409 if (file_exists) {
00410 stat = nc_open(filename, NC_WRITE, &ncid);
00411 if (stat != NC_NOERR) {
00412 stat = PetscPrintf(com, "PISM ERROR: Can't open file '%s'. NetCDF error: %s\n",
00413 filename, nc_strerror(stat)); CHKERRQ(stat);
00414 PetscEnd();
00415 }
00416 } else {
00417 stat = nc_create(filename, NC_CLOBBER|NC_64BIT_OFFSET, &ncid);
00418 CHKERRQ(check_err(stat,__LINE__,__FILE__));
00419 stat = nc_enddef(ncid); CHKERRQ(check_err(stat,__LINE__,__FILE__));
00420 }
00421
00422 stat = nc_set_fill(ncid, NC_NOFILL, NULL); CHKERRQ(check_err(stat,__LINE__,__FILE__));
00423 }
00424
00425 stat = MPI_Bcast(&ncid, 1, MPI_INT, 0, com); CHKERRQ(stat);
00426
00427 return 0;
00428 }
00429
00431 PetscErrorCode NCTool::get_dim_length(const char name[], int *len) const {
00432 int stat, dim_id;
00433
00434 if (rank == 0) {
00435 size_t dim_len;
00436 stat = nc_inq_dimid(ncid, name, &dim_id);
00437 if (stat == NC_NOERR) {
00438 stat = nc_inq_dimlen(ncid, dim_id, &dim_len); CHKERRQ(check_err(stat,__LINE__,__FILE__));
00439 } else
00440 dim_len = 0;
00441
00442 *len = static_cast<int>(dim_len);
00443 }
00444
00445 stat = MPI_Bcast(len, 1, MPI_INT, 0, com); CHKERRQ(stat);
00446
00447 return 0;
00448 }
00449
00451
00457 PetscErrorCode NCTool::get_dim_limits(const char name[], double *min, double *max) const {
00458 PetscErrorCode ierr;
00459 int len = 0, varid = -1;
00460 bool variable_exists = false;
00461 size_t start = 0, count;
00462 double range[2] = {0, 0};
00463
00464 ierr = get_dim_length(name, &len); CHKERRQ(ierr);
00465
00466 if (len != 0) {
00467 ierr = find_variable(name, &varid, variable_exists);
00468 if (!variable_exists) {
00469 ierr = PetscPrintf(com, "PISM ERROR: coordinate variable '%s' does not exist.\n",
00470 name);
00471 CHKERRQ(ierr);
00472 PetscEnd();
00473 }
00474
00475 if (rank == 0) {
00476 double *data;
00477 data = new double[len];
00478
00479 count = static_cast<size_t>(len);
00480 ierr = nc_get_vara_double(ncid, varid, &start, &count, data); CHKERRQ(check_err(ierr,__LINE__,__FILE__));
00481
00482 range[0] = data[0];
00483 range[1] = data[0];
00484 for (int j = 1; j < len; j++) {
00485 range[0] = PetscMin(data[j], range[0]);
00486 range[1] = PetscMax(data[j], range[1]);
00487 }
00488 delete[] data;
00489 }
00490 ierr = MPI_Bcast(range, 2, MPI_DOUBLE, 0, com); CHKERRQ(ierr);
00491
00492 char internal_units[TEMPORARY_STRING_LENGTH];
00493 utUnit input, internal;
00494 bool input_has_units;
00495
00496 if (strcmp(name, "t") == 0) {
00497
00498 strcpy(internal_units, "seconds");
00499 } else {
00500 strcpy(internal_units, "meters");
00501 }
00502
00503 if (utScan(internal_units, &internal) != 0) {
00504 SETERRQ(1, "UDUNITS failed trying to scan internal units.");
00505 }
00506
00507
00508 ierr = get_units(varid, input_has_units, input); CHKERRQ(ierr);
00509 if (!input_has_units) {
00510 ierr = verbPrintf(3, com,
00511 "PISM WARNING: dimensional variable '%s' does not have the units attribute.\n"
00512 " Assuming that it is in '%s'.\n",
00513 name, internal_units); CHKERRQ(ierr);
00514 utCopy(&internal, &input);
00515 }
00516
00517
00518 double slope, intercept;
00519 ierr = utConvert(&input, &internal, &slope, &intercept);
00520 if (ierr != 0) {
00521 if (ierr == UT_ECONVERT) {
00522 ierr = PetscPrintf(com,
00523 "PISM ERROR: dimensional variable '%s' has the units that are not compatible with '%s'.\n",
00524 name, internal_units); CHKERRQ(ierr);
00525 PetscEnd();
00526 }
00527 SETERRQ1(1, "UDUNITS failure: error code = %d", ierr);
00528 }
00529
00530
00531
00532 if (min != NULL)
00533 *min = intercept + range[0]*slope;
00534 if (max != NULL)
00535 *max = intercept + range[1]*slope;
00536
00537 return 0;
00538 }
00539
00540 if (min != NULL) *min = 0.0;
00541 if (max != NULL) *max = 0.0;
00542
00543 return 0;
00544 }
00545
00547
00550 PetscErrorCode NCTool::get_att_text(const int varid, const char name[], string &result) const {
00551 char *str = NULL;
00552 int ierr, stat, len;
00553
00554
00555 if (rank == 0) {
00556 size_t attlen;
00557 stat = nc_inq_attlen(ncid, varid, name, &attlen);
00558 if (stat == NC_NOERR)
00559 len = static_cast<int>(attlen);
00560 else
00561 len = 0;
00562 }
00563 ierr = MPI_Bcast(&len, 1, MPI_INT, 0, com); CHKERRQ(ierr);
00564
00565
00566 if (len == 0) {
00567 result = "";
00568 return 0;
00569 }
00570 str = new char[len + 1];
00571
00572
00573 ierr = PetscMemzero(str, len+1);CHKERRQ(ierr);
00574
00575
00576 if (rank == 0) {
00577 stat = nc_get_att_text(ncid, varid, name, str);
00578 }
00579 ierr = MPI_Bcast(&stat, 1, MPI_INT, 0, com); CHKERRQ(ierr);
00580
00581
00582 if (stat == NC_NOERR) {
00583 stat = MPI_Bcast(str, len, MPI_CHAR, 0, com); CHKERRQ(stat);
00584 } else {
00585 strcpy(str, "");
00586 }
00587
00588 result = str;
00589
00590 delete[] str;
00591 return 0;
00592 }
00593
00595 PetscErrorCode NCTool::get_att_double(const int varid, const char name[],
00596 vector<double> &result) const {
00597 int ierr, stat, len;
00598
00599
00600 if (rank == 0) {
00601 size_t attlen;
00602 stat = nc_inq_attlen(ncid, varid, name, &attlen);
00603 if (stat == NC_NOERR)
00604 len = static_cast<int>(attlen);
00605 else
00606 len = 0;
00607 }
00608 ierr = MPI_Bcast(&len, 1, MPI_INT, 0, com); CHKERRQ(ierr);
00609
00610 if (len == 0) {
00611 result.clear();
00612 return 0;
00613 }
00614
00615 result.resize(len);
00616
00617 if (rank == 0) {
00618 stat = nc_get_att_double(ncid, varid, name, &result[0]);
00619 }
00620 ierr = MPI_Bcast(&stat, 1, MPI_INT, 0, com); CHKERRQ(ierr);
00621
00622
00623 if (stat == NC_NOERR) {
00624 ierr = MPI_Bcast(&result[0], len, MPI_DOUBLE, 0, com); CHKERRQ(ierr);
00625 } else {
00626 SETERRQ(1, "Error reading an attribute.");
00627 }
00628
00629 return 0;
00630 }
00631
00633
00636 PetscErrorCode NCTool::get_units(int varid, bool &has_units, utUnit &units) const {
00637 PetscErrorCode ierr;
00638 string units_string;
00639
00640
00641 ierr = get_att_text(varid, "units", units_string); CHKERRQ(ierr);
00642
00643
00644 if (units_string.empty()) {
00645 has_units = false;
00646 utClear(&units);
00647 return 0;
00648 }
00649
00650
00651
00652
00653
00654
00655 int n = units_string.find("since");
00656 if (n != -1) units_string.resize(n);
00657
00658 ierr = utScan(units_string.c_str(), &units);
00659 if (ierr != 0) {
00660 ierr = PetscPrintf(com, "PISM ERROR: units specification '%s' is unknown or invalid.\n",
00661 units_string.c_str());
00662 PetscEnd();
00663 }
00664
00665 has_units = true;
00666
00667 return 0;
00668 }
00669
00671 PetscErrorCode NCTool::inq_nattrs(int varid, int &N) const {
00672 int stat;
00673
00674 if (rank == 0) {
00675 stat = nc_inq_varnatts(ncid, varid, &N); CHKERRQ(check_err(stat,__LINE__,__FILE__));
00676 }
00677 stat = MPI_Bcast(&N, 1, MPI_INT, 0, com); CHKERRQ(stat);
00678
00679 return 0;
00680 }
00681
00683 PetscErrorCode NCTool::inq_att_type(int varid, const char name[], nc_type &result) const {
00684 int stat, type;
00685 nc_type tmp;
00686
00687 if (rank == 0) {
00688 stat = nc_inq_atttype(ncid, varid, name, &tmp); CHKERRQ(check_err(stat,__LINE__,__FILE__));
00689 type = static_cast<int>(tmp);
00690 }
00691 stat = MPI_Bcast(&type, 1, MPI_INT, 0, com); CHKERRQ(stat);
00692
00693 result = static_cast<nc_type>(type);
00694
00695 return 0;
00696 }
00697
00699 PetscErrorCode NCTool::inq_att_name(int varid, int n, string &name) const {
00700 int stat;
00701 char tmp[NC_MAX_NAME];
00702
00703 if (rank == 0) {
00704 stat = nc_inq_attname(ncid, varid, n, tmp); CHKERRQ(check_err(stat,__LINE__,__FILE__));
00705 }
00706 stat = MPI_Bcast(tmp, NC_MAX_NAME, MPI_CHAR, 0, com); CHKERRQ(stat);
00707
00708 name = tmp;
00709 return 0;
00710 }
00711
00713
00716 PetscErrorCode NCTool::inq_dimids(int varid, vector<int> &dimids) const {
00717 int stat;
00718 int ndims;
00719
00720 if (rank == 0) {
00721 stat = nc_inq_varndims(ncid, varid, &ndims); CHKERRQ(check_err(stat,__LINE__,__FILE__));
00722 }
00723 stat = MPI_Bcast(&ndims, 1, MPI_INT, 0, com); CHKERRQ(stat);
00724
00725 if (ndims == 0) {
00726 dimids.clear();
00727 return 0;
00728 }
00729
00730 dimids.resize(ndims);
00731
00732
00733 if (rank == 0) {
00734 stat = nc_inq_vardimid(ncid, varid, &dimids[0]); CHKERRQ(check_err(stat,__LINE__,__FILE__));
00735 }
00736 stat = MPI_Bcast(&dimids[0], ndims, MPI_INT, 0, com); CHKERRQ(stat);
00737
00738 return 0;
00739 }
00740
00742 PetscErrorCode NCTool::inq_dimname(int dimid, string &name) const {
00743 int stat;
00744 char tmp[NC_MAX_NAME];
00745
00746 if (rank == 0) {
00747 stat = nc_inq_dimname(ncid, dimid, tmp); CHKERRQ(check_err(stat,__LINE__,__FILE__));
00748 }
00749 stat = MPI_Bcast(tmp, NC_MAX_NAME, MPI_CHAR, 0, com); CHKERRQ(stat);
00750
00751 name = tmp;
00752 return 0;
00753 }
00754
00756 PetscErrorCode NCTool::inq_unlimdim(int &unlimdimid) const {
00757 int stat;
00758
00759 if (rank == 0) {
00760 stat = nc_inq_unlimdim(ncid, &unlimdimid); CHKERRQ(check_err(stat,__LINE__,__FILE__));
00761 }
00762 stat = MPI_Bcast(&unlimdimid, 1, MPI_INT, 0, com); CHKERRQ(stat);
00763
00764 return 0;
00765 }