diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4f1e33e..e4ffbef 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,7 +17,7 @@ repos: gsw/tests/test_gibbs\.py )$ args: - - --ignore-words-list=nin,preformed,wih, + - --ignore-words-list=nin,preformed,wih,pres, - repo: https://github.com/tox-dev/pyproject-fmt rev: v2.11.1 diff --git a/gsw/tests/test_geostrophy.py b/gsw/tests/test_geostrophy.py index 8d69bb6..1a1508a 100644 --- a/gsw/tests/test_geostrophy.py +++ b/gsw/tests/test_geostrophy.py @@ -90,6 +90,21 @@ def test_dyn_height_shallower_pref(): expected = strf0[1:] - strf0[1] assert_almost_equal(found, expected) +def test_dyn_height_grid_bug(): + """ + The C toolbox had a bug in the interpolation grid generation, triggered + under specific circumstances demonstrated in issue #225. The fix was copied + from https://github.com/TEOS-10/GSW-C/pull/87. + """ + SA = np.array([34.17634373, 34.17633109, 34.17725485, 34.90704565]) + CT = np.array([ 0.02005831, 0.02003146, 0.01868686, 1.7676452 ]) + pres = np.array([ 4.9000001 , 5.69999981, 15.80000019, 995.90002441]) + # Last 2 args: grid delta-p is 1; interp method is 3 (mrst). + dh1 = gsw._gsw_ufuncs.geo_strf_dyn_height_1(SA, CT, pres, pres[-1], 1, 3) + assert dh1[-1] == 0 + dh2 = gsw.geo_strf_dyn_height(SA, CT, pres, p_ref = pres[-1], interp_method="mrst") + assert_array_equal(dh1, dh2) + def test_pz_roundtrip(): """ The p_z conversion functions have Matlab-based checks that use @@ -124,4 +139,4 @@ def test_steric_height_mrst(): pr = cv.pr strf = gsw.geo_strf_steric_height(SA, CT, p, p_ref=pr, interp_method='mrst') - assert_allclose(strf, cv.geo_strf_steric_height, rtol=0, atol=cv.geo_strf_steric_height_ca) \ No newline at end of file + assert_allclose(strf, cv.geo_strf_steric_height, rtol=0, atol=cv.geo_strf_steric_height_ca) diff --git a/src/c_gsw/gsw_oceanographic_toolbox.c b/src/c_gsw/gsw_oceanographic_toolbox.c index 36f1918..d0715e5 100644 --- a/src/c_gsw/gsw_oceanographic_toolbox.c +++ b/src/c_gsw/gsw_oceanographic_toolbox.c @@ -4057,7 +4057,7 @@ gsw_geo_strf_dyn_height_1(double *sa, double *ct, double *p, double p_ref, Its return argument is the number of elements used in p_i, or -1 on error. */ -static int refine_grid_for_dh(double *p, double p_ref, int nz, +int refine_grid_for_dh(double *p, double p_ref, int nz, double dp, double *p_i, int ni_max, /* size of p_i array; larger than needed */ int *p_indices, int *p_ref_ind_ptr) @@ -4094,10 +4094,16 @@ static int refine_grid_for_dh(double *p, double p_ref, int nz, /* We did not insert p_ref, so insert either p_next or p[iorig]. */ if (p_next < p[iorig] - p_tol) { p_i[i] = p_next; + if (p_ref == p_next) { + *p_ref_ind_ptr = i; + } iuniform++; } else { p_i[i] = p[iorig]; + if (p_ref == p[iorig]) { + *p_ref_ind_ptr = i; + } p_indices[iorig] = i; /* Skip this p_next if it is close to the point we just added. */ if (p_next < p[iorig] + p_tol) { diff --git a/src/c_gsw/gswteos-10.h b/src/c_gsw/gswteos-10.h index 4fb1e76..0f27387 100644 --- a/src/c_gsw/gswteos-10.h +++ b/src/c_gsw/gswteos-10.h @@ -318,6 +318,8 @@ DECLSPEC extern double gsw_z_from_p(double p, double lat, double geo_strf_dyn_he double sea_surface_geopotential); DECLSPEC extern double gsw_p_from_z(double z, double lat, double geo_strf_dyn_height, double sea_surface_geopotential); +DECLSPEC extern int refine_grid_for_dh(double *p, double p_ref, int nz, + double dp, double *p_i, int ni_max, int *p_indices, int *p_ref_ind_ptr); #ifdef __cplusplus } diff --git a/tools/c_header_parser.py b/tools/c_header_parser.py index b36ba88..458e1ea 100644 --- a/tools/c_header_parser.py +++ b/tools/c_header_parser.py @@ -59,7 +59,11 @@ def parse_signature(sig): argtypes.append(parts[0] + parts[1]) argnames.append(parts[2]) - retgroups = retpat.match(sig).groups() + try: + retgroups = retpat.match(sig).groups() + except AttributeError: + # For example, name doesn't start with "gsw_". + return None ret = retgroups[0] + retgroups[1] funcname = retgroups[2] @@ -80,7 +84,8 @@ def parse_signatures(sigs): sigdict = {} for sig in sigs: psig = parse_signature(sig) - sigdict[psig['name']] = psig + if psig is not None: + sigdict[psig['name']] = psig return sigdict def get_sigdict(srcdir="src"):