void grid_getdims(grid* g, int* ni, int* nj, int* nk) { if (ni != NULL) { if (g->htype == GRIDHTYPE_LATLON) { gnxy_simple* nodes = (gnxy_simple*) g->gridnodes_xy; *ni = nodes->nx; *nj = nodes->ny; #if !defined(NO_GRIDUTILS) } else if (g->htype == GRIDHTYPE_CURVILINEAR) { gnxy_curv* nodes = (gnxy_curv*) g->gridnodes_xy; *ni = gridnodes_getnx(nodes->gn); *nj = gridnodes_getny(nodes->gn); #endif } else enkf_quit("programming error"); } if (nk != NULL) { if (g->vtype == GRIDVTYPE_Z || g->vtype == GRIDVTYPE_SIGMA) *nk = ((gnz_simple *) g->gridnodes_z)->nz; else enkf_quit("not implemented"); } }
static void grid_setcoords(grid* g, int htype, int hnodetype, int nx, int ny, int nz, void* x, void* y, double* z) { g->htype = htype; if (htype == GRIDHTYPE_LATLON) g->gridnodes_xy = gnxy_simple_create(nx, ny, x, y); #if !defined(NO_GRIDUTILS) else if (htype == GRIDHTYPE_CURVILINEAR) { g->gridnodes_xy = gnxy_curv_create(hnodetype, nx, ny, x, y, g->maptype); #if defined(GRIDNODES_WRITE) { char fname[MAXSTRLEN]; snprintf(fname, MAXSTRLEN, "gridnodes-%d.txt", grid_getid(g)); if (!file_exists(fname)) gridnodes_write(((gnxy_curv*) g->gridnodes_xy)->gn, fname, CT_XY); } #endif } #endif else enkf_quit("programming error"); grid_setlonbase(g); if (g->vtype == GRIDVTYPE_Z || g->vtype == GRIDVTYPE_SIGMA) g->gridnodes_z = gnz_simple_create(nz, z); else enkf_quit("not implemented"); }
/** Calculates G = inv(I + S' * S) * S' = S' * inv(I + S * S'). */ void calc_G_denkf(int m, int p, double** S, int i, int j, double** G) { double** M; double a = 1.0; double b = 0.0; int lapack_info; if (p < m) { M = alloc2d(p, p, sizeof(double)); /* * M = inv(I + S * S') */ lapack_info = calc_M(p, m, 1, S, M); if (lapack_info != 0) enkf_quit("dpotrf() or dpotri(): lapack_info = %d at (i, j) = (%d, %d)", lapack_info, i, j); /* * G = S' * inv(I + S * S') */ dgemm_(&doT, &noT, &m, &p, &p, &a, S[0], &p, M[0], &p, &b, G[0], &m); } else { /* * M = inv(I + S' * S) */ M = alloc2d(m, m, sizeof(double)); lapack_info = calc_M(p, m, 0, S, M); if (lapack_info != 0) enkf_quit("dpotrf() or dpotri(): lapack_info = %d at (i, j) = (%d, %d)", lapack_info, i, j); /* * G = inv(I + S * S') * S' */ dgemm_(&noT, &doT, &m, &p, &m, &a, M[0], &m, S[0], &p, &b, G[0], &m); } #if defined(CHECK_G) { int e, o; /* * check that columns of G sum up to 0 */ for (o = 1; o < p; ++o) { double* Go = G[o]; double sumG = 0.0; double sumS = 0.0; for (e = 0; e < m; ++e) { sumG += Go[e]; sumS += S[e][o]; } if (fabs(sumG) > EPS) enkf_quit("inconsistency in G: column %d sums up to %.15f for (i, j) = (%d, %d); sum(S(%d,:) = %.15f)", o, sumG, i, j, o, sumS); } } #endif free(M); }
/** Calculates G = inv(I + S' * S) * S' and T = (I + S' * S)^-1/2. */ void calc_G_etkf(int m, int p, double** S, double alpha, int ii, int jj, double** G, double** T) { double** M = alloc2d(m, m, sizeof(double)); int lapack_info; int i; /* * dgemm stuff */ double a = 1.0; double b = 0.0; /* * M = S' * S */ dgemm_(&doT, &noT, &m, &m, &p, &a, S[0], &p, S[0], &p, &b, M[0], &m); /* * M = I + S' * S */ for (i = 0; i < m; ++i) M[i][i] += 1.0; lapack_info = invsqrtm2(m, alpha, M, T); /* M = M^-1, T = M^-1/2 */ if (lapack_info != 0) enkf_quit("dgesvd(): lapack_info = %d at (i, j) = (%d, %d)", lapack_info, ii, jj); /* * G = inv(I + S * S') * S' */ dgemm_(&noT, &doT, &m, &p, &m, &a, M[0], &m, S[0], &p, &b, G[0], &m); #if defined(CHECK_G) /* * check that columns of G sum up to 0 */ { int e, o; for (o = 1; o < p; ++o) { double* Go = G[o]; double sumG = 0.0; double sumS = 0.0; for (e = 0; e < m; ++e) { sumG += Go[e]; sumS += S[e][o]; } if (fabs(sumG) > EPS) enkf_quit("inconsistency in G: column %d sums up to %.15f for (i, j) = (%d, %d); sum(S(%d,:) = %.15f)", o, sumG, ii, jj, o, sumS); } } #endif free(M); }
static void obstype_check(obstype* type) { assert(type->name != NULL); if (type->issurface < 0) enkf_quit("\"%s\": ISSURFACE not specified\n"); if (type->varname == NULL) enkf_quit("\"%s\": VAR not specified\n"); if (type->hfunction == NULL) enkf_quit("\"%s\": HFUNCTION not specified\n"); }
static void model_checkvars(model* m, char* modelprm) { int i; for (i = 0; i < m->nvar; ++i) { variable* v = &m->vars[i]; if (!isfinite(v->inflation) || v->inflation <= 0) enkf_quit("\"%s\": \"%s\": inflation = %.3g\n", modelprm, v->name, v->inflation); if (v->deflation <= 0) enkf_quit("\"%s\": \"%s\": deflation = %.3g\n", modelprm, v->name, v->deflation); } }
void model_addorreplacedata(model* m, char tag[], int vid, int alloctype, void* data) { modeldata* mdata; int i; int ni, nj, nk; for (i = 0; i < m->ndata; ++i) if (strcmp(tag, m->data[i].tag) == 0) break; mdata = &m->data[i]; if (i == m->ndata) { if (m->ndata % NMODELDATA_INC == 0) m->data = realloc(m->data, (m->ndata + NMODELDATA_INC) * sizeof(modeldata)); mdata->tag = strdup(tag); mdata->vid = vid; mdata->alloctype = alloctype; m->ndata++; } else assert(mdata->alloctype == alloctype); model_getvardims(m, mdata->vid, &ni, &nj, &nk); if (mdata->alloctype == ALLOCTYPE_1D) memcpy(mdata->data, data, nk * sizeof(float)); else if (mdata->alloctype == ALLOCTYPE_2D) mdata->data = copy2d(data, nj, ni, sizeof(float)); else if (mdata->alloctype == ALLOCTYPE_3D) mdata->data = copy3d(data, nk, nj, ni, sizeof(float)); else enkf_quit("programming error"); }
void grid_ij2xy(grid* g, int i, int j, double* x, double* y) { if (g->htype == GRIDHTYPE_LATLON) { gnxy_simple* gs = (gnxy_simple*) g->gridnodes_xy; if (i < 0 || j < 0 || i >= gs->nx || j >= gs->ny) { *x = NaN; *y = NaN; } else { *x = gs->x[i]; *y = gs->y[j]; } } #if !defined(NO_GRIDUTILS) else if (g->htype == GRIDHTYPE_CURVILINEAR) { gridnodes* gn = ((gnxy_curv*) g->gridnodes_xy)->gn; if (i < 0 || j < 0 || i >= gridnodes_getnce1(gn) || j >= gridnodes_getnce2(gn)) { *x = NaN; *y = NaN; } else { *x = gridnodes_getx(gn)[j][i]; *y = gridnodes_gety(gn)[j][i]; } } #endif else enkf_quit("programming error"); }
static void z2fk(void* p, double fi, double fj, double z, double* fk) { grid* g = (grid*) p; gnz_simple* nodes = g->gridnodes_z; /* * for sigma coordinates convert `z' to sigma */ if (g->vtype == GRIDVTYPE_SIGMA) { int ni = 0, nj = 0; double depth; if (g->htype == GRIDHTYPE_LATLON) { gnxy_simple* nodes = (gnxy_simple*) g->gridnodes_xy; ni = nodes->nx; nj = nodes->ny; #if !defined(NO_GRIDUTILS) } else if (g->htype == GRIDHTYPE_CURVILINEAR) { gnxy_curv* nodes = (gnxy_curv*) g->gridnodes_xy; ni = gridnodes_getnx(nodes->gn); nj = gridnodes_getny(nodes->gn); #endif } else enkf_quit("programming error"); depth = (double) interpolate2d(fi, fj, ni, nj, g->depth, g->numlevels, grid_isperiodic_x(g)); z /= depth; } *fk = z2fk_basic(nodes->nz, nodes->zt, nodes->zc, z); }
void grid_z2fk(grid* g, double fi, double fj, double z, double* fk) { if (g->vtype == GRIDVTYPE_Z || g->vtype == GRIDVTYPE_SIGMA) z2fk(g, fi, fj, z, fk); else enkf_quit("not implemented"); }
/** Replaces observation errors in the observation file with the modified * values. The original values are stored as "std_orig". */ void das_addmodifiederrors(dasystem* das, char fname[]) { int ncid; int dimid_nobs[1]; size_t nobs; int varid_std; double* std; int i; if (rank != 0) return; ncw_open(fname, NC_WRITE, &ncid); ncw_inq_dimid(fname, ncid, "nobs", dimid_nobs); ncw_inq_dimlen(fname, ncid, dimid_nobs[0], &nobs); ncw_redef(fname, ncid); if (ncw_var_exists(ncid, "std_orig")) enkf_quit("\"observations.nc\" has already been modified by `enkf_calc\'. To proceed please remove observations*.nc and rerun `enkf_prep\'."); ncw_rename_var(fname, ncid, "std", "std_orig"); ncw_def_var(fname, ncid, "std", NC_FLOAT, 1, dimid_nobs, &varid_std); ncw_enddef(fname, ncid); std = malloc(nobs * sizeof(double)); for (i = 0; i < (int) nobs; ++i) std[i] = das->obs->data[i].std; ncw_put_var_double(fname, ncid, varid_std, std); free(std); ncw_close(fname, ncid); }
static void das_changeSmode(dasystem* das, int mode_from, int mode_to) { assert(das->s_mode == mode_from); if (das->obs->nobs == 0) goto finish; if (mode_from == S_MODE_HA_f && mode_to == S_MODE_HE_f) { observations* obs = das->obs; int e, o; for (e = 0; e < das->nmem; ++e) { ENSOBSTYPE* Se = das->S[e]; for (o = 0; o < obs->nobs; ++o) /* * das->s_f is innovation = obs - forecast; hence forecast = obs * - innovation */ Se[o] += obs->data[o].value - das->s_f[o]; } } else enkf_quit("das_changesmode(): transition from mode %d to mode %d is not handled yet\n", mode_from, mode_to); finish: das->s_mode = mode_to; }
static gnz* gnz_create(int nz, double* z) { gnz* nodes = malloc(sizeof(gnz)); int i; /* * reverse z if it is negative */ for (i = 0; i < nz; ++i) if (z[i] < -EPSZ) break; if (i < nz) for (i = 0; i < nz; ++i) z[i] = -z[i]; for (i = 0; i < nz; ++i) if (z[i] < -EPSZ) enkf_quit("layer centre coordinates should be either positive or negative only"); nodes->zt = z; nodes->nz = nz; /* * this code is supposed to work both for z and sigma grids */ nodes->zc = malloc((nz + 1) * sizeof(double)); if (z[nz - 1] >= z[0]) { /* * layer 0 at surface */ nodes->zc[0] = 0.0; for (i = 1; i <= nz; ++i) { nodes->zc[i] = 2.0 * z[i - 1] - nodes->zc[i - 1]; /* * layer boundary should be above the next layer centre */ if (i < nz) assert(nodes->zc[i] < z[i]); } } else { /* * layer 0 the deepest */ nodes->zc[nz] = 0.0; for (i = nz - 1; i >= 0; --i) { nodes->zc[i] = 2.0 * z[i] - nodes->zc[i + 1]; /* * layer boundary should be above the next layer centre */ if (i > 0) assert(nodes->zc[i] < z[i - 1]); } } return nodes; }
void* model_getgridbyname(model* m, char name[]) { int i; for (i = 0; i < m->ngrid; ++i) if (strcmp(grid_getname(m->grids[i]), name) == 0) return m->grids[i]; enkf_quit("model_getgridbyname(): found no grid named \"%s\"", name); return NULL; }
void grid_fij2xy(grid* g, double fi, double fj, double* x, double* y) { if (g->htype == GRIDHTYPE_LATLON) gs_fij2xy(g, fi, fj, x, y); #if !defined(NO_GRIDUTILS) else if (g->htype == GRIDHTYPE_CURVILINEAR) gc_fij2xy(g, fi, fj, x, y); #endif else enkf_quit("programming error"); }
void grid_xy2fij(grid* g, double x, double y, double* fi, double* fj) { if (g->htype == GRIDHTYPE_LATLON) gs_xy2fij(g, x, y, fi, fj); #if !defined(NO_GRIDUTILS) else if (g->htype == GRIDHTYPE_CURVILINEAR) gc_xy2fij(g, x, y, fi, fj); #endif else enkf_quit("programming error"); }
int grid_gettoplayerid(grid* g) { if (g->vtype == GRIDVTYPE_Z || g->vtype == GRIDVTYPE_SIGMA) { gnz_simple* nodes = g->gridnodes_z; int kmax = nodes->nz - 1; double* z = nodes->zt; return (fabs(z[0]) < fabs(z[kmax])) ? 0 : kmax; } else enkf_quit("not implemented"); return -1; }
int model_getvarid(model* m, char* varname, int hastosucceed) { int i; for (i = 0; i < m->nvar; ++i) if (strcmp(m->vars[i].name, varname) == 0) return i; if (hastosucceed) enkf_quit("model_getvarid(): can not find variable \"%s\" in the model", varname); return -1; }
/** Calculates X5 = G * s * 1' + T. * X5 is assumed being initialised to 0 * G is [m x p] * S is [p x m] * s is [p] * X5 is [m x m] */ void calc_X5_denkf(int m, int p, double** G, double** S, double* s, double alpha, int ii, int jj, double** X5) { double* w = calloc(m, sizeof(double)); double a, b; int i, j; /* * w = G * s */ if (enkf_nomeanupdate == 0) /* "normal" way */ calc_w(m, p, G, s, w); /* * X5 = G * s * 1^T */ for (j = 0; j < m; ++j) memcpy(X5[j], w, m * sizeof(double)); /* * X5 = G * s * 1^T - 1/2 * alpha * G * S */ a = -0.5 * alpha; b = 1.0; dgemm_(&noT, &noT, &m, &m, &p, &a, G[0], &m, S[0], &p, &b, X5[0], &m); /* * X5 = G * s * 1^T - 1/2 * alpha * G * S + I */ for (i = 0; i < m; ++i) X5[i][i] += 1.0; #if defined(CHECK_X5) /* * check that columns of X5 sum up to 1 */ for (i = 0; i < m; ++i) { double* X5i = X5[i]; double sum = 0.0; for (j = 0; j < m; ++j) sum += X5i[j]; if (fabs(sum - 1.0) > EPS) enkf_quit("inconsistency in X5: column %d sums up to %.15f for (i, j) = (%d, %d)", i, sum, ii, jj); } #endif free(w); }
void grid_ij2xy(grid* g, int i, int j, double* x, double* y) { if (g->htype == GRIDHTYPE_LATLON_REGULAR || g->htype == GRIDHTYPE_LATLON_IRREGULAR) { *x = ((gnxy_simple*) g->gridnodes_xy)->x[i]; *y = ((gnxy_simple*) g->gridnodes_xy)->y[j]; } #if !defined(NO_GRIDUTILS) else if (g->htype == GRIDHTYPE_CURVILINEAR) { *x = gridnodes_getx(((gnxy_curv*) g->gridnodes_xy)->gn)[j][i]; *y = gridnodes_gety(((gnxy_curv*) g->gridnodes_xy)->gn)[j][i]; } #endif else enkf_quit("programming error"); }
void grid_destroy(grid* g) { free(g->name); if (g->htype == GRIDHTYPE_LATLON) gnxy_simple_destroy(g->gridnodes_xy); #if !defined(NO_GRIDUTILS) else if (g->htype == GRIDHTYPE_CURVILINEAR) gnxy_curv_destroy(g->gridnodes_xy); #endif else enkf_quit("programming_error"); if (g->gridnodes_z != NULL) { if (g->vtype == GRIDVTYPE_Z || g->vtype == GRIDVTYPE_SIGMA) gnz_simple_destroy(g->gridnodes_z); else enkf_quit("not implemented"); } if (g->numlevels != NULL) free(g->numlevels); if (g->depth != NULL) free(g->depth); free(g); }
void model_addmodeldata(model* m, char tag[], int alloctype, void* data) { int i; for (i = 0; i < m->ndata; ++i) if (strcmp(tag, m->data[i].tag) == 0) enkf_quit("model data tag \"%s\" already in use", tag); if (m->ndata % NMODELDATA_INC == 0) m->data = realloc(m->data, (m->ndata + NMODELDATA_INC) * sizeof(modeldata)); m->data[m->ndata].tag = strdup(tag); m->data[m->ndata].alloctype = alloctype; m->data[m->ndata].data = data; m->ndata++; }
void das_destandardise(dasystem* das) { observations* obs = das->obs; double mult = sqrt((double) das->nmem - 1); int e, i; if (das->s_mode == S_MODE_HA_f || das->s_mode == S_MODE_HA_a) return; if (obs->nobs == 0) goto finish; for (e = 0; e < das->nmem; ++e) { ENSOBSTYPE* Se = das->S[e]; for (i = 0; i < obs->nobs; ++i) { observation* o = &obs->data[i]; Se[i] *= o->std * sqrt(obs->obstypes[o->type].rfactor) * mult; } } if (das->s_f != NULL) { for (i = 0; i < obs->nobs; ++i) { observation* o = &obs->data[i]; das->s_f[i] *= o->std * sqrt(obs->obstypes[o->type].rfactor) * mult; } } if (das->s_a != NULL) { for (i = 0; i < obs->nobs; ++i) { observation* o = &obs->data[i]; das->s_a[i] *= o->std * sqrt(obs->obstypes[o->type].rfactor) * mult; } } finish: if (das->s_mode == S_MODE_S_f) das->s_mode = S_MODE_HA_f; else if (das->s_mode == S_MODE_S_a) das->s_mode = S_MODE_HA_a; else enkf_quit("programming error"); }
void grid_print(grid* g, char offset[]) { int nx, ny, nz; enkf_printf("%sgrid info:\n", offset); switch (g->htype) { case GRIDHTYPE_LATLON: enkf_printf("%s hor type = LATLON\n", offset); enkf_printf("%s periodic by X = %s\n", offset, grid_isperiodic_x(g) ? "yes" : "no"); break; #if !defined(NO_GRIDUTILS) case GRIDHTYPE_CURVILINEAR: enkf_printf("%s hor type = CURVILINEAR\n", offset); if (g->maptype == GRIDMAP_TYPE_BINARY) enkf_printf("%s map type = BINARY TREE\n", offset); else if (g->maptype == GRIDMAP_TYPE_KDTREE) enkf_printf("%s map type = KD-TREE\n", offset); else enkf_quit("unknown grid map type"); break; #endif default: enkf_printf("%s h type = NONE\n", offset); } grid_getdims(g, &nx, &ny, &nz); enkf_printf("%s dims = %d x %d x %d\n", offset, nx, ny, nz); if (!isnan(g->lonbase)) enkf_printf("%s longitude range = [%.3f, %.3f]\n", offset, g->lonbase, g->lonbase + 360.0); else enkf_printf("%s longitude range = any\n", offset); switch (g->vtype) { case GRIDVTYPE_Z: enkf_printf("%s vert type = Z\n", offset); break; case GRIDVTYPE_SIGMA: enkf_printf("%s vert type = SIGMA\n", offset); break; default: enkf_printf("%s vert type = NONE\n", offset); } if (g->sfactor != 1.0) enkf_printf("%s SFACTOR = \"%.f\"\n", offset, g->sfactor); }
/** Replaces observation errors in the observation file with the modified * values. The original values are stored as "std_orig". */ void das_addmodifiederrors(dasystem* das, char fname[]) { int ncid; int dimid_nobs[1]; size_t nobs; int varid_std; double* std; int i; double da_julday = NaN; if (rank != 0) return; ncw_open(fname, NC_WRITE, &ncid); ncw_inq_dimid(ncid, "nobs", dimid_nobs); ncw_inq_dimlen(ncid, dimid_nobs[0], &nobs); ncw_get_att_double(ncid, NC_GLOBAL, "DA_JULDAY", &da_julday); if (!enkf_noobsdatecheck && (isnan(da_julday) || fabs(das->obs->da_date - da_julday) > 1e-6)) enkf_quit("\"observations.nc\" from a different cycle"); if (ncw_var_exists(ncid, "std_orig")) { enkf_printf(" nothing to do\n"); ncw_close(ncid); return; } ncw_redef(ncid); ncw_rename_var(ncid, "std", "std_orig"); ncw_def_var(ncid, "std", NC_FLOAT, 1, dimid_nobs, &varid_std); ncw_enddef(ncid); std = malloc(nobs * sizeof(double)); for (i = 0; i < (int) nobs; ++i) std[i] = das->obs->data[i].std; ncw_put_var_double(ncid, varid_std, std); free(std); ncw_close(ncid); }
void grid_destroy(grid* g) { free(g->name); if (g->htype == GRIDHTYPE_LATLON_REGULAR || g->htype == GRIDHTYPE_LATLON_IRREGULAR) gnxy_simple_destroy(g->gridnodes_xy); #if !defined(NO_GRIDUTILS) else if (g->htype == GRIDHTYPE_CURVILINEAR) gnxy_curv_destroy(g->gridnodes_xy); #endif else enkf_quit("programming_error"); if (g->gridnodes_z != NULL) gnz_destroy(g->gridnodes_z); if (g->numlevels != NULL) free2d(g->numlevels); if (g->depth != NULL) free2d(g->depth); free(g); }
static void model_freemodeldata(model* m) { int i; for (i = 0; i < m->ndata; ++i) { modeldata* data = &m->data[i]; if (data->alloctype == ALLOCTYPE_1D) free(data->data); else if (data->alloctype == ALLOCTYPE_2D) free2d(data->data); else if (data->alloctype == ALLOCTYPE_3D) free3d(data->data); else enkf_quit("programming error"); free(data->tag); } free(m->data); m->ndata = 0; }
void grid_getdims(grid* g, int* ni, int* nj, int* nk) { if (ni != NULL) { if (g->htype == GRIDHTYPE_LATLON_REGULAR || g->htype == GRIDHTYPE_LATLON_IRREGULAR) { gnxy_simple* nodes = (gnxy_simple*) g->gridnodes_xy; *ni = nodes->nx; *nj = nodes->ny; #if !defined(NO_GRIDUTILS) } else if (g->htype == GRIDHTYPE_CURVILINEAR) { gnxy_curv* nodes = (gnxy_curv*) g->gridnodes_xy; *ni = gridnodes_getnx(nodes->gn); *nj = gridnodes_getny(nodes->gn); #endif } else enkf_quit("programming error"); } if (nk != NULL) *nk = g->gridnodes_z->nz; }
/** Calculates X5 = G * s * 1' + T. * X5 = T on input */ void calc_X5_etkf(int m, int p, double** G, double* s, int ii, int jj, double** X5) { double* w = calloc(m, sizeof(double)); int i, j; /* * w = G * s */ if (enkf_nomeanupdate == 0) /* "normal" way */ calc_w(m, p, G, s, w); /* * X5 = G * s * 1^T + T */ for (i = 0; i < m; ++i) { double* X5i = X5[i]; for (j = 0; j < m; ++j) X5i[j] += w[j]; } #if defined(CHECK_X5) /* * check that columns of X5 sum up to 1 */ for (i = 0; i < m; ++i) { double* X5i = X5[i]; double sum = 0.0; for (j = 0; j < m; ++j) sum += X5i[j]; if (fabs(sum - 1.0) > EPS) enkf_quit("inconsistency in X5: column %d sums up to %.15f for (i, j) = (%d, %d)", i, sum, ii, jj); } #endif free(w); }
void grid_fk2z(grid* g, int i, int j, double fk, double* z) { if (g->vtype == GRIDVTYPE_Z || g->vtype == GRIDVTYPE_SIGMA) { gnz_simple* nodes = g->gridnodes_z; double* zc = nodes->zc; int nt = nodes->nz; fk += 0.5; if (fk <= 0.0) *z = zc[0]; else if (fk >= nt) *z = zc[nt]; else { int k = (int) floor(fk); *z = zc[k] + (fk - (double) k) * (zc[k + 1] - zc[k]); } if (g->vtype == GRIDVTYPE_SIGMA) *z *= g->depth[j][i]; } else enkf_quit("not implemented"); }