star-schiff

Library for estimating radiative properties
git clone git://git.meso-star.com/star-schiff.git
Log | Files | Refs | README | LICENSE

commit a57cf548c99f5ea0af24d4df231b45f3d14d2df9
parent 1a1eb57276d2d57455b34cde392dc6013935f95a
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Tue, 13 Oct 2015 09:39:26 +0200

Test the Schiff estimator

Test the estimation of radiative properties on spheres whose the radius is
distributed with respect to a lognormal distribution.

Diffstat:
Msrc/sschiff.h | 13+++++--------
Msrc/sschiff_estimator.c | 100+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/test_sschiff_estimator.c | 312+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
3 files changed, 316 insertions(+), 109 deletions(-)

diff --git a/src/sschiff.h b/src/sschiff.h @@ -56,12 +56,10 @@ struct s3d_device; struct s3d_shape; struct ssp_rng; -struct sschiff_refractive_index { double real, imag; }; - -enum sschiff_material_property { - SSCHIFF_MATERIAL_MEDIUM_REFRACTIVE_INDEX, - SSCHIFF_MATERIAL_RELATIVE_REAL_REFRACTIVE_INDEX, - SSCHIFF_MATERIAL_RELATIVE_IMAGINARY_REFRACTIVE_INDEX +struct sschiff_material_properties { + double medium_refractive_index; + double relative_real_refractive_index; + double relative_imaginary_refractive_index; }; struct sschiff_material { @@ -69,8 +67,7 @@ struct sschiff_material { void (*get_property) (void* material, const double wavelength, - const enum sschiff_material_property property, - double* value); + struct sschiff_material_properties* properties); void* material; /* Opaque user defined representation of the material */ }; diff --git a/src/sschiff_estimator.c b/src/sschiff_estimator.c @@ -46,20 +46,21 @@ #define NDIRS 1 /* # Sampled directions */ +struct accum { + double weight; + double sqr_weight; + size_t naccums; +}; + struct sschiff_estimator { size_t nsteps; double wavelength; + struct accum extinction_cross_section; struct sschiff_device* dev; ref_T ref; }; -struct accum { - double weight; - double sqr_weight; - size_t naccums; -}; - /******************************************************************************* * Helper functions ******************************************************************************/ @@ -160,7 +161,7 @@ compute_path_length } while(!S3D_HIT_NONE(&hit)); - *length = len * 1.e-6/* Micron to meter */; + *length = (len - first_hit->distance) * 1.e-6/* Micron to meter */; return RES_OK; } @@ -190,7 +191,7 @@ compute_monte_carlo_weight ASSERT(scn && rng && mtl && basis && pos && lower && upper && accum); f2_sub(plane_size, upper, lower); - rcp_pdf = plane_size[0] * plane_size[1]; + rcp_pdf = plane_size[0] * 1.e-6f * plane_size[1] * 1.e-6f; /* Define the projection axis */ f3_mulf(axis_x, basis + 0, plane_size[0] * 0.5f); @@ -212,6 +213,7 @@ compute_monte_carlo_weight if(S3D_HIT_NONE(&hit)) { weight = 0.0; } else { + struct sschiff_material_properties props; double n_r, k_r; double n_e, k_e, lambda_e; double path_length; @@ -219,13 +221,11 @@ compute_monte_carlo_weight res = compute_path_length(scn, &hit, ray_org, axis_z, &path_length); if(res != RES_OK) goto error; - /* Retrieve material properties */ - mtl->get_property(mtl->material, wavelength, - SSCHIFF_MATERIAL_MEDIUM_REFRACTIVE_INDEX, &n_e); - mtl->get_property(mtl->material, wavelength, - SSCHIFF_MATERIAL_RELATIVE_REAL_REFRACTIVE_INDEX, &n_r); - mtl->get_property(mtl->material, wavelength, - SSCHIFF_MATERIAL_RELATIVE_IMAGINARY_REFRACTIVE_INDEX, &k_r); + mtl->get_property(mtl->material, wavelength, &props); + + n_e = props.medium_refractive_index; + n_r = props.relative_real_refractive_index; + k_r = props.relative_imaginary_refractive_index; lambda_e = wavelength / n_e; k_e = 2.0 * PI / lambda_e; @@ -325,10 +325,10 @@ radiative_properties struct sschiff_integrator_desc* desc, const double wavelength, const int istep, + struct s3d_scene* scene, + struct s3d_shape* shape, struct accum* accum) { - struct s3d_shape* shape = NULL; - struct s3d_scene* scene = NULL; struct sschiff_material material = SSCHIFF_NULL_MATERIAL; float lower[3], upper[3]; float aabb_pt[8][3]; @@ -337,23 +337,6 @@ radiative_properties res_T res = RES_OK; ASSERT(dev && rng && desc && accum); - res = s3d_shape_create_mesh(dev->s3d, &shape); - if(res != RES_OK) { - log_error(dev, "Couldn't create the Star-3D Schiff shape.\n"); - goto error; - } - res = s3d_scene_create(dev->s3d, &scene); - if(res != RES_OK) { - log_error(dev, "Couldn't create the Star-3D Schiff scene.\n"); - goto error; - } - res = s3d_scene_attach_shape(scene, shape); - if(res != RES_OK) { - log_error(dev, - "Couldn't attach the Star-3D Schiff shape to the Star-3D Schiff scene.\n"); - goto error; - } - res = desc->sample_geometry(rng, &material, shape, desc->sampler_context); if(res != RES_OK) { log_error(dev, "Error in sampling a Schiff geometry.\n"); @@ -407,7 +390,7 @@ radiative_properties f3_max(upper, upper, pt[i]); } -#if 0 +#if 1 (void)istep; res = compute_monte_carlo_weight (scene, rng, &material, wavelength, basis, centroid, lower, upper, accum); @@ -425,8 +408,6 @@ radiative_properties S3D(scene_end_session(scene)); exit: - if(scene) S3D(scene_ref_put(scene)); - if(shape) S3D(shape_ref_put(shape)); return res; error: goto exit; @@ -495,7 +476,8 @@ sschiff_integrate struct sschiff_estimator** out_estimator) { struct sschiff_estimator* estimator = NULL; - struct accum accum; + struct s3d_shape* shape = NULL; + struct s3d_scene* scene = NULL; int i; res_T res = RES_OK; @@ -510,14 +492,34 @@ sschiff_integrate estimator->nsteps = steps_count; estimator->wavelength = wavelength; - memset(&accum, 0, sizeof(struct accum)); + res = s3d_shape_create_mesh(dev->s3d, &shape); + if(res != RES_OK) { + log_error(dev, "Couldn't create the Star-3D Schiff shape.\n"); + goto error; + } + res = s3d_scene_create(dev->s3d, &scene); + if(res != RES_OK) { + log_error(dev, "Couldn't create the Star-3D Schiff scene.\n"); + goto error; + } + res = s3d_scene_attach_shape(scene, shape); + if(res != RES_OK) { + log_error(dev, + "Couldn't attach the Star-3D Schiff shape to the Star-3D Schiff scene.\n"); + goto error; + } + + memset(&estimator->extinction_cross_section, 0, sizeof(struct accum)); FOR_EACH(i, 0, (int)steps_count) { - res = radiative_properties(dev, rng, desc, wavelength, i, &accum); + res = radiative_properties(dev, rng, desc, wavelength, i, scene, shape, + &estimator->extinction_cross_section); if(res != RES_OK) goto error; } exit: if(out_estimator) *out_estimator = estimator; + if(shape) S3D(shape_ref_put(shape)); + if(scene) S3D(scene_ref_put(scene)); return res; error: if(estimator) { @@ -543,3 +545,21 @@ sschiff_estimator_ref_put(struct sschiff_estimator* estimator) return RES_OK; } +res_T +sschiff_estimator_get_extinction_cross_section + (struct sschiff_estimator* estimator, + struct sschiff_estimator_status* status) +{ + const struct accum* acc; + if(!estimator || !status) return RES_BAD_ARG; + + acc = &estimator->extinction_cross_section; + + status->E = acc->weight / (double)acc->naccums; + status->V = acc->sqr_weight / (double)acc->naccums + - (acc->weight * acc->weight) / (double)(acc->naccums * acc->naccums); + status->SE = sqrt(status->V / (double)acc->naccums); + status->nsteps = acc->naccums; + return RES_OK; +} + diff --git a/src/test_sschiff_estimator.c b/src/test_sschiff_estimator.c @@ -29,32 +29,65 @@ #include "sschiff.h" #include "test_sschiff_utils.h" +#include <rsys/clock_time.h> #include <rsys/float3.h> +#include <rsys/stretchy_array.h> #include <star/s3d.h> #include <star/ssp.h> -#define STEP 0.05f +#define STEP 0.1f +#define NREALISATIONS 1000 -enum { A, B, M, N0, N1, N2 }; +struct optical_properties { double m, n_r, kappa_r, n_medium; }; -struct super_shape { - float super_formulas[2][6]; +struct sphere_context { + struct optical_properties props; + double mean_radius; +}; + +struct sphere { + double radius; size_t ntheta, nphi; }; +struct cylinder { + float* vertices; + unsigned* indices; +}; + static void -super_shape_get_indices(const unsigned itri, unsigned ids[3], void* ctx) +get_material_property + (void* mtl, + const double wavelength, + struct sschiff_material_properties* properties) { - struct super_shape* sshape = ctx; + struct optical_properties* props = mtl; + + NCHECK(mtl, NULL); + NCHECK(properties, NULL); + CHECK(eq_eps(props->m, wavelength, 1.e-8), 1); + + properties->medium_refractive_index = props->n_medium; + properties->relative_real_refractive_index = props->n_r; + properties->relative_imaginary_refractive_index = props->kappa_r; +} + +/******************************************************************************* + * Sphere geometry + ******************************************************************************/ +static void +sphere_get_indices(const unsigned itri, unsigned ids[3], void* ctx) +{ + struct sphere* sphere = ctx; const size_t iquad = itri / 2; const size_t iquad_tri = itri % 2; - const size_t i = iquad / (sshape->nphi - 1); - const size_t j = iquad % (sshape->nphi - 1); + const size_t i = iquad / (sphere->nphi - 1); + const size_t j = iquad % (sphere->nphi - 1); size_t A, B; - B = i * sshape->nphi + j + 1; - A = ((i + 1) % sshape->ntheta) * sshape->nphi + j; + B = i * sphere->nphi + j + 1; + A = ((i + 1) % sphere->ntheta) * sphere->nphi + j; if(iquad_tri == 0) { ids[0] = (unsigned)B - 1; ids[1] = (unsigned)A; @@ -66,20 +99,17 @@ super_shape_get_indices(const unsigned itri, unsigned ids[3], void* ctx) } } -static void -super_shape_get_vertex_position(const unsigned ivert, float vertex[3], void* ctx) +static INLINE void +sphere_get_vertex_position(const unsigned ivert, float vertex[3], void* ctx) { - struct super_shape* sshape = ctx; - const size_t itheta = ivert / sshape->nphi; - const size_t iphi = ivert % sshape->nphi; + struct sphere* sphere = ctx; + const size_t itheta = ivert / sphere->nphi; + const size_t iphi = ivert % sphere->nphi; double angle[2]; double sin_angle[2]; double cos_angle[2]; - double uv[2]; - int iform; - ASSERT(itheta < sshape->ntheta); - if(iphi == sshape->nphi - 1) { /* Polar vertices: Force polar coordinates */ + if(iphi == sphere->nphi - 1) { /* Polar vertices: Force polar coordinates */ angle[0] = PI; angle[1] = PI/2; } else { @@ -87,40 +117,35 @@ super_shape_get_vertex_position(const unsigned ivert, float vertex[3], void* ctx angle[1] = -PI/2 + (double)iphi * STEP; } - FOR_EACH(iform, 0, 2) { - double m, k, g; - float* form = sshape->super_formulas[iform]; - m = cos(form[M] * angle[iform] / 4.0); - m = fabs(m) / form[A]; - k = sin(form[M] * angle[iform] / 4.0); - k = fabs(k) / form[B]; - g = pow(m, form[N1]) + pow(k, form[N2]); - uv[iform] = pow(g, (-1.0/form[N0])); - - cos_angle[iform] = cos(angle[iform]); - sin_angle[iform] = sin(angle[iform]); - } + cos_angle[0] = cos(angle[0]); + cos_angle[1] = cos(angle[1]); + sin_angle[0] = sin(angle[0]); + sin_angle[1] = sin(angle[1]); - vertex[0] = (float)(uv[0] * cos_angle[0] * uv[1] * cos_angle[1]); - vertex[1] = (float)(uv[0] * sin_angle[0] * uv[1] * cos_angle[1]); - vertex[2] = (float)(uv[1] * sin_angle[1]); + vertex[0] = (float)(cos_angle[0] * cos_angle[1] * sphere->radius); + vertex[1] = (float)(sin_angle[0] * cos_angle[1] * sphere->radius); + vertex[2] = (float)(sin_angle[1] * sphere->radius); } static res_T -sample_geometry +sample_sphere (struct ssp_rng* rng, struct sschiff_material* mtl, struct s3d_shape* shape, void* sampler_context) { struct s3d_vertex_data attrib; - struct super_shape sshape; + struct sphere sphere; + struct sphere_context* ctx = sampler_context; + const size_t ntheta = (int)((2*PI)/STEP) + 1; const size_t nphi = (int)((PI)/STEP) + 1; const size_t nverts = ntheta * nphi; const size_t nprims = ntheta * (nphi - 1)/*#quads*/ * 2/*#triangles*/; - (void)sampler_context; + sphere.radius = ssp_ran_lognormal(rng, log(ctx->mean_radius), log(1.18)); + sphere.ntheta = ntheta; + sphere.nphi = nphi; NCHECK(rng, NULL); NCHECK(mtl, NULL); @@ -128,38 +153,158 @@ sample_geometry attrib.usage = S3D_POSITION; attrib.type = S3D_FLOAT3; - attrib.get = super_shape_get_vertex_position; - - sshape.super_formulas[0][A] = 1; - sshape.super_formulas[0][B] = 1; - sshape.super_formulas[0][M] = (float)ssp_rng_uniform_double(rng, 0.1, 6); - sshape.super_formulas[0][N0] = 1; - sshape.super_formulas[0][N1] = 1; - sshape.super_formulas[0][N2] = (float)ssp_rng_uniform_double(rng, 0.1, 6); - - sshape.super_formulas[1][A] = 1; - sshape.super_formulas[1][B] = 1; - sshape.super_formulas[1][M] = (float)ssp_rng_uniform_double(rng, 0.1, 6); - sshape.super_formulas[1][N0] = 1; - sshape.super_formulas[1][N1] = 1; - sshape.super_formulas[1][N2] = (float)ssp_rng_uniform_double(rng, 0.1, 6); - - sshape.ntheta = ntheta; - sshape.nphi = nphi; - - *mtl = SSCHIFF_NULL_MATERIAL; + attrib.get = sphere_get_vertex_position; + + mtl->get_property = get_material_property; + mtl->material = ctx; + return s3d_mesh_setup_indexed_vertices(shape, (unsigned)nprims, - super_shape_get_indices, (unsigned)nverts, &attrib, 1, &sshape); + sphere_get_indices, (unsigned)nverts, &attrib, 1, &sphere); +} + +/******************************************************************************* + * Cylinder geometry + ******************************************************************************/ +static void +cylinder_get_indices(const unsigned itri, unsigned ids[3], void* ctx) +{ + struct cylinder* cylinder = ctx; + const size_t id = itri * 3; + + CHECK(itri < sa_size(cylinder->indices) / 3, 1); + ids[0] = cylinder->indices[id+0]; + ids[1] = cylinder->indices[id+1]; + ids[2] = cylinder->indices[id+2]; +} + +static void +cylinder_get_vertex_position(const unsigned ivert, float vertex[3], void* ctx) +{ + struct cylinder* cylinder = ctx; + const size_t i = ivert * 3; + + CHECK(ivert < sa_size(cylinder->vertices) / 3, 1); + f3_set(vertex, cylinder->vertices + i); +} + +static res_T +sample_cylinder + (struct ssp_rng* rng, + struct sschiff_material* mtl, + struct s3d_shape* shape, + void* sampler_context) +{ + struct s3d_vertex_data attrib; + struct cylinder cylinder = { NULL, NULL }; + const unsigned nsteps = (unsigned)((2*PI)/STEP); + unsigned istep; + double sample; + const float aspect_ratio = 0.263f; + float radius; + float height; + res_T res = RES_OK; + (void)rng, (void)sampler_context; + + sample = ssp_ran_lognormal(rng, log(0.983), log(1.1374)); + radius = (float)(sample * pow(aspect_ratio, 1.0/3.0)); + height = 2 * radius * aspect_ratio; + + /* Generate the vertex coordinates */ + FOR_EACH(istep, 0, nsteps) { + const float theta = (float)istep * STEP; + const float x = (float)(radius * cos(theta)); + const float y = (float)(radius * sin(theta)); + f3(sa_add(cylinder.vertices, 3), x, 0.f, y); + f3(sa_add(cylinder.vertices, 3), x, height, y); + } + /* "Polar" vertices */ + f3_splat(sa_add(cylinder.vertices, 3), 0.f); + f3(sa_add(cylinder.vertices, 3), 0.f, height, 0.f); + + /* Cylinder primitives */ + FOR_EACH(istep, 0, nsteps) { + const unsigned id = istep * 2; + unsigned* iprim; + + iprim = sa_add(cylinder.indices, 3); + iprim[0] = (id + 0); + iprim[1] = (id + 1); + iprim[2] = (id + 2) % (nsteps*2); + + iprim = sa_add(cylinder.indices, 3); + iprim[0] = (id + 2) % (nsteps*2); + iprim[1] = (id + 1); + iprim[2] = (id + 3) % (nsteps*2); + } + /* Cap primitives */ + FOR_EACH(istep, 0, nsteps) { + const unsigned id = istep * 2; + unsigned* iprim; + + iprim = sa_add(cylinder.indices, 3); + iprim[0] = (nsteps * 2); + iprim[1] = (id + 0); + iprim[2] = (id + 2) % (nsteps*2); + + iprim = sa_add(cylinder.indices, 3); + iprim[0] = (nsteps * 2) + 1; + iprim[1] = (id + 3) % (nsteps*2); + iprim[2] = (id + 1); + } + + attrib.usage = S3D_POSITION; + attrib.type = S3D_FLOAT3; + attrib.get = cylinder_get_vertex_position; + + mtl->get_property = get_material_property; + mtl->material = NULL; + + res = s3d_mesh_setup_indexed_vertices(shape, (unsigned)nsteps*4, + cylinder_get_indices, (unsigned)nsteps*2 + 2, &attrib, 1, &cylinder); + + sa_release(cylinder.vertices); + sa_release(cylinder.indices); + + return res; } int main(int argc, char** argv) { + char buf[64]; struct mem_allocator allocator; + struct sphere_context sphere_ctx; struct sschiff_device* dev; struct sschiff_estimator* estimator; struct sschiff_integrator_desc desc = SSCHIFF_NULL_INTEGRATOR_DESC; + struct sschiff_estimator_status status; struct ssp_rng* rng; + struct time t0, t1; + size_t i; + const double sphere_data_1_01[][2] = { /* mean_radius, result */ + { 1.00, 6.43e-13 }, + { 3.22, 2.66e-11 }, + { 5.44, 1.34e-10 }, + { 7.67, 3.56e-10 }, + { 9.89, 6.83e-10 }, + { 12.1, 1.08e-9 }, + { 14.3, 1.53e-9 }, + { 16.6, 2.00e-9 }, + { 18.8, 2.51e-9 }, + { 21.0, 3.07e-9 } + }; + const double sphere_data_1_1[][2] = { /* mean_radius, result */ + { 1.00, 8.39e-12 }, + { 3.22, 6.93e-11 }, + { 5.44, 1.97e-10 }, + { 7.67, 3.92e-10 }, + { 9.89, 6.51e-10 }, + { 12.1, 9.75e-10 }, + { 14.3, 1.37e-9 }, + { 16.6, 1.82e-9 }, + { 18.8, 2.34e-9 }, + { 21.0, 2.93e-9 } + }; (void)argc, (void)argv; mem_init_proxy_allocator(&allocator, &mem_default_allocator); @@ -167,7 +312,7 @@ main(int argc, char** argv) CHECK(ssp_rng_create(&allocator, &ssp_rng_threefry, &rng), RES_OK); CHECK(sschiff_device_create(NULL, &allocator, 0, NULL, &dev), RES_OK); - desc.sample_geometry = sample_geometry; + desc.sample_geometry = sample_sphere; desc.scattering_angles_count = 1; CHECK(sschiff_integrate(NULL, NULL, NULL, 0, 0, NULL), RES_BAD_ARG); CHECK(sschiff_integrate(dev, NULL, NULL, 0, 0, NULL), RES_BAD_ARG); @@ -184,7 +329,29 @@ main(int argc, char** argv) CHECK(sschiff_integrate(NULL, NULL, &desc, 0, 1, &estimator), RES_BAD_ARG); CHECK(sschiff_integrate(dev, NULL, &desc, 0, 1, &estimator), RES_BAD_ARG); CHECK(sschiff_integrate(NULL, rng, &desc, 0, 1, &estimator), RES_BAD_ARG); - CHECK(sschiff_integrate(dev, rng, &desc, 0, 100, &estimator), RES_OK); + + desc.sampler_context = &sphere_ctx; + + sphere_ctx.props.m = 0.6e-6; + sphere_ctx.props.n_r = 1.1; + sphere_ctx.props.kappa_r = 0.004; + sphere_ctx.props.n_medium = 4.0/3.0; + sphere_ctx.mean_radius = 1.0; + + time_current(&t0); + CHECK(sschiff_integrate(dev, rng, &desc, 6.e-7, NREALISATIONS, &estimator), RES_OK); + time_current(&t1); + + CHECK(sschiff_estimator_get_extinction_cross_section(NULL, NULL), RES_BAD_ARG); + CHECK(sschiff_estimator_get_extinction_cross_section(estimator, NULL), RES_BAD_ARG); + CHECK(sschiff_estimator_get_extinction_cross_section(NULL, &status), RES_BAD_ARG); + CHECK(sschiff_estimator_get_extinction_cross_section(estimator, &status), RES_OK); + + printf("Extinction cross section: %g +/- %g\n", status.E, status.SE); + CHECK(eq_eps(status.E, 8.3e-12, 2*status.SE), 1); + time_sub(&t0, &t1, &t0); + time_dump(&t0, TIME_MIN|TIME_SEC|TIME_MSEC, NULL, buf, sizeof(buf)); + printf("Elapsed time: %s\n", buf); CHECK(sschiff_estimator_ref_get(NULL), RES_BAD_ARG); CHECK(sschiff_estimator_ref_get(estimator), RES_OK); @@ -192,6 +359,29 @@ main(int argc, char** argv) CHECK(sschiff_estimator_ref_put(estimator), RES_OK); CHECK(sschiff_estimator_ref_put(estimator), RES_OK); + sphere_ctx.props.n_r = 1.1; + printf("\nMean radius: 1.1\n"); + FOR_EACH(i, 0, sizeof(sphere_data_1_1)/sizeof(double[2])) { + sphere_ctx.mean_radius = sphere_data_1_1[i][0]; + CHECK(sschiff_integrate(dev, rng, &desc, 6.e-7, NREALISATIONS, &estimator), RES_OK); + CHECK(sschiff_estimator_get_extinction_cross_section(estimator, &status), RES_OK); + printf("%d - Extinction cross section = %g ~ %g +/- %g\n", + (int)i, sphere_data_1_1[i][1], status.E, status.SE); + CHECK(eq_eps(status.E, sphere_data_1_1[i][1], 2*status.SE), 1); + CHECK(sschiff_estimator_ref_put(estimator), RES_OK); + } + sphere_ctx.props.n_r = 1.01; + printf("\nMean radius: 1.01\n"); + FOR_EACH(i, 0, sizeof(sphere_data_1_01)/sizeof(double[2])) { + sphere_ctx.mean_radius = sphere_data_1_01[i][0]; + CHECK(sschiff_integrate(dev, rng, &desc, 6.e-7, NREALISATIONS, &estimator), RES_OK); + CHECK(sschiff_estimator_get_extinction_cross_section(estimator, &status), RES_OK); + printf("%d - Extinction cross section = %g ~ %g +/- %g\n", + (int)i, sphere_data_1_01[i][1], status.E, status.SE); + CHECK(eq_eps(status.E, sphere_data_1_01[i][1], 2*status.SE), 1); + CHECK(sschiff_estimator_ref_put(estimator), RES_OK); + } + CHECK(sschiff_device_ref_put(dev), RES_OK); CHECK(ssp_rng_ref_put(rng), RES_OK);