Mercurial > hg > MonetDB-lsst
view lsst.c @ 1:b1e59438bf0c default tip
Updated directory in stable test output.
author | Sjoerd Mullender <sjoerd@acm.org> |
---|---|
date | Fri, 25 May 2018 14:14:55 +0200 (2018-05-25) |
parents | 710dc03b1ac9 |
children |
line wrap: on
line source
/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 1997 - July 2008 CWI, August 2008 - 2018 MonetDB B.V. */ #include <monetdb_config.h> #include <mal_client.h> #include <mal_interpreter.h> #define _USE_MATH_DEFINES /* needed for WIN32 to define M_PI */ #include <math.h> #include <string.h> #ifndef _MSC_VER /* not Windows */ #define __declspec(x) /* nothing */ #endif extern __declspec(dllexport) str qserv_angSep(dbl *sep, dbl *ra1, dbl *dec1, dbl *ra2, dbl *dec2); extern __declspec(dllexport) str qserv_ptInSphBox(int *ret, dbl *ra, dbl *dec, dbl *ra_min, dbl *dec_min, dbl *ra_max, dbl *dec_max); extern __declspec(dllexport) str qserv_ptInSphEllipse(int *ret, dbl *ra, dbl *dec, dbl *ra_cen, dbl *dec_cen, dbl *smaa, dbl *smia, dbl *ang); extern __declspec(dllexport) str qserv_ptInSphCircle(int *ret, dbl *ra, dbl *dec, dbl *ra_cen, dbl *dec_cen, dbl *radius); extern __declspec(dllexport) str qserv_ptInSphPoly(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci); extern __declspec(dllexport) str LSSTxmatch(bit *res, lng *l, lng *r, int *delta); extern __declspec(dllexport) str LSSTxmatchselect(bat *res, bat *bid, bat *sid, lng *r, int *depth, bit *anti); extern __declspec(dllexport) str LSSTxmatchjoin(bat *l, bat *r, bat *lid, bat *rid, int *delta, bat *sl, bat *sr, bit *nil_matches, lng *estimate); /* The remainder of the code base is taken over from MySQLSpatialUdf.c */ static double const QSERV_DEG_PER_RAD = 180.0 / M_PI; static double const QSERV_RAD_PER_DEG = M_PI / 180.0; static double const QSERV_ARCSEC_PER_DEG = 3600.0; /* -- Angular separation -------- */ /** Returns D^2/4, where D is the euclidian distance between the two input points on the unit sphere. */ static double _qserv_dist(double ra1, double dec1, double ra2, double dec2) { double x, y, z, dist; x = sin((ra1 - ra2) * QSERV_RAD_PER_DEG * 0.5); x *= x; y = sin((dec1 - dec2) * QSERV_RAD_PER_DEG * 0.5); y *= y; z = cos((dec1 + dec2) * QSERV_RAD_PER_DEG * 0.5); z *= z; dist = x * (z - y) + y; return dist < 0.0 ? 0.0 : (dist > 1.0 ? 1.0 : dist); } static double _qserv_angSep(double ra1, double dec1, double ra2, double dec2) { double dist; dist = _qserv_dist(ra1, dec1, ra2, dec2); return 2.0 * QSERV_DEG_PER_RAD * asin(sqrt(dist)); } /** Returns the angular separation in degrees between two spherical * coordinate pairs (ra1, dec1) and (ra2, dec2). * * Consumes 4 arguments ra1, dec1, ra2 and dec2 all of type REAL: * @li ra1: right ascension of the first position (deg) * @li dec1: declination of the first position (deg) * @li ra2: right ascension of the second position (deg) * @li dec2: declination of the second position (deg) * * Also: * @li If any parameter is NULL, NULL is returned. * @li If dec1 or dec2 lies outside of [-90, 90], this is an error * and NULL is returned. */ str qserv_angSep(dbl *sep, dbl *ra1, dbl *dec1, dbl *ra2, dbl *dec2) { /* If any input is null, the result is null. */ if ( is_dbl_nil(*ra1) || is_dbl_nil(*dec1) || is_dbl_nil(*ra2) || is_dbl_nil(*dec2)){ *sep = dbl_nil; return MAL_SUCCEED; } /* Check that dec lies in range. */ if (*dec1 < -90.0 || *dec1 > 90.0 || *dec2 < -90.0 || *dec2 > 90.0) throw(MAL,"lsst.qserv_angSep", "Illegal angulars"); *sep = _qserv_angSep(*ra1, *dec1, *ra2, *dec2); return MAL_SUCCEED; } /* -- Point in spherical box test -------- */ /** Range reduces the given angle to lie in the range [0.0, 360.0). */ static double _qserv_reduceRa(double theta) { if (theta < 0.0 || theta >= 360.0) { theta = fmod(theta, 360.0); if (theta < 0.0) { theta += 360.0; } } return theta; } /** Returns 1 if the given spherical longitude/latitude box contains * the given position, and 0 otherwise. * * Consumes 6 arguments ra, dec, ra_min, dec_min, ra_max and dec_max, in * that order, all of type REAL and in units of degrees. (ra, dec) is the * position to test - the remaining parameters specify the spherical box. * * Note that: * @li If any parameter is NULL, the return value is 0. * @li If dec, dec_min or dec_max lies outside of [-90, 90], * this is an error and NULL is returned. * @li If dec_min is greater than dec_max, the spherical box is empty * and 0 is returned. * @li If both ra_min and ra_max lie in the range [0, 360], then ra_max * can be less than ra_min. For example, a box with ra_min = 350 * and ra_max = 10 includes points with right ascensions in the ranges * [350, 360) and [0, 10]. * @li If either ra_min or ra_max lies outside of [0, 360], then ra_min * must be = ra_max (otherwise, NULL is returned), though the values * can be arbitrary. If the two are separated by 360 degrees or more, * then the box spans [0, 360). Otherwise, both values are range reduced. * For example, a spherical box with ra_min = 350 and ra_max = 370 * includes points with right ascensions in the rnages [350, 360) and * [0, 10]. */ str qserv_ptInSphBox(int *ret, dbl *ra, dbl *dec, dbl *ra_min, dbl *dec_min, dbl *ra_max, dbl *dec_max) { dbl lra, lra_min, lra_max; if (is_dbl_nil(*ra) || is_dbl_nil(*dec) || is_dbl_nil(*ra_min) || is_dbl_nil(*dec_min) || is_dbl_nil(*ra_max) || is_dbl_nil(*dec_max)){ *ret = int_nil; return MAL_SUCCEED; } /* Check arguments. */ if (*dec < -90.0 || *dec_min < -90.0 || *dec_max < -90.0 || *dec > 90.0 || *dec_min > 90.0 || *dec_max > 90.0) { *ret = int_nil; return MAL_SUCCEED; } if ( *ra_max < *ra_min && ( *ra_max < 0.0 || *ra_min > 360.0)) { *ret = int_nil; return MAL_SUCCEED; } if ( *dec_min > *dec_max || *dec < *dec_min || *dec > *dec_max) { *ret = 0; return MAL_SUCCEED; } /* Range-reduce longitude angles */ lra = _qserv_reduceRa(*ra); if ( *ra_max - *ra_min >= 360.0) { lra_min = 0.0; lra_max = 360.0; } else { lra_min = _qserv_reduceRa(*ra_min); lra_max = _qserv_reduceRa(*ra_max); } if (lra_min <= lra_max) *ret = lra >= lra_min && lra <= lra_max; else *ret = lra >= lra_min || lra <= lra_max; return MAL_SUCCEED; } /* -- Point in spherical circle test -------- */ /** Returns 1 if the given circle on the unit sphere contains * the specified position and 0 otherwise. * * Consumes 5 arguments, all of type REAL: * @li ra: right ascension of position to test (deg) * @li dec: declination of position to test (deg) * @li ra_cen: right ascension of circle center (deg) * @li dec_cen: declination of circle center (deg) * @li radius: radius (opening angle) of circle (deg) * * Note that: * @li If any parameter is NULL, the return value is 0. * @li If dec or dec_cen lies outside of [-90, 90], * this is an error and NULL is returned. * @li If radius is negative or greater than 180, this is * an error and NULL is returned. */ str qserv_ptInSphCircle(int *ret, dbl *ra, dbl *dec, dbl *ra_cen, dbl *dec_cen, dbl *radius) { if (is_dbl_nil(*ra) || is_dbl_nil(*dec) || is_dbl_nil(*ra_cen) || is_dbl_nil(*dec_cen) || is_dbl_nil(*radius) ){ *ret = int_nil; return MAL_SUCCEED; } /* Check arguments */ if ( *dec < -90.0 || *dec > 90.0 || *dec_cen < -90.0 || *dec_cen > 90.0) { *ret = int_nil; return MAL_SUCCEED; } if (*radius < 0.0 || *radius > 180.0) { *ret = int_nil; return MAL_SUCCEED; } /* Fail-fast if declination delta exceeds the radius. */ if ( fabs(*dec - *dec_cen) > *radius) { *ret = 0; return MAL_SUCCEED; } *ret = _qserv_angSep(*ra, *dec, *ra_cen, *dec_cen) <= *radius; return MAL_SUCCEED; } /* -- Point in spherical ellipse test -------- */ typedef struct { double sinRa; /** sine of ellipse center longitude angle */ double cosRa; /** cosine of ellipse center longitude angle */ double sinDec; /** sine of ellipse center latitude angle */ double cosDec; /** cosine of ellipse center latitude angle */ double sinAng; /** sine of ellipse position angle */ double cosAng; /** cosine of ellipse position angle */ double invMinor2; /** 1/(m*m); m = semi-minor axis length (rad) */ double invMajor2; /** 1/(M*M); M = semi-major axis length (rad) */ int valid; } _qserv_sphEllipse_t; /** Returns 1 if the given ellipse on the unit sphere contains * the specified position and 0 otherwise. * * Consumes 7 arguments, all of type REAL: * @li ra: right ascension of position to test (deg) * @li dec: declination of position to test (deg) * @li ra_cen: right ascension of ellipse center (deg) * @li dec_cen: declination of ellipse center (deg) * @li smaa: semi-major axis length (arcsec) * @li smia: semi-minor axis length (arcsec) * @li ang: ellipse position angle (deg) * * Note that: * @li If any parameter is NULL, the return value is 0. * @li If dec or dec_cen lies outside of [-90, 90], * this is an error and NULL is returned. * @li If smia is negative or greater than smaa, this is * an error and NULL is returned. * @li If smaa is greater than 36000 arcsec (10 deg), this * is an error and NULL is returned. */ str qserv_ptInSphEllipse(int *ret, dbl *ra, dbl *dec, dbl *ra_cen, dbl *dec_cen, dbl *smaa, dbl *smia, dbl *ang) { double m, M, lra, ldec, x, y, z, w, xne, yne; double raCen, decCen, angle; if (is_dbl_nil(*ra) || is_dbl_nil(*dec) || is_dbl_nil(*ra_cen) || is_dbl_nil(*dec_cen) || is_dbl_nil(*smaa) || is_dbl_nil(*smia) || is_dbl_nil(*ang) ){ *ret = int_nil; return MAL_SUCCEED; } /* Check arguments */ if (*dec < -90.0 || *dec > 90.0 || *dec_cen < -90.0 || *dec_cen > 90.0) { *ret = int_nil; return MAL_SUCCEED; } /* Semi-minor axis length m and semi-major axis length M must satisfy 0 <= m <= M <= 10 deg */ m = *smia; M = *smaa; if (m < 0.0 || m > M || M > 10.0 * QSERV_ARCSEC_PER_DEG) { *ret = int_nil; return MAL_SUCCEED; } raCen = *ra_cen * QSERV_RAD_PER_DEG; angle = *ang * QSERV_RAD_PER_DEG; decCen = *dec_cen *QSERV_RAD_PER_DEG; m = m * QSERV_RAD_PER_DEG / QSERV_ARCSEC_PER_DEG; M = M * QSERV_RAD_PER_DEG / QSERV_ARCSEC_PER_DEG; /* Transform input position from spherical coordinates to a unit cartesian vector. */ lra = *ra * QSERV_RAD_PER_DEG; ldec= *dec * QSERV_RAD_PER_DEG; x = cos(lra); y = sin(lra); z = sin(ldec); w = cos(ldec); x *= w; y *= w; /* get coords of input point in (N,E) basis at ellipse center */ xne = cos(decCen) * z - sin(decCen) * (sin(raCen) * y + cos(raCen) * x); yne = cos(raCen) * y - sin(raCen) * x; /* rotate by negated position angle */ x = sin(raCen) * yne + cos(angle) * xne; y = cos(raCen) * yne - sin(angle) * xne; /* perform standard 2D axis-aligned point-in-ellipse test */ *ret = (x * x * (1.0/ (m * m)) + y * y * (1.0 / ( M * M)) <= 1.0); return MAL_SUCCEED; } /* -- Point in spherical convex polygon test -------- */ typedef struct { double *edges; int nedges; } _qserv_sphPoly_t; static void _qserv_computeEdges( double *edges, double *verts, int nv) { double x, y, z, w, xp, yp, zp, xl, yl, zl; int i; /* Transform last vertex to a unit 3 vector */ #ifdef QSERV_HAVE_SINCOS sincos(verts[nv*2 - 2], &yl, &xl); sincos(verts[nv*2 - 1], &zl, &w); #else xl = cos(verts[nv*2 - 2]); yl = sin(verts[nv*2 - 2]); zl = sin(verts[nv*2 - 1]); w = cos(verts[nv*2 - 1]); #endif xl *= w; yl *= w; xp = xl; yp = yl; zp = zl; for (i = 0; i < nv - 1; ++i) { /* Transform current vertex to a unit 3 vector */ #ifdef QSERV_HAVE_SINCOS sincos(verts[i*2], &y, &x); sincos(verts[i*2 + 1], &z, &w); #else x = cos(verts[i*2]); y = sin(verts[i*2]); z = sin(verts[i*2 + 1]); w = cos(verts[i*2 + 1]); #endif x *= w; y *= w; /* Edge plane equation is the cross product of the 2 vertices */ edges[i*3] = yp * z - zp * y; edges[i*3 + 1] = zp * x - xp * z; edges[i*3 + 2] = xp * y - yp * x; xp = x; yp = y; zp = z; } /* Compute last edge plane equation */ edges[i*3] = yp * zl - zp * yl; edges[i*3 + 1] = zp * xl - xp * zl; edges[i*3 + 2] = xp * yl - yp * xl; } /** Returns 1 if the given spherical convex polygon contains * the specified position and 0 otherwise. * * Consumes 3 arguments ra, dec and poly. The ra and dec parameters * must be convertible to a REAL, and poly must be a STRING. * * @li ra: right ascension of position to test (deg) * @li dec: declination of position to test (deg) * @li poly: polygon specification * * Note that: * @li If any input parameter is NULL, 0 is returned. * @li If dec is outside of [-90,90], this is an error and NULL is returned. * @li If the polygon spec is invalid or cannot be parsed (e.g. because the * the server has run out of memory), this is an error and the return * value is NULL. * * A polygon specification consists of a space separated list of vertex * coordinate pairs: "ra_0 dec_0 ra_1 dec_1 .... ra_n dec_n". There must * be at least 3 coordinate pairs and declinations must lie in the range * [-90,90]. Also, if the following conditions are not satisfied, then the * return value of the function is undefined: * * @li vertices are hemispherical * @li vertices form a convex polygon when connected with great circle edges * @li vertices lie in counter-clockwise order when viewed from a position * @li outside the unit sphere and inside the half-space containing them. */ str qserv_ptInSphPoly(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { int *ret = getArgReference_int(stk,pci,0); dbl ra = *getArgReference_dbl(stk,pci,1); dbl dec = *getArgReference_dbl(stk,pci,2); double x, y, z, w; dbl *edges, *nv; int i, nedges= (pci->argc-3)/2; (void) mb; (void) cntxt; /* If any input is null, the result is 0. */ for (i = 1; i <pci->argc; ++i) { if ( is_dbl_nil(*getArgReference_dbl(stk,pci,i))){ *ret = int_nil; return MAL_SUCCEED; } } /* Check that dec is in range */ if (dec < -90.0 || dec > 90.0) { *ret = int_nil; return MAL_SUCCEED; } /* Have at least 3 coordinate pairs */ if ( nedges < 3 ) throw(MAL,"lsst.ptInSPhPoly","Not enough edges"); ra *= QSERV_RAD_PER_DEG; dec *= QSERV_RAD_PER_DEG; /* Parse polygon spec if it isn't constant */ edges = (dbl*) GDKmalloc( pci->argc -3 * sizeof(dbl)); if ( edges == NULL) throw(MAL,"lsst.ptInSPhPoly", SQLSTATE(HY001) MAL_MALLOC_FAIL); nv = (dbl*) GDKmalloc( pci->argc -3 * sizeof(dbl)); if ( nv == NULL){ GDKfree(edges); throw(MAL,"lsst.ptInSPhPoly", SQLSTATE(HY001) MAL_MALLOC_FAIL); } for (i = 3; i <pci->argc; ++i) nv[i-3] = *getArgReference_dbl(stk,pci,i); _qserv_computeEdges(edges,nv, nedges); GDKfree(nv); /* Transform input position from spherical coordinates to a unit cartesian vector. */ x = cos(ra); y = sin(ra); z = sin(dec); w = cos(dec); x *= w; y *= w; *ret = 1; for (i = 0; i < nedges; ++i) { /* compute dot product of edge plane normal and input position */ double dp = x * edges[i*3 + 0] + y * edges[i*3 + 1] + z * edges[i*3 + 2]; if (dp < 0.0) { *ret = 0; break; } } GDKfree(edges); return MAL_SUCCEED; } /* * the remainder is an example of hooking up a fast cross match operation * using two HtmID columns bounded by the htm delta distance. * The delta indicates the number of triangulat divisions should be ignored. * For delta =0 the pairs match when their HtmID is equal * for delta =1 the pairs match if their HtmID shifted 2 bits match and so on. * The two columns should be sorted upfront. */ static str LSSTxmatch_intern(bat *lres, bat *rres, bat *lid, bat *rid, int *delta) { BAT *xl, *xr, *bl, *br; lng *l, *r; lng lhtm, rhtm; lng *lend, *rend; int shift; oid lo, ro; if( *delta < 0 || *delta >31) throw(MAL, "algebra.xmatch", "delta not in 0--31"); shift = 2 * *delta; if( (bl= BATdescriptor(*lid)) == NULL ) throw(MAL, "algebra.xmatch", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); if( !bl->tsorted){ BBPunfix(*lid); throw(MAL, "algebra.xmatch", "sorted input required"); } if( (br= BATdescriptor(*rid)) == NULL ){ BBPunfix(*lid); throw(MAL, "algebra.xmatch", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); } if( !br->tsorted){ BBPunfix(*lid); BBPunfix(*rid); throw(MAL, "algebra.xmatch", "sorted input required"); } l= (lng*) Tloc(bl, 0); lend= (lng*) Tloc(bl, BUNlast(bl)); rend= (lng*) Tloc(br, BUNlast(br)); xl = COLnew(0, TYPE_oid, MIN(BATcount(bl), BATcount(br)), TRANSIENT); if ( xl == NULL){ BBPunfix(*lid); BBPunfix(*rid); throw(MAL, "algebra.xmatch", SQLSTATE(HY001) MAL_MALLOC_FAIL); } xr = COLnew(0, TYPE_oid, MIN(BATcount(bl), BATcount(br)), TRANSIENT); if ( xr == NULL){ BBPunfix(*lid); BBPunfix(*rid); BBPunfix(xl->batCacheid); throw(MAL, "algebra.xmatch", SQLSTATE(HY001) MAL_MALLOC_FAIL); } for (lo = bl->hseqbase; l < lend; lo++, l++) { if (!is_lng_nil(*l)) { lhtm = *l >> shift; r= (lng*) Tloc(br, 0); ro = br->hseqbase; for(; r < rend; ro++, r++) { if (!is_lng_nil(*r)) { rhtm = *r >> shift; if (lhtm == rhtm){ /* match */ if (BUNappend(xl, &lo, FALSE) != GDK_SUCCEED || BUNappend(xr, &ro, FALSE) != GDK_SUCCEED) { BBPunfix(*lid); BBPunfix(*rid); BBPunfix(xl->batCacheid); BBPunfix(xr->batCacheid); throw(MAL, "algebra.xmatch", SQLSTATE(HY001) MAL_MALLOC_FAIL); } } else if (lhtm < rhtm) { lhtm = lhtm << shift; while (*l < lhtm && l < lend) { lo++; l++; } lhtm = lhtm >> shift; } else { rhtm = rhtm << shift; while (*r < rhtm && r < rend) { ro++; r++; } rhtm = rhtm >> shift; } } } } } BBPunfix(*lid); BBPunfix(*rid); BBPkeepref(*lres = xl->batCacheid); BBPkeepref(*rres = xr->batCacheid); return MAL_SUCCEED; } str LSSTxmatchjoin(bat *lres, bat *rres, bat *lid, bat *rid, int *delta, bat *sl, bat *sr, bit *nil_matches, lng *estimate) { (void)sl; (void)sr; (void)nil_matches; (void)estimate; return LSSTxmatch_intern(lres, rres, lid, rid, delta); } str LSSTxmatch(bit *res, lng *l, lng *r, int *delta) { int shift; if (*delta < 0 || *delta > 31) throw(MAL, "lsst.xmatch", "delta not in 0--31"); shift = 2 * *delta; *res = !is_lng_nil(*l) && !is_lng_nil(*r) && (*l >> shift) == (*r >> shift); return MAL_SUCCEED; } str LSSTxmatchselect(bat *res, bat *bid, bat *sid, lng *r, int *delta, bit *anti) { int shift; BAT *b, *s = NULL, *bn; const lng *l; lng lhtm, rhtm; if (*delta < 0 || *delta > 31) throw(MAL, "lsst.xmatch", "delta not in 0--31"); shift = 2 * *delta; if ((b = BATdescriptor(*bid)) == NULL) throw(MAL, "algebra.xmatch", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); assert(b->ttype == TYPE_lng); if (sid && *sid && (s = BATdescriptor(*sid)) == NULL) { BBPunfix(b->batCacheid); throw(MAL, "algebra.xmatch", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); } if ((bn = COLnew(0, TYPE_oid, 0, TRANSIENT)) == NULL) { BBPunfix(b->batCacheid); if (s) BBPunfix(s->batCacheid); throw(MAL, "algebra.xmatch", SQLSTATE(HY001) MAL_MALLOC_FAIL); } if (is_lng_nil(*r)) { BBPunfix(b->batCacheid); if (s) BBPunfix(s->batCacheid); BBPkeepref(*res = bn->batCacheid); return MAL_SUCCEED; } rhtm = *r >> shift; l = (const lng *) Tloc(b, 0); if (s && !BATtdense(s)) { const oid *sval = (const oid *) Tloc(s, 0); oid o; BUN i, scnt = BATcount(s); for (i = 0; i < scnt && sval[i] < b->hseqbase; i++) ; for (; i < scnt; i++) { o = sval[i]; if (o >= b->hseqbase + BATcount(b)) break; lhtm = l[o - b->hseqbase]; if (!is_lng_nil(lhtm) && ((lhtm >> shift) == rhtm) != *anti && BUNappend(bn, &o, FALSE) != GDK_SUCCEED) { BBPunfix(b->batCacheid); if (s) BBPunfix(s->batCacheid); BBPunfix(bn->batCacheid); throw(MAL, "algebra.xmatch", SQLSTATE(HY001) MAL_MALLOC_FAIL); } } } else { oid o = b->hseqbase; oid e = o + BATcount(b); if (s) { if (s->tseqbase > o) o = s->tseqbase; if (s->tseqbase + BATcount(s) < e) e = s->tseqbase + BATcount(s); } while (o < e) { lhtm = l[o - b->hseqbase]; if (!is_lng_nil(lhtm) && ((lhtm >> shift) == rhtm) != *anti && BUNappend(bn, &o, FALSE) != GDK_SUCCEED) { BBPunfix(b->batCacheid); if (s) BBPunfix(s->batCacheid); BBPunfix(bn->batCacheid); throw(MAL, "algebra.xmatch", SQLSTATE(HY001) MAL_MALLOC_FAIL); } o++; } } BBPunfix(b->batCacheid); if (s) BBPunfix(s->batCacheid); BBPkeepref(*res = bn->batCacheid); return MAL_SUCCEED; }