YAC 3.21.0
Yet Another Coupler
Loading...
Searching...
No Matches
test_interp_method_parallel.c
Go to the documentation of this file.
1// Copyright (c) 2024 The YAC Authors
2//
3// SPDX-License-Identifier: BSD-3-Clause
4
5#include <stdlib.h>
6#include <unistd.h>
7#include <mpi.h>
8#include <yaxt.h>
9#include <string.h>
10
11#include "tests.h"
12#include "test_common.h"
13#include "geometry.h"
14#include "read_icon_grid.h"
27#include "weight_file_common.h"
28#include "instrument.h"
29#include "yac_mpi.h"
30
36static void utest_compute_weights_callback(
37 double const tgt_coords[3], int src_cell_id, size_t src_cell_idx,
38 int const ** global_results_points, double ** result_weights,
39 size_t * result_count, void * user_data);
40
42typedef struct {
43 int start_calls[YAC_INSTR_REGION_COUNT];
44 int stop_calls[YAC_INSTR_REGION_COUNT];
45 int last_start_depth[YAC_INSTR_REGION_COUNT];
46 int bad_handle_calls;
47} utest_instr_counters;
48
50static void utest_instrumentation_enable(utest_instr_counters * instr_ctx);
53static void utest_instrumentation_disable_and_check(
54 utest_instr_counters * instr_ctx, int const * ref_start_calls);
57static void utest_init_ref_start_calls(
58 int * ref_start_calls, int default_start_calls);
59
60int main(int argc, char** argv) {
61
62 MPI_Init(NULL, NULL);
63
64 xt_initialize(MPI_COMM_WORLD);
65
66 int comm_rank, comm_size;
67 MPI_Comm_rank(MPI_COMM_WORLD, &comm_rank);
68 MPI_Comm_size(MPI_COMM_WORLD, &comm_size);
69
70 utest_set_even_io_rank_list(MPI_COMM_WORLD);
71
72 if (argc != 2) {
73 PUT_ERR("ERROR: missing grid file directory");
74 xt_finalize();
75 MPI_Finalize();
76 return TEST_EXIT_CODE;
77 }
78
79 char * filenames[2];
80 char * grid_filenames[] ={"icon_grid_R02B01.nc", "icon_grid_R03B01.nc"};
81 for (int i = 0; i < 2; ++i)
82 filenames[i] =
83 strcat(
84 strcpy(
85 malloc(strlen(argv[1]) + strlen(grid_filenames[i]) + 2), argv[1]),
86 grid_filenames[i]);
87
89
90 for (int i = 0; i < 2; ++i)
91 grid_data[i] =
93 filenames[i], MPI_COMM_WORLD);
94
95 struct yac_basic_grid * grids[2] =
96 {yac_basic_grid_new(filenames[0], grid_data[0]),
97 yac_basic_grid_new(filenames[1], grid_data[1])};
98
99 struct yac_dist_grid_pair * grid_pair =
100 yac_dist_grid_pair_new(grids[0], grids[1], MPI_COMM_WORLD);
101
102 struct yac_interp_field src_fields[] =
103 {{.location = YAC_LOC_CELL, .coordinates_idx = SIZE_MAX, .masks_idx = SIZE_MAX}};
104 size_t num_src_fields = sizeof(src_fields) / sizeof(src_fields[0]);
106 {.location = YAC_LOC_CELL, .coordinates_idx = SIZE_MAX, .masks_idx = SIZE_MAX};
107
108 struct yac_interp_grid * interp_grid =
109 yac_interp_grid_new(grid_pair, filenames[0], filenames[1],
111
112 struct interp_method * method_stack[] =
113 {yac_interp_method_fixed_new(-1.0), NULL};
114
115 // the search over the whole method stack and the do_search routine of each
116 // individual method are instrumented; the callbacks are registered only
117 // around the search itself, so no unrelated region can contribute to the
118 // counts checked afterwards
119 utest_instr_counters instr_ctx;
120 utest_instrumentation_enable(&instr_ctx);
121
122 struct yac_interp_weights * weights =
123 yac_interp_method_do_search(method_stack, interp_grid);
124
125 { // only the fixed value method is in the stack
126 int ref_start_calls[YAC_INSTR_REGION_COUNT];
127 utest_init_ref_start_calls(ref_start_calls, 0);
128 ref_start_calls[YAC_INSTR_INTERP_SEARCH_FIXED] = 1;
129
130 utest_instrumentation_disable_and_check(&instr_ctx, ref_start_calls);
131 }
132
133 struct yac_interpolation * interpolation_src =
135 weights, YAC_MAPPING_ON_SRC, 1,
136 YAC_FRAC_MASK_NO_VALUE, 1.0, 0.0, NULL, 1, 1);
137 struct yac_interpolation * interpolation_tgt =
139 weights, YAC_MAPPING_ON_TGT, 1,
140 YAC_FRAC_MASK_NO_VALUE, 1.0, 0.0, NULL, 1, 1);
141
142 yac_interpolation_delete(interpolation_src);
143 yac_interpolation_delete(interpolation_tgt);
144
146
147 yac_interp_method_delete(method_stack);
148
149 yac_interp_grid_delete(interp_grid);
150 yac_dist_grid_pair_delete(grid_pair);
151
152 for (int i = 0; i < 2; ++i) {
153 yac_basic_grid_delete(grids[i]);
154 free(filenames[i]);
155 }
156
157 { // test interpolation_complete argument
158
159 char const * weight_file_empty =
160 "test_interp_method_parallel_weights_empty.nc";
161 char const * weight_file_fixed =
162 "test_interp_method_parallel_weights_fixed.nc";
163
164 char const * src_grid_name = "src_grid";
165 char const * tgt_grid_name = "tgt_grid";
166
167 // create weight file (one empty and one with a fixed value)
168 if (comm_rank == 0) {
169
170 { // empty file
171 int * tgt_indices = NULL;
172 int * src_indices = NULL;
173 double * weights = NULL;
174 size_t num_links = 0;
175 enum yac_location src_locations[] = {YAC_LOC_CELL};
176 enum yac_location tgt_location = YAC_LOC_CELL;
177 enum {
178 NUM_SRC_FIELDS = sizeof(src_locations) / sizeof(src_locations[0])};
179 int num_links_per_field[NUM_SRC_FIELDS] = {num_links};
180 int * tgt_id_fixed = NULL;
181 size_t num_fixed_tgt = 0;
182 double * fixed_values = NULL;
183 int * num_tgt_per_fixed_value = NULL;
184 size_t num_fixed_values = 0;
185
186 utest_write_weight_file(
187 weight_file_empty, src_indices, tgt_indices, weights, num_links,
188 src_locations, NUM_SRC_FIELDS, num_links_per_field, tgt_id_fixed,
189 num_fixed_tgt, fixed_values, num_tgt_per_fixed_value,
190 num_fixed_values, tgt_location, src_grid_name, tgt_grid_name);
191 }
192
193 { // file with fixed value
194 int * tgt_indices = NULL;
195 int * src_indices = NULL;
196 double * weights = NULL;
197 size_t num_links = 0;
198 enum yac_location src_locations[] = {YAC_LOC_CELL};
199 enum yac_location tgt_location = YAC_LOC_CELL;
200 enum {
201 NUM_SRC_FIELDS = sizeof(src_locations) / sizeof(src_locations[0])};
202 int num_links_per_field[NUM_SRC_FIELDS] = {num_links};
203 int tgt_id_fixed[] = {0, 1, 2, 3};
204 size_t num_fixed_tgt = 4;
205 double fixed_values[] = {999.0};
206 int num_tgt_per_fixed_value[] = {4};
207 size_t num_fixed_values = 1;
208
209 utest_write_weight_file(
210 weight_file_fixed, src_indices, tgt_indices, weights, num_links,
211 src_locations, NUM_SRC_FIELDS, num_links_per_field, tgt_id_fixed,
212 num_fixed_tgt, fixed_values, num_tgt_per_fixed_value,
213 num_fixed_values, tgt_location, src_grid_name, tgt_grid_name);
214 }
215 }
216 double coordinates_x[] = {0.0, 1.0, 2.0};
217 double coordinates_y[] = {0.0, 1.0, 2.0};
218 size_t num_vertices[2] = {3,3};
219 int cyclic[2] = {0,0};
220
221 struct yac_basic_grid * src_grid =
224 struct yac_basic_grid * tgt_grid =
227
228 double cell_coords[4][3];
229 double cell_coords_x[] = {0.5, 1.5};
230 double cell_coords_y[] = {0.5, 1.5};
231 for (int i = 0, k = 0; i < 2; ++i)
232 for (int j = 0; j < 2; ++j, ++k)
233 LLtoXYZ_deg(cell_coords_x[j], cell_coords_y[i], cell_coords[k]);
236
237 struct yac_dist_grid_pair * grid_pair =
238 yac_dist_grid_pair_new(src_grid, tgt_grid, MPI_COMM_WORLD);
239
240 struct yac_interp_field src_fields[] =
241 {{.location = YAC_LOC_CELL, .coordinates_idx = 0, .masks_idx = SIZE_MAX}};
242 size_t num_src_fields = sizeof(src_fields) / sizeof(src_fields[0]);
244 {.location = YAC_LOC_CELL, .coordinates_idx = 0, .masks_idx = SIZE_MAX};
245
246 struct yac_interp_grid * interp_grid =
249
250 //----------------------------------------
251 // test generation of interpolation method
252 //----------------------------------------
253
254 struct interp_method * method_stack[] = {
255 yac_interp_method_file_new( // this method stops the stack
256 weight_file_empty, YAC_INTERP_FILE_MISSING_ERROR,
262 yac_interp_method_callback_new(utest_compute_weights_callback, NULL),
263 yac_interp_method_check_new( NULL, NULL, NULL, NULL),
272 weight_file_fixed, YAC_INTERP_FILE_MISSING_ERROR,
282 .type =
285 .max_search_distance = YAC_INTERP_NNN_MAX_SEARCH_DISTANCE_DEFAULT,
289 NULL};
290
291 //-----------------
292 // generate weights
293 //-----------------
294
295 utest_instr_counters instr_ctx;
296 utest_instrumentation_enable(&instr_ctx);
297
298 struct yac_interp_weights * weights =
299 yac_interp_method_do_search(method_stack, interp_grid);
300
301 if (yac_interp_weights_get_interp_count(weights) != 0)
302 PUT_ERR(
303 "error in handling of interpolation_complete argument by do_search");
304
305 { // the first method in the stack stops the remaining stack, hence all
306 // following methods return immediately without entering their region;
307 // the check method is the only exception, because it ignores
308 // interpolation_complete
309 int ref_start_calls[YAC_INSTR_REGION_COUNT];
310 utest_init_ref_start_calls(ref_start_calls, 0);
311 ref_start_calls[YAC_INSTR_INTERP_SEARCH_FILE] = 1;
312 ref_start_calls[YAC_INSTR_INTERP_SEARCH_CHECK] = 1;
313
314 utest_instrumentation_disable_and_check(&instr_ctx, ref_start_calls);
315 }
316
317 //-----------------------------------------------------------------
318 // check the instrumentation for a method that actually searches
319 //-----------------------------------------------------------------
320
321 { // the distributed grid searches used by a method are instrumented as
322 // well and have to be nested inside the region of that method
323 struct interp_method * nnn_method_stack[] = {
327 .n = 2,
328 .max_search_distance = YAC_INTERP_NNN_MAX_SEARCH_DISTANCE_DEFAULT,
330 NULL};
331
332 utest_instr_counters nnn_instr_ctx;
333 utest_instrumentation_enable(&nnn_instr_ctx);
334
335 struct yac_interp_weights * nnn_weights =
336 yac_interp_method_do_search(nnn_method_stack, interp_grid);
337
338 int ref_start_calls[YAC_INSTR_REGION_COUNT];
339 utest_init_ref_start_calls(ref_start_calls, 0);
340 ref_start_calls[YAC_INSTR_INTERP_SEARCH_NNN] = 1;
341
342 utest_instrumentation_disable_and_check(&nnn_instr_ctx, ref_start_calls);
343
344 int nnn_search_calls =
345 nnn_instr_ctx.start_calls[YAC_INSTR_DIST_GRID_PAIR_NNN_SEARCH];
346 int nnn_search_depth =
347 nnn_instr_ctx.last_start_depth[YAC_INSTR_DIST_GRID_PAIR_NNN_SEARCH];
348
349 if (nnn_search_calls != 1) {
350 PUT_ERR_F(
351 "error in instrumentation of yac_dist_grid_pair_do_nnn_search: "
352 "region was entered %d times (expected 1)\n", nnn_search_calls);
353 } else if (nnn_search_depth != 2) {
354 PUT_ERR_F(
355 "error in instrumentation of yac_dist_grid_pair_do_nnn_search: "
356 "region was entered at depth %d (expected 2)\n", nnn_search_depth);
357 }
358
359 yac_interp_weights_delete(nnn_weights);
360 yac_interp_method_delete(nnn_method_stack);
361 }
362
363 //---------------
364 // cleanup
365 //---------------
366
367 yac_interp_method_delete(method_stack);
369 yac_interp_grid_delete(interp_grid);
370 yac_dist_grid_pair_delete(grid_pair);
371 yac_basic_grid_delete(tgt_grid);
372 yac_basic_grid_delete(src_grid);
373
374 if (comm_rank == 0) {
375 unlink(weight_file_empty);
376 unlink(weight_file_fixed);
377 }
378 }
379
380 xt_finalize();
381
382 MPI_Finalize();
383
384 return TEST_EXIT_CODE;
385}
386
387static void utest_compute_weights_callback(
388 double const tgt_coords[3], int src_cell_id, size_t src_cell_idx,
389 int const ** global_results_points, double ** result_weights,
390 size_t * result_count, void * user_data) {
391
392 UNUSED(tgt_coords);
393 UNUSED(src_cell_id);
394 UNUSED(src_cell_idx);
395 UNUSED(global_results_points);
396 UNUSED(result_weights);
397 UNUSED(result_count);
399
400 PUT_ERR("ERROR: in handling interpolation_complete in do_search_callback");
401}
402
405static int utest_instr_handles[YAC_INSTR_REGION_COUNT];
406
407static void * utest_instrument_create_cb(int id, void * user_data) {
408
410
411 return (void *)&utest_instr_handles[id];
412}
413
414static void utest_instrument_start_cb(
415 int id, void * handle, int depth, void * user_data) {
416
417 utest_instr_counters * instr_ctx = (utest_instr_counters *)user_data;
418
419 if (handle != (void *)&utest_instr_handles[id])
420 instr_ctx->bad_handle_calls++;
421 instr_ctx->start_calls[id]++;
422 instr_ctx->last_start_depth[id] = depth;
423}
424
425static void utest_instrument_stop_cb(
426 int id, void * handle, int depth, void * user_data) {
427
428 UNUSED(depth);
429
430 utest_instr_counters * instr_ctx = (utest_instr_counters *)user_data;
431
432 if (handle != (void *)&utest_instr_handles[id])
433 instr_ctx->bad_handle_calls++;
434 instr_ctx->stop_calls[id]++;
435}
436
437static void utest_instrumentation_enable(utest_instr_counters * instr_ctx) {
438
439 memset(instr_ctx, 0, sizeof(*instr_ctx));
440 for (size_t i = 0; i < YAC_INSTR_REGION_COUNT; ++i)
441 instr_ctx->last_start_depth[i] = -1;
442
444 utest_instrument_create_cb, utest_instrument_start_cb,
445 utest_instrument_stop_cb, NULL, instr_ctx);
446}
447
448static void utest_init_ref_start_calls(
449 int * ref_start_calls, int default_start_calls) {
450
451 // only the interpolation search regions (a contiguous block in
452 // YAC_INSTRUMENT_REGION_TABLE) are checked; the regions entered by the
453 // methods themselves (e.g. the distributed grid searches) are not, because
454 // how often they are entered depends on the domain decomposition
455 for (size_t i = 0; i < YAC_INSTR_REGION_COUNT; ++i)
456 ref_start_calls[i] = -1;
457 for (int i = YAC_INSTR_INTERP_SEARCH;
458 i <= YAC_INSTR_INTERP_SEARCH_SPMAP; ++i)
459 ref_start_calls[i] = default_start_calls;
460
461 // the search over the method stack is entered exactly once per search
462 ref_start_calls[YAC_INSTR_INTERP_SEARCH] = 1;
463}
464
465static void utest_instrumentation_disable_and_check(
466 utest_instr_counters * instr_ctx, int const * ref_start_calls) {
467
469
470 if (instr_ctx->bad_handle_calls != 0)
471 PUT_ERR(
472 "error in instrumentation of do_search "
473 "(handle does not match the one created for the region)");
474
475 for (size_t i = 0; i < YAC_INSTR_REGION_COUNT; ++i) {
476
477 char const * region_name =
479
480 if (instr_ctx->start_calls[i] != instr_ctx->stop_calls[i])
481 PUT_ERR_F(
482 "error in instrumentation of do_search: region \"%s\" was started "
483 "%d times, but stopped %d times\n",
484 region_name, instr_ctx->start_calls[i], instr_ctx->stop_calls[i]);
485
486 if (ref_start_calls[i] == -1) continue;
487
488 if (instr_ctx->start_calls[i] != ref_start_calls[i])
489 PUT_ERR_F(
490 "error in instrumentation of do_search: region \"%s\" was started "
491 "%d times (expected %d)\n",
492 region_name, instr_ctx->start_calls[i], ref_start_calls[i]);
493
494 // the search over the method stack is the outermost region; the regions
495 // of the individual methods are nested directly inside of it
496 if (instr_ctx->start_calls[i] > 0) {
497 int ref_depth = (i == (size_t)YAC_INSTR_INTERP_SEARCH)?0:1;
498 if (instr_ctx->last_start_depth[i] != ref_depth)
499 PUT_ERR_F(
500 "error in instrumentation of do_search: region \"%s\" was started "
501 "at depth %d (expected %d)\n",
502 region_name, instr_ctx->last_start_depth[i], ref_depth);
503 }
504 }
505}
struct yac_basic_grid * yac_basic_grid_new(char const *name, struct yac_basic_grid_data grid_data)
Definition basic_grid.c:57
struct yac_basic_grid * yac_basic_grid_reg_2d_deg_new(char const *name, size_t nbr_vertices[2], int cyclic[2], double *lon_vertices, double *lat_vertices)
Definition basic_grid.c:311
size_t yac_basic_grid_add_coordinates(struct yac_basic_grid *grid, enum yac_location location, yac_coordinate_pointer coordinates, size_t count)
Definition basic_grid.c:222
void yac_basic_grid_delete(struct yac_basic_grid *grid)
Definition basic_grid.c:77
#define UNUSED(x)
Definition core.h:72
void yac_dist_grid_pair_delete(struct yac_dist_grid_pair *grid_pair)
Definition dist_grid.c:2377
struct yac_dist_grid_pair * yac_dist_grid_pair_new(struct yac_basic_grid *grid_a, struct yac_basic_grid *grid_b, MPI_Comm comm)
Definition dist_grid.c:2089
static void LLtoXYZ_deg(double lon, double lat, double p_out[])
Definition geometry.h:278
void yac_instrument_set_callbacks(yac_instrument_create_cb create_cb, yac_instrument_start_cb start_cb, yac_instrument_stop_cb stop_cb, yac_instrument_destroy_cb destroy_cb, void *user_data)
Definition instrument.c:97
void yac_instrument_clear_callbacks(void)
Definition instrument.c:149
const char * yac_instrument_region_name(yac_instrument_region_id id)
Definition instrument.c:154
yac_instrument_region_id
Definition instrument.h:53
@ YAC_INSTR_REGION_COUNT
Definition instrument.h:57
void yac_interp_grid_delete(struct yac_interp_grid *interp_grid)
struct yac_interp_grid * yac_interp_grid_new(struct yac_dist_grid_pair *grid_pair, char const *src_grid_name, char const *tgt_grid_name, size_t num_src_fields, struct yac_interp_field const *src_fields, struct yac_interp_field const tgt_field)
Definition interp_grid.c:31
void yac_interp_method_delete(struct interp_method **method)
Delete an interpolation stack and free its resources (but not the pointer array).
struct yac_interp_weights * yac_interp_method_do_search(struct interp_method **method, struct yac_interp_grid *interp_grid)
Perform weight computation using given interpolation stack and grid.
Defines the interface of the interpolation method "base class" in YAC.
struct interp_method * yac_interp_method_avg_new(enum yac_interp_avg_weight_type weight_type, int partial_coverage)
#define YAC_INTERP_AVG_PARTIAL_COVERAGE_DEFAULT
yac_interp_avg_weight_type
#define YAC_INTERP_AVG_WEIGHT_TYPE_DEFAULT
void * user_data
struct interp_method * yac_interp_method_callback_new(yac_func_compute_weights compute_weights_callback, void *user_data)
struct interp_method * yac_interp_method_check_new(func_constructor constructor_callback, void *constructor_user_data, func_do_search do_search_callback, void *do_search_user_data)
struct interp_method * yac_interp_method_conserv_new(int order, int enforced_conserv, int partial_coverage, enum yac_interp_method_conserv_normalisation normalisation)
#define YAC_INTERP_CONSERV_NORMALISATION_DEFAULT
#define YAC_INTERP_CONSERV_ENFORCED_CONSERV_DEFAULT
#define YAC_INTERP_CONSERV_PARTIAL_COVERAGE_DEFAULT
#define YAC_INTERP_CONSERV_ORDER_DEFAULT
yac_interp_method_conserv_normalisation
struct interp_method * yac_interp_method_creep_new(int creep_distance)
#define YAC_INTERP_CREEP_DISTANCE_DEFAULT
struct interp_method * yac_interp_method_file_new(char const *weight_file_name, enum yac_interp_file_on_missing_file on_missing_file, enum yac_interp_file_on_success on_success)
@ YAC_INTERP_FILE_MISSING_ERROR
abort on missing file
@ YAC_INTERP_FILE_SUCCESS_CONT
@ YAC_INTERP_FILE_SUCCESS_STOP
struct interp_method * yac_interp_method_fixed_new(double value)
#define YAC_INTERP_FIXED_VALUE_DEFAULT
struct interp_method * yac_interp_method_hcsbb_new()
struct interp_method * yac_interp_method_ncc_new(enum yac_interp_ncc_weight_type weight_type, int partial_coverage)
#define YAC_INTERP_NCC_PARTIAL_COVERAGE_DEFAULT
#define YAC_INTERP_NCC_WEIGHT_TYPE_DEFAULT
yac_interp_ncc_weight_type
struct interp_method * yac_interp_method_nnn_new(struct yac_interp_method_nnn_config config)
#define YAC_INTERP_NNN_WEIGHTED_DEFAULT
yac_interp_nnn_weight_type
@ YAC_INTERP_NNN_AVG
average of n source points
#define YAC_INTERP_NNN_MAX_SEARCH_DISTANCE_DEFAULT
#define YAC_INTERP_NNN_GAUSS_SCALE_DEFAULT
#define YAC_INTERP_NNN_N_DEFAULT
struct interp_method * yac_interp_method_spmap_new(struct yac_interp_spmap_config const *default_config, struct yac_spmap_overwrite_config const *const *overwrite_configs)
#define YAC_INTERP_SPMAP_OVERWRITE_DEFAULT
#define YAC_INTERP_SPMAP_DEFAULT_CONFIG
struct yac_interpolation * yac_interp_weights_get_interpolation(struct yac_interp_weights *weights, enum yac_interp_weights_reorder_type reorder, size_t collection_size, double frac_mask_fallback_value, double scaling_factor, double scaling_summand, char const *yaxt_exchanger_name, int is_source, int is_target)
void yac_interp_weights_delete(struct yac_interp_weights *weights)
size_t yac_interp_weights_get_interp_count(struct yac_interp_weights *weights)
@ YAC_MAPPING_ON_TGT
weights will be applied at target processes
@ YAC_MAPPING_ON_SRC
weights will be applied at source processes
void yac_interpolation_delete(struct yac_interpolation *interp)
Free an interpolation object and release all resources.
double const YAC_FRAC_MASK_NO_VALUE
yac_location
Definition location.h:12
@ YAC_LOC_CELL
Definition location.h:14
struct yac_basic_grid_data yac_read_icon_basic_grid_data_parallel(const char *filename, MPI_Comm comm)
enum yac_location location
Definition basic_grid.h:16
struct yac_interp_field tgt_field
Definition interp_grid.c:26
size_t num_src_fields
Definition interp_grid.c:27
struct yac_dist_grid_pair * grid_pair
Definition interp_grid.c:25
struct yac_interp_field src_fields[]
Definition interp_grid.c:28
double * data
char const src_grid_name[]
char const tgt_grid_name[]
double coordinates_x[]
double coordinates_y[]
double cell_coords[36][3]
unsigned cyclic[2]
#define PUT_ERR_F(fmt,...)
Definition tests.h:11
#define TEST_EXIT_CODE
Definition tests.h:15
#define PUT_ERR(string)
Definition tests.h:10
int id
Definition toy_scrip.c:115