stardis-solver

Solve coupled heat transfers
git clone git://git.meso-star.fr/stardis-solver.git
Log | Files | Refs | README | LICENSE

test_sdis_solve_probe.c (23624B)


      1 /* Copyright (C) 2016-2025 |Méso|Star> (contact@meso-star.com)
      2  *
      3  * This program is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published by
      5  * the Free Software Foundation, either version 3 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * This program is distributed in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     15 
     16 #include "sdis.h"
     17 #include "test_sdis_utils.h"
     18 
     19 #include <star/ssp.h>
     20 #include <rsys/math.h>
     21 
     22 #include <string.h>
     23 
     24 /*
     25  * The scene is composed of a solid cube with unknown temperature. The
     26  * surrounding fluid has a fixed constant temperature.
     27  *
     28  *             (1,1,1)
     29  *       +-------+
     30  *      /'      /|    _\
     31  *     +-------+ |   / /
     32  *     | +.....|.+   \__/
     33  *     |,      |/
     34  *     +-------+
     35  * (0,0,0)
     36  */
     37 
     38 /*******************************************************************************
     39  * Geometry
     40  ******************************************************************************/
     41 struct context {
     42   const double* positions;
     43   const size_t* indices;
     44   struct sdis_interface* interf;
     45 };
     46 
     47 static void
     48 get_indices(const size_t itri, size_t ids[3], void* context)
     49 {
     50   struct context* ctx = context;
     51   ids[0] = ctx->indices[itri*3+0];
     52   ids[1] = ctx->indices[itri*3+1];
     53   ids[2] = ctx->indices[itri*3+2];
     54 }
     55 
     56 static void
     57 get_position(const size_t ivert, double pos[3], void* context)
     58 {
     59   struct context* ctx = context;
     60   pos[0] = ctx->positions[ivert*3+0];
     61   pos[1] = ctx->positions[ivert*3+1];
     62   pos[2] = ctx->positions[ivert*3+2];
     63 }
     64 
     65 static void
     66 get_interface(const size_t itri, struct sdis_interface** bound, void* context)
     67 {
     68   struct context* ctx = context;
     69   (void)itri;
     70   *bound = ctx->interf;
     71 }
     72 
     73 /*******************************************************************************
     74  * Fluid medium
     75  ******************************************************************************/
     76 struct fluid {
     77   double temperature;
     78 };
     79 
     80 static double
     81 fluid_get_temperature
     82   (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data)
     83 {
     84   CHK(data != NULL && vtx != NULL);
     85   return ((const struct fluid*)sdis_data_cget(data))->temperature;
     86 }
     87 
     88 /*******************************************************************************
     89  * Solid medium
     90  ******************************************************************************/
     91 struct solid {
     92   double cp;
     93   double lambda;
     94   double rho;
     95   double delta;
     96   double temperature;
     97 };
     98 
     99 static double
    100 solid_get_calorific_capacity
    101   (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data)
    102 {
    103   CHK(data != NULL && vtx != NULL);
    104   return ((const struct solid*)sdis_data_cget(data))->cp;
    105 }
    106 
    107 static double
    108 solid_get_thermal_conductivity
    109   (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data)
    110 {
    111   CHK(data != NULL && vtx != NULL);
    112   return ((const struct solid*)sdis_data_cget(data))->lambda;
    113 }
    114 
    115 static double
    116 solid_get_volumic_mass
    117   (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data)
    118 {
    119   CHK(data != NULL && vtx != NULL);
    120   return ((const struct solid*)sdis_data_cget(data))->rho;
    121 }
    122 
    123 static double
    124 solid_get_delta
    125   (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data)
    126 {
    127   CHK(data != NULL && vtx != NULL);
    128   return ((const struct solid*)sdis_data_cget(data))->delta;
    129 }
    130 
    131 static double
    132 solid_get_temperature
    133   (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data)
    134 {
    135   CHK(data != NULL && vtx != NULL);
    136   return ((const struct solid*)sdis_data_cget(data))->temperature;
    137 }
    138 
    139 /*******************************************************************************
    140  * Interface
    141  ******************************************************************************/
    142 struct interf {
    143   double hc;
    144   double epsilon;
    145   double specular_fraction;
    146   double reference_temperature;
    147 };
    148 
    149 static double
    150 interface_get_convection_coef
    151   (const struct sdis_interface_fragment* frag, struct sdis_data* data)
    152 {
    153   CHK(data != NULL && frag != NULL);
    154   return ((const struct interf*)sdis_data_cget(data))->hc;
    155 }
    156 
    157 static double
    158 interface_get_emissivity
    159   (const struct sdis_interface_fragment* frag,
    160    const unsigned source_id,
    161    struct sdis_data* data)
    162 {
    163   (void)source_id;
    164   CHK(data != NULL && frag != NULL);
    165   return ((const struct interf*)sdis_data_cget(data))->epsilon;
    166 }
    167 
    168 static double
    169 interface_get_specular_fraction
    170   (const struct sdis_interface_fragment* frag,
    171    const unsigned source_id,
    172    struct sdis_data* data)
    173 {
    174   (void)source_id;
    175   CHK(data != NULL && frag != NULL);
    176   return ((const struct interf*)sdis_data_cget(data))->specular_fraction;
    177 }
    178 
    179 static double
    180 interface_get_reference_temperature
    181   (const struct sdis_interface_fragment* frag, struct sdis_data* data)
    182 {
    183   CHK(data != NULL && frag != NULL);
    184   return ((const struct interf*)sdis_data_cget(data))->reference_temperature;
    185 }
    186 
    187 /*******************************************************************************
    188  * Radiative environment
    189  ******************************************************************************/
    190 struct radenv {
    191   double temperature; /* [K] */
    192   double reference; /* [K] */
    193 };
    194 
    195 static double
    196 radenv_get_temperature
    197   (const struct sdis_radiative_ray* ray,
    198    struct sdis_data* data)
    199 {
    200   (void)ray;
    201   return ((const struct radenv*)sdis_data_cget(data))->temperature;
    202 }
    203 
    204 static double
    205 radenv_get_reference_temperature
    206   (const struct sdis_radiative_ray* ray,
    207    struct sdis_data* data)
    208 {
    209   (void)ray;
    210   return ((const struct radenv*)sdis_data_cget(data))->reference;
    211 }
    212 
    213 static struct sdis_radiative_env*
    214 create_radenv
    215   (struct sdis_device* dev,
    216    const double temperature, /* [K] */
    217    const double reference) /* [K] */
    218 {
    219   struct sdis_radiative_env_shader shader = SDIS_RADIATIVE_ENV_SHADER_NULL;
    220   struct sdis_radiative_env* radenv = NULL;
    221   struct sdis_data* data = NULL;
    222   struct radenv* radenv_args = NULL;
    223 
    224   OK(sdis_data_create(dev, sizeof(struct radenv), ALIGNOF(radenv), NULL, &data));
    225   radenv_args = sdis_data_get(data);
    226   radenv_args->temperature = temperature;
    227   radenv_args->reference = reference;
    228 
    229   shader.temperature = radenv_get_temperature;
    230   shader.reference_temperature = radenv_get_reference_temperature;
    231   OK(sdis_radiative_env_create(dev, &shader, data, &radenv));
    232   OK(sdis_data_ref_put(data));
    233   return radenv;
    234 }
    235 
    236 /*******************************************************************************
    237  * Helper functions
    238  ******************************************************************************/
    239 struct dump_path_context {
    240   FILE* stream;
    241   size_t offset;
    242   size_t nfailures;
    243   size_t nsuccesses;
    244 };
    245 static const struct dump_path_context DUMP_PATH_CONTEXT_NULL = {NULL, 0, 0, 0};
    246 
    247 static res_T
    248 dump_vertex_pos(const struct sdis_heat_vertex* vert, void* context)
    249 {
    250   struct dump_path_context* ctx = context;
    251   CHK(vert && context);
    252   fprintf(ctx->stream, "v %g %g %g\n", SPLIT3(vert->P));
    253   return RES_OK;
    254 }
    255 
    256 static res_T
    257 process_heat_path(const struct sdis_heat_path* path, void* context)
    258 {
    259   struct dump_path_context* ctx = context;
    260   struct sdis_heat_vertex vert = SDIS_HEAT_VERTEX_NULL;
    261   enum sdis_heat_path_flag status = SDIS_HEAT_PATH_NONE;
    262   size_t i;
    263   size_t n;
    264   (void)context;
    265 
    266   CHK(path && context);
    267 
    268   BA(sdis_heat_path_get_line_strips_count(NULL, &n));
    269   BA(sdis_heat_path_get_line_strips_count(path, NULL));
    270   OK(sdis_heat_path_get_line_strips_count(path, &n));
    271   CHK(n == 1);
    272 
    273   BA(sdis_heat_path_get_status(NULL, &status));
    274   BA(sdis_heat_path_get_status(path, NULL));
    275   OK(sdis_heat_path_get_status(path, &status));
    276   CHK(status == SDIS_HEAT_PATH_SUCCESS || status == SDIS_HEAT_PATH_FAILURE);
    277 
    278   switch(status) {
    279     case SDIS_HEAT_PATH_FAILURE: ++ctx->nfailures; break;
    280     case SDIS_HEAT_PATH_SUCCESS: ++ctx->nsuccesses; break;
    281     default: FATAL("Unreachable code.\n"); break;
    282   }
    283 
    284   BA(sdis_heat_path_line_strip_get_vertices_count(NULL, 0, &n));
    285   BA(sdis_heat_path_line_strip_get_vertices_count(path, 1, &n));
    286   BA(sdis_heat_path_line_strip_get_vertices_count(path, 0, NULL));
    287   OK(sdis_heat_path_line_strip_get_vertices_count(path, 0, &n));
    288   CHK(n != 0);
    289 
    290   BA(sdis_heat_path_line_strip_get_vertex(NULL, 0, 0, &vert));
    291   BA(sdis_heat_path_line_strip_get_vertex(path, 1, 1, &vert));
    292   BA(sdis_heat_path_line_strip_get_vertex(path, 0, n, &vert));
    293   BA(sdis_heat_path_line_strip_get_vertex(path, 0, 0, NULL));
    294 
    295   FOR_EACH(i, 0, n) {
    296     OK(sdis_heat_path_line_strip_get_vertex(path, 0, i, &vert));
    297     CHK(vert.type == SDIS_HEAT_VERTEX_CONVECTION
    298      || vert.type == SDIS_HEAT_VERTEX_CONDUCTION
    299      || vert.type == SDIS_HEAT_VERTEX_RADIATIVE);
    300   }
    301 
    302   BA(sdis_heat_path_line_strip_for_each_vertex(NULL, 0, dump_vertex_pos, context));
    303   BA(sdis_heat_path_line_strip_for_each_vertex(path, 1, dump_vertex_pos, context));
    304   BA(sdis_heat_path_line_strip_for_each_vertex(path, 0, NULL, context));
    305   OK(sdis_heat_path_line_strip_for_each_vertex(path, 0, dump_vertex_pos, context));
    306 
    307   FOR_EACH(i, 0, n-1) {
    308     fprintf(ctx->stream, "l %lu %lu\n",
    309       (unsigned long)(i+1 + ctx->offset),
    310       (unsigned long)(i+2 + ctx->offset));
    311   }
    312 
    313   ctx->offset += n;
    314 
    315   return RES_OK;
    316 }
    317 
    318 /*******************************************************************************
    319  * Test
    320  ******************************************************************************/
    321 int
    322 main(int argc, char** argv)
    323 {
    324   struct mem_allocator allocator;
    325   struct sdis_mc T = SDIS_MC_NULL;
    326   struct sdis_mc F = SDIS_MC_NULL;
    327   struct sdis_mc time = SDIS_MC_NULL;
    328   struct sdis_device* dev = NULL;
    329   struct sdis_medium* solid = NULL;
    330   struct sdis_medium* fluid = NULL;
    331   struct sdis_interface* interf = NULL;
    332   struct sdis_radiative_env* radenv = NULL;
    333   struct sdis_scene* scn = NULL;
    334   struct sdis_data* data = NULL;
    335   struct sdis_estimator* estimator = NULL;
    336   struct sdis_estimator* estimator2 = NULL;
    337   struct sdis_estimator* estimator3 = NULL;
    338   struct sdis_green_function* green = NULL;
    339   const struct sdis_heat_path* path = NULL;
    340   struct sdis_device_create_args dev_args = SDIS_DEVICE_CREATE_ARGS_DEFAULT;
    341   struct sdis_green_function_create_from_stream_args green_args =
    342     SDIS_GREEN_FUNCTION_CREATE_FROM_STREAM_ARGS_DEFAULT;
    343   struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT;
    344   struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER;
    345   struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER;
    346   struct sdis_interface_shader interface_shader = SDIS_INTERFACE_SHADER_NULL;
    347   struct sdis_solve_probe_args solve_args = SDIS_SOLVE_PROBE_ARGS_DEFAULT;
    348   struct dump_path_context dump_ctx = DUMP_PATH_CONTEXT_NULL;
    349   struct context ctx;
    350   struct radenv* radenv_args;
    351   struct fluid* fluid_args;
    352   struct solid* solid_args;
    353   struct interf* interface_args;
    354   struct ssp_rng* rng_state = NULL;
    355   enum sdis_estimator_type type;
    356   FILE* stream = NULL;
    357   double t_range[2];
    358   double ref;
    359   const size_t N = 1000;
    360   const size_t N_dump = 10;
    361   size_t nreals;
    362   size_t nfails;
    363   size_t n;
    364   (void)argc, (void)argv;
    365 
    366   OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator));
    367   dev_args.allocator = &allocator;
    368   OK(sdis_device_create(&dev_args, &dev));
    369 
    370   /* Create the fluid medium */
    371   OK(sdis_data_create
    372     (dev, sizeof(struct fluid), ALIGNOF(struct fluid), NULL, &data));
    373   fluid_args = sdis_data_get(data);
    374   fluid_args->temperature = 300;
    375   fluid_shader.temperature = fluid_get_temperature;
    376   OK(sdis_fluid_create(dev, &fluid_shader, data, &fluid));
    377   OK(sdis_data_ref_put(data));
    378 
    379   /* Create the solid medium */
    380   OK(sdis_data_create
    381     (dev, sizeof(struct solid), ALIGNOF(struct solid), NULL, &data));
    382   solid_args = sdis_data_get(data);
    383   solid_args->cp = 1.0;
    384   solid_args->lambda = 0.1;
    385   solid_args->rho = 1.0;
    386   solid_args->delta = 1.0/20.0;
    387   solid_args->temperature = SDIS_TEMPERATURE_NONE; /* Unknown temperature */
    388   solid_shader.calorific_capacity = solid_get_calorific_capacity;
    389   solid_shader.thermal_conductivity = solid_get_thermal_conductivity;
    390   solid_shader.volumic_mass = solid_get_volumic_mass;
    391   solid_shader.delta = solid_get_delta;
    392   solid_shader.temperature = solid_get_temperature;
    393   OK(sdis_solid_create(dev, &solid_shader, data, &solid));
    394   OK(sdis_data_ref_put(data));
    395 
    396   /* Create the solid/fluid interface */
    397   OK(sdis_data_create(dev, sizeof(struct interf),
    398     ALIGNOF(struct interf), NULL, &data));
    399   interface_args = sdis_data_get(data);
    400   interface_args->hc = 0.5;
    401   interface_args->epsilon = 0;
    402   interface_args->specular_fraction = 0;
    403   interface_shader.convection_coef = interface_get_convection_coef;
    404   interface_shader.front = SDIS_INTERFACE_SIDE_SHADER_NULL;
    405   interface_shader.back.temperature = NULL;
    406   interface_shader.back.emissivity = interface_get_emissivity;
    407   interface_shader.back.specular_fraction = interface_get_specular_fraction;
    408   interface_shader.back.reference_temperature = interface_get_reference_temperature;
    409   OK(sdis_interface_create
    410     (dev, solid, fluid, &interface_shader, data, &interf));
    411   OK(sdis_data_ref_put(data));
    412 
    413   /* Release the media */
    414   OK(sdis_medium_ref_put(solid));
    415   OK(sdis_medium_ref_put(fluid));
    416 
    417   /* Create the radiative environment */
    418   radenv = create_radenv(dev, SDIS_TEMPERATURE_NONE, SDIS_TEMPERATURE_NONE);
    419   radenv_args = sdis_data_get(sdis_radiative_env_get_data(radenv));
    420 
    421   /* Create the scene */
    422   ctx.positions = box_vertices;
    423   ctx.indices = box_indices;
    424   ctx.interf = interf;
    425   scn_args.get_indices = get_indices;
    426   scn_args.get_interface = get_interface;
    427   scn_args.get_position = get_position;
    428   scn_args.nprimitives = box_ntriangles;
    429   scn_args.nvertices = box_nvertices;
    430   scn_args.radenv = radenv;
    431   scn_args.context = &ctx;
    432   OK(sdis_scene_create(dev, &scn_args, &scn));
    433 
    434   OK(sdis_interface_ref_put(interf));
    435 
    436   /* Test the solver */
    437   solve_args.nrealisations = N;
    438   solve_args.position[0] = 0.5;
    439   solve_args.position[1] = 0.5;
    440   solve_args.position[2] = 0.5;
    441   solve_args.time_range[0] = INF;
    442   solve_args.time_range[1] = INF;
    443 
    444   BA(sdis_solve_probe(NULL, &solve_args, &estimator));
    445   BA(sdis_solve_probe(scn, NULL, &estimator));
    446   BA(sdis_solve_probe(scn, &solve_args, NULL));
    447   solve_args.nrealisations = 0;
    448   BA(sdis_solve_probe(scn, &solve_args, &estimator));
    449   solve_args.nrealisations = N;
    450   solve_args.time_range[0] = solve_args.time_range[1] = -1;
    451   BA(sdis_solve_probe(scn, &solve_args, &estimator));
    452   solve_args.time_range[0] = 1;
    453   BA(sdis_solve_probe(scn, &solve_args, &estimator));
    454   solve_args.time_range[1] = 0;
    455   BA(sdis_solve_probe(scn, &solve_args, &estimator));
    456   solve_args.time_range[0] = solve_args.time_range[1] = INF;
    457   solve_args.picard_order = 0;
    458   BA(sdis_solve_probe(scn, &solve_args, &estimator));
    459   solve_args.picard_order = 1;
    460   solve_args.diff_algo = SDIS_DIFFUSION_NONE;
    461   BA(sdis_solve_probe(scn, &solve_args, &estimator));
    462   solve_args.diff_algo = SDIS_DIFFUSION_DELTA_SPHERE;
    463   OK(sdis_solve_probe(scn, &solve_args, &estimator));
    464 
    465   BA(sdis_estimator_get_type(estimator, NULL));
    466   BA(sdis_estimator_get_type(NULL, &type));
    467   OK(sdis_estimator_get_type(estimator, &type));
    468   CHK(type == SDIS_ESTIMATOR_TEMPERATURE);
    469 
    470   /* Fluxes aren't available after sdis_solve_probe */
    471   BA(sdis_estimator_get_convective_flux(estimator, NULL));
    472   BA(sdis_estimator_get_convective_flux(NULL, &F));
    473   BA(sdis_estimator_get_convective_flux(estimator, &F));
    474 
    475   BA(sdis_estimator_get_radiative_flux(estimator, NULL));
    476   BA(sdis_estimator_get_radiative_flux(NULL, &F));
    477   BA(sdis_estimator_get_radiative_flux(estimator, &F));
    478 
    479   BA(sdis_estimator_get_total_flux(estimator, NULL));
    480   BA(sdis_estimator_get_total_flux(NULL, &F));
    481   BA(sdis_estimator_get_total_flux(estimator, &F));
    482 
    483   BA(sdis_estimator_get_realisation_count(estimator, NULL));
    484   BA(sdis_estimator_get_realisation_count(NULL, &nreals));
    485   OK(sdis_estimator_get_realisation_count(estimator, &nreals));
    486 
    487   BA(sdis_estimator_get_failure_count(estimator, NULL));
    488   BA(sdis_estimator_get_failure_count(NULL, &nfails));
    489   OK(sdis_estimator_get_failure_count(estimator, &nfails));
    490 
    491   BA(sdis_estimator_get_temperature(estimator, NULL));
    492   BA(sdis_estimator_get_temperature(NULL, &T));
    493   OK(sdis_estimator_get_temperature(estimator, &T));
    494 
    495   BA(sdis_estimator_get_realisation_time(estimator, NULL));
    496   BA(sdis_estimator_get_realisation_time(NULL, &time));
    497   OK(sdis_estimator_get_realisation_time(estimator, &time));
    498 
    499   BA(sdis_estimator_get_rng_state(NULL, &rng_state));
    500   BA(sdis_estimator_get_rng_state(estimator, NULL));
    501   OK(sdis_estimator_get_rng_state(estimator, &rng_state));
    502 
    503   ref = 300;
    504   printf("Temperature at (%g, %g, %g) with Tfluid=%g = %g ~ %g +/- %g\n",
    505     SPLIT3(solve_args.position), fluid_args->temperature, ref, T.E, T.SE);
    506   printf("Time per realisation (in usec) = %g +/- %g\n", time.E, time.SE);
    507   printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N);
    508 
    509   CHK(nfails + nreals == N);
    510   CHK(nfails < N/1000);
    511   CHK(eq_eps(T.E, ref, T.SE));
    512 
    513   BA(sdis_estimator_ref_get(NULL));
    514   OK(sdis_estimator_ref_get(estimator));
    515   BA(sdis_estimator_ref_put(NULL));
    516   OK(sdis_estimator_ref_put(estimator));
    517   OK(sdis_estimator_ref_put(estimator));
    518 
    519   /* The external fluid cannot have an unknown temperature */
    520   fluid_args->temperature = SDIS_TEMPERATURE_NONE;
    521   BA(sdis_solve_probe(scn, &solve_args, &estimator));
    522 
    523   fluid_args->temperature = 300;
    524   OK(sdis_solve_probe(scn, &solve_args, &estimator));
    525 
    526   BA(sdis_solve_probe_green_function(NULL, &solve_args, &green));
    527   BA(sdis_solve_probe_green_function(scn, NULL, &green));
    528   BA(sdis_solve_probe_green_function(scn, &solve_args, NULL));
    529   solve_args.nrealisations = 0;
    530   BA(sdis_solve_probe_green_function(scn, &solve_args, &green));
    531   solve_args.nrealisations = N;
    532   OK(sdis_solve_probe_green_function(scn, &solve_args, &green));
    533 
    534   BA(sdis_green_function_solve(NULL, &estimator2));
    535   BA(sdis_green_function_solve(green, NULL));
    536   BA(sdis_green_function_solve(NULL, NULL));
    537   OK(sdis_green_function_solve(green, &estimator2));
    538 
    539   check_green_function(green);
    540   check_estimator_eq(estimator, estimator2);
    541 
    542   OK(sdis_estimator_ref_put(estimator));
    543   OK(sdis_estimator_ref_put(estimator2));
    544   printf("\n");
    545 
    546   /* Check same green used at a different temperature */
    547   fluid_args->temperature = 500;
    548 
    549   OK(sdis_solve_probe(scn, &solve_args, &estimator));
    550   OK(sdis_estimator_get_realisation_count(estimator, &nreals));
    551   OK(sdis_estimator_get_failure_count(estimator, &nfails));
    552   OK(sdis_estimator_get_temperature(estimator, &T));
    553   OK(sdis_estimator_get_realisation_time(estimator, &time));
    554 
    555   ref = 500;
    556   printf("Temperature at (%g, %g, %g) with Tfluid=%g = %g ~ %g +/- %g\n",
    557     SPLIT3(solve_args.position), fluid_args->temperature, ref, T.E, T.SE);
    558   printf("Time per realisation (in usec) = %g +/- %g\n", time.E, time.SE);
    559   printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N);
    560 
    561   CHK(nfails + nreals == N);
    562   CHK(nfails < N / 1000);
    563   CHK(eq_eps(T.E, ref, T.SE));
    564 
    565   OK(sdis_green_function_solve(green, &estimator2));
    566   check_green_function(green);
    567   check_estimator_eq(estimator, estimator2);
    568 
    569   stream = tmpfile();
    570   CHK(stream);
    571   BA(sdis_green_function_write(NULL, stream));
    572   BA(sdis_green_function_write(green, NULL));
    573   OK(sdis_green_function_write(green, stream));
    574 
    575   BA(sdis_green_function_ref_get(NULL));
    576   OK(sdis_green_function_ref_get(green));
    577   BA(sdis_green_function_ref_put(NULL));
    578   OK(sdis_green_function_ref_put(green));
    579   OK(sdis_green_function_ref_put(green));
    580 
    581   rewind(stream);
    582   green_args.scene = NULL;
    583   green_args.stream = stream;
    584   BA(sdis_green_function_create_from_stream(&green_args, &green));
    585   green_args.scene = scn;
    586   green_args.stream = NULL;
    587   BA(sdis_green_function_create_from_stream(&green_args, &green));
    588   green_args.scene = scn;
    589   green_args.stream = stream;
    590   BA(sdis_green_function_create_from_stream(&green_args, NULL));
    591   OK(sdis_green_function_create_from_stream(&green_args, &green));
    592   CHK(!fclose(stream));
    593 
    594   OK(sdis_green_function_solve(green, &estimator3));
    595   check_green_function(green);
    596   check_estimator_eq_strict(estimator2, estimator3);
    597   OK(sdis_green_function_ref_put(green));
    598   OK(sdis_estimator_ref_put(estimator3));
    599 
    600   CHK(stream = tmpfile());
    601   hash_sha256("Hello, world!", strlen("Hello, world!"), solve_args.signature);
    602   OK(sdis_solve_probe_green_function(scn, &solve_args, &green));
    603   OK(sdis_green_function_write(green, stream));
    604   OK(sdis_green_function_ref_put(green));
    605 
    606   green_args.scene = scn;
    607   green_args.stream = stream;
    608   rewind(stream);
    609   BA(sdis_green_function_create_from_stream(&green_args, &green));
    610   memcpy(green_args.signature, solve_args.signature, sizeof(hash256_T));
    611   rewind(stream);
    612   OK(sdis_green_function_create_from_stream(&green_args, &green));
    613   CHK(!fclose(stream));
    614 
    615   OK(sdis_green_function_solve(green, &estimator3));
    616   check_estimator_eq_strict(estimator2, estimator3);
    617   OK(sdis_green_function_ref_put(green));
    618   OK(sdis_estimator_ref_put(estimator3));
    619 
    620   OK(sdis_estimator_ref_put(estimator));
    621   OK(sdis_estimator_ref_put(estimator2));
    622 
    623   OK(sdis_solve_probe(scn, &solve_args, &estimator));
    624   BA(sdis_estimator_get_paths_count(NULL, &n));
    625   BA(sdis_estimator_get_paths_count(estimator, NULL));
    626   OK(sdis_estimator_get_paths_count(estimator, &n));
    627   CHK(n == 0);
    628   OK(sdis_estimator_ref_put(estimator));
    629 
    630   solve_args.nrealisations = N_dump;
    631   solve_args.register_paths = SDIS_HEAT_PATH_ALL;
    632 
    633   /* Check simulation error handling when paths are registered */
    634   fluid_args->temperature = SDIS_TEMPERATURE_NONE;
    635   BA(sdis_solve_probe(scn, &solve_args, &estimator));
    636 
    637   fluid_args->temperature = 300;
    638   OK(sdis_solve_probe(scn, &solve_args, &estimator));
    639   OK(sdis_estimator_get_paths_count(estimator, &n));
    640   CHK(n == N_dump);
    641 
    642   BA(sdis_estimator_get_path(NULL, 0, &path));
    643   BA(sdis_estimator_get_path(estimator, n, &path));
    644   BA(sdis_estimator_get_path(estimator, 0, NULL));
    645   OK(sdis_estimator_get_path(estimator, 0, &path));
    646 
    647   dump_ctx.stream = stderr;
    648   BA(sdis_estimator_for_each_path(NULL, process_heat_path, &dump_ctx));
    649   BA(sdis_estimator_for_each_path(estimator, NULL, &dump_ctx));
    650   OK(sdis_estimator_for_each_path(estimator, process_heat_path, &dump_ctx));
    651 
    652   OK(sdis_estimator_ref_put(estimator));
    653 
    654   printf("\n");
    655 
    656   /* Green and ambient radiative temperature */
    657   solve_args.nrealisations = N;
    658   radenv_args->temperature = 300;
    659   radenv_args->reference = 300;
    660   t_range[0] = 300;
    661   t_range[1] = 300;
    662   OK(sdis_scene_set_temperature_range(scn, t_range));
    663 
    664   interface_args->epsilon = 1;
    665   interface_args->reference_temperature = 300;
    666 
    667   OK(sdis_solve_probe(scn, &solve_args, &estimator));
    668   OK(sdis_solve_probe_green_function(scn, &solve_args, &green));
    669   OK(sdis_green_function_solve(green, &estimator2));
    670 
    671   check_green_function(green);
    672   check_estimator_eq(estimator, estimator2);
    673 
    674   OK(sdis_estimator_ref_put(estimator));
    675   OK(sdis_estimator_ref_put(estimator2));
    676 
    677   /* Check same green used at different ambient radiative temperature */
    678   radenv_args->temperature = 300;
    679   t_range[0] = 300;
    680   t_range[1] = 600;
    681   OK(sdis_scene_set_temperature_range(scn, t_range));
    682 
    683   OK(sdis_solve_probe(scn, &solve_args, &estimator));
    684   OK(sdis_green_function_solve(green, &estimator2));
    685 
    686   check_green_function(green);
    687   check_estimator_eq(estimator, estimator2);
    688 
    689   OK(sdis_estimator_ref_put(estimator));
    690   OK(sdis_estimator_ref_put(estimator2));
    691   OK(sdis_green_function_ref_put(green));
    692   OK(sdis_radiative_env_ref_put(radenv));
    693 
    694   OK(sdis_scene_ref_put(scn));
    695   OK(sdis_device_ref_put(dev));
    696 
    697   check_memory_allocator(&allocator);
    698   mem_shutdown_proxy_allocator(&allocator);
    699   CHK(mem_allocated_size() == 0);
    700   return 0;
    701 }
    702