A test for different area calculations.
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "test_common.h"
#include "tests.h"
#define YAC_EARTH_RADIUS (6371.2290)
static int compare_area(double a, double b);
static void test_area(
struct yac_grid_cell cell,
double ref_area,
double area;
puts ("----- calculating area -----\n");
{
generate_cell_deg((double[4]){0.5, 0.0, -0.5, 0.0},
(double[4]){0.0, 0.5, 0.0, -0.5}, gc_edges, 4);
printf ( "pole area of quad over Equator is %f sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
printf ( "huiliers area of quad over Equator is %f sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
}
{
generate_cell_deg((double[3]){0.5, 0.0, -0.5},
(double[3]){0.0, 0.5, 0.0}, gc_edges, 3);
printf ( "ICON area of triangle over Equator is %f sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
printf ( "pole area of triangle over Equator is %f sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
printf ( "huiliers area of triangle over Equator is %f sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
}
{
generate_cell_deg((double[4]){-0.5, 0.5, 0.5, -0.5},
(double[4]){-0.5, -0.5, 0.5, 0.5}, gc_edges, 4);
printf ( "pole area of quad over Equator is %f sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
printf ( "huiliers area of quad over Equator is %f sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
}
{
generate_cell_deg((double[3]){-0.5, 0.5, 0.5},
(double[3]){-0.5, -0.5, 0.5}, gc_edges, 3);
printf ( "ICON area of triangle over Equator is %f sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
printf ( "pole area of triangle over Equator is %f sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
printf ( "huiliers area of triangle over Equator is %f sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
}
{
generate_cell_deg((double[3]){150.0, 180.0, 180.0},
(double[3]){-60.0, -60.0, -90.0}, gc_edges, 3);
printf ( "ICON area of triangle over South Pole is %f sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
printf ( "pole area of triangle over South Pole is %f sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
printf ( "huiliers area of triangle over South Pole is %f sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
}
{
generate_cell_deg((double[4]){0.0, 90.0, -180.0, -90.0},
(double[4]){89.5, 89.5, 89.5, 89.5}, gc_edges, 4);
printf ( "pole area of quad over North Pole is %f sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
printf ( "huiliers area of quad over North Pole is %f sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
}
{
generate_cell_deg((double[4]){-180.0, -150.0, -150.0, -180.0},
(double[4]){60.0, 60.0, 90.0, 90.0}, gc_edges, 4);
printf ( "pole area of quad near North Pole is %f sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
printf ( "huiliers area of quad near North Pole is %f sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
}
{
generate_cell_deg((double[4]){-180.0, -150.0, -150.0, -180.0},
(double[4]){-90.0, -90.0, -60.0, -60.0}, gc_edges, 4);
printf ( "ICON area of quad near South Pole is %f sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
printf ( "pole area of quad near South Pole is %f sqr Km \n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
printf ( "huiliers area of quad near South Pole is %f sqr Km \n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
}
{
generate_cell_deg((double[5]){-180.0, -150.0, 0.0, -150.0, -180.0},
(double[5]){60.0, 60.0, 90.0, 90.0, 90.0}, gc_edges, 5);
printf ( "pole area of quin near North Pole is %f sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
printf ( "huiliers area of quin near North Pole is %f sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
}
{
generate_cell_deg((double[3]){-0.5, 0.5, -0.5},
(double[3]){-0.5, -0.5, -0.5}, gc_edges, 3);
printf ( "ICON area of triangle over Equator is %f sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
printf ( "pole area of triangle over Equator is %f sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
printf ( "huiliers area of triangle over Equator is %f sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
if ((area < 0.0) || (area > 1e-9))
PUT_ERR("ERROR in yac_huiliers_area for zero size triangle");
}
{
generate_cell_deg((double[4]){0, 1, 1, 0},
(double[4]){0, 0, 1, 1}, gc_edges, 4);
generate_cell_deg((double[3]){1, 1, 1},
(double[3]){0, 0, 1}, gc_edges, 3);
double dx[] = {1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1,
0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9};
for (size_t i = 0; i < sizeof(dx) / sizeof(dx[0]); ++i) {
generate_cell_deg((double[4]){0, 1.0-dx[i], 1, 0},
(double[4]){0, 0, 1, 1}, gc_edges, 4);
generate_cell_deg((double[3]){1.0-dx[i], 1, 1},
(double[3]){0, 0, 1}, gc_edges, 3);
if (compare_area(base_area, partial_area))
PUT_ERR("yac_huiliers_area computed wrong area\n");
printf ("accuracy check for (base_area - partial_area) %8.1e sqr km for dx %.1e\n",
base_area - partial_area, dx[i]);
}
}
{
generate_cell_deg(
gc_edges, 3);
if (area < 0)
PUT_ERR("pole_area computed wrong area\n");
printf ("pole accuracy check of very small area %.2e sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
if (area < 0)
PUT_ERR("huiliers_area computed wrong area\n");
printf ("huiliers accuracy check of very small area %.2e sqr km\n", area * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS );
}
{
double edge_length_deg = 1.0e-8;
printf ("\n accuracy check for a unit square\n");
printf (" edge length [m] | plain | simple | pole | huiliers\n");
printf (" ----------------+--------------------+--------------------+--------------------+--------------------\n");
for ( unsigned i = 1; i < 11; ++i ) {
double edge_length_m =
edge_length_deg *
YAC_RAD * YAC_EARTH_RADIUS * 1.0e3;
generate_cell_deg((double[4]){0, edge_length_deg, edge_length_deg, 0},
(double[4]){0, 0, edge_length_deg, edge_length_deg},
gc_edges, 4);
double t_area = edge_length_m * edge_length_m;
double s_area =
yac_pole_area(Quad) * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS * 1.0e6;
double h_area =
yac_huiliers_area(Quad) * YAC_EARTH_RADIUS * YAC_EARTH_RADIUS * 1.0e6;
if (0)
printf (" %.8e m|%.8e sqr m|%.8e sqr m|%.8e sqr m|%.8e sqr m\n",
edge_length_m, p_area, t_area, s_area, h_area);
edge_length_deg *= 10.0;
}
}
{
for (
int y = -90;
y <= 90;
y += 5) {
generate_cell_deg((double[2]){0.0, 20.0},
(
double[2]){(double)y, (
double)
y},
double ref_area;
{
double coordinates_x[3] = {0.0, 20.0, 0.0};
double coordinates_y[3] = {(double)y, (
double)
y, 0};
if ((y == -90) || (
y == 90) || (y == 0)) {
ref_area = 0.0;
} else {
if (y < 0) {
(1 - cos(M_PI_2 + coordinates_y[0]*
YAC_RAD));
coordinates_y[2] = -90.0;
} else {
(1 - cos(M_PI_2 - coordinates_y[0]*
YAC_RAD));
coordinates_y[2] = 90.0;
}
generate_cell_deg(coordinates_x, coordinates_y, gc_edges, 3);
}
}
}
}
{
double coordinates_x[2][4] = {{-0.5, 0.0, 0.0, -0.5},
{ 0.0, 0.5, 0.5, 0.0}};
double coordinates_y[2][4] = {{-0.5, -0.5, 0.0, 0.0},
{ 0.0, 0.0, 0.5, 0.5}};
for (int x = 0; x < 2; ++x) {
for (
int y = 0;
y < 2; ++
y) {
generate_cell_deg(coordinates_x[x], coordinates_y[y], lat_edges, 4);
}
}
}
{
generate_cell_deg((double[4]){-0.5, 0.5, 0.5, -0.5},
(double[4]){-0.5, -0.5, 0.5, 0.5}, lat_edges, 4);
}
{
double ref_area = sin(1.0 *
YAC_RAD);
for (int i = 0; i < 2; ++i) {
double coordinates_x[4] = {-0.5, 0.5, 0.5, -0.5};
double coordinates_y[2][4] = {{ 0.5, 0.5, 1.0, 1.0},
{-0.5,-0.5,-1.0, -1.0}};
generate_cell_deg(coordinates_x, coordinates_y[i], edge_types, 4);
}
}
{
double intersections[2][3];
intersections[0]))
return EXIT_FAILURE;
intersections[1]))
return EXIT_FAILURE;
double intersections_lon[2], intersections_lat[2];
XYZtoLL(intersections[0], &(intersections_lon[0]), &(intersections_lat[0]));
XYZtoLL(intersections[1], &(intersections_lon[1]), &(intersections_lat[1]));
generate_cell_deg(
(
double[]){20.0, intersections_lon[0]/
YAC_RAD,
(double[]){59.0, 60.0, 60.0},
if (compare_area(partial_area,
PUT_ERR("pole_area != huiliers_area for convex cell");
double ref_area = 2.0 * partial_area;
{generate_cell_deg(
(double[]){20.0,
-intersections_lon[0]/
YAC_RAD, -20.0},
(double[]){59.0, 60.0, 60.0, 60.0, 60.0, 59.0},
generate_cell_deg(
(double[]){20.0,
-20.0,
(double[]){59.0, 60.0, 60.0, 59.0, 60.0, 60.0},
for (int i = 0; i < 2; ++i) {
}
}
{
generate_cell_deg((double[]){-151.1489316535362661,
-151.7087167856307133,
-150.6174180645230933},
(double[]){ 10.6333746562670566,
11.5836995038666437,
11.6243355661022782}, gc_edges, 3),
generate_cell_deg((double[]){-152.8132296086113513,
-153.8786613849353557,
-151.6929566531634066},
(double[]){ 13.4512385235805798,
11.4650630285209161,
11.5568484830228009}, gc_edges, 3),
generate_cell_deg((double[]){-152.8132296086113513,
-151.6929566531634066,
-150.6265735326831532},
(double[]){ 13.4512385235805798,
11.5568484830228009,
13.5446528932721275}, gc_edges, 3),
generate_cell_deg((double[]){-153.8786613849353557,
-151.6929566531634066,
-152.7627337461449031},
(double[]){ 11.4650630285209161,
11.5568484830228009,
9.5827293336264265}, gc_edges, 3),
generate_cell_deg((double[]){-151.6929566531634066,
-152.7627337461449031,
-150.5840076321059371},
(double[]){ 11.5568484830228009,
9.5827293336264265,
9.6520960946755832}, gc_edges, 3),
generate_cell_deg((double[]){-151.6929566531634066,
-150.6265735326831532,
-149.5139953075027108},
(double[]){ 11.5568484830228009,
13.5446528932721275,
11.6284420125700851}, gc_edges, 3),
generate_cell_deg((double[]){-151.6929566531634066,
-149.5139953075027108,
-150.5840076321059371},
(double[]){ 11.5568484830228009,
11.6284420125700851,
9.6520960946755832}, gc_edges, 3)};
size_t num_cells =
sizeof(
cells) /
sizeof(cells[0]);
for (size_t i = 0; i < num_cells-1; ++i)
double area_tol = tgt_area * 1e-8;
double diff_area = tgt_area;
for (size_t i = 0; i < num_cells-1; ++i)
if (fabs(diff_area) > area_tol)
PUT_ERR("yac_huiliers_area is too inaccurate");
for (size_t i = 0; i < num_cells; ++i)
for (size_t i = 0; i < num_cells-1; ++i)
}
{
generate_cell_deg((double[3]){0.5, 0.5, -0.5},
(double[3]){0.0, 0.0, 0.0}, gc_edges, 3);
PUT_ERR("ERROR in yac_triangle_area");
Tri = generate_cell_deg((double[3]){0.5, -0.5, 0.5},
(double[3]){0.0, 0.0, 0.0}, gc_edges, 3);
PUT_ERR("ERROR in yac_triangle_area");
Tri = generate_cell_deg((double[3]){-0.5, 0.5, 0.5},
(double[3]){0.0, 0.0, 0.0}, gc_edges, 3);
PUT_ERR("ERROR in yac_triangle_area");
}
return TEST_EXIT_CODE;
}
static void test_area(
struct yac_grid_cell cell,
double ref_area,
double mem_dummy_[128][3];
.edge_type = edge_dummy,
for (int order = -1; order <= 1; order += 2) {
for (
int start = 0; start < (int)(cell.
num_corners); ++start) {
}
double area = area_func(test_cell);
if (compare_area(area, ref_area)) PUT_ERR("ERROR: wrong area\n");
}
}
}
static int compare_area(double a, double b) {
double diff = fabs(a - b);
if (diff <
tol)
return 0;
else return (a > b) - (a < b);
}
double yac_huiliers_area(struct yac_grid_cell cell)
Area calculation on a unit sphere taken from ESMF based on L'Huilier's Theorem.
double yac_pole_area(struct yac_grid_cell cell)
Calculate the area of a cell in a 3d plane on a unit sphere.
double yac_planar_3dcell_area(struct yac_grid_cell cell)
Area calculation on a unit sphere of a planar polygon in 3D.
double yac_triangle_area(struct yac_grid_cell cell)
Calculate the area of a triangle on a unit sphere.
Structs and interfaces for area calculations.
void yac_cell_clipping(size_t N, struct yac_grid_cell *source_cell, struct yac_grid_cell target_cell, struct yac_grid_cell *overlap_buffer)
cell clipping to get the cells describing the intersections
int main(int argc, char **argv)
static void XYZtoLL(double const p_in[], double *lon, double *lat)
void yac_init_grid_cell(struct yac_grid_cell *cell)
void yac_free_grid_cell(struct yac_grid_cell *cell)
@ YAC_GREAT_CIRCLE_EDGE
great circle
@ YAC_LAT_CIRCLE_EDGE
latitude circle
@ YAC_LON_CIRCLE_EDGE
longitude circle
enum yac_edge_type * edge_type
double(* coordinates_xyz)[3]