A test for the parallel nearest corner cells interpolation method.
#include <stdlib.h>
#include "tests.h"
#include "test_common.h"
#include "dist_grid_utils.h"
#include <mpi.h>
#include <yaxt.h>
#include <netcdf.h>
size_t num_reorder_types = sizeof(reorder_types) / sizeof(reorder_types[0]);
static char const * grid_names[2] = {"src_grid", "tgt_grid"};
static double compute_ref_tgt_value(
size_t tgt_corner, int * tgt_global_core_mask, int * tgt_global_field_mask,
int * src_global_core_mask, int * src_global_field_maske, int with_src_mask,
int partial_coverage);
MPI_Init(NULL, NULL);
xt_initialize(MPI_COMM_WORLD);
int comm_rank, comm_size;
MPI_Comm_rank(MPI_COMM_WORLD, &comm_rank);
MPI_Comm_size(MPI_COMM_WORLD, &comm_size);
MPI_Barrier(MPI_COMM_WORLD);
if (comm_size != 3) {
PUT_ERR("ERROR: wrong number of processes");
xt_finalize();
MPI_Finalize();
return TEST_EXIT_CODE;
}
MPI_Comm split_comm;
MPI_Comm_split(
MPI_COMM_WORLD, comm_rank < 2, 0, &split_comm);
int split_comm_rank, split_comm_size;
MPI_Comm_rank(split_comm, &split_comm_rank);
MPI_Comm_size(split_comm, &split_comm_size);
{
int is_tgt = split_comm_size == 1;
double coordinates_x[2][10] =
{{0,1,2,3,4,5}, {0.25,0.75,1.25,1.75,2.25,2.75,3.25,3.75,4.25,4.75}};
double coordinates_y[2][8] =
{{-2,-1,0,1,2}, {-1.75,-1.25,-0.75,-0.25,0.25,0.75,1.25,1.75}};
double cell_coordiantes_x[5 * 4] =
{0.5,1.5,2.5,3.5,4.5,
0.5,1.5,2.5,3.5,4.5,
0.5,1.5,2.5,3.5,4.5,
0.5,1.5,2.5,3.5,4.5};
double cell_coordiantes_y[5 * 4] =
{-1.5,-1.5,-1.5,-1.5,-1.5,
-0.5,-0.5,-0.5,-0.5,-0.5,
0.5,0.5,0.5,0.5,0.5,
1.5,1.5,1.5,1.5,1.5};
size_t const num_cells[2][2] = {{5,4}, {9,7}};
size_t local_start[2][2][2] = {{{0,0},{3,0}}, {{0,0}}};
size_t local_count[2][2][2] = {{{3,4},{2,4}}, {{9,7}}};
int global_core_mask[2][10*8] =
{{1,0,0,0,0,
1,1,0,0,0,
1,1,1,1,0,
1,1,1,1,0},
{1,1,1,0,0,0,0,0,0,0,
1,1,1,0,0,0,0,0,0,0,
1,1,1,1,1,1,0,0,0,0,
1,1,1,1,1,1,0,0,0,0,
1,1,1,1,1,1,1,1,1,0,
1,1,1,1,1,1,1,1,1,0,
1,1,1,1,1,1,1,1,1,0,
1,1,1,1,1,1,1,1,1,0}};
int global_field_mask[2][10*8] =
{{0,1,1,1,1,
0,1,1,1,1,
0,1,1,1,1,
0,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0,0,0}};
int with_halo = 0;
for (size_t i = 0; i <= num_cells[is_tgt][0]; ++i)
coordinates_x[is_tgt][i] *=
YAC_RAD;
for (size_t i = 0; i <= num_cells[is_tgt][1]; ++i)
coordinates_y[is_tgt][i] *=
YAC_RAD;
yac_generate_basic_grid_data_reg2d(
coordinates_x[is_tgt], coordinates_y[is_tgt],
num_cells[is_tgt],
local_start[is_tgt][split_comm_rank],
local_count[is_tgt][split_comm_rank], with_halo);
if (is_tgt) {
global_core_mask[is_tgt][grid_data.
vertex_ids[i]];
} else {
for (
size_t i = 0; i < grid_data.
num_cells; ++i)
global_core_mask[is_tgt][grid_data.
cell_ids[i]];
}
int field_mask[10*8];
double field_coords[10*8][3];
if (is_tgt) {
field_mask[i] = global_field_mask[is_tgt][grid_data.
vertex_ids[i]];
} else {
for (
size_t i = 0; i < grid_data.
num_cells; ++i) {
field_mask[i] = global_field_mask[is_tgt][grid_data.
cell_ids[i]];
cell_coordiantes_x[grid_data.
cell_ids[i]],
cell_coordiantes_y[grid_data.
cell_ids[i]], field_coords[i]);
}
}
for (int with_src_mask = 0; with_src_mask < 2; ++with_src_mask) {
for (int with_tgt_mask = 0; with_tgt_mask < 2; ++with_tgt_mask) {
.coordinates_idx = 0,
.masks_idx = (with_src_mask)?0:SIZE_MAX}};
size_t num_src_fields = sizeof(src_fields) / sizeof(src_fields[0]);
.coordinates_idx = SIZE_MAX,
.masks_idx = (with_tgt_mask)?0:SIZE_MAX};
size_t const num_weight_types =
sizeof(weight_types) / sizeof(weight_types[0]);
for (size_t weight_types_idx = 0; weight_types_idx < num_weight_types;
++weight_types_idx) {
for (int partial_coverage = 0; partial_coverage < 2;
++partial_coverage) {
weight_types[weight_types_idx], partial_coverage),
for (size_t i = 0; i < num_reorder_types; ++i) {
weights, reorder_types[i], 1,
{
double src_field_data[5*4];
double * src_field = src_field_data;
double ** src_fields = &src_field;
double tgt_field_data[10*8] =
{-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,
-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,
-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,
-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,
-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,
-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,
-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,
-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0,-2.0};
double * tgt_field = tgt_field_data;
if (!is_tgt)
for (
size_t i = 0; i < grid_data.
num_cells; ++i)
src_field[i] = (
double)(grid_data.
cell_ids[i] + 1);
interpolation, &src_fields, &tgt_field);
if (is_tgt) {
double ref_tgt_value =
compute_ref_tgt_value(
i, global_core_mask[1], global_field_mask[1],
global_core_mask[0], global_field_mask[0],
with_src_mask, with_tgt_mask,
weight_types[weight_types_idx], partial_coverage);
if (fabs(ref_tgt_value - tgt_field[i]) > 1e-3)
PUT_ERR("wrong interpolation result");
}
}
}
}
}
}
}
}
}
{
int is_tgt = split_comm_size == 1;
double coordinates_x[2][4] =
{{-2,-1,1,2}, {-1.0,0.0,1.0}};
double coordinates_y[2][4] =
{{-2,-1,1,2}, {-1.0,0.0,1.0}};
double field_coordiantes_x[2][3*3] =
{{-1.5,0.0,1.5,
-1.5,0.0,1.5,
-1.5,0.0,1.5},
{-1.0,0.0,1.0,
-1.0,0.0,1.0,
-1.0,0.0,1.0}};
double field_coordiantes_y[2][3*3] =
{{-1.5,-1.5,-1.5,
0.0,0.0,0.0,
1.5,1.5,1.5},
{-1.0,-1.0,-1.0,
0.0,0.0,0.0,
1.0,1.0,1.0}};
int field_mask[3*3] = {0,1,1, 1,1,1, 1,1,0};
size_t const num_cells[2][2] = {{3,3}, {2,2}};
size_t local_start[2][2][2] = {{{0,0},{0,1}}, {{0,0}}};
size_t local_count[2][2][2] = {{{3,2},{3,2}}, {{2,2}}};
int with_halo = 0;
for (size_t i = 0; i <= num_cells[is_tgt][0]; ++i)
coordinates_x[is_tgt][i] *=
YAC_RAD;
for (size_t i = 0; i <= num_cells[is_tgt][1]; ++i)
coordinates_y[is_tgt][i] *=
YAC_RAD;
yac_generate_basic_grid_data_reg2d(
coordinates_x[is_tgt], coordinates_y[is_tgt],
num_cells[is_tgt],
local_start[is_tgt][split_comm_rank],
local_count[is_tgt][split_comm_rank], with_halo);
double field_coords[3*3][3];
{
size_t count = (is_tgt)?grid_data.
num_vertices:grid_data.num_cells;
int src_field_mask[3*3];
for (size_t i = 0; i < count; ++i) {
field_coordiantes_x[is_tgt][ids[i]],
field_coordiantes_y[is_tgt][ids[i]], field_coords[i]);
if (!is_tgt) src_field_mask[i] = field_mask[ids[i]];
}
grids[0], field_location[is_tgt], field_coords, count);
if (!is_tgt)
grids[0], field_location[is_tgt], src_field_mask, count, NULL);
}
for (int with_src_mask = 0; with_src_mask < 2; ++with_src_mask) {
.coordinates_idx = 0,
.masks_idx = with_src_mask?0:SIZE_MAX}};
size_t num_src_fields = sizeof(src_fields) / sizeof(src_fields[0]);
.coordinates_idx = 0,
.masks_idx = SIZE_MAX};
for (int partial_coverage = 0; partial_coverage < 2; ++partial_coverage) {
{
double src_field_data[3*3];
double * src_field = src_field_data;
double ** src_fields = &src_field;
double tgt_field_data[3*3] =
{-2.0,-2.0,-2.0,
-2.0,-2.0,-2.0,
-2.0,-2.0,-2.0};
double * tgt_field = tgt_field_data;
if (!is_tgt)
for (
size_t i = 0; i < grid_data.
num_cells; ++i)
src_field[i] = (
double)(grid_data.
cell_ids[i] + 1);
interpolation, &src_fields, &tgt_field);
if (is_tgt) {
{{{{0,1,3,4},{0,1,3,4},{1,2,4,5},
{0,1,3,4},{4},{1,2,4,5},
{3,4,6,7},{3,4,6,7},{4,5,7,8}},
{{0,1,3,4},{1,2,4,5},{1,2,4,5},
{3,4,6,7},{4},{4,5,7,8},
{3,4,6,7},{4,5,7,8},{4,5,7,8}}},
{{{1,3,4},{1,3,4},{1,2,4,5},
{1,3,4},{4},{1,2,4,5},
{3,4,6,7},{3,4,6,7},{4,5,7}},
{{1,3,4},{1,2,4,5},{1,2,4,5},
{3,4,6,7},{4},{4,5,7},
{3,4,6,7},{4,5,7},{4,5,7}}}};
int num_src_per_tgt[2][2][2][9] =
{{{{4,4,4, 4,1,4, 4,4,4},{4,4,4, 4,1,4, 4,4,4}},
{{0,0,4, 0,1,4, 4,4,0},{0,4,4, 4,1,0, 4,0,0}}},
{{{4,4,4, 4,1,4, 4,4,4},{4,4,4, 4,1,4, 4,4,4}},
{{3,3,4, 3,1,4, 4,4,3},{3,4,4, 4,1,3, 4,3,3}}}};
double ref_tgt_value[2] = {0.0, 0.0};
double tgt_coord[3];
field_coordiantes_x[1][tgt_id], field_coordiantes_y[1][tgt_id],
tgt_coord);
for (int j = 0; j < 2; ++j) {
double inv_distance_sum = 0.0;
int num_src =
num_src_per_tgt[partial_coverage][with_src_mask][j][tgt_id];
if (num_src > 0) {
for (int k = 0; k < num_src; ++k) {
yac_int src_id = tgt_to_src[with_src_mask][j][tgt_id][k];
double src_coord[3];
field_coordiantes_x[0][src_id],
field_coordiantes_y[0][src_id], src_coord);
ref_tgt_value[j] = (double)(src_id + 1);
inv_distance_sum = 1.0;
break;
}
double inv_distance =
inv_distance_sum += inv_distance;
ref_tgt_value[j] += (double)(src_id + 1) * inv_distance;
}
ref_tgt_value[j] /= inv_distance_sum;
} else {
ref_tgt_value[j] = -1.0;
}
}
if ((fabs(ref_tgt_value[0] - tgt_field[i]) > 1e-3) &&
(fabs(ref_tgt_value[1] - tgt_field[i]) > 1e-3))
PUT_ERR("wrong interpolation result");
}
}
}
}
}
}
MPI_Comm_free(&split_comm);
xt_finalize();
MPI_Finalize();
return TEST_EXIT_CODE;
}
static double compute_ref_tgt_value(
size_t tgt_corner, int * tgt_global_core_mask, int * tgt_global_field_mask,
int * src_global_core_mask, int * src_global_field_mask, int with_src_mask,
int partial_coverage) {
if (!tgt_global_core_mask[tgt_corner] ||
(with_tgt_mask && !tgt_global_field_mask[tgt_corner])) return -2.0;
int source_grid_x_idx = ((int)tgt_corner % 10) / 2;
int source_grid_y_idx = ((int)tgt_corner / 10) / 2;
int source_cell = source_grid_x_idx + source_grid_y_idx * 5;
if (!src_global_core_mask[source_cell]) return -1.0;
int is_right_corner = (int)tgt_corner & 1;
int is_upper_corner = ((int)tgt_corner / 10) & 1;
int source_corner =
source_grid_x_idx + is_right_corner +
6 * (source_grid_y_idx + is_upper_corner);
int source_cells[4];
source_cells[0] = (5 * (source_corner / 6) + source_corner % 6) - 5 - 1;
source_cells[1] = (5 * (source_corner / 6) + source_corner % 6) - 5;
source_cells[2] = (5 * (source_corner / 6) + source_corner % 6) - 1;
source_cells[3] = (5 * (source_corner / 6) + source_corner % 6);
if (source_corner <= 5) {
source_cells[0] = -1;
source_cells[1] = -1;
}
if (source_corner >= 24) {
source_cells[2] = -1;
source_cells[3] = -1;
}
if ((source_corner % 6) == 0) {
source_cells[0] = -1;
source_cells[2] = -1;
}
if (((source_corner + 1) % 6) == 0) {
source_cells[3] = -1;
source_cells[1] = -1;
}
for (int i = 0; i < 4; ++i)
if ((source_cells[i] >= 0) &&
(!src_global_core_mask[source_cells[i]]))
source_cells[i] = -1;
if (with_src_mask) {
for (int i = 0; i < 4; ++i) {
if ((source_cells[i] >= 0) &&
(!src_global_field_mask[source_cells[i]])) {
if (!partial_coverage) return -1.0;
source_cells[i] = -1;
}
}
}
double const ref_distances[2][2] =
{{sqrt(0.25*0.25 + 0.25*0.25),
sqrt(0.75*0.75 + 0.25*0.25)},
{sqrt(0.25*0.25 + 0.75*0.75),
sqrt(0.75*0.75 + 0.75*0.75)}};
double distances[4];
int num_src_cells = 0;
for (int i = 0, k = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j, ++k) {
if (source_cells[k] >= 0) {
distances[num_src_cells] =
ref_distances[i^is_upper_corner^1][j^is_right_corner^1];
source_cells[num_src_cells] = source_cells[k];
++num_src_cells;
}
}
}
if (num_src_cells == 0) return -1.0;
double tgt_value = 0.0;
default:
double weight = 1.0 / (double)num_src_cells;
for (int i = 0; i < num_src_cells; ++i)
tgt_value += (double)(source_cells[i] + 1) * weight;
break;
}
double inv_distance_sum = 0.0;
for (int i = 0; i < num_src_cells; ++i)
inv_distance_sum += 1.0 / distances[i];
for (int i = 0; i < num_src_cells; ++i)
tgt_value +=
(double)(source_cells[i] + 1) / (distances[i] * inv_distance_sum);
break;
}
};
return tgt_value;
}
struct yac_basic_grid * yac_basic_grid_new(char const *name, struct yac_basic_grid_data grid_data)
size_t yac_basic_grid_add_mask(struct yac_basic_grid *grid, enum yac_location location, int const *mask, size_t count, char const *mask_name)
size_t yac_basic_grid_add_coordinates(struct yac_basic_grid *grid, enum yac_location location, yac_coordinate_pointer coordinates, size_t count)
struct yac_basic_grid * yac_basic_grid_empty_new(char const *name)
void yac_basic_grid_delete(struct yac_basic_grid *grid)
enum yac_interp_ncc_weight_type weight_type
int main(int argc, char **argv)
void yac_dist_grid_pair_delete(struct yac_dist_grid_pair *grid_pair)
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)
static void LLtoXYZ_deg(double lon, double lat, double p_out[])
static double get_vector_angle(double const a[3], double const b[3])
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)
void yac_interp_method_delete(struct interp_method **method)
struct yac_interp_weights * yac_interp_method_do_search(struct interp_method **method, struct yac_interp_grid *interp_grid)
struct interp_method * yac_interp_method_fixed_new(double value)
struct interp_method * yac_interp_method_ncc_new(enum yac_interp_ncc_weight_type weight_type, int partial_coverage)
yac_interp_ncc_weight_type
@ YAC_INTERP_NCC_DIST
distance weighted average of n source points
@ YAC_INTERP_NCC_AVG
average of n source points
void yac_interp_weights_delete(struct yac_interp_weights *weights)
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)
yac_interp_weights_reorder_type
@ YAC_MAPPING_ON_TGT
weights will be applied at target processes
@ YAC_MAPPING_ON_SRC
weights will be appied at source processes
void yac_interpolation_execute(struct yac_interpolation *interp, double ***src_fields, double **tgt_field)
void yac_interpolation_delete(struct yac_interpolation *interp)
double const YAC_FRAC_MASK_NO_VALUE
enum yac_location location
struct yac_interp_field tgt_field
struct yac_dist_grid_pair * grid_pair
struct yac_interp_field src_fields[]
struct yac_basic_grid ** grids