From 887a08557c23714aa9c3ac448d09156b9525f176 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 19 Mar 2020 12:01:53 +0000 Subject: [PATCH 001/145] Apply clang-format --- .../functionspace/detail/StructuredColumns_setup.cc | 6 +++--- src/tests/grid/test_grids.cc | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/atlas/functionspace/detail/StructuredColumns_setup.cc b/src/atlas/functionspace/detail/StructuredColumns_setup.cc index 84c5e3a6d..559c76e1e 100644 --- a/src/atlas/functionspace/detail/StructuredColumns_setup.cc +++ b/src/atlas/functionspace/detail/StructuredColumns_setup.cc @@ -274,8 +274,8 @@ void StructuredColumns::setup( const grid::Distribution& distribution, const eck }; auto compute_i_less_equal_x = [this, &eps]( const double& x, idx_t j ) -> idx_t { - const double dx = grid_->dx(j); - idx_t i = idx_t( std::floor( ( x + eps - grid_->xmin(j) ) / dx ) ); + const double dx = grid_->dx( j ); + idx_t i = idx_t( std::floor( ( x + eps - grid_->xmin( j ) ) / dx ) ); return i; }; @@ -345,7 +345,7 @@ void StructuredColumns::setup( const grid::Distribution& distribution, const eck ++i; } - double x = grid_->x( i, j ); + double x = grid_->x( i, j ); double x_next = grid_->x( i + 1, j ); double x_prev = grid_->x( i - 1, j ); diff --git a/src/tests/grid/test_grids.cc b/src/tests/grid/test_grids.cc index e1a211f69..ea37c76c4 100644 --- a/src/tests/grid/test_grids.cc +++ b/src/tests/grid/test_grids.cc @@ -403,11 +403,11 @@ CASE( "test_structured_from_config" ) { }() ); StructuredGrid g{config}; for ( idx_t j = 0; j < g.ny(); ++j ) { - EXPECT_EQ( g.nx(j) , 40 ); - EXPECT_EQ( g.x(0,j), 5. ); - EXPECT_EQ( g.x(g.nx(j),j), 365.); - EXPECT_EQ( g.dx(j), 9.); - EXPECT_EQ( g.xmin(j), 5.); + EXPECT_EQ( g.nx( j ), 40 ); + EXPECT_EQ( g.x( 0, j ), 5. ); + EXPECT_EQ( g.x( g.nx( j ), j ), 365. ); + EXPECT_EQ( g.dx( j ), 9. ); + EXPECT_EQ( g.xmin( j ), 5. ); } EXPECT( not g.domain().global() ); } From bbb6d1a1f5b4fe5afc590cb2d9a7226730fb1978 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 19 Mar 2020 12:00:04 +0000 Subject: [PATCH 002/145] Allow to override version check in apply-clang-format --- tools/apply-clang-format.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/apply-clang-format.sh b/tools/apply-clang-format.sh index f3dfd6a7c..b541c3284 100755 --- a/tools/apply-clang-format.sh +++ b/tools/apply-clang-format.sh @@ -21,7 +21,11 @@ if ! [[ $(clang-format --version) =~ ${_REQUIRED_CLANG_VERSION} ]]; then echo "Error: Require clang-format version: ${_REQUIRED_CLANG_VERSION}" echo " > $(which clang-format) --version" echo " $(clang-format --version)" - exit 1 + if [[ $1 =~ --no-version-check ]]; then + echo "--no-version-check --> Continue anyway" + else + exit 1 + fi fi SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" From b86248fa2224a3dc92dd2f2eb0b16d3bc511368d Mon Sep 17 00:00:00 2001 From: Benjamin Menetrier Date: Fri, 15 Nov 2019 10:38:24 +0100 Subject: [PATCH 003/145] Add createField features and Fortran interfaces for PointCloud function space --- src/atlas/CMakeLists.txt | 2 + src/atlas/functionspace/PointCloud.cc | 34 ++++- src/atlas/functionspace/PointCloud.h | 2 + .../detail/PointCloudInterface.cc | 47 +++++++ .../detail/PointCloudInterface.h | 52 ++++++++ src/atlas_f/CMakeLists.txt | 4 + src/atlas_f/atlas_module.F90 | 2 + .../atlas_functionspace_PointCloud_module.F90 | 118 ++++++++++++++++++ 8 files changed, 257 insertions(+), 4 deletions(-) create mode 100644 src/atlas/functionspace/detail/PointCloudInterface.cc create mode 100644 src/atlas/functionspace/detail/PointCloudInterface.h create mode 100644 src/atlas_f/functionspace/atlas_functionspace_PointCloud_module.F90 diff --git a/src/atlas/CMakeLists.txt b/src/atlas/CMakeLists.txt index 54366565a..5af8a978b 100644 --- a/src/atlas/CMakeLists.txt +++ b/src/atlas/CMakeLists.txt @@ -395,6 +395,8 @@ functionspace/detail/StructuredColumnsInterface.h functionspace/detail/StructuredColumnsInterface.cc functionspace/detail/StructuredColumns_setup.cc functionspace/detail/StructuredColumns_create_remote_index.cc +functionspace/detail/PointCloudInterface.h +functionspace/detail/PointCloudInterface.cc ) list( APPEND atlas_numerics_srcs diff --git a/src/atlas/functionspace/PointCloud.cc b/src/atlas/functionspace/PointCloud.cc index c214a2254..0aac3d7a3 100644 --- a/src/atlas/functionspace/PointCloud.cc +++ b/src/atlas/functionspace/PointCloud.cc @@ -9,13 +9,19 @@ */ -#include "atlas/functionspace/PointCloud.h" #include "atlas/array.h" +#include "atlas/field/Field.h" +#include "atlas/functionspace/PointCloud.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Iterator.h" #include "atlas/option/Options.h" #include "atlas/runtime/Exception.h" +#if ATLAS_HAVE_FORTRAN +#define REMOTE_IDX_BASE 1 +#else +#define REMOTE_IDX_BASE 0 +#endif namespace atlas { namespace functionspace { @@ -69,12 +75,32 @@ const Field& PointCloud::ghost() const { return ghost_; } -Field PointCloud::createField( const eckit::Configuration& ) const { - ATLAS_NOTIMPLEMENTED; +Field PointCloud::createField( const eckit::Configuration& config ) const { + array::DataType::kind_t kind; + if ( !config.get( "datatype", kind ) ) { + throw_Exception( "datatype missing", Here() ); + } + auto datatype = array::DataType( kind ); + + std::string name; + config.get( "name", name ); + idx_t levels = levels_; + config.get( "levels", levels ); + Field field; + if ( levels ) { + field = Field( name, datatype, array::make_shape( size(), levels ) ); + field.set_levels( levels ); + } + else { + field = Field( name, datatype, array::make_shape( size() ) ); + } + field.set_functionspace( this ); + return field; } Field PointCloud::createField( const Field& other, const eckit::Configuration& config ) const { - return createField( option::datatype( other.datatype() ) | config ); + return createField( option::datatype( other.datatype() ) | option::levels( other.levels() ) | + option::variables( other.variables() ) | config ); } std::string PointCloud::distribution() const { diff --git a/src/atlas/functionspace/PointCloud.h b/src/atlas/functionspace/PointCloud.h index 26a43e804..fca6de268 100644 --- a/src/atlas/functionspace/PointCloud.h +++ b/src/atlas/functionspace/PointCloud.h @@ -14,6 +14,7 @@ #include "atlas/field/Field.h" #include "atlas/functionspace/FunctionSpace.h" #include "atlas/functionspace/detail/FunctionSpaceImpl.h" +#include "atlas/util/Config.h" #include "atlas/util/Point.h" namespace atlas { @@ -145,6 +146,7 @@ class PointCloud : public functionspace::FunctionSpaceImpl { Field lonlat_; Field vertical_; mutable Field ghost_; + idx_t levels_{0}; }; //------------------------------------------------------------------------------------------------------ diff --git a/src/atlas/functionspace/detail/PointCloudInterface.cc b/src/atlas/functionspace/detail/PointCloudInterface.cc new file mode 100644 index 000000000..71ab1eb37 --- /dev/null +++ b/src/atlas/functionspace/detail/PointCloudInterface.cc @@ -0,0 +1,47 @@ +/* + * (C) Copyright 200 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include + +#include "PointCloudInterface.h" + +#include "atlas/field/FieldSet.h" +#include "atlas/field/detail/FieldImpl.h" +#include "atlas/grid/Grid.h" +#include "atlas/runtime/Exception.h" + +namespace atlas { +namespace functionspace { + +// ---------------------------------------------------------------------------- +// Fortran interfaces +// ---------------------------------------------------------------------------- + +extern "C" { + +const detail::PointCloud* atlas__functionspace__PointCloud__new__grid( + const Grid::Implementation* grid ) { + return new detail::PointCloud( Grid( grid ) ); +} + +const field::FieldImpl* atlas__fs__PointCloud__lonlat( const detail::PointCloud* This ) { + return This->lonlat().get(); +} + +idx_t atlas__fs__PointCloud__size( const detail::PointCloud* This ) { + return This->size(); +} + +} + +// ---------------------------------------------------------------------------- + +} // namespace functionspace +} // namespace atlas diff --git a/src/atlas/functionspace/detail/PointCloudInterface.h b/src/atlas/functionspace/detail/PointCloudInterface.h new file mode 100644 index 000000000..434b981b3 --- /dev/null +++ b/src/atlas/functionspace/detail/PointCloudInterface.h @@ -0,0 +1,52 @@ +/* + * (C) Copyright 200 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ +#pragma once + +#include "atlas/functionspace/PointCloud.h" + + +namespace atlas { +namespace field { +class FieldSetImpl; +} +} // namespace atlas + +namespace atlas { +namespace grid { +namespace detail { +namespace grid { +class Grid; +} // namespace grid +} // namespace detail +} // namespace grid +using GridImpl = grid::detail::grid::Grid; +} // namespace atlas + +namespace atlas { +namespace functionspace { + +// ------------------------------------------------------------------- +// C wrapper interfaces to C++ routines +extern "C" { + +const detail::PointCloud* atlas__functionspace__PointCloud__new__grid( + const GridImpl* grid ); + +void atlas__functionspace__PointCloud__delete( detail::PointCloud* This ); +field::FieldImpl* atlas__fs__PointCloud__create_field( const detail::PointCloud* This, + const eckit::Configuration* options ); + +idx_t atlas__fs__PointCloud__size( const detail::PointCloud* This ); + +const field::FieldImpl* atlas__fs__PointCloud__lonlat( const detail::PointCloud* This ); +} + +} // namespace functionspace +} // namespace atlas diff --git a/src/atlas_f/CMakeLists.txt b/src/atlas_f/CMakeLists.txt index fdabd3bd7..3f6ca4dd3 100644 --- a/src/atlas_f/CMakeLists.txt +++ b/src/atlas_f/CMakeLists.txt @@ -109,6 +109,9 @@ generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/functionspace/detail/NodeCol generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/functionspace/EdgeColumns.h MODULE atlas_functionspace_EdgeColumns_c_binding OUTPUT functionspace_EdgeColumns_c_binding.f90) +generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/functionspace/detail/PointCloudInterface.h + MODULE atlas_functionspace_PointCloud_c_binding + OUTPUT functionspace_PointCloud_c_binding.f90) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/numerics/Nabla.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/numerics/Nabla.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/numerics/Method.h ) @@ -150,6 +153,7 @@ ecbuild_add_library( TARGET atlas_f functionspace/atlas_functionspace_NodeColumns_module.fypp functionspace/atlas_functionspace_StructuredColumns_module.F90 functionspace/atlas_functionspace_Spectral_module.F90 + functionspace/atlas_functionspace_PointCloud_module.F90 field/atlas_FieldSet_module.F90 field/atlas_State_module.F90 field/atlas_Field_module.fypp diff --git a/src/atlas_f/atlas_module.F90 b/src/atlas_f/atlas_module.F90 index 75e1d02aa..482fc1f64 100644 --- a/src/atlas_f/atlas_module.F90 +++ b/src/atlas_f/atlas_module.F90 @@ -80,6 +80,8 @@ module atlas_module & atlas_functionspace_EdgeColumns use atlas_functionspace_NodeColumns_module, only: & & atlas_functionspace_NodeColumns +use atlas_functionspace_PointCloud_module, only: & + & atlas_functionspace_PointCloud use atlas_functionspace_StructuredColumns_module, only: & & atlas_functionspace_StructuredColumns use atlas_functionspace_Spectral_module, only: & diff --git a/src/atlas_f/functionspace/atlas_functionspace_PointCloud_module.F90 b/src/atlas_f/functionspace/atlas_functionspace_PointCloud_module.F90 new file mode 100644 index 000000000..dc881be27 --- /dev/null +++ b/src/atlas_f/functionspace/atlas_functionspace_PointCloud_module.F90 @@ -0,0 +1,118 @@ +! (C) Copyright 2020 ECMWF. +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +! In applying this licence, ECMWF does not waive the privileges and immunities +! granted to it by virtue of its status as an intergovernmental organisation nor +! does it submit to any jurisdiction. + +#include "atlas/atlas_f.h" + +module atlas_functionspace_PointCloud_module + +use fckit_c_interop_module, only : c_str, c_ptr_to_string, c_ptr_free +use atlas_functionspace_module, only : atlas_FunctionSpace +use atlas_Grid_module, only: atlas_Grid +use atlas_Field_module, only: atlas_Field +use atlas_kinds_module, only: ATLAS_KIND_GIDX + +implicit none + +private :: c_str, c_ptr_to_string, c_ptr_free +private :: atlas_FunctionSpace +private :: atlas_Field +private :: atlas_Grid +private :: ATLAS_KIND_GIDX + +public :: atlas_functionspace_PointCloud + +private + +!------------------------------------------------------------------------------ +TYPE, extends(atlas_FunctionSpace) :: atlas_functionspace_PointCloud + +! Purpose : +! ------- +! *atlas_functionspace_PointCloud* : Interpretes point cloud fields + +! Methods : +! ------- + +! Author : +! ------ +! March-2020 Benjamin Menetrier *IRIT-JCSDA* + +!------------------------------------------------------------------------------ +contains + + procedure, public :: size + procedure, public :: lonlat + +END TYPE atlas_functionspace_PointCloud + +interface atlas_functionspace_PointCloud + module procedure ctor_cptr + module procedure ctor_grid +end interface + +!------------------------------------------------------------------------------ + +!======================================================== +contains +!======================================================== + +!------------------------------------------------------------------------------ + +function ctor_cptr(cptr) result(this) + use, intrinsic :: iso_c_binding, only : c_ptr + type(atlas_functionspace_PointCloud) :: this + type(c_ptr), intent(in) :: cptr + call this%reset_c_ptr( cptr ) + call this%return() +end function + +!------------------------------------------------------------------------------ + +function ctor_grid(grid) result(this) + use atlas_functionspace_PointCloud_c_binding + type(atlas_functionspace_PointCloud) :: this + class(atlas_Grid), intent(in) :: grid + call this%reset_c_ptr( atlas__functionspace__PointCloud__new__grid( grid%CPTR_PGIBUG_A ) ) + call this%return() +end function + +!------------------------------------------------------------------------------ + +function size(this) + use atlas_functionspace_PointCloud_c_binding + integer :: size + class(atlas_functionspace_PointCloud), intent(in) :: this + size = atlas__fs__PointCloud__size(this%CPTR_PGIBUG_A) +end function + +!------------------------------------------------------------------------------ + +function lonlat(this) result(field) + use atlas_functionspace_PointCloud_c_binding + type(atlas_Field) :: field + class(atlas_functionspace_PointCloud), intent(in) :: this + field = atlas_Field( atlas__fs__PointCloud__lonlat(this%CPTR_PGIBUG_A) ) + call field%return() +end function + +!------------------------------------------------------------------------------- + +ATLAS_FINAL subroutine atlas_functionspace_PointCloud__final_auto(this) + type(atlas_functionspace_PointCloud), intent(inout) :: this +#if FCKIT_FINAL_DEBUGGING + write(0,*) "atlas_functionspace_PointCloud__final_auto" +#endif +#if FCKIT_FINAL_NOT_PROPAGATING + call this%final() +#endif + FCKIT_SUPPRESS_UNUSED( this ) +end subroutine + +!------------------------------------------------------------------------------ + +end module atlas_functionspace_PointCloud_module From 525a71ef20e288d599cd53dedea8afab0e5cb3ae Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 18 Mar 2020 22:03:39 +0000 Subject: [PATCH 004/145] Add test for PointCloud::createField --- src/tests/functionspace/test_pointcloud.cc | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/tests/functionspace/test_pointcloud.cc b/src/tests/functionspace/test_pointcloud.cc index c01fc6d99..874fcd98f 100644 --- a/src/tests/functionspace/test_pointcloud.cc +++ b/src/tests/functionspace/test_pointcloud.cc @@ -10,6 +10,7 @@ #include "atlas/array.h" #include "atlas/functionspace/PointCloud.h" +#include "atlas/option.h" #include "tests/AtlasTestEnvironment.h" @@ -34,6 +35,36 @@ CASE( "test_functionspace_PointCloud" ) { Log::info() << std::endl; } +//----------------------------------------------------------------------------- + +CASE( "test_createField" ) { + FunctionSpace p1; + { + Field points( "points", array::make_datatype(), array::make_shape( 10, 2 ) ); + auto xy = array::make_view( points ); + xy.assign( {00., 0., 10., 0., 20., 0., 30., 0., 40., 0., 50., 0., 60., 0., 70., 0., 80., 0., 90., 0.} ); + p1 = functionspace::PointCloud( points ); + } + + Field f1 = p1.createField( option::name( "f1" ) | option::levels( 3 ) ); + EXPECT_EQ( f1.levels(), 3 ); + EXPECT_EQ( f1.shape( 0 ), 10 ); + EXPECT_EQ( f1.shape( 1 ), 3 ); + + FunctionSpace p2; + { + Field points( "points", array::make_datatype(), array::make_shape( 4, 2 ) ); + auto xy = array::make_view( points ); + xy.assign( {20., 0., 40., 0., 70., 0., 90., 0.} ); + p2 = functionspace::PointCloud( points ); + } + Field f2 = p2.createField( f1, util::NoConfig() ); + EXPECT_EQ( f2.levels(), 3 ); + EXPECT_EQ( f2.shape( 0 ), 4 ); + EXPECT_EQ( f2.shape( 1 ), 3 ); +} + + //----------------------------------------------------------------------------- } // namespace test From a0e4df39d7966b1500510ea4c14b5457da2d8d41 Mon Sep 17 00:00:00 2001 From: Benjamin Menetrier Date: Thu, 5 Mar 2020 12:32:12 +0000 Subject: [PATCH 005/145] Lonlat constructor interface for PointCloud --- src/atlas/functionspace/PointCloud.cc | 2 +- src/atlas/functionspace/detail/PointCloudInterface.cc | 8 +++++--- src/atlas/functionspace/detail/PointCloudInterface.h | 5 +++-- .../atlas_functionspace_PointCloud_module.F90 | 11 +++++++++++ 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/atlas/functionspace/PointCloud.cc b/src/atlas/functionspace/PointCloud.cc index 0aac3d7a3..30c65ad2c 100644 --- a/src/atlas/functionspace/PointCloud.cc +++ b/src/atlas/functionspace/PointCloud.cc @@ -9,9 +9,9 @@ */ +#include "atlas/functionspace/PointCloud.h" #include "atlas/array.h" #include "atlas/field/Field.h" -#include "atlas/functionspace/PointCloud.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Iterator.h" #include "atlas/option/Options.h" diff --git a/src/atlas/functionspace/detail/PointCloudInterface.cc b/src/atlas/functionspace/detail/PointCloudInterface.cc index 71ab1eb37..318817b3d 100644 --- a/src/atlas/functionspace/detail/PointCloudInterface.cc +++ b/src/atlas/functionspace/detail/PointCloudInterface.cc @@ -26,8 +26,11 @@ namespace functionspace { extern "C" { -const detail::PointCloud* atlas__functionspace__PointCloud__new__grid( - const Grid::Implementation* grid ) { +const detail::PointCloud* atlas__functionspace__PointCloud__new__lonlat( const Field::Implementation* lonlat ) { + return new detail::PointCloud( Field( lonlat ) ); +} + +const detail::PointCloud* atlas__functionspace__PointCloud__new__grid( const Grid::Implementation* grid ) { return new detail::PointCloud( Grid( grid ) ); } @@ -38,7 +41,6 @@ const field::FieldImpl* atlas__fs__PointCloud__lonlat( const detail::PointCloud* idx_t atlas__fs__PointCloud__size( const detail::PointCloud* This ) { return This->size(); } - } // ---------------------------------------------------------------------------- diff --git a/src/atlas/functionspace/detail/PointCloudInterface.h b/src/atlas/functionspace/detail/PointCloudInterface.h index 434b981b3..caaaeb0c5 100644 --- a/src/atlas/functionspace/detail/PointCloudInterface.h +++ b/src/atlas/functionspace/detail/PointCloudInterface.h @@ -36,8 +36,9 @@ namespace functionspace { // C wrapper interfaces to C++ routines extern "C" { -const detail::PointCloud* atlas__functionspace__PointCloud__new__grid( - const GridImpl* grid ); +const detail::PointCloud* atlas__functionspace__PointCloud__new__lonlat( const field::FieldImpl* lonlat ); + +const detail::PointCloud* atlas__functionspace__PointCloud__new__grid( const GridImpl* grid ); void atlas__functionspace__PointCloud__delete( detail::PointCloud* This ); field::FieldImpl* atlas__fs__PointCloud__create_field( const detail::PointCloud* This, diff --git a/src/atlas_f/functionspace/atlas_functionspace_PointCloud_module.F90 b/src/atlas_f/functionspace/atlas_functionspace_PointCloud_module.F90 index dc881be27..17da5bef6 100644 --- a/src/atlas_f/functionspace/atlas_functionspace_PointCloud_module.F90 +++ b/src/atlas_f/functionspace/atlas_functionspace_PointCloud_module.F90 @@ -52,6 +52,7 @@ module atlas_functionspace_PointCloud_module interface atlas_functionspace_PointCloud module procedure ctor_cptr + module procedure ctor_lonlat module procedure ctor_grid end interface @@ -73,6 +74,16 @@ function ctor_cptr(cptr) result(this) !------------------------------------------------------------------------------ +function ctor_lonlat(lonlat) result(this) + use atlas_functionspace_PointCloud_c_binding + type(atlas_functionspace_PointCloud) :: this + class(atlas_Field), intent(in) :: lonlat + call this%reset_c_ptr( atlas__functionspace__PointCloud__new__lonlat( lonlat%CPTR_PGIBUG_A ) ) + call this%return() +end function + +!------------------------------------------------------------------------------ + function ctor_grid(grid) result(this) use atlas_functionspace_PointCloud_c_binding type(atlas_functionspace_PointCloud) :: this From aefbf6077444ce0aa7b7c9dbc66da117a399ba03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yannick=20Tr=C3=A9molet?= Date: Wed, 4 Mar 2020 10:55:07 +0000 Subject: [PATCH 006/145] Hooks for adding MPI into interpolation --- src/atlas/interpolation/method/Method.cc | 47 +++++++++++++++---- src/atlas/interpolation/method/Method.h | 20 +++++--- .../interpolation/method/fe/FiniteElement.cc | 8 ++-- .../interpolation/method/fe/FiniteElement.h | 9 ++-- .../method/knn/KNearestNeighbours.cc | 8 ++-- .../method/knn/KNearestNeighbours.h | 6 +-- .../method/knn/NearestNeighbour.cc | 8 ++-- .../method/knn/NearestNeighbour.h | 12 ++--- .../structured/StructuredInterpolation2D.h | 24 +++++----- .../structured/StructuredInterpolation2D.tcc | 26 +++++----- .../structured/StructuredInterpolation3D.h | 25 +++++----- .../structured/StructuredInterpolation3D.tcc | 26 +++++----- 12 files changed, 128 insertions(+), 91 deletions(-) diff --git a/src/atlas/interpolation/method/Method.cc b/src/atlas/interpolation/method/Method.cc index 40affe832..ebf97a899 100644 --- a/src/atlas/interpolation/method/Method.cc +++ b/src/atlas/interpolation/method/Method.cc @@ -146,37 +146,66 @@ void Method::interpolate_field_rank3( const Field& src, Field& tgt ) const { } } - Method::Method( const Method::Config& config ) { std::string spmv = ""; config.get( "spmv", spmv ); use_eckit_linalg_spmv_ = ( spmv == "eckit" ); } -void Method::setup( const FunctionSpace& /*source*/, const Field& /*target*/ ) { +void Method::setup( const FunctionSpace& source, const FunctionSpace& target ) { + ATLAS_TRACE( "atlas::interpolation::method::Method::setup(FunctionSpace, FunctionSpace)" ); + this->do_setup(source, target); +} + +void Method::setup( const Grid& source, const Grid& target ) { + ATLAS_TRACE( "atlas::interpolation::method::Method::setup(Grid, Grid)" ); + this->do_setup(source, target); +} + +void Method::setup( const FunctionSpace& source, const Field& target ) { + ATLAS_TRACE( "atlas::interpolation::method::Method::setup(FunctionSpace, Field)" ); + this->do_setup(source, target); +} + +void Method::setup( const FunctionSpace& source, const FieldSet& target ) { + ATLAS_TRACE( "atlas::interpolation::method::Method::setup(FunctionSpace, FieldSet)" ); + this->do_setup(source, target); +} + +void Method::execute( const FieldSet& source, FieldSet& target ) const { + ATLAS_TRACE( "atlas::interpolation::method::Method::execute(FieldSet, FieldSet)" ); + this->do_execute(source, target); +} + +void Method::execute( const Field& source, Field& target ) const { + ATLAS_TRACE( "atlas::interpolation::method::Method::execute(Field, Field)" ); + this->do_execute(source, target); +} + +void Method::do_setup( const FunctionSpace& /*source*/, const Field& /*target*/ ) { ATLAS_NOTIMPLEMENTED; } -void Method::setup( const FunctionSpace& /*source*/, const FieldSet& /*target*/ ) { +void Method::do_setup( const FunctionSpace& /*source*/, const FieldSet& /*target*/ ) { ATLAS_NOTIMPLEMENTED; } -void Method::execute( const FieldSet& fieldsSource, FieldSet& fieldsTarget ) const { - ATLAS_TRACE( "atlas::interpolation::method::Method::execute()" ); +void Method::do_execute( const FieldSet& fieldsSource, FieldSet& fieldsTarget ) const { + ATLAS_TRACE( "atlas::interpolation::method::Method::do_execute()" ); const idx_t N = fieldsSource.size(); ATLAS_ASSERT( N == fieldsTarget.size() ); for ( idx_t i = 0; i < fieldsSource.size(); ++i ) { - Log::debug() << "Method::execute() on field " << ( i + 1 ) << '/' << N << "..." << std::endl; - Method::execute( fieldsSource[i], fieldsTarget[i] ); + Log::debug() << "Method::do_execute() on field " << ( i + 1 ) << '/' << N << "..." << std::endl; + Method::do_execute( fieldsSource[i], fieldsTarget[i] ); } } -void Method::execute( const Field& src, Field& tgt ) const { +void Method::do_execute( const Field& src, Field& tgt ) const { haloExchange( src ); - ATLAS_TRACE( "atlas::interpolation::method::Method::execute()" ); + ATLAS_TRACE( "atlas::interpolation::method::Method::do_execute()" ); if ( src.datatype().kind() == array::DataType::KIND_REAL64 ) { interpolate_field( src, tgt ); diff --git a/src/atlas/interpolation/method/Method.h b/src/atlas/interpolation/method/Method.h index 65d430d8b..76125f333 100644 --- a/src/atlas/interpolation/method/Method.h +++ b/src/atlas/interpolation/method/Method.h @@ -40,13 +40,13 @@ class Method : public util::Object { * @param source functionspace containing source elements * @param target functionspace containing target points */ - virtual void setup( const FunctionSpace& source, const FunctionSpace& target ) = 0; - virtual void setup( const Grid& source, const Grid& target ) = 0; - virtual void setup( const FunctionSpace& source, const Field& target ); - virtual void setup( const FunctionSpace& source, const FieldSet& target ); + void setup( const FunctionSpace& source, const FunctionSpace& target ); + void setup( const Grid& source, const Grid& target ); + void setup( const FunctionSpace& source, const Field& target ); + void setup( const FunctionSpace& source, const FieldSet& target ); - virtual void execute( const FieldSet& source, FieldSet& target ) const; - virtual void execute( const Field& source, Field& target ) const; + void execute( const FieldSet& source, FieldSet& target ) const; + void execute( const Field& source, Field& target ) const; virtual void print( std::ostream& ) const = 0; @@ -54,6 +54,9 @@ class Method : public util::Object { virtual const FunctionSpace& target() const = 0; protected: + virtual void do_execute( const FieldSet& source, FieldSet& target ) const; + virtual void do_execute( const Field& source, Field& target ) const; + using Triplet = eckit::linalg::Triplet; using Triplets = std::vector; using Matrix = eckit::linalg::SparseMatrix; @@ -74,6 +77,11 @@ class Method : public util::Object { bool use_eckit_linalg_spmv_; private: + virtual void do_setup( const FunctionSpace& source, const FunctionSpace& target ) = 0; + virtual void do_setup( const Grid& source, const Grid& target ) = 0; + virtual void do_setup( const FunctionSpace& source, const Field& target ); + virtual void do_setup( const FunctionSpace& source, const FieldSet& target ); + template void interpolate_field( const Field& src, Field& tgt ) const; diff --git a/src/atlas/interpolation/method/fe/FiniteElement.cc b/src/atlas/interpolation/method/fe/FiniteElement.cc index 13e34dfdd..d2d28639e 100644 --- a/src/atlas/interpolation/method/fe/FiniteElement.cc +++ b/src/atlas/interpolation/method/fe/FiniteElement.cc @@ -55,7 +55,7 @@ static const double parametricEpsilon = 1e-15; } // namespace -void FiniteElement::setup( const Grid& source, const Grid& target ) { +void FiniteElement::do_setup( const Grid& source, const Grid& target ) { if ( mpi::size() > 1 ) { ATLAS_NOTIMPLEMENTED; } @@ -70,11 +70,11 @@ void FiniteElement::setup( const Grid& source, const Grid& target ) { return functionspace::NodeColumns( mesh ); }; - setup( functionspace( source ), functionspace( target ) ); + do_setup( functionspace( source ), functionspace( target ) ); } -void FiniteElement::setup( const FunctionSpace& source, const FunctionSpace& target ) { - ATLAS_TRACE( "atlas::interpolation::method::FiniteElement::setup()" ); +void FiniteElement::do_setup( const FunctionSpace& source, const FunctionSpace& target ) { + ATLAS_TRACE( "atlas::interpolation::method::FiniteElement::do_setup()" ); source_ = source; target_ = target; diff --git a/src/atlas/interpolation/method/fe/FiniteElement.h b/src/atlas/interpolation/method/fe/FiniteElement.h index 347da6db4..72e18c82a 100644 --- a/src/atlas/interpolation/method/fe/FiniteElement.h +++ b/src/atlas/interpolation/method/fe/FiniteElement.h @@ -32,10 +32,6 @@ class FiniteElement : public Method { virtual ~FiniteElement() override {} - virtual void setup( const FunctionSpace& source, const FunctionSpace& target ) override; - - virtual void setup( const Grid& source, const Grid& target ) override; - virtual void print( std::ostream& ) const override; protected: @@ -63,6 +59,11 @@ class FiniteElement : public Method { virtual const FunctionSpace& source() const override { return source_; } virtual const FunctionSpace& target() const override { return target_; } +private: + virtual void do_setup( const FunctionSpace& source, const FunctionSpace& target ) override; + + virtual void do_setup( const Grid& source, const Grid& target ) override; + protected: mesh::MultiBlockConnectivity* connectivity_; std::unique_ptr> icoords_; diff --git a/src/atlas/interpolation/method/knn/KNearestNeighbours.cc b/src/atlas/interpolation/method/knn/KNearestNeighbours.cc index 896a507fc..781d21e4c 100644 --- a/src/atlas/interpolation/method/knn/KNearestNeighbours.cc +++ b/src/atlas/interpolation/method/knn/KNearestNeighbours.cc @@ -42,7 +42,7 @@ KNearestNeighbours::KNearestNeighbours( const Method::Config& config ) : KNeares ATLAS_ASSERT( k_ ); } -void KNearestNeighbours::setup( const Grid& source, const Grid& target ) { +void KNearestNeighbours::do_setup( const Grid& source, const Grid& target ) { if ( mpi::size() > 1 ) { ATLAS_NOTIMPLEMENTED; } @@ -57,10 +57,10 @@ void KNearestNeighbours::setup( const Grid& source, const Grid& target ) { return functionspace::NodeColumns( mesh ); }; - setup( functionspace( source ), functionspace( target ) ); + do_setup( functionspace( source ), functionspace( target ) ); } -void KNearestNeighbours::setup( const FunctionSpace& source, const FunctionSpace& target ) { +void KNearestNeighbours::do_setup( const FunctionSpace& source, const FunctionSpace& target ) { source_ = source; target_ = target; functionspace::NodeColumns src = source; @@ -86,7 +86,7 @@ void KNearestNeighbours::setup( const FunctionSpace& source, const FunctionSpace std::vector weights_triplets; weights_triplets.reserve( out_npts * k_ ); { - Trace timer( Here(), "atlas::interpolation::method::NearestNeighbour::setup()" ); + Trace timer( Here(), "atlas::interpolation::method::NearestNeighbour::do_setup()" ); std::vector weights; diff --git a/src/atlas/interpolation/method/knn/KNearestNeighbours.h b/src/atlas/interpolation/method/knn/KNearestNeighbours.h index 4e8c8919f..da0f7dd64 100644 --- a/src/atlas/interpolation/method/knn/KNearestNeighbours.h +++ b/src/atlas/interpolation/method/knn/KNearestNeighbours.h @@ -29,16 +29,16 @@ class KNearestNeighbours : public KNearestNeighboursBase { * @param source functionspace containing source elements * @param target functionspace containing target points */ - virtual void setup( const FunctionSpace& source, const FunctionSpace& target ) override; virtual void print( std::ostream& ) const override {} - virtual void setup( const Grid& source, const Grid& target ) override; - virtual const FunctionSpace& source() const override { return source_; } virtual const FunctionSpace& target() const override { return target_; } private: + virtual void do_setup( const FunctionSpace& source, const FunctionSpace& target ) override; + virtual void do_setup( const Grid& source, const Grid& target ) override; + FunctionSpace source_; FunctionSpace target_; diff --git a/src/atlas/interpolation/method/knn/NearestNeighbour.cc b/src/atlas/interpolation/method/knn/NearestNeighbour.cc index d7eebf11f..95fed2591 100644 --- a/src/atlas/interpolation/method/knn/NearestNeighbour.cc +++ b/src/atlas/interpolation/method/knn/NearestNeighbour.cc @@ -33,7 +33,7 @@ MethodBuilder __builder( "nearest-neighbour" ); } // namespace -void NearestNeighbour::setup( const Grid& source, const Grid& target ) { +void NearestNeighbour::do_setup( const Grid& source, const Grid& target ) { if ( mpi::size() > 1 ) { ATLAS_NOTIMPLEMENTED; } @@ -48,10 +48,10 @@ void NearestNeighbour::setup( const Grid& source, const Grid& target ) { return functionspace::NodeColumns( mesh ); }; - setup( functionspace( source ), functionspace( target ) ); + do_setup( functionspace( source ), functionspace( target ) ); } -void NearestNeighbour::setup( const FunctionSpace& source, const FunctionSpace& target ) { +void NearestNeighbour::do_setup( const FunctionSpace& source, const FunctionSpace& target ) { source_ = source; target_ = target; functionspace::NodeColumns src = source; @@ -77,7 +77,7 @@ void NearestNeighbour::setup( const FunctionSpace& source, const FunctionSpace& std::vector weights_triplets; weights_triplets.reserve( out_npts ); { - Trace timer( Here(), "atlas::interpolation::method::NearestNeighbour::setup()" ); + Trace timer( Here(), "atlas::interpolation::method::NearestNeighbour::do_setup()" ); for ( size_t ip = 0; ip < out_npts; ++ip ) { if ( ip && ( ip % 1000 == 0 ) ) { double rate = ip / timer.elapsed(); diff --git a/src/atlas/interpolation/method/knn/NearestNeighbour.h b/src/atlas/interpolation/method/knn/NearestNeighbour.h index 85ba2c3a7..6b50cf6e4 100644 --- a/src/atlas/interpolation/method/knn/NearestNeighbour.h +++ b/src/atlas/interpolation/method/knn/NearestNeighbour.h @@ -25,6 +25,10 @@ class NearestNeighbour : public KNearestNeighboursBase { virtual void print( std::ostream& ) const override {} protected: + virtual const FunctionSpace& source() const override { return source_; } + virtual const FunctionSpace& target() const override { return target_; } + +private: /** * @brief Create an interpolant sparse matrix relating two (pre-partitioned) * meshes, @@ -32,14 +36,10 @@ class NearestNeighbour : public KNearestNeighboursBase { * @param source functionspace containing source elements * @param target functionspace containing target points */ - virtual void setup( const FunctionSpace& source, const FunctionSpace& target ) override; + virtual void do_setup( const FunctionSpace& source, const FunctionSpace& target ) override; - virtual void setup( const Grid& source, const Grid& target ) override; + virtual void do_setup( const Grid& source, const Grid& target ) override; - virtual const FunctionSpace& source() const override { return source_; } - virtual const FunctionSpace& target() const override { return target_; } - -private: FunctionSpace source_; FunctionSpace target_; }; diff --git a/src/atlas/interpolation/method/structured/StructuredInterpolation2D.h b/src/atlas/interpolation/method/structured/StructuredInterpolation2D.h index 870990168..75be5fb87 100644 --- a/src/atlas/interpolation/method/structured/StructuredInterpolation2D.h +++ b/src/atlas/interpolation/method/structured/StructuredInterpolation2D.h @@ -37,20 +37,8 @@ class StructuredInterpolation2D : public Method { virtual ~StructuredInterpolation2D() override {} - virtual void setup( const Grid& source, const Grid& target ) override; - - virtual void setup( const FunctionSpace& source, const FunctionSpace& target ) override; - - virtual void setup( const FunctionSpace& source, const Field& target ) override; - - virtual void setup( const FunctionSpace& source, const FieldSet& target ) override; - virtual void print( std::ostream& ) const override; - virtual void execute( const Field& src, Field& tgt ) const override; - - virtual void execute( const FieldSet& src, FieldSet& tgt ) const override; - protected: void setup( const FunctionSpace& source ); @@ -60,6 +48,18 @@ class StructuredInterpolation2D : public Method { virtual const FunctionSpace& target() const override { return target_; } private: + virtual void do_setup( const Grid& source, const Grid& target ) override; + + virtual void do_setup( const FunctionSpace& source, const FunctionSpace& target ) override; + + virtual void do_setup( const FunctionSpace& source, const Field& target ) override; + + virtual void do_setup( const FunctionSpace& source, const FieldSet& target ) override; + + virtual void do_execute( const Field& src, Field& tgt ) const override; + + virtual void do_execute( const FieldSet& src, FieldSet& tgt ) const override; + template void execute_impl( const Kernel& kernel, const FieldSet& src, FieldSet& tgt ) const; diff --git a/src/atlas/interpolation/method/structured/StructuredInterpolation2D.tcc b/src/atlas/interpolation/method/structured/StructuredInterpolation2D.tcc index da40717e4..5ed82331c 100644 --- a/src/atlas/interpolation/method/structured/StructuredInterpolation2D.tcc +++ b/src/atlas/interpolation/method/structured/StructuredInterpolation2D.tcc @@ -56,7 +56,7 @@ StructuredInterpolation2D::StructuredInterpolation2D( const Method::Conf template -void StructuredInterpolation2D::setup( const Grid& source, const Grid& target ) { +void StructuredInterpolation2D::do_setup( const Grid& source, const Grid& target ) { if ( mpi::size() > 1 ) { ATLAS_NOTIMPLEMENTED; } @@ -68,13 +68,13 @@ void StructuredInterpolation2D::setup( const Grid& source, const Grid& t // guarantee "1" halo for pole treatment! FunctionSpace target_fs = functionspace::PointCloud( target ); - setup( source_fs, target_fs ); + do_setup( source_fs, target_fs ); } template -void StructuredInterpolation2D::setup( const FunctionSpace& source, const FunctionSpace& target ) { - ATLAS_TRACE( "atlas::interpolation::method::StructuredInterpolation::setup()" ); +void StructuredInterpolation2D::do_setup( const FunctionSpace& source, const FunctionSpace& target ) { + ATLAS_TRACE( "atlas::interpolation::method::StructuredInterpolation2D::do_setup()" ); source_ = source; target_ = target; @@ -101,8 +101,8 @@ void StructuredInterpolation2D::setup( const FunctionSpace& source, cons } template -void StructuredInterpolation2D::setup( const FunctionSpace& source, const Field& target ) { - ATLAS_TRACE( "StructuredInterpolation<" + Kernel::className() + ">::setup(FunctionSpace source, Field target)" ); +void StructuredInterpolation2D::do_setup( const FunctionSpace& source, const Field& target ) { + ATLAS_TRACE( "StructuredInterpolation<" + Kernel::className() + ">::do_setup(FunctionSpace source, Field target)" ); source_ = source; @@ -116,8 +116,8 @@ void StructuredInterpolation2D::setup( const FunctionSpace& source, cons } template -void StructuredInterpolation2D::setup( const FunctionSpace& source, const FieldSet& target ) { - ATLAS_TRACE( "StructuredInterpolation<" + Kernel::className() + ">::setup(FunctionSpace source,FieldSet target)" ); +void StructuredInterpolation2D::do_setup( const FunctionSpace& source, const FieldSet& target ) { + ATLAS_TRACE( "StructuredInterpolation<" + Kernel::className() + ">::do_setup(FunctionSpace source,FieldSet target)" ); source_ = source; @@ -193,20 +193,20 @@ void StructuredInterpolation2D::setup( const FunctionSpace& source ) { template -void StructuredInterpolation2D::execute( const Field& src_field, Field& tgt_field ) const { +void StructuredInterpolation2D::do_execute( const Field& src_field, Field& tgt_field ) const { FieldSet tgt( tgt_field ); - execute( FieldSet( src_field ), tgt ); + do_execute( FieldSet( src_field ), tgt ); } template -void StructuredInterpolation2D::execute( const FieldSet& src_fields, FieldSet& tgt_fields ) const { +void StructuredInterpolation2D::do_execute( const FieldSet& src_fields, FieldSet& tgt_fields ) const { if ( not matrix_free_ ) { - Method::execute( src_fields, tgt_fields ); + Method::do_execute( src_fields, tgt_fields ); return; } - ATLAS_TRACE( "StructuredInterpolation<" + Kernel::className() + ">::execute()" ); + ATLAS_TRACE( "StructuredInterpolation<" + Kernel::className() + ">::do_execute()" ); const idx_t N = src_fields.size(); ATLAS_ASSERT( N == tgt_fields.size() ); diff --git a/src/atlas/interpolation/method/structured/StructuredInterpolation3D.h b/src/atlas/interpolation/method/structured/StructuredInterpolation3D.h index 374b1b2ed..68d8e733b 100644 --- a/src/atlas/interpolation/method/structured/StructuredInterpolation3D.h +++ b/src/atlas/interpolation/method/structured/StructuredInterpolation3D.h @@ -36,21 +36,8 @@ class StructuredInterpolation3D : public Method { virtual ~StructuredInterpolation3D() override {} - virtual void setup( const Grid& source, const Grid& target ) override; - - virtual void setup( const FunctionSpace& source, const FunctionSpace& target ) override; - - virtual void setup( const FunctionSpace& source, const Field& target ) override; - - virtual void setup( const FunctionSpace& source, const FieldSet& target ) override; - virtual void print( std::ostream& ) const override; - virtual void execute( const Field& src, Field& tgt ) const override; - - virtual void execute( const FieldSet& src, FieldSet& tgt ) const override; - - protected: void setup( const FunctionSpace& source ); @@ -59,6 +46,18 @@ class StructuredInterpolation3D : public Method { virtual const FunctionSpace& target() const override { return target_; } private: + virtual void do_setup( const Grid& source, const Grid& target ) override; + + virtual void do_setup( const FunctionSpace& source, const FunctionSpace& target ) override; + + virtual void do_setup( const FunctionSpace& source, const Field& target ) override; + + virtual void do_setup( const FunctionSpace& source, const FieldSet& target ) override; + + virtual void do_execute( const Field& src, Field& tgt ) const override; + + virtual void do_execute( const FieldSet& src, FieldSet& tgt ) const override; + template void execute_impl( const Kernel& kernel, const FieldSet& src, FieldSet& tgt ) const; diff --git a/src/atlas/interpolation/method/structured/StructuredInterpolation3D.tcc b/src/atlas/interpolation/method/structured/StructuredInterpolation3D.tcc index 70423aceb..d27e29ba2 100644 --- a/src/atlas/interpolation/method/structured/StructuredInterpolation3D.tcc +++ b/src/atlas/interpolation/method/structured/StructuredInterpolation3D.tcc @@ -59,7 +59,7 @@ StructuredInterpolation3D::StructuredInterpolation3D( const Method::Conf template -void StructuredInterpolation3D::setup( const Grid& source, const Grid& target ) { +void StructuredInterpolation3D::do_setup( const Grid& source, const Grid& target ) { if ( mpi::size() > 1 ) { ATLAS_NOTIMPLEMENTED; } @@ -69,13 +69,13 @@ void StructuredInterpolation3D::setup( const Grid& source, const Grid& t FunctionSpace source_fs = functionspace::StructuredColumns( source, option::halo( kernel_->stencil_halo() ) ); FunctionSpace target_fs = functionspace::PointCloud( target ); - setup( source_fs, target_fs ); + do_setup( source_fs, target_fs ); } template -void StructuredInterpolation3D::setup( const FunctionSpace& source, const FunctionSpace& target ) { - ATLAS_TRACE( "StructuredInterpolation<" + Kernel::className() + ">::setup()" ); +void StructuredInterpolation3D::do_setup( const FunctionSpace& source, const FunctionSpace& target ) { + ATLAS_TRACE( "StructuredInterpolation<" + Kernel::className() + ">::do_setup()" ); source_ = source; target_ = target; @@ -93,8 +93,8 @@ void StructuredInterpolation3D::setup( const FunctionSpace& source, cons } template -void StructuredInterpolation3D::setup( const FunctionSpace& source, const Field& target ) { - ATLAS_TRACE( "StructuredInterpolation<" + Kernel::className() + ">::setup(FunctionSpace source, Field target)" ); +void StructuredInterpolation3D::do_setup( const FunctionSpace& source, const Field& target ) { + ATLAS_TRACE( "StructuredInterpolation<" + Kernel::className() + ">::do_setup(FunctionSpace source, Field target)" ); source_ = source; @@ -108,8 +108,8 @@ void StructuredInterpolation3D::setup( const FunctionSpace& source, cons } template -void StructuredInterpolation3D::setup( const FunctionSpace& source, const FieldSet& target ) { - ATLAS_TRACE( "StructuredInterpolation<" + Kernel::className() + ">::setup(FunctionSpace source,FieldSet target)" ); +void StructuredInterpolation3D::do_setup( const FunctionSpace& source, const FieldSet& target ) { + ATLAS_TRACE( "StructuredInterpolation<" + Kernel::className() + ">::do_setup(FunctionSpace source,FieldSet target)" ); source_ = source; @@ -136,20 +136,20 @@ void StructuredInterpolation3D::setup( const FunctionSpace& source ) { template -void StructuredInterpolation3D::execute( const Field& src_field, Field& tgt_field ) const { +void StructuredInterpolation3D::do_execute( const Field& src_field, Field& tgt_field ) const { FieldSet tgt( tgt_field ); - execute( FieldSet( src_field ), tgt ); + do_execute( FieldSet( src_field ), tgt ); } template -void StructuredInterpolation3D::execute( const FieldSet& src_fields, FieldSet& tgt_fields ) const { +void StructuredInterpolation3D::do_execute( const FieldSet& src_fields, FieldSet& tgt_fields ) const { if ( not matrix_free_ ) { - Method::execute( src_fields, tgt_fields ); + Method::do_execute( src_fields, tgt_fields ); return; } - ATLAS_TRACE( "StructuredInterpolation<" + Kernel::className() + ">::execute()" ); + ATLAS_TRACE( "StructuredInterpolation<" + Kernel::className() + ">::do_execute()" ); const idx_t N = src_fields.size(); ATLAS_ASSERT( N == tgt_fields.size() ); From 9c8aa05c1764d8665ba4d66446e2f916089e240a Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 19 Mar 2020 09:24:31 +0000 Subject: [PATCH 007/145] Add test for setup of parallel interpolation to unstructured grid --- src/tests/interpolation/CMakeLists.txt | 6 + ...erpolation_structured2D_to_unstructured.cc | 181 ++++++++++++++++++ 2 files changed, 187 insertions(+) create mode 100644 src/tests/interpolation/test_interpolation_structured2D_to_unstructured.cc diff --git a/src/tests/interpolation/CMakeLists.txt b/src/tests/interpolation/CMakeLists.txt index 42fa53d9c..1d73b9611 100644 --- a/src/tests/interpolation/CMakeLists.txt +++ b/src/tests/interpolation/CMakeLists.txt @@ -45,3 +45,9 @@ ecbuild_add_test( TARGET atlas_test_interpolation_biquasicubic COMMAND atlas_test_interpolation_structured2D ARGS --scheme quasicubic ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) + +ecbuild_add_test( TARGET atlas_test_interpolation_structured2D_to_unstructured + SOURCES test_interpolation_structured2D_to_unstructured.cc + LIBS atlas + MPI 2 +) diff --git a/src/tests/interpolation/test_interpolation_structured2D_to_unstructured.cc b/src/tests/interpolation/test_interpolation_structured2D_to_unstructured.cc new file mode 100644 index 000000000..2f5954abc --- /dev/null +++ b/src/tests/interpolation/test_interpolation_structured2D_to_unstructured.cc @@ -0,0 +1,181 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include "atlas/array.h" +#include "atlas/field/Field.h" +#include "atlas/field/FieldSet.h" +#include "atlas/functionspace/NodeColumns.h" +#include "atlas/functionspace/PointCloud.h" +#include "atlas/functionspace/StructuredColumns.h" +#include "atlas/grid/Grid.h" +#include "atlas/grid/Iterator.h" +#include "atlas/interpolation.h" +#include "atlas/mesh/Mesh.h" +#include "atlas/meshgenerator.h" +#include "atlas/output/Gmsh.h" +#include "atlas/util/CoordinateEnums.h" + +#include "tests/AtlasTestEnvironment.h" + +using atlas::functionspace::PointCloud; +using atlas::functionspace::StructuredColumns; +using atlas::util::Config; + +namespace atlas { +namespace test { + +//----------------------------------------------------------------------------- + +static Config scheme() { + Config scheme; + scheme.set( "type", "structured-linear2D" ); + scheme.set( "halo", 1 ); + scheme.set( "name", "linear" ); + return scheme; +} + +std::string input_gridname( const std::string& default_grid ) { + return eckit::Resource( "--input-grid", default_grid ); +} + +double vortex_rollup( double lon, double lat, double t ) { + // lon and lat in degrees! + + // Formula found in "A Lagrangian Particle Method with Remeshing for Tracer Transport on the Sphere" + // by Peter Bosler, James Kent, Robert Krasny, CHristiane Jablonowski, JCP 2015 + + lon *= M_PI / 180.; + lat *= M_PI / 180.; + + auto sqr = []( const double x ) { return x * x; }; + auto sech = []( const double x ) { return 1. / std::cosh( x ); }; + const double T = 1.; + const double Omega = 2. * M_PI / T; + t *= T; + const double lambda_prime = std::atan2( -std::cos( lon - Omega * t ), std::tan( lat ) ); + const double rho = 3. * std::sqrt( 1. - sqr( std::cos( lat ) ) * sqr( std::sin( lon - Omega * t ) ) ); + double omega = 0.; + double a = util::Earth::radius(); + if ( rho != 0. ) { + omega = 0.5 * 3 * std::sqrt( 3 ) * a * Omega * sqr( sech( rho ) ) * std::tanh( rho ) / rho; + } + double q = 1. - std::tanh( 0.2 * rho * std::sin( lambda_prime - omega / a * t ) ); + return q; +}; + + +FunctionSpace output_functionspace_match() { + std::vector points; + if ( mpi::size() == 2 ) { + if ( mpi::rank() == 0 ) { + points = std::vector{ + {45., 45.}, {90., 45.}, {135., 45.}, {180., 45.}, {225., 45.}, {270., 45.}, {315., 45.}, + }; + } + if ( mpi::rank() == 1 ) { + points = std::vector{ + {45., -45.}, {90., -45.}, {135., -45.}, {180., -45.}, {225., -45.}, {270., -45.}, {315., -45.}, + }; + } + } + else { + return FunctionSpace(); + } + return PointCloud( points ); +} +FunctionSpace output_functionspace_nomatch() { + std::vector points; + if ( mpi::size() == 2 ) { + if ( mpi::rank() == 0 ) { + points = std::vector{ + {45., 45.}, {90., 45.}, {135., 45.}, {45., -45.}, {90., -45.}, {135., -45.}, + }; + } + if ( mpi::rank() == 1 ) { + points = std::vector{ + {180., 45.}, {225., 45.}, {270., 45.}, {315., 45.}, + {180., -45.}, {225., -45.}, {270., -45.}, {315., -45.}, + }; + } + } + else { + return FunctionSpace(); + } + return PointCloud( points ); +} + +FieldSet create_source_fields( StructuredColumns& fs, idx_t nb_fields, idx_t nb_levels ) { + using Value = double; + FieldSet fields_source; + auto lonlat = array::make_view( fs.xy() ); + for ( idx_t f = 0; f < nb_fields; ++f ) { + auto field_source = fields_source.add( fs.createField() ); + auto source = array::make_view( field_source ); + for ( idx_t n = 0; n < fs.size(); ++n ) { + for ( idx_t k = 0; k < nb_levels; ++k ) { + source( n, k ) = vortex_rollup( lonlat( n, LON ), lonlat( n, LAT ), 0.5 + double( k ) / 2 ); + } + }; + } + return fields_source; +} +FieldSet create_target_fields( FunctionSpace& fs, idx_t nb_fields, idx_t nb_levels ) { + using Value = double; + FieldSet fields_target; + for ( idx_t f = 0; f < nb_fields; ++f ) { + fields_target.add( fs.createField( option::levels( nb_levels ) ) ); + } + return fields_target; +} + + +CASE( "test_match" ) { + idx_t nb_fields = 2; + idx_t nb_levels = 3; + + Grid input_grid( input_gridname( "O32" ) ); + StructuredColumns input_fs( input_grid, option::halo( 1 ) | option::levels( nb_levels ) ); + + FunctionSpace output_fs = output_functionspace_match(); + + Interpolation interpolation( option::type( "structured-linear2D" ), input_fs, output_fs ); + + FieldSet fields_source = create_source_fields( input_fs, nb_fields, nb_levels ); + FieldSet fields_target = create_target_fields( output_fs, nb_fields, nb_levels ); + + interpolation.execute( fields_source, fields_target ); +} +CASE( "test_nomatch" ) { + idx_t nb_fields = 2; + idx_t nb_levels = 3; + + Grid input_grid( input_gridname( "O32" ) ); + StructuredColumns input_fs( input_grid, option::halo( 1 ) | option::levels( nb_levels ) ); + + FunctionSpace output_fs = output_functionspace_nomatch(); + + if ( false ) // expected to throw + { + Interpolation interpolation( option::type( "structured-linear2D" ), input_fs, output_fs ); + + FieldSet fields_source = create_source_fields( input_fs, nb_fields, nb_levels ); + FieldSet fields_target = create_target_fields( output_fs, nb_fields, nb_levels ); + + interpolation.execute( fields_source, fields_target ); + } +} + + +} // namespace test +} // namespace atlas + +int main( int argc, char** argv ) { + return atlas::test::run( argc, argv ); +} From bbbfa0619b585d9f1835dd10e7179d41056c6468 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 5 Mar 2020 16:04:09 +0000 Subject: [PATCH 008/145] ATLAS-270 Create polygons on each partition, and distribute --- src/atlas/array/SVector.h | 4 +- src/atlas/array/native/NativeArrayView.h | 5 +- src/atlas/domain/Domain.cc | 9 +-- src/atlas/domain/detail/RectangularDomain.cc | 6 +- src/atlas/functionspace/FunctionSpace.cc | 4 + src/atlas/functionspace/FunctionSpace.h | 3 + src/atlas/functionspace/PointCloud.cc | 7 +- src/atlas/functionspace/Spectral.cc | 14 +--- src/atlas/functionspace/StructuredColumns.cc | 3 +- .../functionspace/detail/FunctionSpaceImpl.cc | 4 + .../functionspace/detail/FunctionSpaceImpl.h | 3 + .../functionspace/detail/StructuredColumns.cc | 75 ++++++++++++++++++ .../functionspace/detail/StructuredColumns.h | 3 + src/atlas/grid/StencilComputer.cc | 3 +- src/atlas/grid/StructuredGrid.cc | 9 +-- src/atlas/grid/StructuredPartitionPolygon.cc | 3 +- src/atlas/grid/UnstructuredGrid.cc | 12 +-- src/atlas/grid/UnstructuredGrid.h | 4 + .../detail/distribution/DistributionImpl.cc | 4 +- src/atlas/grid/detail/grid/GridBuilder.cc | 4 +- src/atlas/grid/detail/grid/Structured.cc | 26 ++---- src/atlas/grid/detail/grid/Structured.h | 6 +- src/atlas/grid/detail/grid/Unstructured.cc | 9 +-- .../MatchingFunctionSpacePartitioner.cc | 3 +- .../partitioner/MatchingMeshPartitioner.cc | 3 +- src/atlas/interpolation/element/Quad3D.h | 5 +- src/atlas/interpolation/method/Method.cc | 14 ++-- src/atlas/mesh/Connectivity.cc | 6 +- src/atlas/mesh/Elements.cc | 4 +- src/atlas/mesh/IsGhostNode.h | 3 +- src/atlas/mesh/PartitionPolygon.cc | 4 +- src/atlas/mesh/actions/BuildCellCentres.cc | 4 +- src/atlas/mesh/actions/BuildXYZField.cc | 3 +- src/atlas/parallel/GatherScatter.h | 3 +- src/atlas/parallel/mpi/mpi.h | 6 +- .../projection/detail/LonLatProjection.cc | 3 +- .../projection/detail/MercatorProjection.cc | 3 +- src/atlas/projection/detail/ProjProjection.cc | 4 +- src/atlas/projection/detail/ProjectionImpl.cc | 3 +- .../projection/detail/SchmidtProjection.cc | 3 +- src/atlas/runtime/trace/TraceT.h | 7 +- src/atlas/trans/Cache.cc | 12 +-- src/atlas/trans/detail/TransFactory.cc | 4 +- .../trans/ifs/LegendreCacheCreatorIFS.cc | 4 +- src/atlas/trans/ifs/TransIFS.cc | 12 +-- .../trans/local/LegendreCacheCreatorLocal.cc | 4 +- src/atlas/util/Factory.cc | 3 +- src/atlas/util/Unique.h | 6 +- src/sandbox/interpolation/PartitionedMesh.cc | 3 +- src/tests/functionspace/CMakeLists.txt | 8 ++ src/tests/functionspace/test_polygons.cc | 79 +++++++++++++++++++ .../CubicInterpolationPrototype.h | 7 +- src/tests/mesh/test_shapefunctions.cc | 3 +- src/tests/parallel/test_haloexchange.cc | 53 +++++++------ 54 files changed, 289 insertions(+), 210 deletions(-) create mode 100644 src/tests/functionspace/test_polygons.cc diff --git a/src/atlas/array/SVector.h b/src/atlas/array/SVector.h index 072523427..2d4f2046c 100644 --- a/src/atlas/array/SVector.h +++ b/src/atlas/array/SVector.h @@ -42,9 +42,7 @@ class SVector { ATLAS_HOST_DEVICE SVector( SVector&& other ) : - data_( other.data_ ), - size_( other.size_ ), - externally_allocated_( other.externally_allocated_ ) { + data_( other.data_ ), size_( other.size_ ), externally_allocated_( other.externally_allocated_ ) { other.data_ = nullptr; other.size_ = 0; other.externally_allocated_ = true; diff --git a/src/atlas/array/native/NativeArrayView.h b/src/atlas/array/native/NativeArrayView.h index 8b8c0b1db..e3616a775 100644 --- a/src/atlas/array/native/NativeArrayView.h +++ b/src/atlas/array/native/NativeArrayView.h @@ -130,10 +130,7 @@ class ArrayView { // -- Constructors ArrayView( const ArrayView& other ) : - data_( other.data_ ), - size_( other.size_ ), - shape_( other.shape_ ), - strides_( other.strides_ ) {} + data_( other.data_ ), size_( other.size_ ), shape_( other.shape_ ), strides_( other.strides_ ) {} ENABLE_IF_CONST ArrayView( const ArrayView& other ) : data_( other.data() ), size_( other.size() ) { diff --git a/src/atlas/domain/Domain.cc b/src/atlas/domain/Domain.cc index c3cd1fcc8..ee024bac2 100644 --- a/src/atlas/domain/Domain.cc +++ b/src/atlas/domain/Domain.cc @@ -30,8 +30,7 @@ RectangularDomain::RectangularDomain( const Interval& x, const Interval& y, cons domain_( dynamic_cast( get() ) ) {} RectangularDomain::RectangularDomain( const Domain& domain ) : - Domain( domain ), - domain_( dynamic_cast( get() ) ) {} + Domain( domain ), domain_( dynamic_cast( get() ) ) {} bool RectangularDomain::contains_x( double x ) const { return domain_->contains_x( x ); @@ -72,8 +71,7 @@ ZonalBandDomain::ZonalBandDomain( const Interval& y, const double& west ) : domain_( dynamic_cast( get() ) ) {} ZonalBandDomain::ZonalBandDomain( const Domain& domain ) : - RectangularLonLatDomain( domain ), - domain_( dynamic_cast( get() ) ) {} + RectangularLonLatDomain( domain ), domain_( dynamic_cast( get() ) ) {} GlobalDomain::GlobalDomain( const double& west ) : ZonalBandDomain( new atlas::domain::GlobalDomain( west ) ), @@ -84,8 +82,7 @@ GlobalDomain::GlobalDomain() : domain_( dynamic_cast( get() ) ) {} GlobalDomain::GlobalDomain( const Domain& domain ) : - ZonalBandDomain( domain ), - domain_( dynamic_cast( get() ) ) {} + ZonalBandDomain( domain ), domain_( dynamic_cast( get() ) ) {} std::string atlas::Domain::type() const { diff --git a/src/atlas/domain/detail/RectangularDomain.cc b/src/atlas/domain/detail/RectangularDomain.cc index bb2aeb4a2..6f5094ce6 100644 --- a/src/atlas/domain/detail/RectangularDomain.cc +++ b/src/atlas/domain/detail/RectangularDomain.cc @@ -84,11 +84,7 @@ RectangularDomain::RectangularDomain( const eckit::Parametrisation& params ) : RectangularDomain( get_interval_x( params ), get_interval_y( params ), get_units( params ) ) {} RectangularDomain::RectangularDomain( const Interval& x, const Interval& y, const std::string& units ) : - xmin_( x[0] ), - xmax_( x[1] ), - ymin_( y[0] ), - ymax_( y[1] ), - units_( units ) { + xmin_( x[0] ), xmax_( x[1] ), ymin_( y[0] ), ymax_( y[1] ), units_( units ) { unit_degrees_ = ( units_ == "degrees" ) ? true : false; // Make sure xmax>=xmin and ymax>=ymin diff --git a/src/atlas/functionspace/FunctionSpace.cc b/src/atlas/functionspace/FunctionSpace.cc index a364012c0..4fedd77a5 100644 --- a/src/atlas/functionspace/FunctionSpace.cc +++ b/src/atlas/functionspace/FunctionSpace.cc @@ -66,6 +66,10 @@ const util::PartitionPolygon& FunctionSpace::polygon( idx_t halo ) const { return get()->polygon( halo ); } +const std::vector& FunctionSpace::polygons() const { + return get()->polygons(); +} + template Field FunctionSpace::createField() const { return get()->createField(); diff --git a/src/atlas/functionspace/FunctionSpace.h b/src/atlas/functionspace/FunctionSpace.h index 74e9d0c99..7f89bcd12 100644 --- a/src/atlas/functionspace/FunctionSpace.h +++ b/src/atlas/functionspace/FunctionSpace.h @@ -11,6 +11,7 @@ #pragma once #include +#include #include "atlas/library/config.h" #include "atlas/util/ObjectHandle.h" @@ -60,6 +61,8 @@ class FunctionSpace : DOXYGEN_HIDE( public util::ObjectHandle& polygons() const; + idx_t nb_partitions() const; idx_t size() const; diff --git a/src/atlas/functionspace/PointCloud.cc b/src/atlas/functionspace/PointCloud.cc index 30c65ad2c..d1048b0a9 100644 --- a/src/atlas/functionspace/PointCloud.cc +++ b/src/atlas/functionspace/PointCloud.cc @@ -127,9 +127,7 @@ bool atlas::functionspace::detail::PointCloud::IteratorXYZ::next( PointXYZ& xyz atlas::functionspace::detail::PointCloud::IteratorXY::IteratorXY( const atlas::functionspace::detail::PointCloud& fs, bool begin ) : - fs_( fs ), - xy_( array::make_view( fs_.lonlat() ) ), - n_( begin ? 0 : fs_.size() ) {} + fs_( fs ), xy_( array::make_view( fs_.lonlat() ) ), n_( begin ? 0 : fs_.size() ) {} bool atlas::functionspace::detail::PointCloud::IteratorXY::next( PointXY& xyz ) { if ( n_ < fs_.size() ) { @@ -159,8 +157,7 @@ const PointXYZ atlas::functionspace::detail::PointCloud::IteratorXYZ::operator*( } // namespace detail PointCloud::PointCloud( const FunctionSpace& functionspace ) : - FunctionSpace( functionspace ), - functionspace_( dynamic_cast( get() ) ) {} + FunctionSpace( functionspace ), functionspace_( dynamic_cast( get() ) ) {} PointCloud::PointCloud( const Field& points ) : FunctionSpace( new detail::PointCloud( points ) ), diff --git a/src/atlas/functionspace/Spectral.cc b/src/atlas/functionspace/Spectral.cc index 48627cb9a..5b1ba41ee 100644 --- a/src/atlas/functionspace/Spectral.cc +++ b/src/atlas/functionspace/Spectral.cc @@ -170,16 +170,12 @@ Spectral::Spectral( const eckit::Configuration& config ) : // ---------------------------------------------------------------------- Spectral::Spectral( const int truncation, const eckit::Configuration& config ) : - nb_levels_( 0 ), - truncation_( truncation ), - parallelisation_( new Parallelisation( truncation_ ) ) { + nb_levels_( 0 ), truncation_( truncation ), parallelisation_( new Parallelisation( truncation_ ) ) { config.get( "levels", nb_levels_ ); } Spectral::Spectral( const trans::Trans& trans, const eckit::Configuration& config ) : - nb_levels_( 0 ), - truncation_( trans.truncation() ), - parallelisation_( [&trans, this]() -> Parallelisation* { + nb_levels_( 0 ), truncation_( trans.truncation() ), parallelisation_( [&trans, this]() -> Parallelisation* { #if ATLAS_HAVE_TRANS const auto* trans_ifs = dynamic_cast( trans.get() ); if ( trans_ifs ) { @@ -443,12 +439,10 @@ array::LocalView Spectral::nasm0() const { Spectral::Spectral() : FunctionSpace(), functionspace_{nullptr} {} Spectral::Spectral( const FunctionSpace& functionspace ) : - FunctionSpace( functionspace ), - functionspace_( dynamic_cast( get() ) ) {} + FunctionSpace( functionspace ), functionspace_( dynamic_cast( get() ) ) {} Spectral::Spectral( const eckit::Configuration& config ) : - FunctionSpace( new detail::Spectral( config ) ), - functionspace_( dynamic_cast( get() ) ) {} + FunctionSpace( new detail::Spectral( config ) ), functionspace_( dynamic_cast( get() ) ) {} Spectral::Spectral( const int truncation, const eckit::Configuration& config ) : FunctionSpace( new detail::Spectral( truncation, config ) ), diff --git a/src/atlas/functionspace/StructuredColumns.cc b/src/atlas/functionspace/StructuredColumns.cc index 183d12dd7..19c10418e 100644 --- a/src/atlas/functionspace/StructuredColumns.cc +++ b/src/atlas/functionspace/StructuredColumns.cc @@ -18,8 +18,7 @@ namespace functionspace { StructuredColumns::StructuredColumns() : FunctionSpace(), functionspace_( nullptr ) {} StructuredColumns::StructuredColumns( const FunctionSpace& functionspace ) : - FunctionSpace( functionspace ), - functionspace_( dynamic_cast( get() ) ) {} + FunctionSpace( functionspace ), functionspace_( dynamic_cast( get() ) ) {} StructuredColumns::StructuredColumns( const Grid& grid, const eckit::Configuration& config ) : FunctionSpace( new detail::StructuredColumns( grid, config ) ), diff --git a/src/atlas/functionspace/detail/FunctionSpaceImpl.cc b/src/atlas/functionspace/detail/FunctionSpaceImpl.cc index a7ac1c687..682b904c0 100644 --- a/src/atlas/functionspace/detail/FunctionSpaceImpl.cc +++ b/src/atlas/functionspace/detail/FunctionSpaceImpl.cc @@ -49,6 +49,10 @@ const util::PartitionPolygon& FunctionSpaceImpl::polygon( idx_t halo ) const { throw_Exception( "polygon() not implemented in derived class", Here() ); } +const std::vector& FunctionSpaceImpl::polygons() const { + throw_Exception( "polygons() not implemented in derived class", Here() ); +} + template Field FunctionSpaceImpl::createField( const eckit::Configuration& options ) const { return createField( option::datatypeT() | options ); diff --git a/src/atlas/functionspace/detail/FunctionSpaceImpl.h b/src/atlas/functionspace/detail/FunctionSpaceImpl.h index 45962bfd9..c586c33cf 100644 --- a/src/atlas/functionspace/detail/FunctionSpaceImpl.h +++ b/src/atlas/functionspace/detail/FunctionSpaceImpl.h @@ -12,6 +12,7 @@ #include #include +#include #include "atlas/util/Object.h" @@ -78,6 +79,8 @@ class FunctionSpaceImpl : public util::Object { virtual const util::PartitionPolygon& polygon( idx_t halo = 0 ) const; + virtual const std::vector& polygons() const; + private: util::Metadata* metadata_; }; diff --git a/src/atlas/functionspace/detail/StructuredColumns.cc b/src/atlas/functionspace/detail/StructuredColumns.cc index b7cfa42ee..efecb4bda 100644 --- a/src/atlas/functionspace/detail/StructuredColumns.cc +++ b/src/atlas/functionspace/detail/StructuredColumns.cc @@ -454,10 +454,85 @@ const util::PartitionPolygon& StructuredColumns::polygon( idx_t halo ) const { return *polygon_; } +util::Polygon::edge_set_t compute_edges( idx_t points_size ) { + util::Polygon::edge_set_t edges; + auto add_edge = [&]( idx_t p1, idx_t p2 ) { + util::Polygon::edge_t edge = {p1, p2}; + edges.insert( edge ); + // Log::info() << edge.first << " " << edge.second << std::endl; + }; + for ( idx_t p = 0; p < points_size - 2; ++p ) { + add_edge( p, p + 1 ); + } + add_edge( points_size - 2, 0 ); + return edges; +} + +class SimplePolygon : public util::PartitionPolygon { +public: + explicit SimplePolygon( const std::vector& points ) : points_( points ) { + setup( compute_edges( points_.size() ) ); + } + + const std::vector& xy() const override { return points_; } + const std::vector& lonlat() const override { return points_; } + + +private: + std::vector points_; +}; + + +const std::vector& StructuredColumns::polygons() const { + if ( polygons_.size() ) { + return polygons_; + } + ATLAS_TRACE(); + + polygons_.reserve( mpi::size() ); + + + const mpi::Comm& comm = mpi::comm(); + const int mpi_size = int( comm.size() ); + + auto& poly = polygon(); + + std::vector mypolygon; + mypolygon.reserve( poly.size() * 2 ); + + for ( auto& p : poly.xy() ) { + mypolygon.push_back( p[XX] ); + mypolygon.push_back( p[YY] ); + //Log::info() << p << std::endl; + } + ATLAS_ASSERT( mypolygon.size() >= 4 ); + + eckit::mpi::Buffer recv_polygons( mpi_size ); + + comm.allGatherv( mypolygon.begin(), mypolygon.end(), recv_polygons ); + + using PolygonXY = std::vector; + for ( idx_t p = 0; p < mpi_size; ++p ) { + PolygonXY recv_points; + recv_points.reserve( recv_polygons.counts[p] ); + for ( idx_t j = 0; j < recv_polygons.counts[p] / 2; ++j ) { + PointXY pxy( *( recv_polygons.begin() + recv_polygons.displs[p] + 2 * j + XX ), + *( recv_polygons.begin() + recv_polygons.displs[p] + 2 * j + YY ) ); + recv_points.push_back( pxy ); + } + polygons_.emplace_back( new SimplePolygon( std::move( recv_points ) ) ); + } + + return polygons_; +} + // ---------------------------------------------------------------------------- // Destructor // ---------------------------------------------------------------------------- StructuredColumns::~StructuredColumns() { + for ( auto p : polygons_ ) { + delete p; + } delete grid_; } // ---------------------------------------------------------------------------- diff --git a/src/atlas/functionspace/detail/StructuredColumns.h b/src/atlas/functionspace/detail/StructuredColumns.h index ff844198f..fdbcbe99e 100644 --- a/src/atlas/functionspace/detail/StructuredColumns.h +++ b/src/atlas/functionspace/detail/StructuredColumns.h @@ -154,6 +154,8 @@ class StructuredColumns : public FunctionSpaceImpl { const util::PartitionPolygon& polygon( idx_t halo = 0 ) const override; + const std::vector& polygons() const override; + idx_t nb_partitions() const override { return nb_partitions_; } @@ -199,6 +201,7 @@ class StructuredColumns : public FunctionSpaceImpl { mutable util::ObjectHandle checksum_; mutable util::ObjectHandle halo_exchange_; mutable std::unique_ptr polygon_; + mutable std::vector polygons_; Field field_xy_; Field field_partition_; diff --git a/src/atlas/grid/StencilComputer.cc b/src/atlas/grid/StencilComputer.cc index a1439f219..0e967dc68 100644 --- a/src/atlas/grid/StencilComputer.cc +++ b/src/atlas/grid/StencilComputer.cc @@ -104,8 +104,7 @@ ComputeHorizontalStencil::ComputeHorizontalStencil( const StructuredGrid& grid, } ComputeVerticalStencil::ComputeVerticalStencil( const Vertical& vertical, idx_t stencil_width ) : - compute_lower_( vertical ), - stencil_width_( stencil_width ) { + compute_lower_( vertical ), stencil_width_( stencil_width ) { stencil_begin_ = stencil_width_ - idx_t( double( stencil_width_ ) / 2. + 1. ); clip_begin_ = 0; clip_end_ = vertical.size(); diff --git a/src/atlas/grid/StructuredGrid.cc b/src/atlas/grid/StructuredGrid.cc index 4551df89b..ae4c26399 100644 --- a/src/atlas/grid/StructuredGrid.cc +++ b/src/atlas/grid/StructuredGrid.cc @@ -37,19 +37,16 @@ StructuredGrid::StructuredGrid( const Grid& grid ) : Grid( grid ), grid_( struct StructuredGrid::StructuredGrid( const Grid::Implementation* grid ) : Grid( grid ), grid_( structured_grid( get() ) ) {} StructuredGrid::StructuredGrid( const std::string& grid, const Domain& domain ) : - Grid( grid, domain ), - grid_( structured_grid( get() ) ) {} + Grid( grid, domain ), grid_( structured_grid( get() ) ) {} StructuredGrid::StructuredGrid( const Config& grid ) : Grid( grid ), grid_( structured_grid( get() ) ) {} StructuredGrid::StructuredGrid( const XSpace& xspace, const YSpace& yspace, const Projection& projection, const Domain& domain ) : - Grid( new StructuredGrid::grid_t( xspace, yspace, projection, domain ) ), - grid_( structured_grid( get() ) ) {} + Grid( new StructuredGrid::grid_t( xspace, yspace, projection, domain ) ), grid_( structured_grid( get() ) ) {} StructuredGrid::StructuredGrid( const Grid& grid, const Grid::Domain& domain ) : - Grid( grid, domain ), - grid_( structured_grid( get() ) ) {} + Grid( grid, domain ), grid_( structured_grid( get() ) ) {} ReducedGaussianGrid::ReducedGaussianGrid( const std::vector& nx, const Domain& domain ) : ReducedGaussianGrid::grid_t( grid::detail::grid::reduced_gaussian( nx, domain ) ) {} diff --git a/src/atlas/grid/StructuredPartitionPolygon.cc b/src/atlas/grid/StructuredPartitionPolygon.cc index e29ca607c..96df77fa4 100644 --- a/src/atlas/grid/StructuredPartitionPolygon.cc +++ b/src/atlas/grid/StructuredPartitionPolygon.cc @@ -303,8 +303,7 @@ util::Polygon::edge_set_t compute_edges( const functionspace::FunctionSpaceImpl& } StructuredPartitionPolygon::StructuredPartitionPolygon( const functionspace::FunctionSpaceImpl& fs, idx_t halo ) : - fs_( fs ), - halo_( halo ) { + fs_( fs ), halo_( halo ) { ATLAS_TRACE( "StructuredPartitionPolygon" ); setup( compute_edges( fs, halo, points_, inner_bounding_box_ ) ); points_.emplace_back( points_[0] ); diff --git a/src/atlas/grid/UnstructuredGrid.cc b/src/atlas/grid/UnstructuredGrid.cc index f13e62aa2..ad39d15e9 100644 --- a/src/atlas/grid/UnstructuredGrid.cc +++ b/src/atlas/grid/UnstructuredGrid.cc @@ -32,14 +32,12 @@ UnstructuredGrid::UnstructuredGrid() : Grid() {} UnstructuredGrid::UnstructuredGrid( const Grid& grid ) : Grid( grid ), grid_( unstructured_grid( get() ) ) {} UnstructuredGrid::UnstructuredGrid( const Grid::Implementation* grid ) : - Grid( grid ), - grid_( unstructured_grid( get() ) ) {} + Grid( grid ), grid_( unstructured_grid( get() ) ) {} UnstructuredGrid::UnstructuredGrid( const Config& grid ) : Grid( grid ), grid_( unstructured_grid( get() ) ) {} UnstructuredGrid::UnstructuredGrid( std::vector* xy ) : - Grid( new UnstructuredGrid::grid_t( xy ) ), - grid_( unstructured_grid( get() ) ) {} + Grid( new UnstructuredGrid::grid_t( xy ) ), grid_( unstructured_grid( get() ) ) {} UnstructuredGrid::UnstructuredGrid( std::vector&& xy ) : Grid( new UnstructuredGrid::grid_t( std::forward>( xy ) ) ), @@ -50,12 +48,10 @@ UnstructuredGrid::UnstructuredGrid( const std::vector& xy ) : grid_( unstructured_grid( get() ) ) {} UnstructuredGrid::UnstructuredGrid( std::initializer_list xy ) : - Grid( new UnstructuredGrid::grid_t( xy ) ), - grid_( unstructured_grid( get() ) ) {} + Grid( new UnstructuredGrid::grid_t( xy ) ), grid_( unstructured_grid( get() ) ) {} UnstructuredGrid::UnstructuredGrid( const Grid& grid, const Grid::Domain& domain ) : - Grid( new UnstructuredGrid::grid_t( *grid.get(), domain ) ), - grid_( unstructured_grid( get() ) ) {} + Grid( new UnstructuredGrid::grid_t( *grid.get(), domain ) ), grid_( unstructured_grid( get() ) ) {} } // namespace atlas diff --git a/src/atlas/grid/UnstructuredGrid.h b/src/atlas/grid/UnstructuredGrid.h index bb42d907b..4882c0c21 100644 --- a/src/atlas/grid/UnstructuredGrid.h +++ b/src/atlas/grid/UnstructuredGrid.h @@ -56,12 +56,16 @@ class UnstructuredGrid : public Grid { using Grid::lonlat; using Grid::xy; + // @brief Copy coordinates x,y of point `n` into xy[2] void xy( idx_t n, double xy[] ) const { grid_->xy( n, xy ); } + // @brief Copy coordinates lon,lat of point `n` into lonlat[2] void lonlat( idx_t n, double lonlat[] ) const { grid_->lonlat( n, lonlat ); } + // @brief Create PointXY at grid index `n` PointXY xy( idx_t n ) const { return grid_->xy( n ); } + // @brief Create PointLonLat at grid index `n` PointLonLat lonlat( idx_t n ) const { return grid_->lonlat( n ); } private: diff --git a/src/atlas/grid/detail/distribution/DistributionImpl.cc b/src/atlas/grid/detail/distribution/DistributionImpl.cc index f5eb51bfa..3ef77058a 100644 --- a/src/atlas/grid/detail/distribution/DistributionImpl.cc +++ b/src/atlas/grid/detail/distribution/DistributionImpl.cc @@ -102,9 +102,7 @@ DistributionImpl::DistributionImpl( int nb_partitions, idx_t npts, int part[], i } DistributionImpl::DistributionImpl( int nb_partitions, partition_t&& part ) : - nb_partitions_( nb_partitions ), - part_( std::move( part ) ), - nb_pts_( nb_partitions_, 0 ) { + nb_partitions_( nb_partitions ), part_( std::move( part ) ), nb_pts_( nb_partitions_, 0 ) { size_t size = part_.size(); int num_threads = atlas_omp_get_max_threads(); std::vector > nb_pts_per_thread( num_threads, std::vector( nb_partitions_ ) ); diff --git a/src/atlas/grid/detail/grid/GridBuilder.cc b/src/atlas/grid/detail/grid/GridBuilder.cc index c5e223e35..11ff9f8bb 100644 --- a/src/atlas/grid/detail/grid/GridBuilder.cc +++ b/src/atlas/grid/detail/grid/GridBuilder.cc @@ -145,9 +145,7 @@ GridBuilder::GridBuilder( const std::string& type ) : names_(), type_( type ) { GridBuilder::GridBuilder( const std::string& type, const std::vector& regexes, const std::vector& names ) : - names_( regexes ), - pretty_names_( names ), - type_( type ) { + names_( regexes ), pretty_names_( names ), type_( type ) { pthread_once( &once, init ); eckit::AutoLock lock( local_mutex ); diff --git a/src/atlas/grid/detail/grid/Structured.cc b/src/atlas/grid/detail/grid/Structured.cc index f97327ead..d6c0e7382 100644 --- a/src/atlas/grid/detail/grid/Structured.cc +++ b/src/atlas/grid/detail/grid/Structured.cc @@ -47,10 +47,7 @@ Structured::Structured( XSpace xspace, YSpace yspace, Projection p, Domain domai Structured( Structured::static_type(), xspace, yspace, p, domain ) {} Structured::Structured( const std::string& name, XSpace xspace, YSpace yspace, Projection projection, Domain domain ) : - Grid(), - name_( name ), - xspace_( xspace ), - yspace_( yspace ) { + Grid(), name_( name ), xspace_( xspace ), yspace_( yspace ) { // Copy members projection_ = projection ? projection : Projection(); @@ -217,11 +214,7 @@ void Structured::XSpace::Implementation::Implementation::reserve( idx_t ny ) { template Structured::XSpace::Implementation::Implementation( const std::array& interval, const NVector& N, bool endpoint ) : - ny_( N.size() ), - nx_( N.begin(), N.end() ), - xmin_( ny_, interval[0] ), - xmax_( ny_, interval[1] ), - dx_( ny_ ) { + ny_( N.size() ), nx_( N.begin(), N.end() ), xmin_( ny_, interval[0] ), xmax_( ny_, interval[1] ), dx_( ny_ ) { nxmin_ = std::numeric_limits::max(); nxmax_ = 0; min_ = std::numeric_limits::max(); @@ -246,11 +239,7 @@ Structured::XSpace::Implementation::Implementation( const std::array& Structured::XSpace::Implementation::Implementation( const Spacing& spacing ) : - ny_( 1 ), - nx_( ny_, spacing.size() ), - xmin_( ny_, spacing.min() ), - xmax_( ny_, spacing.max() ), - dx_( ny_ ) { + ny_( 1 ), nx_( ny_, spacing.size() ), xmin_( ny_, spacing.min() ), xmax_( ny_, spacing.max() ), dx_( ny_ ) { const spacing::LinearSpacing& linspace = dynamic_cast( *spacing.get() ); dx_[0] = linspace.step(); nxmax_ = nx_[0]; @@ -260,11 +249,7 @@ Structured::XSpace::Implementation::Implementation( const Spacing& spacing ) : } Structured::XSpace::Implementation::Implementation( const std::vector& spacings ) : - ny_( spacings.size() ), - nx_( ny_ ), - xmin_( ny_ ), - xmax_( ny_ ), - dx_( ny_ ) { + ny_( spacings.size() ), nx_( ny_ ), xmin_( ny_ ), xmax_( ny_ ), dx_( ny_ ) { nxmax_ = 0; nxmin_ = std::numeric_limits::max(); min_ = std::numeric_limits::max(); @@ -340,8 +325,7 @@ namespace { class Normalise { public: Normalise( const RectangularDomain& domain ) : - degrees_( domain.units() == "degrees" ), - normalise_( domain.xmin() ) {} + degrees_( domain.units() == "degrees" ), normalise_( domain.xmin() ) {} double operator()( double x ) const { if ( degrees_ ) { diff --git a/src/atlas/grid/detail/grid/Structured.h b/src/atlas/grid/detail/grid/Structured.h index 8e45710f5..19a197dda 100644 --- a/src/atlas/grid/detail/grid/Structured.h +++ b/src/atlas/grid/detail/grid/Structured.h @@ -62,11 +62,7 @@ class Structured : public Grid { class StructuredIterator : public Base { public: StructuredIterator( const Structured& grid, bool begin = true ) : - grid_( grid ), - ny_( grid_.ny() ), - i_( 0 ), - j_( begin ? 0 : grid_.ny() ), - compute_point{grid_} { + grid_( grid ), ny_( grid_.ny() ), i_( 0 ), j_( begin ? 0 : grid_.ny() ), compute_point{grid_} { if ( j_ != ny_ && grid_.size() ) { compute_point( i_, j_, point_ ); } diff --git a/src/atlas/grid/detail/grid/Unstructured.cc b/src/atlas/grid/detail/grid/Unstructured.cc index 105668709..643ced53a 100644 --- a/src/atlas/grid/detail/grid/Unstructured.cc +++ b/src/atlas/grid/detail/grid/Unstructured.cc @@ -60,8 +60,7 @@ namespace { class Normalise { public: Normalise( const RectangularDomain& domain ) : - degrees_( domain.units() == "degrees" ), - normalise_( domain.xmin(), domain.xmax() ) {} + degrees_( domain.units() == "degrees" ), normalise_( domain.xmin(), domain.xmax() ) {} double operator()( double x ) const { if ( degrees_ ) { @@ -148,8 +147,7 @@ Unstructured::Unstructured( std::vector* pts ) : Grid(), points_( pts ) } Unstructured::Unstructured( std::vector&& pts ) : - Grid(), - points_( new std::vector( std::move( pts ) ) ) { + Grid(), points_( new std::vector( std::move( pts ) ) ) { domain_ = GlobalDomain(); } @@ -158,8 +156,7 @@ Unstructured::Unstructured( const std::vector& pts ) : Grid(), points_( } Unstructured::Unstructured( std::initializer_list initializer_list ) : - Grid(), - points_( new std::vector( initializer_list ) ) { + Grid(), points_( new std::vector( initializer_list ) ) { domain_ = GlobalDomain(); } diff --git a/src/atlas/grid/detail/partitioner/MatchingFunctionSpacePartitioner.cc b/src/atlas/grid/detail/partitioner/MatchingFunctionSpacePartitioner.cc index 2e501b53f..7ba12c145 100644 --- a/src/atlas/grid/detail/partitioner/MatchingFunctionSpacePartitioner.cc +++ b/src/atlas/grid/detail/partitioner/MatchingFunctionSpacePartitioner.cc @@ -28,8 +28,7 @@ MatchingFunctionSpacePartitioner::MatchingFunctionSpacePartitioner( const idx_t } MatchingFunctionSpacePartitioner::MatchingFunctionSpacePartitioner( const FunctionSpace& functionspace ) : - Partitioner( functionspace.nb_partitions() ), - partitioned_( functionspace ) {} + Partitioner( functionspace.nb_partitions() ), partitioned_( functionspace ) {} } // namespace partitioner } // namespace detail diff --git a/src/atlas/grid/detail/partitioner/MatchingMeshPartitioner.cc b/src/atlas/grid/detail/partitioner/MatchingMeshPartitioner.cc index 4492a9759..671882073 100644 --- a/src/atlas/grid/detail/partitioner/MatchingMeshPartitioner.cc +++ b/src/atlas/grid/detail/partitioner/MatchingMeshPartitioner.cc @@ -25,8 +25,7 @@ MatchingMeshPartitioner::MatchingMeshPartitioner( const idx_t nb_partitions ) : } MatchingMeshPartitioner::MatchingMeshPartitioner( const Mesh& mesh ) : - Partitioner( mesh.nb_partitions() ), - prePartitionedMesh_( mesh ) {} + Partitioner( mesh.nb_partitions() ), prePartitionedMesh_( mesh ) {} } // namespace partitioner } // namespace detail diff --git a/src/atlas/interpolation/element/Quad3D.h b/src/atlas/interpolation/element/Quad3D.h index 6a5a1cb06..2626349ec 100644 --- a/src/atlas/interpolation/element/Quad3D.h +++ b/src/atlas/interpolation/element/Quad3D.h @@ -30,10 +30,7 @@ namespace element { class Quad3D { public: Quad3D( const double* x0, const double* x1, const double* x2, const double* x3 ) : - v00( x0 ), - v10( x1 ), - v11( x2 ), - v01( x3 ) {} + v00( x0 ), v10( x1 ), v11( x2 ), v01( x3 ) {} Quad3D( const PointXYZ& x0, const PointXYZ& x1, const PointXYZ& x2, const PointXYZ& x3 ) : Quad3D( x0.data(), x1.data(), x2.data(), x3.data() ) {} diff --git a/src/atlas/interpolation/method/Method.cc b/src/atlas/interpolation/method/Method.cc index ebf97a899..ebdd1f7e7 100644 --- a/src/atlas/interpolation/method/Method.cc +++ b/src/atlas/interpolation/method/Method.cc @@ -69,7 +69,7 @@ void Method::interpolate_field_rank1( const Field& src, Field& tgt ) const { ATLAS_ASSERT( src.contiguous() ); ATLAS_ASSERT( tgt.contiguous() ); - eckit::linalg::Vector v_src( const_cast(array::make_view( src ).data()), src.shape( 0 ) ); + eckit::linalg::Vector v_src( const_cast( array::make_view( src ).data() ), src.shape( 0 ) ); eckit::linalg::Vector v_tgt( array::make_view( tgt ).data(), tgt.shape( 0 ) ); eckit::linalg::LinearAlgebra::backend().spmv( matrix_, v_src, v_tgt ); } @@ -154,32 +154,32 @@ Method::Method( const Method::Config& config ) { void Method::setup( const FunctionSpace& source, const FunctionSpace& target ) { ATLAS_TRACE( "atlas::interpolation::method::Method::setup(FunctionSpace, FunctionSpace)" ); - this->do_setup(source, target); + this->do_setup( source, target ); } void Method::setup( const Grid& source, const Grid& target ) { ATLAS_TRACE( "atlas::interpolation::method::Method::setup(Grid, Grid)" ); - this->do_setup(source, target); + this->do_setup( source, target ); } void Method::setup( const FunctionSpace& source, const Field& target ) { ATLAS_TRACE( "atlas::interpolation::method::Method::setup(FunctionSpace, Field)" ); - this->do_setup(source, target); + this->do_setup( source, target ); } void Method::setup( const FunctionSpace& source, const FieldSet& target ) { ATLAS_TRACE( "atlas::interpolation::method::Method::setup(FunctionSpace, FieldSet)" ); - this->do_setup(source, target); + this->do_setup( source, target ); } void Method::execute( const FieldSet& source, FieldSet& target ) const { ATLAS_TRACE( "atlas::interpolation::method::Method::execute(FieldSet, FieldSet)" ); - this->do_execute(source, target); + this->do_execute( source, target ); } void Method::execute( const Field& source, Field& target ) const { ATLAS_TRACE( "atlas::interpolation::method::Method::execute(Field, Field)" ); - this->do_execute(source, target); + this->do_execute( source, target ); } void Method::do_setup( const FunctionSpace& /*source*/, const Field& /*target*/ ) { diff --git a/src/atlas/mesh/Connectivity.cc b/src/atlas/mesh/Connectivity.cc index 5c37b1c37..fc146996e 100644 --- a/src/atlas/mesh/Connectivity.cc +++ b/src/atlas/mesh/Connectivity.cc @@ -443,11 +443,7 @@ array::ArrayShape{blocks})), //------------------------------------------------------------------------------------------------------ MultiBlockConnectivityImpl::MultiBlockConnectivityImpl( const std::string& name ) : - IrregularConnectivityImpl( name ), - blocks_( 0 ), - block_displs_( 1 ), - block_cols_( 1 ), - block_( 0 ) { + IrregularConnectivityImpl( name ), blocks_( 0 ), block_displs_( 1 ), block_cols_( 1 ), block_( 0 ) { block_displs_( 0 ) = 0; } diff --git a/src/atlas/mesh/Elements.cc b/src/atlas/mesh/Elements.cc index d3339af59..663aa196d 100644 --- a/src/atlas/mesh/Elements.cc +++ b/src/atlas/mesh/Elements.cc @@ -29,9 +29,7 @@ void Elements::rebuild() { } Elements::Elements( HybridElements& elements, idx_t type_idx ) : - owns_( false ), - hybrid_elements_( &elements ), - type_idx_( type_idx ) { + owns_( false ), hybrid_elements_( &elements ), type_idx_( type_idx ) { rebuild(); } diff --git a/src/atlas/mesh/IsGhostNode.h b/src/atlas/mesh/IsGhostNode.h index 37482a08b..3c51847bf 100644 --- a/src/atlas/mesh/IsGhostNode.h +++ b/src/atlas/mesh/IsGhostNode.h @@ -22,8 +22,7 @@ namespace mesh { class IsGhostNode { public: IsGhostNode( const mesh::Nodes& nodes ) : - flags_( array::make_view( nodes.flags() ) ), - ghost_( array::make_view( nodes.ghost() ) ) {} + flags_( array::make_view( nodes.flags() ) ), ghost_( array::make_view( nodes.ghost() ) ) {} bool operator()( idx_t idx ) const { return Nodes::Topology::check( flags_( idx ), Nodes::Topology::GHOST ); } diff --git a/src/atlas/mesh/PartitionPolygon.cc b/src/atlas/mesh/PartitionPolygon.cc index 86e0bff19..7cdf4540b 100644 --- a/src/atlas/mesh/PartitionPolygon.cc +++ b/src/atlas/mesh/PartitionPolygon.cc @@ -57,9 +57,7 @@ util::Polygon::edge_set_t compute_edges( const detail::MeshImpl& mesh, idx_t hal } // namespace PartitionPolygon::PartitionPolygon( const detail::MeshImpl& mesh, idx_t halo ) : - util::Polygon( compute_edges( mesh, halo ) ), - mesh_( mesh ), - halo_( halo ) {} + util::Polygon( compute_edges( mesh, halo ) ), mesh_( mesh ), halo_( halo ) {} size_t PartitionPolygon::footprint() const { size_t size = sizeof( *this ); diff --git a/src/atlas/mesh/actions/BuildCellCentres.cc b/src/atlas/mesh/actions/BuildCellCentres.cc index 9d26cc5b1..7539c3d4b 100644 --- a/src/atlas/mesh/actions/BuildCellCentres.cc +++ b/src/atlas/mesh/actions/BuildCellCentres.cc @@ -28,9 +28,7 @@ namespace actions { //---------------------------------------------------------------------------------------------------------------------- BuildCellCentres::BuildCellCentres( const std::string& field_name, bool force_recompute ) : - field_name_( field_name ), - force_recompute_( force_recompute ), - flatten_virtual_elements_( true ) {} + field_name_( field_name ), force_recompute_( force_recompute ), flatten_virtual_elements_( true ) {} BuildCellCentres::BuildCellCentres( eckit::Configuration& config ) : field_name_( config.getString( "name", "centre" ) ), diff --git a/src/atlas/mesh/actions/BuildXYZField.cc b/src/atlas/mesh/actions/BuildXYZField.cc index 4c5831b88..1773ae92b 100644 --- a/src/atlas/mesh/actions/BuildXYZField.cc +++ b/src/atlas/mesh/actions/BuildXYZField.cc @@ -25,8 +25,7 @@ namespace actions { //---------------------------------------------------------------------------------------------------------------------- BuildXYZField::BuildXYZField( const std::string& name, bool force_recompute ) : - name_( name ), - force_recompute_( force_recompute ) {} + name_( name ), force_recompute_( force_recompute ) {} Field& BuildXYZField::operator()( Mesh& mesh ) const { return operator()( mesh.nodes() ); diff --git a/src/atlas/parallel/GatherScatter.h b/src/atlas/parallel/GatherScatter.h index 56f0ba0f7..d39519a91 100644 --- a/src/atlas/parallel/GatherScatter.h +++ b/src/atlas/parallel/GatherScatter.h @@ -32,8 +32,7 @@ class Field { Field() {} Field( const DATA_TYPE data_[], const idx_t var_strides_[], const idx_t var_shape_[], const idx_t var_rank_ ) : - data( const_cast( data_ ) ), - var_rank( var_rank_ ) { + data( const_cast( data_ ) ), var_rank( var_rank_ ) { var_strides.assign( var_strides_, var_strides_ + var_rank_ ); var_shape.assign( var_shape_, var_shape_ + var_rank_ ); } diff --git a/src/atlas/parallel/mpi/mpi.h b/src/atlas/parallel/mpi/mpi.h index c3bc7c249..68ae606a6 100644 --- a/src/atlas/parallel/mpi/mpi.h +++ b/src/atlas/parallel/mpi/mpi.h @@ -17,8 +17,10 @@ namespace atlas { namespace mpi { -inline const eckit::mpi::Comm& comm() { - static const eckit::mpi::Comm& _comm = eckit::mpi::comm(); +using Comm = eckit::mpi::Comm; + +inline const Comm& comm() { + static const Comm& _comm = eckit::mpi::comm(); return _comm; } diff --git a/src/atlas/projection/detail/LonLatProjection.cc b/src/atlas/projection/detail/LonLatProjection.cc index 09eae7c0f..a88859023 100644 --- a/src/atlas/projection/detail/LonLatProjection.cc +++ b/src/atlas/projection/detail/LonLatProjection.cc @@ -27,8 +27,7 @@ namespace detail { template LonLatProjectionT::LonLatProjectionT( const eckit::Parametrisation& config ) : - ProjectionImpl(), - rotation_( config ) {} + ProjectionImpl(), rotation_( config ) {} template <> void LonLatProjectionT::xy2lonlat( double[] ) const {} diff --git a/src/atlas/projection/detail/MercatorProjection.cc b/src/atlas/projection/detail/MercatorProjection.cc index ba95815a8..3f7f813e5 100644 --- a/src/atlas/projection/detail/MercatorProjection.cc +++ b/src/atlas/projection/detail/MercatorProjection.cc @@ -44,8 +44,7 @@ namespace detail { // constructors template MercatorProjectionT::MercatorProjectionT( const eckit::Parametrisation& params ) : - ProjectionImpl(), - rotation_( params ) { + ProjectionImpl(), rotation_( params ) { // check presence of radius if ( !params.get( "radius", radius_ ) ) { radius_ = util::Earth::radius(); diff --git a/src/atlas/projection/detail/ProjProjection.cc b/src/atlas/projection/detail/ProjProjection.cc index 01c6659af..abb05e166 100644 --- a/src/atlas/projection/detail/ProjProjection.cc +++ b/src/atlas/projection/detail/ProjProjection.cc @@ -67,9 +67,7 @@ std::string geocentric_str( PJ_CONTEXT* ctxt, const std::string& proj_str ) { } // namespace ProjProjection::ProjProjection( const eckit::Parametrisation& param ) : - sourceToTarget_( nullptr ), - sourceToGeocentric_( nullptr ), - context_( PJ_DEFAULT_CTX ) { + sourceToTarget_( nullptr ), sourceToGeocentric_( nullptr ), context_( PJ_DEFAULT_CTX ) { ATLAS_ASSERT( param.get( "proj", proj_ ) && !proj_.empty() ); source_encoded_ = param.get( "proj_source", source_ = source_str( context_, proj_ ) ); geocentric_encoded_ = param.get( "proj_geocentric", geocentric_ = geocentric_str( context_, proj_ ) ); diff --git a/src/atlas/projection/detail/ProjectionImpl.cc b/src/atlas/projection/detail/ProjectionImpl.cc index b82332f3d..1ff4d8c13 100644 --- a/src/atlas/projection/detail/ProjectionImpl.cc +++ b/src/atlas/projection/detail/ProjectionImpl.cc @@ -58,8 +58,7 @@ struct DerivateBackwards final : ProjectionImpl::Derivate { struct DerivateCentral final : ProjectionImpl::Derivate { DerivateCentral( const ProjectionImpl& p, PointXY A, PointXY B, double h ) : - Derivate( p, A, B, h ), - H2_{PointXY::mul( H_, 0.5 )} {} + Derivate( p, A, B, h ), H2_{PointXY::mul( H_, 0.5 )} {} const PointXY H2_; PointLonLat d( PointXY P ) const override { PointXY A( xy2lonlat( PointXY::sub( P, H2_ ) ) ); diff --git a/src/atlas/projection/detail/SchmidtProjection.cc b/src/atlas/projection/detail/SchmidtProjection.cc index ed5d782cd..0bcea5e4d 100644 --- a/src/atlas/projection/detail/SchmidtProjection.cc +++ b/src/atlas/projection/detail/SchmidtProjection.cc @@ -35,8 +35,7 @@ namespace detail { // constructor template SchmidtProjectionT::SchmidtProjectionT( const eckit::Parametrisation& params ) : - ProjectionImpl(), - rotation_( params ) { + ProjectionImpl(), rotation_( params ) { if ( !params.get( "stretching_factor", c_ ) ) { throw_Exception( "stretching_factor missing in Params", Here() ); } diff --git a/src/atlas/runtime/trace/TraceT.h b/src/atlas/runtime/trace/TraceT.h index cf51a345d..86e79ef3c 100644 --- a/src/atlas/runtime/trace/TraceT.h +++ b/src/atlas/runtime/trace/TraceT.h @@ -101,8 +101,7 @@ inline std::string TraceT::formatTitle( const std::string& _title ) template inline TraceT::TraceT( const CodeLocation& loc, const std::string& title ) : - loc_( loc ), - title_( formatTitle( title ) ) { + loc_( loc ), title_( formatTitle( title ) ) { start(); } @@ -113,9 +112,7 @@ inline TraceT::TraceT( const CodeLocation& loc ) : loc_( loc ), tit template inline TraceT::TraceT( const CodeLocation& loc, const std::string& title, const Labels& labels ) : - loc_( loc ), - title_( title ), - labels_( labels ) { + loc_( loc ), title_( title ), labels_( labels ) { start(); } diff --git a/src/atlas/trans/Cache.cc b/src/atlas/trans/Cache.cc index 00a050358..dc9406170 100644 --- a/src/atlas/trans/Cache.cc +++ b/src/atlas/trans/Cache.cc @@ -53,19 +53,13 @@ LegendreCache::LegendreCache( const void* address, size_t size ) : Cache( std::make_shared( address, size ) ) {} Cache::Cache( const std::shared_ptr& legendre ) : - trans_( nullptr ), - legendre_( legendre ), - fft_( new EmptyCacheEntry() ) {} + trans_( nullptr ), legendre_( legendre ), fft_( new EmptyCacheEntry() ) {} Cache::Cache( const std::shared_ptr& legendre, const std::shared_ptr& fft ) : - trans_( nullptr ), - legendre_( legendre ), - fft_( fft ) {} + trans_( nullptr ), legendre_( legendre ), fft_( fft ) {} Cache::Cache( const TransImpl* trans ) : - trans_( trans ), - legendre_( new EmptyCacheEntry() ), - fft_( new EmptyCacheEntry() ) {} + trans_( trans ), legendre_( new EmptyCacheEntry() ), fft_( new EmptyCacheEntry() ) {} Cache::Cache() : trans_( nullptr ), legendre_( new EmptyCacheEntry() ), fft_( new EmptyCacheEntry() ) {} diff --git a/src/atlas/trans/detail/TransFactory.cc b/src/atlas/trans/detail/TransFactory.cc index 2285d7d69..5770eea9d 100644 --- a/src/atlas/trans/detail/TransFactory.cc +++ b/src/atlas/trans/detail/TransFactory.cc @@ -143,9 +143,7 @@ class TransBackend { TransFactory::TransFactory( const std::string& name, const std::string& backend ) : - Factory( name ), - name_( name ), - backend_( backend ) { + Factory( name ), name_( name ), backend_( backend ) { TransBackend::instance().add( backend ); } diff --git a/src/atlas/trans/ifs/LegendreCacheCreatorIFS.cc b/src/atlas/trans/ifs/LegendreCacheCreatorIFS.cc index 1df42da6b..d8053e577 100644 --- a/src/atlas/trans/ifs/LegendreCacheCreatorIFS.cc +++ b/src/atlas/trans/ifs/LegendreCacheCreatorIFS.cc @@ -124,9 +124,7 @@ bool LegendreCacheCreatorIFS::supported() const { LegendreCacheCreatorIFS::LegendreCacheCreatorIFS( const Grid& grid, int truncation, const eckit::Configuration& config ) : - grid_( grid ), - truncation_( truncation ), - config_( config ) {} + grid_( grid ), truncation_( truncation ), config_( config ) {} void LegendreCacheCreatorIFS::create( const std::string& path ) const { Trans( grid_, truncation_, config_ | option::type( "ifs" ) | option::write_legendre( path ) ); diff --git a/src/atlas/trans/ifs/TransIFS.cc b/src/atlas/trans/ifs/TransIFS.cc index 26b3f5fac..4fb482acc 100644 --- a/src/atlas/trans/ifs/TransIFS.cc +++ b/src/atlas/trans/ifs/TransIFS.cc @@ -322,9 +322,7 @@ struct PackNodeColumns { size_t f; PackNodeColumns( LocalView& rgpview, const NodeColumns& fs ) : - rgpview_( rgpview ), - is_ghost( fs.nodes() ), - f( 0 ) {} + rgpview_( rgpview ), is_ghost( fs.nodes() ), f( 0 ) {} void operator()( const Field& field, idx_t components = 0 ) { switch ( field.rank() ) { @@ -481,9 +479,7 @@ struct UnpackNodeColumns { size_t f; UnpackNodeColumns( const LocalView& rgpview, const NodeColumns& fs ) : - rgpview_( rgpview ), - is_ghost( fs.nodes() ), - f( 0 ) {} + rgpview_( rgpview ), is_ghost( fs.nodes() ), f( 0 ) {} void operator()( Field& field, int components = 0 ) { switch ( field.rank() ) { @@ -654,9 +650,7 @@ void TransIFS::assertCompatibleDistributions( const FunctionSpace& gp, const Fun } TransIFS::TransIFS( const Cache& cache, const Grid& grid, const long truncation, const eckit::Configuration& config ) : - grid_( grid ), - cache_( cache.legendre().data() ), - cachesize_( cache.legendre().size() ) { + grid_( grid ), cache_( cache.legendre().data() ), cachesize_( cache.legendre().size() ) { ATLAS_ASSERT( grid.domain().global() ); ATLAS_ASSERT( not grid.projection() ); ctor( grid, truncation, config ); diff --git a/src/atlas/trans/local/LegendreCacheCreatorLocal.cc b/src/atlas/trans/local/LegendreCacheCreatorLocal.cc index 5d38b22ae..f6493f862 100644 --- a/src/atlas/trans/local/LegendreCacheCreatorLocal.cc +++ b/src/atlas/trans/local/LegendreCacheCreatorLocal.cc @@ -122,9 +122,7 @@ LegendreCacheCreatorLocal::~LegendreCacheCreatorLocal() = default; LegendreCacheCreatorLocal::LegendreCacheCreatorLocal( const Grid& grid, int truncation, const eckit::Configuration& config ) : - grid_( grid ), - truncation_( truncation ), - config_( config ) {} + grid_( grid ), truncation_( truncation ), config_( config ) {} bool LegendreCacheCreatorLocal::supported() const { if ( not StructuredGrid( grid_ ) ) { diff --git a/src/atlas/util/Factory.cc b/src/atlas/util/Factory.cc index 1bd7ee66f..a58db1edc 100644 --- a/src/atlas/util/Factory.cc +++ b/src/atlas/util/Factory.cc @@ -98,8 +98,7 @@ void FactoryRegistry::list( std::ostream& out ) const { //---------------------------------------------------------------------------------------------------------------------- FactoryBase::FactoryBase( FactoryRegistry& registry, const std::string& builder ) : - registry_( registry ), - builder_( builder ) { + registry_( registry ), builder_( builder ) { if ( not builder_.empty() ) { registry_.add( builder, this ); } diff --git a/src/atlas/util/Unique.h b/src/atlas/util/Unique.h index ac5665989..6d5dc8e92 100644 --- a/src/atlas/util/Unique.h +++ b/src/atlas/util/Unique.h @@ -198,14 +198,12 @@ inline uidx_t unique_lonlat( const double elem_lonlat[], size_t npts ) { } inline UniqueLonLat::UniqueLonLat( const Mesh& mesh ) : - nodes( &mesh.nodes() ), - lonlat( array::make_view( nodes->lonlat() ) ) { + nodes( &mesh.nodes() ), lonlat( array::make_view( nodes->lonlat() ) ) { update(); } inline UniqueLonLat::UniqueLonLat( const mesh::Nodes& _nodes ) : - nodes( &_nodes ), - lonlat( array::make_view( nodes->lonlat() ) ) { + nodes( &_nodes ), lonlat( array::make_view( nodes->lonlat() ) ) { update(); } diff --git a/src/sandbox/interpolation/PartitionedMesh.cc b/src/sandbox/interpolation/PartitionedMesh.cc index 094e51c9f..6cd23450e 100644 --- a/src/sandbox/interpolation/PartitionedMesh.cc +++ b/src/sandbox/interpolation/PartitionedMesh.cc @@ -20,8 +20,7 @@ namespace interpolation { PartitionedMesh::PartitionedMesh( const std::string& partitioner, const std::string& generator, bool generatorTriangulate, double generatorAngle ) : - optionPartitioner_( partitioner ), - optionGenerator_( generator ) { + optionPartitioner_( partitioner ), optionGenerator_( generator ) { generatorParams_.set( "three_dimensional", false ); generatorParams_.set( "patch_pole", true ); generatorParams_.set( "include_pole", false ); diff --git a/src/tests/functionspace/CMakeLists.txt b/src/tests/functionspace/CMakeLists.txt index 772045abf..370967763 100644 --- a/src/tests/functionspace/CMakeLists.txt +++ b/src/tests/functionspace/CMakeLists.txt @@ -43,6 +43,14 @@ ecbuild_add_test( TARGET atlas_test_structuredcolumns CONDITION eckit_HAVE_MPI ) +ecbuild_add_test( TARGET atlas_test_polygons + SOURCES test_polygons.cc + LIBS atlas + MPI 4 + ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} + CONDITION eckit_HAVE_MPI +) + ecbuild_add_test( TARGET atlas_test_pointcloud SOURCES test_pointcloud.cc LIBS atlas diff --git a/src/tests/functionspace/test_polygons.cc b/src/tests/functionspace/test_polygons.cc new file mode 100644 index 000000000..aeb2ea55c --- /dev/null +++ b/src/tests/functionspace/test_polygons.cc @@ -0,0 +1,79 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include "atlas/array/ArrayView.h" +#include "atlas/array/MakeView.h" +#include "atlas/field/Field.h" +#include "atlas/functionspace/StructuredColumns.h" +#include "atlas/grid/Partitioner.h" +#include "atlas/grid/StructuredGrid.h" +#include "atlas/parallel/mpi/mpi.h" +#include "atlas/util/LonLatPolygon.h" + +#include "tests/AtlasTestEnvironment.h" + +using namespace eckit; +using namespace atlas::functionspace; +using namespace atlas::util; + +namespace atlas { +namespace test { + +//----------------------------------------------------------------------------- + +CASE( "test_polygons" ) { + size_t root = 0; + std::string gridname = eckit::Resource( "--grid", "O32" ); + Grid grid( gridname ); + functionspace::StructuredColumns fs( grid ); + + auto lonlat_polygons = []( const functionspace::StructuredColumns& fs ) { + const auto& polygons = fs.polygons(); + std::vector> ll_poly( polygons.size() ); + for ( idx_t p = 0; p < polygons.size(); ++p ) { + ll_poly[p].reset( new util::LonLatPolygon( *polygons[p] ) ); + } + return ll_poly; + }; + + auto polygons = lonlat_polygons( fs ); + + auto points = std::vector{ + {45., 75.}, {90., 75.}, {135., 75.}, {180., 75.}, {225., 75.}, {270., 75.}, {315., 75.}, + {45., 15.}, {90., 15.}, {135., 15.}, {180., 15.}, {225., 15.}, {270., 15.}, {315., 15.}, + {45., -75.}, {90., -75.}, {135., -75.}, {180., -75.}, {225., -75.}, {270., -75.}, {315., -75.}, + }; + + std::vector part( points.size() ); + for ( idx_t n = 0; n < points.size(); ++n ) { + Log::debug() << n << " " << points[n]; + for ( idx_t p = 0; p < polygons.size(); ++p ) { + if ( polygons[p]->contains( points[n] ) ) { + Log::debug() << " : " << p; + part[n] = p; + } + } + Log::debug() << std::endl; + } + + if ( mpi::size() == 4 ) { + auto expected_part = std::vector{0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3}; + EXPECT( part == expected_part ); + } +} + +//----------------------------------------------------------------------------- + +} // namespace test +} // namespace atlas + +int main( int argc, char** argv ) { + return atlas::test::run( argc, argv ); +} diff --git a/src/tests/interpolation/CubicInterpolationPrototype.h b/src/tests/interpolation/CubicInterpolationPrototype.h index c048c8aaa..5a0e62e3b 100644 --- a/src/tests/interpolation/CubicInterpolationPrototype.h +++ b/src/tests/interpolation/CubicInterpolationPrototype.h @@ -201,8 +201,7 @@ class CubicHorizontalInterpolation { public: CubicHorizontalInterpolation( const functionspace::StructuredColumns& fs ) : - fs_( fs ), - compute_horizontal_stencil_( fs.grid(), stencil_width() ) {} + fs_( fs ), compute_horizontal_stencil_( fs.grid(), stencil_width() ) {} template void compute_weights( const double x, const double y, weights_t& weights ) const { grid::HorizontalStencil stencil; @@ -368,9 +367,7 @@ class Cubic3DInterpolation { using Stencil = grid::Stencil3D<4>; Cubic3DInterpolation( const functionspace::StructuredColumns& fs ) : - fs_( fs ), - horizontal_interpolation_( fs ), - vertical_interpolation_( fs.vertical() ) {} + fs_( fs ), horizontal_interpolation_( fs ), vertical_interpolation_( fs.vertical() ) {} template void compute_stencil( const double x, const double y, const double z, stencil_t& stencil ) const { diff --git a/src/tests/mesh/test_shapefunctions.cc b/src/tests/mesh/test_shapefunctions.cc index 97c5e9027..c4512c8f4 100644 --- a/src/tests/mesh/test_shapefunctions.cc +++ b/src/tests/mesh/test_shapefunctions.cc @@ -566,8 +566,7 @@ class Column : public ElementType // Really is a Elementtype in 3D { public: Column( const ElementType::Ptr& horizontal, const ElementType::Ptr& vertical ) : - horizontal_( horizontal ), - vertical_( vertical ) { + horizontal_( horizontal ), vertical_( vertical ) { shape_function_ = ShapeFunction::Ptr( new ShapeFunction ); } size_t N() const { return npts() * nlev(); } diff --git a/src/tests/parallel/test_haloexchange.cc b/src/tests/parallel/test_haloexchange.cc index f7ec70475..6d0635161 100644 --- a/src/tests/parallel/test_haloexchange.cc +++ b/src/tests/parallel/test_haloexchange.cc @@ -235,17 +235,18 @@ void test_rank1_strided_v1( Fixture& f ) { // (i.e. we are only selecting and exchanging the first component of the // field) - std::unique_ptr arr( array::Array::wrap( arrv_t.data(), - array::ArraySpec { - array::make_shape( f.N, 1 ), + std::unique_ptr arr( array::Array::wrap( + arrv_t.data(), + array::ArraySpec { + array::make_shape( f.N, 1 ), #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA - array::make_strides( 32, 1 ) - } + array::make_strides( 32, 1 ) + } #else - array::make_strides( 2, 1 ) - } + array::make_strides( 2, 1 ) + } #endif - ) ); + ) ); arr->syncHostDevice(); @@ -290,14 +291,15 @@ void test_rank1_strided_v2( Fixture& f ) { // (i.e. we are only selecting and exchanging the first component of the // field) - std::unique_ptr arr( array::Array::wrap( &( arrv_t( 0, 1 ) ), array::ArraySpec { - array::make_shape( f.N, 1 ), + std::unique_ptr arr( array::Array::wrap( + &( arrv_t( 0, 1 ) ), array::ArraySpec { + array::make_shape( f.N, 1 ), #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA - array::make_strides( 32, 1 ) + array::make_strides( 32, 1 ) #else array::make_strides(2, 1) #endif - } ) ); + } ) ); f.halo_exchange.execute( *arr, false ); @@ -374,14 +376,15 @@ void test_rank2_l1( Fixture& f ) { } arr_t.syncHostDevice(); - std::unique_ptr arr( array::Array::wrap( arrv_t.data(), array::ArraySpec { - array::make_shape( f.N, 1, 2 ), + std::unique_ptr arr( array::Array::wrap( + arrv_t.data(), array::ArraySpec { + array::make_shape( f.N, 1, 2 ), #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA - array::make_strides( 96, 32, 1 ) + array::make_strides( 96, 32, 1 ) #else array::make_strides(6, 2, 1) #endif - } ) ); + } ) ); arr_t.syncHostDevice(); @@ -437,14 +440,15 @@ void test_rank2_l2_v2( Fixture& f ) { } } - std::unique_ptr arr( array::Array::wrap( &arrv_t( 0, 1, 1 ), array::ArraySpec { - array::make_shape( f.N, 1, 1 ), + std::unique_ptr arr( array::Array::wrap( + &arrv_t( 0, 1, 1 ), array::ArraySpec { + array::make_shape( f.N, 1, 1 ), #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA - array::make_strides( 192, 32, 1 ) + array::make_strides( 192, 32, 1 ) #else array::make_strides(6, 2, 1) #endif - } ) ); + } ) ); f.halo_exchange.execute( *arr, f.on_device_ ); @@ -496,14 +500,15 @@ void test_rank2_v2( Fixture& f ) { } } - std::unique_ptr arr( array::Array::wrap( &arrv_t( 0, 0, 1 ), array::ArraySpec { - array::make_shape( f.N, 3, 1 ), + std::unique_ptr arr( array::Array::wrap( + &arrv_t( 0, 0, 1 ), array::ArraySpec { + array::make_shape( f.N, 3, 1 ), #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA - array::make_strides( 192, 32, 2 ) + array::make_strides( 192, 32, 2 ) #else array::make_strides(6, 2, 2) #endif - } ) ); + } ) ); f.halo_exchange.execute( *arr, f.on_device_ ); From 95894c3d55d2b3a2fd92b21cb55af60d431fe49e Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 19 Mar 2020 15:50:59 +0000 Subject: [PATCH 009/145] ATLAS-270 Refine API for multiple PartitionPolygons --- src/atlas/CMakeLists.txt | 1 + src/atlas/functionspace/FunctionSpace.cc | 2 +- src/atlas/functionspace/FunctionSpace.h | 6 +- .../functionspace/detail/FunctionSpaceImpl.cc | 2 +- .../functionspace/detail/FunctionSpaceImpl.h | 3 +- .../functionspace/detail/StructuredColumns.cc | 5 +- .../functionspace/detail/StructuredColumns.h | 4 +- src/atlas/util/LonLatPolygon.h | 28 ++++--- src/atlas/util/Polygon.h | 16 ++-- src/atlas/util/VectorOfAbstract.h | 79 +++++++++++++++++++ src/tests/functionspace/test_polygons.cc | 49 +++++++++--- 11 files changed, 154 insertions(+), 41 deletions(-) create mode 100644 src/atlas/util/VectorOfAbstract.h diff --git a/src/atlas/CMakeLists.txt b/src/atlas/CMakeLists.txt index 5af8a978b..c48cffcc0 100644 --- a/src/atlas/CMakeLists.txt +++ b/src/atlas/CMakeLists.txt @@ -598,6 +598,7 @@ util/SphericalPolygon.cc util/SphericalPolygon.h util/UnitSphere.h util/vector.h +util/VectorOfAbstract.h util/detail/BlackMagic.h util/detail/Cache.h util/detail/Debug.h diff --git a/src/atlas/functionspace/FunctionSpace.cc b/src/atlas/functionspace/FunctionSpace.cc index 4fedd77a5..51489f014 100644 --- a/src/atlas/functionspace/FunctionSpace.cc +++ b/src/atlas/functionspace/FunctionSpace.cc @@ -66,7 +66,7 @@ const util::PartitionPolygon& FunctionSpace::polygon( idx_t halo ) const { return get()->polygon( halo ); } -const std::vector& FunctionSpace::polygons() const { +const util::PartitionPolygons& FunctionSpace::polygons() const { return get()->polygons(); } diff --git a/src/atlas/functionspace/FunctionSpace.h b/src/atlas/functionspace/FunctionSpace.h index 7f89bcd12..638c986ea 100644 --- a/src/atlas/functionspace/FunctionSpace.h +++ b/src/atlas/functionspace/FunctionSpace.h @@ -11,7 +11,6 @@ #pragma once #include -#include #include "atlas/library/config.h" #include "atlas/util/ObjectHandle.h" @@ -28,7 +27,8 @@ class FunctionSpaceImpl; } namespace util { class PartitionPolygon; -} +class PartitionPolygons; +} // namespace util } // namespace atlas namespace atlas { @@ -61,7 +61,7 @@ class FunctionSpace : DOXYGEN_HIDE( public util::ObjectHandle& polygons() const; + const util::PartitionPolygons& polygons() const; idx_t nb_partitions() const; diff --git a/src/atlas/functionspace/detail/FunctionSpaceImpl.cc b/src/atlas/functionspace/detail/FunctionSpaceImpl.cc index 682b904c0..b19d3af72 100644 --- a/src/atlas/functionspace/detail/FunctionSpaceImpl.cc +++ b/src/atlas/functionspace/detail/FunctionSpaceImpl.cc @@ -49,7 +49,7 @@ const util::PartitionPolygon& FunctionSpaceImpl::polygon( idx_t halo ) const { throw_Exception( "polygon() not implemented in derived class", Here() ); } -const std::vector& FunctionSpaceImpl::polygons() const { +const util::PartitionPolygons& FunctionSpaceImpl::polygons() const { throw_Exception( "polygons() not implemented in derived class", Here() ); } diff --git a/src/atlas/functionspace/detail/FunctionSpaceImpl.h b/src/atlas/functionspace/detail/FunctionSpaceImpl.h index c586c33cf..e6b93b249 100644 --- a/src/atlas/functionspace/detail/FunctionSpaceImpl.h +++ b/src/atlas/functionspace/detail/FunctionSpaceImpl.h @@ -28,6 +28,7 @@ class Field; namespace util { class Metadata; class PartitionPolygon; +class PartitionPolygons; } // namespace util } // namespace atlas @@ -79,7 +80,7 @@ class FunctionSpaceImpl : public util::Object { virtual const util::PartitionPolygon& polygon( idx_t halo = 0 ) const; - virtual const std::vector& polygons() const; + virtual const util::PartitionPolygons& polygons() const; private: util::Metadata* metadata_; diff --git a/src/atlas/functionspace/detail/StructuredColumns.cc b/src/atlas/functionspace/detail/StructuredColumns.cc index efecb4bda..cea7d62c0 100644 --- a/src/atlas/functionspace/detail/StructuredColumns.cc +++ b/src/atlas/functionspace/detail/StructuredColumns.cc @@ -483,7 +483,7 @@ class SimplePolygon : public util::PartitionPolygon { }; -const std::vector& StructuredColumns::polygons() const { +const atlas::util::PartitionPolygons& StructuredColumns::polygons() const { if ( polygons_.size() ) { return polygons_; } @@ -530,9 +530,6 @@ const std::vector& StructuredColumns::polygons() const // Destructor // ---------------------------------------------------------------------------- StructuredColumns::~StructuredColumns() { - for ( auto p : polygons_ ) { - delete p; - } delete grid_; } // ---------------------------------------------------------------------------- diff --git a/src/atlas/functionspace/detail/StructuredColumns.h b/src/atlas/functionspace/detail/StructuredColumns.h index fdbcbe99e..8272a373a 100644 --- a/src/atlas/functionspace/detail/StructuredColumns.h +++ b/src/atlas/functionspace/detail/StructuredColumns.h @@ -154,7 +154,7 @@ class StructuredColumns : public FunctionSpaceImpl { const util::PartitionPolygon& polygon( idx_t halo = 0 ) const override; - const std::vector& polygons() const override; + const util::PartitionPolygons& polygons() const override; idx_t nb_partitions() const override { return nb_partitions_; } @@ -201,7 +201,7 @@ class StructuredColumns : public FunctionSpaceImpl { mutable util::ObjectHandle checksum_; mutable util::ObjectHandle halo_exchange_; mutable std::unique_ptr polygon_; - mutable std::vector polygons_; + mutable util::PartitionPolygons polygons_; Field field_xy_; Field field_partition_; diff --git a/src/atlas/util/LonLatPolygon.h b/src/atlas/util/LonLatPolygon.h index 9579272c4..8a26008d0 100644 --- a/src/atlas/util/LonLatPolygon.h +++ b/src/atlas/util/LonLatPolygon.h @@ -34,17 +34,11 @@ class LonLatPolygon : public PolygonCoordinates { template = 0> LonLatPolygon( const PointContainer& points, bool removeAlignedPoints = true ); - // -- Overridden methods - - /* - * Point-in-polygon test based on winding number - * @note reference Inclusion of a Point - * in a Polygon - * @param[in] P given point - * @return if point is in polygon - */ - bool contains( const Point2& P ) const; + /// @brief Point-in-polygon test based on winding number + /// @note reference Inclusion of a Point in a Polygon + /// @param[in] P given point + /// @return if point is in polygon + bool contains( const Point2& P ) const override; private: PointLonLat centroid_; @@ -55,5 +49,17 @@ class LonLatPolygon : public PolygonCoordinates { //------------------------------------------------------------------------------------------------------ +class LonLatPolygons : public VectorOfAbstract { +public: + LonLatPolygons( const PartitionPolygons& partition_polygons ) { + reserve( partition_polygons.size() ); + for ( auto& partition_polygon : partition_polygons ) { + emplace_back( new LonLatPolygon( partition_polygon ) ); + } + } +}; + +//------------------------------------------------------------------------------------------------------ + } // namespace util } // namespace atlas diff --git a/src/atlas/util/Polygon.h b/src/atlas/util/Polygon.h index 5249477e9..af286a03c 100644 --- a/src/atlas/util/Polygon.h +++ b/src/atlas/util/Polygon.h @@ -16,6 +16,7 @@ #pragma once #include +#include #include #include #include @@ -24,6 +25,7 @@ #include "atlas/util/Config.h" #include "atlas/util/Object.h" #include "atlas/util/Point.h" +#include "atlas/util/VectorOfAbstract.h" namespace eckit { class PathName; @@ -111,6 +113,10 @@ class PartitionPolygon : public Polygon, util::Object { //------------------------------------------------------------------------------------------------------ +class PartitionPolygons : public VectorOfAbstract {}; + +//------------------------------------------------------------------------------------------------------ + class PolygonCoordinates { public: // -- Constructors @@ -129,16 +135,16 @@ class PolygonCoordinates { // -- Methods - /* - * Point-in-partition test - * @param[in] P given point - * @return if point is in polygon - */ + /// @brief Point-in-partition test + /// @param[in] P given point + /// @return if point is in polygon virtual bool contains( const Point2& P ) const = 0; const Point2& coordinatesMax() const; const Point2& coordinatesMin() const; + idx_t size() const { return coordinates_.size(); } + protected: // -- Members diff --git a/src/atlas/util/VectorOfAbstract.h b/src/atlas/util/VectorOfAbstract.h new file mode 100644 index 000000000..8e0e9634e --- /dev/null +++ b/src/atlas/util/VectorOfAbstract.h @@ -0,0 +1,79 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +/// @file Polygon.h +/// @author Pedro Maciel +/// @author Willem Deconinck +/// @date September 2017 + +#pragma once + +#include +#include + +#include "atlas/library/config.h" + +namespace atlas { +namespace util { + +//------------------------------------------------------------------------------------------------------ + +template +class DereferenceIterator : public BaseIterator { +public: + using value_type = typename BaseIterator::value_type::element_type; + using pointer = value_type*; + using reference = value_type&; + + DereferenceIterator( const BaseIterator& other ) : BaseIterator( other ) {} + + reference operator*() const { return *( this->BaseIterator::operator*() ); } + pointer operator->() const { return this->BaseIterator::operator*().get(); } + reference operator[]( size_t n ) const { return *( this->BaseIterator::operator[]( n ) ); } +}; + +//------------------------------------------------------------------------------------------------------ + +template +DereferenceIterator make_dereference_iterator( Iterator t ) { + return DereferenceIterator( t ); +} + +//------------------------------------------------------------------------------------------------------ + +template +class VectorOfAbstract { +public: + using value_type = Abstract; + using container_type = std::vector >; + using const_reference = const value_type&; + using reference = const_reference; + using const_iterator = DereferenceIterator; + +public: + const_iterator begin() const { return make_dereference_iterator( container_.begin() ); } + const_iterator end() const { return make_dereference_iterator( container_.end() ); } + const_reference operator[]( idx_t i ) const { return *container_[i]; } + idx_t size() const { return container_.size(); } + void reserve( size_t size ) { container_.reserve( size ); } + template + void emplace_back( Args&&... args ) { + container_.emplace_back( std::forward( args )... ); + } + container_type& get() { return container_; } + +private: + container_type container_; +}; + +//------------------------------------------------------------------------------------------------------ + +} // namespace util +} // namespace atlas diff --git a/src/tests/functionspace/test_polygons.cc b/src/tests/functionspace/test_polygons.cc index aeb2ea55c..619defffa 100644 --- a/src/tests/functionspace/test_polygons.cc +++ b/src/tests/functionspace/test_polygons.cc @@ -29,21 +29,27 @@ namespace test { //----------------------------------------------------------------------------- CASE( "test_polygons" ) { - size_t root = 0; std::string gridname = eckit::Resource( "--grid", "O32" ); Grid grid( gridname ); functionspace::StructuredColumns fs( grid ); - auto lonlat_polygons = []( const functionspace::StructuredColumns& fs ) { - const auto& polygons = fs.polygons(); - std::vector> ll_poly( polygons.size() ); - for ( idx_t p = 0; p < polygons.size(); ++p ) { - ll_poly[p].reset( new util::LonLatPolygon( *polygons[p] ) ); - } - return ll_poly; - }; + auto polygons = util::LonLatPolygons( fs.polygons() ); - auto polygons = lonlat_polygons( fs ); + std::vector sizes( mpi::size() ); + std::vector simplified_sizes( mpi::size() ); + for ( idx_t i = 0; i < mpi::size(); ++i ) { + sizes[i] = fs.polygons()[i].size(); + simplified_sizes[i] = polygons[i].size(); + } + + // Test iterator: + for ( auto& polygon : fs.polygons() ) { + Log::info() << "size of polygon = " << polygon.size() << std::endl; + } + + for ( auto& polygon : polygons ) { + Log::info() << "size of lonlatpolygon = " << polygon.size() << std::endl; + } auto points = std::vector{ {45., 75.}, {90., 75.}, {135., 75.}, {180., 75.}, {225., 75.}, {270., 75.}, {315., 75.}, @@ -52,10 +58,13 @@ CASE( "test_polygons" ) { }; std::vector part( points.size() ); - for ( idx_t n = 0; n < points.size(); ++n ) { + for ( size_t n = 0; n < points.size(); ++n ) { Log::debug() << n << " " << points[n]; + + // A brute force approach. + // This could be enhanced by a kd-tree search to nearest polygon centroid for ( idx_t p = 0; p < polygons.size(); ++p ) { - if ( polygons[p]->contains( points[n] ) ) { + if ( polygons[p].contains( points[n] ) ) { Log::debug() << " : " << p; part[n] = p; } @@ -63,9 +72,23 @@ CASE( "test_polygons" ) { Log::debug() << std::endl; } + if ( mpi::size() == 1 ) { + auto expected_part = std::vector{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + auto expected_sizes = std::vector{132}; + auto expected_simplified_sizes = std::vector{5}; + + EXPECT( part == expected_part ); + EXPECT( sizes == expected_sizes ); + EXPECT( simplified_sizes == expected_simplified_sizes ); + } if ( mpi::size() == 4 ) { - auto expected_part = std::vector{0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3}; + auto expected_part = std::vector{0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3}; + auto expected_sizes = std::vector{50, 84, 84, 50}; + auto expected_simplified_sizes = std::vector{7, 43, 43, 7}; + EXPECT( part == expected_part ); + EXPECT( sizes == expected_sizes ); + EXPECT( simplified_sizes == expected_simplified_sizes ); } } From b8f51a76982e9a5076fbc5dfd279884e4b51b8b5 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 19 Mar 2020 22:58:30 +0000 Subject: [PATCH 010/145] ATLAS-270 KDTree used to find partition where point belongs to, using LonLatPolygons --- src/atlas/CMakeLists.txt | 1 + src/atlas/util/KDTree.h | 113 +++++++++++++++++++++++ src/atlas/util/LonLatPolygon.h | 29 ++++++ src/atlas/util/Polygon.cc | 19 +++- src/atlas/util/Polygon.h | 2 + src/atlas/util/VectorOfAbstract.h | 1 + src/tests/functionspace/test_polygons.cc | 11 ++- 7 files changed, 171 insertions(+), 5 deletions(-) create mode 100644 src/atlas/util/KDTree.h diff --git a/src/atlas/CMakeLists.txt b/src/atlas/CMakeLists.txt index c48cffcc0..5ea417dde 100644 --- a/src/atlas/CMakeLists.txt +++ b/src/atlas/CMakeLists.txt @@ -584,6 +584,7 @@ util/Constants.h util/Earth.h util/GaussianLatitudes.cc util/GaussianLatitudes.h +util/KDTree.h util/LonLatPolygon.cc util/LonLatPolygon.h util/Metadata.cc diff --git a/src/atlas/util/KDTree.h b/src/atlas/util/KDTree.h new file mode 100644 index 000000000..02e527efc --- /dev/null +++ b/src/atlas/util/KDTree.h @@ -0,0 +1,113 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#pragma once + +#include "eckit/container/KDTree.h" + +#include "atlas/library/config.h" +#include "atlas/util/Point.h" + +namespace atlas { +namespace util { + +//------------------------------------------------------------------------------------------------------ + +/// @brief k-dimensional tree constructable both with 2D (lon,lat) points as with 3D (x,y,z) points +/// +/// The implementation is based on eckit::KDTreeMemory with 3D (x,y,z) points. +/// 2D points (lon,lat) are converted when needed to 3D during insertion, and during search. +template +class KDTree { +private: + struct eckit_KDTreeTraits { + using Point = Point3; + using Payload = PayloadT; + }; + using eckit_KDTree = eckit::KDTreeMemory; + +public: + using Payload = PayloadT; + using Node = typename eckit_KDTree::NodeInfo; + using NodeList = typename eckit_KDTree::NodeList; + + /// @brief Reserve memory for building the kdtree in one shot (optional, at cost of extra memory) + void reserve( idx_t size ) { tmp_.reserve( size ); } + + /// @brief Insert spherical point (lon,lat) + /// If memory has been reserved with reserve(), insertion will be delayed until build() is called. + void insert( const Point2& p2, const Payload& payload ) { + Point3 p3; + util::Earth::convertSphericalToCartesian( p2, p3 ); + insert( p3, payload ); + } + + /// @brief Insert 3D cartesian point (x,y,z) + /// If memory has been reserved with reserve(), insertion will be delayed until build() is called. + void insert( const Point3& p3, const Payload& payload ) { + if ( tmp_.capacity() ) { + tmp_.emplace_back( p3, payload ); + } + else { + tree_.insert( {p3, payload} ); + } + } + + /// @brief Build the kd-tree in one shot, if memory has been reserved. + /// This will need to be called before all search functions like kNearestNeighbours(). + void build() { + if ( tmp_.size() ) { + tree_.build( tmp_.begin(), tmp_.end() ); + tmp_.clear(); + tmp_.shrink_to_fit(); + } + } + + /// @brief Find k nearest neighbour given a 2D lonlat point (lon,lat) + NodeList kNearestNeighbours( const Point2& p2, size_t k ) const { + Point3 p3; + util::Earth::convertSphericalToCartesian( p2, p3 ); + return tree_.kNearestNeighbours( p3, k ); + } + + /// @brief Find k nearest neighbours given a 3D cartesian point (x,y,z) + NodeList kNearestNeighbours( const Point3& p3, size_t k ) const { return tree_.kNearestNeighbours( p3, k ); } + + /// @brief Find nearest neighbour given a 2D lonlat point (lon,lat) + Node nearestNeighbour( const Point2& p2 ) const { + Point3 p3; + util::Earth::convertSphericalToCartesian( p2, p3 ); + return nearestNeighbour( p3 ); + } + + /// @brief Find nearest neighbour given a 3D cartesian point (x,y,z) + Node nearestNeighbour( const Point3& p3 ) const { return tree_.nearestNeighbour( p3 ); } + + /// @brief Find all points in within a distance of given radius from a given point (lon,lat) + NodeList findInSphere( const Point2& p2, double radius ) const { + Point3 p3; + util::Earth::convertSphericalToCartesian( p2, p3 ); + return findInSphere( p3, radius ); + } + + /// @brief Find all points in within a distance of given radius from a given point (x,y,z) + NodeList findInSphere( const Point3& p3, double radius ) const { return tree_.findInSphere( p3, radius ); } + + const eckit_KDTree& tree() const { return tree_; } + +private: + std::vector tmp_; + eckit_KDTree tree_; +}; + +//------------------------------------------------------------------------------------------------------ + +} // namespace util +} // namespace atlas diff --git a/src/atlas/util/LonLatPolygon.h b/src/atlas/util/LonLatPolygon.h index 8a26008d0..efe662da8 100644 --- a/src/atlas/util/LonLatPolygon.h +++ b/src/atlas/util/LonLatPolygon.h @@ -12,6 +12,7 @@ #include +#include "atlas/util/KDTree.h" #include "atlas/util/Polygon.h" namespace atlas { @@ -20,6 +21,7 @@ namespace util { //------------------------------------------------------------------------------------------------------ +/// @brief Implement PolygonCoordinates::contains for a polygon defined in LonLat space. class LonLatPolygon : public PolygonCoordinates { private: template @@ -47,8 +49,10 @@ class LonLatPolygon : public PolygonCoordinates { PointLonLat inner_coordinatesMax_; }; + //------------------------------------------------------------------------------------------------------ +/// @brief Vector of all polygons with functionality to find partition using a KDTree class LonLatPolygons : public VectorOfAbstract { public: LonLatPolygons( const PartitionPolygons& partition_polygons ) { @@ -56,7 +60,32 @@ class LonLatPolygons : public VectorOfAbstract { for ( auto& partition_polygon : partition_polygons ) { emplace_back( new LonLatPolygon( partition_polygon ) ); } + search_.reserve( size() ); + for ( idx_t p = 0; p < size(); ++p ) { + search_.insert( this->at( p ).centroid(), p ); + } + search_.build(); + k_ = std::min( k_, size() ); } + + /// @brief find the partition that holds the point (lon,lat) + idx_t findPartition( const Point2& point ) { + const auto found = search_.kNearestNeighbours( point, k_ ); + idx_t partition{-1}; + for ( size_t i = 0; i < found.size(); ++i ) { + idx_t ii = found[i].payload(); + if ( this->at( ii ).contains( point ) ) { + partition = ii; + break; + } + } + ASSERT( partition >= 0 ); + return partition; + } + +private: + KDTree search_; + idx_t k_{4}; }; //------------------------------------------------------------------------------------------------------ diff --git a/src/atlas/util/Polygon.cc b/src/atlas/util/Polygon.cc index 40c6315af..cc73fe361 100644 --- a/src/atlas/util/Polygon.cc +++ b/src/atlas/util/Polygon.cc @@ -137,6 +137,7 @@ PolygonCoordinates::PolygonCoordinates( const Polygon& poly, const atlas::Field& size_t nb_removed_points_due_to_alignment = 0; + for ( size_t i = 0; i < poly.size(); ++i ) { Point2 A( coord( poly[i], XX ), coord( poly[i], YY ) ); coordinatesMin_ = Point2::componentsMin( coordinatesMin_, A ); @@ -169,10 +170,15 @@ PolygonCoordinates::PolygonCoordinates( const PointContainer& points ) { coordinatesMin_ = coordinates_.front(); coordinatesMax_ = coordinatesMin_; + centroid_ = Point2{0., 0.}; for ( const Point2& P : coordinates_ ) { coordinatesMin_ = Point2::componentsMin( coordinatesMin_, P ); coordinatesMax_ = Point2::componentsMax( coordinatesMax_, P ); + centroid_[XX] += P[XX]; + centroid_[YY] += P[YY]; } + centroid_[XX] /= coordinates_.size(); + centroid_[YY] /= coordinates_.size(); } template @@ -180,8 +186,9 @@ PolygonCoordinates::PolygonCoordinates( const PointContainer& points, bool remov coordinates_.clear(); coordinates_.reserve( points.size() ); - coordinatesMin_ = Point2( std::numeric_limits::max(), std::numeric_limits::max() ); - coordinatesMax_ = Point2( std::numeric_limits::lowest(), std::numeric_limits::lowest() ); + coordinatesMin_ = Point2{std::numeric_limits::max(), std::numeric_limits::max()}; + coordinatesMax_ = Point2{std::numeric_limits::lowest(), std::numeric_limits::lowest()}; + centroid_ = Point2{0., 0.}; size_t nb_removed_points_due_to_alignment = 0; @@ -189,6 +196,8 @@ PolygonCoordinates::PolygonCoordinates( const PointContainer& points, bool remov const Point2& A = points[i]; coordinatesMin_ = Point2::componentsMin( coordinatesMin_, A ); coordinatesMax_ = Point2::componentsMax( coordinatesMax_, A ); + centroid_[XX] += A[XX]; + centroid_[YY] += A[YY]; // if new point is aligned with existing edge (cross product ~= 0) make the // edge longer @@ -203,6 +212,8 @@ PolygonCoordinates::PolygonCoordinates( const PointContainer& points, bool remov } coordinates_.emplace_back( A ); } + centroid_[XX] /= points.size(); + centroid_[YY] /= points.size(); ATLAS_ASSERT( coordinates_.size() > 2 ); ATLAS_ASSERT( eckit::geometry::points_equal( coordinates_.front(), coordinates_.back() ) ); @@ -227,6 +238,10 @@ const Point2& PolygonCoordinates::coordinatesMin() const { return coordinatesMin_; } +const Point2& PolygonCoordinates::centroid() const { + return centroid_; +} + //------------------------------------------------------------------------------------------------------ } // namespace util diff --git a/src/atlas/util/Polygon.h b/src/atlas/util/Polygon.h index af286a03c..8a89027c0 100644 --- a/src/atlas/util/Polygon.h +++ b/src/atlas/util/Polygon.h @@ -142,6 +142,7 @@ class PolygonCoordinates { const Point2& coordinatesMax() const; const Point2& coordinatesMin() const; + const Point2& centroid() const; idx_t size() const { return coordinates_.size(); } @@ -150,6 +151,7 @@ class PolygonCoordinates { Point2 coordinatesMin_; Point2 coordinatesMax_; + Point2 centroid_; std::vector coordinates_; }; diff --git a/src/atlas/util/VectorOfAbstract.h b/src/atlas/util/VectorOfAbstract.h index 8e0e9634e..149c2a512 100644 --- a/src/atlas/util/VectorOfAbstract.h +++ b/src/atlas/util/VectorOfAbstract.h @@ -61,6 +61,7 @@ class VectorOfAbstract { const_iterator begin() const { return make_dereference_iterator( container_.begin() ); } const_iterator end() const { return make_dereference_iterator( container_.end() ); } const_reference operator[]( idx_t i ) const { return *container_[i]; } + const_reference at( idx_t i ) const { return *container_[i]; } idx_t size() const { return container_.size(); } void reserve( size_t size ) { container_.reserve( size ); } template diff --git a/src/tests/functionspace/test_polygons.cc b/src/tests/functionspace/test_polygons.cc index 619defffa..867756304 100644 --- a/src/tests/functionspace/test_polygons.cc +++ b/src/tests/functionspace/test_polygons.cc @@ -19,7 +19,6 @@ #include "tests/AtlasTestEnvironment.h" -using namespace eckit; using namespace atlas::functionspace; using namespace atlas::util; @@ -33,6 +32,7 @@ CASE( "test_polygons" ) { Grid grid( gridname ); functionspace::StructuredColumns fs( grid ); + ATLAS_TRACE( "computations after setup" ); auto polygons = util::LonLatPolygons( fs.polygons() ); std::vector sizes( mpi::size() ); @@ -72,7 +72,7 @@ CASE( "test_polygons" ) { Log::debug() << std::endl; } - if ( mpi::size() == 1 ) { + if ( mpi::size() == 1 && gridname == "O32" ) { auto expected_part = std::vector{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; auto expected_sizes = std::vector{132}; auto expected_simplified_sizes = std::vector{5}; @@ -81,7 +81,7 @@ CASE( "test_polygons" ) { EXPECT( sizes == expected_sizes ); EXPECT( simplified_sizes == expected_simplified_sizes ); } - if ( mpi::size() == 4 ) { + if ( mpi::size() == 4 && gridname == "O32" ) { auto expected_part = std::vector{0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3}; auto expected_sizes = std::vector{50, 84, 84, 50}; auto expected_simplified_sizes = std::vector{7, 43, 43, 7}; @@ -90,6 +90,11 @@ CASE( "test_polygons" ) { EXPECT( sizes == expected_sizes ); EXPECT( simplified_sizes == expected_simplified_sizes ); } + + for ( size_t n = 0; n < points.size(); ++n ) { + EXPECT_EQ( polygons.findPartition( points[n] ), part[n] ); + } + Log::info() << std::endl; } //----------------------------------------------------------------------------- From bb9cabd59f3b960bb7c95a0aba5b4b27a0e132cf Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 20 Mar 2020 14:58:08 +0000 Subject: [PATCH 011/145] ATLAS-270 Extract PolygonLocator from LonLatPolygons --- .../functionspace/detail/StructuredColumns.cc | 3 +- src/atlas/util/KDTree.h | 178 +++++++++++++----- src/atlas/util/LonLatPolygon.h | 32 +--- src/atlas/util/Polygon.h | 1 + src/atlas/util/PolygonLocator.h | 92 +++++++++ src/atlas/util/VectorOfAbstract.h | 3 + src/tests/functionspace/test_polygons.cc | 72 +++++-- src/tests/util/CMakeLists.txt | 5 + src/tests/util/test_kdtree.cc | 111 +++++++++++ 9 files changed, 407 insertions(+), 90 deletions(-) create mode 100644 src/atlas/util/PolygonLocator.h create mode 100644 src/tests/util/test_kdtree.cc diff --git a/src/atlas/functionspace/detail/StructuredColumns.cc b/src/atlas/functionspace/detail/StructuredColumns.cc index cea7d62c0..d9a564582 100644 --- a/src/atlas/functionspace/detail/StructuredColumns.cc +++ b/src/atlas/functionspace/detail/StructuredColumns.cc @@ -470,7 +470,8 @@ util::Polygon::edge_set_t compute_edges( idx_t points_size ) { class SimplePolygon : public util::PartitionPolygon { public: - explicit SimplePolygon( const std::vector& points ) : points_( points ) { + explicit SimplePolygon( std::vector&& points ) : + points_( std::move(points) ) { setup( compute_edges( points_.size() ) ); } diff --git a/src/atlas/util/KDTree.h b/src/atlas/util/KDTree.h index 02e527efc..d18337007 100644 --- a/src/atlas/util/KDTree.h +++ b/src/atlas/util/KDTree.h @@ -13,6 +13,7 @@ #include "eckit/container/KDTree.h" #include "atlas/library/config.h" +#include "atlas/runtime/Exception.h" #include "atlas/util/Point.h" namespace atlas { @@ -23,7 +24,28 @@ namespace util { /// @brief k-dimensional tree constructable both with 2D (lon,lat) points as with 3D (x,y,z) points /// /// The implementation is based on eckit::KDTreeMemory with 3D (x,y,z) points. -/// 2D points (lon,lat) are converted when needed to 3D during insertion, and during search. +/// 2D points (lon,lat) are converted when needed to 3D during insertion, and during search, so that +/// a search always happens with 3D cartesian points. +/// +/// ### Example: +/// +/// Construct a KDTree, given a `list_of_lonlat_points` (e.g. `std::vector` or other container) +/// A payload given is the index in this list. +/// @code{.cpp} +/// KDTree search; +/// search.reserve( list_of_lonlat_points.size() ); +/// idx_t n{0}; +/// for ( auto& point : list_of_lonlat_points ) { +/// search.insert( point, n++ ); +/// } +/// search.build(); +/// @endcode +/// We can now do e.g. a search for the nearest 4 neighbours (k=4) sorted by shortest distance +/// @code{.cpp} +/// idx_t k = 4; +/// auto neighbours = search.kNearestNeighbours( PointLonLat{180., 45.}, k ).payloads(); +/// @endcode +/// The variable `neighbours` is now a container of indices (the payloads) of the 4 nearest points template class KDTree { private: @@ -34,80 +56,148 @@ class KDTree { using eckit_KDTree = eckit::KDTreeMemory; public: - using Payload = PayloadT; - using Node = typename eckit_KDTree::NodeInfo; - using NodeList = typename eckit_KDTree::NodeList; + using Payload = PayloadT; + using Node = typename eckit_KDTree::NodeInfo; + using PayLoadList = std::vector; + + class NodeList : public eckit_KDTree::NodeList { + using Base = typename eckit_KDTree::NodeList; + + public: + NodeList( typename eckit_KDTree::NodeList&& list ) : Base( std::move( list ) ) {} + PayLoadList payloads() const { + PayLoadList list; + list.reserve( Base::size() ); + for ( auto& item : *this ) { + list.emplace_back( item.payload() ); + } + return list; + } + }; /// @brief Reserve memory for building the kdtree in one shot (optional, at cost of extra memory) - void reserve( idx_t size ) { tmp_.reserve( size ); } + void reserve( idx_t size ); /// @brief Insert spherical point (lon,lat) /// If memory has been reserved with reserve(), insertion will be delayed until build() is called. - void insert( const Point2& p2, const Payload& payload ) { - Point3 p3; - util::Earth::convertSphericalToCartesian( p2, p3 ); - insert( p3, payload ); - } + void insert( const Point2& p2, const Payload& payload ); /// @brief Insert 3D cartesian point (x,y,z) /// If memory has been reserved with reserve(), insertion will be delayed until build() is called. - void insert( const Point3& p3, const Payload& payload ) { - if ( tmp_.capacity() ) { - tmp_.emplace_back( p3, payload ); - } - else { - tree_.insert( {p3, payload} ); - } - } + void insert( const Point3& p3, const Payload& payload ); /// @brief Build the kd-tree in one shot, if memory has been reserved. /// This will need to be called before all search functions like kNearestNeighbours(). - void build() { - if ( tmp_.size() ) { - tree_.build( tmp_.begin(), tmp_.end() ); - tmp_.clear(); - tmp_.shrink_to_fit(); - } - } + void build(); /// @brief Find k nearest neighbour given a 2D lonlat point (lon,lat) - NodeList kNearestNeighbours( const Point2& p2, size_t k ) const { - Point3 p3; - util::Earth::convertSphericalToCartesian( p2, p3 ); - return tree_.kNearestNeighbours( p3, k ); - } + NodeList kNearestNeighbours( const Point2& p2, size_t k ) const; /// @brief Find k nearest neighbours given a 3D cartesian point (x,y,z) - NodeList kNearestNeighbours( const Point3& p3, size_t k ) const { return tree_.kNearestNeighbours( p3, k ); } + NodeList kNearestNeighbours( const Point3& p3, size_t k ) const; /// @brief Find nearest neighbour given a 2D lonlat point (lon,lat) - Node nearestNeighbour( const Point2& p2 ) const { - Point3 p3; - util::Earth::convertSphericalToCartesian( p2, p3 ); - return nearestNeighbour( p3 ); - } + Node nearestNeighbour( const Point2& p2 ) const; /// @brief Find nearest neighbour given a 3D cartesian point (x,y,z) - Node nearestNeighbour( const Point3& p3 ) const { return tree_.nearestNeighbour( p3 ); } + Node nearestNeighbour( const Point3& p3 ) const; /// @brief Find all points in within a distance of given radius from a given point (lon,lat) - NodeList findInSphere( const Point2& p2, double radius ) const { - Point3 p3; - util::Earth::convertSphericalToCartesian( p2, p3 ); - return findInSphere( p3, radius ); - } + NodeList findInSphere( const Point2& p2, double radius ) const; /// @brief Find all points in within a distance of given radius from a given point (x,y,z) - NodeList findInSphere( const Point3& p3, double radius ) const { return tree_.findInSphere( p3, radius ); } + NodeList findInSphere( const Point3& p3, double radius ) const; const eckit_KDTree& tree() const { return tree_; } +private: + void assert_built() const; + private: std::vector tmp_; - eckit_KDTree tree_; + mutable eckit_KDTree tree_; // mutable because its member functions are non-const... }; //------------------------------------------------------------------------------------------------------ +template +void KDTree::reserve( idx_t size ) { + tmp_.reserve( size ); +} + +template +void KDTree::insert( const Point2& p2, const Payload& payload ) { + Point3 p3; + util::Earth::convertSphericalToCartesian( p2, p3 ); + insert( p3, payload ); +} + +template +void KDTree::insert( const Point3& p3, const Payload& payload ) { + if ( tmp_.capacity() ) { + tmp_.emplace_back( p3, payload ); + } + else { + tree_.insert( {p3, payload} ); + } +} + +template +void KDTree::build() { + if ( tmp_.size() ) { + tree_.build( tmp_.begin(), tmp_.end() ); + tmp_.clear(); + tmp_.shrink_to_fit(); + } +} + +template +typename KDTree::NodeList KDTree::kNearestNeighbours( const Point2& p2, size_t k ) const { + Point3 p3; + util::Earth::convertSphericalToCartesian( p2, p3 ); + return kNearestNeighbours( p3, k ); +} + +template +typename KDTree::NodeList KDTree::kNearestNeighbours( const Point3& p3, size_t k ) const { + assert_built(); + return tree_.kNearestNeighbours( p3, k ); +} + +template +typename KDTree::Node KDTree::nearestNeighbour( const Point2& p2 ) const { + Point3 p3; + util::Earth::convertSphericalToCartesian( p2, p3 ); + return nearestNeighbour( p3 ); +} + +template +typename KDTree::Node KDTree::nearestNeighbour( const Point3& p3 ) const { + assert_built(); + return tree_.nearestNeighbour( p3 ); +} + +template +typename KDTree::NodeList KDTree::findInSphere( const Point2& p2, double radius ) const { + Point3 p3; + util::Earth::convertSphericalToCartesian( p2, p3 ); + return findInSphere( p3, radius ); +} + +template +typename KDTree::NodeList KDTree::findInSphere( const Point3& p3, double radius ) const { + assert_built(); + return tree_.findInSphere( p3, radius ); +} + +template +void KDTree::assert_built() const { + if ( tmp_.capacity() ) { + throw_AssertionFailed( "KDTree was used before calling build()" ); + } +} + +//------------------------------------------------------------------------------------------------------ + } // namespace util } // namespace atlas diff --git a/src/atlas/util/LonLatPolygon.h b/src/atlas/util/LonLatPolygon.h index efe662da8..bfa9617a3 100644 --- a/src/atlas/util/LonLatPolygon.h +++ b/src/atlas/util/LonLatPolygon.h @@ -12,12 +12,13 @@ #include -#include "atlas/util/KDTree.h" #include "atlas/util/Polygon.h" namespace atlas { namespace util { +class LonLatPolygon; +class LonLatPolygons; //------------------------------------------------------------------------------------------------------ @@ -28,7 +29,7 @@ class LonLatPolygon : public PolygonCoordinates { using enable_if_not_polygon = typename std::enable_if::value, int>::type; public: - // -- Constructors + using Vector = LonLatPolygons; LonLatPolygon( const Polygon&, const atlas::Field& coordinates, bool removeAlignedPoints = true ); LonLatPolygon( const PartitionPolygon& ); @@ -53,39 +54,14 @@ class LonLatPolygon : public PolygonCoordinates { //------------------------------------------------------------------------------------------------------ /// @brief Vector of all polygons with functionality to find partition using a KDTree -class LonLatPolygons : public VectorOfAbstract { +class LonLatPolygons : public PolygonCoordinates::Vector { public: LonLatPolygons( const PartitionPolygons& partition_polygons ) { reserve( partition_polygons.size() ); for ( auto& partition_polygon : partition_polygons ) { emplace_back( new LonLatPolygon( partition_polygon ) ); } - search_.reserve( size() ); - for ( idx_t p = 0; p < size(); ++p ) { - search_.insert( this->at( p ).centroid(), p ); - } - search_.build(); - k_ = std::min( k_, size() ); - } - - /// @brief find the partition that holds the point (lon,lat) - idx_t findPartition( const Point2& point ) { - const auto found = search_.kNearestNeighbours( point, k_ ); - idx_t partition{-1}; - for ( size_t i = 0; i < found.size(); ++i ) { - idx_t ii = found[i].payload(); - if ( this->at( ii ).contains( point ) ) { - partition = ii; - break; - } - } - ASSERT( partition >= 0 ); - return partition; } - -private: - KDTree search_; - idx_t k_{4}; }; //------------------------------------------------------------------------------------------------------ diff --git a/src/atlas/util/Polygon.h b/src/atlas/util/Polygon.h index 8a89027c0..4d24c8cf3 100644 --- a/src/atlas/util/Polygon.h +++ b/src/atlas/util/Polygon.h @@ -119,6 +119,7 @@ class PartitionPolygons : public VectorOfAbstract {}; class PolygonCoordinates { public: + using Vector = VectorOfAbstract; // -- Constructors PolygonCoordinates( const Polygon&, const atlas::Field& coordinates, bool removeAlignedPoints ); diff --git a/src/atlas/util/PolygonLocator.h b/src/atlas/util/PolygonLocator.h new file mode 100644 index 000000000..cd2079f9e --- /dev/null +++ b/src/atlas/util/PolygonLocator.h @@ -0,0 +1,92 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#pragma once + +#include +#include + +#include "atlas/util/KDTree.h" +#include "atlas/util/Polygon.h" +#include "atlas/library/config.h" + +namespace atlas { +namespace util { + +//------------------------------------------------------------------------------------------------------ + +///@brief Find polygon that contains a point +/// +/// Construction requires a list of polygons. +/// The implementation makes use of a KDTree that holds centroids of all polygons. +/// Once the nearest polygon-centroids (default=4) are found, the corresponding polygons are +/// visited in order of shortest distance, to check if the point is contained within. +class PolygonLocator { +public: + + /// @brief Construct PolygonLocator from shared_ptr of polygons + PolygonLocator( const std::shared_ptr polygons, idx_t k = 4 ) : + shared_polygons_( polygons ), + polygons_( *shared_polygons_ ) { + k_ = std::min( k, polygons_.size() ); + buildKDTree(); + } + + /// @brief Construct PolygonLocator and move polygons inside. + PolygonLocator( PolygonCoordinates::Vector&& polygons, idx_t k = 4 ) : + shared_polygons_( std::make_shared( std::move(polygons) ) ), + polygons_( *shared_polygons_ ) { + k_ = std::min( k, polygons_.size() ); + buildKDTree(); + } + + /// @brief Construct PolygonLocator using reference to polygons. + /// !WARNING! polygons should not go out of scope before PolygonLocator + PolygonLocator( const PolygonCoordinates::Vector& polygons, idx_t k = 4 ) : + polygons_( polygons ) { + k_ = std::min( k, polygons_.size() ); + buildKDTree(); + } + + /// @brief find the polygon that holds the point (lon,lat) + idx_t operator()( const Point2& point ) const { + const auto found = kdtree_.kNearestNeighbours( point, k_ ); + idx_t partition{-1}; + for ( size_t i = 0; i < found.size(); ++i ) { + idx_t ii = found[i].payload(); + if ( polygons_[ii].contains( point ) ) { + partition = ii; + break; + } + } + ATLAS_ASSERT( partition >= 0, "Could not find find point in `k` \"nearest\" polygons. Increase `k`?" ); + return partition; + } + +private: + + void buildKDTree() { + kdtree_.reserve( polygons_.size() ); + for ( idx_t p = 0; p < polygons_.size(); ++p ) { + kdtree_.insert( polygons_[p].centroid(), p ); + } + kdtree_.build(); + } + + std::shared_ptr shared_polygons_; + const PolygonCoordinates::Vector& polygons_; + idx_t k_; + KDTree kdtree_; +}; + +//------------------------------------------------------------------------------------------------------ + +} // namespace util +} // namespace atlas diff --git a/src/atlas/util/VectorOfAbstract.h b/src/atlas/util/VectorOfAbstract.h index 149c2a512..fe0576a94 100644 --- a/src/atlas/util/VectorOfAbstract.h +++ b/src/atlas/util/VectorOfAbstract.h @@ -58,6 +58,9 @@ class VectorOfAbstract { using const_iterator = DereferenceIterator; public: + VectorOfAbstract() = default; + VectorOfAbstract( VectorOfAbstract&& other ) : container_( std::move( other.container_ ) ) {} + const_iterator begin() const { return make_dereference_iterator( container_.begin() ); } const_iterator end() const { return make_dereference_iterator( container_.end() ); } const_reference operator[]( idx_t i ) const { return *container_[i]; } diff --git a/src/tests/functionspace/test_polygons.cc b/src/tests/functionspace/test_polygons.cc index 867756304..695818a02 100644 --- a/src/tests/functionspace/test_polygons.cc +++ b/src/tests/functionspace/test_polygons.cc @@ -16,6 +16,7 @@ #include "atlas/grid/StructuredGrid.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/LonLatPolygon.h" +#include "atlas/util/PolygonLocator.h" #include "tests/AtlasTestEnvironment.h" @@ -25,12 +26,34 @@ using namespace atlas::util; namespace atlas { namespace test { +namespace { +std::string gridname() { + static std::string _gridname = eckit::Resource( "--grid", "O32" ); + return _gridname; +} + +FunctionSpace functionspace() { + static functionspace::StructuredColumns _fs = []() { + Grid grid( gridname() ); + return functionspace::StructuredColumns{grid}; + }(); + return _fs; +} + +std::vector& points() { + static std::vector _points{ + {45., 75.}, {90., 75.}, {135., 75.}, {180., 75.}, {225., 75.}, {270., 75.}, {315., 75.}, + {45., 15.}, {90., 15.}, {135., 15.}, {180., 15.}, {225., 15.}, {270., 15.}, {315., 15.}, + {45., -75.}, {90., -75.}, {135., -75.}, {180., -75.}, {225., -75.}, {270., -75.}, {315., -75.}, + }; + return _points; +} +} // namespace + //----------------------------------------------------------------------------- CASE( "test_polygons" ) { - std::string gridname = eckit::Resource( "--grid", "O32" ); - Grid grid( gridname ); - functionspace::StructuredColumns fs( grid ); + auto fs = functionspace(); ATLAS_TRACE( "computations after setup" ); auto polygons = util::LonLatPolygons( fs.polygons() ); @@ -51,20 +74,14 @@ CASE( "test_polygons" ) { Log::info() << "size of lonlatpolygon = " << polygon.size() << std::endl; } - auto points = std::vector{ - {45., 75.}, {90., 75.}, {135., 75.}, {180., 75.}, {225., 75.}, {270., 75.}, {315., 75.}, - {45., 15.}, {90., 15.}, {135., 15.}, {180., 15.}, {225., 15.}, {270., 15.}, {315., 15.}, - {45., -75.}, {90., -75.}, {135., -75.}, {180., -75.}, {225., -75.}, {270., -75.}, {315., -75.}, - }; - - std::vector part( points.size() ); - for ( size_t n = 0; n < points.size(); ++n ) { - Log::debug() << n << " " << points[n]; + std::vector part( points().size() ); + for ( size_t n = 0; n < points().size(); ++n ) { + Log::debug() << n << " " << points()[n]; // A brute force approach. // This could be enhanced by a kd-tree search to nearest polygon centroid for ( idx_t p = 0; p < polygons.size(); ++p ) { - if ( polygons[p].contains( points[n] ) ) { + if ( polygons[p].contains( points()[n] ) ) { Log::debug() << " : " << p; part[n] = p; } @@ -72,7 +89,7 @@ CASE( "test_polygons" ) { Log::debug() << std::endl; } - if ( mpi::size() == 1 && gridname == "O32" ) { + if ( mpi::size() == 1 && gridname() == "O32" ) { auto expected_part = std::vector{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; auto expected_sizes = std::vector{132}; auto expected_simplified_sizes = std::vector{5}; @@ -81,7 +98,7 @@ CASE( "test_polygons" ) { EXPECT( sizes == expected_sizes ); EXPECT( simplified_sizes == expected_simplified_sizes ); } - if ( mpi::size() == 4 && gridname == "O32" ) { + if ( mpi::size() == 4 && gridname() == "O32" ) { auto expected_part = std::vector{0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3}; auto expected_sizes = std::vector{50, 84, 84, 50}; auto expected_simplified_sizes = std::vector{7, 43, 43, 7}; @@ -91,12 +108,33 @@ CASE( "test_polygons" ) { EXPECT( simplified_sizes == expected_simplified_sizes ); } - for ( size_t n = 0; n < points.size(); ++n ) { - EXPECT_EQ( polygons.findPartition( points[n] ), part[n] ); + PolygonLocator find_partition( polygons ); + for ( size_t n = 0; n < points().size(); ++n ) { + EXPECT_EQ( find_partition( points()[n] ), part[n] ); } Log::info() << std::endl; } +CASE( "test_polygon_locator_from_const_ref_polygons" ) { + auto polygons = LonLatPolygons{functionspace().polygons()}; + PolygonLocator find_partition( polygons ); + EXPECT_EQ( find_partition( PointLonLat{0., 90.} ), 0 ); + EXPECT_EQ( find_partition( PointLonLat{0., -90.} ), mpi::size() - 1 ); +} + +CASE( "test_polygon_locator_from_move" ) { + PolygonLocator find_partition( LonLatPolygons{functionspace().polygons()} ); + EXPECT_EQ( find_partition( PointLonLat{0., 90.} ), 0 ); + EXPECT_EQ( find_partition( PointLonLat{0., -90.} ), mpi::size() - 1 ); +} + +CASE( "test_polygon_locator_from_shared" ) { + auto polygons = std::make_shared( functionspace().polygons() ); + PolygonLocator find_partition( polygons ); + EXPECT_EQ( find_partition( PointLonLat{0., 90.} ), 0 ); + EXPECT_EQ( find_partition( PointLonLat{0., -90.} ), mpi::size() - 1 ); +} + //----------------------------------------------------------------------------- } // namespace test diff --git a/src/tests/util/CMakeLists.txt b/src/tests/util/CMakeLists.txt index eac32bac7..c3e88d1de 100644 --- a/src/tests/util/CMakeLists.txt +++ b/src/tests/util/CMakeLists.txt @@ -56,3 +56,8 @@ ecbuild_add_test( TARGET atlas_test_metadata ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) +ecbuild_add_test( TARGET atlas_test_kdtree + SOURCES test_kdtree.cc + LIBS atlas + ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} +) diff --git a/src/tests/util/test_kdtree.cc b/src/tests/util/test_kdtree.cc new file mode 100644 index 000000000..1cc1eacb2 --- /dev/null +++ b/src/tests/util/test_kdtree.cc @@ -0,0 +1,111 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include +#include +#include + +#include "atlas/grid.h" +#include "atlas/util/KDTree.h" + +#include "tests/AtlasTestEnvironment.h" + +using atlas::util::KDTree; + +namespace atlas { +namespace test { + +CASE( "test kdtree" ) { + auto grid = Grid{"O32"}; + + KDTree search; + search.reserve( grid.size() ); + idx_t n{0}; + for ( auto& point : grid.lonlat() ) { + search.insert( point, n++ ); + } + search.build(); + EXPECT_NO_THROW( search.nearestNeighbour( PointLonLat{180., 45.} ) ); + // ... + // Search 4 nearest neighbours (k=4), sorted by shortest distance + auto neighbours = search.kNearestNeighbours( PointLonLat{180., 45.}, 4 ).payloads(); + auto expected_neighbours = std::vector{760, 842, 759, 761}; + EXPECT( neighbours == expected_neighbours ); +} + +CASE( "test assertion" ) { + auto grid = Grid{"O32"}; + + KDTree search; + search.reserve( grid.size() ); + idx_t n{0}; + for ( auto& point : grid.lonlat() ) { + search.insert( point, n++ ); + } + // Forgot to call search.build() --> assertion thrown when trying to access + EXPECT_THROWS_AS( search.nearestNeighbour( PointLonLat{180., 45.} ), eckit::AssertionFailed ); +} + +CASE( "test no assertion" ) { + // Like case "test assertion", but without reserving size + auto grid = Grid{"O32"}; + + KDTree search; + // No search.reserve() --> build() will not be necessary. + idx_t n{0}; + for ( auto& point : grid.lonlat() ) { + search.insert( point, n++ ); + } + // search.build() Not required + EXPECT_NO_THROW( search.nearestNeighbour( PointLonLat{180., 45.} ) ); +} + +const KDTree& search() { + static KDTree* kdtree = []() { + KDTree* kdtree = new KDTree; + auto grid = Grid{"O32"}; + kdtree->reserve( grid.size() ); + idx_t n{0}; + for ( auto& point : grid.lonlat() ) { + kdtree->insert( point, n++ ); + } + kdtree->build(); + return kdtree; + }(); + EXPECT( kdtree ); + return *kdtree; +} + +CASE( "test nearestNeighbour" ) { + auto neighbour = search().nearestNeighbour( PointLonLat{180., 45.} ).payload(); + auto expected_neighbour = 760; + EXPECT( neighbour == expected_neighbour ); +} + +CASE( "test kNearestNeighbours" ) { + auto neighbours = search().kNearestNeighbours( PointLonLat{180., 45.}, 4 ).payloads(); + auto expected_neighbours = std::vector{760, 842, 759, 761}; + EXPECT( neighbours == expected_neighbours ); +} + +CASE( "test findInSphere" ) { + constexpr double km = 1000.; + auto neighbours = search().findInSphere( PointLonLat{180., 45.}, 500 * km ).payloads(); + auto expected_neighbours = std::vector{760, 842, 759, 761, 841, 843, 682}; + EXPECT( neighbours == expected_neighbours ); + Log::info() << neighbours << std::endl; +} + +} // namespace test +} // namespace atlas + +int main( int argc, char** argv ) { + return atlas::test::run( argc, argv ); +} From 16aecaac21d0cf8bb54e071d7a5b1ddf5eb84b7d Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 1 Apr 2020 17:48:00 +0100 Subject: [PATCH 012/145] ATLAS-270 Add Point2 comparators --- src/atlas/util/Point.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/atlas/util/Point.h b/src/atlas/util/Point.h index d3dc16b8e..1c5b8c0e3 100644 --- a/src/atlas/util/Point.h +++ b/src/atlas/util/Point.h @@ -29,6 +29,13 @@ namespace atlas { using Point2 = eckit::geometry::Point2; using Point3 = eckit::geometry::Point3; +inline bool operator==( const Point2& p1, const Point2& p2 ) { + return eckit::geometry::points_equal( p1, p2 ); +} +inline bool operator!=( const Point2& p1, const Point2& p2 ) { + return !eckit::geometry::points_equal( p1, p2 ); +} + /// @brief Point in arbitrary XY-coordinate system class PointXY : public eckit::geometry::Point2 { using array_t = std::array; From 74067acfedf602959ad5491ecd81b0230002e366 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 1 Apr 2020 17:53:00 +0100 Subject: [PATCH 013/145] ATLAS-270 Refactoring of StructuredPartitionPolygon --- .../functionspace/detail/StructuredColumns.cc | 66 +------------------ src/atlas/grid/StructuredPartitionPolygon.cc | 51 +++++++++++++- src/atlas/grid/StructuredPartitionPolygon.h | 14 ++-- src/atlas/util/Polygon.cc | 18 +++++ src/atlas/util/Polygon.h | 34 +++++++++- src/atlas/util/VectorOfAbstract.h | 1 + 6 files changed, 110 insertions(+), 74 deletions(-) diff --git a/src/atlas/functionspace/detail/StructuredColumns.cc b/src/atlas/functionspace/detail/StructuredColumns.cc index d9a564582..c2024af65 100644 --- a/src/atlas/functionspace/detail/StructuredColumns.cc +++ b/src/atlas/functionspace/detail/StructuredColumns.cc @@ -454,76 +454,12 @@ const util::PartitionPolygon& StructuredColumns::polygon( idx_t halo ) const { return *polygon_; } -util::Polygon::edge_set_t compute_edges( idx_t points_size ) { - util::Polygon::edge_set_t edges; - auto add_edge = [&]( idx_t p1, idx_t p2 ) { - util::Polygon::edge_t edge = {p1, p2}; - edges.insert( edge ); - // Log::info() << edge.first << " " << edge.second << std::endl; - }; - for ( idx_t p = 0; p < points_size - 2; ++p ) { - add_edge( p, p + 1 ); - } - add_edge( points_size - 2, 0 ); - return edges; -} - -class SimplePolygon : public util::PartitionPolygon { -public: - explicit SimplePolygon( std::vector&& points ) : - points_( std::move(points) ) { - setup( compute_edges( points_.size() ) ); - } - - const std::vector& xy() const override { return points_; } - const std::vector& lonlat() const override { return points_; } - - -private: - std::vector points_; -}; - const atlas::util::PartitionPolygons& StructuredColumns::polygons() const { if ( polygons_.size() ) { return polygons_; } - ATLAS_TRACE(); - - polygons_.reserve( mpi::size() ); - - - const mpi::Comm& comm = mpi::comm(); - const int mpi_size = int( comm.size() ); - - auto& poly = polygon(); - - std::vector mypolygon; - mypolygon.reserve( poly.size() * 2 ); - - for ( auto& p : poly.xy() ) { - mypolygon.push_back( p[XX] ); - mypolygon.push_back( p[YY] ); - //Log::info() << p << std::endl; - } - ATLAS_ASSERT( mypolygon.size() >= 4 ); - - eckit::mpi::Buffer recv_polygons( mpi_size ); - - comm.allGatherv( mypolygon.begin(), mypolygon.end(), recv_polygons ); - - using PolygonXY = std::vector; - for ( idx_t p = 0; p < mpi_size; ++p ) { - PolygonXY recv_points; - recv_points.reserve( recv_polygons.counts[p] ); - for ( idx_t j = 0; j < recv_polygons.counts[p] / 2; ++j ) { - PointXY pxy( *( recv_polygons.begin() + recv_polygons.displs[p] + 2 * j + XX ), - *( recv_polygons.begin() + recv_polygons.displs[p] + 2 * j + YY ) ); - recv_points.push_back( pxy ); - } - polygons_.emplace_back( new SimplePolygon( std::move( recv_points ) ) ); - } - + polygon().allGather( polygons_ ); return polygons_; } diff --git a/src/atlas/grid/StructuredPartitionPolygon.cc b/src/atlas/grid/StructuredPartitionPolygon.cc index 96df77fa4..ac0186071 100644 --- a/src/atlas/grid/StructuredPartitionPolygon.cc +++ b/src/atlas/grid/StructuredPartitionPolygon.cc @@ -310,7 +310,7 @@ StructuredPartitionPolygon::StructuredPartitionPolygon( const functionspace::Fun inner_bounding_box_.emplace_back( inner_bounding_box_[0] ); auto min = Point2( std::numeric_limits::max(), std::numeric_limits::max() ); auto max = Point2( std::numeric_limits::lowest(), std::numeric_limits::lowest() ); - for ( size_t i = 0; i < inner_bounding_box_.size(); ++i ) { + for ( size_t i = 0; i < inner_bounding_box_.size() - 1; ++i ) { min = Point2::componentsMin( min, inner_bounding_box_[i] ); max = Point2::componentsMax( max, inner_bounding_box_[i] ); } @@ -423,6 +423,55 @@ void StructuredPartitionPolygon::outputPythonScript( const eckit::PathName& file } } +util::PartitionPolygon::PointsXY StructuredPartitionPolygon::xy() const { + return points_; +} + +util::PartitionPolygon::PointsLonLat StructuredPartitionPolygon::lonlat() const { + return points_; +} + +void StructuredPartitionPolygon::allGather( util::PartitionPolygons& polygons ) const { + ATLAS_TRACE(); + + const auto& fs = dynamic_cast( fs_ ); + if ( fs.grid().projection() ) { + ATLAS_NOTIMPLEMENTED; // because LonLat != XY + } + + polygons.clear(); + polygons.reserve( mpi::size() ); + + const mpi::Comm& comm = mpi::comm(); + const int mpi_size = int( comm.size() ); + + auto& poly = *this; + + std::vector mypolygon; + mypolygon.reserve( poly.size() * 2 ); + + for ( auto& p : poly.xy() ) { + mypolygon.push_back( p[XX] ); + mypolygon.push_back( p[YY] ); + } + ATLAS_ASSERT( mypolygon.size() >= 4 ); + + eckit::mpi::Buffer recv_polygons( mpi_size ); + + comm.allGatherv( mypolygon.begin(), mypolygon.end(), recv_polygons ); + + for ( idx_t p = 0; p < mpi_size; ++p ) { + PointsXY recv_points; + recv_points.reserve( recv_polygons.counts[p] ); + for ( idx_t j = 0; j < recv_polygons.counts[p] / 2; ++j ) { + PointXY pxy( *( recv_polygons.begin() + recv_polygons.displs[p] + 2 * j + XX ), + *( recv_polygons.begin() + recv_polygons.displs[p] + 2 * j + YY ) ); + recv_points.push_back( pxy ); + } + polygons.emplace_back( new util::ExplicitPartitionPolygon( std::move( recv_points ) ) ); + } +} + // void StructuredPartitionPolygon::print( std::ostream& out ) const { // out << "polygon:{" // << "halo:" << halo_ << ",size:" << polygon_.size() << ",nodes:" << static_cast( polygon_ ) diff --git a/src/atlas/grid/StructuredPartitionPolygon.h b/src/atlas/grid/StructuredPartitionPolygon.h index 4ef3de1be..bd5e16ba5 100644 --- a/src/atlas/grid/StructuredPartitionPolygon.h +++ b/src/atlas/grid/StructuredPartitionPolygon.h @@ -42,10 +42,14 @@ class StructuredPartitionPolygon : public util::PartitionPolygon { void outputPythonScript( const eckit::PathName&, const eckit::Configuration& = util::NoConfig() ) const override; - const std::vector& xy() const override { return points_; } - const std::vector& lonlat() const override { return points_; } + PointsXY xy() const override; + + PointsLonLat lonlat() const override; + const RectangularDomain& inscribedDomain() const override { return inscribed_domain_; } + void allGather( util::PartitionPolygons& ) const override; + private: // void print( std::ostream& ) const; @@ -56,10 +60,10 @@ class StructuredPartitionPolygon : public util::PartitionPolygon { //util::Polygon::edge_set_t compute_edges( std::vector&, std::vector& ); + private: - //util::Polygon polygon_; - std::vector points_; - std::vector inner_bounding_box_; + PointsXY points_; + PointsXY inner_bounding_box_; RectangularDomain inscribed_domain_; const functionspace::FunctionSpaceImpl& fs_; idx_t halo_; diff --git a/src/atlas/util/Polygon.cc b/src/atlas/util/Polygon.cc index cc73fe361..673e556a7 100644 --- a/src/atlas/util/Polygon.cc +++ b/src/atlas/util/Polygon.cc @@ -242,6 +242,24 @@ const Point2& PolygonCoordinates::centroid() const { return centroid_; } +Polygon::edge_set_t ExplicitPartitionPolygon::compute_edges( idx_t points_size ) { + util::Polygon::edge_set_t edges; + auto add_edge = [&]( idx_t p1, idx_t p2 ) { + util::Polygon::edge_t edge = {p1, p2}; + edges.insert( edge ); + // Log::info() << edge.first << " " << edge.second << std::endl; + }; + for ( idx_t p = 0; p < points_size - 2; ++p ) { + add_edge( p, p + 1 ); + } + add_edge( points_size - 2, 0 ); + return edges; +} + +void ExplicitPartitionPolygon::allGather( PartitionPolygons& ) const { + ATLAS_NOTIMPLEMENTED; +} + //------------------------------------------------------------------------------------------------------ } // namespace util diff --git a/src/atlas/util/Polygon.h b/src/atlas/util/Polygon.h index 4d24c8cf3..40238329b 100644 --- a/src/atlas/util/Polygon.h +++ b/src/atlas/util/Polygon.h @@ -90,14 +90,18 @@ class Polygon : public std::vector { //------------------------------------------------------------------------------------------------------ +class PartitionPolygons; + class PartitionPolygon : public Polygon, util::Object { public: using Polygon::Polygon; + using PointsXY = std::vector; + using PointsLonLat = std::vector; /// @brief Return inscribed rectangular domain (not rotated) virtual const RectangularDomain& inscribedDomain() const; - /// @brief Return the memory footprint of the Polygon + /// @brief Return value of halo virtual idx_t halo() const { return 0; } /// @brief Return the memory footprint of the Polygon @@ -106,9 +110,33 @@ class PartitionPolygon : public Polygon, util::Object { /// @brief Output a python script that plots the partition virtual void outputPythonScript( const eckit::PathName&, const eckit::Configuration& = util::NoConfig() ) const {} - virtual const std::vector& xy() const = 0; + /// @brief All (x,y) coordinates defining a polygon. Last point should match first. + virtual PointsXY xy() const = 0; + + /// @brief All (lon,lat) coordinates defining a polygon. Last point should match first. + virtual PointsLonLat lonlat() const = 0; + + virtual void allGather( PartitionPolygons& ) const = 0; +}; + +//------------------------------------------------------------------------------------------------------ + +class ExplicitPartitionPolygon : public util::PartitionPolygon { +public: + explicit ExplicitPartitionPolygon( PointsXY&& points ) : points_( std::move( points ) ) { + setup( compute_edges( points_.size() ) ); + } + + PointsXY xy() const override { return points_; } + PointsLonLat lonlat() const override { return points_; } + +private: + static util::Polygon::edge_set_t compute_edges( idx_t points_size ); + + void allGather( util::PartitionPolygons& ) const override; - virtual const std::vector& lonlat() const = 0; +private: + std::vector points_; }; //------------------------------------------------------------------------------------------------------ diff --git a/src/atlas/util/VectorOfAbstract.h b/src/atlas/util/VectorOfAbstract.h index fe0576a94..5dec52c6a 100644 --- a/src/atlas/util/VectorOfAbstract.h +++ b/src/atlas/util/VectorOfAbstract.h @@ -72,6 +72,7 @@ class VectorOfAbstract { container_.emplace_back( std::forward( args )... ); } container_type& get() { return container_; } + void clear() { return container_.clear(); } private: container_type container_; From 5db6fba6ec101916e93b0f0b6723ebfc7b71891c Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 1 Apr 2020 18:00:40 +0100 Subject: [PATCH 014/145] ATLAS-270 Make mesh::PartitionPolygon derive from util::PartitionPolygon, and link with functionspace::NodeColumns --- src/atlas/functionspace/NodeColumns.h | 4 + src/atlas/mesh/Mesh.h | 5 +- src/atlas/mesh/PartitionPolygon.cc | 119 +++++++++++++++++++++++--- src/atlas/mesh/PartitionPolygon.h | 14 ++- src/atlas/mesh/detail/MeshImpl.cc | 7 ++ src/atlas/mesh/detail/MeshImpl.h | 6 ++ 6 files changed, 138 insertions(+), 17 deletions(-) diff --git a/src/atlas/functionspace/NodeColumns.h b/src/atlas/functionspace/NodeColumns.h index 2834118a1..1eeba6f24 100644 --- a/src/atlas/functionspace/NodeColumns.h +++ b/src/atlas/functionspace/NodeColumns.h @@ -258,6 +258,10 @@ class NodeColumns : public functionspace::FunctionSpaceImpl { idx_t nb_partitions() const override { return mesh_.nb_partitions(); } + const util::PartitionPolygon& polygon( idx_t halo = 0 ) const override { return mesh_.polygon( halo ); } + + const util::PartitionPolygons& polygons() const override { return mesh_.polygons(); } + private: // methods void constructor(); diff --git a/src/atlas/mesh/Mesh.h b/src/atlas/mesh/Mesh.h index 3e0e68334..7cabf3a00 100644 --- a/src/atlas/mesh/Mesh.h +++ b/src/atlas/mesh/Mesh.h @@ -26,7 +26,8 @@ class Projection; namespace atlas { namespace util { class Metadata; -} +class PartitionPolygons; +} // namespace util } // namespace atlas namespace atlas { @@ -58,6 +59,7 @@ class Mesh : DOXYGEN_HIDE( public util::ObjectHandle ) { using HybridElements = mesh::HybridElements; using PartitionGraph = mesh::detail::PartitionGraph; using Polygon = mesh::PartitionPolygon; + using Polygons = util::PartitionPolygons; public: using Handle::Handle; @@ -117,6 +119,7 @@ class Mesh : DOXYGEN_HIDE( public util::ObjectHandle ) { PartitionGraph::Neighbours nearestNeighbourPartitions() const { return get()->nearestNeighbourPartitions(); } const Polygon& polygon( idx_t halo = 0 ) const { return get()->polygon( halo ); } + const Polygons& polygons() const { return get()->polygons(); } const Grid grid() const { return get()->grid(); } diff --git a/src/atlas/mesh/PartitionPolygon.cc b/src/atlas/mesh/PartitionPolygon.cc index 7cdf4540b..c3c66c62e 100644 --- a/src/atlas/mesh/PartitionPolygon.cc +++ b/src/atlas/mesh/PartitionPolygon.cc @@ -57,7 +57,7 @@ util::Polygon::edge_set_t compute_edges( const detail::MeshImpl& mesh, idx_t hal } // namespace PartitionPolygon::PartitionPolygon( const detail::MeshImpl& mesh, idx_t halo ) : - util::Polygon( compute_edges( mesh, halo ) ), mesh_( mesh ), halo_( halo ) {} + util::PartitionPolygon( compute_edges( mesh, halo ) ), mesh_( mesh ), halo_( halo ) {} size_t PartitionPolygon::footprint() const { size_t size = sizeof( *this ); @@ -70,13 +70,14 @@ void PartitionPolygon::outputPythonScript( const eckit::PathName& filepath, cons int mpi_rank = int( comm.rank() ); int mpi_size = int( comm.size() ); - auto xy = array::make_view( mesh_.nodes().xy() ); - - double xmin = std::numeric_limits::max(); - double xmax = -std::numeric_limits::max(); - for ( idx_t i : *this ) { - xmin = std::min( xmin, xy( i, XX ) ); - xmax = std::max( xmax, xy( i, XX ) ); + auto points = this->xy(); + ATLAS_ASSERT( points.size() == size() ); + const auto nodes_xy = array::make_view( mesh_.nodes().xy() ); + double xmin = std::numeric_limits::max(); + double xmax = -std::numeric_limits::max(); + for ( const auto& p : points ) { + xmin = std::min( xmin, p[XX] ); + xmax = std::max( xmax, p[XX] ); } comm.allReduceInPlace( xmin, eckit::mpi::min() ); comm.allReduceInPlace( xmax, eckit::mpi::max() ); @@ -126,8 +127,8 @@ void PartitionPolygon::outputPythonScript( const eckit::PathName& filepath, cons f << "\n" "verts_" << r << " = ["; - for ( idx_t i : static_cast( *this ) ) { - f << "\n (" << xy( i, XX ) << ", " << xy( i, YY ) << "), "; + for ( const auto& p : points ) { + f << "\n (" << p[XX] << ", " << p[YY] << "), "; } f << "\n]" "\n" @@ -159,14 +160,14 @@ void PartitionPolygon::outputPythonScript( const eckit::PathName& filepath, cons "x_" << r << " = ["; for ( idx_t i = 0; i < count; ++i ) { - f << xy( i, XX ) << ", "; + f << nodes_xy( i, XX ) << ", "; } f << "]" "\n" "y_" << r << " = ["; for ( idx_t i = 0; i < count; ++i ) { - f << xy( i, YY ) << ", "; + f << nodes_xy( i, YY ) << ", "; } f << "]"; } @@ -206,10 +207,104 @@ void PartitionPolygon::outputPythonScript( const eckit::PathName& filepath, cons } } +void PartitionPolygon::allGather( util::PartitionPolygons& polygons ) const { + ATLAS_TRACE(); + + auto xy = array::make_view( mesh_.nodes().xy() ); + auto lonlat = array::make_view( mesh_.nodes().lonlat() ); + + if ( Point2{lonlat( 0, LON ), lonlat( 0, LAT )} != Point2{xy( 0, XX ), xy( 0, YY )} ) { + ATLAS_NOTIMPLEMENTED; // because LonLat != XY + } + + polygons.clear(); + polygons.reserve( mpi::size() ); + + const mpi::Comm& comm = mpi::comm(); + const int mpi_size = int( comm.size() ); + + auto& poly = *this; + + std::vector mypolygon; + mypolygon.reserve( poly.size() * 2 ); + + auto points_xy = poly.xy(); + ATLAS_ASSERT( points_xy.front() == points_xy.back() ); + for ( auto& p : points_xy ) { + mypolygon.push_back( p[XX] ); + mypolygon.push_back( p[YY] ); + } + ATLAS_ASSERT( mypolygon.size() >= 4 ); + + eckit::mpi::Buffer recv_polygons( mpi_size ); + + comm.allGatherv( mypolygon.begin(), mypolygon.end(), recv_polygons ); + + for ( idx_t p = 0; p < mpi_size; ++p ) { + PointsXY recv_points; + recv_points.reserve( recv_polygons.counts[p] ); + for ( idx_t j = 0; j < recv_polygons.counts[p] / 2; ++j ) { + PointXY pxy( *( recv_polygons.begin() + recv_polygons.displs[p] + 2 * j + XX ), + *( recv_polygons.begin() + recv_polygons.displs[p] + 2 * j + YY ) ); + recv_points.push_back( pxy ); + } + polygons.emplace_back( new util::ExplicitPartitionPolygon( std::move( recv_points ) ) ); + } +} + void PartitionPolygon::print( std::ostream& out ) const { out << "polygon:{" << "halo:" << halo_ << ",size:" << size() << ",nodes:" << static_cast( *this ) << "}"; } +PartitionPolygon::PointsXY PartitionPolygon::xy() const { + PointsXY points_xy; + points_xy.reserve( size() ); + + auto xy_view = array::make_view( mesh_.nodes().xy() ); + auto flags = array::make_view( mesh_.nodes().flags() ); + + bool domain_includes_north_pole = false; + bool domain_includes_south_pole = false; + if ( mesh_.grid() ) { + if ( mesh_.grid().domain() && not mesh_.grid().projection() ) { + domain_includes_north_pole = mesh_.grid().domain().containsNorthPole(); + domain_includes_south_pole = mesh_.grid().domain().containsSouthPole(); + } + } + auto bc_north = [&flags, &domain_includes_north_pole]( idx_t n ) { + if ( domain_includes_north_pole ) { + using Topology = atlas::mesh::Nodes::Topology; + return Topology::check( flags( n ), Topology::BC | Topology::NORTH ); + } + return false; + }; + auto bc_south = [&flags, &domain_includes_south_pole]( idx_t n ) { + if ( domain_includes_south_pole ) { + using Topology = atlas::mesh::Nodes::Topology; + return Topology::check( flags( n ), Topology::BC | Topology::SOUTH ); + } + return false; + }; + + for ( idx_t i : static_cast( *this ) ) { + double y = bc_north( i ) ? 90. : bc_south( i ) ? -90. : xy_view( i, idx_t( YY ) ); + points_xy.emplace_back( xy_view( i, idx_t( XX ) ), y ); + } + return points_xy; +} + +PartitionPolygon::PointsLonLat PartitionPolygon::lonlat() const { + if ( not mesh_.projection() ) { + return xy(); + } + PointsLonLat points_lonlat = xy(); + for ( auto& p : points_lonlat ) { + mesh_.projection().xy2lonlat( p.data() ); + } + return points_lonlat; +} + + } // namespace mesh } // namespace atlas diff --git a/src/atlas/mesh/PartitionPolygon.h b/src/atlas/mesh/PartitionPolygon.h index 1c795186a..e59d7a62c 100644 --- a/src/atlas/mesh/PartitionPolygon.h +++ b/src/atlas/mesh/PartitionPolygon.h @@ -37,7 +37,7 @@ namespace mesh { /** * @brief Polygon class that holds the boundary of a mesh partition */ -class PartitionPolygon : public util::Polygon, public util::Object { +class PartitionPolygon : public util::PartitionPolygon { public: // methods //-- Constructors @@ -46,12 +46,18 @@ class PartitionPolygon : public util::Polygon, public util::Object { //-- Accessors - idx_t halo() const { return halo_; } + idx_t halo() const override { return halo_; } /// @brief Return the memory footprint of the Polygon - size_t footprint() const; + size_t footprint() const override; - void outputPythonScript( const eckit::PathName&, const eckit::Configuration& = util::NoConfig() ) const; + void outputPythonScript( const eckit::PathName&, const eckit::Configuration& = util::NoConfig() ) const override; + + void allGather( util::PartitionPolygons& ) const override; + + PointsXY xy() const override; + + PointsLonLat lonlat() const override; private: void print( std::ostream& ) const; diff --git a/src/atlas/mesh/detail/MeshImpl.cc b/src/atlas/mesh/detail/MeshImpl.cc index dc894de28..1d60d4f03 100644 --- a/src/atlas/mesh/detail/MeshImpl.cc +++ b/src/atlas/mesh/detail/MeshImpl.cc @@ -205,6 +205,13 @@ const PartitionPolygon& MeshImpl::polygon( idx_t halo ) const { return *polygons_[halo]; } +const util::PartitionPolygons& MeshImpl::polygons() const { + if ( all_polygons_.size() == 0 ) { + polygon().allGather( all_polygons_ ); + } + return all_polygons_; +} + void MeshImpl::attachObserver( MeshObserver& observer ) const { if ( std::find( mesh_observers_.begin(), mesh_observers_.end(), &observer ) == mesh_observers_.end() ) { mesh_observers_.push_back( &observer ); diff --git a/src/atlas/mesh/detail/MeshImpl.h b/src/atlas/mesh/detail/MeshImpl.h index 2d5ea2048..f88c29496 100644 --- a/src/atlas/mesh/detail/MeshImpl.h +++ b/src/atlas/mesh/detail/MeshImpl.h @@ -28,6 +28,9 @@ class Stream; } namespace atlas { +namespace util { +class PartitionPolygons; +} class Mesh; namespace mesh { class PartitionPolygon; @@ -107,6 +110,7 @@ class MeshImpl : public util::Object { PartitionGraph::Neighbours nearestNeighbourPartitions() const; const PartitionPolygon& polygon( idx_t halo = 0 ) const; + const util::PartitionPolygons& polygons() const; const Grid grid() const { return grid_; } @@ -149,6 +153,8 @@ class MeshImpl : public util::Object { mutable std::vector> polygons_; + mutable util::PartitionPolygons all_polygons_; // from all partitions + mutable std::vector mesh_observers_; }; From 3eee83cfa2667c29d0596258e3451638cde0017b Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 1 Apr 2020 18:01:29 +0100 Subject: [PATCH 015/145] ATLAS-270 Fix in StructuredMeshGenerator node flags --- .../detail/StructuredMeshGenerator.cc | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/src/atlas/meshgenerator/detail/StructuredMeshGenerator.cc b/src/atlas/meshgenerator/detail/StructuredMeshGenerator.cc index 68cea9382..594a371f3 100644 --- a/src/atlas/meshgenerator/detail/StructuredMeshGenerator.cc +++ b/src/atlas/meshgenerator/detail/StructuredMeshGenerator.cc @@ -983,6 +983,9 @@ void StructuredMeshGenerator::generate_mesh( const StructuredGrid& rg, const atl } } + const int y_numbering = ( rg.y().front() < rg.y().back() ) ? +1 : -1; + + idx_t jnode = 0; l = 0; for ( idx_t jlat = region.north; jlat <= region.south; ++jlat ) { @@ -1017,14 +1020,25 @@ void StructuredMeshGenerator::generate_mesh( const StructuredGrid& rg, const atl ghost( inode ) = 0; halo( inode ) = 0; Topology::reset( flags( inode ) ); - if ( jlat == 0 && !include_north_pole ) { - Topology::set( flags( inode ), Topology::BC | Topology::NORTH ); + Topology::set( flags( inode ), Topology::BC ); + if ( jlat == 0 ) { + if ( y_numbering < 0 && !include_north_pole ) { + Topology::set( flags( inode ), Topology::NORTH ); + } + if ( y_numbering > 0 && !include_south_pole ) { + Topology::set( flags( inode ), Topology::SOUTH ); + } } - if ( jlat == rg.ny() - 1 && !include_south_pole ) { - Topology::set( flags( inode ), Topology::BC | Topology::SOUTH ); + if ( jlat == rg.ny() - 1 ) { + if ( y_numbering < 0 && !include_south_pole ) { + Topology::set( flags( inode ), Topology::SOUTH ); + } + if ( y_numbering > 0 && !include_north_pole ) { + Topology::set( flags( inode ), Topology::NORTH ); + } } if ( jlon == 0 && include_periodic_ghost_points ) { - Topology::set( flags( inode ), Topology::BC | Topology::WEST ); + Topology::set( flags( inode ), Topology::WEST ); } if ( part( inode ) != mypart ) { Topology::set( flags( inode ), Topology::GHOST ); @@ -1059,6 +1073,22 @@ void StructuredMeshGenerator::generate_mesh( const StructuredGrid& rg, const atl Topology::reset( flags( inode ) ); Topology::set( flags( inode ), Topology::BC | Topology::EAST ); Topology::set( flags( inode ), Topology::GHOST ); + if ( jlat == 0 ) { + if ( y_numbering < 0 && !include_north_pole ) { + Topology::set( flags( inode ), Topology::NORTH ); + } + if ( y_numbering > 0 && !include_south_pole ) { + Topology::set( flags( inode ), Topology::SOUTH ); + } + } + if ( jlat == rg.ny() - 1 ) { + if ( y_numbering < 0 && !include_south_pole ) { + Topology::set( flags( inode ), Topology::SOUTH ); + } + if ( y_numbering > 0 && !include_north_pole ) { + Topology::set( flags( inode ), Topology::NORTH ); + } + } ++jnode; } else { @@ -1149,7 +1179,6 @@ void StructuredMeshGenerator::generate_mesh( const StructuredGrid& rg, const atl idx_t quad_nodes[4]; idx_t triag_nodes[3]; - const int y_numbering = ( rg.y().front() < rg.y().back() ) ? +1 : -1; auto fix_quad_orientation = []( idx_t nodes[] ) { idx_t tmp; tmp = nodes[0]; From 32c71f5bc8dd9d3697c0dccee7f20fb65ddceac8 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 3 Apr 2020 22:16:18 +0100 Subject: [PATCH 016/145] ATLAS-270 Add function `void Projection::lonlat2xy(Point2&)` and inverse --- src/atlas/projection/Projection.cc | 9 +++++++++ src/atlas/projection/Projection.h | 6 ++++++ src/atlas/projection/detail/ProjectionImpl.h | 11 +++++++++++ 3 files changed, 26 insertions(+) diff --git a/src/atlas/projection/Projection.cc b/src/atlas/projection/Projection.cc index f01e8b4c9..632085c25 100644 --- a/src/atlas/projection/Projection.cc +++ b/src/atlas/projection/Projection.cc @@ -34,10 +34,19 @@ void atlas::Projection::xy2lonlat( double crd[] ) const { return get()->xy2lonlat( crd ); } +void atlas::Projection::xy2lonlat( Point2& point ) const { + return get()->xy2lonlat( point ); +} + + void atlas::Projection::lonlat2xy( double crd[] ) const { return get()->lonlat2xy( crd ); } +void atlas::Projection::lonlat2xy( Point2& point ) const { + return get()->lonlat2xy( point ); +} + PointLonLat atlas::Projection::lonlat( const PointXY& xy ) const { return get()->lonlat( xy ); } diff --git a/src/atlas/projection/Projection.h b/src/atlas/projection/Projection.h index 195731e62..74aee4426 100644 --- a/src/atlas/projection/Projection.h +++ b/src/atlas/projection/Projection.h @@ -22,6 +22,9 @@ namespace eckit { class Parametrisation; class Hash; +namespace geometry { +class Point2; +} } // namespace eckit //--------------------------------------------------------------------------------------------------------------------- @@ -30,6 +33,7 @@ namespace atlas { class PointLonLat; class PointXY; +using Point2 = eckit::geometry::Point2; //--------------------------------------------------------------------------------------------------------------------- namespace util { @@ -53,7 +57,9 @@ class Projection : DOXYGEN_HIDE( public util::ObjectHandle Date: Fri, 3 Apr 2020 21:21:30 +0100 Subject: [PATCH 017/145] ATLAS-270 Functionspace::projection() function added --- src/atlas/functionspace/FunctionSpace.cc | 4 ++++ src/atlas/functionspace/FunctionSpace.h | 3 +++ src/atlas/functionspace/NodeColumns.h | 2 ++ src/atlas/functionspace/detail/FunctionSpaceImpl.cc | 6 +++++- src/atlas/functionspace/detail/FunctionSpaceImpl.h | 3 +++ src/atlas/functionspace/detail/StructuredColumns.h | 2 ++ 6 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/atlas/functionspace/FunctionSpace.cc b/src/atlas/functionspace/FunctionSpace.cc index 51489f014..97d1ba54e 100644 --- a/src/atlas/functionspace/FunctionSpace.cc +++ b/src/atlas/functionspace/FunctionSpace.cc @@ -70,6 +70,10 @@ const util::PartitionPolygons& FunctionSpace::polygons() const { return get()->polygons(); } +const Projection& FunctionSpace::projection() const { + return get()->projection(); +} + template Field FunctionSpace::createField() const { return get()->createField(); diff --git a/src/atlas/functionspace/FunctionSpace.h b/src/atlas/functionspace/FunctionSpace.h index 638c986ea..9b74b3658 100644 --- a/src/atlas/functionspace/FunctionSpace.h +++ b/src/atlas/functionspace/FunctionSpace.h @@ -22,6 +22,7 @@ class Configuration; namespace atlas { class Field; class FieldSet; +class Projection; namespace functionspace { class FunctionSpaceImpl; } @@ -63,6 +64,8 @@ class FunctionSpace : DOXYGEN_HIDE( public util::ObjectHandle Field FunctionSpaceImpl::createField( const eckit::Configuration& options ) const { return createField( option::datatypeT() | options ); diff --git a/src/atlas/functionspace/detail/FunctionSpaceImpl.h b/src/atlas/functionspace/detail/FunctionSpaceImpl.h index e6b93b249..ec8606871 100644 --- a/src/atlas/functionspace/detail/FunctionSpaceImpl.h +++ b/src/atlas/functionspace/detail/FunctionSpaceImpl.h @@ -25,6 +25,7 @@ class Configuration; namespace atlas { class FieldSet; class Field; +class Projection; namespace util { class Metadata; class PartitionPolygon; @@ -82,6 +83,8 @@ class FunctionSpaceImpl : public util::Object { virtual const util::PartitionPolygons& polygons() const; + virtual const Projection& projection() const; + private: util::Metadata* metadata_; }; diff --git a/src/atlas/functionspace/detail/StructuredColumns.h b/src/atlas/functionspace/detail/StructuredColumns.h index 8272a373a..5da44dfc0 100644 --- a/src/atlas/functionspace/detail/StructuredColumns.h +++ b/src/atlas/functionspace/detail/StructuredColumns.h @@ -109,6 +109,8 @@ class StructuredColumns : public FunctionSpaceImpl { const StructuredGrid& grid() const; + const Projection& projection() const override { return grid().projection(); } + idx_t i_begin( idx_t j ) const { return i_begin_[j]; } idx_t i_end( idx_t j ) const { return i_end_[j]; } From a01b8b29c62dc487ca4f01d86ae656144a29176a Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 1 Apr 2020 18:21:09 +0100 Subject: [PATCH 018/145] ATLAS-270 Extend atlas_test_polygons to also test polygons from NodeColumns --- src/tests/functionspace/CMakeLists.txt | 31 ++++-- src/tests/functionspace/test_polygons.cc | 127 +++++++++++++++++++---- 2 files changed, 129 insertions(+), 29 deletions(-) diff --git a/src/tests/functionspace/CMakeLists.txt b/src/tests/functionspace/CMakeLists.txt index 370967763..9a64a1ee1 100644 --- a/src/tests/functionspace/CMakeLists.txt +++ b/src/tests/functionspace/CMakeLists.txt @@ -43,14 +43,6 @@ ecbuild_add_test( TARGET atlas_test_structuredcolumns CONDITION eckit_HAVE_MPI ) -ecbuild_add_test( TARGET atlas_test_polygons - SOURCES test_polygons.cc - LIBS atlas - MPI 4 - ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} - CONDITION eckit_HAVE_MPI -) - ecbuild_add_test( TARGET atlas_test_pointcloud SOURCES test_pointcloud.cc LIBS atlas @@ -89,3 +81,26 @@ ecbuild_add_test( TARGET atlas_test_stencil_parallel_mpi16 CONDITION eckit_HAVE_MPI ) +ecbuild_add_executable( TARGET atlas_test_polygons + SOURCES test_polygons.cc + LIBS atlas + NOINSTALL +) + +set( _WITH_MPI ) +if( eckit_HAVE_MPI ) + set( _WITH_MPI "MPI 4" ) +endif() + +ecbuild_add_test( TARGET atlas_test_polygons_structuredcolumns + COMMAND atlas_test_polygons ARGS --functionspace StructuredColumns + ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} + ${_WITH_MPI} +) + +ecbuild_add_test( TARGET atlas_test_polygons_nodecolumns + COMMAND atlas_test_polygons ARGS --functionspace NodeColumns + ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} + ${_WITH_MPI} +) + diff --git a/src/tests/functionspace/test_polygons.cc b/src/tests/functionspace/test_polygons.cc index 695818a02..b017eb18f 100644 --- a/src/tests/functionspace/test_polygons.cc +++ b/src/tests/functionspace/test_polygons.cc @@ -11,9 +11,11 @@ #include "atlas/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" +#include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/StructuredGrid.h" +#include "atlas/meshgenerator.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/LonLatPolygon.h" #include "atlas/util/PolygonLocator.h" @@ -27,19 +29,48 @@ namespace atlas { namespace test { namespace { -std::string gridname() { +std::string grid_name() { static std::string _gridname = eckit::Resource( "--grid", "O32" ); return _gridname; } -FunctionSpace functionspace() { +std::string functionspace_name() { + static std::string _name = eckit::Resource( "--functionspace", "StructuredColumns" ); + return _name; +} + +std::string configuration() { + std::stringstream out; + out << "mpi::size()=" << mpi::size() << ",grid_name()=" << grid_name() + << ",functionspace_name()=" << functionspace_name(); + return out.str(); +}; + + +FunctionSpace structured_columns() { static functionspace::StructuredColumns _fs = []() { - Grid grid( gridname() ); + Grid grid( grid_name() ); return functionspace::StructuredColumns{grid}; }(); return _fs; } +FunctionSpace node_columns() { + static functionspace::NodeColumns _fs = []() { + Grid grid( grid_name() ); + Mesh mesh = StructuredMeshGenerator().generate( grid ); + return functionspace::NodeColumns{mesh}; + }(); + return _fs; +} + + +FunctionSpace functionspace() { + static FunctionSpace _fs = functionspace_name() == "NodeColumns" ? node_columns() : structured_columns(); + return _fs; +} + + std::vector& points() { static std::vector _points{ {45., 75.}, {90., 75.}, {135., 75.}, {180., 75.}, {225., 75.}, {270., 75.}, {315., 75.}, @@ -48,6 +79,75 @@ std::vector& points() { }; return _points; } + +void check_part( const std::vector& vec ) { + Log::debug() << "part = " << vec << std::endl; + std::vector expected; + if ( mpi::size() == 1 && grid_name() == "O32" ) { + expected = std::vector{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + } + if ( mpi::size() == 4 && grid_name() == "O32" && functionspace_name() == "StructuredColumns" ) { + expected = std::vector{0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3}; + } + if ( mpi::size() == 4 && grid_name() == "O32" && functionspace_name() == "NodeColumns" ) { + expected = std::vector{0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3}; + } + if ( expected.size() ) { + EXPECT( vec == expected ); + } + else { + Log::warning() << "Check for part not implemented for configuration " << configuration() << std::endl; + } +} + +void check_sizes( const std::vector& vec ) { + Log::debug() << "sizes = " << vec << std::endl; + std::vector expected; + if ( mpi::size() == 1 && grid_name() == "O32" && functionspace_name() == "StructuredColumns" ) { + expected = std::vector{132}; + } + if ( mpi::size() == 4 && grid_name() == "O32" && functionspace_name() == "StructuredColumns" ) { + expected = std::vector{50, 84, 84, 50}; + } + if ( mpi::size() == 1 && grid_name() == "O32" && functionspace_name() == "NodeColumns" ) { + expected = std::vector{167}; + } + if ( mpi::size() == 4 && grid_name() == "O32" && functionspace_name() == "NodeColumns" ) { + expected = std::vector{169, 147, 149, 165}; + } + if ( expected.size() ) { + EXPECT( vec == expected ); + } + else { + Log::warning() << "Check for sizes not implemented for configuration " << configuration() << std::endl; + } +} + +void check_simplified_sizes( const std::vector& vec ) { + Log::debug() << "simplified_sizes = " << vec << std::endl; + std::vector expected; + if ( mpi::size() == 1 && grid_name() == "O32" && functionspace_name() == "StructuredColumns" ) { + expected = std::vector{5}; + } + if ( mpi::size() == 4 && grid_name() == "O32" && functionspace_name() == "StructuredColumns" ) { + expected = std::vector{7, 43, 43, 7}; + } + if ( mpi::size() == 1 && grid_name() == "O32" && functionspace_name() == "NodeColumns" ) { + expected = std::vector{5}; + } + if ( mpi::size() == 4 && grid_name() == "O32" && functionspace_name() == "NodeColumns" ) { + expected = std::vector{8, 5, 8, 7}; + } + if ( expected.size() ) { + EXPECT( vec == expected ); + } + else { + Log::warning() << "Check for simplified_sizes not implemented for configuration " << configuration() + << std::endl; + } +} + + } // namespace //----------------------------------------------------------------------------- @@ -89,24 +189,9 @@ CASE( "test_polygons" ) { Log::debug() << std::endl; } - if ( mpi::size() == 1 && gridname() == "O32" ) { - auto expected_part = std::vector{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - auto expected_sizes = std::vector{132}; - auto expected_simplified_sizes = std::vector{5}; - - EXPECT( part == expected_part ); - EXPECT( sizes == expected_sizes ); - EXPECT( simplified_sizes == expected_simplified_sizes ); - } - if ( mpi::size() == 4 && gridname() == "O32" ) { - auto expected_part = std::vector{0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3}; - auto expected_sizes = std::vector{50, 84, 84, 50}; - auto expected_simplified_sizes = std::vector{7, 43, 43, 7}; - - EXPECT( part == expected_part ); - EXPECT( sizes == expected_sizes ); - EXPECT( simplified_sizes == expected_simplified_sizes ); - } + check_part( part ); + check_sizes( sizes ); + check_simplified_sizes( simplified_sizes ); PolygonLocator find_partition( polygons ); for ( size_t n = 0; n < points().size(); ++n ) { From 6de92c7640b71ebb9210086a33c07e46152b26ac Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 2 Apr 2020 15:52:29 +0100 Subject: [PATCH 019/145] ATLAS-270 Remove PartitionPolygon::lonlat() --- src/atlas/grid/StructuredPartitionPolygon.cc | 19 ++----- src/atlas/grid/StructuredPartitionPolygon.h | 3 +- .../MatchingMeshPartitionerLonLatPolygon.cc | 2 +- src/atlas/mesh/PartitionPolygon.cc | 21 +------- src/atlas/mesh/PartitionPolygon.h | 5 +- src/atlas/util/LonLatPolygon.cc | 23 +-------- src/atlas/util/LonLatPolygon.h | 14 ++---- src/atlas/util/Polygon.cc | 12 +++++ src/atlas/util/Polygon.h | 23 ++++++--- src/atlas/util/PolygonLocator.h | 49 ++++++++++++------- 10 files changed, 73 insertions(+), 98 deletions(-) diff --git a/src/atlas/grid/StructuredPartitionPolygon.cc b/src/atlas/grid/StructuredPartitionPolygon.cc index ac0186071..689cd81de 100644 --- a/src/atlas/grid/StructuredPartitionPolygon.cc +++ b/src/atlas/grid/StructuredPartitionPolygon.cc @@ -365,7 +365,7 @@ void StructuredPartitionPolygon::outputPythonScript( const eckit::PathName& file "\n" "import matplotlib.pyplot as plt" "\n" "from matplotlib.path import Path" "\n" "import matplotlib.patches as patches" - "\n" + "\n" "\n" "from itertools import cycle" "\n" "import matplotlib.cm as cm" "\n" "import numpy as np" @@ -427,20 +427,11 @@ util::PartitionPolygon::PointsXY StructuredPartitionPolygon::xy() const { return points_; } -util::PartitionPolygon::PointsLonLat StructuredPartitionPolygon::lonlat() const { - return points_; -} - -void StructuredPartitionPolygon::allGather( util::PartitionPolygons& polygons ) const { +void StructuredPartitionPolygon::allGather( util::PartitionPolygons& polygons_ ) const { ATLAS_TRACE(); - const auto& fs = dynamic_cast( fs_ ); - if ( fs.grid().projection() ) { - ATLAS_NOTIMPLEMENTED; // because LonLat != XY - } - - polygons.clear(); - polygons.reserve( mpi::size() ); + polygons_.clear(); + polygons_.reserve( mpi::size() ); const mpi::Comm& comm = mpi::comm(); const int mpi_size = int( comm.size() ); @@ -468,7 +459,7 @@ void StructuredPartitionPolygon::allGather( util::PartitionPolygons& polygons ) *( recv_polygons.begin() + recv_polygons.displs[p] + 2 * j + YY ) ); recv_points.push_back( pxy ); } - polygons.emplace_back( new util::ExplicitPartitionPolygon( std::move( recv_points ) ) ); + polygons_.emplace_back( new util::ExplicitPartitionPolygon( std::move( recv_points ) ) ); } } diff --git a/src/atlas/grid/StructuredPartitionPolygon.h b/src/atlas/grid/StructuredPartitionPolygon.h index bd5e16ba5..9fb29e286 100644 --- a/src/atlas/grid/StructuredPartitionPolygon.h +++ b/src/atlas/grid/StructuredPartitionPolygon.h @@ -14,6 +14,7 @@ #include "atlas/domain/Domain.h" #include "atlas/functionspace/FunctionSpace.h" #include "atlas/library/config.h" +#include "atlas/projection/Projection.h" #include "atlas/util/Config.h" #include "atlas/util/Point.h" #include "atlas/util/Polygon.h" @@ -44,8 +45,6 @@ class StructuredPartitionPolygon : public util::PartitionPolygon { PointsXY xy() const override; - PointsLonLat lonlat() const override; - const RectangularDomain& inscribedDomain() const override { return inscribed_domain_; } void allGather( util::PartitionPolygons& ) const override; diff --git a/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.cc b/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.cc index 18cd5de53..c2df746f1 100644 --- a/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.cc +++ b/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.cc @@ -49,7 +49,7 @@ void MatchingMeshPartitionerLonLatPolygon::partition( const Grid& grid, int part bool includesNorthPole = ( mpi_rank == 0 ); bool includesSouthPole = ( mpi_rank == mpi_size - 1 ); - const util::LonLatPolygon poly( prePartitionedMesh_.polygon( 0 ), prePartitionedMesh_.nodes().lonlat() ); + const util::LonLatPolygon poly{prePartitionedMesh_.polygon( 0 )}; { eckit::ProgressTimer timer( "Partitioning", grid.size(), "point", double( 10 ), atlas::Log::trace() ); diff --git a/src/atlas/mesh/PartitionPolygon.cc b/src/atlas/mesh/PartitionPolygon.cc index c3c66c62e..aacd1d957 100644 --- a/src/atlas/mesh/PartitionPolygon.cc +++ b/src/atlas/mesh/PartitionPolygon.cc @@ -210,13 +210,6 @@ void PartitionPolygon::outputPythonScript( const eckit::PathName& filepath, cons void PartitionPolygon::allGather( util::PartitionPolygons& polygons ) const { ATLAS_TRACE(); - auto xy = array::make_view( mesh_.nodes().xy() ); - auto lonlat = array::make_view( mesh_.nodes().lonlat() ); - - if ( Point2{lonlat( 0, LON ), lonlat( 0, LAT )} != Point2{xy( 0, XX ), xy( 0, YY )} ) { - ATLAS_NOTIMPLEMENTED; // because LonLat != XY - } - polygons.clear(); polygons.reserve( mpi::size() ); @@ -267,7 +260,7 @@ PartitionPolygon::PointsXY PartitionPolygon::xy() const { bool domain_includes_north_pole = false; bool domain_includes_south_pole = false; if ( mesh_.grid() ) { - if ( mesh_.grid().domain() && not mesh_.grid().projection() ) { + if ( mesh_.grid().domain() ) { domain_includes_north_pole = mesh_.grid().domain().containsNorthPole(); domain_includes_south_pole = mesh_.grid().domain().containsSouthPole(); } @@ -294,17 +287,5 @@ PartitionPolygon::PointsXY PartitionPolygon::xy() const { return points_xy; } -PartitionPolygon::PointsLonLat PartitionPolygon::lonlat() const { - if ( not mesh_.projection() ) { - return xy(); - } - PointsLonLat points_lonlat = xy(); - for ( auto& p : points_lonlat ) { - mesh_.projection().xy2lonlat( p.data() ); - } - return points_lonlat; -} - - } // namespace mesh } // namespace atlas diff --git a/src/atlas/mesh/PartitionPolygon.h b/src/atlas/mesh/PartitionPolygon.h index e59d7a62c..14b32b538 100644 --- a/src/atlas/mesh/PartitionPolygon.h +++ b/src/atlas/mesh/PartitionPolygon.h @@ -20,6 +20,7 @@ #include "atlas/util/Object.h" #include "atlas/library/config.h" +#include "atlas/projection/Projection.h" #include "atlas/util/Config.h" #include "atlas/util/Polygon.h" @@ -53,11 +54,9 @@ class PartitionPolygon : public util::PartitionPolygon { void outputPythonScript( const eckit::PathName&, const eckit::Configuration& = util::NoConfig() ) const override; - void allGather( util::PartitionPolygons& ) const override; - PointsXY xy() const override; - PointsLonLat lonlat() const override; + void allGather( util::PartitionPolygons& ) const override; private: void print( std::ostream& ) const; diff --git a/src/atlas/util/LonLatPolygon.cc b/src/atlas/util/LonLatPolygon.cc index c7f7e7a9c..f89852b47 100644 --- a/src/atlas/util/LonLatPolygon.cc +++ b/src/atlas/util/LonLatPolygon.cc @@ -80,23 +80,8 @@ double compute_inner_radius_squared( const PointContainer& points, const PointLo //------------------------------------------------------------------------------------------------------ -LonLatPolygon::LonLatPolygon( const Polygon& poly, const atlas::Field& coordinates, bool removeAlignedPoints ) : - PolygonCoordinates( poly, coordinates, removeAlignedPoints ) { - centroid_ = compute_centroid( coordinates_ ); - inner_radius_squared_ = compute_inner_radius_squared( coordinates_, centroid_ ); -} - -template > -LonLatPolygon::LonLatPolygon( const PointContainer& points, bool removeAlignedPoints ) : - PolygonCoordinates( points, removeAlignedPoints ) { - centroid_ = compute_centroid( coordinates_ ); - inner_radius_squared_ = compute_inner_radius_squared( coordinates_, centroid_ ); - - ATLAS_ASSERT( contains( centroid_ ) ); -} - LonLatPolygon::LonLatPolygon( const PartitionPolygon& partition_polygon ) : - PolygonCoordinates( partition_polygon.lonlat(), true ) { + PolygonCoordinates( partition_polygon.xy(), true ) { RectangularLonLatDomain inscribed = partition_polygon.inscribedDomain(); if ( inscribed ) { inner_coordinatesMin_ = {inscribed.xmin(), inscribed.ymin()}; @@ -168,11 +153,5 @@ bool LonLatPolygon::contains( const Point2& P ) const { //------------------------------------------------------------------------------------------------------ -template LonLatPolygon::LonLatPolygon( const std::vector&, bool ); -template LonLatPolygon::LonLatPolygon( const std::vector&, bool ); -template LonLatPolygon::LonLatPolygon( const std::vector&, bool ); - -//------------------------------------------------------------------------------------------------------ - } // namespace util } // namespace atlas diff --git a/src/atlas/util/LonLatPolygon.h b/src/atlas/util/LonLatPolygon.h index bfa9617a3..a8084e7d5 100644 --- a/src/atlas/util/LonLatPolygon.h +++ b/src/atlas/util/LonLatPolygon.h @@ -22,26 +22,18 @@ class LonLatPolygons; //------------------------------------------------------------------------------------------------------ -/// @brief Implement PolygonCoordinates::contains for a polygon defined in LonLat space. +/// @brief Implement PolygonCoordinates::contains for a polygon defined in XY space. class LonLatPolygon : public PolygonCoordinates { -private: - template - using enable_if_not_polygon = typename std::enable_if::value, int>::type; - public: using Vector = LonLatPolygons; - LonLatPolygon( const Polygon&, const atlas::Field& coordinates, bool removeAlignedPoints = true ); LonLatPolygon( const PartitionPolygon& ); - template = 0> - LonLatPolygon( const PointContainer& points, bool removeAlignedPoints = true ); - /// @brief Point-in-polygon test based on winding number /// @note reference Inclusion of a Point in a Polygon /// @param[in] P given point - /// @return if point is in polygon - bool contains( const Point2& P ) const override; + /// @return if point (x,y) is in polygon + bool contains( const Point2& Pxy ) const override; private: PointLonLat centroid_; diff --git a/src/atlas/util/Polygon.cc b/src/atlas/util/Polygon.cc index 673e556a7..680f6902e 100644 --- a/src/atlas/util/Polygon.cc +++ b/src/atlas/util/Polygon.cc @@ -18,6 +18,7 @@ #include "atlas/array.h" #include "atlas/domain/Domain.h" #include "atlas/field/Field.h" +#include "atlas/projection/Projection.h" #include "atlas/runtime/Exception.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Polygon.h" @@ -242,6 +243,17 @@ const Point2& PolygonCoordinates::centroid() const { return centroid_; } +void PolygonCoordinates::print( std::ostream& out ) const { + out << "["; + for ( size_t i = 0; i < coordinates_.size(); ++i ) { + if ( i > 0 ) { + out << " "; + } + out << coordinates_[i]; + } + out << "]"; +} + Polygon::edge_set_t ExplicitPartitionPolygon::compute_edges( idx_t points_size ) { util::Polygon::edge_set_t edges; auto add_edge = [&]( idx_t p1, idx_t p2 ) { diff --git a/src/atlas/util/Polygon.h b/src/atlas/util/Polygon.h index 40238329b..c01d7de8b 100644 --- a/src/atlas/util/Polygon.h +++ b/src/atlas/util/Polygon.h @@ -27,6 +27,8 @@ #include "atlas/util/Point.h" #include "atlas/util/VectorOfAbstract.h" +#include "atlas/projection/Projection.h" // for ExplicitPartitionPolygon + namespace eckit { class PathName; } @@ -34,6 +36,7 @@ class PathName; namespace atlas { class Field; class RectangularDomain; +class Projection; } // namespace atlas namespace atlas { @@ -113,9 +116,6 @@ class PartitionPolygon : public Polygon, util::Object { /// @brief All (x,y) coordinates defining a polygon. Last point should match first. virtual PointsXY xy() const = 0; - /// @brief All (lon,lat) coordinates defining a polygon. Last point should match first. - virtual PointsLonLat lonlat() const = 0; - virtual void allGather( PartitionPolygons& ) const = 0; }; @@ -123,21 +123,28 @@ class PartitionPolygon : public Polygon, util::Object { class ExplicitPartitionPolygon : public util::PartitionPolygon { public: - explicit ExplicitPartitionPolygon( PointsXY&& points ) : points_( std::move( points ) ) { + explicit ExplicitPartitionPolygon( PointsXY&& points ) : + ExplicitPartitionPolygon( std::move( points ), RectangularDomain() ) {} + + explicit ExplicitPartitionPolygon( PointsXY&& points, const RectangularDomain& inscribed ) : + points_( std::move( points ) ), inscribed_( inscribed ) { setup( compute_edges( points_.size() ) ); } PointsXY xy() const override { return points_; } - PointsLonLat lonlat() const override { return points_; } + + void allGather( util::PartitionPolygons& ) const override; + + const RectangularDomain& inscribedDomain() const override { return inscribed_; } private: static util::Polygon::edge_set_t compute_edges( idx_t points_size ); - void allGather( util::PartitionPolygons& ) const override; private: std::vector points_; -}; + RectangularDomain inscribed_; +}; // namespace util //------------------------------------------------------------------------------------------------------ @@ -175,6 +182,8 @@ class PolygonCoordinates { idx_t size() const { return coordinates_.size(); } + void print( std::ostream& ) const; + protected: // -- Members diff --git a/src/atlas/util/PolygonLocator.h b/src/atlas/util/PolygonLocator.h index cd2079f9e..35db0ea4a 100644 --- a/src/atlas/util/PolygonLocator.h +++ b/src/atlas/util/PolygonLocator.h @@ -10,12 +10,13 @@ #pragma once -#include #include +#include +#include "atlas/library/config.h" +#include "atlas/projection/Projection.h" #include "atlas/util/KDTree.h" #include "atlas/util/Polygon.h" -#include "atlas/library/config.h" namespace atlas { namespace util { @@ -30,28 +31,28 @@ namespace util { /// visited in order of shortest distance, to check if the point is contained within. class PolygonLocator { public: - /// @brief Construct PolygonLocator from shared_ptr of polygons - PolygonLocator( const std::shared_ptr polygons, idx_t k = 4 ) : - shared_polygons_( polygons ), - polygons_( *shared_polygons_ ) { - k_ = std::min( k, polygons_.size() ); + PolygonLocator( const std::shared_ptr polygons, + const Projection& projection = Projection() ) : + shared_polygons_( polygons ), polygons_( *shared_polygons_ ), projection_( projection ) { + k_ = std::min( k_, polygons_.size() ); buildKDTree(); } /// @brief Construct PolygonLocator and move polygons inside. - PolygonLocator( PolygonCoordinates::Vector&& polygons, idx_t k = 4 ) : - shared_polygons_( std::make_shared( std::move(polygons) ) ), - polygons_( *shared_polygons_ ) { - k_ = std::min( k, polygons_.size() ); + PolygonLocator( PolygonCoordinates::Vector&& polygons, const Projection& projection = Projection() ) : + shared_polygons_( std::make_shared( std::move( polygons ) ) ), + polygons_( *shared_polygons_ ), + projection_( projection ) { + k_ = std::min( k_, polygons_.size() ); buildKDTree(); } /// @brief Construct PolygonLocator using reference to polygons. /// !WARNING! polygons should not go out of scope before PolygonLocator - PolygonLocator( const PolygonCoordinates::Vector& polygons, idx_t k = 4 ) : - polygons_( polygons ) { - k_ = std::min( k, polygons_.size() ); + PolygonLocator( const PolygonCoordinates::Vector& polygons, const Projection& projection = Projection() ) : + polygons_( polygons ), projection_( projection ) { + k_ = std::min( k_, polygons_.size() ); buildKDTree(); } @@ -61,7 +62,7 @@ class PolygonLocator { idx_t partition{-1}; for ( size_t i = 0; i < found.size(); ++i ) { idx_t ii = found[i].payload(); - if ( polygons_[ii].contains( point ) ) { + if ( polygons_[ii].contains( lonlat2xy( point ) ) ) { partition = ii; break; } @@ -71,18 +72,30 @@ class PolygonLocator { } private: - void buildKDTree() { kdtree_.reserve( polygons_.size() ); for ( idx_t p = 0; p < polygons_.size(); ++p ) { - kdtree_.insert( polygons_[p].centroid(), p ); + kdtree_.insert( xy2lonlat( polygons_[p].centroid() ), p ); } kdtree_.build(); } + Point2 lonlat2xy( const Point2& lonlat ) const { + Point2 xy{lonlat}; + projection_.lonlat2xy( xy.data() ); + return xy; + } + Point2 xy2lonlat( const Point2& xy ) const { + Point2 lonlat{xy}; + projection_.xy2lonlat( lonlat.data() ); + return lonlat; + } + + std::shared_ptr shared_polygons_; const PolygonCoordinates::Vector& polygons_; - idx_t k_; + Projection projection_; + idx_t k_{4}; KDTree kdtree_; }; From 36316c449618b8917d545f5a7b85934a87a7c32a Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 3 Apr 2020 22:08:14 +0100 Subject: [PATCH 020/145] ATLAS-270 Simplify StructuredPartitionPolygon upon creation --- src/atlas/grid/StructuredPartitionPolygon.cc | 201 ++++++++++--------- src/tests/functionspace/test_polygons.cc | 10 +- 2 files changed, 109 insertions(+), 102 deletions(-) diff --git a/src/atlas/grid/StructuredPartitionPolygon.cc b/src/atlas/grid/StructuredPartitionPolygon.cc index 689cd81de..1acbb5f16 100644 --- a/src/atlas/grid/StructuredPartitionPolygon.cc +++ b/src/atlas/grid/StructuredPartitionPolygon.cc @@ -40,6 +40,20 @@ util::Polygon::edge_set_t compute_edges( const functionspace::FunctionSpaceImpl& auto equal = []( const double& a, const double& b ) { return std::abs( a - b ) < 1.e-12; }; util::Polygon::edge_set_t edges; + auto last_edge_horizontal = [&]() { + if ( edges.empty() ) { + return false; + } + size_t size = points.size(); + return equal( points.at( size - 1 )[YY], points.at( size - 2 )[YY] ); + }; + auto last_edge_vertical = [&]() { + if ( edges.empty() ) { + return false; + } + size_t size = points.size(); + return equal( points.at( size - 1 )[XX], points.at( size - 2 )[XX] ); + }; PointXY p; idx_t c{0}; @@ -48,29 +62,36 @@ util::Polygon::edge_set_t compute_edges( const functionspace::FunctionSpaceImpl& auto add_edge = [&]( idx_t p1, idx_t p2 ) { util::Polygon::edge_t edge = {p1, p2}; edges.insert( edge ); - //Log::info() << edge.first << " " << edge.second << std::endl; + // Log::info() << edge.first << " " << edge.second << std::endl; }; - auto make_corner = [&]() { - PointXY ptmp; - ptmp = p; - p[XX] = points.back()[XX]; - p[YY] = 0.5 * ( points.back()[YY] + grid.y( j ) ); - points.emplace_back( p ); - add_edge( c, c + 1 ); - c++; - p = ptmp; - - - ptmp = p; - p[YY] = points.back()[YY]; - points.emplace_back( p ); - add_edge( c, c + 1 ); - c++; - p = ptmp; + auto add_point = [&]( const Point2& point ) { + points.emplace_back( point ); + // Log::info() << "add point (" << points.size() - 1 << ") " << point << std::endl; }; - bool debug = true; + auto add_vertical_edge = [&]( const Point2& point ) { + if ( last_edge_vertical() ) { + points.back()[YY] = point[YY]; + // Log::info() << "mod point (" << points.size() - 1 << ") " << point << std::endl; + } + else { + add_point( point ); + add_edge( points.size() - 2, points.size() - 1 ); + c++; + } + }; + auto add_horizontal_edge = [&]( const Point2& point ) { + if ( last_edge_horizontal() ) { + points.back()[XX] = point[XX]; + // Log::info() << "mod point (" << points.size() - 1 << ") " << point << std::endl; + } + else { + add_point( point ); + add_edge( points.size() - 2, points.size() - 1 ); + c++; + } + }; double ymax, ymin, xmax, xmin; @@ -91,7 +112,7 @@ util::Polygon::edge_set_t compute_edges( const functionspace::FunctionSpaceImpl& else { p[XX] = 0.5 * ( grid.x( i - 1, j ) + grid.x( i, j ) ); } - points.emplace_back( p ); + add_point( p ); } // Top right point @@ -110,9 +131,7 @@ util::Polygon::edge_set_t compute_edges( const functionspace::FunctionSpaceImpl& else { p[XX] = 0.5 * ( grid.x( i, j ) + grid.x( i + 1, j ) ); } - points.emplace_back( p ); - add_edge( c, c + 1 ); - c++; + add_horizontal_edge( p ); ymax = p[YY]; xmax = p[XX]; @@ -128,30 +147,27 @@ util::Polygon::edge_set_t compute_edges( const functionspace::FunctionSpaceImpl& else { p[XX] = 0.5 * ( grid.x( i, j ) + grid.x( i + 1, j ) ); } - if ( j > fs.j_begin() && not equal( p[XX], points.back()[XX] ) ) { - if ( not debug ) { - make_corner(); - } - else { - PointXY ptmp = p; - p[XX] = points.back()[XX]; - p[YY] = 0.5 * ( points.back()[YY] + p[YY] ); - points.emplace_back( p ); - add_edge( c, c + 1 ); - c++; - p = ptmp; - - ptmp = p; - p[YY] = points.back()[YY]; - points.emplace_back( p ); - add_edge( c, c + 1 ); - c++; - p = ptmp; - } + if ( p == points.back() ) { + continue; } - points.emplace_back( p ); - add_edge( c, c + 1 ); - c++; + if ( not equal( p[XX], points.back()[XX] ) ) { + // Make a corner plus horizontal edge + + PointXY ptmp = p; + + // vertical edge + p[XX] = points.back()[XX]; + p[YY] = 0.5 * ( points.back()[YY] + p[YY] ); + add_vertical_edge( p ); + p = ptmp; + + // horizontal edge + ptmp = p; + p[YY] = points.back()[YY]; + add_horizontal_edge( p ); + p = ptmp; + } + add_vertical_edge( p ); xmax = std::min( xmax, p[XX] ); } @@ -179,30 +195,22 @@ util::Polygon::edge_set_t compute_edges( const functionspace::FunctionSpaceImpl& PointXY pmin = p; if ( not equal( p[XX], points.back()[XX] ) ) { - if ( not debug ) { - make_corner(); - } - else { - PointXY ptmp; - ptmp = p; - p[XX] = points.back()[XX]; - p[YY] = 0.5 * ( points.back()[YY] + grid.y( j ) ); - points.emplace_back( p ); - add_edge( c, c + 1 ); - c++; - pmin = p; - xmax = std::min( xmax, p[XX] ); + PointXY ptmp; + ptmp = p; + p[XX] = points.back()[XX]; + p[YY] = 0.5 * ( points.back()[YY] + grid.y( j ) ); + add_vertical_edge( p ); - p = ptmp; + pmin = p; + xmax = std::min( xmax, p[XX] ); + p = ptmp; - ptmp = p; - p[YY] = points.back()[YY]; - points.emplace_back( p ); - add_edge( c, c + 1 ); - c++; - p = ptmp; - } + + ptmp = p; + p[YY] = points.back()[YY]; + add_horizontal_edge( p ); + p = ptmp; } if ( xmax - grid.xspace().dx()[j] < grid.x( i, j ) ) { xmax = std::min( xmax, 0.5 * ( grid.x( i + 1, j ) + grid.x( i, j ) ) ); @@ -210,10 +218,7 @@ util::Polygon::edge_set_t compute_edges( const functionspace::FunctionSpaceImpl& else { ymin = pmin[YY]; } - - points.emplace_back( p ); - add_edge( c, c + 1 ); - c++; + add_vertical_edge( p ); } // Bottom left point @@ -232,9 +237,7 @@ util::Polygon::edge_set_t compute_edges( const functionspace::FunctionSpaceImpl& else { p[XX] = 0.5 * ( grid.x( i - 1, j ) + grid.x( i, j ) ); } - points.emplace_back( p ); - add_edge( c, c + 1 ); - c++; + add_horizontal_edge( p ); xmin = p[XX]; } // Left side @@ -263,37 +266,37 @@ util::Polygon::edge_set_t compute_edges( const functionspace::FunctionSpaceImpl& } } - if ( j < fs.j_end() - 1 && not equal( p[XX], points.back()[XX] ) ) { - if ( not debug ) { - make_corner(); - } - else { - PointXY ptmp; - ptmp = p; - p[XX] = points.back()[XX]; - p[YY] = 0.5 * ( points.back()[YY] + grid.y( j ) ); - points.emplace_back( p ); - add_edge( c, c + 1 ); - c++; - p = ptmp; - - - ptmp = p; - p[YY] = points.back()[YY]; - points.emplace_back( p ); - add_edge( c, c + 1 ); - c++; - p = ptmp; - } + if ( p == points.back() ) { + continue; } + if ( not equal( p[XX], points.back()[XX] ) ) { + PointXY ptmp; + ptmp = p; - points.emplace_back( p ); - add_edge( c, c + 1 ); - c++; + // vertical edge + p[XX] = points.back()[XX]; + p[YY] = 0.5 * ( points.back()[YY] + grid.y( j ) ); + add_vertical_edge( p ); + p = ptmp; + + // horizontal edge + ptmp = p; + p[YY] = points.back()[YY]; + add_horizontal_edge( p ); + p = ptmp; + } + + add_vertical_edge( p ); } } // Connect to top + if ( last_edge_vertical() ) { + util::Polygon::edge_t last_edge = {c - 1, c}; + edges.erase( last_edge ); + points.pop_back(); + c--; + } add_edge( c, 0 ); diff --git a/src/tests/functionspace/test_polygons.cc b/src/tests/functionspace/test_polygons.cc index b017eb18f..89c3e67de 100644 --- a/src/tests/functionspace/test_polygons.cc +++ b/src/tests/functionspace/test_polygons.cc @@ -104,10 +104,10 @@ void check_sizes( const std::vector& vec ) { Log::debug() << "sizes = " << vec << std::endl; std::vector expected; if ( mpi::size() == 1 && grid_name() == "O32" && functionspace_name() == "StructuredColumns" ) { - expected = std::vector{132}; + expected = std::vector{5}; } if ( mpi::size() == 4 && grid_name() == "O32" && functionspace_name() == "StructuredColumns" ) { - expected = std::vector{50, 84, 84, 50}; + expected = std::vector{7, 43, 43, 7}; } if ( mpi::size() == 1 && grid_name() == "O32" && functionspace_name() == "NodeColumns" ) { expected = std::vector{167}; @@ -152,6 +152,10 @@ void check_simplified_sizes( const std::vector& vec ) { //----------------------------------------------------------------------------- +CASE( "info" ) { + functionspace().polygon().outputPythonScript( "polygon.py" ); +} + CASE( "test_polygons" ) { auto fs = functionspace(); @@ -179,7 +183,7 @@ CASE( "test_polygons" ) { Log::debug() << n << " " << points()[n]; // A brute force approach. - // This could be enhanced by a kd-tree search to nearest polygon centroid + // Use PolygonLocator class for optimized result for ( idx_t p = 0; p < polygons.size(); ++p ) { if ( polygons[p].contains( points()[n] ) ) { Log::debug() << " : " << p; From 273ca5c2160a3c6aa254bb94ff81ae9ccc013a0d Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 3 Apr 2020 22:17:33 +0100 Subject: [PATCH 021/145] ATLAS-270 MatchingMeshPartitionerPolygon makes now use of projection --- .../partitioner/MatchingMeshPartitionerLonLatPolygon.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.cc b/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.cc index c2df746f1..3bf34c13c 100644 --- a/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.cc +++ b/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.cc @@ -50,13 +50,15 @@ void MatchingMeshPartitionerLonLatPolygon::partition( const Grid& grid, int part bool includesSouthPole = ( mpi_rank == mpi_size - 1 ); const util::LonLatPolygon poly{prePartitionedMesh_.polygon( 0 )}; + Projection projection = prePartitionedMesh_.projection(); { eckit::ProgressTimer timer( "Partitioning", grid.size(), "point", double( 10 ), atlas::Log::trace() ); size_t i = 0; - for ( const PointLonLat& P : grid.lonlat() ) { + for ( PointLonLat P : grid.lonlat() ) { ++timer; + projection.lonlat2xy( P ); const bool atThePole = ( includesNorthPole && P[LAT] >= poly.coordinatesMax()[LAT] ) || ( includesSouthPole && P[LAT] < poly.coordinatesMin()[LAT] ); From 9e10b51c413c6caca34d38eba0b30f7126279f1e Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 3 Apr 2020 22:32:49 +0100 Subject: [PATCH 022/145] ATLAS-270 Add tests for PolygonLocator with projections --- src/atlas/util/PolygonLocator.h | 30 ++- src/tests/functionspace/CMakeLists.txt | 28 ++- .../functionspace/test_polygons_projection.cc | 224 ++++++++++++++++++ 3 files changed, 276 insertions(+), 6 deletions(-) create mode 100644 src/tests/functionspace/test_polygons_projection.cc diff --git a/src/atlas/util/PolygonLocator.h b/src/atlas/util/PolygonLocator.h index 35db0ea4a..0ba804f21 100644 --- a/src/atlas/util/PolygonLocator.h +++ b/src/atlas/util/PolygonLocator.h @@ -12,12 +12,18 @@ #include #include +#include #include "atlas/library/config.h" #include "atlas/projection/Projection.h" +#include "atlas/runtime/Exception.h" #include "atlas/util/KDTree.h" #include "atlas/util/Polygon.h" +#ifdef POLYGONLOCATOR_DEBUGGING +#include "atlas/runtime/Log.h" +#endif + namespace atlas { namespace util { @@ -62,12 +68,34 @@ class PolygonLocator { idx_t partition{-1}; for ( size_t i = 0; i < found.size(); ++i ) { idx_t ii = found[i].payload(); +#ifdef POLYGONLOCATOR_DEBUGGING + Log::info() << "Search point " << lonlat2xy( point ) << " in polygon " << ii << ": "; + polygons_[ii].print( Log::info() ); + Log::info() << " ... "; +#endif if ( polygons_[ii].contains( lonlat2xy( point ) ) ) { partition = ii; +#ifdef POLYGONLOCATOR_DEBUGGING + Log::info() << "FOUND" << std::endl; +#endif break; } +#ifdef POLYGONLOCATOR_DEBUGGING + Log::info() << "NOT_FOUND" << std::endl; +#endif + } + if ( partition < 0 ) { + std::stringstream out; + out << "Could not find find point {lon,lat} = " << point << " in `k=" << k_ << "` \"nearest\" polygons ["; + for ( size_t i = 0; i < found.size(); ++i ) { + if ( i > 0 ) { + out << ", "; + } + out << found[i].payload(); + } + out << "]. Increase `k`?"; + throw_AssertionFailed( out.str(), Here() ); } - ATLAS_ASSERT( partition >= 0, "Could not find find point in `k` \"nearest\" polygons. Increase `k`?" ); return partition; } diff --git a/src/tests/functionspace/CMakeLists.txt b/src/tests/functionspace/CMakeLists.txt index 9a64a1ee1..ab69b4eb6 100644 --- a/src/tests/functionspace/CMakeLists.txt +++ b/src/tests/functionspace/CMakeLists.txt @@ -81,17 +81,17 @@ ecbuild_add_test( TARGET atlas_test_stencil_parallel_mpi16 CONDITION eckit_HAVE_MPI ) +set( _WITH_MPI ) +if( eckit_HAVE_MPI ) + set( _WITH_MPI "MPI 4" ) +endif() + ecbuild_add_executable( TARGET atlas_test_polygons SOURCES test_polygons.cc LIBS atlas NOINSTALL ) -set( _WITH_MPI ) -if( eckit_HAVE_MPI ) - set( _WITH_MPI "MPI 4" ) -endif() - ecbuild_add_test( TARGET atlas_test_polygons_structuredcolumns COMMAND atlas_test_polygons ARGS --functionspace StructuredColumns ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} @@ -104,3 +104,21 @@ ecbuild_add_test( TARGET atlas_test_polygons_nodecolumns ${_WITH_MPI} ) +ecbuild_add_executable( TARGET atlas_test_polygons_projection + SOURCES test_polygons_projection.cc + LIBS atlas + NOINSTALL +) + +ecbuild_add_test( TARGET atlas_test_polygons_projections_structuredcolumns + COMMAND atlas_test_polygons_projection ARGS --functionspace StructuredColumns + ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} + ${_WITH_MPI} +) + +ecbuild_add_test( TARGET atlas_test_polygons_projections_nodecolumns + COMMAND atlas_test_polygons_projection ARGS --functionspace NodeColumns + ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} + ${_WITH_MPI} +) + diff --git a/src/tests/functionspace/test_polygons_projection.cc b/src/tests/functionspace/test_polygons_projection.cc new file mode 100644 index 000000000..21ce5f8bd --- /dev/null +++ b/src/tests/functionspace/test_polygons_projection.cc @@ -0,0 +1,224 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include "atlas/array/ArrayView.h" +#include "atlas/array/MakeView.h" +#include "atlas/field/Field.h" +#include "atlas/functionspace/NodeColumns.h" +#include "atlas/functionspace/StructuredColumns.h" +#include "atlas/grid/Partitioner.h" +#include "atlas/grid/StructuredGrid.h" +#include "atlas/meshgenerator.h" +#include "atlas/output/Gmsh.h" +#include "atlas/parallel/mpi/mpi.h" +#include "atlas/util/LonLatPolygon.h" +#include "atlas/util/PolygonLocator.h" + +#include "tests/AtlasTestEnvironment.h" + +using namespace atlas::functionspace; +using namespace atlas::util; + +namespace atlas { +namespace test { + +namespace { +std::string grid_name() { + static std::string _gridname = eckit::Resource( "--grid", "L90" ); + return _gridname; +} + +std::string functionspace_name() { + static std::string _name = eckit::Resource( "--functionspace", "StructuredColumns" ); + return _name; +} + +std::string configuration() { + std::stringstream out; + out << "mpi::size()=" << mpi::size() << ",grid_name()=" << grid_name() + << ",functionspace_name()=" << functionspace_name(); + return out.str(); +}; + +Grid grid() { + static Grid _grid{ + option::name( grid_name() ) | util::Config( "projection", util::Config( "type", "rotated_lonlat" )( + "south_pole", std::vector{45., -60.} ) ), + RectangularDomain{{0., 90}, {-45, 45.}}}; + return _grid; +} + +Mesh mesh() { + static Mesh _mesh{StructuredMeshGenerator{util::Config( "partitioner", "checkerboard" )}.generate( grid() )}; + return _mesh; +} + + +FunctionSpace structured_columns() { + static functionspace::StructuredColumns _fs{grid(), grid::Partitioner( "checkerboard" )}; + return _fs; +} + +FunctionSpace node_columns() { + static functionspace::NodeColumns _fs{mesh()}; + return _fs; +} + +FunctionSpace functionspace() { + static FunctionSpace _fs = functionspace_name() == "NodeColumns" ? node_columns() : structured_columns(); + return _fs; +} + + +std::vector& points() { + static std::vector _points{{90., 45.}, {135., 45.}, {60., 0.}, {90., 0.}}; + return _points; +} + +void check_part( const std::vector& vec ) { + Log::debug() << "part = " << vec << std::endl; + std::vector expected; + if ( mpi::size() == 1 && grid_name() == "L90" ) { + expected = std::vector{0, 0, 0, 0}; + } + if ( mpi::size() == 4 && grid_name() == "L90" && functionspace_name() == "StructuredColumns" ) { + expected = std::vector{0, 1, 2, 3}; + } + if ( mpi::size() == 4 && grid_name() == "L90" && functionspace_name() == "NodeColumns" ) { + expected = std::vector{0, 1, 2, 3}; + } + if ( expected.size() ) { + EXPECT( vec == expected ); + } + else { + Log::warning() << "Check for part not implemented for configuration " << configuration() << std::endl; + } +} + +void check_sizes( const std::vector& vec ) { + Log::debug() << "sizes = " << vec << std::endl; + std::vector expected; + if ( mpi::size() == 1 && grid_name() == "L90" && functionspace_name() == "StructuredColumns" ) { + expected = std::vector{5}; + } + if ( mpi::size() == 4 && grid_name() == "L90" && functionspace_name() == "StructuredColumns" ) { + expected = std::vector{7, 9, 5, 5}; + } + if ( mpi::size() == 1 && grid_name() == "L90" && functionspace_name() == "NodeColumns" ) { + expected = std::vector{361}; + } + if ( mpi::size() == 4 && grid_name() == "L90" && functionspace_name() == "NodeColumns" ) { + expected = std::vector{185, 183, 181, 179}; + } + if ( expected.size() ) { + EXPECT( vec == expected ); + } + else { + Log::warning() << "Check for sizes not implemented for configuration " << configuration() << std::endl; + } +} + +void check_simplified_sizes( const std::vector& vec ) { + Log::debug() << "simplified_sizes = " << vec << std::endl; + std::vector expected; + if ( mpi::size() == 1 && grid_name() == "L90" && functionspace_name() == "StructuredColumns" ) { + expected = std::vector{5}; + } + if ( mpi::size() == 4 && grid_name() == "L90" && functionspace_name() == "StructuredColumns" ) { + expected = std::vector{7, 9, 5, 5}; + } + if ( mpi::size() == 1 && grid_name() == "L90" && functionspace_name() == "NodeColumns" ) { + expected = std::vector{5}; + } + if ( mpi::size() == 4 && grid_name() == "L90" && functionspace_name() == "NodeColumns" ) { + expected = std::vector{7, 9, 5, 5}; + } + if ( expected.size() ) { + EXPECT( vec == expected ); + } + else { + Log::warning() << "Check for simplified_sizes not implemented for configuration " << configuration() + << std::endl; + } +} + + +} // namespace + +CASE( "info" ) { + Log::info() << grid().spec() << std::endl; + functionspace().polygon().outputPythonScript( "rotated_polygon.py" ); + output::Gmsh{"mesh.msh", util::Config( "coordinates", "lonlat" )}.write( mesh() ); +} + +//----------------------------------------------------------------------------- + +CASE( "test_polygons" ) { + auto fs = functionspace(); + + ATLAS_TRACE( "computations after setup" ); + auto polygons = util::LonLatPolygons( fs.polygons() ); + + auto projection = fs.projection(); + EXPECT( projection ); + + std::vector sizes( mpi::size() ); + std::vector simplified_sizes( mpi::size() ); + for ( idx_t i = 0; i < mpi::size(); ++i ) { + sizes[i] = fs.polygons()[i].size(); + simplified_sizes[i] = polygons[i].size(); + } + + // Test iterator: + Log::info() << "Iterating PartitionPolygons" << std::endl; + for ( auto& polygon : fs.polygons() ) { + Log::info() << " size of polygon = " << polygon.size() << std::endl; + } + + Log::info() << "Iterating LonLatPolygons" << std::endl; + for ( auto& polygon : polygons ) { + Log::info() << " size of lonlatpolygon = " << polygon.size() << std::endl; + } + + std::vector part( points().size() ); + for ( size_t n = 0; n < points().size(); ++n ) { + Log::debug() << n << " " << points()[n]; + + // A brute force approach. + // Use PolygonLocator class for optimized result + for ( idx_t p = 0; p < polygons.size(); ++p ) { + if ( polygons[p].contains( projection.xy( points()[n] ) ) ) { + Log::debug() << " : " << p; + part[n] = p; + } + } + Log::debug() << std::endl; + } + + check_part( part ); + check_sizes( sizes ); + check_simplified_sizes( simplified_sizes ); + + PolygonLocator find_partition( polygons, projection ); + for ( size_t n = 0; n < points().size(); ++n ) { + int found_partition; + EXPECT_NO_THROW( found_partition = find_partition( points()[n] ) ); + EXPECT_EQ( found_partition, part[n] ); + } +} + +//----------------------------------------------------------------------------- + +} // namespace test +} // namespace atlas + +int main( int argc, char** argv ) { + return atlas::test::run( argc, argv ); +} From 68b57feb07bee57a457fb4979ef4c76fb48b9997 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Sat, 4 Apr 2020 00:24:22 +0100 Subject: [PATCH 023/145] ATLAS-270 PolygonLocator can operatore on containers --- src/atlas/util/PolygonLocator.h | 11 ++++ .../functionspace/test_polygons_projection.cc | 58 +++++++------------ 2 files changed, 33 insertions(+), 36 deletions(-) diff --git a/src/atlas/util/PolygonLocator.h b/src/atlas/util/PolygonLocator.h index 0ba804f21..0f21c2e70 100644 --- a/src/atlas/util/PolygonLocator.h +++ b/src/atlas/util/PolygonLocator.h @@ -62,6 +62,17 @@ class PolygonLocator { buildKDTree(); } + template + void operator()( const PointContainer& points, PolygonIndexContainer& index ) { + ATLAS_ASSERT( points.size() == index.size() ); + typename PointContainer::const_iterator p = points.begin(); + typename PointContainer::const_iterator p_end = points.end(); + typename PolygonIndexContainer::iterator i = index.begin(); + for ( ; p != p_end; ++p, ++i ) { + *i = this->operator()( *p ); + } + } + /// @brief find the polygon that holds the point (lon,lat) idx_t operator()( const Point2& point ) const { const auto found = kdtree_.kNearestNeighbours( point, k_ ); diff --git a/src/tests/functionspace/test_polygons_projection.cc b/src/tests/functionspace/test_polygons_projection.cc index 21ce5f8bd..ace872062 100644 --- a/src/tests/functionspace/test_polygons_projection.cc +++ b/src/tests/functionspace/test_polygons_projection.cc @@ -160,60 +160,46 @@ CASE( "info" ) { //----------------------------------------------------------------------------- -CASE( "test_polygons" ) { - auto fs = functionspace(); - - ATLAS_TRACE( "computations after setup" ); +CASE( "test_polygon_sizes" ) { + auto fs = functionspace(); auto polygons = util::LonLatPolygons( fs.polygons() ); - auto projection = fs.projection(); - EXPECT( projection ); - std::vector sizes( mpi::size() ); std::vector simplified_sizes( mpi::size() ); for ( idx_t i = 0; i < mpi::size(); ++i ) { sizes[i] = fs.polygons()[i].size(); simplified_sizes[i] = polygons[i].size(); } + check_sizes( sizes ); + check_simplified_sizes( simplified_sizes ); +} - // Test iterator: - Log::info() << "Iterating PartitionPolygons" << std::endl; - for ( auto& polygon : fs.polygons() ) { - Log::info() << " size of polygon = " << polygon.size() << std::endl; - } +CASE( "test_polygon_locator (per point)" ) { + std::vector part; + + auto fs = functionspace(); + auto find_partition = PolygonLocator{util::LonLatPolygons{fs.polygons()}, fs.projection()}; - Log::info() << "Iterating LonLatPolygons" << std::endl; - for ( auto& polygon : polygons ) { - Log::info() << " size of lonlatpolygon = " << polygon.size() << std::endl; + part.reserve( points().size() ); + for ( auto& point : points() ) { + part.emplace_back( find_partition( point ) ); } + check_part( part ); +} + +CASE( "test_polygon_locator (for array)" ) { std::vector part( points().size() ); - for ( size_t n = 0; n < points().size(); ++n ) { - Log::debug() << n << " " << points()[n]; - // A brute force approach. - // Use PolygonLocator class for optimized result - for ( idx_t p = 0; p < polygons.size(); ++p ) { - if ( polygons[p].contains( projection.xy( points()[n] ) ) ) { - Log::debug() << " : " << p; - part[n] = p; - } - } - Log::debug() << std::endl; - } + auto fs = functionspace(); + auto find_partition = PolygonLocator{util::LonLatPolygons{fs.polygons()}, fs.projection()}; - check_part( part ); - check_sizes( sizes ); - check_simplified_sizes( simplified_sizes ); + find_partition( points(), part ); - PolygonLocator find_partition( polygons, projection ); - for ( size_t n = 0; n < points().size(); ++n ) { - int found_partition; - EXPECT_NO_THROW( found_partition = find_partition( points()[n] ) ); - EXPECT_EQ( found_partition, part[n] ); - } + check_part( part ); } + //----------------------------------------------------------------------------- } // namespace test From 1058d32bd7276eee3401eb276285a09e5bfbec0e Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 6 Apr 2020 11:05:23 +0100 Subject: [PATCH 024/145] ATLAS-270 Rename LonLatPolygon to PolygonXY as it better reflects which coordinates are used to define the polygon --- src/atlas/CMakeLists.txt | 4 ++-- ...hingFunctionSpacePartitionerLonLatPolygon.cc | 6 +++--- .../MatchingMeshPartitionerLonLatPolygon.cc | 4 ++-- .../util/{LonLatPolygon.cc => PolygonXY.cc} | 7 +++---- src/atlas/util/{LonLatPolygon.h => PolygonXY.h} | 17 +++++++++-------- src/tests/functionspace/test_polygons.cc | 12 ++++++------ .../functionspace/test_polygons_projection.cc | 8 ++++---- 7 files changed, 29 insertions(+), 29 deletions(-) rename src/atlas/util/{LonLatPolygon.cc => PolygonXY.cc} (95%) rename src/atlas/util/{LonLatPolygon.h => PolygonXY.h} (81%) diff --git a/src/atlas/CMakeLists.txt b/src/atlas/CMakeLists.txt index 5ea417dde..c85760a02 100644 --- a/src/atlas/CMakeLists.txt +++ b/src/atlas/CMakeLists.txt @@ -585,8 +585,8 @@ util/Earth.h util/GaussianLatitudes.cc util/GaussianLatitudes.h util/KDTree.h -util/LonLatPolygon.cc -util/LonLatPolygon.h +util/PolygonXY.cc +util/PolygonXY.h util/Metadata.cc util/Metadata.h util/Point.cc diff --git a/src/atlas/grid/detail/partitioner/MatchingFunctionSpacePartitionerLonLatPolygon.cc b/src/atlas/grid/detail/partitioner/MatchingFunctionSpacePartitionerLonLatPolygon.cc index 1de2cfa9a..a5be121db 100644 --- a/src/atlas/grid/detail/partitioner/MatchingFunctionSpacePartitionerLonLatPolygon.cc +++ b/src/atlas/grid/detail/partitioner/MatchingFunctionSpacePartitionerLonLatPolygon.cc @@ -21,7 +21,7 @@ #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/CoordinateEnums.h" -#include "atlas/util/LonLatPolygon.h" +#include "atlas/util/PolygonXY.h" #include "atlas/parallel/omp/fill.h" #include "atlas/parallel/omp/omp.h" @@ -48,7 +48,7 @@ void MatchingFunctionSpacePartitionerLonLatPolygon::partition( const Grid& grid, const auto& p = partitioned_.polygon(); int rank = mpi::rank(); - util::LonLatPolygon poly{p}; + util::PolygonXY poly{p}; { ATLAS_TRACE( "point-in-polygon check for entire grid (" + std::to_string( grid.size() ) + " points)" ); size_t num_threads = atlas_omp_get_max_threads(); @@ -91,7 +91,7 @@ void MatchingFunctionSpacePartitionerLonLatPolygon::partition( const Grid& grid, bool includesNorthPole = ( mpi_rank == 0 ); bool includesSouthPole = ( mpi_rank == mpi_size - 1 ); - const util::LonLatPolygon poly( prePartitionedFunctionSpace_.polygon( 0 ), prePartitionedFunctionSpace_.nodes().lonlat() ); + const util::PolygonXY poly( prePartitionedFunctionSpace_.polygon( 0 ), prePartitionedFunctionSpace_.nodes().lonlat() ); { eckit::ProgressTimer timer( "Partitioning", grid.size(), "point", double( 10 ), atlas::Log::trace() ); diff --git a/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.cc b/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.cc index 3bf34c13c..dfcf4e8a1 100644 --- a/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.cc +++ b/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.cc @@ -22,7 +22,7 @@ #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/CoordinateEnums.h" -#include "atlas/util/LonLatPolygon.h" +#include "atlas/util/PolygonXY.h" namespace atlas { namespace grid { @@ -49,7 +49,7 @@ void MatchingMeshPartitionerLonLatPolygon::partition( const Grid& grid, int part bool includesNorthPole = ( mpi_rank == 0 ); bool includesSouthPole = ( mpi_rank == mpi_size - 1 ); - const util::LonLatPolygon poly{prePartitionedMesh_.polygon( 0 )}; + const util::PolygonXY poly{prePartitionedMesh_.polygon( 0 )}; Projection projection = prePartitionedMesh_.projection(); { diff --git a/src/atlas/util/LonLatPolygon.cc b/src/atlas/util/PolygonXY.cc similarity index 95% rename from src/atlas/util/LonLatPolygon.cc rename to src/atlas/util/PolygonXY.cc index f89852b47..f7e8853e0 100644 --- a/src/atlas/util/LonLatPolygon.cc +++ b/src/atlas/util/PolygonXY.cc @@ -18,7 +18,7 @@ #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/CoordinateEnums.h" -#include "atlas/util/LonLatPolygon.h" +#include "atlas/util/PolygonXY.h" namespace atlas { namespace util { @@ -80,8 +80,7 @@ double compute_inner_radius_squared( const PointContainer& points, const PointLo //------------------------------------------------------------------------------------------------------ -LonLatPolygon::LonLatPolygon( const PartitionPolygon& partition_polygon ) : - PolygonCoordinates( partition_polygon.xy(), true ) { +PolygonXY::PolygonXY( const PartitionPolygon& partition_polygon ) : PolygonCoordinates( partition_polygon.xy(), true ) { RectangularLonLatDomain inscribed = partition_polygon.inscribedDomain(); if ( inscribed ) { inner_coordinatesMin_ = {inscribed.xmin(), inscribed.ymin()}; @@ -95,7 +94,7 @@ LonLatPolygon::LonLatPolygon( const PartitionPolygon& partition_polygon ) : } } -bool LonLatPolygon::contains( const Point2& P ) const { +bool PolygonXY::contains( const Point2& P ) const { auto distance2 = []( const Point2& p, const Point2& centroid ) { double dx = ( p[0] - centroid[0] ); double dy = ( p[1] - centroid[1] ); diff --git a/src/atlas/util/LonLatPolygon.h b/src/atlas/util/PolygonXY.h similarity index 81% rename from src/atlas/util/LonLatPolygon.h rename to src/atlas/util/PolygonXY.h index a8084e7d5..53ebdf687 100644 --- a/src/atlas/util/LonLatPolygon.h +++ b/src/atlas/util/PolygonXY.h @@ -17,17 +17,18 @@ namespace atlas { namespace util { -class LonLatPolygon; -class LonLatPolygons; +class PartitionPolygon; +class PolygonXY; +class ListPolygonXY; //------------------------------------------------------------------------------------------------------ /// @brief Implement PolygonCoordinates::contains for a polygon defined in XY space. -class LonLatPolygon : public PolygonCoordinates { +class PolygonXY : public PolygonCoordinates { public: - using Vector = LonLatPolygons; + using Vector = ListPolygonXY; - LonLatPolygon( const PartitionPolygon& ); + PolygonXY( const PartitionPolygon& ); /// @brief Point-in-polygon test based on winding number /// @note reference Inclusion of a Point in a Polygon @@ -46,12 +47,12 @@ class LonLatPolygon : public PolygonCoordinates { //------------------------------------------------------------------------------------------------------ /// @brief Vector of all polygons with functionality to find partition using a KDTree -class LonLatPolygons : public PolygonCoordinates::Vector { +class ListPolygonXY : public PolygonCoordinates::Vector { public: - LonLatPolygons( const PartitionPolygons& partition_polygons ) { + ListPolygonXY( const PartitionPolygons& partition_polygons ) { reserve( partition_polygons.size() ); for ( auto& partition_polygon : partition_polygons ) { - emplace_back( new LonLatPolygon( partition_polygon ) ); + emplace_back( new PolygonXY( partition_polygon ) ); } } }; diff --git a/src/tests/functionspace/test_polygons.cc b/src/tests/functionspace/test_polygons.cc index 89c3e67de..9deec6977 100644 --- a/src/tests/functionspace/test_polygons.cc +++ b/src/tests/functionspace/test_polygons.cc @@ -17,8 +17,8 @@ #include "atlas/grid/StructuredGrid.h" #include "atlas/meshgenerator.h" #include "atlas/parallel/mpi/mpi.h" -#include "atlas/util/LonLatPolygon.h" #include "atlas/util/PolygonLocator.h" +#include "atlas/util/PolygonXY.h" #include "tests/AtlasTestEnvironment.h" @@ -160,7 +160,7 @@ CASE( "test_polygons" ) { auto fs = functionspace(); ATLAS_TRACE( "computations after setup" ); - auto polygons = util::LonLatPolygons( fs.polygons() ); + auto polygons = ListPolygonXY( fs.polygons() ); std::vector sizes( mpi::size() ); std::vector simplified_sizes( mpi::size() ); @@ -175,7 +175,7 @@ CASE( "test_polygons" ) { } for ( auto& polygon : polygons ) { - Log::info() << "size of lonlatpolygon = " << polygon.size() << std::endl; + Log::info() << "size of PolygonXY = " << polygon.size() << std::endl; } std::vector part( points().size() ); @@ -205,20 +205,20 @@ CASE( "test_polygons" ) { } CASE( "test_polygon_locator_from_const_ref_polygons" ) { - auto polygons = LonLatPolygons{functionspace().polygons()}; + auto polygons = ListPolygonXY{functionspace().polygons()}; PolygonLocator find_partition( polygons ); EXPECT_EQ( find_partition( PointLonLat{0., 90.} ), 0 ); EXPECT_EQ( find_partition( PointLonLat{0., -90.} ), mpi::size() - 1 ); } CASE( "test_polygon_locator_from_move" ) { - PolygonLocator find_partition( LonLatPolygons{functionspace().polygons()} ); + PolygonLocator find_partition( ListPolygonXY{functionspace().polygons()} ); EXPECT_EQ( find_partition( PointLonLat{0., 90.} ), 0 ); EXPECT_EQ( find_partition( PointLonLat{0., -90.} ), mpi::size() - 1 ); } CASE( "test_polygon_locator_from_shared" ) { - auto polygons = std::make_shared( functionspace().polygons() ); + auto polygons = std::make_shared( functionspace().polygons() ); PolygonLocator find_partition( polygons ); EXPECT_EQ( find_partition( PointLonLat{0., 90.} ), 0 ); EXPECT_EQ( find_partition( PointLonLat{0., -90.} ), mpi::size() - 1 ); diff --git a/src/tests/functionspace/test_polygons_projection.cc b/src/tests/functionspace/test_polygons_projection.cc index ace872062..762af6368 100644 --- a/src/tests/functionspace/test_polygons_projection.cc +++ b/src/tests/functionspace/test_polygons_projection.cc @@ -18,8 +18,8 @@ #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/mpi/mpi.h" -#include "atlas/util/LonLatPolygon.h" #include "atlas/util/PolygonLocator.h" +#include "atlas/util/PolygonXY.h" #include "tests/AtlasTestEnvironment.h" @@ -162,7 +162,7 @@ CASE( "info" ) { CASE( "test_polygon_sizes" ) { auto fs = functionspace(); - auto polygons = util::LonLatPolygons( fs.polygons() ); + auto polygons = ListPolygonXY( fs.polygons() ); std::vector sizes( mpi::size() ); std::vector simplified_sizes( mpi::size() ); @@ -178,7 +178,7 @@ CASE( "test_polygon_locator (per point)" ) { std::vector part; auto fs = functionspace(); - auto find_partition = PolygonLocator{util::LonLatPolygons{fs.polygons()}, fs.projection()}; + auto find_partition = PolygonLocator{ListPolygonXY{fs.polygons()}, fs.projection()}; part.reserve( points().size() ); for ( auto& point : points() ) { @@ -192,7 +192,7 @@ CASE( "test_polygon_locator (for array)" ) { std::vector part( points().size() ); auto fs = functionspace(); - auto find_partition = PolygonLocator{util::LonLatPolygons{fs.polygons()}, fs.projection()}; + auto find_partition = PolygonLocator{ListPolygonXY{fs.polygons()}, fs.projection()}; find_partition( points(), part ); From dc214aa8a38ba33f06660e32d6f5ce0a1a5c88aa Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 6 Apr 2020 10:51:52 +0100 Subject: [PATCH 025/145] ATLAS-270 Cleanup --- src/atlas/grid/StructuredPartitionPolygon.cc | 30 +++++-------------- ...ngFunctionSpacePartitionerLonLatPolygon.cc | 2 +- ...MatchingMeshPartitionerSphericalPolygon.cc | 5 +++- src/atlas/util/SphericalPolygon.cc | 4 +-- src/atlas/util/SphericalPolygon.h | 11 +++---- 5 files changed, 18 insertions(+), 34 deletions(-) diff --git a/src/atlas/grid/StructuredPartitionPolygon.cc b/src/atlas/grid/StructuredPartitionPolygon.cc index 1acbb5f16..5d719be6c 100644 --- a/src/atlas/grid/StructuredPartitionPolygon.cc +++ b/src/atlas/grid/StructuredPartitionPolygon.cc @@ -24,8 +24,8 @@ namespace atlas { namespace grid { -util::Polygon::edge_set_t compute_edges( const functionspace::FunctionSpaceImpl& _fs, idx_t _halo, - std::vector& points, std::vector& bb ) { +void compute( const functionspace::FunctionSpaceImpl& _fs, idx_t _halo, std::vector& points, + std::vector& bb ) { if ( not dynamic_cast( &_fs ) ) { throw_Exception( "Could not cast functionspace to StructuredColumns", Here() ); } @@ -39,16 +39,15 @@ util::Polygon::edge_set_t compute_edges( const functionspace::FunctionSpaceImpl& auto equal = []( const double& a, const double& b ) { return std::abs( a - b ) < 1.e-12; }; - util::Polygon::edge_set_t edges; auto last_edge_horizontal = [&]() { - if ( edges.empty() ) { + if ( points.size() < 2 ) { return false; } size_t size = points.size(); return equal( points.at( size - 1 )[YY], points.at( size - 2 )[YY] ); }; auto last_edge_vertical = [&]() { - if ( edges.empty() ) { + if ( points.size() < 2 ) { return false; } size_t size = points.size(); @@ -59,12 +58,6 @@ util::Polygon::edge_set_t compute_edges( const functionspace::FunctionSpaceImpl& idx_t c{0}; idx_t i, j; - auto add_edge = [&]( idx_t p1, idx_t p2 ) { - util::Polygon::edge_t edge = {p1, p2}; - edges.insert( edge ); - // Log::info() << edge.first << " " << edge.second << std::endl; - }; - auto add_point = [&]( const Point2& point ) { points.emplace_back( point ); // Log::info() << "add point (" << points.size() - 1 << ") " << point << std::endl; @@ -77,7 +70,6 @@ util::Polygon::edge_set_t compute_edges( const functionspace::FunctionSpaceImpl& } else { add_point( point ); - add_edge( points.size() - 2, points.size() - 1 ); c++; } }; @@ -88,7 +80,6 @@ util::Polygon::edge_set_t compute_edges( const functionspace::FunctionSpaceImpl& } else { add_point( point ); - add_edge( points.size() - 2, points.size() - 1 ); c++; } }; @@ -292,25 +283,18 @@ util::Polygon::edge_set_t compute_edges( const functionspace::FunctionSpaceImpl& } // Connect to top if ( last_edge_vertical() ) { - util::Polygon::edge_t last_edge = {c - 1, c}; - edges.erase( last_edge ); points.pop_back(); c--; } - add_edge( c, 0 ); - - - bb = std::vector{{xmin, ymax}, {xmin, ymin}, {xmax, ymin}, {xmax, ymax}}; + add_point( points.front() ); - return edges; + bb = std::vector{{xmin, ymax}, {xmin, ymin}, {xmax, ymin}, {xmax, ymax}, {xmin, ymax}}; } StructuredPartitionPolygon::StructuredPartitionPolygon( const functionspace::FunctionSpaceImpl& fs, idx_t halo ) : fs_( fs ), halo_( halo ) { ATLAS_TRACE( "StructuredPartitionPolygon" ); - setup( compute_edges( fs, halo, points_, inner_bounding_box_ ) ); - points_.emplace_back( points_[0] ); - inner_bounding_box_.emplace_back( inner_bounding_box_[0] ); + compute( fs, halo, points_, inner_bounding_box_ ); auto min = Point2( std::numeric_limits::max(), std::numeric_limits::max() ); auto max = Point2( std::numeric_limits::lowest(), std::numeric_limits::lowest() ); for ( size_t i = 0; i < inner_bounding_box_.size() - 1; ++i ) { diff --git a/src/atlas/grid/detail/partitioner/MatchingFunctionSpacePartitionerLonLatPolygon.cc b/src/atlas/grid/detail/partitioner/MatchingFunctionSpacePartitionerLonLatPolygon.cc index a5be121db..8ec07483a 100644 --- a/src/atlas/grid/detail/partitioner/MatchingFunctionSpacePartitionerLonLatPolygon.cc +++ b/src/atlas/grid/detail/partitioner/MatchingFunctionSpacePartitionerLonLatPolygon.cc @@ -91,7 +91,7 @@ void MatchingFunctionSpacePartitionerLonLatPolygon::partition( const Grid& grid, bool includesNorthPole = ( mpi_rank == 0 ); bool includesSouthPole = ( mpi_rank == mpi_size - 1 ); - const util::PolygonXY poly( prePartitionedFunctionSpace_.polygon( 0 ), prePartitionedFunctionSpace_.nodes().lonlat() ); + const util::PolygonXY poly{prePartitionedFunctionSpace_.polygon( 0 )}; { eckit::ProgressTimer timer( "Partitioning", grid.size(), "point", double( 10 ), atlas::Log::trace() ); diff --git a/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerSphericalPolygon.cc b/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerSphericalPolygon.cc index 5a2a25585..1b2882475 100644 --- a/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerSphericalPolygon.cc +++ b/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerSphericalPolygon.cc @@ -48,7 +48,10 @@ void MatchingMeshPartitionerSphericalPolygon::partition( const Grid& grid, int p bool includesNorthPole = ( mpi_rank == 0 ); bool includesSouthPole = ( mpi_rank == mpi_size - 1 ); - const util::SphericalPolygon poly( prePartitionedMesh_.polygon( 0 ), prePartitionedMesh_.nodes().lonlat() ); + if ( not prePartitionedMesh_.projection() ) { + ATLAS_NOTIMPLEMENTED; + } + const util::SphericalPolygon poly{prePartitionedMesh_.polygon( 0 )}; const double maxlat = poly.coordinatesMax()[LAT]; const double minlat = poly.coordinatesMin()[LAT]; auto at_the_pole = [&]( const PointLonLat& P ) { diff --git a/src/atlas/util/SphericalPolygon.cc b/src/atlas/util/SphericalPolygon.cc index af31ae4f5..61db16d34 100644 --- a/src/atlas/util/SphericalPolygon.cc +++ b/src/atlas/util/SphericalPolygon.cc @@ -24,8 +24,8 @@ namespace util { //------------------------------------------------------------------------------------------------------ -SphericalPolygon::SphericalPolygon( const Polygon& poly, const atlas::Field& lonlat ) : - PolygonCoordinates( poly, lonlat, false ) {} +SphericalPolygon::SphericalPolygon( const PartitionPolygon& partition_polygon ) : + PolygonCoordinates( partition_polygon.xy(), false ) {} SphericalPolygon::SphericalPolygon( const std::vector& points ) : PolygonCoordinates( points ) {} diff --git a/src/atlas/util/SphericalPolygon.h b/src/atlas/util/SphericalPolygon.h index 1f4bcca40..9d1acce48 100644 --- a/src/atlas/util/SphericalPolygon.h +++ b/src/atlas/util/SphericalPolygon.h @@ -17,27 +17,24 @@ namespace atlas { namespace util { +class PartitionPolygon; //------------------------------------------------------------------------------------------------------ class SphericalPolygon : public PolygonCoordinates { public: - // -- Constructors - - SphericalPolygon( const Polygon&, const atlas::Field& lonlat ); + SphericalPolygon( const PartitionPolygon& ); SphericalPolygon( const std::vector& points ); - // -- Overridden methods - /* * Point-in-polygon test based on winding number * @note reference Inclusion of a Point * in a Polygon - * @param[in] P given point + * @param[in] P given point in (lon,lat) coordinates * @return if point is in polygon */ - bool contains( const Point2& P ) const; + bool contains( const Point2& lonlat ) const override; }; //------------------------------------------------------------------------------------------------------ From 5846c2f57aa2a8344d235eb95954518cc6149cb6 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 6 Apr 2020 15:30:15 +0100 Subject: [PATCH 026/145] Fix atlas_test_interpolation_structured2D_to_unstructured when eckit was not compiled with MPI --- ...interpolation_structured2D_to_unstructured.cc | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/tests/interpolation/test_interpolation_structured2D_to_unstructured.cc b/src/tests/interpolation/test_interpolation_structured2D_to_unstructured.cc index 2f5954abc..11fa31eb1 100644 --- a/src/tests/interpolation/test_interpolation_structured2D_to_unstructured.cc +++ b/src/tests/interpolation/test_interpolation_structured2D_to_unstructured.cc @@ -85,8 +85,14 @@ FunctionSpace output_functionspace_match() { }; } } + else if ( mpi::size() == 1 ) { + points = std::vector{ + {45., 45.}, {90., 45.}, {135., 45.}, {180., 45.}, {225., 45.}, {270., 45.}, {315., 45.}, + {45., -45.}, {90., -45.}, {135., -45.}, {180., -45.}, {225., -45.}, {270., -45.}, {315., -45.}, + }; + } else { - return FunctionSpace(); + ATLAS_NOTIMPLEMENTED; } return PointCloud( points ); } @@ -105,8 +111,14 @@ FunctionSpace output_functionspace_nomatch() { }; } } + else if ( mpi::size() == 1 ) { + points = std::vector{ + {45., 45.}, {90., 45.}, {135., 45.}, {180., 45.}, {225., 45.}, {270., 45.}, {315., 45.}, + {45., -45.}, {90., -45.}, {135., -45.}, {180., -45.}, {225., -45.}, {270., -45.}, {315., -45.}, + }; + } else { - return FunctionSpace(); + ATLAS_NOTIMPLEMENTED; } return PointCloud( points ); } From 655f87f22b91c98d456278d825b2ae8ffba12537 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 7 Apr 2020 10:08:13 +0100 Subject: [PATCH 027/145] Avoid atlas_test_interpolation_structured2D_to_unstructured when no MPI is available --- src/tests/interpolation/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tests/interpolation/CMakeLists.txt b/src/tests/interpolation/CMakeLists.txt index 1d73b9611..187af2b9b 100644 --- a/src/tests/interpolation/CMakeLists.txt +++ b/src/tests/interpolation/CMakeLists.txt @@ -50,4 +50,7 @@ ecbuild_add_test( TARGET atlas_test_interpolation_structured2D_to_unstructured SOURCES test_interpolation_structured2D_to_unstructured.cc LIBS atlas MPI 2 + CONDITION eckit_HAVE_MPI + ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) + From 992c586ba65d13f948ac79595a425d598919a41b Mon Sep 17 00:00:00 2001 From: Rahul Mahajan Date: Wed, 8 Apr 2020 10:05:01 -0400 Subject: [PATCH 028/145] Use cmake provided FindLATEX.cmake to make it generic. Update bugs in documentation code. Change-Id: I2b8b2afa3db997c021917d2452690bb30b221135 --- .gitignore | 1 + cmake/FindLatex.cmake | 32 ------------------- cmake/features/DOCS.cmake | 6 ++-- doc/user-guide/CMakeLists.txt | 6 ++-- .../fields/fields-on-grid.cc | 4 +-- .../core-functionalities/fields/fields.cc | 2 +- .../functionspace/NodeColumns.cc | 4 +-- .../functionspace/StructuredColumns.cc | 4 +-- .../Unstructured/global-grids-Unstructured.cc | 2 +- 9 files changed, 15 insertions(+), 46 deletions(-) delete mode 100644 cmake/FindLatex.cmake diff --git a/.gitignore b/.gitignore index 92db20bec..8ad3a5a4b 100755 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ CMakeLists.txt.user* doc/html doc/latex *.sublime-workspace +*.swp .nfs* build/* install/* diff --git a/cmake/FindLatex.cmake b/cmake/FindLatex.cmake deleted file mode 100644 index fae30966b..000000000 --- a/cmake/FindLatex.cmake +++ /dev/null @@ -1,32 +0,0 @@ -# (C) Copyright 2013 ECMWF. -# -# This software is licensed under the terms of the Apache Licence Version 2.0 -# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -# In applying this licence, ECMWF does not waive the privileges and immunities -# granted to it by virtue of its status as an intergovernmental organisation nor -# does it submit to any jurisdiction. - -############################################################################### - -# find latex libraries -FIND_PROGRAM (PDFLATEX - NAMES pdflatex - PATHS /usr/local/share/apps/TeXLive/2014/bin/x86_64-linux/) -FIND_PROGRAM (BIBTEX - NAMES bibtex - PATHS /usr/local/share/apps/TeXLive/2014/bin/x86_64-linux/) -FIND_PROGRAM (MAKEINDEX - NAMES makeindex - PATHS /usr/local/share/apps/TeXLive/2014/bin/x86_64-linux/) -FIND_PROGRAM (HTLATEX - NAMES htlatex - PATHS /usr/local/share/apps/TeXLive/2014/bin/x86_64-linux/) - -if ( PDFLATEX AND BIBTEX AND MAKEINDEX AND HTLATEX ) - set( LATEX_FOUND TRUE ) -endif() - - -mark_as_advanced(PDFLATEX BIBTEX MAKEINDEX HTLATEX) - - diff --git a/cmake/features/DOCS.cmake b/cmake/features/DOCS.cmake index e81a4fd35..ac2d5fb82 100644 --- a/cmake/features/DOCS.cmake +++ b/cmake/features/DOCS.cmake @@ -1,9 +1,9 @@ ################################################################################ # documentation -if( ENABLE_DOCS ) - find_package(Latex) +if( ENABLE_DOCS ) + find_package(LATEX REQUIRED COMPONENTS PDFLATEX BIBTEX MAKEINDEX HTLATEX) endif() ecbuild_add_option( FEATURE DOCS DESCRIPTION "Atlas documentation" DEFAULT OFF - CONDITION Latex_FOUND ) + CONDITION LATEX_FOUND ) diff --git a/doc/user-guide/CMakeLists.txt b/doc/user-guide/CMakeLists.txt index 31e8f2b67..be3faaf6b 100644 --- a/doc/user-guide/CMakeLists.txt +++ b/doc/user-guide/CMakeLists.txt @@ -21,7 +21,7 @@ file(MAKE_DIRECTORY ${USERGUIDE}/html) add_custom_target(atlas-user-guide-html export TEXINPUTS=${CMAKE_SOURCE_DIR}//:${USERGUIDESRC}//:${CMAKE_CURRENT_BINARY_DIR}//: && - ${HTLATEX} ${USERGUIDESRC}/user-guide.tex + ${HTLATEX_COMPILER} ${USERGUIDESRC}/user-guide.tex "${USERGUIDESRC}/styling.cfg,html,3,next,NoFonts" WORKING_DIRECTORY ${USERGUIDE}/html ) @@ -69,8 +69,8 @@ ENDFOREACH() # PDF GENERATION ============================================================== add_custom_target(atlas-user-guide-pdf export TEXINPUTS=${CMAKE_SOURCE_DIR}//:${CMAKE_CURRENT_BINARY_DIR}//: && - ${PDFLATEX} --output-directory ${USERGUIDE} ${USERGUIDESRC}/user-guide.tex + ${PDFLATEX_COMPILER} --output-directory ${USERGUIDE} ${USERGUIDESRC}/user-guide.tex COMMAND export TEXINPUTS=${CMAKE_SOURCE_DIR}//:${CMAKE_CURRENT_BINARY_DIR}//: && - ${PDFLATEX} --output-directory ${USERGUIDE} ${USERGUIDESRC}/user-guide.tex + ${PDFLATEX_COMPILER} --output-directory ${USERGUIDE} ${USERGUIDESRC}/user-guide.tex WORKING_DIRECTORY ${USERGUIDESRC} ) # ============================================================================= diff --git a/doc/user-guide/core-functionalities/fields/fields-on-grid.cc b/doc/user-guide/core-functionalities/fields/fields-on-grid.cc index c38d827fc..1f41646a8 100644 --- a/doc/user-guide/core-functionalities/fields/fields-on-grid.cc +++ b/doc/user-guide/core-functionalities/fields/fields-on-grid.cc @@ -12,7 +12,7 @@ using atlas::array::make_shape; using atlas::array::make_view; int main( int argc, char* argv[] ) { - atlas::Library::initialise( argc, argv ); + atlas::Library::instance().initialise( argc, argv ); int jnode = 0; const double rpi = 2.0 * asin( 1.0 ); @@ -50,7 +50,7 @@ int main( int argc, char* argv[] ) { Log::info() << "memory field_pressure = " << field_pressure.bytes() * 1.e-9 << " GB" << std::endl; Log::info() << "==========================================" << std::endl; - atlas::Library::finalise(); + atlas::Library::instance().finalise(); atlas::mpi::finalize(); return 0; diff --git a/doc/user-guide/core-functionalities/fields/fields.cc b/doc/user-guide/core-functionalities/fields/fields.cc index 1d951a5cc..1709d8c1a 100644 --- a/doc/user-guide/core-functionalities/fields/fields.cc +++ b/doc/user-guide/core-functionalities/fields/fields.cc @@ -13,7 +13,7 @@ using atlas::array::make_shape; using atlas::array::make_view; int main( int argc, char* argv[] ) { - atlas::Library::initialise( argc, argv ); + atlas::Library::instance().initialise( argc, argv ); // Define fields Field field_pressure( "pressure", make_datatype(), make_shape( 100 ) ); diff --git a/doc/user-guide/core-functionalities/functionspace/NodeColumns.cc b/doc/user-guide/core-functionalities/functionspace/NodeColumns.cc index ecacc67d1..e516947fb 100644 --- a/doc/user-guide/core-functionalities/functionspace/NodeColumns.cc +++ b/doc/user-guide/core-functionalities/functionspace/NodeColumns.cc @@ -19,7 +19,7 @@ using atlas::functionspace::NodeColumns; using atlas::output::Gmsh; int main( int argc, char* argv[] ) { - atlas::Library::initialise( argc, argv ); + atlas::Library::instance().initialise( argc, argv ); // Generate global classic reduced Gaussian grid StructuredGrid grid( "N32" ); @@ -147,7 +147,7 @@ int main( int argc, char* argv[] ) { << "std_deviation: " << stddev << ", " << "nb_nodes: " << N << std::endl; - atlas::Library::finalise(); + atlas::Library::instance().finalise(); atlas::mpi::finalize(); return 0; diff --git a/doc/user-guide/core-functionalities/functionspace/StructuredColumns.cc b/doc/user-guide/core-functionalities/functionspace/StructuredColumns.cc index 9e09bd4ec..e512a812e 100644 --- a/doc/user-guide/core-functionalities/functionspace/StructuredColumns.cc +++ b/doc/user-guide/core-functionalities/functionspace/StructuredColumns.cc @@ -16,7 +16,7 @@ using atlas::functionspace::StructuredColumns; using atlas::output::Gmsh; int main( int argc, char* argv[] ) { - Atlas::initialise( argc, argv ); + atlas::Library::instance().initialise( argc, argv ); // Generate global reduced grid Grid grid( "N32" ); @@ -63,7 +63,7 @@ int main( int argc, char* argv[] ) { gmsh.write( field_scalar1 ); } - Library::finalise(); + Library::instance().finalise(); mpi::finalise(); return 0; } diff --git a/doc/user-guide/core-functionalities/global-grids/Unstructured/global-grids-Unstructured.cc b/doc/user-guide/core-functionalities/global-grids/Unstructured/global-grids-Unstructured.cc index c4cfe411c..843a786d1 100644 --- a/doc/user-guide/core-functionalities/global-grids/Unstructured/global-grids-Unstructured.cc +++ b/doc/user-guide/core-functionalities/global-grids/Unstructured/global-grids-Unstructured.cc @@ -12,7 +12,7 @@ using atlas::output::Gmsh; using atlas::util::Config; int main( int argc, char* argv[] ) { - atlas::Library::initialise( argc, argv ); + atlas::Library::instance().initialise( argc, argv ); Grid grid = UnstructuredGrid( {{180, 0}, {90, 0}, From 080db8f312dabb7d9b29dd26631b78f7c87883a4 Mon Sep 17 00:00:00 2001 From: Rahul Mahajan Date: Wed, 8 Apr 2020 19:53:06 -0400 Subject: [PATCH 029/145] Make html components optional --- cmake/features/DOCS.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/features/DOCS.cmake b/cmake/features/DOCS.cmake index ac2d5fb82..03b194822 100644 --- a/cmake/features/DOCS.cmake +++ b/cmake/features/DOCS.cmake @@ -1,7 +1,7 @@ ################################################################################ # documentation if( ENABLE_DOCS ) - find_package(LATEX REQUIRED COMPONENTS PDFLATEX BIBTEX MAKEINDEX HTLATEX) + find_package(LATEX REQUIRED COMPONENTS PDFLATEX BIBTEX OPTIONAL_COMPONENTS MAKEINDEX HTLATEX) endif() ecbuild_add_option( FEATURE DOCS DESCRIPTION "Atlas documentation" From 0f813e316c12dcadf8a44c6afb3e5c08c1bbac86 Mon Sep 17 00:00:00 2001 From: Rahul Mahajan Date: Thu, 9 Apr 2020 09:34:48 -0400 Subject: [PATCH 030/145] wrap html documentation under LATEX_HTLATEX_FOUND Change-Id: Ieead1909b2e0bd87d28477b5267180973a806031 --- doc/CMakeLists.txt | 8 ++-- doc/user-guide/CMakeLists.txt | 73 ++++++++++++++++++----------------- 2 files changed, 42 insertions(+), 39 deletions(-) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index e2a1c2863..1d6be3b66 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -39,9 +39,11 @@ add_custom_target(atlas_doc_pdf) add_dependencies (atlas_doc_pdf atlas-user-guide-pdf ) -add_custom_target(atlas_doc_html) -add_dependencies (atlas_doc_html - atlas-user-guide-html ) +if( LATEX_HTLATEX_FOUND ) + add_custom_target(atlas_doc_html) + add_dependencies (atlas_doc_html + atlas-user-guide-html ) +endif() add_custom_target(atlas_doc) add_dependencies (atlas_doc diff --git a/doc/user-guide/CMakeLists.txt b/doc/user-guide/CMakeLists.txt index be3faaf6b..adf429048 100644 --- a/doc/user-guide/CMakeLists.txt +++ b/doc/user-guide/CMakeLists.txt @@ -17,50 +17,51 @@ set(USERGUIDE ${CMAKE_CURRENT_BINARY_DIR}) # HTML GENERATION ============================================================= +if( LATEX_HTLATEX_FOUND ) file(MAKE_DIRECTORY ${USERGUIDE}/html) -add_custom_target(atlas-user-guide-html - export TEXINPUTS=${CMAKE_SOURCE_DIR}//:${USERGUIDESRC}//:${CMAKE_CURRENT_BINARY_DIR}//: && - ${HTLATEX_COMPILER} ${USERGUIDESRC}/user-guide.tex - "${USERGUIDESRC}/styling.cfg,html,3,next,NoFonts" - WORKING_DIRECTORY ${USERGUIDE}/html ) - + add_custom_target(atlas-user-guide-html + export TEXINPUTS=${CMAKE_SOURCE_DIR}//:${USERGUIDESRC}//:${CMAKE_CURRENT_BINARY_DIR}//: && + ${HTLATEX_COMPILER} ${USERGUIDESRC}/user-guide.tex + "${USERGUIDESRC}/styling.cfg,html,3,next,NoFonts" + WORKING_DIRECTORY ${USERGUIDE}/html ) # If tex4ht successful, create img dir and copy images across # .png and .jpg images -------------------------------------------------------- -FILE(GLOB_RECURSE imgfiles - "introduction/imgs/*.png" "introduction/imgs/*.jpg" - "getting-started/*/imgs/*.png" "getting-started/*/imgs/*jpg" - "core-functionalities/*/imgs/*.png" "core-functionalities/*/imgs/*jpg" - "applications/*/imgs/*.png" "applications/*/imgs/*jpg") - -add_custom_command( - TARGET atlas-user-guide-html - POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${USERGUIDE}/html/imgs) - -FOREACH(img ${imgfiles}) - ADD_CUSTOM_COMMAND(TARGET atlas-user-guide-html - POST_BUILD COMMAND - ${CMAKE_COMMAND} -E copy ${img} ${USERGUIDE}/html/imgs) -ENDFOREACH() + FILE(GLOB_RECURSE imgfiles + "introduction/imgs/*.png" "introduction/imgs/*.jpg" + "getting-started/*/imgs/*.png" "getting-started/*/imgs/*jpg" + "core-functionalities/*/imgs/*.png" "core-functionalities/*/imgs/*jpg" + "applications/*/imgs/*.png" "applications/*/imgs/*jpg") + + add_custom_command( + TARGET atlas-user-guide-html + POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${USERGUIDE}/html/imgs) + + FOREACH(img ${imgfiles}) + ADD_CUSTOM_COMMAND(TARGET atlas-user-guide-html + POST_BUILD COMMAND + ${CMAKE_COMMAND} -E copy ${img} ${USERGUIDE}/html/imgs) + ENDFOREACH() # ----------------------------------------------------------------------------- # .pdf images ----------------------------------------------------------------- -FILE(GLOB_RECURSE pdffiles - "introduction/imgs/*.pdf" - "getting-started/*/imgs/*.pdf" - "core-functionalities/*/imgs/*.pdf" - "applications/*/imgs/*.pdf") - -FIND_PROGRAM(CONVERT convert) - -FOREACH(pdf ${pdffiles}) - GET_FILENAME_COMPONENT(BASENAME ${pdf} NAME_WE) - ADD_CUSTOM_COMMAND( - TARGET atlas-user-guide-html - POST_BUILD COMMAND - ${CONVERT} ${pdf} ${USERGUIDE}/html/imgs/${BASENAME}.png) -ENDFOREACH() + FILE(GLOB_RECURSE pdffiles + "introduction/imgs/*.pdf" + "getting-started/*/imgs/*.pdf" + "core-functionalities/*/imgs/*.pdf" + "applications/*/imgs/*.pdf") + + FIND_PROGRAM(CONVERT convert) + + FOREACH(pdf ${pdffiles}) + GET_FILENAME_COMPONENT(BASENAME ${pdf} NAME_WE) + ADD_CUSTOM_COMMAND( + TARGET atlas-user-guide-html + POST_BUILD COMMAND + ${CONVERT} ${pdf} ${USERGUIDE}/html/imgs/${BASENAME}.png) + ENDFOREACH() +endif( LATEX_HTLATEX_FOUND ) # ============================================================================= From 79f0d3524858494127b197c67253f321bb85a78c Mon Sep 17 00:00:00 2001 From: Rahul Mahajan Date: Thu, 9 Apr 2020 09:41:44 -0400 Subject: [PATCH 031/145] revert code changes to develop Change-Id: I07732b32a4e69522667283ebf261730a8d2752b7 --- doc/user-guide/core-functionalities/fields/fields-on-grid.cc | 4 ++-- doc/user-guide/core-functionalities/fields/fields.cc | 2 +- .../core-functionalities/functionspace/NodeColumns.cc | 4 ++-- .../core-functionalities/functionspace/StructuredColumns.cc | 4 ++-- .../global-grids/Unstructured/global-grids-Unstructured.cc | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/user-guide/core-functionalities/fields/fields-on-grid.cc b/doc/user-guide/core-functionalities/fields/fields-on-grid.cc index 1f41646a8..c38d827fc 100644 --- a/doc/user-guide/core-functionalities/fields/fields-on-grid.cc +++ b/doc/user-guide/core-functionalities/fields/fields-on-grid.cc @@ -12,7 +12,7 @@ using atlas::array::make_shape; using atlas::array::make_view; int main( int argc, char* argv[] ) { - atlas::Library::instance().initialise( argc, argv ); + atlas::Library::initialise( argc, argv ); int jnode = 0; const double rpi = 2.0 * asin( 1.0 ); @@ -50,7 +50,7 @@ int main( int argc, char* argv[] ) { Log::info() << "memory field_pressure = " << field_pressure.bytes() * 1.e-9 << " GB" << std::endl; Log::info() << "==========================================" << std::endl; - atlas::Library::instance().finalise(); + atlas::Library::finalise(); atlas::mpi::finalize(); return 0; diff --git a/doc/user-guide/core-functionalities/fields/fields.cc b/doc/user-guide/core-functionalities/fields/fields.cc index 1709d8c1a..1d951a5cc 100644 --- a/doc/user-guide/core-functionalities/fields/fields.cc +++ b/doc/user-guide/core-functionalities/fields/fields.cc @@ -13,7 +13,7 @@ using atlas::array::make_shape; using atlas::array::make_view; int main( int argc, char* argv[] ) { - atlas::Library::instance().initialise( argc, argv ); + atlas::Library::initialise( argc, argv ); // Define fields Field field_pressure( "pressure", make_datatype(), make_shape( 100 ) ); diff --git a/doc/user-guide/core-functionalities/functionspace/NodeColumns.cc b/doc/user-guide/core-functionalities/functionspace/NodeColumns.cc index e516947fb..ecacc67d1 100644 --- a/doc/user-guide/core-functionalities/functionspace/NodeColumns.cc +++ b/doc/user-guide/core-functionalities/functionspace/NodeColumns.cc @@ -19,7 +19,7 @@ using atlas::functionspace::NodeColumns; using atlas::output::Gmsh; int main( int argc, char* argv[] ) { - atlas::Library::instance().initialise( argc, argv ); + atlas::Library::initialise( argc, argv ); // Generate global classic reduced Gaussian grid StructuredGrid grid( "N32" ); @@ -147,7 +147,7 @@ int main( int argc, char* argv[] ) { << "std_deviation: " << stddev << ", " << "nb_nodes: " << N << std::endl; - atlas::Library::instance().finalise(); + atlas::Library::finalise(); atlas::mpi::finalize(); return 0; diff --git a/doc/user-guide/core-functionalities/functionspace/StructuredColumns.cc b/doc/user-guide/core-functionalities/functionspace/StructuredColumns.cc index e512a812e..9e09bd4ec 100644 --- a/doc/user-guide/core-functionalities/functionspace/StructuredColumns.cc +++ b/doc/user-guide/core-functionalities/functionspace/StructuredColumns.cc @@ -16,7 +16,7 @@ using atlas::functionspace::StructuredColumns; using atlas::output::Gmsh; int main( int argc, char* argv[] ) { - atlas::Library::instance().initialise( argc, argv ); + Atlas::initialise( argc, argv ); // Generate global reduced grid Grid grid( "N32" ); @@ -63,7 +63,7 @@ int main( int argc, char* argv[] ) { gmsh.write( field_scalar1 ); } - Library::instance().finalise(); + Library::finalise(); mpi::finalise(); return 0; } diff --git a/doc/user-guide/core-functionalities/global-grids/Unstructured/global-grids-Unstructured.cc b/doc/user-guide/core-functionalities/global-grids/Unstructured/global-grids-Unstructured.cc index 843a786d1..c4cfe411c 100644 --- a/doc/user-guide/core-functionalities/global-grids/Unstructured/global-grids-Unstructured.cc +++ b/doc/user-guide/core-functionalities/global-grids/Unstructured/global-grids-Unstructured.cc @@ -12,7 +12,7 @@ using atlas::output::Gmsh; using atlas::util::Config; int main( int argc, char* argv[] ) { - atlas::Library::instance().initialise( argc, argv ); + atlas::Library::initialise( argc, argv ); Grid grid = UnstructuredGrid( {{180, 0}, {90, 0}, From 0c275d5b499552d802ef971dca7b7705df64d591 Mon Sep 17 00:00:00 2001 From: Rahul Mahajan Date: Thu, 9 Apr 2020 10:55:26 -0400 Subject: [PATCH 032/145] use cleaner API for atlas as suggested by @wdeconinck Change-Id: Iccb3f33131b32378737e5b647504b5e671db6ad4 --- .../core-functionalities/fields/fields-on-grid.cc | 4 ++-- doc/user-guide/core-functionalities/fields/fields.cc | 2 +- .../core-functionalities/functionspace/NodeColumns.cc | 4 ++-- .../core-functionalities/functionspace/StructuredColumns.cc | 6 +++--- .../global-grids/Unstructured/global-grids-Unstructured.cc | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/user-guide/core-functionalities/fields/fields-on-grid.cc b/doc/user-guide/core-functionalities/fields/fields-on-grid.cc index c38d827fc..d06e6bee5 100644 --- a/doc/user-guide/core-functionalities/fields/fields-on-grid.cc +++ b/doc/user-guide/core-functionalities/fields/fields-on-grid.cc @@ -12,7 +12,7 @@ using atlas::array::make_shape; using atlas::array::make_view; int main( int argc, char* argv[] ) { - atlas::Library::initialise( argc, argv ); + atlas::initialise( argc, argv ); int jnode = 0; const double rpi = 2.0 * asin( 1.0 ); @@ -50,7 +50,7 @@ int main( int argc, char* argv[] ) { Log::info() << "memory field_pressure = " << field_pressure.bytes() * 1.e-9 << " GB" << std::endl; Log::info() << "==========================================" << std::endl; - atlas::Library::finalise(); + atlas::finalise(); atlas::mpi::finalize(); return 0; diff --git a/doc/user-guide/core-functionalities/fields/fields.cc b/doc/user-guide/core-functionalities/fields/fields.cc index 1d951a5cc..77498e04f 100644 --- a/doc/user-guide/core-functionalities/fields/fields.cc +++ b/doc/user-guide/core-functionalities/fields/fields.cc @@ -13,7 +13,7 @@ using atlas::array::make_shape; using atlas::array::make_view; int main( int argc, char* argv[] ) { - atlas::Library::initialise( argc, argv ); + atlas::initialise( argc, argv ); // Define fields Field field_pressure( "pressure", make_datatype(), make_shape( 100 ) ); diff --git a/doc/user-guide/core-functionalities/functionspace/NodeColumns.cc b/doc/user-guide/core-functionalities/functionspace/NodeColumns.cc index ecacc67d1..12a01a128 100644 --- a/doc/user-guide/core-functionalities/functionspace/NodeColumns.cc +++ b/doc/user-guide/core-functionalities/functionspace/NodeColumns.cc @@ -19,7 +19,7 @@ using atlas::functionspace::NodeColumns; using atlas::output::Gmsh; int main( int argc, char* argv[] ) { - atlas::Library::initialise( argc, argv ); + atlas::initialise( argc, argv ); // Generate global classic reduced Gaussian grid StructuredGrid grid( "N32" ); @@ -147,7 +147,7 @@ int main( int argc, char* argv[] ) { << "std_deviation: " << stddev << ", " << "nb_nodes: " << N << std::endl; - atlas::Library::finalise(); + atlas::finalise(); atlas::mpi::finalize(); return 0; diff --git a/doc/user-guide/core-functionalities/functionspace/StructuredColumns.cc b/doc/user-guide/core-functionalities/functionspace/StructuredColumns.cc index 9e09bd4ec..6c2ad7718 100644 --- a/doc/user-guide/core-functionalities/functionspace/StructuredColumns.cc +++ b/doc/user-guide/core-functionalities/functionspace/StructuredColumns.cc @@ -16,7 +16,7 @@ using atlas::functionspace::StructuredColumns; using atlas::output::Gmsh; int main( int argc, char* argv[] ) { - Atlas::initialise( argc, argv ); + atlas::initialise( argc, argv ); // Generate global reduced grid Grid grid( "N32" ); @@ -63,7 +63,7 @@ int main( int argc, char* argv[] ) { gmsh.write( field_scalar1 ); } - Library::finalise(); - mpi::finalise(); + atlas::finalise(); + atlas::mpi::finalise(); return 0; } diff --git a/doc/user-guide/core-functionalities/global-grids/Unstructured/global-grids-Unstructured.cc b/doc/user-guide/core-functionalities/global-grids/Unstructured/global-grids-Unstructured.cc index c4cfe411c..772bb36eb 100644 --- a/doc/user-guide/core-functionalities/global-grids/Unstructured/global-grids-Unstructured.cc +++ b/doc/user-guide/core-functionalities/global-grids/Unstructured/global-grids-Unstructured.cc @@ -12,7 +12,7 @@ using atlas::output::Gmsh; using atlas::util::Config; int main( int argc, char* argv[] ) { - atlas::Library::initialise( argc, argv ); + atlas::initialise( argc, argv ); Grid grid = UnstructuredGrid( {{180, 0}, {90, 0}, From d0307dd7bd432d9194ab25ed765e425b960625da Mon Sep 17 00:00:00 2001 From: Rahul Mahajan Date: Thu, 9 Apr 2020 10:56:13 -0400 Subject: [PATCH 033/145] simplify doxygen logic. rely on FindDoxygen to find doxygen. Change-Id: Ie799ffadc999166f1fc6f4fc7e1046f9d1521f43 --- doc/CMakeLists.txt | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 1d6be3b66..556b961f6 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -69,23 +69,22 @@ if( NOT DEFINED ATLAS_DOXYGEN_GENERATOR ) endif() if( ATLAS_DOXYGEN_GENERATOR STREQUAL "m.css" ) - if( NOT DEFINED ATLAS_DOXYGEN_EXECUTABLE ) - set( ATLAS_DOXYGEN_EXECUTABLE doxygen.py ) - endif() set( ATLAS_DOXYFILE Doxyfile-mcss ) else() - if( NOT DEFINED ATLAS_DOXYGEN_EXECUTABLE ) - if( DOXYGEN_EXECUTABLE ) - set( ATLAS_DOXYGEN_EXECUTABLE ${DOXYGEN_EXECUTABLE} ) - else() - set( ATLAS_DOXYGEN_EXECUTABLE doxygen ) - endif() - endif() set( ATLAS_DOXYFILE Doxyfile-stock ) endif() +if( NOT DEFINED ATLAS_DOXYGEN_EXECUTABLE ) + find_package(Doxygen REQUIRED dot) + if( DOXYGEN_FOUND ) + set( ATLAS_DOXYGEN_EXECUTABLE ${DOXYGEN_EXECUTABLE} ) + else() + set( ATLAS_DOXYGEN_EXECUTABLE doxygen ) + endif() +endif() + foreach( doxyfile Doxyfile-default Doxyfile-custom Doxyfile-mcss Doxyfile-stock ) - execute_process( COMMAND ${CMAKE_COMMAND} -E create_symlink + execute_process( COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/${doxyfile} ${CMAKE_CURRENT_BINARY_DIR}/${doxyfile} ) endforeach() From 6679e6e8dbf9b1038ba0ae1a89b55784efa7e2fd Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 14 Apr 2020 15:30:44 +0100 Subject: [PATCH 034/145] compilation fixes with GridTools backend --- doc/user-guide/core-functionalities/fields/fields.cc | 6 +++--- .../core-functionalities/functionspace/NodeColumns.cc | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/user-guide/core-functionalities/fields/fields.cc b/doc/user-guide/core-functionalities/fields/fields.cc index 77498e04f..04d754dc3 100644 --- a/doc/user-guide/core-functionalities/fields/fields.cc +++ b/doc/user-guide/core-functionalities/fields/fields.cc @@ -25,9 +25,9 @@ int main( int argc, char* argv[] ) { // Assign values to fields for ( size_t jnode = 0; jnode < 100; ++jnode ) { - pressure( jnode ) = 101325.0; - wind( jnode, 0 ) = 0.01 + double( jnode ); - wind( jnode, 1 ) = 0.02 + double( jnode ); + pressure( jnode ) = 101325.0; + wind( jnode, size_t( 0 ) ) = 0.01 + double( jnode ); + wind( jnode, size_t( 1 ) ) = 0.02 + double( jnode ); } // Add info to fields diff --git a/doc/user-guide/core-functionalities/functionspace/NodeColumns.cc b/doc/user-guide/core-functionalities/functionspace/NodeColumns.cc index 12a01a128..775b1c22c 100644 --- a/doc/user-guide/core-functionalities/functionspace/NodeColumns.cc +++ b/doc/user-guide/core-functionalities/functionspace/NodeColumns.cc @@ -60,8 +60,8 @@ int main( int argc, char* argv[] ) { auto scalar1 = make_view( field_scalar1 ); auto lonlat = make_view( mesh.nodes().lonlat() ); for ( size_t jnode = 0; jnode < nb_nodes; ++jnode ) { - zlon = lonlat( jnode, 0 ) * deg2rad; - zlat = lonlat( jnode, 1 ) * deg2rad; + zlon = lonlat( jnode, size_t( 0 ) ) * deg2rad; + zlat = lonlat( jnode, size_t( 1 ) ) * deg2rad; zdist = 2.0 * sqrt( ( cos( zlat ) * sin( ( zlon - zlonc ) / 2 ) ) * ( cos( zlat ) * sin( ( zlon - zlonc ) / 2 ) ) + From 810a7f3893d68c55ce029f527684c665e5bda719 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 14 Apr 2020 15:31:38 +0100 Subject: [PATCH 035/145] Hide BaseIterator template that confuses Doxygen output --- src/atlas/util/VectorOfAbstract.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/atlas/util/VectorOfAbstract.h b/src/atlas/util/VectorOfAbstract.h index 5dec52c6a..097b55cd6 100644 --- a/src/atlas/util/VectorOfAbstract.h +++ b/src/atlas/util/VectorOfAbstract.h @@ -25,8 +25,8 @@ namespace util { //------------------------------------------------------------------------------------------------------ -template -class DereferenceIterator : public BaseIterator { +template +class DereferenceIterator : DOXYGEN_HIDE( public BaseIterator ) { public: using value_type = typename BaseIterator::value_type::element_type; using pointer = value_type*; From b6e7f0428c2e7c6a973790dd49f76f461d47388f Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 14 Apr 2020 18:55:37 +0100 Subject: [PATCH 036/145] Fix find_package(Doxygen) by replacing REQUIRED keyword with COMPONENTS --- doc/CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 556b961f6..ca5d9f8be 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -65,17 +65,18 @@ if( NOT DEFINED ATLAS_DOC_VERSION ) endif() if( NOT DEFINED ATLAS_DOXYGEN_GENERATOR ) - set( ATLAS_DOXYGEN_GENERATOR "m.css") + set( ATLAS_DOXYGEN_GENERATOR "stock") endif() if( ATLAS_DOXYGEN_GENERATOR STREQUAL "m.css" ) set( ATLAS_DOXYFILE Doxyfile-mcss ) + set( ATLAS_DOXYGEN_EXECUTABLE doxygen.py ) else() set( ATLAS_DOXYFILE Doxyfile-stock ) endif() if( NOT DEFINED ATLAS_DOXYGEN_EXECUTABLE ) - find_package(Doxygen REQUIRED dot) + find_package(Doxygen COMPONENTS dot) if( DOXYGEN_FOUND ) set( ATLAS_DOXYGEN_EXECUTABLE ${DOXYGEN_EXECUTABLE} ) else() From 7f53b428536d7a6be9da2e637c4bd9d71fea46d0 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 14 Apr 2020 23:04:27 +0100 Subject: [PATCH 037/145] Fix find_package(LATEX) by removing REQUIRED keyword --- cmake/features/DOCS.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/features/DOCS.cmake b/cmake/features/DOCS.cmake index 03b194822..1ce7bce80 100644 --- a/cmake/features/DOCS.cmake +++ b/cmake/features/DOCS.cmake @@ -1,7 +1,7 @@ ################################################################################ # documentation if( ENABLE_DOCS ) - find_package(LATEX REQUIRED COMPONENTS PDFLATEX BIBTEX OPTIONAL_COMPONENTS MAKEINDEX HTLATEX) + find_package(LATEX COMPONENTS PDFLATEX BIBTEX OPTIONAL_COMPONENTS MAKEINDEX HTLATEX) endif() ecbuild_add_option( FEATURE DOCS DESCRIPTION "Atlas documentation" From d02da29639a26376becfa5b6ea8b263aaba38b3b Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 17 Apr 2020 17:19:48 +0100 Subject: [PATCH 038/145] FunctionSpace::lonlat() and FunctionSpace::ghost() API --- src/atlas/functionspace/CellColumns.cc | 4 ++++ src/atlas/functionspace/CellColumns.h | 2 ++ src/atlas/functionspace/EdgeColumns.cc | 5 ++++ src/atlas/functionspace/EdgeColumns.h | 2 ++ src/atlas/functionspace/FunctionSpace.cc | 8 +++++++ src/atlas/functionspace/FunctionSpace.h | 4 ++++ src/atlas/functionspace/NodeColumns.h | 7 ++++-- src/atlas/functionspace/PointCloud.cc | 2 +- src/atlas/functionspace/PointCloud.h | 6 ++--- .../functionspace/detail/FunctionSpaceImpl.cc | 8 +++++++ .../functionspace/detail/FunctionSpaceImpl.h | 4 ++++ .../functionspace/detail/StructuredColumns.cc | 3 +-- .../functionspace/detail/StructuredColumns.h | 3 ++- .../test_interpolation_cubic_prototype.cc | 24 +++++++++---------- 14 files changed, 60 insertions(+), 22 deletions(-) diff --git a/src/atlas/functionspace/CellColumns.cc b/src/atlas/functionspace/CellColumns.cc index e5352b569..2d9eb60fa 100644 --- a/src/atlas/functionspace/CellColumns.cc +++ b/src/atlas/functionspace/CellColumns.cc @@ -559,6 +559,10 @@ const parallel::Checksum& CellColumns::checksum() const { return *checksum_; } +Field CellColumns::lonlat() const { + return mesh_.cells().field( "lonlat" ); +} + //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ diff --git a/src/atlas/functionspace/CellColumns.h b/src/atlas/functionspace/CellColumns.h index 94903c4c5..485c2b0a0 100644 --- a/src/atlas/functionspace/CellColumns.h +++ b/src/atlas/functionspace/CellColumns.h @@ -85,6 +85,8 @@ class CellColumns : public functionspace::FunctionSpaceImpl { virtual idx_t size() const override { return nb_cells_; } + Field lonlat() const override; + private: // methods idx_t config_size( const eckit::Configuration& config ) const; array::DataType config_datatype( const eckit::Configuration& ) const; diff --git a/src/atlas/functionspace/EdgeColumns.cc b/src/atlas/functionspace/EdgeColumns.cc index 9448adc8e..c849d70c9 100644 --- a/src/atlas/functionspace/EdgeColumns.cc +++ b/src/atlas/functionspace/EdgeColumns.cc @@ -15,6 +15,7 @@ #include "eckit/utils/MD5.h" #include "atlas/array/MakeView.h" +#include "atlas/field/detail/FieldImpl.h" #include "atlas/functionspace/EdgeColumns.h" #include "atlas/library/config.h" #include "atlas/mesh/HybridElements.h" @@ -557,6 +558,10 @@ const parallel::Checksum& EdgeColumns::checksum() const { return *checksum_; } +Field EdgeColumns::lonlat() const { + return mesh_.edges().field("lonlat"); +} + //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ diff --git a/src/atlas/functionspace/EdgeColumns.h b/src/atlas/functionspace/EdgeColumns.h index 9b11ca9d7..8dc4ba6de 100644 --- a/src/atlas/functionspace/EdgeColumns.h +++ b/src/atlas/functionspace/EdgeColumns.h @@ -85,6 +85,8 @@ class EdgeColumns : public functionspace::FunctionSpaceImpl { virtual idx_t size() const override { return nb_edges_; } + Field lonlat() const override; + private: // methods idx_t config_size( const eckit::Configuration& config ) const; array::DataType config_datatype( const eckit::Configuration& ) const; diff --git a/src/atlas/functionspace/FunctionSpace.cc b/src/atlas/functionspace/FunctionSpace.cc index 97d1ba54e..3c724530b 100644 --- a/src/atlas/functionspace/FunctionSpace.cc +++ b/src/atlas/functionspace/FunctionSpace.cc @@ -58,6 +58,14 @@ idx_t FunctionSpace::nb_partitions() const { return get()->nb_partitions(); } +Field FunctionSpace::lonlat() const { + return get()->lonlat(); +} + +Field FunctionSpace::ghost() const { + return get()->ghost(); +} + void FunctionSpace::haloExchange( const FieldSet& fields, bool on_device ) const { return get()->haloExchange( fields, on_device ); } diff --git a/src/atlas/functionspace/FunctionSpace.h b/src/atlas/functionspace/FunctionSpace.h index 9b74b3658..749ab915e 100644 --- a/src/atlas/functionspace/FunctionSpace.h +++ b/src/atlas/functionspace/FunctionSpace.h @@ -69,6 +69,10 @@ class FunctionSpace : DOXYGEN_HIDE( public util::ObjectHandle(), array::make_shape( size() ) ); array::make_view( ghost_ ).assign( 0 ); diff --git a/src/atlas/functionspace/PointCloud.h b/src/atlas/functionspace/PointCloud.h index fca6de268..5c4ae0bef 100644 --- a/src/atlas/functionspace/PointCloud.h +++ b/src/atlas/functionspace/PointCloud.h @@ -39,9 +39,9 @@ class PointCloud : public functionspace::FunctionSpaceImpl { virtual operator bool() const override { return true; } virtual size_t footprint() const override { return sizeof( *this ); } virtual std::string distribution() const override; - const Field& lonlat() const { return lonlat_; } + Field lonlat() const override { return lonlat_; } const Field& vertical() const { return vertical_; } - const Field& ghost() const; + Field ghost() const override; virtual idx_t size() const override { return lonlat_.shape( 0 ); } using FunctionSpaceImpl::createField; @@ -167,9 +167,7 @@ class PointCloud : public FunctionSpace { operator bool() const { return valid(); } bool valid() const { return functionspace_; } - const Field& lonlat() const { return functionspace_->lonlat(); } const Field& vertical() const { return functionspace_->vertical(); } - const Field& ghost() const { return functionspace_->ghost(); } detail::PointCloud::Iterate iterate() const { return functionspace_->iterate(); } diff --git a/src/atlas/functionspace/detail/FunctionSpaceImpl.cc b/src/atlas/functionspace/detail/FunctionSpaceImpl.cc index 84e2c3642..a8fb3c73f 100644 --- a/src/atlas/functionspace/detail/FunctionSpaceImpl.cc +++ b/src/atlas/functionspace/detail/FunctionSpaceImpl.cc @@ -45,6 +45,14 @@ Field NoFunctionSpace::createField( const Field&, const eckit::Configuration& ) ATLAS_NOTIMPLEMENTED; } +Field FunctionSpaceImpl::lonlat() const { + ATLAS_NOTIMPLEMENTED; +} + +Field FunctionSpaceImpl::ghost() const { + ATLAS_NOTIMPLEMENTED; +} + const util::PartitionPolygon& FunctionSpaceImpl::polygon( idx_t /*halo */ ) const { throw_Exception( "polygon() not implemented in derived class", Here() ); } diff --git a/src/atlas/functionspace/detail/FunctionSpaceImpl.h b/src/atlas/functionspace/detail/FunctionSpaceImpl.h index ec8606871..f71b013aa 100644 --- a/src/atlas/functionspace/detail/FunctionSpaceImpl.h +++ b/src/atlas/functionspace/detail/FunctionSpaceImpl.h @@ -81,6 +81,10 @@ class FunctionSpaceImpl : public util::Object { virtual const util::PartitionPolygon& polygon( idx_t halo = 0 ) const; + virtual atlas::Field lonlat() const; + + virtual atlas::Field ghost() const; + virtual const util::PartitionPolygons& polygons() const; virtual const Projection& projection() const; diff --git a/src/atlas/functionspace/detail/StructuredColumns.cc b/src/atlas/functionspace/detail/StructuredColumns.cc index c2024af65..335367f52 100644 --- a/src/atlas/functionspace/detail/StructuredColumns.cc +++ b/src/atlas/functionspace/detail/StructuredColumns.cc @@ -454,8 +454,7 @@ const util::PartitionPolygon& StructuredColumns::polygon( idx_t halo ) const { return *polygon_; } - -const atlas::util::PartitionPolygons& StructuredColumns::polygons() const { +const util::PartitionPolygons& StructuredColumns::polygons() const { if ( polygons_.size() ) { return polygons_; } diff --git a/src/atlas/functionspace/detail/StructuredColumns.h b/src/atlas/functionspace/detail/StructuredColumns.h index 5da44dfc0..b93b1039a 100644 --- a/src/atlas/functionspace/detail/StructuredColumns.h +++ b/src/atlas/functionspace/detail/StructuredColumns.h @@ -131,6 +131,7 @@ class StructuredColumns : public FunctionSpaceImpl { return ij2gp_( i, j ); } + Field lonlat() const override { return field_xy_; } Field xy() const { return field_xy_; } Field z() const { return vertical().z(); } Field partition() const { return field_partition_; } @@ -143,7 +144,7 @@ class StructuredColumns : public FunctionSpaceImpl { } Field index_i() const { return field_index_i_; } Field index_j() const { return field_index_j_; } - Field ghost() const { return field_ghost_; } + Field ghost() const override { return field_ghost_; } void compute_xy( idx_t i, idx_t j, PointXY& xy ) const; PointXY compute_xy( idx_t i, idx_t j ) const { diff --git a/src/tests/interpolation/test_interpolation_cubic_prototype.cc b/src/tests/interpolation/test_interpolation_cubic_prototype.cc index 8e7b8cb6b..41ce7f4e0 100644 --- a/src/tests/interpolation/test_interpolation_cubic_prototype.cc +++ b/src/tests/interpolation/test_interpolation_cubic_prototype.cc @@ -160,18 +160,18 @@ bool operator==( const std::vector& t1, const std::vecto } return true; } -std::ostream& operator<<( std::ostream& out, const LocalView& array ) { - out << "{ "; - for ( idx_t i = 0; i < array.size(); ++i ) { - out << array( i ); - if ( i < array.size() - 1 ) { - out << ","; - } - out << " "; - } - out << "}"; - return out; -} +//std::ostream& operator<<( std::ostream& out, const LocalView& array ) { +// out << "{ "; +// for ( idx_t i = 0; i < array.size(); ++i ) { +// out << array( i ); +// if ( i < array.size() - 1 ) { +// out << ","; +// } +// out << " "; +// } +// out << "}"; +// return out; +//} //----------------------------------------------------------------------------- From 10f3647ff9044485bbdd43fd7c2b6d57f69f72f7 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 27 Apr 2020 17:49:36 +0100 Subject: [PATCH 039/145] Fix compilation warnings --- src/atlas/field/State.cc | 14 ++++--- .../detail/StructuredColumns_setup.cc | 9 ++-- .../partitioner/EqualRegionsPartitioner.cc | 3 +- .../grid/detail/spacing/SpacingFactory.cc | 2 + src/atlas/interpolation/method/Method.h | 3 +- .../interpolation/method/fe/FiniteElement.h | 1 + .../method/knn/KNearestNeighbours.h | 1 + .../method/knn/NearestNeighbour.h | 2 + src/atlas/parallel/omp/omp.cc | 42 +++++++++++++++---- 9 files changed, 58 insertions(+), 19 deletions(-) diff --git a/src/atlas/field/State.cc b/src/atlas/field/State.cc index 4a167fc75..da4d46789 100644 --- a/src/atlas/field/State.cc +++ b/src/atlas/field/State.cc @@ -43,9 +43,11 @@ void load_builder() { StateGeneratorBuilder( "tmp" ); } -struct force_link { - force_link() = default; -}; +void force_link() { + static struct Link { Link() = default; } link; + []( const Link& ) {}( link ); // disable unused warnings +} + } // namespace void State::initialize( const std::string& generator, const eckit::Parametrisation& params ) { @@ -151,7 +153,7 @@ StateGenerator* StateGeneratorFactory::build( const std::string& name, const eck eckit::AutoLock lock( local_mutex ); - static force_link static_linking; + force_link(); std::map::const_iterator j = m->find( name ); @@ -174,7 +176,7 @@ void StateGeneratorFactory::list( std::ostream& out ) { eckit::AutoLock lock( local_mutex ); - static force_link static_linking; + force_link(); const char* sep = ""; for ( std::map::const_iterator j = m->begin(); j != m->end(); ++j ) { @@ -188,7 +190,7 @@ bool StateGeneratorFactory::has( const std::string& name ) { eckit::AutoLock lock( local_mutex ); - static force_link static_linking; + force_link(); return ( m->find( name ) != m->end() ); } diff --git a/src/atlas/functionspace/detail/StructuredColumns_setup.cc b/src/atlas/functionspace/detail/StructuredColumns_setup.cc index 559c76e1e..06790840a 100644 --- a/src/atlas/functionspace/detail/StructuredColumns_setup.cc +++ b/src/atlas/functionspace/detail/StructuredColumns_setup.cc @@ -73,11 +73,12 @@ struct GridPointSet { idx_t size() const { return static_cast( gp_.size() ); } - using const_iterator = decltype( gp_ )::const_iterator; - - const_iterator begin() const { return gp_.begin(); } - const_iterator end() const { return gp_.end(); } const GridPoint& operator[]( idx_t i ) const { return gp_[i]; } + + // unused: + //using const_iterator = decltype( gp_ )::const_iterator; + //const_iterator begin() const { return gp_.begin(); } + //const_iterator end() const { return gp_.end(); } }; } // namespace diff --git a/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.cc b/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.cc index 585da25c5..1caacbeba 100644 --- a/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.cc +++ b/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.cc @@ -48,7 +48,8 @@ struct range_t { Sentinel e; Iterator begin() const { return b; } Sentinel end() const { return e; } - bool empty() const { return begin() == end(); } + // unused: + // bool empty() const { return begin() == end(); } }; template diff --git a/src/atlas/grid/detail/spacing/SpacingFactory.cc b/src/atlas/grid/detail/spacing/SpacingFactory.cc index a849f47cc..f33d89566 100644 --- a/src/atlas/grid/detail/spacing/SpacingFactory.cc +++ b/src/atlas/grid/detail/spacing/SpacingFactory.cc @@ -18,8 +18,10 @@ namespace spacing { //---------------------------------------------------------------------------------------------------------------------- + void force_link() { static struct Link { Link() = default; } link; + []( const Link& ) {}( link ); // disable unused warnings } //---------------------------------------------------------------------------------------------------------------------- diff --git a/src/atlas/interpolation/method/Method.h b/src/atlas/interpolation/method/Method.h index 76125f333..d78a5e25d 100644 --- a/src/atlas/interpolation/method/Method.h +++ b/src/atlas/interpolation/method/Method.h @@ -76,12 +76,13 @@ class Method : public util::Object { bool use_eckit_linalg_spmv_; -private: +protected: virtual void do_setup( const FunctionSpace& source, const FunctionSpace& target ) = 0; virtual void do_setup( const Grid& source, const Grid& target ) = 0; virtual void do_setup( const FunctionSpace& source, const Field& target ); virtual void do_setup( const FunctionSpace& source, const FieldSet& target ); +private: template void interpolate_field( const Field& src, Field& tgt ) const; diff --git a/src/atlas/interpolation/method/fe/FiniteElement.h b/src/atlas/interpolation/method/fe/FiniteElement.h index 72e18c82a..5173da23b 100644 --- a/src/atlas/interpolation/method/fe/FiniteElement.h +++ b/src/atlas/interpolation/method/fe/FiniteElement.h @@ -60,6 +60,7 @@ class FiniteElement : public Method { virtual const FunctionSpace& target() const override { return target_; } private: + using Method::do_setup; virtual void do_setup( const FunctionSpace& source, const FunctionSpace& target ) override; virtual void do_setup( const Grid& source, const Grid& target ) override; diff --git a/src/atlas/interpolation/method/knn/KNearestNeighbours.h b/src/atlas/interpolation/method/knn/KNearestNeighbours.h index da0f7dd64..958bd10e3 100644 --- a/src/atlas/interpolation/method/knn/KNearestNeighbours.h +++ b/src/atlas/interpolation/method/knn/KNearestNeighbours.h @@ -36,6 +36,7 @@ class KNearestNeighbours : public KNearestNeighboursBase { virtual const FunctionSpace& target() const override { return target_; } private: + using KNearestNeighboursBase::do_setup; virtual void do_setup( const FunctionSpace& source, const FunctionSpace& target ) override; virtual void do_setup( const Grid& source, const Grid& target ) override; diff --git a/src/atlas/interpolation/method/knn/NearestNeighbour.h b/src/atlas/interpolation/method/knn/NearestNeighbour.h index 6b50cf6e4..9dc8f5768 100644 --- a/src/atlas/interpolation/method/knn/NearestNeighbour.h +++ b/src/atlas/interpolation/method/knn/NearestNeighbour.h @@ -29,6 +29,8 @@ class NearestNeighbour : public KNearestNeighboursBase { virtual const FunctionSpace& target() const override { return target_; } private: + using KNearestNeighboursBase::do_setup; + /** * @brief Create an interpolant sparse matrix relating two (pre-partitioned) * meshes, diff --git a/src/atlas/parallel/omp/omp.cc b/src/atlas/parallel/omp/omp.cc index 3b3f83368..2065e36b9 100644 --- a/src/atlas/parallel/omp/omp.cc +++ b/src/atlas/parallel/omp/omp.cc @@ -53,8 +53,12 @@ int atlas_omp_get_num_threads( void ) { if ( omp_get_num_threads ) { return omp_get_num_threads(); } -#endif + else { + return 1; + } +#else return 1; +#endif } int atlas_omp_get_max_threads( void ) { @@ -62,8 +66,12 @@ int atlas_omp_get_max_threads( void ) { if ( omp_get_max_threads ) { return omp_get_max_threads(); } -#endif + else { + return 1; + } +#else return 1; +#endif } int atlas_omp_get_thread_num( void ) { @@ -71,8 +79,12 @@ int atlas_omp_get_thread_num( void ) { if ( omp_get_thread_num ) { return omp_get_thread_num(); } -#endif + else { + return 0; + } +#else return 0; +#endif } int atlas_omp_get_num_procs( void ) { @@ -80,8 +92,12 @@ int atlas_omp_get_num_procs( void ) { if ( omp_get_num_procs ) { return omp_get_num_procs(); } -#endif + else { + return 1; + } +#else return 1; +#endif } int atlas_omp_in_parallel( void ) { @@ -89,8 +105,12 @@ int atlas_omp_in_parallel( void ) { if ( omp_in_parallel ) { return omp_in_parallel(); } -#endif + else { + return 0; + } +#else return 0; +#endif } void atlas_omp_set_dynamic( int dynamic_threads ) { @@ -106,8 +126,12 @@ int atlas_omp_get_dynamic( void ) { if ( omp_get_dynamic ) { return omp_get_dynamic(); } -#endif + else { + return 0; + } +#else return 0; +#endif } void atlas_omp_set_nested( int nested ) { @@ -123,6 +147,10 @@ int atlas_omp_get_nested( void ) { if ( omp_get_nested ) { return omp_get_nested(); } -#endif + else { + return 0; + } +#else return 0; +#endif } From d17da0cacf5ee93038c9a3f295bf7ede7e0fc685 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 27 Apr 2020 22:22:38 +0100 Subject: [PATCH 040/145] Update clang-format version to 9.0.1 --- src/atlas/functionspace/EdgeColumns.cc | 2 +- tools/apply-clang-format.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/atlas/functionspace/EdgeColumns.cc b/src/atlas/functionspace/EdgeColumns.cc index c849d70c9..7b7a1e5a2 100644 --- a/src/atlas/functionspace/EdgeColumns.cc +++ b/src/atlas/functionspace/EdgeColumns.cc @@ -559,7 +559,7 @@ const parallel::Checksum& EdgeColumns::checksum() const { } Field EdgeColumns::lonlat() const { - return mesh_.edges().field("lonlat"); + return mesh_.edges().field( "lonlat" ); } //------------------------------------------------------------------------------ diff --git a/tools/apply-clang-format.sh b/tools/apply-clang-format.sh index b541c3284..68e10cd40 100755 --- a/tools/apply-clang-format.sh +++ b/tools/apply-clang-format.sh @@ -8,9 +8,9 @@ # granted to it by virtue of its status as an intergovernmental organisation nor # does it submit to any jurisdiction. -_REQUIRED_CLANG_VERSION='7.0.1' +_REQUIRED_CLANG_VERSION='9.0.1' -export PATH=/usr/local/apps/clang/7.0.1/bin:$PATH +export PATH=/usr/local/apps/clang/9.0.1/bin:$PATH if ! [ -x "$(command -v clang-format)" ]; then echo 'Error: clang-format is not installed.' >&2 From 9448e68fc266ffa2677dba3e76074f47adadeade Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 28 Apr 2020 02:02:49 +0100 Subject: [PATCH 041/145] Fix warnings for clang that were introduced in 0.20.2 (ATLAS-274) --- src/atlas/array/LocalView.h | 30 +++++++++-------- .../array/gridtools/GridToolsArrayView.h | 32 +++++++++++-------- src/atlas/array/native/NativeArrayView.h | 32 ++++++++++--------- 3 files changed, 52 insertions(+), 42 deletions(-) diff --git a/src/atlas/array/LocalView.h b/src/atlas/array/LocalView.h index 4a8df7206..fce27e16a 100644 --- a/src/atlas/array/LocalView.h +++ b/src/atlas/array/LocalView.h @@ -64,13 +64,19 @@ namespace array { template class LocalView { -#define ENABLE_IF_CONST \ - template ::value && EnableBool ), int>::type* = nullptr> + template + using is_non_const_value_type = typename std::is_same::type>; + #define ENABLE_IF_NON_CONST \ template ::value && EnableBool ), int>::type* = nullptr> +#define ENABLE_IF_CONST_WITH_NON_CONST( T ) \ + template ::value && is_non_const_value_type::value ), \ + int>::type* = nullptr> + + public: // -- Type definitions using value_type = Value; @@ -95,8 +101,7 @@ class LocalView { public: // -- Constructors - template ::value && - !std::is_const::value>::type> + ENABLE_IF_CONST_WITH_NON_CONST( value_type ) LocalView( value_type* data, const idx_t shape[], const idx_t strides[] ) : data_( data ) { size_ = 1; for ( idx_t j = 0; j < Rank; ++j ) { @@ -116,8 +121,7 @@ class LocalView { } - template ::value && - !std::is_const::value>::type> + ENABLE_IF_CONST_WITH_NON_CONST( value_type ) LocalView( value_type* data, const idx_t shape[] ) : data_( data ) { size_ = 1; for ( int j = Rank - 1; j >= 0; --j ) { @@ -157,11 +161,11 @@ class LocalView { } } - using non_const_value_type = typename std::remove_const::type; - - ENABLE_IF_CONST - operator const LocalView&() const { - return (const LocalView&)( *this ); + ENABLE_IF_CONST_WITH_NON_CONST( value_type ) + operator const LocalView&() const { + static_assert( std::is_const::value, "must be const" ); + static_assert( !std::is_const::value, "must be non-const" ); + return (const LocalView&)( *this ); } // -- Access methods @@ -292,8 +296,8 @@ class LocalView { idx_t shape_[Rank]; idx_t strides_[Rank]; -#undef ENABLE_IF_CONST #undef ENABLE_IF_NON_CONST +#undef ENABLE_IF_CONST_WITH_NON_CONST }; } // namespace array diff --git a/src/atlas/array/gridtools/GridToolsArrayView.h b/src/atlas/array/gridtools/GridToolsArrayView.h index ae5bae949..9e101eb2c 100644 --- a/src/atlas/array/gridtools/GridToolsArrayView.h +++ b/src/atlas/array/gridtools/GridToolsArrayView.h @@ -30,18 +30,24 @@ namespace array { template class ArrayView { + template + using is_non_const_value_type = typename std::is_same::type>; + +#define ENABLE_IF_NON_CONST \ + template ::value && EnableBool ), int>::type* = nullptr> + +#define ENABLE_IF_CONST_WITH_NON_CONST( T ) \ + template ::value && is_non_const_value_type::value ), \ + int>::type* = nullptr> + public: // -- Type definitions using value_type = Value; using non_const_value_type = typename std::remove_const::type; static constexpr bool is_const = std::is_const::value; static constexpr bool is_non_const = !std::is_const::value; -#define ENABLE_IF_CONST \ - template ::value && EnableBool ), int>::type* = nullptr> -#define ENABLE_IF_NON_CONST \ - template ::value && EnableBool ), int>::type* = nullptr> // static constexpr Intent ACCESS{AccessMode}; static constexpr int RANK{Rank}; @@ -68,8 +74,8 @@ class ArrayView { ArrayView( const ArrayView& other ); ArrayView( const Array& array, bool device_view ); - ENABLE_IF_CONST - ArrayView( const ArrayView& other ) : + ENABLE_IF_CONST_WITH_NON_CONST( value_type ) + ArrayView( const ArrayView& other ) : gt_data_view_( other.is_device_view_ ? gridtools::make_gt_device_view( *other.array_ ) : gridtools::make_gt_host_view( *other.array_ ) ), data_store_orig_( other.data_store_orig_ ), @@ -81,10 +87,8 @@ class ArrayView { // TODO: check compatibility } - ENABLE_IF_CONST - operator const ArrayView&() const { - return *(const ArrayView*)( this ); - } + ENABLE_IF_CONST_WITH_NON_CONST( value_type ) + operator const ArrayView&() const { return *(const ArrayView*)( this ); } value_type* data() { return gt_data_view_.data(); } value_type const* data() const { return gt_data_view_.data(); } @@ -179,11 +183,11 @@ class ArrayView { ArrayDataStore const* data_store_orig_; Array const* array_; bool is_device_view_{false}; +#undef ENABLE_IF_NON_CONST +#undef ENABLE_IF_CONST_WITH_NON_CONST }; //------------------------------------------------------------------------------------------------------ } // namespace array } // namespace atlas -#undef ENABLE_IF_NON_CONST -#undef ENABLE_IF_CONST diff --git a/src/atlas/array/native/NativeArrayView.h b/src/atlas/array/native/NativeArrayView.h index f86348599..4d531b5d0 100644 --- a/src/atlas/array/native/NativeArrayView.h +++ b/src/atlas/array/native/NativeArrayView.h @@ -96,20 +96,24 @@ namespace array { template class ArrayView { + template + using is_non_const_value_type = typename std::is_same::type>; + +#define ENABLE_IF_NON_CONST \ + template ::value && EnableBool ), int>::type* = nullptr> + +#define ENABLE_IF_CONST_WITH_NON_CONST( T ) \ + template ::value && is_non_const_value_type::value ), \ + int>::type* = nullptr> + public: // -- Type definitions using value_type = Value; using non_const_value_type = typename std::remove_const::type; static constexpr bool is_const = std::is_const::value; static constexpr bool is_non_const = !std::is_const::value; -#define ENABLE_IF_CONST \ - template ::value && EnableBool ), int>::type* = nullptr> -#define ENABLE_IF_NON_CONST \ - template ::value && EnableBool ), int>::type* = nullptr> - - // static constexpr Intent ACCESS{AccessMode}; static constexpr int RANK{Rank}; private: @@ -132,8 +136,8 @@ class ArrayView { ArrayView( const ArrayView& other ) : data_( other.data_ ), size_( other.size_ ), shape_( other.shape_ ), strides_( other.strides_ ) {} - ENABLE_IF_CONST - ArrayView( const ArrayView& other ) : data_( other.data() ), size_( other.size() ) { + ENABLE_IF_CONST_WITH_NON_CONST( value_type ) + ArrayView( const ArrayView& other ) : data_( other.data() ), size_( other.size() ) { for ( idx_t j = 0; j < Rank; ++j ) { shape_[j] = other.shape( j ); strides_[j] = other.stride( j ); @@ -152,10 +156,8 @@ class ArrayView { } #endif - ENABLE_IF_CONST - operator const ArrayView&() const { - return *(const ArrayView*)( this ); - } + ENABLE_IF_CONST_WITH_NON_CONST( value_type ) + operator const ArrayView&() const { return *(const ArrayView*)( this ); } // -- Access methods @@ -370,8 +372,8 @@ class ArrayView { //------------------------------------------------------------------------------------------------------ -#undef ENABLE_IF_CONST #undef ENABLE_IF_NON_CONST +#undef ENABLE_IF_CONST_WITH_NON_CONST } // namespace array } // namespace atlas From 8452fb6f9f8a6e0fdc094edfb4d56aaf4fa6fcd5 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 24 Apr 2020 21:38:22 +0100 Subject: [PATCH 042/145] ATLAS-273 IndexKDTree with customizable geometry (Earth radius) and more consistent function names --- src/atlas/CMakeLists.txt | 2 + src/atlas/util/Geometry.h | 108 ++++++++ src/atlas/util/KDTree.h | 247 +++++++++--------- src/atlas/util/Point.h | 1 + src/atlas/util/PolygonLocator.h | 4 +- src/atlas/util/detail/KDTree.h | 413 +++++++++++++++++++++++++++++++ src/tests/AtlasTestEnvironment.h | 19 +- src/tests/util/test_kdtree.cc | 264 +++++++++++++++++--- 8 files changed, 878 insertions(+), 180 deletions(-) create mode 100644 src/atlas/util/Geometry.h create mode 100644 src/atlas/util/detail/KDTree.h diff --git a/src/atlas/CMakeLists.txt b/src/atlas/CMakeLists.txt index c85760a02..c65ff448d 100644 --- a/src/atlas/CMakeLists.txt +++ b/src/atlas/CMakeLists.txt @@ -584,6 +584,7 @@ util/Constants.h util/Earth.h util/GaussianLatitudes.cc util/GaussianLatitudes.h +util/Geometry.h util/KDTree.h util/PolygonXY.cc util/PolygonXY.h @@ -603,6 +604,7 @@ util/VectorOfAbstract.h util/detail/BlackMagic.h util/detail/Cache.h util/detail/Debug.h +util/detail/KDTree.h ) list( APPEND atlas_internals_srcs diff --git a/src/atlas/util/Geometry.h b/src/atlas/util/Geometry.h new file mode 100644 index 000000000..a2b81889f --- /dev/null +++ b/src/atlas/util/Geometry.h @@ -0,0 +1,108 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#pragma once + +#include +#include + +#include "atlas/runtime/Exception.h" +#include "atlas/util/Earth.h" +#include "atlas/util/Point.h" + +namespace atlas { +namespace util { + +struct GeometryBase { + virtual ~GeometryBase() = default; + virtual void lonlat2xyz( const Point2&, Point3& ) const = 0; + virtual void xyz2lonlat( const Point3&, Point2& ) const = 0; + + Point3 xyz( const Point2& lonlat ) const { + Point3 xyz; + lonlat2xyz( lonlat, xyz ); + return xyz; + } + Point2 lonlat( const Point3& xyz ) const { + Point2 lonlat; + xyz2lonlat( xyz, lonlat ); + return lonlat; + } +}; + +template +struct GeometrySphereT : public GeometryBase { + void lonlat2xyz( const Point2& lonlat, Point3& xyz ) const override { + SphereT::convertSphericalToCartesian( lonlat, xyz ); + } + void xyz2lonlat( const Point3& xyz, Point2& lonlat ) const override { + SphereT::convertCartesianToSpherical( xyz, lonlat ); + } +}; + +struct GeometrySphere : public GeometryBase { + GeometrySphere( double radius ) : radius_( radius ) {} + void lonlat2xyz( const Point2& lonlat, Point3& xyz ) const override { + eckit::geometry::Sphere::convertSphericalToCartesian( radius_, lonlat, xyz ); + } + void xyz2lonlat( const Point3& xyz, Point2& lonlat ) const override { + eckit::geometry::Sphere::convertCartesianToSpherical( radius_, xyz, lonlat ); + } + double radius_; +}; + +struct Geometry { + Geometry() { build(); } + + Geometry( const std::string& name ) { build( name ); } + + Geometry( const std::shared_ptr& conversion ) : + shared_impl_( conversion ), impl_( shared_impl_.get() ) {} + + Geometry( const Geometry& other ) : shared_impl_( other.shared_impl_ ), impl_( shared_impl_.get() ) {} + + Point3 xyz( const Point2& lonlat ) const { return impl_->xyz( lonlat ); } + Point2 lonlat( const Point3& xyz ) const { return impl_->lonlat( xyz ); } + void xyz2lonlat( const Point3& xyz, Point2& lonlat ) const { return impl_->xyz2lonlat( xyz, lonlat ); } + void lonlat2xyz( const Point2& lonlat, Point3& xyz ) const { return impl_->lonlat2xyz( lonlat, xyz ); } + +private: + std::shared_ptr shared_impl_; + GeometryBase* impl_; + + template + void build( Args... args ) { + shared_impl_ = std::make_shared( args... ); + impl_ = shared_impl_.get(); + } + + void build( const std::string& name = "Earth" ) { + // Factory without self registration + if ( name == "Earth" ) { + build>(); + } + else if ( name == "UnitSphere" ) { + build>(); + } + else { + ATLAS_THROW_EXCEPTION( "name " + name + " is not a valid key for a Geometry" ); + } + } +}; + +struct Sphere : public Geometry { + using Geometry::Geometry; + Sphere( double radius = Earth::radius() ) : Geometry( std::make_shared( radius ) ) {} +}; + +//------------------------------------------------------------------------------------------------------ + +} // namespace util +} // namespace atlas diff --git a/src/atlas/util/KDTree.h b/src/atlas/util/KDTree.h index d18337007..81dd3104a 100644 --- a/src/atlas/util/KDTree.h +++ b/src/atlas/util/KDTree.h @@ -10,11 +10,9 @@ #pragma once -#include "eckit/container/KDTree.h" - -#include "atlas/library/config.h" -#include "atlas/runtime/Exception.h" -#include "atlas/util/Point.h" +#include "atlas/util/Geometry.h" +#include "atlas/util/ObjectHandle.h" +#include "atlas/util/detail/KDTree.h" namespace atlas { namespace util { @@ -43,159 +41,138 @@ namespace util { /// We can now do e.g. a search for the nearest 4 neighbours (k=4) sorted by shortest distance /// @code{.cpp} /// idx_t k = 4; -/// auto neighbours = search.kNearestNeighbours( PointLonLat{180., 45.}, k ).payloads(); +/// auto neighbours = search.closestPoints( PointLonLat{180., 45.}, k ).payloads(); /// @endcode /// The variable `neighbours` is now a container of indices (the payloads) of the 4 nearest points -template -class KDTree { -private: - struct eckit_KDTreeTraits { - using Point = Point3; - using Payload = PayloadT; - }; - using eckit_KDTree = eckit::KDTreeMemory; +template +class KDTree : ObjectHandle> { public: - using Payload = PayloadT; - using Node = typename eckit_KDTree::NodeInfo; - using PayLoadList = std::vector; - - class NodeList : public eckit_KDTree::NodeList { - using Base = typename eckit_KDTree::NodeList; - - public: - NodeList( typename eckit_KDTree::NodeList&& list ) : Base( std::move( list ) ) {} - PayLoadList payloads() const { - PayLoadList list; - list.reserve( Base::size() ); - for ( auto& item : *this ) { - list.emplace_back( item.payload() ); - } - return list; - } - }; + using Handle = typename ObjectHandle>::Handle; + using Implementation = typename Handle::Implementation; + using Point = typename Implementation::Point; + using Payload = typename Implementation::Payload; + using PayloadList = typename Implementation::PayloadList; + using Value = typename Implementation::Value; + using ValueList = typename Implementation::ValueList; - /// @brief Reserve memory for building the kdtree in one shot (optional, at cost of extra memory) - void reserve( idx_t size ); + using Handle::get; + using Handle::Handle; - /// @brief Insert spherical point (lon,lat) - /// If memory has been reserved with reserve(), insertion will be delayed until build() is called. - void insert( const Point2& p2, const Payload& payload ); +public: + //-------------------------------------------------------------------------------------- + // Constructors - /// @brief Insert 3D cartesian point (x,y,z) - /// If memory has been reserved with reserve(), insertion will be delayed until build() is called. - void insert( const Point3& p3, const Payload& payload ); + KDTree( const KDTree& handle ) : Handle( handle ) {} - /// @brief Build the kd-tree in one shot, if memory has been reserved. - /// This will need to be called before all search functions like kNearestNeighbours(). - void build(); + /// @brief Construct an empty kd-tree with default geometry (Earth) + KDTree() : Handle( new detail::KDTreeMemory() ) {} - /// @brief Find k nearest neighbour given a 2D lonlat point (lon,lat) - NodeList kNearestNeighbours( const Point2& p2, size_t k ) const; + /// @brief Construct an empty kd-tree with custom geometry + KDTree( const Geometry& geometry ) : Handle( new detail::KDTreeMemory( geometry ) ) {} - /// @brief Find k nearest neighbours given a 3D cartesian point (x,y,z) - NodeList kNearestNeighbours( const Point3& p3, size_t k ) const; + /// @brief Construct a shared kd-tree with default geometry (Earth) + template + KDTree( const std::shared_ptr& kdtree ) : + Handle( new detail::KDTree_eckit( kdtree ) ) {} - /// @brief Find nearest neighbour given a 2D lonlat point (lon,lat) - Node nearestNeighbour( const Point2& p2 ) const; + /// @brief Construct a shared kd-tree with custom geometry + template + KDTree( const std::shared_ptr& kdtree, const Geometry& geometry ) : + Handle( new detail::KDTree_eckit( kdtree, geometry ) ) {} - /// @brief Find nearest neighbour given a 3D cartesian point (x,y,z) - Node nearestNeighbour( const Point3& p3 ) const; + //-------------------------------------------------------------------------------------- + // Methods to build the KDTree - /// @brief Find all points in within a distance of given radius from a given point (lon,lat) - NodeList findInSphere( const Point2& p2, double radius ) const; + /// @brief Reserve memory for building the kd-tree in one shot (optional, at cost of extra memory) + void reserve( idx_t size ) { get()->reserve( size ); } - /// @brief Find all points in within a distance of given radius from a given point (x,y,z) - NodeList findInSphere( const Point3& p3, double radius ) const; + /// @brief Insert spherical point (lon,lat) or 3D cartesian point (x,y,z) + /// @warning If memory has been reserved with reserve(), insertion will be delayed until build() is called. + template + void insert( const Point& p, const Payload& payload ) { + get()->insert( p, payload ); + } - const eckit_KDTree& tree() const { return tree_; } + /// @brief Insert kd-tree value in tree + /// @warning If memory has been reserved with reserve(), insertion will be delayed until build() is called. + void insert( const Value& value ) { get()->insert( value ); } -private: - void assert_built() const; + /// @brief Build the kd-tree in one shot, if memory has been reserved. + /// This will need to be called before all search functions like closestPoints(). + /// @post The KDTree is ready to be used + void build() { get()->build(); } + + /// @brief Build with spherical points (lon,lat) where longitudes, latitudes, and payloads are separate containers. + /// Memory will be reserved with reserve() to match the size + /// @post The KDTree is ready to be used + template + void build( const Longitudes& longitudes, const Latitudes& latitudes, const Payloads& payloads ) { + get()->build( longitudes, latitudes, payloads ); + }; -private: - std::vector tmp_; - mutable eckit_KDTree tree_; // mutable because its member functions are non-const... -}; + /// @brief Build with spherical points (lon,lat) given separate iterator ranges for longitudes, latitudes, and payloads. + /// Memory will be reserved with reserve() to match the size + /// @post The KDTree is ready to be used + template + void build( const LongitudesIterator& longitudes_begin, const LongitudesIterator& longitudes_end, + const LatitudesIterator& latitudes_begin, const LatitudesIterator& latitudes_end, + const PayloadsIterator& payloads_begin, const PayloadsIterator& payloads_end ) { + get()->build( longitudes_begin, longitudes_end, latitudes_begin, latitudes_end, payloads_begin, payloads_end ); + } -//------------------------------------------------------------------------------------------------------ + /// @brief Build with templated points. Points can be either 2D (lon,lat) or 3D (x,y,z) + /// Memory will be reserved with reserve() to match the size + /// @post The KDTree is ready to be used + template + void build( const Points& points, const Payloads& payloads ) { + get()->build( points, payloads ); + }; -template -void KDTree::reserve( idx_t size ) { - tmp_.reserve( size ); -} - -template -void KDTree::insert( const Point2& p2, const Payload& payload ) { - Point3 p3; - util::Earth::convertSphericalToCartesian( p2, p3 ); - insert( p3, payload ); -} - -template -void KDTree::insert( const Point3& p3, const Payload& payload ) { - if ( tmp_.capacity() ) { - tmp_.emplace_back( p3, payload ); + /// @brief Build with spherical points (lon,lat) given separate iterator ranges for longitudes, latitudes, and payloads. + /// Memory will be reserved with reserve() to match the size + /// @post The KDTree is ready to be used + template + void build( const PointIterator& points_begin, const PointIterator& points_end, + const PayloadsIterator& payloads_begin, const PayloadsIterator& payloads_end ) { + get()->build( points_begin, points_end, payloads_begin, payloads_end ); } - else { - tree_.insert( {p3, payload} ); + + /// @brief Build with vector of Value + /// @post The KDTree is ready to be used + void build( const std::vector& values ) { get()->build( values ); } + + //-------------------------------------------------------------------------------------- + // Methods to access the KDTree + + /// @brief Find k closest points given a 3D cartesian point (x,y,z) or 2D lonlat point(lon,lat) + template + ValueList closestPoints( const Point& p, size_t k ) const { + return get()->closestPoints( p, k ); } -} - -template -void KDTree::build() { - if ( tmp_.size() ) { - tree_.build( tmp_.begin(), tmp_.end() ); - tmp_.clear(); - tmp_.shrink_to_fit(); + + /// @brief Find closest point given a 3D cartesian point (x,y,z) or 2D lonlat point(lon,lat) + template + Value closestPoint( const Point& p ) const { + return get()->closestPoint( p ); } -} - -template -typename KDTree::NodeList KDTree::kNearestNeighbours( const Point2& p2, size_t k ) const { - Point3 p3; - util::Earth::convertSphericalToCartesian( p2, p3 ); - return kNearestNeighbours( p3, k ); -} - -template -typename KDTree::NodeList KDTree::kNearestNeighbours( const Point3& p3, size_t k ) const { - assert_built(); - return tree_.kNearestNeighbours( p3, k ); -} - -template -typename KDTree::Node KDTree::nearestNeighbour( const Point2& p2 ) const { - Point3 p3; - util::Earth::convertSphericalToCartesian( p2, p3 ); - return nearestNeighbour( p3 ); -} - -template -typename KDTree::Node KDTree::nearestNeighbour( const Point3& p3 ) const { - assert_built(); - return tree_.nearestNeighbour( p3 ); -} - -template -typename KDTree::NodeList KDTree::findInSphere( const Point2& p2, double radius ) const { - Point3 p3; - util::Earth::convertSphericalToCartesian( p2, p3 ); - return findInSphere( p3, radius ); -} - -template -typename KDTree::NodeList KDTree::findInSphere( const Point3& p3, double radius ) const { - assert_built(); - return tree_.findInSphere( p3, radius ); -} - -template -void KDTree::assert_built() const { - if ( tmp_.capacity() ) { - throw_AssertionFailed( "KDTree was used before calling build()" ); + + /// @brief Find all points within a distance of given radius from a given point 3D cartesian point (x,y,z) + /// or a 2D (lon,lat) point + template + ValueList closestPointsWithinRadius( const Point& p, double radius ) const { + return get()->closestPointsWithinRadius( p, radius ); } -} + + /// @brief Return geometry used to convert (lon,lat) to (x,y,z) coordinates + const Geometry& geometry() const { return get()->geometry(); } +}; + +//------------------------------------------------------------------------------------------------------ + +using IndexKDTree2D = KDTree; // 2D search: implementation is using 2D points only +using IndexKDTree3D = KDTree; // 3D search: lonlat (2D) to xyz (3D) conversion is done internally +using IndexKDTree = IndexKDTree3D; //------------------------------------------------------------------------------------------------------ diff --git a/src/atlas/util/Point.h b/src/atlas/util/Point.h index 1c5b8c0e3..e4a5f988f 100644 --- a/src/atlas/util/Point.h +++ b/src/atlas/util/Point.h @@ -72,6 +72,7 @@ class PointXYZ : public eckit::geometry::Point3 { public: using Point3::Point3; + using Point3::x; PointXYZ() : Point3() {} diff --git a/src/atlas/util/PolygonLocator.h b/src/atlas/util/PolygonLocator.h index 0f21c2e70..ead76a7ff 100644 --- a/src/atlas/util/PolygonLocator.h +++ b/src/atlas/util/PolygonLocator.h @@ -75,7 +75,7 @@ class PolygonLocator { /// @brief find the polygon that holds the point (lon,lat) idx_t operator()( const Point2& point ) const { - const auto found = kdtree_.kNearestNeighbours( point, k_ ); + const auto found = kdtree_.closestPoints( point, k_ ); idx_t partition{-1}; for ( size_t i = 0; i < found.size(); ++i ) { idx_t ii = found[i].payload(); @@ -135,7 +135,7 @@ class PolygonLocator { const PolygonCoordinates::Vector& polygons_; Projection projection_; idx_t k_{4}; - KDTree kdtree_; + IndexKDTree kdtree_; }; //------------------------------------------------------------------------------------------------------ diff --git a/src/atlas/util/detail/KDTree.h b/src/atlas/util/detail/KDTree.h new file mode 100644 index 000000000..ee4d0d85e --- /dev/null +++ b/src/atlas/util/detail/KDTree.h @@ -0,0 +1,413 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#pragma once + +#include +#include + +#include "eckit/container/KDTree.h" + +#include "atlas/library/config.h" +#include "atlas/runtime/Exception.h" +#include "atlas/runtime/Log.h" +#include "atlas/util/Geometry.h" +#include "atlas/util/Object.h" +#include "atlas/util/Point.h" + +namespace atlas { +namespace util { +namespace detail { + +//------------------------------------------------------------------------------------------------------ + +// Abstract KDTree, intended to erase the internal KDTree type from eckit (Mapped or Memory) +// For usage, see atlas::util::KDTree +template +class KDTreeBase : public Object { +#define ENABLE_IF_3D_AND_IS_LONLAT( POINT ) \ + typename = typename std::enable_if < PointT::DIMS == 3 && POINT::DIMS == 2 > ::type + +private: + Geometry geometry_; + +public: + using Payload = PayloadT; + using Point = PointT; + using PayloadList = std::vector; + + struct KDTreeTraits { + using Point = PointT; + using Payload = PayloadT; + }; + + struct Value { + using Point = typename KDTreeTraits::Point; + using Payload = typename KDTreeTraits::Payload; + + template + Value( const Node& node ) : Value( node.point(), node.payload(), node.distance() ) {} + + Value( const Point& point, const Payload& payload ) : point_( point ), payload_( payload ) {} + + Value( const Point& point, const Payload& payload, double distance ) : + point_( point ), payload_( payload ), distance_( distance ) {} + + const Point& point() const { return point_; } + const Payload& payload() const { return payload_; } + const double& distance() const { return distance_; } + + private: + Point point_; + Payload payload_; + double distance_; + }; + + class ValueList : public std::vector { + public: + using std::vector::vector; + PayloadList payloads() const { + PayloadList list; + list.reserve( this->size() ); + for ( auto& item : *this ) { + list.emplace_back( item.payload() ); + } + return list; + } + + template + ValueList( const NodeList& _list ) { + this->reserve( _list.size() ); + for ( const auto& item : _list ) { + this->emplace_back( item ); + } + } + + void print( std::ostream& out ) const { + out << "["; + for ( size_t i = 0; i < this->size(); ++i ) { + const auto& value = this->at( i ); + out << "{payload:" << value.payload() << ",distance:" << value.distance() << ",point:" << value.point() + << "}"; + if ( i < this->size() - 1 ) { + out << ","; + } + } + out << "]"; + } + friend std::ostream& operator<<( std::ostream& out, ValueList& This ) { + This.print( out ); + return out; + } + }; + + +public: + KDTreeBase() = default; + + KDTreeBase( const Geometry& geometry ) : geometry_( geometry ) {} + + virtual ~KDTreeBase() = default; + + const Geometry& geometry() const { return geometry_; } + + /// @brief Reserve memory for building the kdtree in one shot (optional, at cost of extra memory) + /// Implementation depends in derived classes + virtual void reserve( idx_t /*size*/ ){}; + + /// @brief Insert spherical point (lon,lat) or 3D cartesian point (x,y,z) + /// If memory has been reserved with reserve(), insertion will be delayed until build() is called. + template + void insert( const Point& p, const Payload& payload ) { + do_insert( p, payload ); + } + + /// @brief Insert Value + /// If memory has been reserved with reserve(), insertion will be delayed until build() is called. + virtual void insert( const Value& ) = 0; + + /// @brief Build the kd-tree in one shot, if memory has been reserved, depending on derived class implementation + /// This will need to be called before all search functions like closestPoints(). + virtual void build() {} + + /// @brief Build the kd-tree in one shot + virtual void build( std::vector& values ) = 0; + + /// @brief Build with spherical points (lon,lat) where longitudes, latitudes, and payloads are separate containers. + /// Memory will be reserved with reserve() to match the size + template + void build( const Longitudes& longitudes, const Latitudes& latitudes, const Payloads& payloads ) { + build( longitudes.begin(), longitudes.end(), latitudes.begin(), latitudes.end(), payloads.begin(), + payloads.end() ); + }; + + /// @brief Build with spherical points (lon,lat) given separate iterator ranges for longitudes, latitudes, and payloads. + /// Memory will be reserved with reserve() to match the size + template + void build( const LongitudesIterator& longitudes_begin, const LongitudesIterator& longitudes_end, + const LatitudesIterator& latitudes_begin, const LatitudesIterator& latitudes_end, + const PayloadsIterator& payloads_begin, const PayloadsIterator& payloads_end ) { + reserve( std::distance( payloads_begin, payloads_end ) ); + auto lon = longitudes_begin; + auto lat = latitudes_begin; + auto payload = payloads_begin; + for ( ; lon != longitudes_end && lat != latitudes_end && payload != payloads_end; lon++, lat++, payload++ ) { + insert( Point2{*lon, *lat}, *payload ); + } + ATLAS_ASSERT( lon == longitudes_end ); + ATLAS_ASSERT( lat == latitudes_end ); + ATLAS_ASSERT( payload == payloads_end ); + build(); + } + + /// @brief Build with spherical points (lon,lat) where longitudes, latitudes, and payloads are separate containers. + /// Memory will be reserved with reserve() to match the size + template + void build( const Points& points, const Payloads& payloads ) { + build( points.begin(), points.end(), payloads.begin(), payloads.end() ); + }; + + /// @brief Build with spherical points (lon,lat) given separate iterator ranges for longitudes, latitudes, and payloads. + /// Memory will be reserved with reserve() to match the size + template + void build( const PointIterator& points_begin, const PointIterator& points_end, + const PayloadsIterator& payloads_begin, const PayloadsIterator& payloads_end ) { + reserve( std::distance( points_begin, points_end ) ); + PointIterator point = points_begin; + PayloadsIterator payload = payloads_begin; + for ( ; point != points_end && payload != payloads_end; ++point, ++payload ) { + insert( *point, *payload ); + } + ATLAS_ASSERT( point == points_end ); + ATLAS_ASSERT( payload == payloads_end ); + build(); + } + + + /// @brief Find k nearest neighbours given a 3D cartesian point (x,y,z) or 2D lonlat point(lon,lat) + template + ValueList closestPoints( const Point& p, size_t k ) const { + return do_closestPoints( p, k ); + } + + /// @brief Find nearest neighbour given a 3D cartesian point (x,y,z) + template + Value closestPoint( const Point& p ) const { + return do_closestPoint( p ); + } + + /// @brief Find all points within a distance of given radius from a given point (x,y,z) + template + ValueList closestPointsWithinRadius( const Point& p, double radius ) const { + return do_closestPointsWithinRadius( p, radius ); + } + +private: + /// @brief Insert spherical point (lon,lat) + /// If memory has been reserved with reserve(), insertion will be delayed until build() is called. + void do_insert( const Point& p, const Payload& payload ) { insert( Value{p, payload} ); } + + + /// @brief Insert spherical point (lon,lat) + /// If memory has been reserved with reserve(), insertion will be delayed until build() is called. + template + void do_insert( const LonLat& p, const Payload& payload ) { + do_insert( make_Point( p ), payload ); + } + + /// @brief Find k nearest neighbours given a 3D cartesian point (x,y,z) + virtual ValueList do_closestPoints( const Point&, size_t k ) const = 0; + + /// @brief Find nearest neighbour given a 3D cartesian point (x,y,z) + virtual Value do_closestPoint( const Point& ) const = 0; + + /// @brief Find all points within a distance of given radius from a given point (x,y,z) + virtual ValueList do_closestPointsWithinRadius( const Point&, double radius ) const = 0; + + + /// @brief Find k nearest neighbour given a 2D lonlat point (lon,lat) + template + ValueList do_closestPoints( const LonLat& p, size_t k ) const { + return do_closestPoints( make_Point( p ), k ); + } + + /// @brief Find nearest neighbour given a 2D lonlat point (lon,lat) + template + Value do_closestPoint( const LonLat& p ) const { + return do_closestPoint( make_Point( p ) ); + } + + /// @brief Find all points within a distance of given radius from a given point (lon,lat) + template + ValueList do_closestPointsWithinRadius( const LonLat& p, double radius ) const { + return do_closestPointsWithinRadius( make_Point( p ), radius ); + } + + template + Point make_Point( const LonLat& lonlat ) const { + static_assert( std::is_base_of::value, "LonLat must be derived from Point2" ); + static_assert( std::is_base_of::value, "Point must be derived from Point3" ); + Point xyz; + geometry().lonlat2xyz( lonlat, xyz ); + return xyz; + } +#undef ENABLE_IF_3D_AND_IS_LONLAT +}; + +//------------------------------------------------------------------------------------------------------ +// Concrete implementation + +template +class KDTree_eckit : public KDTreeBase { + using Tree = TreeT; + using Base = KDTreeBase; + +public: + using Point = typename Base::Point; + using Payload = typename Base::Payload; + using PayloadList = typename Base::PayloadList; + using Value = typename Base::Value; + using ValueList = typename Base::ValueList; + + using Base::build; + using Base::closestPoint; + using Base::closestPoints; + using Base::closestPointsWithinRadius; + using Base::insert; + using Base::reserve; + +public: + template + KDTree_eckit( const Geometry& geometry, Args... args ) : + KDTreeBase( geometry ), tree_( new Tree( args... ) ) { + static_asserts(); + } + + + template + KDTree_eckit( Args... args ) : tree_( new Tree( args... ) ) { + static_asserts(); + } + + KDTree_eckit( const std::shared_ptr& tree ) : tree_( tree ) { static_asserts(); } + + KDTree_eckit( const std::shared_ptr& tree, const Geometry& geometry ) : + KDTreeBase( geometry ), tree_( tree ) { + static_asserts(); + } + + + void reserve( idx_t size ) override; + + void build() override; + + void build( std::vector& ) override; + + /// @brief Insert 3D cartesian point (x,y,z) + /// If memory has been reserved with reserve(), insertion will be delayed until build() is called. + void insert( const Value& value ) override; + + /// @brief Find k nearest neighbours given a 3D cartesian point (x,y,z) + ValueList do_closestPoints( const Point&, size_t k ) const override; + + /// @brief Find nearest neighbour given a 3D cartesian point (x,y,z) + Value do_closestPoint( const Point& ) const override; + + /// @brief Find all points within a distance of given radius from a given point (x,y,z) + ValueList do_closestPointsWithinRadius( const Point&, double radius ) const override; + + const Tree& tree() const { return *tree_; } + +private: + void assert_built() const; + + static void static_asserts() { + static_assert( std::is_convertible::value, + "Tree::Payload must be convertible this Payload type" ); + static_assert( std::is_convertible::value, + "Tree::Point must be convertible to this Point type" ); + } + +private: + std::vector tmp_; + mutable std::shared_ptr tree_; // mutable because its member functions are non-const... +}; + +//------------------------------------------------------------------------------------------------------ + +template +using KDTreeMemory = KDTree_eckit::KDTreeTraits>>; + +//------------------------------------------------------------------------------------------------------ + +template +void KDTree_eckit::reserve( idx_t size ) { + tmp_.reserve( size ); +} + + +template +void KDTree_eckit::insert( const Value& value ) { + if ( tmp_.capacity() ) { + tmp_.emplace_back( value ); + } + else { + tree_->insert( value ); + } +} + +template +void KDTree_eckit::build() { + if ( tmp_.size() ) { + build( tmp_ ); + tmp_.clear(); + tmp_.shrink_to_fit(); + } +} + +template +void KDTree_eckit::build( std::vector& values ) { + tree_->build( values ); +} + + +template +typename KDTree_eckit::ValueList KDTree_eckit::do_closestPoints( + const Point& p, size_t k ) const { + assert_built(); + return tree_->kNearestNeighbours( p, k ); +} + +template +typename KDTree_eckit::Value KDTree_eckit::do_closestPoint( + const Point& p ) const { + assert_built(); + return tree_->nearestNeighbour( p ); +} + +template +typename KDTree_eckit::ValueList +KDTree_eckit::do_closestPointsWithinRadius( const Point& p, double radius ) const { + assert_built(); + return tree_->findInSphere( p, radius ); +} + +template +void KDTree_eckit::assert_built() const { + if ( tmp_.capacity() ) { + throw_AssertionFailed( "KDTree was used before calling build()" ); + } +} + +//------------------------------------------------------------------------------------------------------ + +} // namespace detail +} // namespace util +} // namespace atlas diff --git a/src/tests/AtlasTestEnvironment.h b/src/tests/AtlasTestEnvironment.h index 96fdb3811..d8f0a7834 100644 --- a/src/tests/AtlasTestEnvironment.h +++ b/src/tests/AtlasTestEnvironment.h @@ -84,15 +84,16 @@ using eckit::types::is_approximately_equal; #ifdef EXPECT_EQ #undef EXPECT_EQ #endif -#define EXPECT_EQ( lhs, rhs ) \ - do { \ - if ( !( lhs == rhs ) ) { \ - throw eckit::testing::TestException( "EXPECT condition failed: " #lhs " == " #rhs \ - "\n" \ - " --> " + \ - std::to_string( lhs ) + " != " + std::to_string( rhs ), \ - Here() ); \ - } \ +#define EXPECT_EQ( lhs, rhs ) \ + do { \ + if ( !( lhs == rhs ) ) { \ + using namespace std; \ + throw eckit::testing::TestException( "EXPECT condition failed: " #lhs " == " #rhs \ + "\n" \ + " --> " + \ + to_string( lhs ) + " != " + to_string( rhs ), \ + Here() ); \ + } \ } while ( false ) //---------------------------------------------------------------------------------------------------------------------- diff --git a/src/tests/util/test_kdtree.cc b/src/tests/util/test_kdtree.cc index 1cc1eacb2..215683518 100644 --- a/src/tests/util/test_kdtree.cc +++ b/src/tests/util/test_kdtree.cc @@ -8,8 +8,10 @@ * nor does it submit to any jurisdiction. */ +#include #include #include +#include #include #include "atlas/grid.h" @@ -17,92 +19,286 @@ #include "tests/AtlasTestEnvironment.h" -using atlas::util::KDTree; +using namespace atlas::util; namespace atlas { namespace test { +//------------------------------------------------------------------------------------------------ +namespace { // helpers + +template +std::string to_string( const std::vector& vector ) { + std::stringstream s; + s << vector; + return s.str(); +} +template +std::string to_string( const T& v ) { + return std::to_string( v ); +} + +class PayloadGenerator { +public: + class iterator { + friend class PayloadGenerator; + + public: + long int operator*() const { return i_; } + const iterator& operator++() { + ++i_; + return *this; + } + iterator operator++( int ) { + iterator copy( *this ); + ++i_; + return copy; + } + + bool operator==( const iterator& other ) const { return i_ == other.i_; } + bool operator!=( const iterator& other ) const { return i_ != other.i_; } + + protected: + iterator( long int start ) : i_( start ) {} + + private: + unsigned long i_; + }; + + iterator begin() const { return begin_; } + iterator end() const { return end_; } + PayloadGenerator( long int end ) : begin_( 0 ), end_( end ) {} + + template + void fill( Container& container ) { + std::iota( container.begin(), container.end(), Value( *begin_ ) ); + } + + template + std::array make_array() { + std::array array; + fill( array ); + return array; + } + +private: + iterator begin_; + iterator end_; +}; + +static double radius() { + return util::Earth::radius(); +} + +static Geometry& geometry() { + static Sphere _geometry( radius() ); + return _geometry; +} + +static PointXYZ make_xyz( const PointLonLat& lonlat ) { + return geometry().xyz( lonlat ); +} + +static std::array& test_lon() { + static std::array lon = {0., 30., 60., 90., 120., 150., 180.}; + return lon; +} + +static std::array& test_lat() { + static std::array lat = {90., 60., 30., 0., -30., -60., -90.}; + return lat; +} + +static std::array& test_lonlat() { + static std::array lonlat{PointLonLat{0., 90.}, PointLonLat{30., 60.}, PointLonLat{60., 30.}, + PointLonLat{90., 0.}, PointLonLat{120., -30.}, PointLonLat{150., -60.}, + PointLonLat{180., -90.}}; + return lonlat; +} + +static std::array& test_xyz() { + static std::array xyz{make_xyz( PointLonLat{0., 90.} ), make_xyz( PointLonLat{30., 60.} ), + make_xyz( PointLonLat{60., 30.} ), make_xyz( PointLonLat{90., 0.} ), + make_xyz( PointLonLat{120., -30.} ), make_xyz( PointLonLat{150., -60.} ), + make_xyz( PointLonLat{180., -90.} )}; + return xyz; +} + +static std::array& test_payloads() { + static auto payloads = PayloadGenerator( 7 ).make_array(); + EXPECT_EQ( std::distance( payloads.begin(), payloads.end() ), 7 ); + return payloads; +} + +void validate( const KDTree& tree ) { + EXPECT_NO_THROW( tree.closestPoint( PointLonLat{180., 45.} ) ); + // Search 4 nearest neighbours (k=4), sorted by shortest distance + auto neighbours = tree.closestPoints( PointLonLat{89.9, 44.9}, 4 ); + auto expected_payloads = std::vector{2, 1, 3, 0}; + EXPECT_EQ( neighbours.payloads(), expected_payloads ); +} + +static const IndexKDTree& search() { + static IndexKDTree kdtree = []() { + IndexKDTree kdtree( geometry() ); + auto grid = Grid{"O32"}; + kdtree.build( grid.lonlat(), PayloadGenerator( grid.size() ) ); + return kdtree; + }(); + return kdtree; +} + +} // namespace +//------------------------------------------------------------------------------------------------ + + CASE( "test kdtree" ) { auto grid = Grid{"O32"}; - KDTree search; + IndexKDTree search( geometry() ); search.reserve( grid.size() ); idx_t n{0}; for ( auto& point : grid.lonlat() ) { search.insert( point, n++ ); } search.build(); - EXPECT_NO_THROW( search.nearestNeighbour( PointLonLat{180., 45.} ) ); + EXPECT_NO_THROW( search.closestPoint( PointLonLat{180., 45.} ) ); // ... // Search 4 nearest neighbours (k=4), sorted by shortest distance - auto neighbours = search.kNearestNeighbours( PointLonLat{180., 45.}, 4 ).payloads(); + auto neighbours = search.closestPoints( PointLonLat{180., 45.}, 4 ).payloads(); auto expected_neighbours = std::vector{760, 842, 759, 761}; - EXPECT( neighbours == expected_neighbours ); + EXPECT_EQ( neighbours, expected_neighbours ); } CASE( "test assertion" ) { auto grid = Grid{"O32"}; - KDTree search; + IndexKDTree search( geometry() ); search.reserve( grid.size() ); idx_t n{0}; for ( auto& point : grid.lonlat() ) { search.insert( point, n++ ); } // Forgot to call search.build() --> assertion thrown when trying to access - EXPECT_THROWS_AS( search.nearestNeighbour( PointLonLat{180., 45.} ), eckit::AssertionFailed ); + EXPECT_THROWS_AS( search.closestPoint( PointLonLat{180., 45.} ), eckit::AssertionFailed ); } CASE( "test no assertion" ) { // Like case "test assertion", but without reserving size auto grid = Grid{"O32"}; - KDTree search; + IndexKDTree search( geometry() ); // No search.reserve() --> build() will not be necessary. idx_t n{0}; for ( auto& point : grid.lonlat() ) { search.insert( point, n++ ); } // search.build() Not required - EXPECT_NO_THROW( search.nearestNeighbour( PointLonLat{180., 45.} ) ); + EXPECT_NO_THROW( search.closestPoint( PointLonLat{180., 45.} ) ); } -const KDTree& search() { - static KDTree* kdtree = []() { - KDTree* kdtree = new KDTree; - auto grid = Grid{"O32"}; - kdtree->reserve( grid.size() ); - idx_t n{0}; - for ( auto& point : grid.lonlat() ) { - kdtree->insert( point, n++ ); - } - kdtree->build(); - return kdtree; - }(); - EXPECT( kdtree ); - return *kdtree; +CASE( "test kdtree building with separate lon and lat and payload arrays" ) { + IndexKDTree search( geometry() ); + search.build( test_lon(), test_lat(), test_payloads() ); + validate( search ); +} + +CASE( "test kdtree building with separate lon and lat and raw payload iterators" ) { + IndexKDTree search( geometry() ); + auto lon = test_lon(); + auto lat = test_lat(); + auto payloads_ = test_payloads(); + search.build( lon.begin(), lon.end(), lat.begin(), lat.end(), payloads_.begin(), payloads_.end() ); + validate( search ); +} + +CASE( "test kdtree building with separate PointLonLat and payload containers" ) { + IndexKDTree search( geometry() ); + search.build( test_lonlat(), test_payloads() ); + validate( search ); } -CASE( "test nearestNeighbour" ) { - auto neighbour = search().nearestNeighbour( PointLonLat{180., 45.} ).payload(); +CASE( "test kdtree building with separate PointXYZ and payload containers" ) { + IndexKDTree search( geometry() ); + search.build( test_xyz(), test_payloads() ); + validate( search ); +} + +CASE( "test assignment" ) { + IndexKDTree search; + search = IndexKDTree(); + search.build( test_lonlat(), test_payloads() ); + validate( search ); +} + +CASE( "test closestPoint" ) { + auto neighbour = search().closestPoint( PointLonLat{180., 45.} ).payload(); auto expected_neighbour = 760; - EXPECT( neighbour == expected_neighbour ); + EXPECT_EQ( neighbour, expected_neighbour ); } -CASE( "test kNearestNeighbours" ) { - auto neighbours = search().kNearestNeighbours( PointLonLat{180., 45.}, 4 ).payloads(); +CASE( "test closestPoints" ) { + auto neighbours = search().closestPoints( PointLonLat{180., 45.}, 4 ).payloads(); auto expected_neighbours = std::vector{760, 842, 759, 761}; - EXPECT( neighbours == expected_neighbours ); + EXPECT_EQ( neighbours, expected_neighbours ); } -CASE( "test findInSphere" ) { - constexpr double km = 1000.; - auto neighbours = search().findInSphere( PointLonLat{180., 45.}, 500 * km ).payloads(); +CASE( "test closestPointsWithinRadius" ) { + double km = 1000. * radius() / util::Earth::radius(); + auto neighbours = search().closestPointsWithinRadius( PointLonLat{180., 45.}, 500 * km ).payloads(); auto expected_neighbours = std::vector{760, 842, 759, 761, 841, 843, 682}; - EXPECT( neighbours == expected_neighbours ); - Log::info() << neighbours << std::endl; + EXPECT_EQ( neighbours, expected_neighbours ); +} + +CASE( "test compatibility with external eckit KDTree" ) { + // External world + struct ExternalKDTreeTraits { + using Point = Point3; + using Payload = size_t; + }; + using ExternalKDTree = typename eckit::KDTreeMemory; + auto external_kdtree = std::make_shared(); + + // Atlas world + IndexKDTree search( external_kdtree, geometry() ); + + // Construction of tree; could happen in Atlas world or External world separately + auto grid = Grid{"O32"}; + search.build( grid.lonlat(), PayloadGenerator( grid.size() ) ); + + // Usage in External world + { + auto neighbours = external_kdtree->kNearestNeighbours( make_xyz( {180., 45.} ), 4 ); + auto payloads = std::vector{}; + for ( auto& neighbour : neighbours ) { + payloads.push_back( neighbour.payload() ); + } + auto expected_payloads = std::vector{760, 842, 759, 761}; + EXPECT_EQ( payloads, expected_payloads ); + } + + // Usage in Atlas world + { + auto payloads = search.closestPoints( PointLonLat{180., 45.}, 4 ).payloads(); + auto expected_payloads = std::vector{760, 842, 759, 761}; + EXPECT_EQ( payloads, expected_payloads ); + } +} + +CASE( "test IndexKDTree 2D vs 3D" ) { + IndexKDTree2D search2d( geometry() ); + IndexKDTree3D search3d( geometry() ); + search2d.build( test_lonlat(), test_payloads() ); + search3d.build( test_lonlat(), test_payloads() ); + auto payloads2d = search2d.closestPoints( PointLonLat{89.9, 44.9}, 4 ).payloads(); + auto payloads3d = search3d.closestPoints( PointLonLat{89.9, 44.9}, 4 ).payloads(); + EXPECT_EQ( payloads2d, ( std::vector{2, 3, 1, 4} ) ); + EXPECT_EQ( payloads3d, ( std::vector{2, 1, 3, 0} ) ); + // Note that the expected values are different whether 2D search or 3D search is used } +//------------------------------------------------------------------------------------------------ + } // namespace test } // namespace atlas From 20fdda774e5b214bf4231f145c6f169fe7e3b497 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 28 Apr 2020 15:09:48 +0100 Subject: [PATCH 043/145] Geometry class refactoring --- src/atlas/util/Geometry.h | 103 +++++++++++++++++++++++----------- src/tests/util/test_kdtree.cc | 2 +- 2 files changed, 70 insertions(+), 35 deletions(-) diff --git a/src/atlas/util/Geometry.h b/src/atlas/util/Geometry.h index a2b81889f..3bd70b992 100644 --- a/src/atlas/util/Geometry.h +++ b/src/atlas/util/Geometry.h @@ -11,19 +11,31 @@ #pragma once #include +#include #include #include "atlas/runtime/Exception.h" #include "atlas/util/Earth.h" +#include "atlas/util/Object.h" +#include "atlas/util/ObjectHandle.h" #include "atlas/util/Point.h" namespace atlas { -namespace util { +namespace geometry { -struct GeometryBase { - virtual ~GeometryBase() = default; - virtual void lonlat2xyz( const Point2&, Point3& ) const = 0; - virtual void xyz2lonlat( const Point3&, Point2& ) const = 0; +//------------------------------------------------------------------------------------------------------ + +namespace detail { + +class GeometryBase : util::Object { +public: + virtual ~GeometryBase() = default; + virtual void lonlat2xyz( const Point2&, Point3& ) const = 0; + virtual void xyz2lonlat( const Point3&, Point2& ) const = 0; + virtual double distance( const Point2& p1, const Point2& p2 ) const = 0; + virtual double distance( const Point3& p1, const Point3& p2 ) const = 0; + virtual double radius() const = 0; + virtual double area() const = 0; Point3 xyz( const Point2& lonlat ) const { Point3 xyz; @@ -37,72 +49,95 @@ struct GeometryBase { } }; +//------------------------------------------------------------------------------------------------------ + template -struct GeometrySphereT : public GeometryBase { +class GeometrySphereT : public GeometryBase { +public: void lonlat2xyz( const Point2& lonlat, Point3& xyz ) const override { SphereT::convertSphericalToCartesian( lonlat, xyz ); } void xyz2lonlat( const Point3& xyz, Point2& lonlat ) const override { SphereT::convertCartesianToSpherical( xyz, lonlat ); } + double distance( const Point2& p1, const Point2& p2 ) const override { return SphereT::distance( p1, p2 ); } + double distance( const Point3& p1, const Point3& p2 ) const override { return SphereT::distance( p1, p2 ); } + double radius() const override { return SphereT::radius(); } + double area() const override { return SphereT::area(); } }; -struct GeometrySphere : public GeometryBase { +class GeometrySphere : public GeometryBase { + using Sphere = eckit::geometry::Sphere; + +public: GeometrySphere( double radius ) : radius_( radius ) {} void lonlat2xyz( const Point2& lonlat, Point3& xyz ) const override { - eckit::geometry::Sphere::convertSphericalToCartesian( radius_, lonlat, xyz ); + Sphere::convertSphericalToCartesian( radius_, lonlat, xyz ); } void xyz2lonlat( const Point3& xyz, Point2& lonlat ) const override { - eckit::geometry::Sphere::convertCartesianToSpherical( radius_, xyz, lonlat ); + Sphere::convertCartesianToSpherical( radius_, xyz, lonlat ); } + double distance( const Point2& p1, const Point2& p2 ) const override { return Sphere::distance( radius_, p1, p2 ); } + double distance( const Point3& p1, const Point3& p2 ) const override { return Sphere::distance( radius_, p1, p2 ); } + double radius() const override { return radius_; } + double area() const override { return Sphere::area( radius_ ); } + +private: double radius_; }; -struct Geometry { - Geometry() { build(); } +} // namespace detail +} // namespace geometry - Geometry( const std::string& name ) { build( name ); } +//------------------------------------------------------------------------------------------------------ - Geometry( const std::shared_ptr& conversion ) : - shared_impl_( conversion ), impl_( shared_impl_.get() ) {} +class Geometry : util::ObjectHandle { +public: + using Handle::Handle; - Geometry( const Geometry& other ) : shared_impl_( other.shared_impl_ ), impl_( shared_impl_.get() ) {} + Geometry() : Handle( build>() ) {} + Geometry( const std::string& name ) : Handle( build( name ) ) {} + Geometry( double radius ) : Handle( build( radius ) ) {} - Point3 xyz( const Point2& lonlat ) const { return impl_->xyz( lonlat ); } - Point2 lonlat( const Point3& xyz ) const { return impl_->lonlat( xyz ); } - void xyz2lonlat( const Point3& xyz, Point2& lonlat ) const { return impl_->xyz2lonlat( xyz, lonlat ); } - void lonlat2xyz( const Point2& lonlat, Point3& xyz ) const { return impl_->lonlat2xyz( lonlat, xyz ); } + template + Geometry( const SphereT& ) : Handle( build() ) {} -private: - std::shared_ptr shared_impl_; - GeometryBase* impl_; + Point3 xyz( const Point2& lonlat ) const { return get()->xyz( lonlat ); } + Point2 lonlat( const Point3& xyz ) const { return get()->lonlat( xyz ); } + void xyz2lonlat( const Point3& xyz, Point2& lonlat ) const { get()->xyz2lonlat( xyz, lonlat ); } + void lonlat2xyz( const Point2& lonlat, Point3& xyz ) const { get()->lonlat2xyz( lonlat, xyz ); } + double distance( const Point2& p1, const Point2& p2 ) const { return get()->distance( p1, p2 ); } + double distance( const Point3& p1, const Point3& p2 ) const { return get()->distance( p1, p2 ); } + double radius() const { return get()->radius(); } + double area() const { return get()->area(); } +protected: template - void build( Args... args ) { - shared_impl_ = std::make_shared( args... ); - impl_ = shared_impl_.get(); + static Implementation* build( Args... args ) { + return new GeometryT( args... ); } - void build( const std::string& name = "Earth" ) { + static Implementation* build( const std::string& name ) { // Factory without self registration if ( name == "Earth" ) { - build>(); + return build>(); } else if ( name == "UnitSphere" ) { - build>(); + return build>(); } else { - ATLAS_THROW_EXCEPTION( "name " + name + " is not a valid key for a Geometry" ); + ATLAS_THROW_EXCEPTION( "name " << name << " is not a valid key for a Geometry" ); } } }; -struct Sphere : public Geometry { - using Geometry::Geometry; - Sphere( double radius = Earth::radius() ) : Geometry( std::make_shared( radius ) ) {} -}; +//------------------------------------------------------------------------------------------------------ + +namespace geometry { +using Earth = Geometry; // Sphere with util::Earth radius by default +using UnitSphere = Geometry( eckit::geometry::UnitSphere() ); +} // namespace geometry //------------------------------------------------------------------------------------------------------ -} // namespace util } // namespace atlas diff --git a/src/tests/util/test_kdtree.cc b/src/tests/util/test_kdtree.cc index 215683518..99c35b98e 100644 --- a/src/tests/util/test_kdtree.cc +++ b/src/tests/util/test_kdtree.cc @@ -91,7 +91,7 @@ static double radius() { } static Geometry& geometry() { - static Sphere _geometry( radius() ); + static Geometry _geometry( radius() ); return _geometry; } From 66e40483fa0b2882f1e412e7c1df65aad672fc14 Mon Sep 17 00:00:00 2001 From: Philippe Marguinaud Date: Thu, 16 Apr 2020 11:32:10 +0000 Subject: [PATCH 044/145] ATLAS-275 (github #20) Fix rotation to support Arpege --- src/atlas/util/Rotation.cc | 8 +- src/tests/grid/CMakeLists.txt | 1 + .../grid/test_stretchedrotatedgaussian.cc | 918 ++++++++++++++++++ src/tests/projection/test_rotation.cc | 43 + 4 files changed, 966 insertions(+), 4 deletions(-) create mode 100644 src/tests/grid/test_stretchedrotatedgaussian.cc diff --git a/src/atlas/util/Rotation.cc b/src/atlas/util/Rotation.cc index 71ff2a7a5..0873230fb 100644 --- a/src/atlas/util/Rotation.cc +++ b/src/atlas/util/Rotation.cc @@ -172,6 +172,8 @@ void Rotation::rotate( double crd[] ) const { return; } + crd[LON] -= angle_; + if ( !rotation_angle_only_ ) { const PointLonLat L( wrap_latitude( {crd[LON], crd[LAT]} ) ); PointXYZ P; @@ -184,8 +186,6 @@ void Rotation::rotate( double crd[] ) const { crd[LON] = Lt.lon(); crd[LAT] = Lt.lat(); } - - crd[LON] -= angle_; } void Rotation::unrotate( double crd[] ) const { @@ -193,8 +193,6 @@ void Rotation::unrotate( double crd[] ) const { return; } - crd[LON] += angle_; - if ( !rotation_angle_only_ ) { const PointLonLat Lt( crd ); PointXYZ Pt; @@ -207,6 +205,8 @@ void Rotation::unrotate( double crd[] ) const { crd[LON] = L.lon(); crd[LAT] = L.lat(); } + + crd[LON] += angle_; } } // namespace util diff --git a/src/tests/grid/CMakeLists.txt b/src/tests/grid/CMakeLists.txt index a43eefd88..67d581535 100644 --- a/src/tests/grid/CMakeLists.txt +++ b/src/tests/grid/CMakeLists.txt @@ -22,6 +22,7 @@ foreach(test test_field test_grid_iterator test_grids + test_stretchedrotatedgaussian test_grid_cropping test_vertical test_state diff --git a/src/tests/grid/test_stretchedrotatedgaussian.cc b/src/tests/grid/test_stretchedrotatedgaussian.cc new file mode 100644 index 000000000..c35ea1ef4 --- /dev/null +++ b/src/tests/grid/test_stretchedrotatedgaussian.cc @@ -0,0 +1,918 @@ +#include +#include + +#include "atlas/array.h" +#include "atlas/functionspace.h" +#include "atlas/grid.h" +#include "atlas/library/Library.h" +#include "atlas/parallel/mpi/mpi.h" +#include "atlas/util/Config.h" +#include "atlas/util/UnitSphere.h" +#include "eckit/types/FloatCompare.h" +#include "tests/AtlasTestEnvironment.h" + +using namespace atlas::util; +using namespace atlas::grid; +using namespace atlas; + + +namespace { + +const double lonlat_arp_t32c24[] = { + 2.00000000000000000, 48.46708828700425187, 1.17799068975270704, 48.37772379915030996, + 0.44418972240584109, 48.11926090196744354, -0.12512266986032569, 47.71926346185548340, + -0.47479391972813606, 47.21956618120344018, -0.57569351104372468, 46.67109320702403608, + -0.42528132740345748, 46.12813070368466839, -0.04494770792091035, 45.64287023699112211, + 0.52444137510688571, 45.26073815098433784, 1.22762737065264282, 45.01674523281888440, + 2.00000000000000000, 44.93291171299576092, 2.77237262934735629, 45.01674523281888440, + 3.47555862489311407, 45.26073815098433784, 4.04494770792090996, 45.64287023699112211, + 4.42528132740345725, 46.12813070368466839, 4.57569351104372490, 46.67109320702403608, + 4.47479391972813634, 47.21956618120344018, 4.12512266986032561, 47.71926346185548340, + 3.55581027759415846, 48.11926090196744354, 2.82200931024729318, 48.37772379915030996, + 2.00000000000000000, 50.76274729016172671, 0.52357145823104279, 50.64425941434981837, + -0.85309402263029532, 50.29690558565773273, -2.04123891610972796, 49.74407157963349846, + -2.97152383528954323, 49.02183412771946536, -3.59837298636155323, 48.17535393470686955, + -3.90029306374494222, 47.25496955383284359, -3.87730059701688790, 46.31256836954069911, + -3.54692456688646551, 45.39855083314470363, -2.93998927128814502, 44.55946699558614910, + -2.09688983996385936, 43.83625800397906147, -1.06462721424400342, 43.26298044031561574, + 0.10541754825032876, 42.86589673132546352, 1.35912400589531490, 42.66284629701862485, + 2.64087599410468910, 42.66284629701862485, 3.89458245174967033, 42.86589673132546352, + 5.06462721424400453, 43.26298044031561574, 6.09688983996385936, 43.83625800397906147, + 6.93998927128814458, 44.55946699558614910, 7.54692456688646551, 45.39855083314470363, + 7.87730059701688656, 46.31256836954069911, 7.90029306374494134, 47.25496955383284359, + 7.59837298636155190, 48.17535393470686955, 6.97152383528954367, 49.02183412771946536, + 6.04123891610972930, 49.74407157963349846, 4.85309402263029188, 50.29690558565773273, + 3.47642854176895577, 50.64425941434981837, 2.00000000000000089, 53.08763788054714894, + -0.06435502534210064, 52.94800110445672203, -2.01378910799337474, 52.53707313311167582, + -3.74622775989939782, 51.87782553848720113, -5.18197003975669812, 51.00557820511744467, + -6.26810804950859701, 49.96440199105025926, -6.97806564551988018, 48.80336562297891589, + -7.30784824781774223, 47.57322688923063225, -7.27090590027329053, 46.32385288536406875, + -6.89307303713296715, 45.10238649522262477, -6.20837670733706215, 43.95202523375940729, + -5.25594876339865014, 42.91123363953519743, -4.07794770476515911, 42.01322983391511201, + -2.71826031811739588, 41.28563184510834105, -1.22173788476928480, 40.75019560382368411, + 0.36623707967922525, 40.42261301497919845, 1.99999999999999933, 40.31236211945285675, + 3.63376292032077286, 40.42261301497919845, 5.22173788476928369, 40.75019560382368411, + 6.71826031811739455, 41.28563184510834105, 8.07794770476515822, 42.01322983391511201, + 9.25594876339864925, 42.91123363953519743, 10.20837670733705949, 43.95202523375940729, + 10.89307303713296804, 45.10238649522262477, 11.27090590027328965, 46.32385288536406875, + 11.30784824781774311, 47.57322688923063225, 10.97806564551988195, 48.80336562297891589, + 10.26810804950859790, 49.96440199105025926, 9.18197003975669901, 51.00557820511744467, + 7.74622775989939960, 51.87782553848720113, 6.01378910799337607, 52.53707313311167582, + 4.06435502534210169, 52.94800110445672203, 2.00000000000000089, 55.44054043583248159, + -0.39391037845720578, 55.31115535850048559, -2.68766853851381882, 54.92865613654313961, + -4.79116029542177202, 54.30937174533021050, -6.63193584333677855, 53.47856301011842106, + -8.15903975007321769, 52.46802323745790630, -9.34306219743110411, 51.31353838046217675, + -10.17346129570704782, 50.05261748260089405, -10.65454278958191026, 48.72271255422421632, + -10.80126342211737800, 47.35996933207513848, -10.63557388477568999, 45.99843704779454612, + -10.18359521918176824, 44.66961909752426152, -9.47363915243209931, 43.40224634648851065, + -8.53494002107633598, 42.22217718966415845, -7.39692139773956647, 41.15235690381517486, + -6.08882962579569487, 40.21279467492543347, -4.63959771096107954, 39.42053684981195261, + -3.07783882564465205, 38.78962916423042628, -1.43190111561111988, 38.33106975566338548, + 0.27005804914242593, 38.05275970261258323, 1.99999999999999956, 37.95945956416753120, + 3.72994195085757241, 38.05275970261258323, 5.43190111561111788, 38.33106975566338548, + 7.07783882564465028, 38.78962916423042628, 8.63959771096107865, 39.42053684981195261, + 10.08882962579569309, 40.21279467492543347, 11.39692139773956647, 41.15235690381517486, + 12.53494002107633243, 42.22217718966415845, 13.47363915243209576, 43.40224634648851065, + 14.18359521918176824, 44.66961909752426152, 14.63557388477568999, 45.99843704779454612, + 14.80126342211737445, 47.35996933207513848, 14.65454278958191381, 48.72271255422421632, + 14.17346129570704782, 50.05261748260089405, 13.34306219743110766, 51.31353838046217675, + 12.15903975007321769, 52.46802323745790630, 10.63193584333677855, 53.47856301011842106, + 8.79116029542177380, 54.30937174533021050, 6.68766853851382059, 54.92865613654313961, + 4.39391037845720867, 55.31115535850048559, 2.00000000000000222, 57.82915094012270174, + -0.88089731877775757, 57.69078537163564846, -3.64890062321652664, 57.28147795233979167, + -6.20331014929099656, 56.61787273415653488, -8.46463908845755597, 55.72551502002679058, + -10.37873026754043693, 54.63620962052475960, -11.91616209561276740, 53.38530347804498888, + -13.06845153476416321, 52.00935252683229493, -13.84288249522568925, 50.54439040009851425, + -14.25738892575702366, 49.02480894996942595, -14.33628744864260440, 47.48274104170999976, + -14.10711920596712332, 45.94779768044404733, -13.59853603019004176, 44.44702279967973624, + -12.83902541924944174, 43.00496062724707258, -11.85624296375186937, 41.64376435793402464, + -10.67674963753560036, 40.38330297070848474, -9.32599718132065902, 39.24124360712294646, + -7.82844927012504055, 38.23310085983992224, -6.20776260081896769, 37.37225322309888043, + -4.48697963071903061, 36.66993230518373537, -2.68870484500073204, 36.13519327177002793, + -0.83525097543211291, 35.77487607444932394, 1.05124791602986001, 35.59356675116022473, + 2.94875208397013866, 35.59356675116022473, 4.83525097543211047, 35.77487607444932394, + 6.68870484500073026, 36.13519327177002793, 8.48697963071903061, 36.66993230518373537, + 10.20776260081896680, 37.37225322309888043, 11.82844927012503966, 38.23310085983992224, + 13.32599718132065725, 39.24124360712293225, 14.67674963753560391, 40.38330297070848474, + 15.85624296375187114, 41.64376435793402464, 16.83902541924944174, 43.00496062724707258, + 17.59853603019003998, 44.44702279967973624, 18.10711920596712687, 45.94779768044404733, + 18.33628744864260796, 47.48274104170998555, 18.25738892575703076, 49.02480894996942595, + 17.84288249522569458, 50.54439040009851425, 17.06845153476416499, 52.00935252683229493, + 15.91616209561277273, 53.38530347804498888, 14.37873026754043870, 54.63620962052475960, + 12.46463908845756130, 55.72551502002679058, 10.20331014929099922, 56.61787273415653488, + 7.64890062321653019, 57.28147795233979167, 4.88089731877776067, 57.69078537163564846, + 2.00000000000000222, 60.26295411645657651, -1.52099396835736300, 60.10440004572244277, + -4.89637618755807136, 59.63591573610307250, -7.99912115224313958, 58.87795079119066344, + -10.73370655373201643, 57.86135900031617751, -13.04067793585433499, 56.62367212326139310, + -14.89384545876526111, 55.20548791890712437, -16.29319722451073105, 53.64760968250063655, + -17.25667322722224029, 51.98914605534769606, -17.81285996059914467, 50.26647329190694080, + -17.99546453290829007, 48.51282952087984768, -17.83961562141898938, 46.75830290430587155, + -17.37966075266355759, 45.03002540480565585, -16.64804001511417297, 43.35244484209054860, + -15.67486490585672243, 41.74759899443786537, -14.48792157318536411, 40.23535159216241652, + -13.11290455740948957, 38.83357308646102979, -11.57375501535206475, 37.55826279697890158, + -9.89302536639832653, 36.42361680044041350, -8.09222434325374884, 35.44205010622848562, + -6.19211732580419838, 34.62418375193039566, -4.21297051898843300, 33.97880825794451454, + -2.17473678976558649, 33.51283479810019372, -0.09718755711549092, 33.23124463343540214, + 1.99999999999999889, 33.13704588354342917, 4.09718755711548877, 33.23124463343540214, + 6.17473678976558382, 33.51283479810019372, 8.21297051898843122, 33.97880825794451454, + 10.19211732580419572, 34.62418375193039566, 12.09222434325374707, 35.44205010622848562, + 13.89302536639832830, 36.42361680044041350, 15.57375501535206297, 37.55826279697889447, + 17.11290455740948602, 38.83357308646102268, 18.48792157318536766, 40.23535159216240942, + 19.67486490585671888, 41.74759899443786537, 20.64804001511417653, 43.35244484209054860, + 21.37966075266355404, 45.03002540480565585, 21.83961562141898582, 46.75830290430587155, + 21.99546453290829007, 48.51282952087984768, 21.81285996059914822, 50.26647329190694080, + 21.25667322722224739, 51.98914605534769606, 20.29319722451073105, 53.64760968250062234, + 18.89384545876525934, 55.20548791890712437, 17.04067793585434032, 56.62367212326139310, + 14.73370655373202354, 57.86135900031617751, 11.99912115224314491, 58.87795079119066344, + 8.89637618755807580, 59.63591573610307250, 5.52099396835736744, 60.10440004572244277, + 2.00000000000000222, 62.75242980718265784, -1.60371952785031557, 62.62270582496653049, + -5.08984714396560278, 62.23829052070698253, -8.35401191716921510, 61.61281120273996237, + -11.31471471060360656, 60.76704034614787986, -13.91751559979281083, 59.72658657388572578, + -16.13405311103923978, 58.51955862209560166, -17.95764487214750815, 57.17460858105235388, + -19.39751805719553701, 55.71953428872550518, -20.47321926655622448, 54.18043392376822709, + -21.21002592824931199, 52.58130404832083116, -21.63559425749199150, 50.94394459368972150, + -21.77773936992417703, 49.28804930270956675, -21.66310777053267600, 47.63139077670034283, + -21.31648826424519072, 45.99003977573201496, -20.76054697060167342, 44.37858250245571412, + -20.01582559662803007, 42.81031632956308641, -19.10089088783405842, 41.29741507659190347, + -18.03256121225549791, 39.85106125278488065, -16.82616341777592339, 38.48154618161254348, + -15.49579145727241247, 37.19834072287546434, -14.05455017155123087, 36.01014014726423795, + -12.51477509868480809, 34.92488704934376642, -10.88822378743807739, 33.94977626735131793, + -9.18623692372254119, 33.09124574576954103, -7.41986936545087783, 32.35495718584156322, + -5.59999239238874669, 31.74577019145995394, -3.73736939611796615, 31.26771342119434749, + -1.84270801296844233, 30.92395598105918353, 0.07330759755401026, 30.71678191817960268, + 1.99999999999999889, 30.64757019281734074, 3.92669240244598639, 30.71678191817960268, + 5.84270801296843878, 30.92395598105918353, 7.73736939611796526, 31.26771342119434749, + 9.59999239238874225, 31.74577019145995394, 11.41986936545087516, 32.35495718584156322, + 13.18623692372254119, 33.09124574576954103, 14.88822378743807207, 33.94977626735131793, + 16.51477509868480809, 34.92488704934376642, 18.05455017155123087, 36.01014014726423795, + 19.49579145727241070, 37.19834072287546434, 20.82616341777592339, 38.48154618161253637, + 22.03256121225549791, 39.85106125278488065, 23.10089088783405842, 41.29741507659190347, + 24.01582559662803007, 42.81031632956308641, 24.76054697060167342, 44.37858250245571412, + 25.31648826424519072, 45.99003977573201496, 25.66310777053267600, 47.63139077670033572, + 25.77773936992417703, 49.28804930270955964, 25.63559425749199505, 50.94394459368972150, + 25.21002592824931199, 52.58130404832083116, 24.47321926655622448, 54.18043392376822709, + 23.39751805719553701, 55.71953428872550518, 21.95764487214750460, 57.17460858105235388, + 20.13405311103924333, 58.51955862209560166, 17.91751559979281438, 59.72658657388572578, + 15.31471471060361011, 60.76704034614787986, 12.35401191716921865, 61.61281120273996237, + 9.08984714396560634, 62.23829052070698253, 5.60371952785031979, 62.62270582496653049, + 2.00000000000000311, 65.30899890463146562, -2.55165837938126616, 65.14506774788591770, + -6.91821393310037980, 64.66099291349783584, -10.94248311853021427, 63.87850997409253040, + -14.51323316925390294, 62.82970044453450953, -17.56939664790982292, 61.55243525845713748, + -20.09325961331439458, 60.08625295311978931, -22.09844461997003506, 58.46939890910893922, + -23.61769060836897438, 56.73712764036461920, -24.69307609487012556, 54.92100894435518654, + -25.36933297612858951, 53.04888396821036167, -25.68983696232584535, 51.14517010976478417, + -25.69453569648834801, 49.23130925676766623, -25.41912613625677153, 47.32623888788154431, + -24.89496528133907205, 45.44682484300304282, -24.14937074714975296, 43.60823042135866956, + -23.20610004359021517, 41.82421587722975431, -22.08588698105332782, 40.10737174655498194, + -20.80696969977402944, 38.46929323359992736, -19.38557795842460507, 36.92070387379718710, + -17.83636590786354148, 35.47153642700349963, -16.17278641740254130, 34.13097826525054046, + -14.40740784814351017, 32.90748779465315721, -12.55217628249286932, 31.80878785141789677, + -10.61862699669389443, 30.84184157026976436, -8.61804920168179045, 30.01281590885307438, + -6.56160821625925728, 29.32703776203888069, -4.46042949045145676, 28.78894734383437992, + -2.32564933647368921, 28.40205318377053345, -0.16843782661930862, 28.16889262543881500, + 1.99999999999999889, 28.09100109536852585, 4.16843782661930629, 28.16889262543881500, + 6.32564933647368743, 28.40205318377053345, 8.46042949045145498, 28.78894734383437992, + 10.56160821625925372, 29.32703776203888069, 12.61804920168178867, 30.01281590885307438, + 14.61862699669389265, 30.84184157026976436, 16.55217628249286577, 31.80878785141789677, + 18.40740784814351017, 32.90748779465315721, 20.17278641740253775, 34.13097826525054046, + 21.83636590786353793, 35.47153642700349963, 23.38557795842460152, 36.92070387379718710, + 24.80696969977402588, 38.46929323359992026, 26.08588698105332426, 40.10737174655498194, + 27.20610004359021161, 41.82421587722975431, 28.14937074714974941, 43.60823042135866956, + 28.89496528133906494, 45.44682484300304282, 29.41912613625676798, 47.32623888788153721, + 29.69453569648834446, 49.23130925676766623, 29.68983696232584180, 51.14517010976478417, + 29.36933297612858951, 53.04888396821034746, 28.69307609487012911, 54.92100894435518654, + 27.61769060836897438, 56.73712764036461920, 26.09844461997003506, 58.46939890910893922, + 24.09325961331439814, 60.08625295311978931, 21.56939664790982292, 61.55243525845713748, + 18.51323316925390827, 62.82970044453450953, 14.94248311853021960, 63.87850997409253040, + 10.91821393310038779, 64.66099291349783584, 6.55165837938127193, 65.14506774788591770, + 2.00000000000000400, 67.94514236453578349, -3.38533547682125890, 67.76325974975715383, + -8.52326664933801226, 67.22759281764989225, -13.21138564172178143, 66.36584339331807314, + -17.31841683118042496, 65.21781824976642383, -20.78586461017365750, 63.82887077001763032, + -23.61278608277912383, 62.24449065808524040, -25.83517004248913196, 60.50690475312734407, + -27.50782877801382043, 58.65352526302899605, -28.69170289014789077, 56.71665696607985296, + -29.44632120220701665, 54.72388646563504011, -29.82602201650912477, 52.69875031809839783, + -29.87852862440772839, 50.66145188613276673, -29.64483525740958925, 48.62951713112133945, + -29.15973753507462618, 46.61834908938176625, -28.45262246278107554, 44.64167567492752653, + -27.54831327014242959, 42.71190039889389567, -26.46787020033523774, 40.84037031204417190, + -25.22930648122764552, 39.03757551632936185, -23.84820859564655038, 37.31329283883029291, + -22.33826403031283903, 35.67668409468497970, -20.71170528699355629, 34.13635737784334623, + -18.97968026705781242, 32.70039821822412307, -17.15255849246134900, 31.37637626233231813, + -15.24018123567453742, 30.17133232687284305, -13.25206214192900944, 29.09175016726920759, + -11.19754366142445079, 28.14351701208745027, -9.08591371200913400, 27.33187675649334381, + -6.92648650789847675, 26.66137960320710931, -4.72865139696445702, 26.13583181560235502, + -2.50189378344492486, 25.75824904385734371, -0.25579267831605296, 25.53081635683327733, + 1.99999999999999889, 25.45485763546421154, 4.25579267831605002, 25.53081635683327733, + 6.50189378344492308, 25.75824904385734371, 8.72865139696445347, 26.13583181560235502, + 10.92648650789847764, 26.66137960320710931, 13.08591371200913400, 27.33187675649334381, + 15.19754366142444724, 28.14351701208745027, 17.25206214192900589, 29.09175016726920759, + 19.24018123567453742, 30.17133232687284305, 21.15255849246134545, 31.37637626233231813, + 22.97968026705781242, 32.70039821822412307, 24.71170528699354918, 34.13635737784334623, + 26.33826403031282837, 35.67668409468497259, 27.84820859564654683, 37.31329283883029291, + 29.22930648122764197, 39.03757551632935474, 30.46787020033523419, 40.84037031204417190, + 31.54831327014242248, 42.71190039889389567, 32.45262246278107199, 44.64167567492752653, + 33.15973753507462618, 46.61834908938176625, 33.64483525740958214, 48.62951713112133945, + 33.87852862440772839, 50.66145188613276673, 33.82602201650912122, 52.69875031809839783, + 33.44632120220701665, 54.72388646563502590, 32.69170289014788011, 56.71665696607985296, + 31.50782877801381687, 58.65352526302899605, 29.83517004248913906, 60.50690475312734407, + 27.61278608277913094, 62.24449065808524040, 24.78586461017366460, 63.82887077001763032, + 21.31841683118043562, 65.21781824976642383, 17.21138564172179031, 66.36584339331807314, + 12.52326664933801936, 67.22759281764989225, 7.38533547682126645, 67.76325974975715383, + 2.00000000000000488, 70.67459026401789401, -4.83336207267744200, 70.44359392189112157, + -11.25145387811900299, 69.76794994271870110, -16.94303945184942606, 68.69426490502790728, + -21.74696599568290978, 67.28571148577093197, -25.63456841105837825, 65.60910965714572285, + -28.66225471925319823, 63.72633555655123416, -30.92626834358740595, 61.69055635034530383, + -32.53242307522426557, 59.54571651024583190, -33.58017141013023377, 57.32758829402004608, + -34.15616674923442986, 55.06530104282313687, -34.33296113360040636, 52.78282385843581892, + -34.17004600591189956, 50.50021197832979425, -33.71572773969587900, 48.23458650173874673, + -33.00911969333094476, 46.00087796034757304, -32.08195470333168231, 43.81237943576640248, + -30.96012666362175025, 41.68115224019865650, -29.66495985223924237, 39.61831917500947498, + -28.21423852322426740, 37.63427208010365632, -26.62303747247054986, 35.73881343521247089, + -24.90439178940930276, 33.94124647006630369, -23.06983782140640216, 32.25042441190934994, + -21.12985059268007149, 30.67476685924403057, -19.09419678103115459, 29.22224954465700009, + -16.97221728412494457, 27.90037270541385794, -14.77304947679039238, 26.71611272875198750, + -12.50579641562138100, 25.67586152120415832, -10.17964838758363832, 24.78535803035031648, + -7.80396120930039583, 24.04961640006232315, -5.38829543151647705, 23.47285525677743223, + -2.94242093509331726, 23.05843251151437201, -0.47629214130109782, 22.80878975038074685, + 1.99999999999999800, 22.72540973598210812, 4.47629214130109521, 22.80878975038074685, + 6.94242093509331326, 23.05843251151437201, 9.38829543151647350, 23.47285525677743223, + 11.80396120930039316, 24.04961640006232315, 14.17964838758363655, 24.78535803035031648, + 16.50579641562137567, 25.67586152120415832, 18.77304947679039060, 26.71611272875198750, + 20.97221728412493746, 27.90037270541385439, 23.09419678103115103, 29.22224954465700009, + 25.12985059268006793, 30.67476685924402702, 27.06983782140639860, 32.25042441190934994, + 28.90439178940930631, 33.94124647006630369, 30.62303747247054275, 35.73881343521246379, + 32.21423852322426029, 37.63427208010365632, 33.66495985223924237, 39.61831917500946787, + 34.96012666362174315, 41.68115224019865650, 36.08195470333167520, 43.81237943576640248, + 37.00911969333093765, 46.00087796034757304, 37.71572773969587189, 48.23458650173873252, + 38.17004600591188535, 50.50021197832979425, 38.33296113360039925, 52.78282385843581892, + 38.15616674923441565, 55.06530104282313687, 37.58017141013023377, 57.32758829402004608, + 36.53242307522427268, 59.54571651024581769, 34.92626834358741661, 61.69055635034530383, + 32.66225471925319823, 63.72633555655123416, 29.63456841105838535, 65.60910965714572285, + 25.74696599568291333, 67.28571148577093197, 20.94303945184943316, 68.69426490502790728, + 15.25145387811901188, 69.76794994271870110, 8.83336207267745088, 70.44359392189112157, + 2.00000000000000622, 73.51255823132638056, -6.80643587234800940, 73.21444539538327945, + -14.87413258170036379, 72.35214877025040892, -21.72462936947633594, 71.00751554077180572, + -27.20553396293055215, 69.28213523998356038, -31.39257932570032139, 67.27265449907143591, + -34.46283411458643542, 65.05952649321666570, -36.61111913565843423, 62.70556553816149403, + -38.01204327449661236, 60.25870541573158334, -38.80901819915838047, 57.75559962131574565, + -39.11518954384546731, 55.22475594928482678, -39.01828312042780311, 52.68892136253921876, + -38.58594887503603132, 50.16679739569313057, -37.87042151784342536, 47.67424596736638875, + -36.91224672041000332, 45.22513042719018728, -35.74315247327934486, 42.83190113242096686, + -34.38823113470312620, 40.50600232791481403, -32.86759479425492714, 38.25815259440047811, + -31.19763777135731431, 36.09853399317181299, -29.39200856069424006, 34.03691345955112979, + -27.46236656888655148, 32.08271233019573287, -25.41897786066869003, 30.24503490367625247, + -23.27118821325056430, 28.53266378578365448, -21.02780000980340347, 26.95402789495273055, + -18.69737092792362887, 25.51714801636046559, -16.28844626261447814, 24.22956442487017625, + -13.80973254683687124, 23.09825113914620331, -11.27021753707410845, 22.12952164603582617, + -8.67924035399200910, 21.32893128539883065, -6.04651538477557082, 20.70118176192879744, + -3.38211423303386649, 20.25003331964319386, -0.69641128035122979, 19.97822987242789239, + 1.99999999999999756, 19.88744176867360025, 4.69641128035122613, 19.97822987242789239, + 7.38211423303386205, 20.25003331964319386, 10.04651538477556727, 20.70118176192879744, + 12.67924035399200378, 21.32893128539883065, 15.27021753707410667, 22.12952164603582617, + 17.80973254683686946, 23.09825113914620331, 20.28844626261447814, 24.22956442487017270, + 22.69737092792362532, 25.51714801636046559, 25.02780000980339992, 26.95402789495272700, + 27.27118821325056786, 28.53266378578365448, 29.41897786066868292, 30.24503490367624536, + 31.46236656888655503, 32.08271233019573287, 33.39200856069423651, 34.03691345955112979, + 35.19763777135730720, 36.09853399317180589, 36.86759479425493424, 38.25815259440047811, + 38.38823113470313331, 40.50600232791481403, 39.74315247327934486, 42.83190113242095975, + 40.91224672041000332, 45.22513042719018728, 41.87042151784342536, 47.67424596736638165, + 42.58594887503603132, 50.16679739569313057, 43.01828312042781022, 52.68892136253921876, + 43.11518954384546731, 55.22475594928482678, 42.80901819915838757, 57.75559962131573144, + 42.01204327449661946, 60.25870541573157624, 40.61111913565844134, 62.70556553816149403, + 38.46283411458643542, 65.05952649321666570, 35.39257932570031784, 67.27265449907143591, + 31.20553396293055926, 69.28213523998356038, 25.72462936947633949, 71.00751554077180572, + 18.87413258170037267, 72.35214877025040892, 10.80643587234802361, 73.21444539538327945, + 2.00000000000000844, 76.47602837656471308, -9.67376613190775814, 76.07990221077976400, + -19.91726594386999949, 74.95625177955801632, -28.02735732207738550, 73.25702648451857613, + -34.02194152863378918, 71.14627956201684356, -38.25028545833758642, 68.75766348420627594, + -41.10709162054251209, 66.18773615332521842, -42.92334661203305757, 63.50367340927638793, + -43.94927569193657035, 60.75248226909388904, -44.36709840828364548, 57.96800639328736793, + -44.30826862884845241, 55.17560336970880286, -43.86791757533286074, 52.39514197266055362, + -43.11549450115907689, 49.64291142290011294, -42.10226597817306526, 46.93284831506371546, + -40.86653894639825779, 44.27733603455973110, -39.43731045663169965, 41.68773239540036002, + -37.83684688715485578, 39.17472027603687934, -36.08253581911941410, 36.74853923728758076, + -34.18824090805679816, 34.41913389699746517, -32.16531367916817885, 32.19624133969532664, + -30.02336509370288198, 30.08943159705432890, -27.77086550998262382, 28.10811023218007776, + -25.41561850057673411, 26.26148912292403992, -22.96513811238918379, 24.55852997196868515, + -20.42694818201796281, 23.00786444863284785, -17.80881477377849720, 21.61769490666709714, + -15.11891780975726896, 20.39568010883170501, -12.36596498789072740, 19.34881113120014717, + -9.55924980839402672, 18.48328342761256593, -6.70865569654629645, 17.80437171431731613, + -3.82460955068694286, 17.31631470128239414, -0.91799021933643277, 17.02221659443388191, + 1.99999999999999756, 16.92397162343524286, 4.91799021933642866, 17.02221659443388191, + 7.82460955068693664, 17.31631470128239414, 10.70865569654629290, 17.80437171431731613, + 13.55924980839402139, 18.48328342761256593, 16.36596498789072740, 19.34881113120014717, + 19.11891780975726363, 20.39568010883170501, 21.80881477377850075, 21.61769490666709714, + 24.42694818201795925, 23.00786444863284430, 26.96513811238918024, 24.55852997196867804, + 29.41561850057672345, 26.26148912292403637, 31.77086550998261671, 28.10811023218007065, + 34.02336509370287132, 30.08943159705432180, 36.16531367916817885, 32.19624133969532664, + 38.18824090805679106, 34.41913389699745807, 40.08253581911941410, 36.74853923728758076, + 41.83684688715484867, 39.17472027603687934, 43.43731045663169255, 41.68773239540036002, + 44.86653894639826490, 44.27733603455973110, 46.10226597817306526, 46.93284831506371546, + 47.11549450115907689, 49.64291142290011294, 47.86791757533286784, 52.39514197266055362, + 48.30826862884845951, 55.17560336970880286, 48.36709840828365259, 57.96800639328736793, + 47.94927569193657746, 60.75248226909387483, 46.92334661203305757, 63.50367340927638793, + 45.10709162054252630, 66.18773615332521842, 42.25028545833760063, 68.75766348420627594, + 38.02194152863379628, 71.14627956201684356, 32.02735732207738550, 73.25702648451857613, + 23.91726594387000659, 74.95625177955801632, 13.67376613190777590, 76.07990221077976400, + 2.00000000000001155, 79.58407807962322522, -14.24038894313819092, 79.03039199474196153, + -27.34759503822197502, 77.51867325981696411, -36.54693285975560713, 75.34972495190746145, + -42.57629367046713753, 72.78039612779681988, -46.37308749001456221, 69.97672254379963874, + -48.64383991815974895, 67.03853932314146391, -49.85773173662808233, 64.02688869849654907, + -50.31863539857851464, 60.98111330635318694, -50.22511448054331140, 57.92830381592441569, + -49.70950811496560107, 54.88845672164920586, -48.86197695521289575, 51.87735585154322138, + -47.74524369263553325, 48.90823812634897649, -46.40377244059971673, 45.99279145534946167, + -44.86961926381133026, 43.14177213432515856, -43.16626004327478228, 40.36539642075791789, + -41.31116510791174079, 37.67359181604234664, -39.31758136998610098, 35.07615645611910082, + -37.19580324741198041, 32.58285444844916867, -34.95410730493897233, 30.20346328869343466, + -32.59946098845734497, 27.94778271439298933, -30.13807554901531915, 25.82561045671564770, + -27.57584735879238025, 23.84668824688734645, -24.91871466466540852, 22.02062053097340311, + -22.17294518233925871, 20.35676830949395466, -19.34536199373425802, 18.86412113035556004, + -16.44350999615492270, 17.55115134744557182, -13.47576217013061850, 16.42565612762181360, + -10.45136393475438830, 15.49459412897927280, -7.38041466390653600, 14.76392503362292707, + -4.27378779825515664, 14.23846093632396936, -1.14299451095196525, 13.92173872937387458, + 1.99999999999999756, 13.81592192037676980, 5.14299451095195970, 13.92173872937387458, + 8.27378779825515132, 14.23846093632396936, 11.38041466390652978, 14.76392503362292707, + 14.45136393475438119, 15.49459412897927280, 17.47576217013061495, 16.42565612762181360, + 20.44350999615491560, 17.55115134744557182, 23.34536199373425447, 18.86412113035556004, + 26.17294518233925515, 20.35676830949395466, 28.91871466466540852, 22.02062053097340311, + 31.57584735879237314, 23.84668824688734645, 34.13807554901531205, 25.82561045671564415, + 36.59946098845733786, 27.94778271439298933, 38.95410730493897233, 30.20346328869342756, + 41.19580324741196620, 32.58285444844916867, 43.31758136998610098, 35.07615645611910082, + 45.31116510791174079, 37.67359181604234664, 47.16626004327477517, 40.36539642075791789, + 48.86961926381131605, 43.14177213432515856, 50.40377244059970963, 45.99279145534945457, + 51.74524369263552614, 48.90823812634896228, 52.86197695521290285, 51.87735585154322138, + 53.70950811496559396, 54.88845672164920586, 54.22511448054331140, 57.92830381592441569, + 54.31863539857851464, 60.98111330635318694, 53.85773173662808233, 64.02688869849654907, + 52.64383991815974895, 67.03853932314146391, 50.37308749001457642, 69.97672254379963874, + 46.57629367046714464, 72.78039612779681988, 40.54693285975561423, 75.34972495190746145, + 31.34759503822198212, 77.51867325981696411, 18.24038894313820691, 79.03039199474196153, + 2.00000000000001998, 82.85826214470431239, -22.58690897303193523, 82.01033616765489853, + -38.96575654499159924, 79.88802174923017674, -48.17735288037921038, 77.11452529323827321, + -53.18593275925196195, 74.03707888623914357, -55.81801935702030448, 70.81486693953937106, + -57.03453256694833584, 67.52503657439315532, -57.35114187047393841, 64.20957103321012482, + -57.05957379451162126, 60.89430451319173443, -56.33378767939932885, 57.59709162031844443, + -55.28230228211860009, 54.33161333463023368, -53.97533567047437231, 51.10929358481432416, + -52.45964870701570248, 47.94033352757975308, -50.76707508825830928, 44.83430401955754974, + -48.91964996438991875, 41.80050199367043007, -46.93282539675249154, 38.84817223653459450, + -44.81756887288366897, 35.98664677769215103, -42.58178882927894193, 33.22542941501406233, + -40.23134383513492196, 30.57423988556289629, -37.77078829775713587, 28.04302503644353806, + -35.20394762734520100, 25.64194033091114022, -32.53437968696509586, 23.38130282889364508, + -29.76575655354334771, 21.27151577227776258, -26.90218550361062810, 19.32296477964254322, + -23.94847769357362566, 17.54588626406437513, -20.91036566952359976, 15.95020993686824795, + -17.79466590782826074, 14.54537904344352306, -14.60937983520193661, 13.34015413091371371, + -11.36372621835939434, 12.34240842556757833, -8.06809948196382720, 11.55892497220584580, + -4.73395228112668587, 10.99520717814031734, -1.37360609917944543, 10.65531494200142504, + 1.99999999999999756, 10.54173785529561336, 5.37360609917944032, 10.65531494200142504, + 8.73395228112667965, 10.99520717814031734, 12.06809948196382187, 11.55892497220584580, + 15.36372621835939078, 12.34240842556757833, 18.60937983520193484, 13.34015413091371371, + 21.79466590782825364, 14.54537904344352306, 24.91036566952359621, 15.95020993686824795, + 27.94847769357362210, 17.54588626406437513, 30.90218550361061745, 19.32296477964254322, + 33.76575655354334060, 21.27151577227776258, 36.53437968696508875, 23.38130282889363798, + 39.20394762734519389, 25.64194033091114022, 41.77078829775712165, 28.04302503644353095, + 44.23134383513491485, 30.57423988556289629, 46.58178882927893483, 33.22542941501406233, + 48.81756887288366187, 35.98664677769215103, 50.93282539675248444, 38.84817223653458029, + 52.91964996438991164, 41.80050199367041586, 54.76707508825830928, 44.83430401955754263, + 56.45964870701569538, 47.94033352757973887, 57.97533567047437231, 51.10929358481431706, + 59.28230228211859298, 54.33161333463023368, 60.33378767939932885, 57.59709162031844443, + 61.05957379451161415, 60.89430451319173443, 61.35114187047394552, 64.20957103321012482, + 61.03453256694833584, 67.52503657439315532, 59.81801935702030448, 70.81486693953937106, + 57.18593275925196906, 74.03707888623914357, 52.17735288037921038, 77.11452529323827321, + 42.96575654499162056, 79.88802174923017674, 26.58690897303196721, 82.01033616765489853, + 2.00000000000004352, 86.32305404620944955, -41.28583165458979920, 84.76914638163376026, + -57.58131858106001744, 81.70467639663135628, -63.56415050352948981, 78.26774990878784877, + -65.82714145548160900, 74.71983727667711150, -66.41516373728856593, 71.13755089586129543, + -66.11115128919476547, 67.55274433031233627, -65.27043361461275595, 63.98289851135272244, + -64.07470870335011170, 60.43992354246546483, -62.62500642980751309, 56.93332642202356197, + -60.98089250042357889, 53.47155025248752480, -59.17889520468775544, 50.06261728049957327, + -57.24193904387428233, 46.71447239476329827, -55.18452370168171939, 43.43518400478754415, + -53.01574257648425004, 40.23307020728445593, -50.74114071516557800, 37.11678138456306897, + -48.36392121341754802, 34.09535373993877982, -45.88577372237665486, 31.17824007995576707, + -43.30747895739605013, 28.37531978094453677, -40.62937881000767959, 25.69688738457191945, + -37.85176517580522670, 23.15361781597290758, -34.97521854780547557, 20.75650546647104377, + -32.00091302933286386, 18.51677422677156670, -28.93089420696503922, 16.44575600838861718, + -25.76832854644280602, 14.55473640862136619, -22.51771689758312078, 12.85476800444173051, + -19.18506021909563941, 11.35645428598744111, -15.77796305903055440, 10.06971032975658353, + -12.30566013367807265, 9.00350968307662569, -8.77895396829167218, 8.16563014493928918, + -5.21005712455211789, 7.56241362477411272, -1.61234064761579554, 7.19855645524003407, + 1.99999999999999734, 7.07694595379053126, 5.61234064761579088, 7.19855645524003407, + 9.21005712455211167, 7.56241362477411272, 12.77895396829166685, 8.16563014493928918, + 16.30566013367807088, 9.00350968307662569, 19.77796305903055085, 10.06971032975658353, + 23.18506021909563231, 11.35645428598744111, 26.51771689758312078, 12.85476800444173051, + 29.76832854644280246, 14.55473640862136619, 32.93089420696504277, 16.44575600838861718, + 36.00091302933286386, 18.51677422677156670, 38.97521854780546846, 20.75650546647104022, + 41.85176517580521960, 23.15361781597290403, 44.62937881000767248, 25.69688738457191590, + 47.30747895739604303, 28.37531978094453322, 49.88577372237665486, 31.17824007995575997, + 52.36392121341754091, 34.09535373993877982, 54.74114071516557800, 37.11678138456306897, + 57.01574257648425004, 40.23307020728444883, 59.18452370168172649, 43.43518400478754415, + 61.24193904387428944, 46.71447239476329827, 63.17889520468774833, 50.06261728049956616, + 64.98089250042356468, 53.47155025248752480, 66.62500642980749888, 56.93332642202356197, + 68.07470870335012592, 60.43992354246546483, 69.27043361461275595, 63.98289851135272244, + 70.11115128919477968, 67.55274433031233627, 70.41516373728855172, 71.13755089586129543, + 69.82714145548160900, 74.71983727667711150, 67.56415050352947560, 78.26774990878784877, + 61.58131858106001744, 81.70467639663135628, 45.28583165458984183, 84.76914638163376026, + -178.00000000002495426, 89.99364961382421768, -86.04670840724655534, 86.14286053253547948, + -83.94730489861873934, 82.29066165213360762, -81.87003129087820241, 78.44836496333867615, + -79.78661098119418682, 74.62102007313750107, -77.68757690134611948, 70.81379178736982283, + -75.56653776770212971, 67.03200075627314902, -73.41791965045068480, 63.28116415502336878, + -71.23639478559491067, 59.56703711552955127, -69.01668877482984499, 55.89565494125256606, + -66.75350545455997064, 52.27337593848611164, -64.44149951448400770, 48.70692450151241104, + -62.07527488350018530, 45.20343384586799118, -59.64940188352277772, 41.77048747717444854, + -57.15845171105518574, 38.41615810166682365, -54.59704918478396252, 35.14904222145087687, + -51.95994579953059400, 31.97828811153562256, -49.24211556220075892, 28.91361425616476666, + -46.43887601207384819, 25.96531465470105005, -43.54603622089217652, 23.14424674184217068, + -40.56007232189229228, 20.46179708442632972, -37.47832910130670570, 17.92981963683196156, + -34.29924330014637945, 15.56054131855884570, -31.02258051120933402, 13.36643021383323848, + -27.64967308093982012, 11.36002298851478542, -24.18364165385173337, 9.55371034930798224, + -20.62957864939022201, 7.95948261839810733, -16.99466904928961242, 6.58864168091602309, + -13.28822357553577405, 5.45149036402020837, -9.52160273595813855, 4.55701513369246669, + -5.70801791517022217, 3.91258202080964690, -1.86220744039441199, 3.52366797872773274, + 1.99999999999999734, 3.39364961385021413, 5.86220744039440689, 3.52366797872773274, + 9.70801791517021861, 3.91258202080964690, 13.52160273595813145, 4.55701513369246669, + 17.28822357553576694, 5.45149036402020837, 20.99466904928960886, 6.58864168091602309, + 24.62957864939021491, 7.95948261839810733, 28.18364165385172981, 9.55371034930798224, + 31.64967308093981302, 11.36002298851478542, 35.02258051120933402, 13.36643021383323848, + 38.29924330014637235, 15.56054131855884570, 41.47832910130669859, 17.92981963683195801, + 44.56007232189227807, 20.46179708442632261, 47.54603622089216231, 23.14424674184217068, + 50.43887601207384819, 25.96531465470104649, 53.24211556220075892, 28.91361425616475955, + 55.95994579953058690, 31.97828811153562256, 58.59704918478396252, 35.14904222145087687, + 61.15845171105517863, 38.41615810166681655, 63.64940188352278483, 41.77048747717444854, + 66.07527488350018530, 45.20343384586799118, 68.44149951448399349, 48.70692450151241104, + 70.75350545455997064, 52.27337593848611164, 73.01668877482984499, 55.89565494125256606, + 75.23639478559491067, 59.56703711552955127, 77.41791965045068480, 63.28116415502336878, + 79.56653776770211550, 67.03200075627314902, 81.68757690134611948, 70.81379178736982283, + 83.78661098119418682, 74.62102007313750107, 85.87003129087820241, 78.44836496333867615, + 87.94730489861875355, 82.29066165213360762, 90.04670840724658376, 86.14286053253547948, + -178.00000000000005684, 86.05996156130565566, -130.58473801020662108, 84.39088722277173815, + -110.19717929614465390, 81.10053146183230410, -100.10346048738929881, 77.40995108179684792, + -93.69886509454548218, 73.59868574092435267, -88.93011750021230455, 69.74815881341463353, + -85.00534896567366161, 65.89187266757556927, -81.55981529780905248, 62.04795929474798299, + -78.40205491898922219, 58.22861013398448193, -75.42099130058548440, 54.44347433172315220, + -72.54676554128549526, 50.70110112272598712, -69.73234087330638431, 47.00963897352001908, + -66.94410839385282941, 43.37721635788606989, -64.15676435641834985, 39.81217237539897269, + -61.35037195723166548, 36.32321005821970061, -58.50861449014277582, 32.91950572923986584, + -55.61773745881250619, 29.61078975921034129, -52.66591251909912330, 26.40740499964346810, + -49.64287529119710030, 23.32034407821754840, -46.53975201683548590, 20.36126354510683711, + -43.34902402209520034, 17.54247066454981407, -40.06459700293881809, 14.87687712846902599, + -36.68195074563113423, 12.37791309905906445, -33.19834740797973893, 10.05939491571369260, + -29.61307490596015057, 7.93534078498541273, -25.92769777693465372, 6.01973109672258389, + -22.14628266758476371, 4.32621388981253130, -18.27556116130716646, 2.86776143955401652, + -14.32499114491301384, 1.65629062554436479, -10.30668142622437244, 0.70226688878528098, + -6.23515434659414680, 0.01431798130227213, -2.12693783758673716, -0.40111214042971233, + 1.99999999999999689, -0.54003843869414525, 6.12693783758673050, -0.40111214042971233, + 10.23515434659414147, 0.01431798130227213, 14.30668142622436712, 0.70226688878528098, + 18.32499114491300318, 1.65629062554436479, 22.27556116130715580, 2.86776143955401652, + 26.14628266758476016, 4.32621388981253130, 29.92769777693464306, 6.01973109672258389, + 33.61307490596014702, 7.93534078498541273, 37.19834740797973183, 10.05939491571369260, + 40.68195074563112001, 12.37791309905905912, 44.06459700293881809, 14.87687712846902421, + 47.34902402209520034, 17.54247066454981052, 50.53975201683548590, 20.36126354510683711, + 53.64287529119709319, 23.32034407821754485, 56.66591251909911620, 26.40740499964346455, + 59.61773745881250619, 29.61078975921033418, 62.50861449014276872, 32.91950572923986584, + 65.35037195723165837, 36.32321005821969351, 68.15676435641833564, 39.81217237539895848, + 70.94410839385281520, 43.37721635788606989, 73.73234087330637010, 47.00963897352000487, + 76.54676554128549526, 50.70110112272598002, 79.42099130058548440, 54.44347433172315220, + 82.40205491898922219, 58.22861013398448193, 85.55981529780903827, 62.04795929474798299, + 89.00534896567364740, 65.89187266757556927, 92.93011750021230455, 69.74815881341463353, + 97.69886509454546797, 73.59868574092435267, 104.10346048738931302, 77.40995108179684792, + 114.19717929614462548, 81.10053146183230410, 134.58473801020659266, 84.39088722277173815, + -178.00000000000002842, 81.83938189765240168, -149.15668933029422760, 80.85560324742941418, + -128.64763601333984866, 78.39753950634394641, -115.34882263320223217, 75.18923730101467129, + -106.23509874248557594, 71.62987479878029262, -99.45761459629794388, 67.90061168070624831, + -94.04421718085441739, 64.08831694761892095, -89.47013935832345055, 60.23965935256792648, + -85.43460651588344490, 56.38292751246400769, -81.75424365182693975, 52.53738247574246856, + -78.31071017117777444, 48.71761719224470966, -75.02360369831293951, 44.93576201889581512, + -71.83567948061782715, 41.20268880474466755, -68.70439458886511375, 37.52871692222674938, + -65.59687011372152199, 33.92405612452550656, -62.48679120890344052, 30.39910192691253599, + -59.35245736607464551, 26.96464262473804041, -56.17554747078253996, 23.63200834498021408, + -52.94035098918124760, 20.41317688045037926, -49.63331929525543984, 17.32084182287969298, + -46.24284889074778704, 14.36844259642658983, -42.75924066894448572, 11.57015196980180605, + -39.17479644658766347, 8.94081392759362537, -35.48402100288603123, 6.49582334126627892, + -31.68389780238809195, 4.25093893994812166, -27.77420154083882053, 2.22202305439570358, + -23.75780275269854158, 0.42470591907018640, -19.64091162595336826, -1.12602081331554005, + -15.43320327160518524, -2.41626750988024330, -11.14776873556009384, -3.43374257581262743, + -6.80084816537008940, -4.16824654595516098, -2.41132592404073165, -4.61212239418478553, + 1.99999999999999689, -4.76061810234754024, 6.41132592404072632, -4.61212239418478553, + 10.80084816537008230, -4.16824654595516098, 15.14776873556009029, -3.43374257581262743, + 19.43320327160517991, -2.41626750988024330, 23.64091162595336115, -1.12602081331554005, + 27.75780275269853803, 0.42470591907018640, 31.77420154083881698, 2.22202305439570358, + 35.68389780238808129, 4.25093893994812166, 39.48402100288601702, 6.49582334126627270, + 43.17479644658765636, 8.94081392759361826, 46.75924066894447861, 11.57015196980179894, + 50.24284889074777283, 14.36844259642658628, 53.63331929525543273, 17.32084182287968943, + 56.94035098918124049, 20.41317688045037571, 60.17554747078253286, 23.63200834498020697, + 63.35245736607463840, 26.96464262473803331, 66.48679120890342631, 30.39910192691252178, + 69.59687011372152199, 33.92405612452549946, 72.70439458886511375, 37.52871692222674227, + 75.83567948061782715, 41.20268880474466755, 79.02360369831293951, 44.93576201889580091, + 82.31071017117776023, 48.71761719224470255, 85.75424365182693975, 52.53738247574246145, + 89.43460651588344490, 56.38292751246400769, 93.47013935832345055, 60.23965935256792648, + 98.04421718085441739, 64.08831694761892095, 103.45761459629794388, 67.90061168070624831, + 110.23509874248557594, 71.62987479878029262, 119.34882263320223217, 75.18923730101467129, + 132.64763601333982024, 78.39753950634394641, 153.15668933029419918, 80.85560324742941418, + -178.00000000000002842, 77.29014606148251687, -157.47781378002883912, 76.59572545663588983, + -140.18467631053607647, 74.70285264202438213, -126.87442892417496410, 71.99165187875776439, + -116.75095597801099245, 68.78228497629521598, -108.83745587761724494, 65.27873175231235336, + -102.40541928769302160, 61.60190913800556700, -96.97173315822051620, 57.82466562901673512, + -92.22157019495104180, 53.99321139219064491, -87.94629406419838347, 50.13884820321026581, + -84.00357763838492531, 46.28434439562811065, -80.29307189810650414, 42.44749809608728697, + -76.74158359961857911, 38.64321178012773572, -73.29390254081907585, 34.88475663106463287, + -69.90700610430916129, 31.18458087158649406, -66.54632400302145356, 27.55485191826680591, + -63.18329478816111333, 24.00783659504740086, -59.79375952890369916, 20.55617726580612015, + -56.35691981311008192, 17.21309536720795919, -52.85469423510679832, 13.99253787292509621, + -49.27137129744098587, 10.90927185095380558, -45.59349430020986915, 7.97892504367260802, + -41.80993456941939712, 5.21796518077428839, -37.91211819209929246, 2.64360721178194558, + -33.89437094593881739, 0.27363605581641759, -29.75433817509684786, -1.87386656696368226, + -25.49342310484703589, -3.78089833576525525, -21.11717183125961128, -5.43001433583734983, + -16.63552097548324937, -6.80489321395614066, -12.06282113870962114, -7.89096231408011839, + -7.41756212661334047, -8.67603648837036268, -2.72175805422022021, -9.15091230991673399, + 1.99999999999999645, -9.30985393851741172, 6.72175805422021444, -9.15091230991673399, + 11.41756212661333691, -8.67603648837036268, 16.06282113870961581, -7.89096231408011839, + 20.63552097548324582, -6.80489321395614066, 25.11717183125960418, -5.43001433583734983, + 29.49342310484702523, -3.78089833576525525, 33.75433817509684786, -1.87386656696368226, + 37.89437094593881028, 0.27363605581641121, 41.91211819209927114, 2.64360721178193936, + 45.80993456941938291, 5.21796518077428217, 49.59349430020984784, 7.97892504367260180, + 53.27137129744098587, 10.90927185095380203, 56.85469423510678411, 13.99253787292509266, + 60.35691981311008192, 17.21309536720795563, 63.79375952890369206, 20.55617726580611659, + 67.18329478816110623, 24.00783659504739376, 70.54632400302143935, 27.55485191826679880, + 73.90700610430916129, 31.18458087158649050, 77.29390254081907585, 34.88475663106463287, + 80.74158359961856490, 38.64321178012773572, 84.29307189810650414, 42.44749809608728697, + 88.00357763838491110, 46.28434439562810354, 91.94629406419836926, 50.13884820321025160, + 96.22157019495104180, 53.99321139219064491, 100.97173315822050199, 57.82466562901673512, + 106.40541928769302160, 61.60190913800556700, 112.83745587761723073, 65.27873175231235336, + 120.75095597801100666, 68.78228497629521598, 130.87442892417493567, 71.99165187875776439, + 144.18467631053604805, 74.70285264202438213, 161.47781378002881070, 76.59572545663588983, + -178.00000000000002842, 72.36459406509962378, -162.05644118521095720, 71.82662636485281382, + -147.60742176334812825, 70.30250402254888797, -135.36255278451204731, 68.00113945880528377, + -125.26976638159491984, 65.14478524125108549, -116.94158660124730886, 61.91142197433249095, + -109.95470750710923369, 58.42738157410047961, -103.95825647893495614, 54.77900423463843538, + -98.68725830824647005, 51.02562354013957702, -93.94726867835278483, 47.20919167069192923, + -89.59567144235911940, 43.36062114047545890, -85.52649044264862255, 39.50382677214758331, + -81.65938939021744147, 35.65830698143167155, -77.93201512996090230, 31.84082402428231973, + -74.29472888122153051, 28.06652943785655552, -70.70698212428176532, 24.34974403770184992, + -67.13481958525987636, 20.70451849248450316, -63.54916484650964748, 17.14505004500528074, + -59.92466443809041010, 13.68599977255222377, -56.23894695811429756, 10.34273467530951152, + -52.47220691225792422, 7.13150506907927006, -48.60705694133756083, 4.06955754229704603, + -44.62861191461713162, 1.17517574056815666, -40.52477649322946007, -1.53236497867592147, + -36.28670514160292981, -4.03294710811187507, -31.90939060170597941, -6.30588392466837533, + -27.39231472226267528, -8.33033151194295662, -22.74006765897273397, -10.08584610023313921, + -17.96281473836326725, -11.55307649365679090, -13.07647532339788654, -12.71455858250937254, + -8.10248720394762678, -13.55555303790299426, -3.06707279862612969, -14.06484423228424063, + 1.99999999999999600, -14.23540593490034212, 7.06707279862612125, -14.06484423228424063, + 12.10248720394761790, -13.55555303790299426, 17.07647532339788299, -12.71455858250937254, + 21.96281473836326015, -11.55307649365679090, 26.74006765897273041, -10.08584610023313921, + 31.39231472226266817, -8.33033151194295662, 35.90939060170597230, -6.30588392466837533, + 40.28670514160292271, -4.03294710811188128, 44.52477649322944586, -1.53236497867592791, + 48.62861191461712451, 1.17517574056815022, 52.60705694133754662, 4.06955754229703981, + 56.47220691225791711, 7.13150506907926740, 60.23894695811429756, 10.34273467530950796, + 63.92466443809040300, 13.68599977255221667, 67.54916484650964037, 17.14505004500527363, + 71.13481958525987636, 20.70451849248449605, 74.70698212428177953, 24.34974403770184992, + 78.29472888122153051, 28.06652943785655197, 81.93201512996088809, 31.84082402428231262, + 85.65938939021744147, 35.65830698143166444, 89.52649044264860834, 39.50382677214758331, + 93.59567144235911940, 43.36062114047545180, 97.94726867835277062, 47.20919167069192213, + 102.68725830824645584, 51.02562354013956281, 107.95825647893494192, 54.77900423463843538, + 113.95470750710921948, 58.42738157410047961, 120.94158660124729465, 61.91142197433249095, + 129.26976638159487720, 65.14478524125108549, 139.36255278451201889, 68.00113945880528377, + 151.60742176334809983, 70.30250402254888797, 166.05644118521092878, 71.82662636485281382, + -178.00000000000002842, 67.00865712643569339, -164.95645291666266985, 66.56908368553206401, + -152.69437932769912436, 65.29863962109095610, -141.70578215046154469, 63.31961305621509695, + -132.12712761110685733, 60.78156430751474204, -123.85428534979440940, 57.82417470821774685, + -116.68240638414135901, 54.56141374911145192, -110.39390839554111778, 51.08045112569507751, + -104.79614728017998004, 47.44644408577498496, -99.73088255981890882, 43.70821480745370025, + -95.07197748871266185, 39.90302877816822757, -90.71961138434764393, 36.06017287638244539, + -86.59435351833845118, 32.20351875194761959, -82.63216392910496211, 28.35333972343855535, + -78.78048521884275601, 24.52760876444716587, -74.99529352424694650, 20.74294398926493699, + -71.23891693218340038, 17.01531594444593765, -67.47845003929684538, 13.36059248585088355, + -63.68463403089357655, 9.79496963802121101, -59.83111160507219495, 6.33531713186757539, + -55.89399913408760057, 2.99945241539763696, -51.85174332801064168, -0.19365520038940248, + -47.68524592294902931, -3.22375917751486485, -43.37824652323212860, -6.06930752874206636, + -38.91794868987490474, -8.70753794849322027, -34.29585521437643081, -11.11472732454917001, + -29.50874372060167872, -13.26662598462659126, -24.55966518490711081, -15.13909773195326558, + -19.45879403720245193, -16.70896594558295334, -14.22391622121717880, -17.95503295463868554, + -8.88033504279663255, -18.85919797266866027, -3.46002704385166027, -19.40755726556069405, + 1.99999999999999645, -19.59134287356426540, 7.46002704385165316, -19.40755726556069405, + 12.88033504279662367, -18.85919797266866027, 18.22391622121717347, -17.95503295463868554, + 23.45879403720244127, -16.70896594558295334, 28.55966518490710015, -15.13909773195326558, + 33.50874372060167161, -13.26662598462659126, 38.29585521437642370, -11.11472732454917534, + 42.91794868987490474, -8.70753794849322738, 47.37824652323212149, -6.06930752874207258, + 51.68524592294901510, -3.22375917751487107, 55.85174332801063457, -0.19365520038940884, + 59.89399913408759346, 2.99945241539763385, 63.83111160507218784, 6.33531713186756917, + 67.68463403089357655, 9.79496963802120568, 71.47845003929684538, 13.36059248585087822, + 75.23891693218338617, 17.01531594444593054, 78.99529352424693229, 20.74294398926492988, + 82.78048521884274180, 24.52760876444715521, 86.63216392910494790, 28.35333972343854825, + 90.59435351833845118, 32.20351875194761959, 94.71961138434764393, 36.06017287638243118, + 99.07197748871266185, 39.90302877816822047, 103.73088255981890882, 43.70821480745368603, + 108.79614728017998004, 47.44644408577497074, 114.39390839554110357, 51.08045112569504909, + 120.68240638414135901, 54.56141374911145192, 127.85428534979438098, 57.82417470821774685, + 136.12712761110682891, 60.78156430751474204, 145.70578215046154469, 63.31961305621509695, + 156.69437932769909594, 65.29863962109095610, 168.95645291666264143, 66.56908368553206401, + -178.00000000000002842, 61.16162880179430772, -166.97884079028878546, 60.79043532425956897, + -156.40162010671977555, 59.70518607311999659, -146.59283359977425221, 57.98141283097307053, + -137.70772458687926587, 55.72003625021811501, -129.75638606304184464, 53.02581189527144545, + -122.65952710171549711, 49.99368843662635697, -116.29889149413011751, 46.70342202473146642, + -110.54933935400606515, 43.21943287866469774, -105.29473060869931089, 39.59298941537671368, + -100.43366587620559471, 35.86497163386139420, -95.88003723653146437, 32.06843215731343832, + -91.56137924921046078, 28.23071203179445376, -87.41655400532016529, 24.37510563102819461, + -83.39346098727328638, 20.52214967325533124, -79.44703540885105042, 16.69062430422997423, + -75.53760403336077900, 12.89834287286322301, -71.62959031828354739, 9.16278970353470967, + -67.69053915995506543, 5.50164833296652400, -63.69043463465567356, 1.93324784086672841, + -59.60129716846515180, -1.52305829704237294, -55.39706222812606740, -4.84657887125734721, + -51.05375641269629483, -8.01504773475606314, -46.54999441522540593, -11.00448645615599474, + -41.86781613095839560, -13.78920372951384543, -36.99386004100858116, -16.34197985921976581, + -31.92081946357624034, -18.63449057116782015, -26.64904852681421588, -20.63801909702448611, + -21.18808138888132930, -22.32448212182582026, -15.55772615655294366, -23.66774871950555692, + -9.78834011215971245, -24.64516330804185174, -3.91994134356475854, -25.23910692117754806, + 1.99999999999999556, -25.43837119820565107, 7.91994134356474966, -25.23910692117754806, + 13.78834011215970534, -24.64516330804185174, 19.55772615655293123, -23.66774871950555692, + 25.18808138888131865, -22.32448212182582026, 30.64904852681421232, -20.63801909702448611, + 35.92081946357622968, -18.63449057116782015, 40.99386004100855985, -16.34197985921976581, + 45.86781613095838850, -13.78920372951385076, 50.54999441522539882, -11.00448645615600007, + 55.05375641269628773, -8.01504773475606846, 59.39706222812605318, -4.84657887125735343, + 63.60129716846514469, -1.52305829704237916, 67.69043463465565935, 1.93324784086672197, + 71.69053915995505122, 5.50164833296651778, 75.62959031828353318, 9.16278970353470434, + 79.53760403336076479, 12.89834287286321590, 83.44703540885105042, 16.69062430422996712, + 87.39346098727328638, 20.52214967325532413, 91.41655400532015108, 24.37510563102818750, + 95.56137924921046078, 28.23071203179444666, 99.88003723653145016, 32.06843215731343832, + 104.43366587620558050, 35.86497163386137288, 109.29473060869932510, 39.59298941537669947, + 114.54933935400606515, 43.21943287866469774, 120.29889149413010330, 46.70342202473146642, + 126.65952710171551132, 49.99368843662635697, 133.75638606304187306, 53.02581189527144545, + 141.70772458687926587, 55.72003625021811501, 150.59283359977422379, 57.98141283097307053, + 160.40162010671977555, 59.70518607311999659, 170.97884079028878546, 60.79043532425956897, + -178.00000000000002842, 54.75649468439711143, -168.49285933765114009, 54.43639475342744305, + -159.25249258416440057, 53.49372597518600259, -150.49218436749950456, 51.97706503648645082, + -142.34201331309026273, 49.95526128262039123, -134.84908760812987794, 47.50530446205828383, + -127.99715883033752561, 44.70273256173389598, -121.73118977168118704, 41.61600262291977259, + -115.97779813807071037, 38.30427835916992052, -110.65884380836621403, 34.81740352705595143, + -105.69899804029306267, 31.19696754875727152, -101.02917319698202903, 27.47775433026597725, + -96.58749981932022877, 23.68920100972959730, -92.31902021078286680, 19.85670886149115688, + -88.17480572169651509, 16.00276430383187076, -84.11088800598805904, 12.14788119395578825, + -80.08720437104130951, 8.31139512800513991, -76.06665407661381550, 4.51214364124032752, + -72.01431282527251199, 0.76906207426875217, -67.89683474738467339, -2.89828246890512542, + -63.68207179147747610, -6.46920580966549608, -59.33895182482493880, -9.92146778444055499, + -54.83767345094592827, -13.23089104518994574, -50.15029168417838434, -16.37106576006626568, + -45.25177543408179304, -19.31318879069117145, -40.12160159745184274, -22.02610835172851722, + -34.74589265732009835, -24.47666503668304117, -29.11998552133064422, -26.63042818683534207, + -23.25113130544623985, -28.45290874783366064, -17.16079514785620930, -29.91127029421519978, + -10.88583393507997776, -30.97645080660139172, -4.47781225763253765, -31.62546289221583962, + 1.99999999999999534, -31.84350531560284736, 8.47781225763252877, -31.62546289221583962, + 14.88583393507996711, -30.97645080660139172, 21.16079514785619509, -29.91127029421519978, + 27.25113130544623274, -28.45290874783366064, 33.11998552133064067, -26.63042818683534207, + 38.74589265732009835, -24.47666503668304117, 44.12160159745183563, -22.02610835172851722, + 49.25177543408178593, -19.31318879069118211, 54.15029168417837724, -16.37106576006627279, + 58.83767345094592827, -13.23089104518995285, 63.33895182482493169, -9.92146778444056032, + 67.68207179147748320, -6.46920580966550229, 71.89683474738467339, -2.89828246890513164, + 76.01431282527251199, 0.76906207426874584, 80.06665407661380129, 4.51214364124032308, + 84.08720437104132372, 8.31139512800513458, 88.11088800598805904, 12.14788119395578470, + 92.17480572169650088, 16.00276430383186366, 96.31902021078285259, 19.85670886149114978, + 100.58749981932022877, 23.68920100972959020, 105.02917319698201482, 27.47775433026597014, + 109.69899804029304846, 31.19696754875725375, 114.65884380836618561, 34.81740352705593722, + 119.97779813807069615, 38.30427835916992052, 125.73118977168117283, 41.61600262291977259, + 131.99715883033749719, 44.70273256173389598, 138.84908760812987794, 47.50530446205828383, + 146.34201331309026273, 49.95526128262039123, 154.49218436749950456, 51.97706503648645082, + 163.25249258416440057, 53.49372597518600259, 172.49285933765111167, 54.43639475342744305, + -178.00000000000002842, 47.72124472298390430, -169.69217969866491558, 47.44157216781381692, + -161.55074086228117380, 46.61395503653132266, -153.71667350566602295, 45.27051130755705799, + -146.28885177460344380, 43.45866798082070659, -139.31958252044236701, 41.23439386179803279, + -132.82020109922686402, 38.65601141858345358, -126.77168357131760956, 35.77969749489390949, + -121.13576891124588997, 32.65687547770472321, -115.86406835852179142, 29.33315055785289260, + -110.90442707083994378, 25.84827445710250160, -106.20481280215612685, 22.23668760419472790, + -101.71535755141638901, 18.52832281285149207, -97.38917071735673403, 14.74948212031801020, + -93.18240447461121789, 10.92369066480776674, -89.05390189983984328, 7.07248905242842607, + -84.96463936229969249, 3.21615784383777559, -80.87709425893895343, -0.62561625366165152, + -76.75462189677602964, -4.43311662588922051, -72.56090370117536281, -8.18599910867232161, + -68.25952713288315010, -11.86270778471483389, -63.81377209114523907, -15.43989911427394368, + -59.18670666190919150, -18.89188184637555068, -54.34173321358174746, -22.19009961204913850, + -49.24376548995358149, -25.30271165018387691, -43.86123932751760890, -28.19436585459229860, + -38.16912769952225659, -30.82630445836841915, -32.15298964123236658, -33.15698491696964112, + -25.81377244810833815, -35.14341202945125531, -19.17259140539265516, -36.74332279213026453, + -12.27414365511957151, -37.91820250638207312, -5.18708125479775184, -38.63683300066042392, + 1.99999999999999489, -38.87875527701606160, 9.18708125479774296, -38.63683300066042392, + 16.27414365511955907, -37.91820250638207312, 23.17259140539264095, -36.74332279213026453, + 29.81377244810832394, -35.14341202945125531, 36.15298964123235947, -33.15698491696964112, + 42.16912769952225659, -30.82630445836841915, 47.86123932751760179, -28.19436585459229860, + 53.24376548995357439, -25.30271165018388402, 58.34173321358174746, -22.19009961204914205, + 63.18670666190918439, -18.89188184637555779, 67.81377209114522486, -15.43989911427395079, + 72.25952713288313589, -11.86270778471484277, 76.56090370117536281, -8.18599910867232872, + 80.75462189677602964, -4.43311662588922673, 84.87709425893895343, -0.62561625366165630, + 88.96463936229967828, 3.21615784383776981, 93.05390189983984328, 7.07248905242842163, + 97.18240447461121789, 10.92369066480776141, 101.38917071735671982, 14.74948212031800310, + 105.71535755141637480, 18.52832281285148497, 110.20481280215611264, 22.23668760419472079, + 114.90442707083992957, 25.84827445710249449, 119.86406835852177721, 29.33315055785288550, + 125.13576891124587576, 32.65687547770470900, 130.77168357131759535, 35.77969749489390949, + 136.82020109922683559, 38.65601141858345358, 143.31958252044236701, 41.23439386179803279, + 150.28885177460347222, 43.45866798082070659, 157.71667350566602295, 45.27051130755705799, + 165.55074086228114538, 46.61395503653132266, 173.69217969866488716, 47.44157216781381692, + -178.00000000000002842, 39.98177927524727693, -170.20476659278793363, 39.70189618399165710, + -162.53782868216148927, 38.87197455633481979, -155.11038900941949237, 37.51968052984662449, + -148.00450116657361832, 35.68659928106227852, -141.26850797118672176, 33.42299645292870736, + -134.91921485966395267, 30.78272820361293327, -128.94811747553873715, 27.81921722445741452, + -123.32885205682140395, 24.58281725765701253, -118.02393786261990272, 21.11944988981388605, + -112.98994385952538266, 17.47019805768939449, -108.18096363521175363, 13.67152082594358653, + -103.55065416712271542, 9.75582109859903390, -99.05319914949195947, 5.75218534812640492, + -94.64352777118511995, 1.68719008387413827, -90.27704477012913742, -2.41427530523583656, + -85.90905356028720519, -6.52818370181667529, -81.49400025392820623, -10.63053585607600837, + -76.98463901984250413, -14.69649678201448140, -72.33122188826867216, -18.69947873486281864, + -67.48085419487966874, -22.61014977269736193, -62.37723925505212463, -26.39536140379772533, + -56.96117255247644096, -30.01702215459737033, -51.17233704527057370, -33.43100825355479344, + -44.95316063861177014, -36.58631299796492442, -38.25559890256131723, -39.42480136235259636, + -31.05142021090808413, -41.88213387768102791, -23.34546416576216643, -43.89055765093644368, + -15.18911080317680451, -45.38412606933154336, -6.68839576675081293, -46.30624543028365991, + 1.99999999999999445, -46.61822072475268186, 10.68839576675079961, -46.30624543028365991, + 19.18911080317679918, -45.38412606933154336, 27.34546416576215222, -43.89055765093644368, + 35.05142021090807702, -41.88213387768102791, 42.25559890256131013, -39.42480136235259636, + 48.95316063861175593, -36.58631299796492442, 55.17233704527057370, -33.43100825355479344, + 60.96117255247643385, -30.01702215459737744, 66.37723925505210332, -26.39536140379773244, + 71.48085419487965453, -22.61014977269736548, 76.33122188826865795, -18.69947873486282575, + 80.98463901984250413, -14.69649678201448850, 85.49400025392819202, -10.63053585607601548, + 89.90905356028720519, -6.52818370181668062, 94.27704477012913742, -2.41427530523584188, + 98.64352777118510573, 1.68719008387413361, 103.05319914949195947, 5.75218534812639781, + 107.55065416712271542, 9.75582109859902680, 112.18096363521173942, 13.67152082594358120, + 116.98994385952536845, 17.47019805768938738, 122.02393786261990272, 21.11944988981387894, + 127.32885205682140395, 24.58281725765700187, 132.94811747553873715, 27.81921722445741452, + 138.91921485966392424, 30.78272820361293327, 145.26850797118672176, 33.42299645292870736, + 152.00450116657361832, 35.68659928106227852, 159.11038900941949237, 37.51968052984662449, + 166.53782868216146085, 38.87197455633481979, 174.20476659278793363, 39.70189618399165710, + -178.00000000000002842, 31.46720374958098887, -171.12910064316457692, 31.22051723140710067, + -164.34108626834762390, 30.48695101230147841, -157.71017626763574526, 29.28521267182222587, + -151.29523174265207786, 27.64411687523926986, -145.13620894054648147, 25.59967735271197498, + -139.25381666270027381, 23.19203115841211726, -133.65157520973266969, 20.46271167815037728, + -128.31914580979304219, 17.45256126555711873, -123.23592626204657563, 14.20035582641498806, + -118.37425008640165913, 10.74206713847307348, -113.70187269530693186, 7.11062322287716331, + -109.18367588372132104, 3.33602075627005945, -104.78265670493266271, -0.55433148808729127, + -100.46031614728222792, -4.53512373056416163, -96.17656220612794016, -8.58256192749418290, + -91.88921878897831164, -12.67371220268469223, -87.55320457280681978, -16.78577161643179849, + -83.11942634360052296, -20.89524495725206421, -78.53343020155088539, -24.97699141478169338, + -73.73388667252179118, -29.00309226896919412, -68.65107920217621995, -32.94148403257255353, + -63.20576633421556068, -36.75430974219704439, -57.30916699148811233, -40.39598469592144880, + -50.86545369174167774, -43.81109166541019562, -43.77902429867569367, -46.93247902115529513, + -35.96960367385943158, -49.68040408706252720, -27.39769711559288723, -51.96422430511800172, + -18.09872704828334378, -53.68864395349498864, -8.21451927262478065, -54.76594013073759015, + 1.99999999999999289, -55.13279625041898413, 12.21451927262476822, -54.76594013073759015, + 22.09872704828332601, -53.68864395349498864, 31.39769711559288012, -51.96422430511800172, + 39.96960367385942448, -49.68040408706252720, 47.77902429867568657, -46.93247902115529513, + 54.86545369174167064, -43.81109166541019562, 61.30916699148811233, -40.39598469592146301, + 67.20576633421555357, -36.75430974219705149, 72.65107920217620574, -32.94148403257256064, + 77.73388667252179118, -29.00309226896920123, 82.53343020155088539, -24.97699141478170048, + 87.11942634360052296, -20.89524495725207132, 91.55320457280680557, -16.78577161643180560, + 95.88921878897831164, -12.67371220268469756, 100.17656220612792595, -8.58256192749419000, + 104.46031614728221371, -4.53512373056416607, 108.78265670493264849, -0.55433148808729760, + 113.18367588372132104, 3.33602075627005323, 117.70187269530691765, 7.11062322287715620, + 122.37425008640165913, 10.74206713847306816, 127.23592626204656142, 14.20035582641498095, + 132.31914580979304219, 17.45256126555711162, 137.65157520973264127, 20.46271167815036662, + 143.25381666270027381, 23.19203115841211726, 149.13620894054645305, 25.59967735271197498, + 155.29523174265207786, 27.64411687523926986, 161.71017626763577368, 29.28521267182222587, + 168.34108626834759548, 30.48695101230147841, 175.12910064316457692, 31.22051723140710067, + -178.00000000000002842, 22.11837781753018817, -170.46891148411387462, 21.78042227826199095, + -163.04151372648448159, 20.77699151155475477, -155.80845268931921055, 19.13794220572576776, + -148.83768595785252842, 16.90865838596958781, -142.17005258660481104, 14.14485028302004999, + -135.81978239976487544, 10.90731557455581147, -129.77826339847271697, 7.25759760165333567, + -124.01906809075352101, 3.25496397729521814, -118.50268451884778642, -1.04529436283411115, + -113.18006743498142441, -5.59247248797413210, -107.99465974304708027, -10.34025377357217934, + -102.88281945262124850, -15.24594064653213898, -97.77265218881962028, -20.26925377022462982, + -92.58116190590757810, -25.37072818781790673, -87.20945067766288616, -30.50961194071436111, + -81.53547443418635510, -35.64102404029979709, -75.40370496549049051, -40.71192873614232610, + -68.61131289029849256, -45.65520975111465418, -60.89227178252910733, -50.38082342875217989, + -51.90710322947828104, -54.76300160634039571, -41.26325159838054901, -58.62403790832019723, + -28.62428016143747200, -61.72174914658255318, -13.98037118830461445, -63.76341508305664263, + 1.99999999999999090, -64.48162218246977773, 17.98037118830459491, -63.76341508305664263, + 32.62428016143745424, -61.72174914658255318, 45.26325159838053480, -58.62403790832019723, + 55.90710322947827393, -54.76300160634039571, 64.89227178252910733, -50.38082342875217989, + 72.61131289029847835, -45.65520975111467550, 79.40370496549049051, -40.71192873614232610, + 85.53547443418635510, -35.64102404029981841, 91.20945067766287195, -30.50961194071436822, + 96.58116190590757810, -25.37072818781791383, 101.77265218881962028, -20.26925377022463692, + 106.88281945262124850, -15.24594064653214609, 111.99465974304708027, -10.34025377357218289, + 117.18006743498143862, -5.59247248797413921, 122.50268451884780063, -1.04529436283411759, + 128.01906809075350679, 3.25496397729521192, 133.77826339847268855, 7.25759760165332857, + 139.81978239976487544, 10.90731557455580436, 146.17005258660478262, 14.14485028302004999, + 152.83768595785250000, 16.90865838596958781, 159.80845268931921055, 19.13794220572576776, + 167.04151372648448159, 20.77699151155475477, 174.46891148411387462, 21.78042227826199095, + -178.00000000000002842, 11.90033356154828503, -171.03550378237531504, 11.56695807154089728, + -164.15259832130166728, 10.57562338306961180, -157.42382961455143686, 8.95164656051215069, + -150.90567761562240889, 6.73398631258200187, -144.63474532961075170, 3.97127289925851068, + -138.62718020698264354, 0.71762564121834338, -132.88045216690298389, -2.97103149043460757, + -127.37626453526326031, -7.03975713563777195, -122.08348616854385682, -11.43638098615609344, + -116.96030366998827787, -16.11238508810905401, -111.95506961066591600, -21.02304593053227677, + -107.00543419047215821, -26.12699149209596428, -102.03522828445495918, -31.38525381109429446, + -96.94814060146437384, -36.75977477892173084, -91.61632617812561818, -42.21112686087874266, + -85.86029859026153588, -47.69485768882572785, -79.41299264536502278, -53.15514845029933610, + -71.85455169648275842, -58.51289100414830102, -62.49613000707146426, -63.64173578517077345, + -50.20367738593296281, -68.31866526945105988, -33.33636828365784055, -72.13161644510194037, + -10.78607194201629582, -74.39304559691805707, 14.78607194201627273, -74.39304559691805707, + 37.33636828365781213, -72.13161644510194037, 54.20367738593294149, -68.31866526945105988, + 66.49613000707145716, -63.64173578517077345, 75.85455169648274421, -58.51289100414830102, + 83.41299264536502278, -53.15514845029933610, 89.86029859026150746, -47.69485768882574916, + 95.61632617812561818, -42.21112686087874266, 100.94814060146437384, -36.75977477892173084, + 106.03522828445494497, -31.38525381109429446, 111.00543419047215821, -26.12699149209597138, + 115.95506961066591600, -21.02304593053228388, 120.96030366998827787, -16.11238508810905756, + 126.08348616854387103, -11.43638098615609699, 131.37626453526326031, -7.03975713563777816, + 136.88045216690298389, -2.97103149043461379, 142.62718020698264354, 0.71762564121833694, + 148.63474532961075170, 3.97127289925851068, 154.90567761562243732, 6.73398631258200187, + 161.42382961455140844, 8.95164656051215069, 168.15259832130169571, 10.57562338306961180, + 175.03550378237531504, 11.56695807154089728, -178.00000000000002842, 0.81833312891115306, + -171.37485237931247184, 0.46152536384328174, -164.82591385569978115, -0.60006016949342578, + -158.42170502724641779, -2.34091526057736310, -152.21700460768090579, -4.72161659803607048, + -146.24942513902283281, -7.69266569608215889, -140.53869867413175143, -11.19861017149900739, + -135.08801440266989857, -15.18177122287804615, -129.88641650799127092, -19.58517424494497305, + -124.91129483912591525, -24.35455549606792047, -120.13018318901130499, -29.43951176908901957, + -115.50120511177955507, -34.79395188944583595, -110.97138028465980142, -40.37601690822956613, + -106.47134737780879732, -46.14758359783259323, -101.90317607822893820, -52.07335345715244301, + -97.11263541171206271, -58.11928872485960085, -91.82057104813961246, -64.24948773689963843, + -85.42499621356618889, -70.41813086842684299, -76.27768438369074033, -76.54083121758115738, + -57.87324357773111672, -82.33459525449163152, 1.99999999999996114, -85.78166687108868871, + 61.87324357773107408, -82.33459525449163152, 80.27768438369074033, -76.54083121758115738, + 89.42499621356618889, -70.41813086842684299, 95.82057104813959825, -64.24948773689963843, + 101.11263541171206271, -58.11928872485960085, 105.90317607822892398, -52.07335345715245722, + 110.47134737780879732, -46.14758359783259323, 114.97138028465980142, -40.37601690822956613, + 119.50120511177955507, -34.79395188944583595, 124.13018318901127657, -29.43951176908902667, + 128.91129483912592946, -24.35455549606792403, 133.88641650799127092, -19.58517424494497661, + 139.08801440266989857, -15.18177122287804970, 144.53869867413172301, -11.19861017149901272, + 150.24942513902280439, -7.69266569608215889, 156.21700460768090579, -4.72161659803607048, + 162.42170502724644621, -2.34091526057736310, 168.82591385569978115, -0.60006016949342578, + 175.37485237931247184, 0.46152536384328174, -178.00000000000000000, -11.06408152044379101, + -171.33870321353637678, -11.51266748991093891, -164.78020742076523675, -12.84550987892848539, + -158.41822389716838870, -15.02545489467462403, -152.33085313434585828, -17.99542592709287803, + -146.57821873782722832, -21.68429473238734673, -141.20455296082769792, -26.01294410398721979, + -136.24424010430539056, -30.89942669541638764, -131.73150447900647464, -36.26259289105208694, + -127.71480673751507595, -42.02394287044848653, -124.28024682035200499, -48.10755262562803125, + -121.59604777627559713, -54.43751136022502379, -120.01228809564838684, -60.93070105520797597, + -120.32470998353322500, -67.47696540683870126, -124.60898756414846389, -73.87349687490876704, + -139.22778135121401988, -79.54230031515007227, -178.00000000000000000, -82.33591847955618448, + 143.22778135121404830, -79.54230031515007227, 128.60898756414846389, -73.87349687490876704, + 124.32470998353322500, -67.47696540683870126, 124.01228809564837263, -60.93070105520797597, + 125.59604777627561134, -54.43751136022503090, 128.28024682035200499, -48.10755262562803125, + 131.71480673751509016, -42.02394287044850074, 135.73150447900647464, -36.26259289105208694, + 140.24424010430539056, -30.89942669541638764, 145.20455296082769792, -26.01294410398722334, + 150.57821873782722832, -21.68429473238734673, 156.33085313434585828, -17.99542592709287803, + 162.41822389716838870, -15.02545489467462403, 168.78020742076520833, -12.84550987892848539, + 175.33870321353637678, -11.51266748991093891, -178.00000000000000000, -23.60656536858034116, + -172.31484199740026497, -24.06074499849094650, -166.76206145225543764, -25.40835290264588053, + -161.47033176340409000, -27.60594661847371611, -156.56387974300261590, -30.58514629912493277, + -152.16715015384193066, -34.25717258827405942, -148.41689072479888978, -38.51667916926085411, + -145.48413942807309240, -43.24305393544465659, -143.61056018478930696, -48.29703252527734492, + -143.16669319413330186, -53.50931894776815057, -144.73891790293430404, -58.65522080377154168, + -149.21912766680259210, -63.40582055258879279, -157.69810487935359333, -67.25441721659638006, + -170.53118545218148938, -69.49384617858525814, 174.53118545218146096, -69.49384617858525814, + 161.69810487935359333, -67.25441721659638006, 153.21912766680256368, -63.40582055258877858, + 148.73891790293430404, -58.65522080377152747, 147.16669319413330186, -53.50931894776815767, + 147.61056018478930696, -48.29703252527734492, 149.48413942807309240, -43.24305393544464948, + 152.41689072479886136, -38.51667916926086122, 156.16715015384193066, -34.25717258827403811, + 160.56387974300261590, -30.58514629912493277, 165.47033176340409000, -27.60594661847371611, + 170.76206145225546607, -25.40835290264587698, 176.31484199740026497, -24.06074499849094295, + -178.00000000000000000, -36.54741287921620341, -174.09065200901946469, -36.97055608025468132, + -170.42266827604018431, -38.21219158951615213, -167.24101930549508666, -40.18968456371464271, + -164.79950790329061761, -42.76713193279059766, -163.36621335583259906, -45.75617589753742465, + -163.22080918295719698, -48.91487419033276751, -164.62326642581021474, -51.94637057143930292, + -167.72066876086483944, -54.50623527875517738, -172.37525674045562596, -56.23764348052387874, + -178.00000000000000000, -56.85258712078380228, 176.37525674045562596, -56.23764348052387874, + 171.72066876086483944, -54.50623527875517738, 168.62326642581021474, -51.94637057143930292, + 167.22080918295719698, -48.91487419033276751, 167.36621335583259906, -45.75617589753742465, + 168.79950790329058918, -42.76713193279060476, 171.24101930549508666, -40.18968456371464271, + 174.42266827604018431, -38.21219158951615213, 178.09065200901943626, -36.97055608025468132}; + +}; + +namespace atlas { +namespace test { +CASE( "t31c2.4" ) { + atlas::StructuredGrid grid; + atlas::Projection proj; + + + const std::vector nx = {20, 27, 32, 40, 45, 48, 60, 60, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 60, 60, 48, 45, 40, 32, 27, 20}; + + const double stretch = 2.4, centre[2] = {2.0, 46.7}; + + std::vector spacings( nx.size() ); + + for ( int i = 0; i < nx.size(); i++ ) { + double lonmax = 360.0 * double( nx[i] - 1 ) / double( nx[i] ); + spacings[i] = + atlas::grid::Spacing( atlas::util::Config( "type", "linear" ) | atlas::util::Config( "N", nx[i] ) | + atlas::util::Config( "start", 0 ) | atlas::util::Config( "end", lonmax ) ); + } + + atlas::StructuredGrid::XSpace xspace( spacings ); + atlas::StructuredGrid::YSpace yspace( atlas::util::Config( "type", "gaussian" ) | + atlas::util::Config( "N", nx.size() ) ); + + proj = atlas::Projection( atlas::util::Config( "type", "rotated_schmidt" ) | + atlas::util::Config( "stretching_factor", stretch ) | + atlas::util::Config( "rotation_angle", 180.0 ) | + atlas::util::Config( "north_pole", std::vector{centre[0], centre[1]} ) ); + + grid = atlas::StructuredGrid( xspace, yspace, proj, atlas::Domain( atlas::util::Config( "type", "global" ) ) ); + + for ( int j = 0, jglo = 0; j < grid.ny(); j++ ) + for ( int i = 0; i < grid.nx( j ); i++, jglo++ ) { + auto ll2 = grid.lonlat( i, j ); + PointXYZ p1, p2; + PointLonLat ll1 = PointLonLat( lonlat_arp_t32c24[2 * jglo + 0], lonlat_arp_t32c24[2 * jglo + 1] ); + atlas::util::UnitSphere::convertSphericalToCartesian( ll1, p1 ); + atlas::util::UnitSphere::convertSphericalToCartesian( ll2, p2 ); + EXPECT( is_approximately_equal( ll1.lon(), ll2.lon(), 0.001 ) ); + EXPECT( is_approximately_equal( ll1.lat(), ll2.lat(), 0.001 ) ); + } +} +} // namespace test +} // namespace atlas + + +int main( int argc, char* argv[] ) { + return atlas::test::run( argc, argv ); +} diff --git a/src/tests/projection/test_rotation.cc b/src/tests/projection/test_rotation.cc index d9476a399..7ee792455 100644 --- a/src/tests/projection/test_rotation.cc +++ b/src/tests/projection/test_rotation.cc @@ -120,6 +120,49 @@ class MagicsRotation { //----------------------------------------------------------------------------- + +CASE( "test_rotation_angle" ) { + const int nx = 12, ny = 6; + + Rotation rot( Config( "rotation_angle", 180. ) | Config( "north_pole", std::vector{2.0, 46.7} ) ); + + const PointLonLat ref[] = { + {-178.00000, -46.70000}, {-178.00000, -16.70000}, {-178.00000, 13.30000}, {-178.00000, 43.30000}, + {-178.00000, 73.30000}, {2.00000, 76.70000}, {2.00000, 46.70000}, {-178.00000, -46.70000}, + {-162.62343, -19.46929}, {-152.02366, 8.65459}, {-139.57464, 36.43683}, {-113.10894, 61.43199}, + {-39.88245, 68.00825}, {2.00000, 46.70000}, {-178.00000, -46.70000}, {-148.83443, -27.31067}, + {-129.26346, -3.83700}, {-110.79116, 20.05422}, {-85.87917, 41.36507}, {-44.42496, 53.29508}, + {2.00000, 46.70000}, {-178.00000, -46.70000}, {-137.90794, -39.07002}, {-109.60146, -21.33906}, + {-88.00000, 0.00000}, {-66.39854, 21.33906}, {-38.09206, 39.07002}, {2.00000, 46.70000}, + {-178.00000, -46.70000}, {-131.57504, -53.29508}, {-90.12083, -41.36507}, {-65.20884, -20.05422}, + {-46.73654, 3.83700}, {-27.16557, 27.31067}, {2.00000, 46.70000}, {-178.00000, -46.70000}, + {-136.11755, -68.00825}, {-62.89106, -61.43199}, {-36.42536, -36.43683}, {-23.97634, -8.65459}, + {-13.37657, 19.46929}, {2.00000, 46.70000}, {-178.00000, -46.70000}, {-178.00000, -76.70000}, + {2.00000, -73.30000}, {2.00000, -43.30000}, {2.00000, -13.30000}, {2.00000, 16.70000}, + {2.00000, 46.70000}, {-178.00000, -46.70000}, {140.11755, -68.00825}, {66.89106, -61.43199}, + {40.42536, -36.43683}, {27.97634, -8.65459}, {17.37657, 19.46929}, {2.00000, 46.70000}, + {-178.00000, -46.70000}, {135.57504, -53.29508}, {94.12083, -41.36507}, {69.20884, -20.05422}, + {50.73654, 3.83700}, {31.16557, 27.31067}, {2.00000, 46.70000}, {-178.00000, -46.70000}, + {141.90794, -39.07002}, {113.60146, -21.33906}, {92.00000, 0.00000}, {70.39854, 21.33906}, + {42.09206, 39.07002}, {2.00000, 46.70000}, {-178.00000, -46.70000}, {152.83443, -27.31067}, + {133.26346, -3.83700}, {114.79116, 20.05422}, {89.87917, 41.36507}, {48.42496, 53.29508}, + {2.00000, 46.70000}, {-178.00000, -46.70000}, {166.62343, -19.46929}, {156.02366, 8.65459}, + {143.57464, 36.43683}, {117.10894, 61.43199}, {43.88245, 68.00825}, {2.00000, 46.70000}, + }; + + + for ( int i = 0, jglo = 0; i < nx; i++ ) + for ( int j = 0; j < ny + 1; j++, jglo++ ) { + double lon = static_cast( i ) * 360. / static_cast( nx ); + double lat = static_cast( j - ny / 2 ) * 90. / static_cast( ny / 2 ); + PointLonLat p0( lon, lat ); + PointLonLat p1 = rot.rotate( p0 ); + PointLonLat p2 = rot.unrotate( p1 ); + EXPECT( equivalent( p0, p2 ) ); + EXPECT( equivalent( p1, ref[jglo] ) ); + } +} + CASE( "test_rotation_construction" ) { static const PointLonLat SP{0., -90.}; static const PointLonLat NP{180., 90.}; From 59e75d3142fe361b4c026bc9607f39eba846ae8a Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 4 May 2020 17:34:00 +0100 Subject: [PATCH 045/145] ATLAS-279 Workaround OpenMP tasking bug (with Clang) using compiler introspection --- cmake/features/OMP.cmake | 35 ++++++++++++++++++ cmake/features/OMP/test_omp_untied.cc | 51 +++++++++++++++++++++++++++ src/atlas/library/defines.h.in | 1 + src/atlas/parallel/omp/sort.h | 10 ------ 4 files changed, 87 insertions(+), 10 deletions(-) create mode 100644 cmake/features/OMP/test_omp_untied.cc diff --git a/cmake/features/OMP.cmake b/cmake/features/OMP.cmake index 35931c688..f7cbb8e6e 100644 --- a/cmake/features/OMP.cmake +++ b/cmake/features/OMP.cmake @@ -22,3 +22,38 @@ endif() if( TARGET OpenMP::OpenMP_Fortran ) set( OMP_Fortran OpenMP::OpenMP_Fortran ) endif() + +if( HAVE_OMP_CXX ) + + if( NOT CMAKE_CXX_COMPILER_ID MATCHES Clang ) + set( ATLAS_OMP_TASK_UNTIED_SUPPORTED 1 ) + endif() + + if( NOT DEFINED ATLAS_OMP_TASK_UNTIED_SUPPORTED ) + try_run( execute_result compile_result + ${CMAKE_CURRENT_BINARY_DIR} + ${PROJECT_SOURCE_DIR}/cmake/features/OMP/test_omp_untied.cc + LINK_LIBRARIES ${OMP_CXX} + COMPILE_OUTPUT_VARIABLE compile_output + RUN_OUTPUT_VARIABLE execute_output ) + + ecbuild_debug("Compiling and running ${PROJECT_SOURCE_DIR}/cmake/features/OMP/test_omp_untied.cc") + ecbuild_debug_var( compile_result ) + ecbuild_debug_var( compile_output ) + ecbuild_debug_var( execute_result ) + ecbuild_debug_var( execute_output ) + + if( compile_result ) + if( execute_result MATCHES 0 ) + set( ATLAS_OMP_TASK_UNTIED_SUPPORTED 1 ) + else() + ecbuild_info(" Compiler failed to run program with omp pragma with 'untied if' construct." + "Workaround will be enabled.") + set( ATLAS_OMP_TASK_UNTIED_SUPPORTED 0 ) + endif() + else() + set( ATLAS_OMP_TASK_UNTIED_SUPPORTED 0 ) + endif() + endif() + +endif() \ No newline at end of file diff --git a/cmake/features/OMP/test_omp_untied.cc b/cmake/features/OMP/test_omp_untied.cc new file mode 100644 index 000000000..3ef19bed0 --- /dev/null +++ b/cmake/features/OMP/test_omp_untied.cc @@ -0,0 +1,51 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + + +// This test mirrors functionality from src/atlas/parallel/omp/sort.h +// It appears that on some installations / compilers the "untied if" in the +// openmp pragma compiles but leads to runtime errors. This executable is +// added to allow compiler introspection for this feature. + +#include +#include +#include + +template +void merge_sort_recursive( const RandomAccessIterator& iterator, size_t begin, size_t end ) { + auto size = end - begin; + if ( size >= 2 ) { // should be much larger in real case (e.g. 256) + auto mid = begin + size / 2; + { +#pragma omp task shared( iterator ) untied if ( size >= ( 1 << 15 ) ) + merge_sort_recursive( iterator, begin, mid ); +#pragma omp task shared( iterator ) untied if ( size >= ( 1 << 15 ) ) + merge_sort_recursive( iterator, mid, end ); +#pragma omp taskwait + } + std::inplace_merge( iterator + begin, iterator + mid, iterator + end ); + } + else { + std::sort( iterator + begin, iterator + end ); + } +} + +template +void omp_sort( RandomAccessIterator first, RandomAccessIterator last ) { +#pragma omp parallel +#pragma omp single + merge_sort_recursive( first, 0, std::distance( first, last ) ); +} + +int main() { + auto integers = std::vector(8); + omp_sort( integers.begin(), integers.end() ); + return 0; +} diff --git a/src/atlas/library/defines.h.in b/src/atlas/library/defines.h.in index ee128bb41..894f632d0 100644 --- a/src/atlas/library/defines.h.in +++ b/src/atlas/library/defines.h.in @@ -15,6 +15,7 @@ #define atlas_library_defines_h #define ATLAS_HAVE_OMP @atlas_HAVE_OMP_CXX@ +#define ATLAS_OMP_TASK_UNTIED_SUPPORTED @ATLAS_OMP_TASK_UNTIED_SUPPORTED@ #define ATLAS_HAVE_ACC @atlas_HAVE_ACC@ #define ATLAS_HAVE_TESSELATION @atlas_HAVE_TESSELATION@ #define ATLAS_HAVE_FORTRAN @atlas_HAVE_FORTRAN@ diff --git a/src/atlas/parallel/omp/sort.h b/src/atlas/parallel/omp/sort.h index 480009b02..e782de673 100644 --- a/src/atlas/parallel/omp/sort.h +++ b/src/atlas/parallel/omp/sort.h @@ -32,16 +32,6 @@ #endif #endif -#ifndef ATLAS_OMP_TASK_UNTIED_SUPPORTED -#define ATLAS_OMP_TASK_UNTIED_SUPPORTED 1 -#endif -#ifdef __clang__ -#if __clang_major__ <= 7 -#undef ATLAS_OMP_TASK_UNTIED_SUPPORTED -#define ATLAS_OMP_TASK_UNTIED_SUPPORTED 0 -#endif -#endif - namespace atlas { namespace omp { From 6d1d4fa63c511392e701e9b2da8e9e89e1b2412d Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 1 May 2020 18:08:38 +0100 Subject: [PATCH 046/145] Remove spurious debug output --- src/atlas_f/mesh/atlas_Connectivity_module.F90 | 1 - 1 file changed, 1 deletion(-) diff --git a/src/atlas_f/mesh/atlas_Connectivity_module.F90 b/src/atlas_f/mesh/atlas_Connectivity_module.F90 index fc341aedb..e476dec79 100644 --- a/src/atlas_f/mesh/atlas_Connectivity_module.F90 +++ b/src/atlas_f/mesh/atlas_Connectivity_module.F90 @@ -543,7 +543,6 @@ subroutine set_access(this) call c_f_pointer(ctxt,this%access) else allocate( this%access ) - write(0,*) "allocate access ", loc(this%access) call atlas__connectivity__register_ctxt ( this%CPTR_PGIBUG_A, c_loc(this%access) ) call atlas__connectivity__register_update( this%CPTR_PGIBUG_A, c_funloc(update_access_c) ) call atlas__connectivity__register_delete( this%CPTR_PGIBUG_A, c_funloc(delete_access_c) ) From 5f2bad36b71bfda4f5f2e132729de6d48348eb2f Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 4 May 2020 11:54:38 +0000 Subject: [PATCH 047/145] ATLAS-278 Fix unregistration in NodeColumnsHaloExchangeCache --- src/atlas/functionspace/NodeColumns.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/atlas/functionspace/NodeColumns.cc b/src/atlas/functionspace/NodeColumns.cc index b491489bb..a0b37d9a3 100644 --- a/src/atlas/functionspace/NodeColumns.cc +++ b/src/atlas/functionspace/NodeColumns.cc @@ -85,7 +85,7 @@ class NodeColumnsHaloExchangeCache : public util::Cache Date: Fri, 1 May 2020 18:10:59 +0100 Subject: [PATCH 048/145] ATLAS-277 Fixes to fvm::Nabla for surface fields --- src/atlas/numerics/fvm/Nabla.cc | 37 +++--- src/tests/AtlasTestEnvironment.h | 33 +++++ src/tests/numerics/CMakeLists.txt | 14 +- src/tests/numerics/test_fvm_nabla.cc | 186 ++++++++++++++++++++------- 4 files changed, 205 insertions(+), 65 deletions(-) diff --git a/src/atlas/numerics/fvm/Nabla.cc b/src/atlas/numerics/fvm/Nabla.cc index 39649faeb..852743ee3 100644 --- a/src/atlas/numerics/fvm/Nabla.cc +++ b/src/atlas/numerics/fvm/Nabla.cc @@ -92,10 +92,6 @@ void Nabla::gradient_of_scalar( const Field& scalar_field, Field& grad_field ) c const idx_t nnodes = fvm_->node_columns().nb_nodes(); const idx_t nedges = fvm_->edge_columns().nb_edges(); - const idx_t nlev = scalar_field.levels() ? scalar_field.levels() : 1; - if ( ( grad_field.levels() ? grad_field.levels() : 1 ) != nlev ) { - throw_AssertionFailed( "gradient field should have same number of levels", Here() ); - } const auto scalar = scalar_field.levels() ? array::make_view( scalar_field ).slice( Range::all(), Range::all() ) @@ -104,6 +100,11 @@ void Nabla::gradient_of_scalar( const Field& scalar_field, Field& grad_field ) c ? array::make_view( grad_field ).slice( Range::all(), Range::all(), Range::all() ) : array::make_view( grad_field ).slice( Range::all(), Range::dummy(), Range::all() ); + const idx_t nlev = scalar.shape( 1 ); + if ( grad.shape( 1 ) != nlev ) { + throw_AssertionFailed( "gradient field should have same number of levels", Here() ); + } + const auto lonlat_deg = array::make_view( nodes.lonlat() ); const auto dual_volumes = array::make_view( nodes.field( "dual_volumes" ) ); const auto dual_normals = array::make_view( edges.field( "dual_normals" ) ); @@ -144,6 +145,7 @@ void Nabla::gradient_of_scalar( const Field& scalar_field, Field& grad_field ) c } } } + const double y = lonlat_deg( jnode, LAT ) * deg2rad; const double metric_y = 1. / ( dual_volumes( jnode ) * scale ); const double metric_x = metric_y / std::cos( y ); @@ -167,10 +169,6 @@ void Nabla::gradient_of_vector( const Field& vector_field, Field& grad_field ) c const idx_t nnodes = fvm_->node_columns().nb_nodes(); const idx_t nedges = fvm_->edge_columns().nb_edges(); - const idx_t nlev = vector_field.levels(); - if ( vector_field.levels() != nlev ) { - throw_AssertionFailed( "gradient field should have same number of levels", Here() ); - } const auto vector = vector_field.levels() @@ -180,6 +178,11 @@ void Nabla::gradient_of_vector( const Field& vector_field, Field& grad_field ) c ? array::make_view( grad_field ).slice( Range::all(), Range::all(), Range::all() ) : array::make_view( grad_field ).slice( Range::all(), Range::dummy(), Range::all() ); + const idx_t nlev = vector.shape( 1 ); + if ( grad.shape( 1 ) != nlev ) { + throw_AssertionFailed( "gradient field should have same number of levels", Here() ); + } + const auto lonlat_deg = array::make_view( nodes.lonlat() ); const auto dual_volumes = array::make_view( nodes.field( "dual_volumes" ) ); const auto dual_normals = array::make_view( edges.field( "dual_normals" ) ); @@ -274,10 +277,6 @@ void Nabla::divergence( const Field& vector_field, Field& div_field ) const { const idx_t nnodes = fvm_->node_columns().nb_nodes(); const idx_t nedges = fvm_->edge_columns().nb_edges(); - const idx_t nlev = vector_field.levels(); - if ( div_field.levels() != nlev ) { - throw_AssertionFailed( "divergence field should have same number of levels", Here() ); - } const auto vector = vector_field.levels() @@ -286,6 +285,11 @@ void Nabla::divergence( const Field& vector_field, Field& div_field ) const { auto div = div_field.levels() ? array::make_view( div_field ).slice( Range::all(), Range::all() ) : array::make_view( div_field ).slice( Range::all(), Range::dummy() ); + const idx_t nlev = vector.shape( 1 ); + if ( div.shape( 1 ) != nlev ) { + throw_AssertionFailed( "div_field should have same number of levels", Here() ); + } + const auto lonlat_deg = array::make_view( nodes.lonlat() ); const auto dual_volumes = array::make_view( nodes.field( "dual_volumes" ) ); const auto dual_normals = array::make_view( edges.field( "dual_normals" ) ); @@ -359,10 +363,6 @@ void Nabla::curl( const Field& vector_field, Field& curl_field ) const { const idx_t nnodes = fvm_->node_columns().nb_nodes(); const idx_t nedges = fvm_->edge_columns().nb_edges(); - const idx_t nlev = vector_field.levels(); - if ( curl_field.levels() != nlev ) { - throw_AssertionFailed( "curl field should have same number of levels", Here() ); - } const auto vector = vector_field.levels() @@ -371,6 +371,11 @@ void Nabla::curl( const Field& vector_field, Field& curl_field ) const { auto curl = curl_field.levels() ? array::make_view( curl_field ).slice( Range::all(), Range::all() ) : array::make_view( curl_field ).slice( Range::all(), Range::dummy() ); + const idx_t nlev = vector.shape( 1 ); + if ( curl.shape( 1 ) != nlev ) { + throw_AssertionFailed( "curl field should have same number of levels", Here() ); + } + const auto lonlat_deg = array::make_view( nodes.lonlat() ); const auto dual_volumes = array::make_view( nodes.field( "dual_volumes" ) ); const auto dual_normals = array::make_view( edges.field( "dual_normals" ) ); diff --git a/src/tests/AtlasTestEnvironment.h b/src/tests/AtlasTestEnvironment.h index d8f0a7834..3254d1df4 100644 --- a/src/tests/AtlasTestEnvironment.h +++ b/src/tests/AtlasTestEnvironment.h @@ -84,6 +84,10 @@ using eckit::types::is_approximately_equal; #ifdef EXPECT_EQ #undef EXPECT_EQ #endif +#ifdef EXPECT_APPROX_EQ +#undef EXPECT_APPROX_EQ +#endif + #define EXPECT_EQ( lhs, rhs ) \ do { \ if ( !( lhs == rhs ) ) { \ @@ -96,6 +100,35 @@ using eckit::types::is_approximately_equal; } \ } while ( false ) +#define __EXPECT_APPROX_EQ( lhs, rhs ) \ + do { \ + if ( !( is_approximately_equal( lhs, rhs ) ) ) { \ + std::stringstream err; \ + err << "EXPECT condition failed: " #lhs " ~= " #rhs \ + "\n" \ + " --> " \ + << lhs << " != " << rhs; \ + throw eckit::testing::TestException( err.str(), Here() ); \ + } \ + } while ( false ) + +#define __EXPECT_APPROX_EQ_TOL( lhs, rhs, tol ) \ + do { \ + if ( !( is_approximately_equal( lhs, rhs, tol ) ) ) { \ + std::stringstream err; \ + err << "EXPECT condition failed: " #lhs " ~= " #rhs \ + "\n" \ + " --> " \ + << lhs << " != " << rhs; \ + throw eckit::testing::TestException( err.str(), Here() ); \ + } \ + } while ( false ) + +#define EXPECT_APPROX_EQ( ... ) __ATLAS_SPLICE( __EXPECT_APPROX_EQ__, __ATLAS_NARG( __VA_ARGS__ ) )( __VA_ARGS__ ) +#define __EXPECT_APPROX_EQ__2 __EXPECT_APPROX_EQ +#define __EXPECT_APPROX_EQ__3 __EXPECT_APPROX_EQ_TOL + + //---------------------------------------------------------------------------------------------------------------------- static double ATLAS_MPI_BARRIER_TIMEOUT() { diff --git a/src/tests/numerics/CMakeLists.txt b/src/tests/numerics/CMakeLists.txt index 9995f5cba..15e9f1860 100644 --- a/src/tests/numerics/CMakeLists.txt +++ b/src/tests/numerics/CMakeLists.txt @@ -6,9 +6,21 @@ # granted to it by virtue of its status as an intergovernmental organisation nor # does it submit to any jurisdiction. -ecbuild_add_test( TARGET atlas_test_fvm_nabla +ecbuild_add_executable( TARGET atlas_test_fvm_nabla_exe SOURCES test_fvm_nabla.cc LIBS atlas ${OMP_CXX} + NOINSTALL +) + +ecbuild_add_test( TARGET atlas_test_fvm_nabla_L0 + COMMAND atlas_test_fvm_nabla_exe + ARGS "--levels 0" + ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} +) + +ecbuild_add_test( TARGET atlas_test_fvm_nabla_L5 + COMMAND atlas_test_fvm_nabla_exe + ARGS "--levels 5" ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) diff --git a/src/tests/numerics/test_fvm_nabla.cc b/src/tests/numerics/test_fvm_nabla.cc index 5410d0b10..b26154074 100644 --- a/src/tests/numerics/test_fvm_nabla.cc +++ b/src/tests/numerics/test_fvm_nabla.cc @@ -9,8 +9,11 @@ */ #include +#include #include +#include "eckit/config/Resource.h" + #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" @@ -40,6 +43,42 @@ using namespace atlas::grid; namespace atlas { namespace test { + +int test_levels() { + static int levels = []() { + int levels = eckit::Resource( "--levels", 0 ); + Log::info() << "levels = " << levels << std::endl; + return levels; + }(); + return levels; +} + +static std::string griduid() { + return "Slat20"; +} + +array::LocalView make_vectorview( Field& field ) { + using array::Range; + return field.levels() ? array::make_view( field ).slice( Range::all(), Range::all(), Range::all() ) + : array::make_view( field ).slice( Range::all(), Range::dummy(), Range::all() ); +} + +array::LocalView make_scalarview( Field& field ) { + using array::Range; + return field.levels() ? array::make_view( field ).slice( Range::all(), Range::all() ) + : array::make_view( field ).slice( Range::all(), Range::dummy() ); +} + + +#define print_min_max_mean( name ) \ + do { \ + Log::info() << #name << std::endl; \ + Log::info() << std::setprecision( 18 ) << " min " << min << std::endl; \ + Log::info() << std::setprecision( 18 ) << " max " << max << std::endl; \ + Log::info() << std::setprecision( 18 ) << " mean " << mean << std::endl; \ + } while ( 0 ) + + //----------------------------------------------------------------------------- double dual_volume( const Mesh& mesh ) { @@ -68,16 +107,17 @@ void rotated_flow( const fvm::Method& fvm, Field& field, const double& beta ) { const double deg2rad = M_PI / 180.; auto lonlat_deg = array::make_view( fvm.mesh().nodes().lonlat() ); - auto var = array::make_view( field ); + auto var = make_vectorview( field ); - idx_t nnodes = fvm.mesh().nodes().size(); + const idx_t nnodes = fvm.mesh().nodes().size(); + const idx_t nlev = var.shape( 1 ); for ( idx_t jnode = 0; jnode < nnodes; ++jnode ) { double x = lonlat_deg( jnode, LON ) * deg2rad; double y = lonlat_deg( jnode, LAT ) * deg2rad; double Ux = pvel * ( std::cos( beta ) + std::tan( y ) * std::cos( x ) * std::sin( beta ) ) * radius * std::cos( y ); double Uy = -pvel * std::sin( x ) * std::sin( beta ) * radius; - for ( idx_t jlev = 0; jlev < field.levels(); ++jlev ) { + for ( idx_t jlev = 0; jlev < nlev; ++jlev ) { var( jnode, jlev, LON ) = Ux; var( jnode, jlev, LAT ) = Uy; } @@ -93,25 +133,22 @@ void rotated_flow_magnitude( const fvm::Method& fvm, Field& field, const double& const double deg2rad = M_PI / 180.; auto lonlat_deg = array::make_view( fvm.mesh().nodes().lonlat() ); - auto var = array::make_view( field ); + auto var = make_scalarview( field ); - idx_t nnodes = fvm.mesh().nodes().size(); + const idx_t nnodes = fvm.mesh().nodes().size(); + const idx_t nlev = var.shape( 1 ); for ( idx_t jnode = 0; jnode < nnodes; ++jnode ) { double x = lonlat_deg( jnode, LON ) * deg2rad; double y = lonlat_deg( jnode, LAT ) * deg2rad; double Ux = pvel * ( std::cos( beta ) + std::tan( y ) * std::cos( x ) * std::sin( beta ) ) * radius * std::cos( y ); double Uy = -pvel * std::sin( x ) * std::sin( beta ) * radius; - for ( idx_t jlev = 0; jlev < field.levels(); ++jlev ) { + for ( idx_t jlev = 0; jlev < nlev; ++jlev ) { var( jnode, jlev ) = std::sqrt( Ux * Ux + Uy * Uy ); } } } -static std::string griduid() { - return "Slat20"; -} - //----------------------------------------------------------------------------- CASE( "test_factory" ) { @@ -132,15 +169,15 @@ CASE( "test_build" ) { CASE( "test_grad" ) { Log::info() << "test_grad" << std::endl; - idx_t nlev = 1; auto radius = option::radius( "Earth" ); Grid grid( griduid() ); MeshGenerator meshgenerator( "structured" ); Mesh mesh = meshgenerator.generate( grid, Distribution( grid, Partitioner( "equal_regions" ) ) ); - fvm::Method fvm( mesh, radius | option::levels( nlev ) ); + fvm::Method fvm( mesh, radius | option::levels( test_levels() ) ); Nabla nabla( fvm ); idx_t nnodes = mesh.nodes().size(); + idx_t nlev = std::max( 1, test_levels() ); FieldSet fields; fields.add( fvm.node_columns().createField( option::name( "scalar" ) ) ); @@ -152,36 +189,22 @@ CASE( "test_grad" ) { fields.add( fvm.node_columns().createField( option::name( "rxder" ) ) ); fields.add( fvm.node_columns().createField( option::name( "ryder" ) ) ); - EXPECT( fields["scalar"].rank() == 2 ); - EXPECT( fields["grad"].rank() == 3 ); - EXPECT( fields["scalar"].levels() == nlev ); - EXPECT( fields["grad"].levels() == nlev ); - // fields.add( fvm.createField("exact_yder",nlev) ); - - // const double deg2rad = M_PI/180.; - // array::ArrayView var( fields["scalar"] ); - //// array::ArrayView exact_yder( fields["exact_yder"] ); - // for( size_t jnode=0; jnode< nnodes ; ++jnode ) - // { - // const double y = lonlat(jnode,LAT) * deg2rad ; - - // for(size_t jlev = 0; jlev < nlev; ++jlev) { - // var(jnode,jlev) = std::sin(4.*y); - //// exact_yder(jnode,jlev) = 4.*std::cos(4.*y)/radius; - // } - // } + EXPECT( fields["scalar"].rank() == ( test_levels() ? 2 : 1 ) ); + EXPECT( fields["grad"].rank() == fields["scalar"].rank() + 1 ); + EXPECT( fields["scalar"].levels() == test_levels() ); + EXPECT( fields["grad"].levels() == test_levels() ); rotated_flow_magnitude( fvm, fields["scalar"], 0. ); rotated_flow_magnitude( fvm, fields["rscalar"], M_PI_2 * 0.75 ); nabla.gradient( fields["scalar"], fields["grad"] ); nabla.gradient( fields["rscalar"], fields["rgrad"] ); - auto xder = array::make_view( fields["xder"] ); - auto yder = array::make_view( fields["yder"] ); - auto rxder = array::make_view( fields["rxder"] ); - auto ryder = array::make_view( fields["ryder"] ); - const auto grad = array::make_view( fields["grad"] ); - const auto rgrad = array::make_view( fields["rgrad"] ); + auto xder = make_scalarview( fields["xder"] ); + auto yder = make_scalarview( fields["yder"] ); + auto rxder = make_scalarview( fields["rxder"] ); + auto ryder = make_scalarview( fields["ryder"] ); + const auto grad = make_vectorview( fields["grad"] ); + const auto rgrad = make_vectorview( fields["rgrad"] ); for ( idx_t jnode = 0; jnode < nnodes; ++jnode ) { for ( idx_t jlev = 0; jlev < nlev; ++jlev ) { xder( jnode, jlev ) = grad( jnode, jlev, LON ); @@ -203,17 +226,52 @@ CASE( "test_grad" ) { gmsh_fields.write( fields["rxder"] ); gmsh_fields.write( fields["ryder"] ); } + + double min, max, mean; + int N; + + fvm.node_columns().minimum( fields["xder"], min ); + fvm.node_columns().maximum( fields["xder"], max ); + fvm.node_columns().mean( fields["xder"], mean, N ); + print_min_max_mean( "xder" ); + EXPECT_APPROX_EQ( min, 0., 1.e-20 ); + EXPECT_APPROX_EQ( max, 0., 1.e-20 ); + EXPECT_APPROX_EQ( mean, 0., 1.e-20 ); + + fvm.node_columns().minimum( fields["yder"], min ); + fvm.node_columns().maximum( fields["yder"], max ); + fvm.node_columns().mean( fields["yder"], mean, N ); + print_min_max_mean( "yder" ); + EXPECT_APPROX_EQ( min, -3.1141489788326316614e-06 ); + EXPECT_APPROX_EQ( max, 3.1141489788326316614e-06 ); + EXPECT_APPROX_EQ( mean, 0., 1.e-20 ); + + + fvm.node_columns().minimum( fields["rxder"], min ); + fvm.node_columns().maximum( fields["rxder"], max ); + fvm.node_columns().mean( fields["rxder"], mean, N ); + print_min_max_mean( "rxder" ); + EXPECT_APPROX_EQ( min, -3.02863817262107e-06 ); + EXPECT_APPROX_EQ( max, +3.02863817262107e-06 ); + EXPECT_APPROX_EQ( mean, 0., 1.e-20 ); + + fvm.node_columns().minimum( fields["ryder"], min ); + fvm.node_columns().maximum( fields["ryder"], max ); + fvm.node_columns().mean( fields["ryder"], mean, N ); + print_min_max_mean( "ryder" ); + EXPECT_APPROX_EQ( min, -3.114148978832633e-06 ); + EXPECT_APPROX_EQ( max, +3.114148978832633e-06 ); + EXPECT_APPROX_EQ( mean, 0., 1.e-20 ); } CASE( "test_div" ) { Log::info() << "test_div" << std::endl; - size_t nlev = 1; const double radius = util::Earth::radius(); // const double radius = 1.; Grid grid( griduid() ); MeshGenerator meshgenerator( "structured" ); Mesh mesh = meshgenerator.generate( grid, Distribution( grid, Partitioner( "equal_regions" ) ) ); - fvm::Method fvm( mesh, util::Config( "radius", radius ) | option::levels( nlev ) ); + fvm::Method fvm( mesh, util::Config( "radius", radius ) | option::levels( test_levels() ) ); Nabla nabla( fvm ); FieldSet fields; @@ -231,17 +289,28 @@ CASE( "test_div" ) { gmsh.write( fields["wind"] ); gmsh.write( fields["div"] ); } + + double min, max, mean; + int N; + fvm.node_columns().minimum( fields["div"], min ); + fvm.node_columns().maximum( fields["div"], max ); + fvm.node_columns().mean( fields["div"], mean, N ); + + // Divergence free flow! + print_min_max_mean( "div" ); + EXPECT_APPROX_EQ( min, 0., 1.e-18 ); + EXPECT_APPROX_EQ( max, 0., 1.e-18 ); + EXPECT_APPROX_EQ( mean, 0., 1.e-20 ); } CASE( "test_curl" ) { Log::info() << "test_curl" << std::endl; - size_t nlev = 1; const double radius = util::Earth::radius(); // const double radius = 1.; Grid grid( griduid() ); MeshGenerator meshgenerator( "structured" ); Mesh mesh = meshgenerator.generate( grid, Distribution( grid, Partitioner( "equal_regions" ) ) ); - fvm::Method fvm( mesh, util::Config( "radius", radius ) | option::levels( nlev ) ); + fvm::Method fvm( mesh, util::Config( "radius", radius ) | option::levels( test_levels() ) ); Nabla nabla( fvm ); FieldSet fields; @@ -261,15 +330,15 @@ CASE( "test_curl" ) { fields.add( fvm.node_columns().createField( option::name( "windXgradY" ) ) ); fields.add( fvm.node_columns().createField( option::name( "windYgradX" ) ) ); fields.add( fvm.node_columns().createField( option::name( "windYgradY" ) ) ); - auto wind = array::make_view( fields["wind"] ); - auto windgrad = array::make_view( fields["windgrad"] ); + auto wind = make_vectorview( fields["wind"] ); + auto windgrad = make_vectorview( fields["windgrad"] ); auto windX = array::make_view( fields["windX"] ); auto windY = array::make_view( fields["windY"] ); - auto windXgradX = array::make_view( fields["windXgradX"] ); - auto windXgradY = array::make_view( fields["windXgradY"] ); - auto windYgradX = array::make_view( fields["windYgradX"] ); - auto windYgradY = array::make_view( fields["windYgradY"] ); + auto windXgradX = make_scalarview( fields["windXgradX"] ); + auto windXgradY = make_scalarview( fields["windXgradY"] ); + auto windYgradX = make_scalarview( fields["windYgradX"] ); + auto windYgradY = make_scalarview( fields["windYgradY"] ); for ( idx_t j = 0; j < windX.size(); ++j ) { static const idx_t lev0 = 0; static const idx_t XdX = XX * 2 + XX; @@ -297,17 +366,28 @@ CASE( "test_curl" ) { gmsh.write( fields["windYgradY"] ); gmsh.write( fields["windgrad"] ); } + + double min, max, mean; + int N; + + // Vorticity! + fvm.node_columns().minimum( fields["vor"], min ); + fvm.node_columns().maximum( fields["vor"], max ); + fvm.node_columns().mean( fields["vor"], mean, N ); + print_min_max_mean( "vor" ); + EXPECT_APPROX_EQ( min, -6.257451225821150e-06 ); + EXPECT_APPROX_EQ( max, 6.257451225821150e-06 ); + EXPECT_APPROX_EQ( mean, 0., 1.e-20 ); } CASE( "test_lapl" ) { Log::info() << "test_lapl" << std::endl; - size_t nlev = 1; const double radius = util::Earth::radius(); // const double radius = 1.; Grid grid( griduid() ); MeshGenerator meshgenerator( "structured" ); Mesh mesh = meshgenerator.generate( grid, Distribution( grid, Partitioner( "equal_regions" ) ) ); - fvm::Method fvm( mesh, util::Config( "radius", radius ) | option::levels( nlev ) ); + fvm::Method fvm( mesh, util::Config( "radius", radius ) | option::levels( test_levels() ) ); Nabla nabla( fvm ); FieldSet fields; @@ -324,6 +404,16 @@ CASE( "test_lapl" ) { output::Gmsh gmsh( grid.name() + "_fields.msh", "a" ); gmsh.write( fields["lapl"] ); } + + double min, max, mean; + int N; + fvm.node_columns().minimum( fields["lapl"], min ); + fvm.node_columns().maximum( fields["lapl"], max ); + fvm.node_columns().mean( fields["lapl"], mean, N ); + print_min_max_mean( "lapl" ); + EXPECT_APPROX_EQ( min, -6.4088005677811607095e-13 ); + EXPECT_APPROX_EQ( max, 9.8984499569639476135e-12 ); + EXPECT_APPROX_EQ( mean, -1.03409e-13 ); } //----------------------------------------------------------------------------- From ccfe8f820045fc6a2f31e0698a6b8430de2a7159 Mon Sep 17 00:00:00 2001 From: Pedro Maciel Date: Wed, 29 Apr 2020 21:13:08 +0100 Subject: [PATCH 049/145] ATLAS-276: Unit test for UKMO grid on cca/gcc7 (failure possibly due to fast math) --- src/tests/grid/test_grid_iterator.cc | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/tests/grid/test_grid_iterator.cc b/src/tests/grid/test_grid_iterator.cc index b8487433c..2dbcaa314 100644 --- a/src/tests/grid/test_grid_iterator.cc +++ b/src/tests/grid/test_grid_iterator.cc @@ -12,6 +12,9 @@ #include #include +#include "eckit/types/FloatCompare.h" +#include "eckit/types/Fraction.h" + #include "atlas/grid/Iterator.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/grid/UnstructuredGrid.h" @@ -90,6 +93,33 @@ CASE( "test_iterator" ) { //----------------------------------------------------------------------------- +CASE( "ATLAS-276" ) { + using eckit::Fraction; + using eckit::types::is_approximately_equal; + + Fraction n( 90. ); + Fraction s( -90. ); + Fraction sn( 5, 6 ); + + Fraction N_as_fraction = ( ( n - s ) / sn ) + 1; + EXPECT( N_as_fraction.integer() ); + + long N = N_as_fraction.integralPart(); + EXPECT( N == 217 ); + + StructuredGrid::YSpace y( grid::LinearSpacing( n, s, N ) ); + + // Tolerance might be suitable + EXPECT( is_approximately_equal( y.front(), 90. ) ); + EXPECT( is_approximately_equal( y.back(), -90. ) ); + + // Tolerance isn't suitable + EXPECT( !( y.front() > 90. ) ); + EXPECT( !( y.back() < -90. ) ); +} + +//----------------------------------------------------------------------------- + } // namespace test } // namespace atlas From e95f7734486b1047ceb3825b9b5d496e5eeb9e7b Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 30 Apr 2020 14:29:46 +0100 Subject: [PATCH 050/145] ATLAS-276 Expect interval to be global --- src/tests/grid/test_grid_iterator.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tests/grid/test_grid_iterator.cc b/src/tests/grid/test_grid_iterator.cc index 2dbcaa314..b8f699978 100644 --- a/src/tests/grid/test_grid_iterator.cc +++ b/src/tests/grid/test_grid_iterator.cc @@ -15,6 +15,7 @@ #include "eckit/types/FloatCompare.h" #include "eckit/types/Fraction.h" +#include "atlas/domain/detail/ZonalBandDomain.h" #include "atlas/grid/Iterator.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/grid/UnstructuredGrid.h" @@ -113,6 +114,8 @@ CASE( "ATLAS-276" ) { EXPECT( is_approximately_equal( y.front(), 90. ) ); EXPECT( is_approximately_equal( y.back(), -90. ) ); + EXPECT( domain::ZonalBandDomain::is_global( {y.front(), y.back()} ) ); + // Tolerance isn't suitable EXPECT( !( y.front() > 90. ) ); EXPECT( !( y.back() < -90. ) ); From 7ebbf75303fb4a55059cf8080f3fa8d4f182b655 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 30 Apr 2020 15:19:57 +0000 Subject: [PATCH 051/145] ATLAS-276 Demonstrated problem is harmless, but we can aim to make direct comparison pass as well --- src/tests/grid/test_grid_iterator.cc | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/tests/grid/test_grid_iterator.cc b/src/tests/grid/test_grid_iterator.cc index b8f699978..0754fe58c 100644 --- a/src/tests/grid/test_grid_iterator.cc +++ b/src/tests/grid/test_grid_iterator.cc @@ -109,14 +109,21 @@ CASE( "ATLAS-276" ) { EXPECT( N == 217 ); StructuredGrid::YSpace y( grid::LinearSpacing( n, s, N ) ); + Log::info() << "y.front() = " << std::setprecision(20) << y.front() << std::endl; + Log::info() << "y.back() = " << std::setprecision(20) << y.back() << std::endl; - // Tolerance might be suitable + // The true check that matters + EXPECT( domain::ZonalBandDomain::is_global( {y.front(), y.back()} ) ); + + // Tolerance is suitable EXPECT( is_approximately_equal( y.front(), 90. ) ); EXPECT( is_approximately_equal( y.back(), -90. ) ); - EXPECT( domain::ZonalBandDomain::is_global( {y.front(), y.back()} ) ); + // Fraction is suitable + EXPECT( Fraction( y.front() ) == n ); + EXPECT( Fraction( y.back() ) == s ); - // Tolerance isn't suitable + // Direct comparison is not suitable EXPECT( !( y.front() > 90. ) ); EXPECT( !( y.back() < -90. ) ); } From 1c88f9d3c800811b669ff1766e9ff42ec5ea6b84 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 30 Apr 2020 15:48:59 +0000 Subject: [PATCH 052/145] ATLAS-276 Snap LinearSpacing boundaries to start/end if expected (no round-off error) --- .../grid/detail/spacing/LinearSpacing.cc | 33 +++++-------------- 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/src/atlas/grid/detail/spacing/LinearSpacing.cc b/src/atlas/grid/detail/spacing/LinearSpacing.cc index 67c33e1c5..98d1daf0f 100644 --- a/src/atlas/grid/detail/spacing/LinearSpacing.cc +++ b/src/atlas/grid/detail/spacing/LinearSpacing.cc @@ -13,6 +13,7 @@ #include #include + #include "eckit/config/Parametrisation.h" #include "atlas/grid/detail/spacing/SpacingFactory.h" @@ -26,21 +27,6 @@ LinearSpacing::Params::Params( const eckit::Parametrisation& params ) { endpoint = true; params.get( "endpoint", endpoint ); std::vector interval; - // if( params.get("step",step) ) { - // - // // Several combinations possible: - // if( params.get("start",start) && params.get("end",end) ) { - // N = long( (end-start)/step ) + (endpoint ? 1 : 0 ); - // } else if( params.get("centre",centre) && params.get("N",N) ) { - // start = endpoint ? centre - step * 0.5*double(N-1) - // : centre - step * 0.5*double(N); - // end = endpoint ? start + step * double(N-1) : - // start + step * double(N); - // } else { - // throw_Exception("Invalid combination of parameters",Here()); - // } - // } - // else if ( params.get( "N", N ) ) { // Only one remaining combinations possible: if ( params.get( "start", start ) && params.get( "end", end ) ) { @@ -75,15 +61,6 @@ LinearSpacing::LinearSpacing( const eckit::Parametrisation& params ) { setup( p.start, p.end, p.N, p.endpoint ); } -// LinearSpacing::LinearSpacing( double centre, double step, long N, bool -// endpoint ) { -// double start = endpoint ? centre - step * double(N-1)/2. : -// centre - step * double(N)/2.; -// double end = endpoint ? start + step * double(N-1) : -// start + step * double(N); -// setup(start,end,N,endpoint); -// } - LinearSpacing::LinearSpacing( double start, double end, long N, bool endpoint ) { setup( start, end, N, endpoint ); } @@ -114,6 +91,14 @@ void LinearSpacing::setup( double start, double end, long N, bool endpoint ) { end_ = end; N_ = N; endpoint_ = endpoint; + + // For exact comparisons: + if( N > 1 ) { + x_.front() = start; + } + if( N > 2 && endpoint ) { + x_.back() = end; + } } double LinearSpacing::step() const { From ba013766ad360bf029f9ea80aee4a53b7fd5725e Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 1 May 2020 11:56:45 +0100 Subject: [PATCH 053/145] ATLAS-276 Move test to separate file, and expand --- src/atlas/grid/Spacing.cc | 4 + src/atlas/grid/Spacing.h | 1 + src/tests/grid/CMakeLists.txt | 1 + src/tests/grid/test_grid_iterator.cc | 40 -------- src/tests/grid/test_spacing.cc | 139 +++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 40 deletions(-) create mode 100644 src/tests/grid/test_spacing.cc diff --git a/src/atlas/grid/Spacing.cc b/src/atlas/grid/Spacing.cc index 1c756bb16..7db3cbf0d 100644 --- a/src/atlas/grid/Spacing.cc +++ b/src/atlas/grid/Spacing.cc @@ -69,6 +69,10 @@ LinearSpacing::LinearSpacing( double start, double stop, long N, bool endpoint ) LinearSpacing::LinearSpacing( const Interval& interval, long N, bool endpoint ) : Spacing( new atlas::grid::spacing::LinearSpacing( interval, N, endpoint ) ) {} +double LinearSpacing::step() const { + return dynamic_cast( get() )->step(); +} + GaussianSpacing::GaussianSpacing( long N ) : Spacing( new atlas::grid::spacing::GaussianSpacing( N ) ) {} } // namespace grid diff --git a/src/atlas/grid/Spacing.h b/src/atlas/grid/Spacing.h index 1dcf68d68..8112a4908 100644 --- a/src/atlas/grid/Spacing.h +++ b/src/atlas/grid/Spacing.h @@ -82,6 +82,7 @@ class LinearSpacing : public Spacing { LinearSpacing() = default; LinearSpacing( double start, double stop, long N, bool endpoint = true ); LinearSpacing( const Interval&, long N, bool endpoint = true ); + double step() const; }; //--------------------------------------------------------------------------------------------------------------------- diff --git a/src/tests/grid/CMakeLists.txt b/src/tests/grid/CMakeLists.txt index 67d581535..6332857fb 100644 --- a/src/tests/grid/CMakeLists.txt +++ b/src/tests/grid/CMakeLists.txt @@ -25,6 +25,7 @@ foreach(test test_stretchedrotatedgaussian test_grid_cropping test_vertical + test_spacing test_state test_grid_hash) diff --git a/src/tests/grid/test_grid_iterator.cc b/src/tests/grid/test_grid_iterator.cc index 0754fe58c..b8487433c 100644 --- a/src/tests/grid/test_grid_iterator.cc +++ b/src/tests/grid/test_grid_iterator.cc @@ -12,10 +12,6 @@ #include #include -#include "eckit/types/FloatCompare.h" -#include "eckit/types/Fraction.h" - -#include "atlas/domain/detail/ZonalBandDomain.h" #include "atlas/grid/Iterator.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/grid/UnstructuredGrid.h" @@ -94,42 +90,6 @@ CASE( "test_iterator" ) { //----------------------------------------------------------------------------- -CASE( "ATLAS-276" ) { - using eckit::Fraction; - using eckit::types::is_approximately_equal; - - Fraction n( 90. ); - Fraction s( -90. ); - Fraction sn( 5, 6 ); - - Fraction N_as_fraction = ( ( n - s ) / sn ) + 1; - EXPECT( N_as_fraction.integer() ); - - long N = N_as_fraction.integralPart(); - EXPECT( N == 217 ); - - StructuredGrid::YSpace y( grid::LinearSpacing( n, s, N ) ); - Log::info() << "y.front() = " << std::setprecision(20) << y.front() << std::endl; - Log::info() << "y.back() = " << std::setprecision(20) << y.back() << std::endl; - - // The true check that matters - EXPECT( domain::ZonalBandDomain::is_global( {y.front(), y.back()} ) ); - - // Tolerance is suitable - EXPECT( is_approximately_equal( y.front(), 90. ) ); - EXPECT( is_approximately_equal( y.back(), -90. ) ); - - // Fraction is suitable - EXPECT( Fraction( y.front() ) == n ); - EXPECT( Fraction( y.back() ) == s ); - - // Direct comparison is not suitable - EXPECT( !( y.front() > 90. ) ); - EXPECT( !( y.back() < -90. ) ); -} - -//----------------------------------------------------------------------------- - } // namespace test } // namespace atlas diff --git a/src/tests/grid/test_spacing.cc b/src/tests/grid/test_spacing.cc new file mode 100644 index 000000000..9e851007a --- /dev/null +++ b/src/tests/grid/test_spacing.cc @@ -0,0 +1,139 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include +#include +#include + +#include "eckit/types/Fraction.h" + +#include "atlas/grid/Spacing.h" +#include "atlas/grid/StructuredGrid.h" +#include "atlas/runtime/Log.h" + +#include "tests/AtlasTestEnvironment.h" + +using LinearSpacing = atlas::grid::LinearSpacing; +using Spacing = atlas::grid::Spacing; +using Config = atlas::util::Config; + +namespace atlas { +namespace test { + +using std::to_string; +std::string to_string( const eckit::Fraction& f ) { + std::stringstream s; + s << f; + return s.str(); +} + +//----------------------------------------------------------------------------- + +CASE( "LinearSpacing " ) { + auto check = []( const Spacing& s, const std::vector& expected ) { + size_t j = 0; + for ( double x : s ) { + if ( not is_approximately_equal( x, expected[j++] ) ) { + return false; + } + } + return true; + }; + + /// Using the constructor LinearSpacing( start, end, N, endpoint ) we can create + EXPECT( check( LinearSpacing( 2, 3, 5, true ), {2.0, 2.25, 2.5, 2.75, 3.0} ) ); + EXPECT( check( LinearSpacing( 2, 3, 5, false ), {2.0, 2.2, 2.4, 2.6, 2.8} ) ); + + /// Using the constructor LinearSpacing( {start, end}, N, endpoint ) we can create + EXPECT( check( LinearSpacing( {2, 3}, 5, true ), {2.0, 2.25, 2.5, 2.75, 3.0} ) ); + EXPECT( check( LinearSpacing( {2, 3}, 5, false ), {2.0, 2.2, 2.4, 2.6, 2.8} ) ); + + /// Configuration parameters can be passed as well with following keys: + EXPECT( check( Spacing( Config( "type", "linear" )( "start", 2 )( "end", 3 )( "N", 5 )( "endpoint", true ) ), + {2.0, 2.25, 2.5, 2.75, 3.0} ) ); + EXPECT( check( Spacing( Config( "type", "linear" )( "start", 2 )( "end", 3 )( "N", 5 )( "endpoint", false ) ), + {2.0, 2.2, 2.4, 2.6, 2.8} ) ); + + /// Instead of the "end" key, you can provide the "length" key, to achieve the same results: + EXPECT( check( Spacing( Config( "type", "linear" )( "start", 2 )( "length", 1 )( "N", 5 )( "endpoint", true ) ), + {2.0, 2.25, 2.5, 2.75, 3.0} ) ); + EXPECT( check( Spacing( Config( "type", "linear" )( "start", 2 )( "length", 1 )( "N", 5 )( "endpoint", false ) ), + {2.0, 2.2, 2.4, 2.6, 2.8} ) ); +} + +CASE( "LinearSpacing exactness ATLAS-276" ) { + using eckit::Fraction; + + double north = 90.; + double south = -90.; + long N = 217; + + SECTION( "LinearSpacing" ) { + auto y = grid::LinearSpacing( north, south, N ); + + // Ensure y does not go outside interval [90.,-90] + EXPECT( !( y.front() > 90. ) ); + EXPECT( !( y.back() < -90. ) ); + + // Exact front and end + EXPECT( y.front() == 90. ); + EXPECT( y.back() == -90. ); + } + + SECTION( "LinearSpacing constructed with Fractions" ) { + Fraction n( north ); + Fraction s( south ); + Fraction sn( 5, 6 ); + + Fraction N_as_fraction = ( ( n - s ) / Fraction{5, 6} ) + 1; + EXPECT( N_as_fraction.integer() ); + EXPECT_EQ( N_as_fraction.integralPart(), N ); + auto y = grid::LinearSpacing( n, s, N ); + + EXPECT_EQ( Fraction{y.step()}, ( -Fraction{5, 6} ) ); + + // Ensure y does not go outside interval [90.,-90] + EXPECT( !( y.front() > 90. ) ); + EXPECT( !( y.back() < -90. ) ); + + // Exact front and end + EXPECT( y.front() == 90. ); + EXPECT( y.back() == -90. ); + } + + SECTION( "YSpace " ) { + StructuredGrid::YSpace y( grid::LinearSpacing( north, south, N ) ); + + // Ensure yspace does not go outside interval [90.,-90] + EXPECT( !( y.front() > 90. ) ); + EXPECT( !( y.back() < -90. ) ); + + // Exact front and end + EXPECT( y.front() == 90. ); + EXPECT( y.back() == -90. ); + } + + SECTION( "LinearSpacing without endpoint" ) { + auto y = grid::LinearSpacing( north, south, N - 1, /*endpoint*/ false ); + EXPECT_EQ( y.front(), 90. ); + EXPECT_EQ( Fraction{y.front() + ( N - 1 ) * y.step()}, -90. ); + EXPECT_EQ( Fraction{y.back() + y.step()}, -90. ); + EXPECT_EQ( Fraction{y.step()}, ( -Fraction{5, 6} ) ); + } +} + +//----------------------------------------------------------------------------- + +} // namespace test +} // namespace atlas + +int main( int argc, char** argv ) { + return atlas::test::run( argc, argv ); +} From f2fddea20a350790aeb7748db2219a633dacc10b Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 4 May 2020 18:20:46 +0100 Subject: [PATCH 054/145] Fix warnings --- src/atlas/util/KDTree.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/atlas/util/KDTree.h b/src/atlas/util/KDTree.h index 81dd3104a..8e19cde42 100644 --- a/src/atlas/util/KDTree.h +++ b/src/atlas/util/KDTree.h @@ -48,7 +48,7 @@ namespace util { template class KDTree : ObjectHandle> { public: - using Handle = typename ObjectHandle>::Handle; + using Handle = ObjectHandle>; using Implementation = typename Handle::Implementation; using Point = typename Implementation::Point; using Payload = typename Implementation::Payload; @@ -109,7 +109,7 @@ class KDTree : ObjectHandle> { template void build( const Longitudes& longitudes, const Latitudes& latitudes, const Payloads& payloads ) { get()->build( longitudes, latitudes, payloads ); - }; + } /// @brief Build with spherical points (lon,lat) given separate iterator ranges for longitudes, latitudes, and payloads. /// Memory will be reserved with reserve() to match the size @@ -127,7 +127,7 @@ class KDTree : ObjectHandle> { template void build( const Points& points, const Payloads& payloads ) { get()->build( points, payloads ); - }; + } /// @brief Build with spherical points (lon,lat) given separate iterator ranges for longitudes, latitudes, and payloads. /// Memory will be reserved with reserve() to match the size From 443ffe8262084c25004ab5c78088881ab53bbbca Mon Sep 17 00:00:00 2001 From: Philippe Marguinaud Date: Fri, 17 Apr 2020 07:12:20 +0000 Subject: [PATCH 055/145] ATLAS-280 (Github #30) Add index functions to StructuredGrid --- src/atlas/grid/StructuredGrid.h | 10 ++++ src/atlas/grid/detail/grid/Structured.cc | 18 +++++++ src/atlas/grid/detail/grid/Structured.h | 28 ++++++++++ src/atlas_f/grid/atlas_Grid_module.F90 | 68 ++++++++++++++++++++++++ src/tests/grid/CMakeLists.txt | 1 + src/tests/grid/fctest_grids.F90 | 59 ++++++++++++++++++++ src/tests/grid/test_grids.cc | 15 ++++++ 7 files changed, 199 insertions(+) create mode 100644 src/tests/grid/fctest_grids.F90 diff --git a/src/atlas/grid/StructuredGrid.h b/src/atlas/grid/StructuredGrid.h index e8de1c8fb..2945dc9bc 100644 --- a/src/atlas/grid/StructuredGrid.h +++ b/src/atlas/grid/StructuredGrid.h @@ -115,6 +115,16 @@ class StructuredGrid : public Grid { const YSpace& yspace() const { return grid_->yspace(); } + gidx_t index (idx_t i, idx_t j) const + { + return grid_->index (i, j); + } + + void index2ij (gidx_t gidx, idx_t & i, idx_t & j) const + { + grid_->index2ij (gidx, i, j); + } + private: const grid_t* grid_; }; diff --git a/src/atlas/grid/detail/grid/Structured.cc b/src/atlas/grid/detail/grid/Structured.cc index d6c0e7382..c77f11128 100644 --- a/src/atlas/grid/detail/grid/Structured.cc +++ b/src/atlas/grid/detail/grid/Structured.cc @@ -80,6 +80,12 @@ Structured::Structured( const std::string& name, XSpace xspace, YSpace yspace, P crop( domain ); computeTruePeriodicity(); + + jglooff_.resize (ny + 1); + jglooff_[0] = 0; + for (int j = 1; j < ny + 1; j++) + jglooff_[j] = jglooff_[j-1] + nx_[j-1]; + } Domain Structured::computeDomain() const { @@ -609,6 +615,18 @@ idx_t atlas__grid__Structured__nx( Structured* This, idx_t jlat ) { return This->nx( jlat ); } +gidx_t atlas__grid__Structured__index (Structured* This, idx_t i, idx_t j) +{ + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_StructuredGrid" ); + return This->index( i, j ); +} + +void atlas__grid__Structured__index2ij (Structured* This, gidx_t gidx, idx_t & i, idx_t & j) +{ + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_StructuredGrid" ); + This->index2ij (gidx, i, j); +} + void atlas__grid__Structured__nx_array( Structured* This, const idx_t*& nx_array, idx_t& size ) { ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_StructuredGrid" ); nx_array = This->nx().data(); diff --git a/src/atlas/grid/detail/grid/Structured.h b/src/atlas/grid/detail/grid/Structured.h index 19a197dda..f25874128 100644 --- a/src/atlas/grid/detail/grid/Structured.h +++ b/src/atlas/grid/detail/grid/Structured.h @@ -345,6 +345,29 @@ class Structured : public Grid { return std::unique_ptr( new IteratorLonLat( *this, false ) ); } + gidx_t index (idx_t i, idx_t j) const + { + return jglooff_[j] + i; + } + + void index2ij (gidx_t gidx, idx_t & i, idx_t & j) const + { + if ((gidx < 0) || (gidx >= jglooff_.back ())) + throw_Exception ("Structured::index2ij: gidx out of bounds", Here ()); + idx_t ja = 0, jb = jglooff_.size (); + while (jb - ja > 1) + { + idx_t jm = (ja + jb) / 2; + if (gidx < jglooff_[jm]) + jb = jm; + else + ja = jm; + } + i = gidx - jglooff_[ja]; + j = ja; + } + + protected: // methods virtual void print( std::ostream& ) const override; @@ -386,6 +409,9 @@ class Structured : public Grid { /// Periodicity in x-direction bool periodic_x_; + /// Per-row offset + std::vector jglooff_; + private: std::string name_ = {"structured"}; XSpace xspace_; @@ -408,6 +434,8 @@ Structured* atlas__grid__regular__ShiftedLat( long nx, long ny ); void atlas__grid__Structured__nx_array( Structured* This, const idx_t*& nx, idx_t& size ); idx_t atlas__grid__Structured__nx( Structured* This, idx_t j ); idx_t atlas__grid__Structured__ny( Structured* This ); +gidx_t atlas__grid__Structured__index (Structured* This, idx_t i, idx_t j); +void atlas__grid__Structured__index2ij (Structured* This, gidx_t gidx, idx_t & i, idx_t & j); idx_t atlas__grid__Structured__nxmin( Structured* This ); idx_t atlas__grid__Structured__nxmax( Structured* This ); idx_t atlas__grid__Structured__size( Structured* This ); diff --git a/src/atlas_f/grid/atlas_Grid_module.F90 b/src/atlas_f/grid/atlas_Grid_module.F90 index 77480bccd..390eaaae3 100644 --- a/src/atlas_f/grid/atlas_Grid_module.F90 +++ b/src/atlas_f/grid/atlas_Grid_module.F90 @@ -110,6 +110,15 @@ module atlas_Grid_module procedure, private :: nx_int32 => Structured__nx_int32 procedure, private :: nx_int64 => Structured__nx_int64 generic :: nx => nx_int32, nx_int64 + procedure, private :: Structured__index_int32 + procedure, private :: Structured__index_int64 + generic :: index => Structured__index_int32, Structured__index_int64 + procedure, private :: Structured__index2ij_int32 + procedure, private :: Structured__index2ij_int64 + generic :: index2ij => Structured__index2ij_int32, Structured__index2ij_int64 + procedure, private :: Structured__ij_int32 + procedure, private :: Structured__ij_int64 + generic :: ij => Structured__ij_int32, Structured__ij_int64 procedure :: nx_array => Structured__nx_array procedure :: nxmin => Structured__nxmin procedure :: nxmax => Structured__nxmax @@ -553,6 +562,65 @@ function Structured__ny(this) result(ny) ny = atlas__grid__Structured__ny(this%CPTR_PGIBUG_A) end function +subroutine Structured__index2ij_int32(this, gidx, i, j) + use, intrinsic :: iso_c_binding, only: c_long, c_int + use atlas_grid_Structured_c_binding + integer(c_int), intent (in) :: gidx + class(atlas_StructuredGrid), intent(in) :: this + integer(c_int), intent(out) :: i, j + call atlas__grid__Structured__index2ij(this%CPTR_PGIBUG_A, int (gidx-1, c_long), i, j) + i = i + 1 + j = j + 1 +end subroutine + +subroutine Structured__index2ij_int64(this, gidx, i, j) + use, intrinsic :: iso_c_binding, only: c_long, c_int + use atlas_grid_Structured_c_binding + integer(c_long), intent (in) :: gidx + class(atlas_StructuredGrid), intent(in) :: this + integer(c_int), intent(out) :: i, j + call atlas__grid__Structured__index2ij(this%CPTR_PGIBUG_A, gidx-1, i, j) + i = i + 1 + j = j + 1 +end subroutine + +function Structured__ij_int32(this, gidx) result(ij) + use, intrinsic :: iso_c_binding, only: c_long, c_int + use atlas_grid_Structured_c_binding + integer(c_int), intent (in) :: gidx + class(atlas_StructuredGrid), intent(in) :: this + integer(c_int) :: ij (2) + call atlas__grid__Structured__index2ij(this%CPTR_PGIBUG_A, int (gidx-1, c_long), ij(1), ij(2)) + ij = ij + 1 +end function + +function Structured__ij_int64(this, gidx) result(ij) + use, intrinsic :: iso_c_binding, only: c_long, c_int + use atlas_grid_Structured_c_binding + integer(c_long), intent (in) :: gidx + class(atlas_StructuredGrid), intent(in) :: this + integer(c_int) :: ij (2) + call atlas__grid__Structured__index2ij(this%CPTR_PGIBUG_A, gidx-1, ij(1), ij(2)) + ij = ij + 1 +end function + +function Structured__index_int32(this, i, j) result(gidx) + use, intrinsic :: iso_c_binding, only: c_long, c_int + use atlas_grid_Structured_c_binding + integer(c_long) :: gidx + class(atlas_StructuredGrid), intent(in) :: this + integer(c_int), intent(in) :: i, j + gidx = 1 + atlas__grid__Structured__index(this%CPTR_PGIBUG_A, c_idx(i), c_idx(j) ) +end function + +function Structured__index_int64(this, i, j) result(gidx) + use, intrinsic :: iso_c_binding, only: c_long + use atlas_grid_Structured_c_binding + integer(c_long) :: gidx + class(atlas_StructuredGrid), intent(in) :: this + integer(c_long), intent(in) :: i, j + gidx = 1 + atlas__grid__Structured__index(this%CPTR_PGIBUG_A, c_idx(i), c_idx(j) ) +end function function Structured__nx_int32(this, j) result(nx) use, intrinsic :: iso_c_binding, only: c_long, c_int diff --git a/src/tests/grid/CMakeLists.txt b/src/tests/grid/CMakeLists.txt index 6332857fb..97e14be36 100644 --- a/src/tests/grid/CMakeLists.txt +++ b/src/tests/grid/CMakeLists.txt @@ -9,6 +9,7 @@ if( HAVE_FCTEST ) foreach(test fctest_griddistribution + fctest_grids fctest_state ) add_fctest( TARGET atlas_${test} SOURCES ${test}.F90 LINKER_LANGUAGE Fortran LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) diff --git a/src/tests/grid/fctest_grids.F90 b/src/tests/grid/fctest_grids.F90 new file mode 100644 index 000000000..31bd0ea0c --- /dev/null +++ b/src/tests/grid/fctest_grids.F90 @@ -0,0 +1,59 @@ +! (C) Copyright 2013 ECMWF. +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +! In applying this licence, ECMWF does not waive the privileges and immunities +! granted to it by virtue of its status as an intergovernmental organisation nor +! does it submit to any jurisdiction. + +! This File contains Unit Tests for testing the +! C++ / Fortran Interfaces to the Mesh Datastructure +! @author Willem Deconinck + +#include "fckit/fctest.h" + +! ----------------------------------------------------------------------------- + +TESTSUITE(fctest_Grid) + +! ----------------------------------------------------------------------------- + +TESTSUITE_INIT + use atlas_module + call atlas_library%initialise() +END_TESTSUITE_INIT + +! ----------------------------------------------------------------------------- + +TESTSUITE_FINALIZE + use atlas_module + call atlas_library%finalise() +END_TESTSUITE_FINALIZE + +TEST( test_ij2gidx ) + use atlas_module + implicit none + type(atlas_StructuredGrid) :: grid + integer :: jglo, i, j, i1, j1, jglo1 + + grid = atlas_StructuredGrid ("N16") + + jglo = 1 + do j = 1, grid%ny () + do i = 1, grid%nx (j) + call grid%index2ij (jglo, i1, j1) + jglo1 = grid%index (i, j) + FCTEST_CHECK_EQUAL( jglo, jglo1 ) + FCTEST_CHECK_EQUAL( i, i1 ) + FCTEST_CHECK_EQUAL( j, j1 ) + jglo = jglo + 1 + enddo + enddo + + call grid%final() + +END_TEST + +! ----------------------------------------------------------------------------- + +END_TESTSUITE + diff --git a/src/tests/grid/test_grids.cc b/src/tests/grid/test_grids.cc index ea37c76c4..29ec11409 100644 --- a/src/tests/grid/test_grids.cc +++ b/src/tests/grid/test_grids.cc @@ -30,6 +30,21 @@ namespace test { //----------------------------------------------------------------------------- + +CASE( "test_ij2gidx") { + StructuredGrid n16 = Grid( "N16" ); + + for (int j = 0, jglo = 0; j < n16.ny (); j++) + for (int i = 0; i < n16.nx (j); i++, jglo++) + { + idx_t i1, j1; + n16.index2ij (jglo, i1, j1); + EXPECT (n16.index (i, j) == jglo); + EXPECT (i1 == i); + EXPECT (j1 == j); + } +} + CASE( "test_factory" ) { StructuredGrid structured = Grid( "N80" ); From 153e914319e58d35dc83d75965167ac9b3b88199 Mon Sep 17 00:00:00 2001 From: Philippe Marguinaud Date: Fri, 17 Apr 2020 08:27:56 +0000 Subject: [PATCH 056/145] ATLAS-281 (Github #36) Add a Fortran binding to create a StructuredColumns object from a grid, a distribution and a config object --- .../detail/StructuredColumnsInterface.cc | 5 +++++ .../detail/StructuredColumnsInterface.h | 3 +++ ...las_functionspace_StructuredColumns_module.F90 | 15 +++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/src/atlas/functionspace/detail/StructuredColumnsInterface.cc b/src/atlas/functionspace/detail/StructuredColumnsInterface.cc index e53c655ca..189689f97 100644 --- a/src/atlas/functionspace/detail/StructuredColumnsInterface.cc +++ b/src/atlas/functionspace/detail/StructuredColumnsInterface.cc @@ -53,6 +53,11 @@ const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__g const eckit::Configuration* config ) { return new detail::StructuredColumns( Grid( grid ), grid::Distribution( dist ), *vert, *config ); } +const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__grid_dist_config( + const Grid::Implementation* grid, const grid::DistributionImpl* dist, + const eckit::Configuration* config ) { + return new detail::StructuredColumns( Grid( grid ), grid::Distribution( dist ), *config ); +} const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__grid_part( const Grid::Implementation* grid, const PartitionerImpl* partitioner, const eckit::Configuration* config ) { diff --git a/src/atlas/functionspace/detail/StructuredColumnsInterface.h b/src/atlas/functionspace/detail/StructuredColumnsInterface.h index 0e5b91c10..e2d01aa5f 100644 --- a/src/atlas/functionspace/detail/StructuredColumnsInterface.h +++ b/src/atlas/functionspace/detail/StructuredColumnsInterface.h @@ -60,6 +60,9 @@ const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__g const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__grid_dist_vert( const GridImpl* grid, const grid::DistributionImpl* dist, const Vertical* vert, const eckit::Configuration* config ); +const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__grid_dist_config( + const GridImpl* grid, const grid::DistributionImpl* dist, + const eckit::Configuration* config ); const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__grid_part( const GridImpl* grid, const PartitionerImpl* dist, const eckit::Configuration* config ); diff --git a/src/atlas_f/functionspace/atlas_functionspace_StructuredColumns_module.F90 b/src/atlas_f/functionspace/atlas_functionspace_StructuredColumns_module.F90 index 0b2f6c306..2793a745d 100644 --- a/src/atlas_f/functionspace/atlas_functionspace_StructuredColumns_module.F90 +++ b/src/atlas_f/functionspace/atlas_functionspace_StructuredColumns_module.F90 @@ -110,6 +110,7 @@ module atlas_functionspace_StructuredColumns_module module procedure ctor_cptr module procedure ctor_grid module procedure ctor_grid_dist + module procedure ctor_grid_dist_config module procedure ctor_grid_dist_levels module procedure ctor_grid_dist_vertical module procedure ctor_grid_part @@ -221,6 +222,20 @@ function ctor_grid_dist_vertical(grid, distribution, vertical, halo) result(this call this%return() end function +function ctor_grid_dist_config(grid, distribution, config) result(this) + use atlas_functionspace_StructuredColumns_c_binding + type(atlas_functionspace_StructuredColumns) :: this + class(atlas_Grid), intent(in) :: grid + type(atlas_griddistribution), intent(in) :: distribution + type(atlas_Config), intent (in) :: config + call this%reset_c_ptr(atlas__functionspace__StructuredColumns__new__grid_dist_config( & + & grid%CPTR_PGIBUG_A, distribution%CPTR_PGIBUG_A, & + & config%CPTR_PGIBUG_B ) ) + call this%set_index() + call this%return() +end function + + function ctor_grid_part(grid, partitioner, halo, levels) result(this) use atlas_functionspace_StructuredColumns_c_binding type(atlas_functionspace_StructuredColumns) :: this From 37faae7df2f565ad48293e635cd492d3d4e787d8 Mon Sep 17 00:00:00 2001 From: Philippe Marguinaud Date: Mon, 20 Apr 2020 16:09:20 +0000 Subject: [PATCH 057/145] ATLAS-282 (Github #44) Copy constructor & assignment operator for atlas::vector --- src/atlas/util/vector.h | 19 ++++++-- src/tests/array/CMakeLists.txt | 7 +++ src/tests/array/test_array_vector.cc | 56 ++++++++++++++++++++++ src/tests/util/CMakeLists.txt | 1 - src/tests/util/test_vector.cc | 70 +++++++++++++++++++--------- 5 files changed, 126 insertions(+), 27 deletions(-) create mode 100644 src/tests/array/test_array_vector.cc diff --git a/src/atlas/util/vector.h b/src/atlas/util/vector.h index 59db51ae8..26bdb916e 100644 --- a/src/atlas/util/vector.h +++ b/src/atlas/util/vector.h @@ -10,6 +10,8 @@ #pragma once +#include // std::swap + #include "atlas/library/config.h" #include "atlas/parallel/omp/copy.h" #include "atlas/parallel/omp/fill.h" @@ -37,10 +39,19 @@ class vector { assign( size, value ); } - vector( vector&& other ) : data_( other.data_ ), size_( other.size_ ) { - other.data_ = nullptr; - other.size_ = 0; - other.capacity_ = 0; + vector( const vector& other ) { assign( other.data_, other.data_ + other.size_ ); } + + vector( vector&& other ) { + std::swap( data_, other.data_ ); + std::swap( size_, other.size_ ); + std::swap( capacity_, other.capacity_ ); + } + + vector& operator=( vector other ) { + std::swap( data_, other.data_ ); + std::swap( size_, other.size_ ); + std::swap( capacity_, other.capacity_ ); + return *this; } ~vector() { diff --git a/src/tests/array/CMakeLists.txt b/src/tests/array/CMakeLists.txt index c5e1a2eba..3afbb4832 100644 --- a/src/tests/array/CMakeLists.txt +++ b/src/tests/array/CMakeLists.txt @@ -25,6 +25,13 @@ ecbuild_add_test( TARGET atlas_test_array # ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} #) +ecbuild_add_test( TARGET atlas_test_array_vector + SOURCES test_array_vector.cc + LIBS atlas + ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} + CONDITION atlas_HAVE_GRIDTOOLS_STORAGE +) + ecbuild_add_test( TARGET atlas_test_svector SOURCES test_svector.cc LIBS atlas diff --git a/src/tests/array/test_array_vector.cc b/src/tests/array/test_array_vector.cc new file mode 100644 index 000000000..8156eb4a1 --- /dev/null +++ b/src/tests/array/test_array_vector.cc @@ -0,0 +1,56 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include "atlas/array/Vector.h" +#include "tests/AtlasTestEnvironment.h" + +using namespace atlas::array; + +namespace atlas { +namespace test { + +//----------------------------------------------------------------------------- + +CASE( "test_vector" ) { + Vector vec( 3 ); + + VectorView vec_view = make_host_vector_view( vec ); + + vec_view[0] = 3; + vec_view[1] = -3; + vec_view[2] = 1; + + EXPECT( vec_view.size() == 3 ); + EXPECT( vec_view[0] == 3 ); + EXPECT( vec_view[1] == -3 ); + EXPECT( vec_view[2] == 1 ); + + vec.resize( 5 ); + + // TODO invalidate preview views + VectorView vec_viewb = make_host_vector_view( vec ); + vec_viewb[3] = 5; + vec_viewb[4] = 6; + + EXPECT( vec_viewb[0] == 3 ); + EXPECT( vec_viewb[1] == -3 ); + EXPECT( vec_viewb[2] == 1 ); + EXPECT( vec_viewb[3] == 5 ); + EXPECT( vec_viewb[4] == 6 ); +} + +//----------------------------------------------------------------------------- + +} // namespace test +} // namespace atlas + +int main( int argc, char** argv ) { + return atlas::test::run( argc, argv ); +} diff --git a/src/tests/util/CMakeLists.txt b/src/tests/util/CMakeLists.txt index c3e88d1de..c3e9f236b 100644 --- a/src/tests/util/CMakeLists.txt +++ b/src/tests/util/CMakeLists.txt @@ -45,7 +45,6 @@ ecbuild_add_test( TARGET atlas_test_vector SOURCES test_vector.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} - CONDITION atlas_HAVE_GRIDTOOLS_STORAGE ) ecbuild_add_test( TARGET atlas_test_metadata diff --git a/src/tests/util/test_vector.cc b/src/tests/util/test_vector.cc index 8156eb4a1..85fc2101a 100644 --- a/src/tests/util/test_vector.cc +++ b/src/tests/util/test_vector.cc @@ -8,42 +8,68 @@ * nor does it submit to any jurisdiction. */ -#include "atlas/array/Vector.h" -#include "tests/AtlasTestEnvironment.h" +#include -using namespace atlas::array; +#include "atlas/library/config.h" +#include "atlas/util/vector.h" +#include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { //----------------------------------------------------------------------------- +// + + +template +atlas::vector square( const int n ) { + atlas::vector x( n ); + for ( int i = 0; i < n; i++ ) + x[i] = static_cast( i ) * static_cast( i ); + + return x; +} + +template +void pp( const std::string& t, T& v ) { + Log::info() << t << " = " << std::endl; + Log::info() << v.size() << std::endl; + for ( int i = 0; i < v.size(); i++ ) + Log::info() << i << " > " << v[i] << std::endl; +} + CASE( "test_vector" ) { - Vector vec( 3 ); + const int N = 100; + + // Ctor + atlas::vector x( N ); + + std::iota( std::begin( x ), std::end( x ), 0 ); + + // Copy ctor + atlas::vector y = x; + + EXPECT( x.size() == y.size() ); - VectorView vec_view = make_host_vector_view( vec ); + for ( int i = 0; i < x.size(); i++ ) { + EXPECT( x[i] == y[i] ); + } - vec_view[0] = 3; - vec_view[1] = -3; - vec_view[2] = 1; - EXPECT( vec_view.size() == 3 ); - EXPECT( vec_view[0] == 3 ); - EXPECT( vec_view[1] == -3 ); - EXPECT( vec_view[2] == 1 ); + // Assignment operator + auto z = square( 20 ); - vec.resize( 5 ); + for ( int i = 0; i < z.size(); i++ ) { + EXPECT( z[i] == i * i ); + } - // TODO invalidate preview views - VectorView vec_viewb = make_host_vector_view( vec ); - vec_viewb[3] = 5; - vec_viewb[4] = 6; + // Assignment operator + x = y; - EXPECT( vec_viewb[0] == 3 ); - EXPECT( vec_viewb[1] == -3 ); - EXPECT( vec_viewb[2] == 1 ); - EXPECT( vec_viewb[3] == 5 ); - EXPECT( vec_viewb[4] == 6 ); + for ( int i = 0; i < x.size(); i++ ) { + EXPECT( static_cast( x[i] ) == i ); + } } //----------------------------------------------------------------------------- From e7fdce4cea1dd2e23f82ecba9b39fa9f100676ef Mon Sep 17 00:00:00 2001 From: Philippe Marguinaud Date: Thu, 16 Apr 2020 16:31:38 +0000 Subject: [PATCH 058/145] ATLAS-283 (Github #28) Add Fortran bindings to retrieve the spec of a grid --- src/atlas/grid/detail/grid/Grid.cc | 12 ++++++++++++ src/atlas/grid/detail/grid/Grid.h | 4 ++++ src/atlas/grid/detail/grid/Structured.cc | 5 ----- src/atlas/grid/detail/grid/Structured.h | 1 - src/atlas_f/CMakeLists.txt | 3 +++ src/atlas_f/grid/atlas_Grid_module.F90 | 13 +++++++++++-- 6 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/atlas/grid/detail/grid/Grid.cc b/src/atlas/grid/detail/grid/Grid.cc index f935375f1..03d399d0c 100644 --- a/src/atlas/grid/detail/grid/Grid.cc +++ b/src/atlas/grid/detail/grid/Grid.cc @@ -138,6 +138,18 @@ void Grid::detachObserver( GridObserver& observer ) const { grid_observers_.end() ); } +idx_t atlas__grid__Grid__size( Grid* This ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_Grid" ); + return This->size(); +} + +util::Config * atlas__grid__Grid__spec( Grid* This ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_Grid" ); + util::Config * config = new util::Config (); + *config = This->spec(); + return config; +} + } // namespace grid } // namespace detail } // namespace grid diff --git a/src/atlas/grid/detail/grid/Grid.h b/src/atlas/grid/detail/grid/Grid.h index 320b68024..389e2253c 100644 --- a/src/atlas/grid/detail/grid/Grid.h +++ b/src/atlas/grid/detail/grid/Grid.h @@ -177,6 +177,10 @@ class GridObserver { //---------------------------------------------------------------------------------------------------------------------- +extern "C" { +idx_t atlas__grid__Grid__size( Grid* This ); +util::Config * atlas__grid__Grid__spec ( Grid* This ); +} } // namespace grid } // namespace detail diff --git a/src/atlas/grid/detail/grid/Structured.cc b/src/atlas/grid/detail/grid/Structured.cc index c77f11128..d4d26d941 100644 --- a/src/atlas/grid/detail/grid/Structured.cc +++ b/src/atlas/grid/detail/grid/Structured.cc @@ -643,11 +643,6 @@ idx_t atlas__grid__Structured__nxmin( Structured* This ) { return This->nxmin(); } -idx_t atlas__grid__Structured__size( Structured* This ) { - ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_StructuredGrid" ); - return This->size(); -} - double atlas__grid__Structured__y( Structured* This, idx_t j ) { ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_StructuredGrid" ); return This->y( j ); diff --git a/src/atlas/grid/detail/grid/Structured.h b/src/atlas/grid/detail/grid/Structured.h index f25874128..7f15810a1 100644 --- a/src/atlas/grid/detail/grid/Structured.h +++ b/src/atlas/grid/detail/grid/Structured.h @@ -438,7 +438,6 @@ gidx_t atlas__grid__Structured__index (Structured* This, idx_t i, idx_t j); void atlas__grid__Structured__index2ij (Structured* This, gidx_t gidx, idx_t & i, idx_t & j); idx_t atlas__grid__Structured__nxmin( Structured* This ); idx_t atlas__grid__Structured__nxmax( Structured* This ); -idx_t atlas__grid__Structured__size( Structured* This ); double atlas__grid__Structured__y( Structured* This, idx_t j ); double atlas__grid__Structured__x( Structured* This, idx_t i, idx_t j ); void atlas__grid__Structured__xy( Structured* This, idx_t i, idx_t j, double crd[] ); diff --git a/src/atlas_f/CMakeLists.txt b/src/atlas_f/CMakeLists.txt index 3f6ca4dd3..4eaee5917 100644 --- a/src/atlas_f/CMakeLists.txt +++ b/src/atlas_f/CMakeLists.txt @@ -55,6 +55,9 @@ function(generate_fortran_bindings output filename) endfunction() generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/grid.h) +generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/grid/detail/grid/Grid.h + MODULE atlas_grid_Grid_c_binding + OUTPUT grid_Grid_c_binding.f90 ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/grid/detail/grid/Structured.h MODULE atlas_grid_Structured_c_binding OUTPUT grid_Structured_c_binding.f90 ) diff --git a/src/atlas_f/grid/atlas_Grid_module.F90 b/src/atlas_f/grid/atlas_Grid_module.F90 index 390eaaae3..537324a7f 100644 --- a/src/atlas_f/grid/atlas_Grid_module.F90 +++ b/src/atlas_f/grid/atlas_Grid_module.F90 @@ -48,6 +48,7 @@ module atlas_Grid_module !------------------------------------------------------------------------------ contains procedure :: size => atlas_Grid__size + procedure :: spec => atlas_Grid__spec #if FCKIT_FINAL_NOT_INHERITING final :: atlas_Grid__final_auto @@ -524,10 +525,18 @@ function atlas_grid_RegularLonLat__ctor_int64(nlon,nlat) result(this) function atlas_Grid__size(this) result(npts) use, intrinsic :: iso_c_binding, only: c_long - use atlas_grid_Structured_c_binding + use atlas_grid_Grid_c_binding class(atlas_Grid), intent(in) :: this integer(c_long) :: npts - npts = atlas__grid__Structured__size(this%CPTR_PGIBUG_A) + npts = atlas__grid__Grid__size(this%CPTR_PGIBUG_A) +end function + +function atlas_Grid__spec(this) result(spec) + use atlas_grid_Grid_c_binding + class(atlas_Grid), intent(in) :: this + type(atlas_Config) :: spec + call spec%reset_c_ptr( atlas__grid__Grid__spec(this%CPTR_PGIBUG_A) ) + call spec%return () end function function Gaussian__N(this) result(N) From d2cfbc972612e348204964d02d5d6cb9e485a9f4 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 5 May 2020 16:27:50 +0100 Subject: [PATCH 059/145] ATLAS-283 (Github #28) Add test --- src/atlas/grid/detail/grid/Grid.cc | 6 ++---- src/atlas/grid/detail/grid/Grid.h | 2 +- src/atlas_f/grid/atlas_Grid_module.F90 | 2 +- src/tests/grid/fctest_grids.F90 | 18 ++++++++++++++++++ 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/atlas/grid/detail/grid/Grid.cc b/src/atlas/grid/detail/grid/Grid.cc index 03d399d0c..67081b65a 100644 --- a/src/atlas/grid/detail/grid/Grid.cc +++ b/src/atlas/grid/detail/grid/Grid.cc @@ -143,11 +143,9 @@ idx_t atlas__grid__Grid__size( Grid* This ) { return This->size(); } -util::Config * atlas__grid__Grid__spec( Grid* This ) { +Grid::Spec* atlas__grid__Grid__spec( Grid* This ) { ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_Grid" ); - util::Config * config = new util::Config (); - *config = This->spec(); - return config; + return new Grid::Spec( This->spec() ); } } // namespace grid diff --git a/src/atlas/grid/detail/grid/Grid.h b/src/atlas/grid/detail/grid/Grid.h index 389e2253c..71bc3a9ef 100644 --- a/src/atlas/grid/detail/grid/Grid.h +++ b/src/atlas/grid/detail/grid/Grid.h @@ -179,7 +179,7 @@ class GridObserver { extern "C" { idx_t atlas__grid__Grid__size( Grid* This ); -util::Config * atlas__grid__Grid__spec ( Grid* This ); +Grid::Spec* atlas__grid__Grid__spec( Grid* This ); } } // namespace grid diff --git a/src/atlas_f/grid/atlas_Grid_module.F90 b/src/atlas_f/grid/atlas_Grid_module.F90 index 537324a7f..3fee4fdf3 100644 --- a/src/atlas_f/grid/atlas_Grid_module.F90 +++ b/src/atlas_f/grid/atlas_Grid_module.F90 @@ -535,7 +535,7 @@ function atlas_Grid__spec(this) result(spec) use atlas_grid_Grid_c_binding class(atlas_Grid), intent(in) :: this type(atlas_Config) :: spec - call spec%reset_c_ptr( atlas__grid__Grid__spec(this%CPTR_PGIBUG_A) ) + spec = atlas_Config( atlas__grid__Grid__spec(this%CPTR_PGIBUG_A) ) call spec%return () end function diff --git a/src/tests/grid/fctest_grids.F90 b/src/tests/grid/fctest_grids.F90 index 31bd0ea0c..1307f0395 100644 --- a/src/tests/grid/fctest_grids.F90 +++ b/src/tests/grid/fctest_grids.F90 @@ -53,6 +53,24 @@ END_TEST +TEST( test_spec ) + use atlas_module + implicit none + type(atlas_Grid) :: grid + type(atlas_Config) :: spec + + grid = atlas_Grid ("O32") + spec = grid%spec() + + FCTEST_CHECK_EQUAL( spec%owners(), 1 ) + FCTEST_CHECK_EQUAL( spec%json(), '{"domain":{"type":"global"},"name":"O32","projection":{"type":"lonlat"}}' ) + + call spec%final() + call grid%final() + +END_TEST + + ! ----------------------------------------------------------------------------- END_TESTSUITE From 32b33b6bd348542cfde53e35cd04f4b340b089c1 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 5 May 2020 18:18:29 +0100 Subject: [PATCH 060/145] Apply clang-format --- .../detail/StructuredColumnsInterface.cc | 5 +-- .../detail/StructuredColumnsInterface.h | 3 +- src/atlas/grid/StructuredGrid.h | 10 +---- src/atlas/grid/detail/grid/Structured.cc | 16 ++++---- src/atlas/grid/detail/grid/Structured.h | 38 +++++++++---------- .../grid/detail/spacing/LinearSpacing.cc | 4 +- src/tests/grid/fctest_grids.F90 | 2 - src/tests/grid/test_grids.cc | 20 +++++----- 8 files changed, 42 insertions(+), 56 deletions(-) diff --git a/src/atlas/functionspace/detail/StructuredColumnsInterface.cc b/src/atlas/functionspace/detail/StructuredColumnsInterface.cc index 189689f97..b544462df 100644 --- a/src/atlas/functionspace/detail/StructuredColumnsInterface.cc +++ b/src/atlas/functionspace/detail/StructuredColumnsInterface.cc @@ -54,9 +54,8 @@ const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__g return new detail::StructuredColumns( Grid( grid ), grid::Distribution( dist ), *vert, *config ); } const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__grid_dist_config( - const Grid::Implementation* grid, const grid::DistributionImpl* dist, - const eckit::Configuration* config ) { - return new detail::StructuredColumns( Grid( grid ), grid::Distribution( dist ), *config ); + const Grid::Implementation* grid, const grid::DistributionImpl* dist, const eckit::Configuration* config ) { + return new detail::StructuredColumns( Grid( grid ), grid::Distribution( dist ), *config ); } const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__grid_part( diff --git a/src/atlas/functionspace/detail/StructuredColumnsInterface.h b/src/atlas/functionspace/detail/StructuredColumnsInterface.h index e2d01aa5f..31b4abc0b 100644 --- a/src/atlas/functionspace/detail/StructuredColumnsInterface.h +++ b/src/atlas/functionspace/detail/StructuredColumnsInterface.h @@ -61,8 +61,7 @@ const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__g const GridImpl* grid, const grid::DistributionImpl* dist, const Vertical* vert, const eckit::Configuration* config ); const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__grid_dist_config( - const GridImpl* grid, const grid::DistributionImpl* dist, - const eckit::Configuration* config ); + const GridImpl* grid, const grid::DistributionImpl* dist, const eckit::Configuration* config ); const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__grid_part( const GridImpl* grid, const PartitionerImpl* dist, const eckit::Configuration* config ); diff --git a/src/atlas/grid/StructuredGrid.h b/src/atlas/grid/StructuredGrid.h index 2945dc9bc..f7bd8f53a 100644 --- a/src/atlas/grid/StructuredGrid.h +++ b/src/atlas/grid/StructuredGrid.h @@ -115,15 +115,9 @@ class StructuredGrid : public Grid { const YSpace& yspace() const { return grid_->yspace(); } - gidx_t index (idx_t i, idx_t j) const - { - return grid_->index (i, j); - } + gidx_t index( idx_t i, idx_t j ) const { return grid_->index( i, j ); } - void index2ij (gidx_t gidx, idx_t & i, idx_t & j) const - { - grid_->index2ij (gidx, i, j); - } + void index2ij( gidx_t gidx, idx_t& i, idx_t& j ) const { grid_->index2ij( gidx, i, j ); } private: const grid_t* grid_; diff --git a/src/atlas/grid/detail/grid/Structured.cc b/src/atlas/grid/detail/grid/Structured.cc index d4d26d941..e529a1aac 100644 --- a/src/atlas/grid/detail/grid/Structured.cc +++ b/src/atlas/grid/detail/grid/Structured.cc @@ -81,11 +81,11 @@ Structured::Structured( const std::string& name, XSpace xspace, YSpace yspace, P computeTruePeriodicity(); - jglooff_.resize (ny + 1); + jglooff_.resize( ny + 1 ); jglooff_[0] = 0; - for (int j = 1; j < ny + 1; j++) - jglooff_[j] = jglooff_[j-1] + nx_[j-1]; - + for ( idx_t j = 1; j < ny + 1; j++ ) { + jglooff_[j] = jglooff_[j - 1] + nx_[j - 1]; + } } Domain Structured::computeDomain() const { @@ -615,16 +615,14 @@ idx_t atlas__grid__Structured__nx( Structured* This, idx_t jlat ) { return This->nx( jlat ); } -gidx_t atlas__grid__Structured__index (Structured* This, idx_t i, idx_t j) -{ +gidx_t atlas__grid__Structured__index( Structured* This, idx_t i, idx_t j ) { ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_StructuredGrid" ); return This->index( i, j ); } -void atlas__grid__Structured__index2ij (Structured* This, gidx_t gidx, idx_t & i, idx_t & j) -{ +void atlas__grid__Structured__index2ij( Structured* This, gidx_t gidx, idx_t& i, idx_t& j ) { ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_StructuredGrid" ); - This->index2ij (gidx, i, j); + This->index2ij( gidx, i, j ); } void atlas__grid__Structured__nx_array( Structured* This, const idx_t*& nx_array, idx_t& size ) { diff --git a/src/atlas/grid/detail/grid/Structured.h b/src/atlas/grid/detail/grid/Structured.h index 7f15810a1..18e16cd43 100644 --- a/src/atlas/grid/detail/grid/Structured.h +++ b/src/atlas/grid/detail/grid/Structured.h @@ -345,26 +345,24 @@ class Structured : public Grid { return std::unique_ptr( new IteratorLonLat( *this, false ) ); } - gidx_t index (idx_t i, idx_t j) const - { - return jglooff_[j] + i; - } + gidx_t index( idx_t i, idx_t j ) const { return jglooff_[j] + i; } - void index2ij (gidx_t gidx, idx_t & i, idx_t & j) const - { - if ((gidx < 0) || (gidx >= jglooff_.back ())) - throw_Exception ("Structured::index2ij: gidx out of bounds", Here ()); - idx_t ja = 0, jb = jglooff_.size (); - while (jb - ja > 1) - { - idx_t jm = (ja + jb) / 2; - if (gidx < jglooff_[jm]) - jb = jm; - else - ja = jm; + void index2ij( gidx_t gidx, idx_t& i, idx_t& j ) const { + if ( ( gidx < 0 ) || ( gidx >= jglooff_.back() ) ) { + throw_Exception( "Structured::index2ij: gidx out of bounds", Here() ); + } + idx_t ja = 0, jb = jglooff_.size(); + while ( jb - ja > 1 ) { + idx_t jm = ( ja + jb ) / 2; + if ( gidx < jglooff_[jm] ) { + jb = jm; + } + else { + ja = jm; + } } - i = gidx - jglooff_[ja]; - j = ja; + i = gidx - jglooff_[ja]; + j = ja; } @@ -434,8 +432,8 @@ Structured* atlas__grid__regular__ShiftedLat( long nx, long ny ); void atlas__grid__Structured__nx_array( Structured* This, const idx_t*& nx, idx_t& size ); idx_t atlas__grid__Structured__nx( Structured* This, idx_t j ); idx_t atlas__grid__Structured__ny( Structured* This ); -gidx_t atlas__grid__Structured__index (Structured* This, idx_t i, idx_t j); -void atlas__grid__Structured__index2ij (Structured* This, gidx_t gidx, idx_t & i, idx_t & j); +gidx_t atlas__grid__Structured__index( Structured* This, idx_t i, idx_t j ); +void atlas__grid__Structured__index2ij( Structured* This, gidx_t gidx, idx_t& i, idx_t& j ); idx_t atlas__grid__Structured__nxmin( Structured* This ); idx_t atlas__grid__Structured__nxmax( Structured* This ); double atlas__grid__Structured__y( Structured* This, idx_t j ); diff --git a/src/atlas/grid/detail/spacing/LinearSpacing.cc b/src/atlas/grid/detail/spacing/LinearSpacing.cc index 98d1daf0f..3926b2d9a 100644 --- a/src/atlas/grid/detail/spacing/LinearSpacing.cc +++ b/src/atlas/grid/detail/spacing/LinearSpacing.cc @@ -93,10 +93,10 @@ void LinearSpacing::setup( double start, double end, long N, bool endpoint ) { endpoint_ = endpoint; // For exact comparisons: - if( N > 1 ) { + if ( N > 1 ) { x_.front() = start; } - if( N > 2 && endpoint ) { + if ( N > 2 && endpoint ) { x_.back() = end; } } diff --git a/src/tests/grid/fctest_grids.F90 b/src/tests/grid/fctest_grids.F90 index 1307f0395..00c2b1703 100644 --- a/src/tests/grid/fctest_grids.F90 +++ b/src/tests/grid/fctest_grids.F90 @@ -5,8 +5,6 @@ ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. -! This File contains Unit Tests for testing the -! C++ / Fortran Interfaces to the Mesh Datastructure ! @author Willem Deconinck #include "fckit/fctest.h" diff --git a/src/tests/grid/test_grids.cc b/src/tests/grid/test_grids.cc index 29ec11409..37e32350b 100644 --- a/src/tests/grid/test_grids.cc +++ b/src/tests/grid/test_grids.cc @@ -31,18 +31,18 @@ namespace test { //----------------------------------------------------------------------------- -CASE( "test_ij2gidx") { +CASE( "test_ij2gidx" ) { StructuredGrid n16 = Grid( "N16" ); - for (int j = 0, jglo = 0; j < n16.ny (); j++) - for (int i = 0; i < n16.nx (j); i++, jglo++) - { - idx_t i1, j1; - n16.index2ij (jglo, i1, j1); - EXPECT (n16.index (i, j) == jglo); - EXPECT (i1 == i); - EXPECT (j1 == j); - } + for ( int j = 0, jglo = 0; j < n16.ny(); j++ ) { + for ( int i = 0; i < n16.nx( j ); i++, jglo++ ) { + idx_t i1, j1; + n16.index2ij( jglo, i1, j1 ); + EXPECT( n16.index( i, j ) == jglo ); + EXPECT( i1 == i ); + EXPECT( j1 == j ); + } + } } CASE( "test_factory" ) { From 6e5a96ece32f93228d3b1f6d45bee7152a53ac0e Mon Sep 17 00:00:00 2001 From: Philippe Marguinaud Date: Thu, 23 Apr 2020 12:29:09 +0000 Subject: [PATCH 061/145] ATLAS-284 (Github #46) Add Fortran binding for distribution method nb_pts and nb_partitions --- .../detail/distribution/DistributionImpl.cc | 10 ++++++++++ .../detail/distribution/DistributionImpl.h | 2 ++ .../grid/atlas_GridDistribution_module.F90 | 19 +++++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/src/atlas/grid/detail/distribution/DistributionImpl.cc b/src/atlas/grid/detail/distribution/DistributionImpl.cc index 3ef77058a..d6d85e1a2 100644 --- a/src/atlas/grid/detail/distribution/DistributionImpl.cc +++ b/src/atlas/grid/detail/distribution/DistributionImpl.cc @@ -148,5 +148,15 @@ void atlas__GridDistribution__delete( DistributionImpl* This ) { delete This; } +void atlas__GridDistribution__nb_pts( DistributionImpl* This, idx_t nb_pts[] ) { + const auto& nb_pts_ = This->nb_pts(); + std::copy( nb_pts_.begin(), nb_pts_.end(), &nb_pts[0] ); +} + +idx_t atlas__atlas__GridDistribution__nb_partitions( DistributionImpl* This ) { + return This->nb_partitions(); +} + + } // namespace grid } // namespace atlas diff --git a/src/atlas/grid/detail/distribution/DistributionImpl.h b/src/atlas/grid/detail/distribution/DistributionImpl.h index b1bc379cc..187b13e68 100644 --- a/src/atlas/grid/detail/distribution/DistributionImpl.h +++ b/src/atlas/grid/detail/distribution/DistributionImpl.h @@ -75,6 +75,8 @@ class DistributionImpl : public util::Object { extern "C" { DistributionImpl* atlas__GridDistribution__new( idx_t npts, int part[], int part0 ); void atlas__GridDistribution__delete( DistributionImpl* This ); +void atlas__GridDistribution__nb_pts( DistributionImpl* This, idx_t nb_pts[] ); +idx_t atlas__atlas__GridDistribution__nb_partitions( DistributionImpl* This ); } } // namespace grid diff --git a/src/atlas_f/grid/atlas_GridDistribution_module.F90 b/src/atlas_f/grid/atlas_GridDistribution_module.F90 index 105085d8f..a873133b2 100644 --- a/src/atlas_f/grid/atlas_GridDistribution_module.F90 +++ b/src/atlas_f/grid/atlas_GridDistribution_module.F90 @@ -40,6 +40,8 @@ module atlas_GridDistribution_module !------------------------------------------------------------------------------ contains + procedure :: nb_partitions => atlas_GridDistribution__nb_partitions + procedure :: nb_pts => atlas_GridDistribution__nb_pts #if FCKIT_FINAL_NOT_INHERITING final :: atlas_GridDistribution__final_auto #endif @@ -84,6 +86,23 @@ function atlas_GridDistribution__ctor( part, part0 ) result(this) call this%return() end function +function atlas_GridDistribution__nb_pts(this) result(nb_pts) + use atlas_distribution_c_binding + use atlas_kinds_module, only : ATLAS_KIND_IDX + class(atlas_GridDistribution) :: this + integer(kind=ATLAS_KIND_IDX), allocatable :: nb_pts(:) + allocate (nb_pts (this%nb_partitions ())) + call atlas__GridDistribution__nb_pts(this%CPTR_PGIBUG_A, nb_pts) +end function + +function atlas_GridDistribution__nb_partitions(this) result(nb_partitions) + use, intrinsic :: iso_c_binding, only: c_long + use atlas_distribution_c_binding + class(atlas_GridDistribution), intent(in) :: this + integer(c_long) :: nb_partitions + nb_partitions = atlas__atlas__GridDistribution__nb_partitions(this%CPTR_PGIBUG_A) +end function + ! ---------------------------------------------------------------------------------------- ATLAS_FINAL subroutine atlas_GridDistribution__final_auto(this) From d2bdd842fd421722ce545d969f543f22bd680fc8 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 6 May 2020 11:53:10 +0100 Subject: [PATCH 062/145] ATLAS-284 (Github #46) Add test (only one partition though) --- src/tests/grid/fctest_griddistribution.F90 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/tests/grid/fctest_griddistribution.F90 b/src/tests/grid/fctest_griddistribution.F90 index 526b8951a..b6c7a4b49 100644 --- a/src/tests/grid/fctest_griddistribution.F90 +++ b/src/tests/grid/fctest_griddistribution.F90 @@ -66,8 +66,6 @@ integer :: jnode grid = atlas_StructuredGrid("O16") - !grid = atlas_StructuredGrid("ll.128x64") - !grid = atlas_grid_ShiftedLonLat(128,64) allocate( part(grid%size()) ) do jnode=1,grid%size()/3 @@ -79,6 +77,10 @@ griddistribution = atlas_GridDistribution(part, part0=1) + FCTEST_CHECK_EQUAL( griddistribution%nb_partitions(), 1 ) + FCTEST_CHECK_EQUAL( griddistribution%nb_pts(), [ int(grid%size(),ATLAS_KIND_IDX) ] ) + + FCTEST_CHECK_EQUAL( grid%owners(), 1 ) meshgenerator = atlas_MeshGenerator() From 388b1c87f44575a59b81fa90feddfc0987df238e Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 6 May 2020 18:50:54 +0100 Subject: [PATCH 063/145] Disable atlsa_test_stencil_parallel_mpi16 for travis build 'OSX + OpenMPI' --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 35c95949c..9d6f677e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -67,6 +67,7 @@ matrix: - CXX_COMPILER='clang++' C_COMPILER='clang' Fortran_COMPILER='gfortran' - MPI=openmpi - ATLAS_CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=DEBUG" + - ATLAS_CTEST_OPTIONS='-E "atlas_test_stencil_parallel_mpi16"' osx_image: xcode10.1 addons: homebrew: @@ -232,7 +233,7 @@ script: ################################################################# # Test Atlas ################################################################# - - ctest + - ctest ${ATLAS_CTEST_OPTIONS} after_success: @@ -250,5 +251,5 @@ after_success: after_failure: - cd ${ATLAS_BUILD_DIR} - - ctest -VV --rerun-failed + - ctest -VV --rerun-failed ${ATLAS_CTEST_OPTIONS} - cat ecbuild.log From 501be8c06d109e7477078cd44c9a043425f5f10f Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 7 May 2020 14:57:09 +0100 Subject: [PATCH 064/145] Fix warnings --- src/atlas/util/detail/KDTree.h | 4 ++-- src/tests/projection/test_rotation.cc | 7 ++++--- src/tests/util/test_kdtree.cc | 4 +++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/atlas/util/detail/KDTree.h b/src/atlas/util/detail/KDTree.h index ee4d0d85e..c28729a96 100644 --- a/src/atlas/util/detail/KDTree.h +++ b/src/atlas/util/detail/KDTree.h @@ -146,7 +146,7 @@ class KDTreeBase : public Object { void build( const Longitudes& longitudes, const Latitudes& latitudes, const Payloads& payloads ) { build( longitudes.begin(), longitudes.end(), latitudes.begin(), latitudes.end(), payloads.begin(), payloads.end() ); - }; + } /// @brief Build with spherical points (lon,lat) given separate iterator ranges for longitudes, latitudes, and payloads. /// Memory will be reserved with reserve() to match the size @@ -172,7 +172,7 @@ class KDTreeBase : public Object { template void build( const Points& points, const Payloads& payloads ) { build( points.begin(), points.end(), payloads.begin(), payloads.end() ); - }; + } /// @brief Build with spherical points (lon,lat) given separate iterator ranges for longitudes, latitudes, and payloads. /// Memory will be reserved with reserve() to match the size diff --git a/src/tests/projection/test_rotation.cc b/src/tests/projection/test_rotation.cc index 7ee792455..b331a1df9 100644 --- a/src/tests/projection/test_rotation.cc +++ b/src/tests/projection/test_rotation.cc @@ -149,9 +149,9 @@ CASE( "test_rotation_angle" ) { {2.00000, 46.70000}, {-178.00000, -46.70000}, {166.62343, -19.46929}, {156.02366, 8.65459}, {143.57464, 36.43683}, {117.10894, 61.43199}, {43.88245, 68.00825}, {2.00000, 46.70000}, }; - - - for ( int i = 0, jglo = 0; i < nx; i++ ) + + + for ( int i = 0, jglo = 0; i < nx; i++ ) { for ( int j = 0; j < ny + 1; j++, jglo++ ) { double lon = static_cast( i ) * 360. / static_cast( nx ); double lat = static_cast( j - ny / 2 ) * 90. / static_cast( ny / 2 ); @@ -161,6 +161,7 @@ CASE( "test_rotation_angle" ) { EXPECT( equivalent( p0, p2 ) ); EXPECT( equivalent( p1, ref[jglo] ) ); } + } } CASE( "test_rotation_construction" ) { diff --git a/src/tests/util/test_kdtree.cc b/src/tests/util/test_kdtree.cc index 99c35b98e..b418c5d23 100644 --- a/src/tests/util/test_kdtree.cc +++ b/src/tests/util/test_kdtree.cc @@ -49,11 +49,13 @@ class PayloadGenerator { ++i_; return *this; } + /* + // unused iterator operator++( int ) { iterator copy( *this ); ++i_; return copy; - } + }*/ bool operator==( const iterator& other ) const { return i_ == other.i_; } bool operator!=( const iterator& other ) const { return i_ != other.i_; } From ff7e225ff6cee050f8156e18c5a70eec3b9fe429 Mon Sep 17 00:00:00 2001 From: Marek Wlasak Date: Thu, 7 May 2020 10:56:37 +0100 Subject: [PATCH 065/145] ATLAS-285 (Github #16) Adjoint halo exchange --- src/atlas/CMakeLists.txt | 1 + src/atlas/field/Field.cc | 4 + src/atlas/field/Field.h | 1 + src/atlas/field/FieldSet.cc | 6 + src/atlas/field/FieldSet.h | 1 + src/atlas/field/detail/FieldImpl.cc | 10 + src/atlas/field/detail/FieldImpl.h | 1 + src/atlas/field/detail/FieldInterface.cc | 5 + src/atlas/field/detail/FieldInterface.h | 1 + src/atlas/functionspace/FunctionSpace.cc | 9 + src/atlas/functionspace/FunctionSpace.h | 3 + .../functionspace/detail/FunctionSpaceImpl.cc | 8 + .../functionspace/detail/FunctionSpaceImpl.h | 3 + .../detail/FunctionSpaceInterface.cc | 19 + .../detail/FunctionSpaceInterface.h | 3 + .../functionspace/detail/StructuredColumns.cc | 55 + .../functionspace/detail/StructuredColumns.h | 3 + src/atlas/library/defines.h.in | 6 + src/atlas/parallel/HaloAdjointExchangeImpl.h | 98 + src/atlas/parallel/HaloExchange.cc | 110 +- src/atlas/parallel/HaloExchange.h | 337 +++- .../functionspace/test_structuredcolumns.cc | 82 + src/tests/parallel/CMakeLists.txt | 8 + .../parallel/test_haloexchange_adjoint.cc | 1587 +++++++++++++++++ 24 files changed, 2277 insertions(+), 84 deletions(-) create mode 100644 src/atlas/parallel/HaloAdjointExchangeImpl.h create mode 100644 src/tests/parallel/test_haloexchange_adjoint.cc diff --git a/src/atlas/CMakeLists.txt b/src/atlas/CMakeLists.txt index c65ff448d..8f1156123 100644 --- a/src/atlas/CMakeLists.txt +++ b/src/atlas/CMakeLists.txt @@ -574,6 +574,7 @@ parallel/GatherScatter.cc parallel/GatherScatter.h parallel/HaloExchange.cc parallel/HaloExchange.h +parallel/HaloAdjointExchangeImpl.h parallel/HaloExchangeImpl.h parallel/mpi/Buffer.h runtime/Exception.cc diff --git a/src/atlas/field/Field.cc b/src/atlas/field/Field.cc index a4ed34315..352d90bfa 100644 --- a/src/atlas/field/Field.cc +++ b/src/atlas/field/Field.cc @@ -187,6 +187,10 @@ void Field::haloExchange( bool on_device ) const { get()->haloExchange( on_device ); } +void Field::adjointHaloExchange( bool on_device ) const { + get()->adjointHaloExchange( on_device ); +} + // -- Methods related to host-device synchronisation, requires gridtools_storage void Field::updateHost() const { diff --git a/src/atlas/field/Field.h b/src/atlas/field/Field.h index 8cfaea0dd..f2efd1499 100644 --- a/src/atlas/field/Field.h +++ b/src/atlas/field/Field.h @@ -169,6 +169,7 @@ class Field : DOXYGEN_HIDE( public util::ObjectHandle ) { void set_dirty( bool = true ) const; void haloExchange( bool on_device = false ) const; + void adjointHaloExchange( bool on_device = false ) const; // -- Methods related to host-device synchronisation void updateHost() const; diff --git a/src/atlas/field/FieldSet.cc b/src/atlas/field/FieldSet.cc index 6a53950fb..aa06eee56 100644 --- a/src/atlas/field/FieldSet.cc +++ b/src/atlas/field/FieldSet.cc @@ -59,6 +59,12 @@ void FieldSetImpl::haloExchange( bool on_device ) const { } } +void FieldSetImpl::adjointHaloExchange( bool on_device ) const { + for ( idx_t i = 0; i < size(); ++i ) { + field( i ).adjointHaloExchange( on_device ); + } +} + void FieldSetImpl::set_dirty( bool value ) const { for ( idx_t i = 0; i < size(); ++i ) { field( i ).set_dirty( value ); diff --git a/src/atlas/field/FieldSet.h b/src/atlas/field/FieldSet.h index bf35127f9..80292f960 100644 --- a/src/atlas/field/FieldSet.h +++ b/src/atlas/field/FieldSet.h @@ -87,6 +87,7 @@ class FieldSetImpl : public util::Object { const_iterator cend() const { return fields_.end(); } void haloExchange( bool on_device = false ) const; + void adjointHaloExchange( bool on_device = false ) const; void set_dirty( bool = true ) const; protected: // data diff --git a/src/atlas/field/detail/FieldImpl.cc b/src/atlas/field/detail/FieldImpl.cc index f0cf99542..cf6acac9e 100644 --- a/src/atlas/field/detail/FieldImpl.cc +++ b/src/atlas/field/detail/FieldImpl.cc @@ -17,6 +17,9 @@ #include "atlas/functionspace/FunctionSpace.h" #include "atlas/runtime/Exception.h" + +#include "atlas/runtime/Log.h" + namespace atlas { namespace field { @@ -158,7 +161,14 @@ void FieldImpl::haloExchange( bool on_device ) const { set_dirty( false ); } } +void FieldImpl::adjointHaloExchange( bool on_device ) const { + { + set_dirty(); + ATLAS_ASSERT( functionspace() ); + functionspace().adjointHaloExchange( Field( this ), on_device ); + } +} // ------------------------------------------------------------------ diff --git a/src/atlas/field/detail/FieldImpl.h b/src/atlas/field/detail/FieldImpl.h index ba78f62fd..9bdb57e09 100644 --- a/src/atlas/field/detail/FieldImpl.h +++ b/src/atlas/field/detail/FieldImpl.h @@ -190,6 +190,7 @@ class FieldImpl : public util::Object { void reactivateHostWriteViews() const { array_->reactivateHostWriteViews(); } void haloExchange( bool on_device = false ) const; + void adjointHaloExchange( bool on_device = false ) const; private: // methods void print( std::ostream& os, bool dump = false ) const; diff --git a/src/atlas/field/detail/FieldInterface.cc b/src/atlas/field/detail/FieldInterface.cc index c370eae10..fa488994e 100644 --- a/src/atlas/field/detail/FieldInterface.cc +++ b/src/atlas/field/detail/FieldInterface.cc @@ -236,6 +236,11 @@ void atlas__Field__halo_exchange( FieldImpl* This, int on_device ) { ATLAS_ASSERT( This != nullptr, "Cannot halo-exchange uninitialised atlas_Field" ); return This->haloExchange( on_device ); } + +void atlas__Field__adjoint_halo_exchange( FieldImpl* This, int on_device ) { + ATLAS_ASSERT( This != nullptr, "Cannot adjoint-halo-exchange uninitialised atlas_Field" ); + return This->adjointHaloExchange( on_device ); +} } // ------------------------------------------------------------------ diff --git a/src/atlas/field/detail/FieldInterface.h b/src/atlas/field/detail/FieldInterface.h index b2b356c90..6ea59f049 100644 --- a/src/atlas/field/detail/FieldInterface.h +++ b/src/atlas/field/detail/FieldInterface.h @@ -64,6 +64,7 @@ void atlas__Field__update_host( FieldImpl* This ); void atlas__Field__sync_host_device( FieldImpl* This ); void atlas__Field__set_dirty( FieldImpl* This, int value ); void atlas__Field__halo_exchange( FieldImpl* This, int on_device ); +void atlas__Field__adjoint_halo_exchange( FieldImpl* This, int on_device ); int atlas__Field__dirty( FieldImpl* This ); int atlas__Field__contiguous( FieldImpl* This ); } diff --git a/src/atlas/functionspace/FunctionSpace.cc b/src/atlas/functionspace/FunctionSpace.cc index 3c724530b..ed4839dfa 100644 --- a/src/atlas/functionspace/FunctionSpace.cc +++ b/src/atlas/functionspace/FunctionSpace.cc @@ -50,6 +50,11 @@ void FunctionSpace::haloExchange( const Field& field, bool on_device ) const { return get()->haloExchange( field, on_device ); } +void FunctionSpace::adjointHaloExchange( const Field& field, bool on_device ) const { + return get()->adjointHaloExchange( field, on_device ); +} + + idx_t FunctionSpace::size() const { return get()->size(); } @@ -70,6 +75,10 @@ void FunctionSpace::haloExchange( const FieldSet& fields, bool on_device ) const return get()->haloExchange( fields, on_device ); } +void FunctionSpace::adjointHaloExchange( const FieldSet& fields, bool on_device ) const { + return get()->adjointHaloExchange( fields, on_device ); +} + const util::PartitionPolygon& FunctionSpace::polygon( idx_t halo ) const { return get()->polygon( halo ); } diff --git a/src/atlas/functionspace/FunctionSpace.h b/src/atlas/functionspace/FunctionSpace.h index 749ab915e..65fe11759 100644 --- a/src/atlas/functionspace/FunctionSpace.h +++ b/src/atlas/functionspace/FunctionSpace.h @@ -60,6 +60,9 @@ class FunctionSpace : DOXYGEN_HIDE( public util::ObjectHandlehaloExchange( f ); } + +//------------------------------------------------------------------------------ + +void atlas__FunctionSpace_adjoint_halo_exchange_field( const FunctionSpaceImpl* This, field::FieldImpl* field ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_FunctionSpace" ); + ATLAS_ASSERT( field != nullptr, "Cannot access uninitialised atlas_Field" ); + Field f( field ); + This->adjointHaloExchange( f ); +} + +//------------------------------------------------------------------------------ + +void atlas__FunctionSpace__adjoint_halo_exchange_fieldset( const FunctionSpaceImpl* This, + field::FieldSetImpl* fieldset ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_FunctionSpace" ); + ATLAS_ASSERT( fieldset != nullptr, "Cannot access uninitialised atlas_FieldSet" ); + FieldSet f( fieldset ); + This->adjointHaloExchange( f ); +} } // ------------------------------------------------------------------ diff --git a/src/atlas/functionspace/detail/FunctionSpaceInterface.h b/src/atlas/functionspace/detail/FunctionSpaceInterface.h index c31217792..21a25cf84 100644 --- a/src/atlas/functionspace/detail/FunctionSpaceInterface.h +++ b/src/atlas/functionspace/detail/FunctionSpaceInterface.h @@ -39,6 +39,9 @@ field::FieldImpl* atlas__FunctionSpace__create_field_template( const FunctionSpa const eckit::Configuration* options ); void atlas__FunctionSpace__halo_exchange_field( const FunctionSpaceImpl* This, field::FieldImpl* field ); void atlas__FunctionSpace__halo_exchange_fieldset( const FunctionSpaceImpl* This, field::FieldSetImpl* fieldset ); +void atlas__FunctionSpace__adjoint_halo_exchange_field( const FunctionSpaceImpl* This, field::FieldImpl* field ); +void atlas__FunctionSpace__adjoint_halo_exchange_fieldset( const FunctionSpaceImpl* This, + field::FieldSetImpl* fieldset ); } //------------------------------------------------------------------------------------------------------ diff --git a/src/atlas/functionspace/detail/StructuredColumns.cc b/src/atlas/functionspace/detail/StructuredColumns.cc index 335367f52..2e746093a 100644 --- a/src/atlas/functionspace/detail/StructuredColumns.cc +++ b/src/atlas/functionspace/detail/StructuredColumns.cc @@ -727,6 +727,33 @@ void dispatch_haloExchange( Field& field, const parallel::HaloExchange& halo_exc } field.set_dirty( false ); } + + +template +void dispatch_adjointHaloExchange( Field& field, const parallel::HaloExchange& halo_exchange, + const StructuredColumns& fs ) { + FixupHaloForVectors fixup_halos( fs ); + if ( field.datatype() == array::DataType::kind() ) { + halo_exchange.template execute_adjoint( field.array(), false ); + fixup_halos.template apply( field ); + } + else if ( field.datatype() == array::DataType::kind() ) { + halo_exchange.template execute_adjoint( field.array(), false ); + fixup_halos.template apply( field ); + } + else if ( field.datatype() == array::DataType::kind() ) { + halo_exchange.template execute_adjoint( field.array(), false ); + fixup_halos.template apply( field ); + } + else if ( field.datatype() == array::DataType::kind() ) { + halo_exchange.template execute_adjoint( field.array(), false ); + fixup_halos.template apply( field ); + } + else { + throw_Exception( "datatype not supported", Here() ); + } + field.set_dirty( false ); +} } // namespace void StructuredColumns::haloExchange( const FieldSet& fieldset, bool ) const { @@ -751,12 +778,40 @@ void StructuredColumns::haloExchange( const FieldSet& fieldset, bool ) const { } } +void StructuredColumns::adjointHaloExchange( const FieldSet& fieldset, bool ) const { + for ( idx_t f = 0; f < fieldset.size(); ++f ) { + Field& field = const_cast( fieldset )[f]; + switch ( field.rank() ) { + case 1: + dispatch_adjointHaloExchange<1>( field, halo_exchange(), *this ); + break; + case 2: + dispatch_adjointHaloExchange<2>( field, halo_exchange(), *this ); + break; + case 3: + dispatch_adjointHaloExchange<3>( field, halo_exchange(), *this ); + break; + case 4: + dispatch_adjointHaloExchange<4>( field, halo_exchange(), *this ); + break; + default: + throw_Exception( "Rank not supported", Here() ); + } + } +} + void StructuredColumns::haloExchange( const Field& field, bool ) const { FieldSet fieldset; fieldset.add( field ); haloExchange( fieldset ); } +void StructuredColumns::adjointHaloExchange( const Field& field, bool ) const { + FieldSet fieldset; + fieldset.add( field ); + adjointHaloExchange( fieldset ); +} + size_t StructuredColumns::footprint() const { size_t size = sizeof( *this ); size += ij2gp_.footprint(); diff --git a/src/atlas/functionspace/detail/StructuredColumns.h b/src/atlas/functionspace/detail/StructuredColumns.h index b93b1039a..8a374d8a6 100644 --- a/src/atlas/functionspace/detail/StructuredColumns.h +++ b/src/atlas/functionspace/detail/StructuredColumns.h @@ -93,6 +93,9 @@ class StructuredColumns : public FunctionSpaceImpl { virtual void haloExchange( const FieldSet&, bool on_device = false ) const override; virtual void haloExchange( const Field&, bool on_device = false ) const override; + virtual void adjointHaloExchange( const FieldSet&, bool on_device = false ) const override; + virtual void adjointHaloExchange( const Field&, bool on_device = false ) const override; + idx_t sizeOwned() const { return size_owned_; } idx_t sizeHalo() const { return size_halo_; } virtual idx_t size() const override { return size_halo_; } diff --git a/src/atlas/library/defines.h.in b/src/atlas/library/defines.h.in index 894f632d0..a1aaaeb22 100644 --- a/src/atlas/library/defines.h.in +++ b/src/atlas/library/defines.h.in @@ -43,4 +43,10 @@ #define ATLAS_BITS_LOCAL @ATLAS_BITS_LOCAL@ +#if defined( __GNUC__ ) +#define ATLAS_MAYBE_UNUSED __attribute__( ( unused ) ) +#else +#define ATLAS_MAYBE_UNUSED +#endif + #endif diff --git a/src/atlas/parallel/HaloAdjointExchangeImpl.h b/src/atlas/parallel/HaloAdjointExchangeImpl.h new file mode 100644 index 000000000..b70e18c0c --- /dev/null +++ b/src/atlas/parallel/HaloAdjointExchangeImpl.h @@ -0,0 +1,98 @@ +/* + * (C) British Crown Copyright 2020 Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + */ + +#pragma once + +#include "atlas/array/ArrayView.h" +#include "atlas/array/SVector.h" + +namespace atlas { +namespace parallel { + +template +struct halo_zeroer_impl { + template + ATLAS_HOST_DEVICE static void apply( idx_t& buf_idx, const idx_t node_idx, array::ArrayView& field, + DATA_TYPE* recv_buffer, Idx... idxs ) { + for ( idx_t i = 0; i < field.template shape(); ++i ) { + halo_zeroer_impl::apply( buf_idx, node_idx, field, recv_buffer, + idxs..., i ); + } + } +}; + +template +struct halo_zeroer_impl { + template + ATLAS_HOST_DEVICE static void apply( idx_t& buf_idx, const idx_t node_idx, array::ArrayView& field, + DATA_TYPE* recv_buffer, Idx... idxs ) { + field( idxs... ) = 0; + } +}; + +template +struct halo_zeroer_impl { + template + ATLAS_HOST_DEVICE static void apply( idx_t& buf_idx, const idx_t node_idx, array::ArrayView& field, + DATA_TYPE* recv_buffer, Idx... idxs ) { + halo_zeroer_impl::apply( buf_idx, node_idx, field, recv_buffer, idxs..., + node_idx ); + } +}; + +template +struct halo_zeroer_impl { + template + ATLAS_HOST_DEVICE static void apply( idx_t& buf_idx, const idx_t node_idx, array::ArrayView& field, + DATA_TYPE* recv_buffer, Idx... idxs ) { + field( idxs... ) = 0; + } +}; + +template +struct halo_adjoint_unpacker_impl { + template + ATLAS_HOST_DEVICE static void apply( idx_t& buf_idx, const idx_t node_idx, const DATA_TYPE* recv_buffer, + array::ArrayView& field, Idx... idxs ) { + for ( idx_t i = 0; i < field.template shape(); ++i ) { + halo_adjoint_unpacker_impl::apply( buf_idx, node_idx, recv_buffer, + field, idxs..., i ); + } + } +}; + + +template +struct halo_adjoint_unpacker_impl { + template + ATLAS_HOST_DEVICE static void apply( idx_t& buf_idx, const idx_t node_idx, const DATA_TYPE* recv_buffer, + array::ArrayView& field, Idx... idxs ) { + field( idxs... ) += recv_buffer[buf_idx++]; + } +}; + +template +struct halo_adjoint_unpacker_impl { + template + ATLAS_HOST_DEVICE static void apply( idx_t& buf_idx, const idx_t node_idx, const DATA_TYPE* recv_buffer, + array::ArrayView& field, Idx... idxs ) { + halo_adjoint_unpacker_impl::apply( buf_idx, node_idx, recv_buffer, field, + idxs..., node_idx ); + } +}; + +template +struct halo_adjoint_unpacker_impl { + template + ATLAS_HOST_DEVICE static void apply( idx_t& buf_idx, const idx_t node_idx, const DATA_TYPE* recv_buffer, + array::ArrayView& field, Idx... idxs ) { + field( idxs... ) += recv_buffer[buf_idx++]; + } +}; + +} // namespace parallel +} // namespace atlas diff --git a/src/atlas/parallel/HaloExchange.cc b/src/atlas/parallel/HaloExchange.cc index f5ddb5fd8..55fa19d11 100644 --- a/src/atlas/parallel/HaloExchange.cc +++ b/src/atlas/parallel/HaloExchange.cc @@ -27,7 +27,7 @@ namespace parallel { namespace { struct IsGhostPoint { - IsGhostPoint( const int part[], const idx_t ridx[], const idx_t base, const int N ) { + IsGhostPoint( const int part[], const idx_t ridx[], const idx_t base, const int /*N*/ ) { part_ = part; ridx_ = ridx; base_ = base; @@ -81,13 +81,13 @@ void HaloExchange::setup( const int part[], const idx_t remote_idx[], const int recvdispls_.assign( nproc, 0 ); /* - Find the amount of nodes this proc has to receive from each other proc -*/ + Find the amount of nodes this proc has to receive from each other proc + */ IsGhostPoint is_ghost( part, remote_idx, base, parsize_ ); - atlas::vector ghost_points( parsize_ ); idx_t nghost = 0; + atlas_omp_parallel_for( int jj = halo_begin; jj < parsize_; ++jj ) { if ( is_ghost( jj ) ) { idx_t p = part[jj]; @@ -98,11 +98,12 @@ void HaloExchange::setup( const int part[], const idx_t remote_idx[], const int } } } + recvcnt_ = std::accumulate( recvcounts_.begin(), recvcounts_.end(), 0 ); /* - Find the amount of nodes this proc has to send to each other proc -*/ + Find the amount of nodes this proc has to send to each other proc + */ ATLAS_TRACE_MPI( ALLTOALL ) { mpi::comm().allToAll( recvcounts_, sendcounts_ ); } sendcnt_ = std::accumulate( sendcounts_.begin(), sendcounts_.end(), 0 ); @@ -115,16 +116,16 @@ void HaloExchange::setup( const int part[], const idx_t remote_idx[], const int senddispls_[jproc] = sendcounts_[jproc - 1] + senddispls_[jproc - 1]; } /* - Fill vector "send_requests" with remote index of nodes needed, but are on - other procs - We can also fill in the vector "recvmap_" which holds local indices of - requested nodes -*/ - + Fill vector "send_requests" with remote index of nodes needed, but are on + other procs + We can also fill in the vector "recvmap_" which holds local indices of + requested nodes + */ std::vector send_requests( recvcnt_ ); + std::vector recv_requests( sendcnt_ ); + std::vector cnt( nproc, 0 ); recvmap_.resize( recvcnt_ ); - std::vector cnt( nproc, 0 ); for ( idx_t jghost = 0; jghost < nghost; ++jghost ) { const auto jj = ghost_points[jghost]; const int req_idx = recvdispls_[part[jj]] + cnt[part[jj]]; @@ -134,19 +135,18 @@ void HaloExchange::setup( const int part[], const idx_t remote_idx[], const int } /* - Fill vector "recv_requests" with what is needed by other procs -*/ + Fill vector "recv_requests" with what is needed by other procs + */ - std::vector recv_requests( sendcnt_ ); ATLAS_TRACE_MPI( ALLTOALL ) { mpi::comm().allToAllv( send_requests.data(), recvcounts_.data(), recvdispls_.data(), recv_requests.data(), sendcounts_.data(), senddispls_.data() ); } /* - What needs to be sent to other procs is asked by remote_idx, which is local - here -*/ + What needs to be sent to other procs is asked by remote_idx, which is local + here + */ sendmap_.resize( sendcnt_ ); for ( int jj = 0; jj < sendcnt_; ++jj ) { sendmap_[jj] = recv_requests[jj]; @@ -156,7 +156,16 @@ void HaloExchange::setup( const int part[], const idx_t remote_idx[], const int backdoor.parsize = parsize_; } -///////////////////// +void HaloExchange::wait_for_send( std::vector& send_counts_init, + std::vector& send_req ) const { + ATLAS_TRACE_MPI( WAIT, "mpi-wait send" ) { + for ( int jproc = 0; jproc < nproc; ++jproc ) { + if ( send_counts_init[jproc] > 0 ) { + mpi::comm().wait( send_req[jproc] ); + } + } + } +} namespace { @@ -198,6 +207,46 @@ void execute_halo_exchange( HaloExchange* This, Value field[], int var_strides[] throw_NotImplemented( "Rank not supported in halo exchange", Here() ); } } + +template +void execute_adjoint_halo_exchange( HaloExchange* This, Value field[], int var_strides[], int var_extents[], + int var_rank ) { + // WARNING: Only works if there is only one parallel dimension AND being + // slowest moving + + array::ArrayShape shape{This->backdoor.parsize}; + for ( int j = 0; j < var_rank; ++j ) { + shape.push_back( var_extents[j] ); + } + + array::ArrayStrides strides{var_extents[0] * var_strides[0]}; + for ( int j = 0; j < var_rank; ++j ) { + strides.push_back( var_strides[j] ); + } + + std::unique_ptr arr( array::Array::wrap( field, array::ArraySpec{shape, strides} ) ); + + switch ( arr->rank() ) { + case 1: { + This->execute_adjoint( *arr ); + break; + } + case 2: { + This->execute_adjoint( *arr ); + break; + } + case 3: { + This->execute_adjoint( *arr ); + break; + } + case 4: { + This->execute_adjoint( *arr ); + break; + } + default: + throw_NotImplemented( "Rank not supported in halo exchange", Here() ); + } +} } // namespace extern "C" { @@ -214,6 +263,27 @@ void atlas__HaloExchange__setup( HaloExchange* This, int part[], idx_t remote_id This->setup( part, remote_idx, base, size ); } + +void atlas__HaloExchange__execute_adjoint_strided_int( HaloExchange* This, int field[], int var_strides[], + int var_extents[], int var_rank ) { + execute_adjoint_halo_exchange( This, field, var_strides, var_extents, var_rank ); +} + +void atlas__HaloExchange__execute_adjoint_strided_long( HaloExchange* This, long field[], int var_strides[], + int var_extents[], int var_rank ) { + execute_adjoint_halo_exchange( This, field, var_strides, var_extents, var_rank ); +} + +void atlas__HaloExchange__execute_adjoint_strided_float( HaloExchange* This, float field[], int var_strides[], + int var_extents[], int var_rank ) { + execute_adjoint_halo_exchange( This, field, var_strides, var_extents, var_rank ); +} + +void atlas__HaloExchange__execute_adjoint_strided_double( HaloExchange* This, double field[], int var_strides[], + int var_extents[], int var_rank ) { + execute_adjoint_halo_exchange( This, field, var_strides, var_extents, var_rank ); +} + void atlas__HaloExchange__execute_strided_int( HaloExchange* This, int field[], int var_strides[], int var_extents[], int var_rank ) { execute_halo_exchange( This, field, var_strides, var_extents, var_rank ); diff --git a/src/atlas/parallel/HaloExchange.h b/src/atlas/parallel/HaloExchange.h index 2b2aa648d..e883cf868 100644 --- a/src/atlas/parallel/HaloExchange.h +++ b/src/atlas/parallel/HaloExchange.h @@ -18,17 +18,18 @@ #include #include +#include "atlas/parallel/HaloAdjointExchangeImpl.h" #include "atlas/parallel/HaloExchangeImpl.h" #include "atlas/parallel/mpi/Statistics.h" #include "atlas/parallel/mpi/mpi.h" - #include "atlas/array/ArrayView.h" #include "atlas/array/ArrayViewDefs.h" #include "atlas/array/ArrayViewUtil.h" #include "atlas/array/SVector.h" #include "atlas/array_fwd.h" #include "atlas/library/config.h" +#include "atlas/library/defines.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Object.h" @@ -43,6 +44,7 @@ class HaloExchange : public util::Object { public: HaloExchange(); HaloExchange( const std::string& name ); + virtual ~HaloExchange(); public: // methods @@ -58,15 +60,40 @@ class HaloExchange : public util::Object { template void execute( array::Array& field, bool on_device = false ) const; + template + void execute_adjoint( array::Array& field, bool on_device = false ) const; + private: // methods - void create_mappings( std::vector& send_map, std::vector& recv_map, idx_t nb_vars ) const; + idx_t index( idx_t i, idx_t j, idx_t k, idx_t ni, idx_t nj, idx_t /*nk*/ ) const { + return ( i + ni * ( j + nj * k ) ); + } + + idx_t index( idx_t i, idx_t j, idx_t ni, idx_t /*nj*/ ) const { return ( i + ni * j ); } + + template + void counts_displs_setup( const idx_t var_size, std::vector& send_counts_init, + std::vector& recv_counts_init, std::vector& send_counts, + std::vector& recv_counts, std::vector& send_displs, + std::vector& recv_displs ) const; + - template - void create_mappings_impl( std::vector& send_map, std::vector& recv_map, idx_t nb_vars ) const; + template + void ireceive( int tag, std::vector& recv_displs, std::vector& recv_counts, + std::vector& recv_req, DATA_TYPE* recv_buffer ) const; - idx_t index( idx_t i, idx_t j, idx_t k, idx_t ni, idx_t nj, idx_t nk ) const { return ( i + ni * ( j + nj * k ) ); } + template + void isend_and_wait_for_receive( int tag, std::vector& recv_counts_init, + std::vector& recv_req, std::vector& send_displs, + std::vector& send_counts, std::vector& send_req, + DATA_TYPE* send_buffer ) const; - idx_t index( idx_t i, idx_t j, idx_t ni, idx_t nj ) const { return ( i + ni * j ); } + void wait_for_send( std::vector& send_counts, std::vector& send_req ) const; + + template + DATA_TYPE* allocate_buffer( const int buffer_size, const bool on_device ) const; + + template + void deallocate_buffer( DATA_TYPE* buffer, const bool on_device ) const; template void pack_send_buffer( const array::ArrayView& hfield, @@ -78,6 +105,20 @@ class HaloExchange : public util::Object { array::ArrayView& hfield, array::ArrayView& dfield, const bool on_device ) const; + template + void pack_recv_adjoint_buffer( const array::ArrayView& hfield, + const array::ArrayView& dfield, DATA_TYPE* recv_buffer, + int recv_buffer_size, const bool on_device ) const; + + template + void unpack_send_adjoint_buffer( const DATA_TYPE* send_buffer, int send_buffer_size, + array::ArrayView& hfield, + array::ArrayView& dfield, const bool on_device ) const; + + template + void zero_halos( const array::ArrayView& hfield, array::ArrayView& dfield, + DATA_TYPE* recv_buffer, int recv_buffer_size, const bool on_device ) const; + template void var_info( const array::ArrayView& arr, std::vector& varstrides, std::vector& varshape ) const; @@ -107,64 +148,165 @@ class HaloExchange : public util::Object { template void HaloExchange::execute( array::Array& field, bool on_device ) const { + ATLAS_TRACE( "HaloExchange", {"halo-exchange"} ); if ( !is_setup_ ) { throw_Exception( "HaloExchange was not setup", Here() ); } - ATLAS_TRACE( "HaloExchange", {"halo-exchange"} ); + auto field_hv = array::make_host_view( field ); + auto field_dv = + on_device ? array::make_device_view( field ) : array::make_host_view( field ); + + constexpr int parallelDim = array::get_parallel_dim( field_hv ); + idx_t var_size = array::get_var_size( field_hv ); + + int tag( 1 ); + std::size_t nproc_loc( static_cast( nproc ) ); + std::vector inner_counts( nproc_loc ), halo_counts( nproc_loc ); + std::vector inner_counts_init( nproc_loc ), halo_counts_init( nproc_loc ); + std::vector inner_displs( nproc_loc ), halo_displs( nproc_loc ); + std::vector inner_req( nproc_loc ), halo_req( nproc_loc ); + + int inner_size = sendcnt_ * var_size; + int halo_size = recvcnt_ * var_size; + DATA_TYPE* inner_buffer = allocate_buffer( inner_size, on_device ); + DATA_TYPE* halo_buffer = allocate_buffer( halo_size, on_device ); + + counts_displs_setup( var_size, inner_counts_init, halo_counts_init, inner_counts, halo_counts, + inner_displs, halo_displs ); + + ireceive( tag, halo_displs, halo_counts, halo_req, halo_buffer ); + + /// Pack + pack_send_buffer( field_hv, field_dv, inner_buffer, inner_size, on_device ); + + isend_and_wait_for_receive( tag, halo_counts_init, halo_req, inner_displs, inner_counts, inner_req, + inner_buffer ); + + /// Unpack + unpack_recv_buffer( halo_buffer, halo_size, field_hv, field_dv, on_device ); + + wait_for_send( inner_counts_init, inner_req ); + + deallocate_buffer( inner_buffer, on_device ); + deallocate_buffer( halo_buffer, on_device ); +} + +template +void HaloExchange::execute_adjoint( array::Array& field, bool on_device ) const { + if ( !is_setup_ ) { + throw_Exception( "HaloExchange was not setup", Here() ); + } + + ATLAS_TRACE( "HaloExchange", {"halo-exchange-adjoint"} ); auto field_hv = array::make_host_view( field ); + auto field_dv = + on_device ? array::make_device_view( field ) : array::make_host_view( field ); - int tag = 1; constexpr int parallelDim = array::get_parallel_dim( field_hv ); idx_t var_size = array::get_var_size( field_hv ); - int send_size = sendcnt_ * var_size; - int recv_size = recvcnt_ * var_size; - DATA_TYPE* send_buffer{nullptr}; - DATA_TYPE* recv_buffer{nullptr}; + int tag( 1 ); + std::size_t nproc_loc( static_cast( nproc ) ); + std::vector halo_counts( nproc_loc ), inner_counts( nproc_loc ); + std::vector halo_counts_init( nproc_loc ), inner_counts_init( nproc_loc ); + std::vector halo_displs( nproc_loc ), inner_displs( nproc_loc ); + std::vector halo_req( nproc_loc ), inner_req( nproc_loc ); + + int halo_size = sendcnt_ * var_size; + int inner_size = recvcnt_ * var_size; + DATA_TYPE* halo_buffer = allocate_buffer( halo_size, on_device ); + DATA_TYPE* inner_buffer = allocate_buffer( inner_size, on_device ); + + counts_displs_setup( var_size, halo_counts_init, inner_counts_init, halo_counts, inner_counts, + halo_displs, inner_displs ); + + ireceive( tag, halo_displs, halo_counts, halo_req, halo_buffer ); + + /// Pack + pack_recv_adjoint_buffer( field_hv, field_dv, inner_buffer, inner_size, on_device ); + + /// Send + isend_and_wait_for_receive( tag, halo_counts_init, halo_req, inner_displs, inner_counts, inner_req, + inner_buffer ); + + /// Unpack + unpack_send_adjoint_buffer( halo_buffer, halo_size, field_hv, field_dv, on_device ); + + /// Wait for sending to finish + wait_for_send( inner_counts_init, inner_req ); + + zero_halos( field_hv, field_dv, halo_buffer, halo_size, on_device ); + + deallocate_buffer( halo_buffer, on_device ); + deallocate_buffer( inner_buffer, on_device ); +} + +template +DATA_TYPE* HaloExchange::allocate_buffer( const int buffer_size, const bool on_device ) const { + DATA_TYPE* buffer{nullptr}; + if ( on_device ) { - util::allocate_devicemem( send_buffer, send_size ); - util::allocate_devicemem( recv_buffer, recv_size ); + util::allocate_devicemem( buffer, buffer_size ); } else { - util::allocate_hostmem( send_buffer, send_size ); - util::allocate_hostmem( recv_buffer, recv_size ); + util::allocate_hostmem( buffer, buffer_size ); + } + + return buffer; +} + + +template +void HaloExchange::deallocate_buffer( DATA_TYPE* buffer, const bool on_device ) const { + if ( on_device ) { + util::delete_devicemem( buffer ); } - std::vector send_displs( nproc ); - std::vector recv_displs( nproc ); - std::vector send_counts( nproc ); - std::vector recv_counts( nproc ); - - std::vector send_req( nproc ); - std::vector recv_req( nproc ); - - for ( int jproc = 0; jproc < nproc; ++jproc ) { - send_counts[jproc] = sendcounts_[jproc] * var_size; - recv_counts[jproc] = recvcounts_[jproc] * var_size; - send_displs[jproc] = senddispls_[jproc] * var_size; - recv_displs[jproc] = recvdispls_[jproc] * var_size; + else { + util::delete_hostmem( buffer ); } +} - auto field_dv = - on_device ? array::make_device_view( field ) : array::make_host_view( field ); +template +void HaloExchange::counts_displs_setup( const idx_t var_size, std::vector& send_counts_init, + std::vector& recv_counts_init, std::vector& send_counts, + std::vector& recv_counts, std::vector& send_displs, + std::vector& recv_displs ) const { + for ( size_t jproc = 0; jproc < static_cast( nproc ); ++jproc ) { + send_counts_init[jproc] = sendcounts_[jproc]; + recv_counts_init[jproc] = recvcounts_[jproc]; + send_counts[jproc] = sendcounts_[jproc] * var_size; + recv_counts[jproc] = recvcounts_[jproc] * var_size; + send_displs[jproc] = senddispls_[jproc] * var_size; + recv_displs[jproc] = recvdispls_[jproc] * var_size; + } +} + +template +void HaloExchange::ireceive( int tag, std::vector& recv_displs, std::vector& recv_counts, + std::vector& recv_req, DATA_TYPE* recv_buffer ) const { ATLAS_TRACE_MPI( IRECEIVE ) { /// Let MPI know what we like to receive - for ( int jproc = 0; jproc < nproc; ++jproc ) { + for ( size_t jproc = 0; jproc < static_cast( nproc ); ++jproc ) { if ( recv_counts[jproc] > 0 ) { recv_req[jproc] = mpi::comm().iReceive( &recv_buffer[recv_displs[jproc]], recv_counts[jproc], jproc, tag ); } } } +} - /// Pack - pack_send_buffer( field_hv, field_dv, send_buffer, send_size, on_device ); - +template +void HaloExchange::isend_and_wait_for_receive( int tag, std::vector& recv_counts_init, + std::vector& recv_req, + std::vector& send_displs, std::vector& send_counts, + std::vector& send_req, + DATA_TYPE* send_buffer ) const { /// Send ATLAS_TRACE_MPI( ISEND ) { - for ( int jproc = 0; jproc < nproc; ++jproc ) { + for ( size_t jproc = 0; jproc < static_cast( nproc ); ++jproc ) { if ( send_counts[jproc] > 0 ) { send_req[jproc] = mpi::comm().iSend( &send_buffer[send_displs[jproc]], send_counts[jproc], jproc, tag ); } @@ -173,39 +315,20 @@ void HaloExchange::execute( array::Array& field, bool on_device ) const { /// Wait for receiving to finish ATLAS_TRACE_MPI( WAIT, "mpi-wait receive" ) { - for ( int jproc = 0; jproc < nproc; ++jproc ) { - if ( recvcounts_[jproc] > 0 ) { + for ( size_t jproc = 0; jproc < static_cast( nproc ); ++jproc ) { + if ( recv_counts_init[jproc] > 0 ) { mpi::comm().wait( recv_req[jproc] ); } } } - - /// Unpack - unpack_recv_buffer( recv_buffer, recv_size, field_hv, field_dv, on_device ); - - /// Wait for sending to finish - ATLAS_TRACE_MPI( WAIT, "mpi-wait send" ) { - for ( int jproc = 0; jproc < nproc; ++jproc ) { - if ( sendcounts_[jproc] > 0 ) { - mpi::comm().wait( send_req[jproc] ); - } - } - } - if ( on_device ) { - util::delete_devicemem( send_buffer ); - util::delete_devicemem( recv_buffer ); - } - else { - util::delete_hostmem( send_buffer ); - util::delete_hostmem( recv_buffer ); - } } template struct halo_packer { template static void pack( const int sendcnt, array::SVector const& sendmap, - const array::ArrayView& field, DATA_TYPE* send_buffer, int send_buffer_size ) { + const array::ArrayView& field, DATA_TYPE* send_buffer, + int /*send_buffer_size*/ ) { idx_t ibuf = 0; for ( int node_cnt = 0; node_cnt < sendcnt; ++node_cnt ) { const idx_t node_idx = sendmap[node_cnt]; @@ -215,7 +338,7 @@ struct halo_packer { template static void unpack( const int recvcnt, array::SVector const& recvmap, const DATA_TYPE* recv_buffer, - int recv_buffer_size, array::ArrayView& field ) { + int /*recv_buffer_size*/, array::ArrayView& field ) { idx_t ibuf = 0; for ( int node_cnt = 0; node_cnt < recvcnt; ++node_cnt ) { const idx_t node_idx = recvmap[node_cnt]; @@ -224,10 +347,52 @@ struct halo_packer { } }; +template +struct halo_adjoint_packer { + template + static void unpack( const int recvcnt, array::SVector const& recvmap, const DATA_TYPE* recv_buffer, + int /*recv_buffer_size*/, array::ArrayView& field ) { + idx_t ibuf = 0; + for ( int node_cnt = 0; node_cnt < recvcnt; ++node_cnt ) { + const idx_t node_idx = recvmap[node_cnt]; + halo_adjoint_unpacker_impl::apply( ibuf, node_idx, recv_buffer, field ); + } + } +}; + + +template +struct halo_zeroer { + template + static void zeroer( const int sendcnt, array::SVector const& sendmap, array::ArrayView& field, + DATA_TYPE* recv_buffer, int /*recv_buffer_size*/ ) { + idx_t ibuf = 0; + for ( int node_cnt = 0; node_cnt < sendcnt; ++node_cnt ) { + const idx_t node_idx = sendmap[node_cnt]; + halo_zeroer_impl::apply( ibuf, node_idx, field, recv_buffer ); + } + } +}; + + +template +void HaloExchange::zero_halos( ATLAS_MAYBE_UNUSED const array::ArrayView& hfield, + array::ArrayView& dfield, DATA_TYPE* recv_buffer, int recv_size, + ATLAS_MAYBE_UNUSED const bool on_device ) const { + ATLAS_TRACE(); +#if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA + if ( on_device ) { + ATLAS_NOTIMPLEMENTED; + } + else +#endif + halo_zeroer::zeroer( recvcnt_, recvmap_, dfield, recv_buffer, recv_size ); +} + template -void HaloExchange::pack_send_buffer( const array::ArrayView& hfield, +void HaloExchange::pack_send_buffer( ATLAS_MAYBE_UNUSED const array::ArrayView& hfield, const array::ArrayView& dfield, DATA_TYPE* send_buffer, - int send_size, const bool on_device ) const { + int send_size, ATLAS_MAYBE_UNUSED const bool on_device ) const { ATLAS_TRACE(); #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA if ( on_device ) { @@ -241,8 +406,9 @@ void HaloExchange::pack_send_buffer( const array::ArrayView& hf template void HaloExchange::unpack_recv_buffer( const DATA_TYPE* recv_buffer, int recv_size, - array::ArrayView& hfield, - array::ArrayView& dfield, const bool on_device ) const { + ATLAS_MAYBE_UNUSED array::ArrayView& hfield, + array::ArrayView& dfield, + ATLAS_MAYBE_UNUSED const bool on_device ) const { ATLAS_TRACE(); #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA if ( on_device ) { @@ -254,6 +420,37 @@ void HaloExchange::unpack_recv_buffer( const DATA_TYPE* recv_buffer, int recv_si halo_packer::unpack( recvcnt_, recvmap_, recv_buffer, recv_size, dfield ); } +template +void HaloExchange::pack_recv_adjoint_buffer( ATLAS_MAYBE_UNUSED const array::ArrayView& hfield, + const array::ArrayView& dfield, DATA_TYPE* recv_buffer, + int recv_size, ATLAS_MAYBE_UNUSED const bool on_device ) const { + ATLAS_TRACE(); +#if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA + if ( on_device ) { + halo_packer_cuda::pack( recvcnt_, recvmap_, hfield, dfield, recv_buffer, + recv_size ); + } + else +#endif + halo_packer::pack( recvcnt_, recvmap_, dfield, recv_buffer, recv_size ); +} + +template +void HaloExchange::unpack_send_adjoint_buffer( const DATA_TYPE* send_buffer, int send_size, + ATLAS_MAYBE_UNUSED array::ArrayView& hfield, + array::ArrayView& dfield, + ATLAS_MAYBE_UNUSED const bool on_device ) const { + ATLAS_TRACE(); +#if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA + if ( on_device ) { + halo_packer_cuda::unpack( sendcnt_, sendmap_, send_buffer, send_size, hfield, + dfield ); + } + else +#endif + halo_adjoint_packer::unpack( sendcnt_, sendmap_, send_buffer, send_size, dfield ); +} + // template // void HaloExchange::execute( DATA_TYPE field[], idx_t nb_vars ) const //{ @@ -287,6 +484,18 @@ void atlas__HaloExchange__execute_strided_double( HaloExchange* This, double fie void atlas__HaloExchange__execute_int( HaloExchange* This, int field[], int var_rank ); void atlas__HaloExchange__execute_float( HaloExchange* This, float field[], int var_rank ); void atlas__HaloExchange__execute_double( HaloExchange* This, double field[], int var_rank ); + +void atlas__HaloExchange__execute_adjoint_strided_int( HaloExchange* This, int field[], int var_strides[], + int var_shape[], int var_rank ); +void atlas__HaloExchange__execute_adjoint_strided_long( HaloExchange* This, long field[], int var_strides[], + int var_shape[], int var_rank ); +void atlas__HaloExchange__execute_adjoint_strided_float( HaloExchange* This, float field[], int var_strides[], + int var_shape[], int var_rank ); +void atlas__HaloExchange__execute_adjoint_strided_double( HaloExchange* This, double field[], int var_strides[], + int var_shape[], int var_rank ); +void atlas__HaloExchange__execute_adjoint_int( HaloExchange* This, int field[], int var_rank ); +void atlas__HaloExchange__execute_adjoint_float( HaloExchange* This, float field[], int var_rank ); +void atlas__HaloExchange__execute_adjoint_double( HaloExchange* This, double field[], int var_rank ); } //---------------------------------------------------------------------------------------------------------------------- diff --git a/src/tests/functionspace/test_structuredcolumns.cc b/src/tests/functionspace/test_structuredcolumns.cc index c181ba90d..e519a1e79 100644 --- a/src/tests/functionspace/test_structuredcolumns.cc +++ b/src/tests/functionspace/test_structuredcolumns.cc @@ -327,6 +327,88 @@ CASE( "test_functionspace_StructuredColumns halo exchange registration" ) { } } +//----------------------------------------------------------------------------- + +long innerproductwithhalo( const atlas::Field& f1, const atlas::Field& f2 ) { + long sum( 0 ); + + auto view1 = atlas::array::make_view( f1 ); + auto view2 = atlas::array::make_view( f2 ); + + for ( atlas::idx_t jn = 0; jn < f1.shape( 0 ); ++jn ) { + for ( atlas::idx_t jl = 0; jl < f1.levels(); ++jl ) { + sum += view1( jn, jl ) * view2( jn, jl ); + } + } + + atlas::mpi::comm().allReduceInPlace( sum, eckit::mpi::sum() ); + return sum; +} + + +CASE( "test_functionspace_StructuredColumns halo exchange adjoint test 1" ) { + // Adjoint test for fields + + std::string gridname = eckit::Resource( "--grid", "S20x3" ); + + StructuredGrid grid( gridname ); + + util::Config config; + config.set( "levels", 1 ); + config.set( "halo", 1 ); + + long sum1( 0 ), sum2( 0 ); + functionspace::StructuredColumns fs( grid, grid::Partitioner( "checkerboard" ), config ); + Field fieldInit = fs.createField( option::name( "fieldInit" ) ); + + // Field setup values 1 in interior and zeros in halos. + auto view1 = atlas::array::make_view( fieldInit ); + auto i_index = atlas::array::make_view( fs.index_i() ); + auto j_index = atlas::array::make_view( fs.index_j() ); + for ( atlas::idx_t jn = 0; jn < fs.sizeOwned(); ++jn ) { + for ( atlas::idx_t jl = 0; jl < fieldInit.levels(); ++jl ) { + view1( jn, jl ) = 1; + std::cout << " initial interior regions:: " + << " size = " << atlas::mpi::comm().size() << " " + << " jn = " << jn << " " + << " rank = " << atlas::mpi::comm().rank() << " " + << " jn = " << jn << " " + << " view1 = " << view1( jn, 0 ) << " " + << " i_index = " << i_index( jn ) << " " + << " j_index = " << j_index( jn ) << std::endl; + } + } + + Field fieldTmp = fs.createField( option::name( "fieldTmp" ) ); + auto viewTmp = atlas::array::make_view( fieldTmp ); + for ( atlas::idx_t jn = 0; jn < fieldTmp.shape( 0 ); ++jn ) { + for ( atlas::idx_t jl = 0; jl < fieldTmp.levels(); ++jl ) { + viewTmp( jn, jl ) = view1( jn, jl ); + } + } + fieldTmp.haloExchange(); + + sum1 = innerproductwithhalo( fieldTmp, fieldTmp ); + + fieldTmp.adjointHaloExchange(); + + sum2 = innerproductwithhalo( fieldInit, fieldTmp ); + + atlas::Log::info() << "sum1 " << sum1 << "sum2 " << sum2 << std::endl; + EXPECT( sum1 == sum2 ); + + // check that it is zero is in the halo + sum1 = 0; + for ( atlas::idx_t jn = fs.sizeOwned(); jn < fieldTmp.shape( 0 ); ++jn ) { + for ( atlas::idx_t jl = 0; jl < fieldTmp.levels(); ++jl ) { + sum1 += viewTmp( jn, jl ); + } + } + atlas::mpi::comm().allReduceInPlace( sum1, eckit::mpi::sum() ); + EXPECT( sum1 == 0 ); +} + + //----------------------------------------------------------------------------- } // namespace test diff --git a/src/tests/parallel/CMakeLists.txt b/src/tests/parallel/CMakeLists.txt index d70623641..09939af87 100644 --- a/src/tests/parallel/CMakeLists.txt +++ b/src/tests/parallel/CMakeLists.txt @@ -6,6 +6,14 @@ # granted to it by virtue of its status as an intergovernmental organisation nor # does it submit to any jurisdiction. +ecbuild_add_test( TARGET atlas_test_haloexchange_adjoint + MPI 3 + CONDITION eckit_HAVE_MPI + SOURCES test_haloexchange_adjoint.cc + LIBS atlas + ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} +) + ecbuild_add_test( TARGET atlas_test_haloexchange MPI 3 CONDITION eckit_HAVE_MPI diff --git a/src/tests/parallel/test_haloexchange_adjoint.cc b/src/tests/parallel/test_haloexchange_adjoint.cc new file mode 100644 index 000000000..aa011eeb4 --- /dev/null +++ b/src/tests/parallel/test_haloexchange_adjoint.cc @@ -0,0 +1,1587 @@ +/* + * (C) British Crown Copyright, 2020, Met Office + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include +#include +#include +#include + +#include "atlas/array.h" +#include "atlas/array/ArrayView.h" +#include "atlas/array/MakeView.h" +#include "atlas/library/config.h" +#include "atlas/parallel/HaloExchange.h" +#include "atlas/parallel/mpi/mpi.h" + +#include "tests/AtlasTestEnvironment.h" + +/// POD: Type to test +using POD = double; + +namespace atlas { +namespace test { + +template +std::vector vec( const T ( &list )[N] ) { + return std::vector( list, list + N ); +} + +template +size_t eval_idx( size_t pos, std::array& strides, FirstDim first ) { + return static_cast( first ) * strides[pos]; +} + +template +size_t eval_idx( size_t pos, std::array& strides, FirstDim first, SecondDim second ) { + return static_cast( first ) * strides[pos] + eval_idx( pos + 1, strides, second ); +} + +template +size_t eval_idx( size_t pos, std::array& strides, FirstDim first, SecondDim second, ThirdDim third ) { + return static_cast( first ) * strides[pos] + eval_idx( pos + 1, strides, second, third ); +} + +template +struct validate_impl; + +template +struct validate_impl { + template + static void apply( array::ArrayView& arrv, DATA_TYPE arr_c[], std::array& strides, + Int... dims ) { + EXPECT( arrv( dims... ) == arr_c[eval_idx( static_cast( 0 ), strides, dims... )] ); + } +}; + +template +struct validate_impl { + template + static void apply( array::ArrayView& arrv, DATA_TYPE arr_c[], std::array& strides, + Int... dims ) { + for ( idx_t cnt = 0; cnt < arrv.template shape(); ++cnt ) { + validate_impl::apply( arrv, arr_c, strides, dims..., cnt ); + } + } +}; + +template +struct compute_strides; + +template <> +struct compute_strides<0> { + template + static void apply( array::ArrayView& arrv, + std::array( Rank )>& strides ) {} +}; + +template +struct compute_strides { + template + static void apply( array::ArrayView& arrv, + std::array( Rank )>& strides ) { + strides[Dim - 1] = + strides[Dim] * static_cast( arrv.template shape( Dim )>() ); + compute_strides::apply( arrv, strides ); + } +}; + +template +struct validate { + static void apply( array::ArrayView& arrv, DATA_TYPE arr_c[] ) { + std::array strides; + strides[Rank - 1] = 1; + compute_strides::apply( arrv, strides ); + + for ( idx_t i = 0; i < arrv.template shape<0>(); ++i ) { + validate_impl::apply( arrv, arr_c, strides, i ); + } + } +}; + +struct Fixture { + int N; + std::vector nb_nodes; + std::vector part; + std::vector ridx; + std::vector gidx; + bool on_device_; + + std::unique_ptr halo_exchange_std{new parallel::HaloExchange()}; + + Fixture( bool on_device ) : on_device_( on_device ) { + int nnodes_c[] = {5, 6, 7}; + nb_nodes = vec( nnodes_c ); + N = nb_nodes[mpi::comm().rank()]; + + switch ( mpi::comm().rank() ) { + case 0: { + int part_c[] = {2, 0, 0, 0, 1}; + part = vec( part_c ); + idx_t ridx_c[] = {4, 1, 2, 3, 1}; + ridx = vec( ridx_c ); + POD gidx_c[] = {0, 1, 2, 3, 0}; + gidx = vec( gidx_c ); + break; + } + case 1: { + int part_c[] = {0, 1, 1, 1, 2, 2}; + part = vec( part_c ); + idx_t ridx_c[] = {3, 1, 2, 3, 2, 3}; + ridx = vec( ridx_c ); + POD gidx_c[] = {0, 4, 5, 6, 0, 0}; + gidx = vec( gidx_c ); + break; + } + case 2: { + int part_c[] = {1, 1, 2, 2, 2, 0, 0}; + part = vec( part_c ); + idx_t ridx_c[] = {2, 3, 2, 3, 4, 1, 2}; + ridx = vec( ridx_c ); + POD gidx_c[] = {0, 0, 7, 8, 9, 0, 0}; + gidx = vec( gidx_c ); + break; + } + } + halo_exchange_std->setup( part.data(), ridx.data(), 0, N ); + } +}; + +//----------------------------------------------------------------------------- + +void test_rank0_arrview( Fixture& f ) { + array::ArrayT arr( f.N ); + array::ArrayView arrv = array::make_host_view( arr ); + + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {90, 1, 2, 3, 40}; + for ( int j = 0; j < f.N; ++j ) { + arrv( j ) = arr_c[j]; + } + break; + } + case 1: { + POD arr_c[] = {30, 4, 5, 6, 70, 80}; + for ( int j = 0; j < f.N; ++j ) { + arrv( j ) = arr_c[j]; + } + break; + } + case 2: { + POD arr_c[] = {50, 60, 7, 8, 9, 10, 20}; + for ( int j = 0; j < f.N; ++j ) { + arrv( j ) = arr_c[j]; + } + break; + } + } + + arr.syncHostDevice(); + + f.halo_exchange_std->execute_adjoint( arr, f.on_device_ ); + + arr.syncHostDevice(); + + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {0, 11, 22, 33, 0}; + validate::apply( arrv, arr_c ); + break; + } + case 1: { + POD arr_c[] = {0, 44, 55, 66, 0, 0}; + validate::apply( arrv, arr_c ); + break; + } + case 2: { + POD arr_c[] = {0, 0, 77, 88, 99, 0, 0}; + validate::apply( arrv, arr_c ); + break; + } + } +} + +//----------------------------------------------------------------------------- + +void adjoint_test( POD sum1, POD sum2, const std::string string_test ) { + POD tol( 1e-8 ); + + atlas::mpi::comm().allReduceInPlace( sum1, eckit::mpi::sum() ); + + atlas::mpi::comm().allReduceInPlace( sum2, eckit::mpi::sum() ); + + + std::cout << "Adjoint test " << string_test << " " << sum1 << " " << sum2 << std::endl; + + EXPECT( std::abs( sum1 - sum2 ) < tol ); +} + +//----------------------------------------------------------------------------- +// The *_adj_tests perform the adjoint test of the form +// < A x , A x> = < A^T A x , x > +// where A is the halo exchange and A^T is the adjoint of the halo exchange +// and < > are the inner products +//----------------------------------------------------------------------------- +void test_rank0_arrview_adj_test( Fixture& f ) { + array::ArrayT arr_init( f.N ); + array::ArrayT arr( f.N ); + array::ArrayView arrv_init = array::make_host_view( arr_init ); + array::ArrayView arrv = array::make_host_view( arr ); + + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + arrv_init( j ) = ( static_cast( f.part[j] ) != mpi::comm().rank() ? 0 : f.gidx[j] ); + arrv( j ) = arrv_init( j ); + } + + arr.syncHostDevice(); + + f.halo_exchange_std->execute( arr, f.on_device_ ); + + arr.syncHostDevice(); + + // sum1 + POD sum1( 0 ); + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + sum1 += arrv( j ) * arrv( j ); + } + + arr.syncHostDevice(); + + f.halo_exchange_std->execute_adjoint( arr, f.on_device_ ); + + arr.syncHostDevice(); + + // sum2 + POD sum2( 0 ); + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + sum2 += arrv_init( j ) * arrv( j ); + } + + adjoint_test( sum1, sum2, "test_rank0_arrview_adj_test" ); +} + + +void test_rank1( Fixture& f ) { + array::ArrayT arr( f.N, 2 ); + array::ArrayView arrv = array::make_host_view( arr ); + + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {9, 1, 2, 3, 4}; + for ( int j = 0; j < f.N; ++j ) { + arrv( j, 0 ) = arr_c[j] * 10; + arrv( j, 1 ) = arr_c[j] * 100; + } + break; + } + case 1: { + POD arr_c[] = {3, 4, 5, 6, 7, 8}; + for ( int j = 0; j < f.N; ++j ) { + arrv( j, 0 ) = arr_c[j] * 10; + arrv( j, 1 ) = arr_c[j] * 100; + } + break; + } + case 2: { + POD arr_c[] = {5, 6, 7, 8, 9, 1, 2}; + for ( int j = 0; j < f.N; ++j ) { + arrv( j, 0 ) = arr_c[j] * 10; + arrv( j, 1 ) = arr_c[j] * 100; + } + break; + } + } + + arr.syncHostDevice(); + + f.halo_exchange_std->execute_adjoint( arr, f.on_device_ ); + + arr.syncHostDevice(); + + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {0, 0, 20, 200, 40, 400, 60, 600, 0, 0}; + validate::apply( arrv, arr_c ); + break; + } + case 1: { + POD arr_c[] = {0, 0, 80, 800, 100, 1000, 120, 1200, 0, 0, 0, 0}; + validate::apply( arrv, arr_c ); + break; + } + case 2: { + POD arr_c[] = {0, 0, 0, 0, 140, 1400, 160, 1600, 180, 1800, 0, 0, 0, 0}; + validate::apply( arrv, arr_c ); + break; + } + } +} + +void test_rank1_adj_test( Fixture& f ) { + array::ArrayT arr_init( f.N, 2 ); + array::ArrayT arr( f.N, 2 ); + array::ArrayView arrv_init = array::make_host_view( arr_init ); + array::ArrayView arrv = array::make_host_view( arr ); + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + arrv_init( j, 0ul ) = ( static_cast( f.part[j] ) != mpi::comm().rank() ? 0 : f.gidx[j] * 10 ); + arrv_init( j, 1ul ) = ( static_cast( f.part[j] ) != mpi::comm().rank() ? 0 : f.gidx[j] * 100 ); + arrv( j, 0ul ) = arrv_init( j, 0ul ); + arrv( j, 1ul ) = arrv_init( j, 1ul ); + } + + arr.syncHostDevice(); + + f.halo_exchange_std->execute( arr, f.on_device_ ); + + arr.syncHostDevice(); + + // sum1 + POD sum1( 0 ); + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + for ( std::size_t i = 0; i < 2; ++i ) { + sum1 += arrv( j, i ) * arrv( j, i ); + } + } + + arr.syncHostDevice(); + + f.halo_exchange_std->execute_adjoint( arr, f.on_device_ ); + + arr.syncHostDevice(); + + // sum2 + POD sum2( 0 ); + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + for ( std::size_t i = 0; i < 2; ++i ) { + sum2 += arrv_init( j, i ) * arrv( j, i ); + } + } + + adjoint_test( sum1, sum2, "test_rank1_adj_test" ); +} + +void test_rank1_strided_v1( Fixture& f ) { + // create a 2d field from the gidx data, with two components per grid point + array::ArrayT arr_t( f.N, 2 ); + array::ArrayView arrv_t = array::make_host_view( arr_t ); + + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {9, 1, 2, 3, 4}; + for ( int j = 0; j < f.N; ++j ) { + arrv_t( j, 0 ) = arr_c[j] * 10; + arrv_t( j, 1 ) = arr_c[j] * 100; + } + break; + } + case 1: { + POD arr_c[] = {3, 4, 5, 6, 7, 8}; + for ( int j = 0; j < f.N; ++j ) { + arrv_t( j, 0 ) = arr_c[j] * 10; + arrv_t( j, 1 ) = arr_c[j] * 100; + } + break; + } + case 2: { + POD arr_c[] = {5, 6, 7, 8, 9, 1, 2}; + for ( int j = 0; j < f.N; ++j ) { + arrv_t( j, 0 ) = arr_c[j] * 10; + arrv_t( j, 1 ) = arr_c[j] * 100; + } + break; + } + } + + arr_t.syncHostDevice(); + + // create a wrap array where we fake the strides in a way that the second + // dimension + // (number of components) contains only one component but the associated + // stride is 2 + // (i.e. we are only selecting and exchanging the first component of the + // field) + + std::unique_ptr arr( array::Array::wrap( + arrv_t.data(), + array::ArraySpec { + array::make_shape( f.N, 1 ), +#if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA + array::make_strides( 32, 1 ) + } +#else + array::make_strides( 2, 1 ) + } +#endif + ) ); + + arr->syncHostDevice(); + + f.halo_exchange_std->execute_adjoint( *arr, f.on_device_ ); + + arr->syncHostDevice(); + + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {0, 900, 20, 100, 40, 200, 60, 300, 0, 400}; + validate::apply( arrv_t, arr_c ); + break; + } + case 1: { + POD arr_c[] = {0, 300, 80, 400, 100, 500, 120, 600, 0, 700, 0, 800}; + validate::apply( arrv_t, arr_c ); + break; + } + case 2: { + POD arr_c[] = {0, 500, 0, 600, 140, 700, 160, 800, 180, 900, 0, 100, 0, 200}; + validate::apply( arrv_t, arr_c ); + break; + } + } +} + +void test_rank1_strided_v1_adj_test( Fixture& f ) { + // create a 2d field from the gidx data, with two components per grid point + array::ArrayT arr_init_t( f.N, 2 ); + array::ArrayView arrv_init_t = array::make_host_view( arr_init_t ); + array::ArrayT arr_t( f.N, 2 ); + array::ArrayView arrv_t = array::make_host_view( arr_t ); + + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + arrv_init_t( j, 0ul ) = ( static_cast( f.part[j] ) != mpi::comm().rank() ? 0 : f.gidx[j] * 10 ); + arrv_init_t( j, 1ul ) = ( static_cast( f.part[j] ) != mpi::comm().rank() ? 0 : f.gidx[j] * 100 ); + arrv_t( j, 0ul ) = arrv_init_t( j, 0ul ); + arrv_t( j, 1ul ) = arrv_init_t( j, 1ul ); + } + + arr_init_t.syncHostDevice(); + arr_t.syncHostDevice(); + + // create a wrap array where we fake the strides in a way that the second + // dimension + // (number of components) contains only one component but the associated + // stride is 2 + // (i.e. we are only selecting and exchanging the first component of the + // field) + + std::unique_ptr arr( array::Array::wrap( + arrv_t.data(), + array::ArraySpec { + array::make_shape( f.N, 1 ), +#if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA + array::make_strides( 32, 1 ) + } +#else + array::make_strides( 2, 1 ) + } +#endif + ) ); + + arr->syncHostDevice(); + + f.halo_exchange_std->execute( *arr, f.on_device_ ); + + arr->syncHostDevice(); + + // sum1 + POD sum1( 0 ); + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + for ( std::size_t i = 0; i < 2; ++i ) { + sum1 += arrv_t( j, i ) * arrv_t( j, i ); + } + } + + arr->syncHostDevice(); + + f.halo_exchange_std->execute_adjoint( *arr, f.on_device_ ); + + arr->syncHostDevice(); + + // sum2 + POD sum2( 0 ); + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + for ( std::size_t i = 0; i < 2; ++i ) { + sum2 += arrv_init_t( j, i ) * arrv_t( j, i ); + } + } + + adjoint_test( sum1, sum2, "test_rank1_strided_v1_adj_test" ); +} + +void test_rank1_strided_v2( Fixture& f ) { + // create a 2d field from the gidx data, with two components per grid point + array::ArrayT arr_t( f.N, 2 ); + array::ArrayView arrv_t = array::make_host_view( arr_t ); + + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {9, 1, 2, 3, 4}; + for ( int j = 0; j < f.N; ++j ) { + arrv_t( j, 0 ) = arr_c[j] * 10; + arrv_t( j, 1 ) = arr_c[j] * 100; + } + break; + } + case 1: { + POD arr_c[] = {3, 4, 5, 6, 7, 8}; + for ( int j = 0; j < f.N; ++j ) { + arrv_t( j, 0 ) = arr_c[j] * 10; + arrv_t( j, 1 ) = arr_c[j] * 100; + } + break; + } + case 2: { + POD arr_c[] = {5, 6, 7, 8, 9, 1, 2}; + for ( int j = 0; j < f.N; ++j ) { + arrv_t( j, 0 ) = arr_c[j] * 10; + arrv_t( j, 1 ) = arr_c[j] * 100; + } + break; + } + } + + arr_t.syncHostDevice(); + + // create a wrap array where we fake the strides in a way that the second + // dimension + // (number of components) contains only one component but the associated + // stride is 2 + // (i.e. we are only selecting and exchanging the first component of the + // field) + + std::unique_ptr arr( array::Array::wrap( + &( arrv_t( 0, 1 ) ), array::ArraySpec { + array::make_shape( f.N, 1 ), +#if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA + array::make_strides( 32, 1 ) +#else + array::make_strides(2, 1) +#endif + } ) ); + + f.halo_exchange_std->execute_adjoint( *arr, false ); + + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {90, 0, 10, 200, 20, 400, 30, 600, 40, 0}; + validate::apply( arrv_t, arr_c ); + break; + } + case 1: { + POD arr_c[] = {30, 0, 40, 800, 50, 1000, 60, 1200, 70, 0, 80, 0}; + validate::apply( arrv_t, arr_c ); + break; + } + case 2: { + POD arr_c[] = {50, 0, 60, 0, 70, 1400, 80, 1600, 90, 1800, 10, 0, 20, 0}; + validate::apply( arrv_t, arr_c ); + break; + } + } +} + +void test_rank1_strided_v2_adj_test( Fixture& f ) { + // create a 2d field from the gidx data, with two components per grid point + array::ArrayT arr_init_t( f.N, 2 ); + array::ArrayView arrv_init_t = array::make_host_view( arr_init_t ); + array::ArrayT arr_t( f.N, 2 ); + array::ArrayView arrv_t = array::make_host_view( arr_t ); + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + arrv_init_t( j, 0ul ) = ( static_cast( f.part[j] ) != mpi::comm().rank() ? 0 : f.gidx[j] * 10 ); + arrv_init_t( j, 1ul ) = ( static_cast( f.part[j] ) != mpi::comm().rank() ? 0 : f.gidx[j] * 100 ); + arrv_t( j, 0ul ) = arrv_init_t( j, 0ul ); + arrv_t( j, 1ul ) = arrv_init_t( j, 1ul ); + } + + arr_init_t.syncHostDevice(); + arr_t.syncHostDevice(); + + // create a wrap array where we fake the strides in a way that the second + // dimension + // (number of components) contains only one component but the associated + // stride is 2 + // (i.e. we are only selecting and exchanging the second component of the + // field) + + std::unique_ptr arr( array::Array::wrap( + &( arrv_t( 0, 1 ) ), array::ArraySpec { + array::make_shape( f.N, 1 ), +#if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA + array::make_strides( 32, 1 ) +#else + array::make_strides(2, 1) +#endif + } ) ); + + arr->syncHostDevice(); + + f.halo_exchange_std->execute( *arr, f.on_device_ ); + + arr->syncHostDevice(); + + // sum1 + POD sum1( 0 ); + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + for ( std::size_t i = 0; i < 2; ++i ) { + sum1 += arrv_t( j, i ) * arrv_t( j, i ); + } + } + + arr->syncHostDevice(); + + f.halo_exchange_std->execute_adjoint( *arr, f.on_device_ ); + + arr->syncHostDevice(); + + // sum2 + POD sum2( 0 ); + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + for ( std::size_t i = 0; i < 2; ++i ) { + sum2 += arrv_init_t( j, i ) * arrv_t( j, i ); + } + } + + adjoint_test( sum1, sum2, "test_rank1_strided_v2_adj_test" ); +} + + +void test_rank2( Fixture& f ) { + array::ArrayT arr( f.N, 3, 2 ); + array::ArrayView arrv = array::make_host_view( arr ); + + + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {9, 1, 2, 3, 4}; + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + for ( std::size_t i = 0; i < 3; ++i ) { + for ( std::size_t k = 0; k < 2; ++k ) { + arrv( j, i, k ) = ( 2 * static_cast( k ) - 1 ) * arr_c[j] * std::pow( 10, i ); + } + } + } + break; + } + case 1: { + POD arr_c[] = {3, 4, 5, 6, 7, 8}; + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + for ( std::size_t i = 0; i < 3; ++i ) { + for ( std::size_t k = 0; k < 2; ++k ) { + arrv( j, i, k ) = ( 2 * static_cast( k ) - 1 ) * arr_c[j] * std::pow( 10, i ); + } + } + } + break; + } + case 2: { + POD arr_c[] = {5, 6, 7, 8, 9, 1, 2}; + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + for ( std::size_t i = 0; i < 3; ++i ) { + for ( std::size_t k = 0; k < 2; ++k ) { + arrv( j, i, k ) = ( 2 * static_cast( k ) - 1 ) * arr_c[j] * std::pow( 10, i ); + } + } + } + break; + } + } + + arr.syncHostDevice(); + + f.halo_exchange_std->execute_adjoint( arr, f.on_device_ ); + + arr.syncHostDevice(); + + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {0, 0, 0, 0, 0, 0, -2, 2, -20, 20, -200, 200, -4, 4, -40, + 40, -400, 400, -6, 6, -60, 60, -600, 600, 0, 0, 0, 0, 0, 0}; + validate::apply( arrv, arr_c ); + break; + } + case 1: { + POD arr_c[] = {0, 0, 0, 0, 0, 0, -8, 8, -80, 80, -800, 800, -10, 10, -100, 100, -1000, 1000, + -12, 12, -120, 120, -1200, 1200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + validate::apply( arrv, arr_c ); + break; + } + case 2: { + POD arr_c[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -14, 14, + -140, 140, -1400, 1400, -16, 16, -160, 160, -1600, 1600, -18, 18, -180, 180, + -1800, 1800, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + validate::apply( arrv, arr_c ); + break; + } + } +} + +void test_rank2_adj_test( Fixture& f ) { + array::ArrayT arr_init( f.N, 3, 2 ); + array::ArrayView arrv_init = array::make_host_view( arr_init ); + array::ArrayT arr( f.N, 3, 2 ); + array::ArrayView arrv = array::make_host_view( arr ); + + for ( size_t p = 0; p < static_cast( f.N ); ++p ) { + for ( size_t i = 0; i < 3; ++i ) { + arrv_init( p, i, static_cast( 0 ) ) = + ( static_cast( f.part[p] ) != mpi::comm().rank() ? 0 : -f.gidx[p] * std::pow( 10, i ) ); + arrv_init( p, i, static_cast( 1 ) ) = + ( static_cast( f.part[p] ) != mpi::comm().rank() ? 0 : f.gidx[p] * std::pow( 10, i ) ); + arrv( p, i, static_cast( 0 ) ) = arrv_init( p, i, static_cast( 0 ) ); + arrv( p, i, static_cast( 1 ) ) = arrv_init( p, i, static_cast( 1 ) ); + } + } + + arr.syncHostDevice(); + + f.halo_exchange_std->execute( arr, f.on_device_ ); + + // sum1 + POD sum1( 0 ); + for ( std::size_t p = 0; p < static_cast( f.N ); ++p ) { + for ( size_t i = 0; i < 3; ++i ) { + for ( std::size_t k = 0; k < 2; ++k ) { + sum1 += arrv( p, i, k ) * arrv( p, i, k ); + } + } + } + + arr.syncHostDevice(); + + f.halo_exchange_std->execute_adjoint( arr, f.on_device_ ); + + arr.syncHostDevice(); + + // sum2 + POD sum2( 0 ); + for ( std::size_t p = 0; p < static_cast( f.N ); ++p ) { + for ( size_t i = 0; i < 3; ++i ) { + for ( std::size_t k = 0; k < 2; ++k ) { + sum2 += arrv_init( p, i, k ) * arrv( p, i, k ); + } + } + } + + adjoint_test( sum1, sum2, "test_rank2_adj_test" ); +} + +void test_rank2_l1( Fixture& f ) { + array::ArrayT arr_t( f.N, 3, 2 ); + array::ArrayView arrv_t = array::make_host_view( arr_t ); + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {9, 1, 2, 3, 4}; + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + for ( std::size_t i = 0; i < 3; ++i ) { + for ( std::size_t k = 0; k < 2; ++k ) { + arrv_t( j, i, k ) = ( 2 * static_cast( k ) - 1 ) * arr_c[j] * std::pow( 10, i ); + } + } + } + break; + } + case 1: { + POD arr_c[] = {3, 4, 5, 6, 7, 8}; + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + for ( std::size_t i = 0; i < 3; ++i ) { + for ( std::size_t k = 0; k < 2; ++k ) { + arrv_t( j, i, k ) = ( 2 * static_cast( k ) - 1 ) * arr_c[j] * std::pow( 10, i ); + } + } + } + break; + } + case 2: { + POD arr_c[] = {5, 6, 7, 8, 9, 1, 2}; + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + for ( std::size_t i = 0; i < 3; ++i ) { + for ( std::size_t k = 0; k < 2; ++k ) { + arrv_t( j, i, k ) = ( 2 * static_cast( k ) - 1 ) * arr_c[j] * std::pow( 10, i ); + } + } + } + break; + } + } + + arr_t.syncHostDevice(); + + std::unique_ptr arr( array::Array::wrap( + arrv_t.data(), array::ArraySpec { + array::make_shape( f.N, 1, 2 ), +#if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA + array::make_strides( 96, 32, 1 ) +#else + array::make_strides(6, 2, 1) +#endif + } ) ); + + arr_t.syncHostDevice(); + + f.halo_exchange_std->execute_adjoint( *arr, false ); + + arr_t.syncHostDevice(); + + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {0, 0, -90, 90, -900, 900, // halo + -2, 2, -10, 10, -100, 100, // core + -4, 4, -20, 20, -200, 200, // core + -6, 6, -30, 30, -300, 300, // core + 0, 0, -40, 40, -400, 400}; // halo + validate::apply( arrv_t, arr_c ); + break; + } + case 1: { + POD arr_c[] = {0, 0, -30, 30, -300, 300, // halo + -8, 8, -40, 40, -400, 400, // core + -10, 10, -50, 50, -500, 500, // core + -12, 12, -60, 60, -600, 600, // core + 0, 0, -70, 70, -700, 700, // halo + 0, 0, -80, 80, -800, 800}; // halo + validate::apply( arrv_t, arr_c ); + break; + } + case 2: { + POD arr_c[] = {0, 0, -50, 50, -500, 500, // halo + 0, 0, -60, 60, -600, 600, // halo + -14, 14, -70, 70, -700, 700, // core + -16, 16, -80, 80, -800, 800, // core + -18, 18, -90, 90, -900, 900, // core + 0, 0, -10, 10, -100, 100, // halo + 0, 0, -20, 20, -200, 200}; // halo + validate::apply( arrv_t, arr_c ); + break; + } + } +} + +void test_rank2_l1_adj_test( Fixture& f ) { + array::ArrayT arr_init_t( f.N, 3, 2 ); + array::ArrayView arrv_init_t = array::make_host_view( arr_init_t ); + array::ArrayT arr_t( f.N, 3, 2 ); + array::ArrayView arrv_t = array::make_host_view( arr_t ); + for ( std::size_t p = 0; p < static_cast( f.N ); ++p ) { + for ( std::size_t i = 0; i < 3; ++i ) { + arrv_init_t( p, i, static_cast( 0 ) ) = + ( static_cast( f.part[p] ) != mpi::comm().rank() ? 0 : -f.gidx[p] * std::pow( 10, i ) ); + arrv_init_t( p, i, static_cast( 1 ) ) = + ( size_t( f.part[p] ) != mpi::comm().rank() ? 0 : f.gidx[p] * std::pow( 10, i ) ); + arrv_t( p, i, static_cast( 0 ) ) = arrv_init_t( p, i, static_cast( 0 ) ); + arrv_t( p, i, static_cast( 1 ) ) = arrv_init_t( p, i, static_cast( 1 ) ); + } + } + arr_t.syncHostDevice(); + + std::unique_ptr arr( array::Array::wrap( + arrv_t.data(), array::ArraySpec { + array::make_shape( f.N, 1, 2 ), +#if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA + array::make_strides( 96, 32, 1 ) +#else + array::make_strides(6, 2, 1) +#endif + } ) ); + + arr_t.syncHostDevice(); + + f.halo_exchange_std->execute( *arr, false ); + + arr_t.syncHostDevice(); + + // sum1 + POD sum1( 0 ); + for ( std::size_t p = 0; p < static_cast( f.N ); ++p ) { + for ( std::size_t i = 0; i < 3; ++i ) { + for ( std::size_t k = 0; k < 2; ++k ) { + sum1 += arrv_t( p, i, k ) * arrv_t( p, i, k ); + } + } + } + + arr->syncHostDevice(); + + f.halo_exchange_std->execute_adjoint( *arr, false ); + + arr->syncHostDevice(); + + // sum2 + POD sum2( 0 ); + for ( std::size_t p = 0; p < static_cast( f.N ); ++p ) { + for ( std::size_t i = 0; i < 3; ++i ) { + for ( std::size_t k = 0; k < 2; ++k ) { + sum2 += arrv_init_t( p, i, k ) * arrv_t( p, i, k ); + } + } + } + + adjoint_test( sum1, sum2, "test_rank2_l1_adj_test" ); +} + +void test_rank2_l2_v2( Fixture& f ) { +#if ATLAS_GRIDTOOLS_STORAGE_BACKEND_HOST + // Test rank 2 halo-exchange + array::ArrayT arr_t( f.N, 3, 2 ); + array::ArrayView arrv_t = array::make_host_view( arr_t ); + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {9, 1, 2, 3, 4}; + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + for ( std::size_t i = 0; i < 3; ++i ) { + for ( std::size_t k = 0; k < 2; ++k ) { + arrv_t( j, i, k ) = ( 2 * static_cast( k ) - 1 ) * arr_c[j] * std::pow( 10, i ); + } + } + } + break; + } + case 1: { + POD arr_c[] = {3, 4, 5, 6, 7, 8}; + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + for ( std::size_t i = 0; i < 3; ++i ) { + for ( std::size_t k = 0; k < 2; ++k ) { + arrv_t( j, i, k ) = ( 2 * static_cast( k ) - 1 ) * arr_c[j] * std::pow( 10, i ); + } + } + } + break; + } + case 2: { + POD arr_c[] = {5, 6, 7, 8, 9, 1, 2}; + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + for ( std::size_t i = 0; i < 3; ++i ) { + for ( std::size_t k = 0; k < 2; ++k ) { + arrv_t( j, i, k ) = ( 2 * static_cast( k ) - 1 ) * arr_c[j] * std::pow( 10, i ); + } + } + } + break; + } + } + + /* + for ( int p = 0; p < f.N; ++p ) { + for ( size_t i = 0; i < 3; ++i ) { + arrv_t( (size_t)p, i, (size_t)0 ) = + ( size_t( f.part[p] ) != mpi::comm().rank() ? 0 : -f.gidx[p] * std::pow( 10, i ) ); + arrv_t( (size_t)p, i, (size_t)1 ) = + ( size_t( f.part[p] ) != mpi::comm().rank() ? 0 : f.gidx[p] * std::pow( 10, i ) ); + } + } + */ + + std::unique_ptr arr( array::Array::wrap( + &arrv_t( 0, 1, 1 ), array::ArraySpec { + array::make_shape( f.N, 1, 1 ), +#if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA + array::make_strides( 192, 32, 1 ) +#else + array::make_strides(6, 2, 1) +#endif + } ) ); + + f.halo_exchange_std->execute_adjoint( *arr, f.on_device_ ); + + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {-9, 9, -90, 0, -900, 900, // halo + -1, 1, -10, 20, -100, 100, // core + -2, 2, -20, 40, -200, 200, // core + -3, 3, -30, 60, -300, 300, // core + -4, 4, -40, 0, -400, 400}; // halo + validate::apply( arrv_t, arr_c ); + break; + } + case 1: { + POD arr_c[] = {-3, + 3, + -30, + 0, + -300, + 300, // halo + -4, + 4, + -40, + 80, + -400, + 400, // core + -5, + 5, + -50, + 100, + -500, + 500, // core + -6, + 6, + -60, + 120, + -600, + 600, // core + -7, + 7, + -70, + 0, + -700, + 700 // halo + - 8, + 8, + -80, + 0, + -800, + 800}; // halo + validate::apply( arrv_t, arr_c ); + break; + } + case 2: { + POD arr_c[] = {-5, 5, -50, 0, -500, 500, // halo + -6, 6, -60, 0, -600, 600, // halo + -7, 7, -70, 140, -700, 700, // core + -8, 8, -80, 160, -800, 800, // core + -9, 9, -90, 180, -900, 900, // core + -1, 1, -10, 0, -100, 100, // halo + -2, 2, -20, 0, -200, 200}; // halo + validate::apply( arrv_t, arr_c ); + break; + } + } +#endif +} + +void test_rank2_v2( Fixture& f ) { +#if ATLAS_GRIDTOOLS_STORAGE_BACKEND_HOST + array::ArrayT arr_t( f.N, 3, 2 ); + array::ArrayView arrv_t = array::make_view( arr_t ); + + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {9, 1, 2, 3, 4}; + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + for ( std::size_t i = 0; i < 3; ++i ) { + for ( std::size_t k = 0; k < 2; ++k ) { + arrv_t( j, i, k ) = ( 2 * static_cast( k ) - 1 ) * arr_c[j] * std::pow( 10, i ); + } + } + } + break; + } + case 1: { + POD arr_c[] = {3, 4, 5, 6, 7, 8}; + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + for ( std::size_t i = 0; i < 3; ++i ) { + for ( std::size_t k = 0; k < 2; ++k ) { + arrv_t( j, i, k ) = ( 2 * static_cast( k ) - 1 ) * arr_c[j] * std::pow( 10, i ); + } + } + } + break; + } + case 2: { + POD arr_c[] = {5, 6, 7, 8, 9, 1, 2}; + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + for ( std::size_t i = 0; i < 3; ++i ) { + for ( std::size_t k = 0; k < 2; ++k ) { + arrv_t( j, i, k ) = ( 2 * static_cast( k ) - 1 ) * arr_c[j] * std::pow( 10, i ); + } + } + } + break; + } + } + /* + for ( int p = 0; p < f.N; ++p ) { + for ( size_t i = 0; i < 3; ++i ) { + arrv_t( (size_t)p, i, (size_t)0 ) = + ( size_t( f.part[p] ) != mpi::comm().rank() ? 0 : -f.gidx[p] * std::pow( 10, i ) ); + arrv_t( (size_t)p, i, (size_t)1 ) = + ( size_t( f.part[p] ) != mpi::comm().rank() ? 0 : f.gidx[p] * std::pow( 10, i ) ); + } + } +*/ + + std::unique_ptr arr( array::Array::wrap( + &arrv_t( 0, 0, 1 ), array::ArraySpec { + array::make_shape( f.N, 3, 1 ), +#if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA + array::make_strides( 192, 32, 2 ) +#else + array::make_strides(6, 2, 2) +#endif + } ) ); + + + f.halo_exchange_std->execute_adjoint( *arr, f.on_device_ ); + + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {-9, 0, -90, 0, -900, 0, // halo + -1, 2, -10, 20, -100, 200, // core + -2, 4, -20, 40, -200, 400, // core + -3, 6, -30, 60, -300, 600, // core + -4, 0, -40, 0, -400, 0}; // halo + validate::apply( arrv_t, arr_c ); + break; + } + case 1: { + POD arr_c[] = {-3, 0, -30, 0, -300, 0, // halo + -4, 8, -40, 80, -400, 800, // core + -5, 10, -50, 100, -500, 1000, // core + -6, 12, -60, 120, -600, 1200, // core + -7, 0, -70, 0, -700, 0, // halo + -8, 0, -80, 0, -800, 0}; // halo + validate::apply( arrv_t, arr_c ); + break; + } + case 2: { + POD arr_c[] = {-5, 0, -50, 0, -500, 0, // halo + -6, 0, -60, 0, -600, 0, // halo + -7, 14, -70, 140, -700, 1400, // core + -8, 16, -80, 160, -800, 1600, // core + -9, 18, -90, 180, -900, 1800, // core + -1, 0, -10, 0, -100, 0, // halo + -2, 0, -20, 0, -200, 0}; // halo + validate::apply( arrv_t, arr_c ); + break; + } + } +#endif +} + +void test_rank0_wrap( Fixture& f ) { + std::unique_ptr arr( array::Array::wrap( f.gidx.data(), array::make_shape( f.N ) ) ); + array::ArrayView arrv = array::make_view( *arr ); + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {0, 2, 4, 6, 0}; + for ( int j = 0; j < f.N; ++j ) { + arrv( j ) = arr_c[j]; + } + break; + } + case 1: { + POD arr_c[] = {0, 8, 10, 12, 0, 0}; + for ( int j = 0; j < f.N; ++j ) { + arrv( j ) = arr_c[j]; + } + break; + } + case 2: { + POD arr_c[] = {0, 0, 14, 16, 18, 0, 0}; + for ( int j = 0; j < f.N; ++j ) { + arrv( j ) = arr_c[j]; + } + break; + } + } + + arr->syncHostDevice(); + + f.halo_exchange_std->execute_adjoint( *arr, f.on_device_ ); + + arr->syncHostDevice(); + + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {0, 2, 4, 6, 0}; + validate::apply( arrv, arr_c ); + break; + } + case 1: { + POD arr_c[] = {0, 8, 10, 12, 0, 0}; + validate::apply( arrv, arr_c ); + break; + } + case 2: { + POD arr_c[] = {0, 0, 14, 16, 18, 0, 0}; + validate::apply( arrv, arr_c ); + break; + } + } +} + +void test_rank0_wrap_adj_test( Fixture& f ) { + std::unique_ptr arr( array::Array::wrap( f.gidx.data(), array::make_shape( f.N ) ) ); + array::ArrayView arrv = array::make_view( *arr ); + + // note we have to be VERY careful here + // we can't do + //std::unique_ptr arr_init( + // array::Array::wrap( f.gidx.data(), array::make_shape( f.N ) ) ); + //array::ArrayView arrv_init = + // array::make_view( *arr_init ); + // + // as both pointers will end up sharing the same memory !! + // + // but instead we need to do; + + array::ArrayT arr_init( f.N ); + array::ArrayView arrv_init = array::make_host_view( arr_init ); + + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + arrv_init( j ) = arrv( j ); + } + + arr->syncHostDevice(); + + f.halo_exchange_std->execute( *arr, f.on_device_ ); + + // sum1 + POD sum1( 0 ); + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + sum1 += arrv( j ) * arrv( j ); + } + + arr->syncHostDevice(); + + f.halo_exchange_std->execute_adjoint( *arr, f.on_device_ ); + + arr->syncHostDevice(); + + // sum2 + POD sum2( 0 ); + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + sum2 += arrv_init( j ) * arrv( j ); + } + + adjoint_test( sum1, sum2, "test_rank0_wrap_adj_test" ); +} + +void test_rank1_paralleldim1( Fixture& f ) { + array::ArrayT arr( 2, f.N ); + array::ArrayView arrv = array::make_view( arr ); + + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {9, 1, 2, 3, 4}; + for ( int j = 0; j < f.N; ++j ) { + arrv( 0, j ) = arr_c[j] * 10; + arrv( 1, j ) = arr_c[j] * 100; + } + break; + } + case 1: { + POD arr_c[] = {3, 4, 5, 6, 7, 8}; + for ( int j = 0; j < f.N; ++j ) { + arrv( 0, j ) = arr_c[j] * 10; + arrv( 1, j ) = arr_c[j] * 100; + } + break; + } + case 2: { + POD arr_c[] = {5, 6, 7, 8, 9, 1, 2}; + for ( int j = 0; j < f.N; ++j ) { + arrv( 0, j ) = arr_c[j] * 10; + arrv( 1, j ) = arr_c[j] * 100; + } + break; + } + } + + f.halo_exchange_std->execute_adjoint( arr, false ); + + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {0, 20, 40, 60, 0, 0, 200, 400, 600, 0}; // 90,900, 10,100, 20,200, 30,300, 40,400 }; + validate::apply( arrv, arr_c ); + break; + } + case 1: { + POD arr_c[] = {0, 80, 100, 120, 0, 0, 0, 800, 1000, 1200, 0, 0}; + validate::apply( arrv, arr_c ); + break; + } + case 2: { + POD arr_c[] = {0, 0, 140, 160, 180, 0, 0, 0, 0, 1400, 1600, 1800, 0, 0}; + validate::apply( arrv, arr_c ); + break; + } + } +} + +void test_rank1_paralleldim1_adj_test( Fixture& f ) { + array::ArrayT arr_init( 2, f.N ); + array::ArrayView arrv_init = array::make_view( arr_init ); + array::ArrayT arr( 2, f.N ); + array::ArrayView arrv = array::make_view( arr ); + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + arrv_init( 0ul, j ) = ( static_cast( f.part[j] ) != mpi::comm().rank() ? 0 : f.gidx[j] * 10 ); + arrv_init( 1ul, j ) = ( static_cast( f.part[j] ) != mpi::comm().rank() ? 0 : f.gidx[j] * 100 ); + arrv( 0ul, j ) = arrv_init( 0ul, j ); + arrv( 1ul, j ) = arrv_init( 1ul, j ); + } + + f.halo_exchange_std->execute( arr, false ); + + // sum1 + POD sum1( 0 ); + for ( std::size_t p = 0; p < static_cast( f.N ); ++p ) { + for ( std::size_t i = 0; i < 2; ++i ) { + sum1 += arrv( i, p ) * arrv( i, p ); + } + } + + f.halo_exchange_std->execute_adjoint( arr, false ); + + // sum2 + POD sum2( 0 ); + for ( std::size_t p = 0; p < static_cast( f.N ); ++p ) { + for ( std::size_t i = 0; i < 2; ++i ) { + sum2 += arrv_init( i, p ) * arrv( i, p ); + } + } + + adjoint_test( sum1, sum2, "test_rank1_paralleldim1_adj_test" ); +} + +void test_rank2_paralleldim2( Fixture& f ) { + array::ArrayT arr( 3, f.N, 2 ); + array::ArrayView arrv = array::make_view( arr ); + + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {9, 1, 2, 3, 4}; + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + for ( std::size_t i = 0; i < 3; ++i ) { + for ( std::size_t k = 0; k < 2; ++k ) { + arrv( i, j, k ) = ( 2 * static_cast( k ) - 1 ) * arr_c[j] * std::pow( 10, i ); + } + } + } + break; + } + case 1: { + POD arr_c[] = {3, 4, 5, 6, 7, 8}; + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + for ( std::size_t i = 0; i < 3; ++i ) { + for ( std::size_t k = 0; k < 2; ++k ) { + arrv( i, j, k ) = ( 2 * static_cast( k ) - 1 ) * arr_c[j] * std::pow( 10, i ); + } + } + } + break; + } + case 2: { + POD arr_c[] = {5, 6, 7, 8, 9, 1, 2}; + for ( std::size_t j = 0; j < static_cast( f.N ); ++j ) { + for ( std::size_t i = 0; i < 3; ++i ) { + for ( std::size_t k = 0; k < 2; ++k ) { + arrv( i, j, k ) = ( 2 * static_cast( k ) - 1 ) * arr_c[j] * std::pow( 10, i ); + } + } + } + break; + } + } + + f.halo_exchange_std->execute_adjoint>( arr, false ); + + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {0, 0, -2, 2, -4, 4, -6, 6, 0, 0, 0, 0, -20, 20, -40, + 40, -60, 60, 0, 0, 0, 0, -200, 200, -400, 400, -600, 600, 0, 0}; + + validate::apply( arrv, arr_c ); + break; + } + case 1: { + POD arr_c[] = {0, 0, -8, 8, -10, 10, -12, 12, 0, 0, 0, 0, 0, 0, -80, 80, -100, 100, + -120, 120, 0, 0, 0, 0, 0, 0, -800, 800, -1000, 1000, -1200, 1200, 0, 0, 0, 0}; + validate::apply( arrv, arr_c ); + break; + } + case 2: { + POD arr_c[] = {0, 0, 0, 0, -14, 14, -16, 16, -18, 18, 0, 0, 0, 0, + 0, 0, 0, 0, -140, 140, -160, 160, -180, 180, 0, 0, 0, 0, + 0, 0, 0, 0, -1400, 1400, -1600, 1600, -1800, 1800, 0, 0, 0, 0}; + validate::apply( arrv, arr_c ); + break; + } + } +} + +void test_rank2_paralleldim2_adj_test( Fixture& f ) { + array::ArrayT arr_init( 3, f.N, 2 ); + array::ArrayView arrv_init = array::make_view( arr_init ); + array::ArrayT arr( 3, f.N, 2 ); + array::ArrayView arrv = array::make_view( arr ); + for ( size_t p = 0; p < static_cast( f.N ); ++p ) { + for ( size_t i = 0; i < 3; ++i ) { + arrv_init( i, p, static_cast( 0 ) ) = + ( size_t( f.part[p] ) != mpi::comm().rank() ? 0 : -f.gidx[p] * std::pow( 10, i ) ); + arrv_init( i, p, static_cast( 1 ) ) = + ( size_t( f.part[p] ) != mpi::comm().rank() ? 0 : f.gidx[p] * std::pow( 10, i ) ); + arrv( i, p, static_cast( 0 ) ) = arrv_init( i, p, static_cast( 0 ) ); + arrv( i, p, static_cast( 1 ) ) = arrv_init( i, p, static_cast( 1 ) ); + } + } + + f.halo_exchange_std->execute>( arr, false ); + + + // sum1 + POD sum1( 0 ); + for ( std::size_t p = 0; p < static_cast( f.N ); ++p ) { + for ( std::size_t i = 0; i < 3; ++i ) { + for ( std::size_t k = 0; k < 2; ++k ) { + sum1 += arrv( i, p, k ) * arrv( i, p, k ); + } + } + } + + f.halo_exchange_std->execute_adjoint>( arr, false ); + + // sum2 + POD sum2( 0 ); + for ( std::size_t p = 0; p < static_cast( f.N ); ++p ) { + for ( std::size_t i = 0; i < 3; ++i ) { + for ( std::size_t k = 0; k < 2; ++k ) { + sum2 += arrv_init( i, p, k ) * arrv( i, p, k ); + } + } + } + + adjoint_test( sum1, sum2, "test_rank2_paralleldim2_adj_test" ); +} + + +void test_rank1_cinterface( Fixture& f ) { +#if ATLAS_GRIDTOOLS_STORAGE_BACKEND_HOST + + std::cout << "entering test_rank1_cinterface" << std::endl; + + array::ArrayT arr( f.N, 2 ); + + array::ArrayView arrv = array::make_host_view( arr ); + + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {9, 1, 2, 3, 4}; + for ( int j = 0; j < f.N; ++j ) { + arrv( j, 0 ) = arr_c[j] * 10; + arrv( j, 1 ) = arr_c[j] * 100; + } + break; + } + case 1: { + POD arr_c[] = {3, 4, 5, 6, 7, 8}; + for ( int j = 0; j < f.N; ++j ) { + arrv( j, 0 ) = arr_c[j] * 10; + arrv( j, 1 ) = arr_c[j] * 100; + } + break; + } + case 2: { + POD arr_c[] = {5, 6, 7, 8, 9, 1, 2}; + for ( int j = 0; j < f.N; ++j ) { + arrv( j, 0 ) = arr_c[j] * 10; + arrv( j, 1 ) = arr_c[j] * 100; + } + break; + } + } + + arr.syncHostDevice(); + + int shapes[2] = {(int)arrv.shape( 0 ), (int)arrv.shape( 1 )}; + int strides[2] = {(int)arrv.stride( 0 ), (int)arrv.stride( 1 )}; + + atlas__HaloExchange__execute_adjoint_strided_double( f.halo_exchange_std.get(), arrv.data(), &( strides[1] ), + &( shapes[1] ), 1 ); + + switch ( mpi::comm().rank() ) { + case 0: { + POD arr_c[] = {0, 0, 20, 200, 40, 400, 60, 600, 0, 0}; + validate::apply( arrv, arr_c ); + break; + } + case 1: { + POD arr_c[] = {0, 0, 80, 800, 100, 1000, 120, 1200, 0, 0, 0, 0}; + validate::apply( arrv, arr_c ); + break; + } + case 2: { + POD arr_c[] = {0, 0, 0, 0, 140, 1400, 160, 1600, 180, 1800, 0, 0, 0, 0}; + validate::apply( arrv, arr_c ); + break; + } + } +#endif +} + +CASE( "test_haloexchange_adjoint" ) { + Fixture f( false ); + + SECTION( "test_rank0_arrview" ) { test_rank0_arrview( f ); } + + SECTION( "test_rank0_arrview_adj_test" ) { test_rank0_arrview_adj_test( f ); } + + SECTION( "test_rank1" ) { test_rank1( f ); } + + SECTION( "test_rank1_adj_test" ) { test_rank1_adj_test( f ); } + + SECTION( "test_rank1_strided_v1" ) { test_rank1_strided_v1( f ); } + + SECTION( "test_rank1_strided_v1_adj_test" ) { test_rank1_strided_v1_adj_test( f ); } + + SECTION( "test_rank1_strided_v2" ) { test_rank1_strided_v2( f ); } + + SECTION( "test_rank1_strided_v2_adj_test" ) { test_rank1_strided_v2_adj_test( f ); } + + SECTION( "test_rank2" ) { test_rank2( f ); } + + SECTION( "test_rank2_adj_test" ) { test_rank2_adj_test( f ); } + + SECTION( "test_rank2_l1" ) { test_rank2_l1( f ); } + + SECTION( "test_rank2_l1_adj_test" ) { test_rank2_l1_adj_test( f ); } + + /* NOT TESTED AS USES ATLAS_GRIDTOOLS_STORAGE_BACKEND_HOST + SECTION( "test_rank2_l2_v2" ) { test_rank2_l2_v2( f ); } + + SECTION( "test_rank2_v2" ) { test_rank2_v2( f ); } +*/ + + SECTION( "test_rank0_wrap" ) { test_rank0_wrap( f ); } + + SECTION( "test_rank0_wrap_adj_test" ) { test_rank0_wrap_adj_test( f ); } + + SECTION( "test_rank1_paralleldim_1" ) { test_rank1_paralleldim1( f ); } + + SECTION( "test_rank1_paralleldim_1_adj_test" ) { test_rank1_paralleldim1_adj_test( f ); } + + SECTION( "test_rank2_paralleldim_2" ) { test_rank2_paralleldim2( f ); } + + SECTION( "test_rank2_paralleldim_2_adj_test" ) { test_rank2_paralleldim2_adj_test( f ); } + + /* NOT TESTED AS USES ATLAS_GRIDTOOLS_STORAGE_BACKEND_HOST + SECTION( "test_rank1_cinterface" ) { test_rank1_cinterface( f ); } +*/ + + /* NOT IMPLEMENTED +#if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA + f.on_device_ = true; + + SECTION( "test_rank0_arrview" ) { test_rank0_arrview( f ); } + + SECTION( "test_rank1" ) { test_rank1( f ); } + + SECTION( "test_rank2" ) { test_rank2( f ); } + SECTION( "test_rank0_wrap" ) { test_rank0_wrap( f ); } + +#endif +*/ +} + +//----------------------------------------------------------------------------- + +} // namespace test +} // namespace atlas + +int main( int argc, char** argv ) { + return atlas::test::run( argc, argv ); +} From ef9312fc191ecdacb06ffcbe594361cbe03ee2ed Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 7 May 2020 16:16:42 +0100 Subject: [PATCH 066/145] Try travis quarantine again --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9d6f677e3..3c1ad5f08 100644 --- a/.travis.yml +++ b/.travis.yml @@ -67,7 +67,7 @@ matrix: - CXX_COMPILER='clang++' C_COMPILER='clang' Fortran_COMPILER='gfortran' - MPI=openmpi - ATLAS_CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=DEBUG" - - ATLAS_CTEST_OPTIONS='-E "atlas_test_stencil_parallel_mpi16"' + - ATLAS_CTEST_OPTIONS="-E atlas_test_stencil_parallel_mpi16" osx_image: xcode10.1 addons: homebrew: From b4c90f83e9540c2ddf6a2e742a79d1e90fffc4d1 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 7 May 2020 18:32:32 +0100 Subject: [PATCH 067/145] Enble wrongly disabled MPI tests --- src/tests/functionspace/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/functionspace/CMakeLists.txt b/src/tests/functionspace/CMakeLists.txt index ab69b4eb6..eae4b5a45 100644 --- a/src/tests/functionspace/CMakeLists.txt +++ b/src/tests/functionspace/CMakeLists.txt @@ -83,7 +83,7 @@ ecbuild_add_test( TARGET atlas_test_stencil_parallel_mpi16 set( _WITH_MPI ) if( eckit_HAVE_MPI ) - set( _WITH_MPI "MPI 4" ) + set( _WITH_MPI MPI 4 ) endif() ecbuild_add_executable( TARGET atlas_test_polygons From f860c3cefee06e8d2f74626aff1d3feaaebc7069 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 7 May 2020 07:36:49 +0000 Subject: [PATCH 068/145] ATLAS-286 (Github #49) Fix StructuredColumnsHaloExchange caching Co-authored-by: Philippe Marguinaud --- .../functionspace/detail/StructuredColumns.cc | 7 +- .../functionspace/detail/StructuredColumns.h | 5 + .../detail/StructuredColumns_setup.cc | 4 +- src/tests/functionspace/CMakeLists.txt | 8 ++ .../test_structuredcolumns_haloexchange.cc | 100 ++++++++++++++++++ 5 files changed, 119 insertions(+), 5 deletions(-) create mode 100644 src/tests/functionspace/test_structuredcolumns_haloexchange.cc diff --git a/src/atlas/functionspace/detail/StructuredColumns.cc b/src/atlas/functionspace/detail/StructuredColumns.cc index 2e746093a..12d037b23 100644 --- a/src/atlas/functionspace/detail/StructuredColumns.cc +++ b/src/atlas/functionspace/detail/StructuredColumns.cc @@ -105,15 +105,16 @@ class StructuredColumnsHaloExchangeCache : public util::Cache gather_scatter_; mutable util::ObjectHandle checksum_; diff --git a/src/atlas/functionspace/detail/StructuredColumns_setup.cc b/src/atlas/functionspace/detail/StructuredColumns_setup.cc index 06790840a..1bdd7f972 100644 --- a/src/atlas/functionspace/detail/StructuredColumns_setup.cc +++ b/src/atlas/functionspace/detail/StructuredColumns_setup.cc @@ -85,7 +85,7 @@ struct GridPointSet { void StructuredColumns::setup( const grid::Distribution& distribution, const eckit::Configuration& config ) { - bool periodic_points = config.getInt( "periodic_points", false ); + config.get( "periodic_points", periodic_points_ ); if ( not( *grid_ ) ) { throw_Exception( "Grid is not a grid::Structured type", Here() ); } @@ -342,7 +342,7 @@ void StructuredColumns::setup( const grid::Distribution& distribution, const eck for ( idx_t j = j_begin_; j < j_end_; ++j ) { for ( idx_t i : {i_begin_[j], i_end_[j] - 1} ) { // Following line only, increases periodic halo on the east side by 1 - if ( periodic_points && i == grid_->nx( j ) - 1 ) { + if ( periodic_points_ && i == grid_->nx( j ) - 1 ) { ++i; } diff --git a/src/tests/functionspace/CMakeLists.txt b/src/tests/functionspace/CMakeLists.txt index eae4b5a45..9f3730375 100644 --- a/src/tests/functionspace/CMakeLists.txt +++ b/src/tests/functionspace/CMakeLists.txt @@ -122,3 +122,11 @@ ecbuild_add_test( TARGET atlas_test_polygons_projections_nodecolumns ${_WITH_MPI} ) +# Tests ATLAS-286 +ecbuild_add_test( TARGET atlas_test_structuredcolumns_haloexchange + ${_WITH_MPI} + SOURCES test_structuredcolumns_haloexchange.cc + LIBS atlas + ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} +) + diff --git a/src/tests/functionspace/test_structuredcolumns_haloexchange.cc b/src/tests/functionspace/test_structuredcolumns_haloexchange.cc new file mode 100644 index 000000000..19c638bb9 --- /dev/null +++ b/src/tests/functionspace/test_structuredcolumns_haloexchange.cc @@ -0,0 +1,100 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include +#include +#include +#include + +#include "atlas/array.h" +#include "atlas/field.h" +#include "atlas/functionspace.h" +#include "atlas/grid.h" +#include "atlas/parallel/mpi/mpi.h" +#include "atlas/util/Config.h" + + +#include +#include +#include +#include + + +#include "tests/AtlasTestEnvironment.h" + +using Grid = atlas::Grid; +using Config = atlas::util::Config; + +namespace atlas { +namespace test { + +atlas::FieldSet getIJ( const atlas::functionspace::StructuredColumns& fs ) { + atlas::FieldSet ij; + + auto vi0 = atlas::array::make_view( fs.index_i() ); + auto vj0 = atlas::array::make_view( fs.index_j() ); + + auto fi = fs.createField(); + auto fj = fs.createField(); + + auto vi1 = atlas::array::make_view( fi ); + auto vj1 = atlas::array::make_view( fj ); + + for ( int i = 0; i < fs.size(); i++ ) { + vi1( i ) = vi0( i ); + vj1( i ) = vj0( i ); + } + + ij.add( fi ); + ij.add( fj ); + + return ij; +} + + +//----------------------------------------------------------------------------- + +CASE( "Two haloexchanges for StructuredColumns differing only by 'periodic_points'" ) { + Grid grid( "L400x200" ); + + grid::Distribution dist( grid, grid::Partitioner( "checkerboard" ) ); + + functionspace::StructuredColumns fs1( grid, dist, Config( "halo", 1 ) | Config( "periodic_points", true ) ); + functionspace::StructuredColumns fs2( grid, dist, Config( "halo", 1 ) ); + + if ( mpi::size() == 1 ) { + EXPECT_EQ( fs1.sizeOwned(), 80000 ); + EXPECT_EQ( fs2.sizeOwned(), 80000 ); + EXPECT_EQ( fs1.sizeHalo(), 81406 ); + EXPECT_EQ( fs2.sizeHalo(), 81204 ); + } + if ( mpi::size() == 4 ) { + EXPECT_EQ( fs1.sizeOwned(), 20000 ); + EXPECT_EQ( fs2.sizeOwned(), 20000 ); + EXPECT_EQ( fs1.sizeHalo(), ( std::vector{20604, 20604, 20604, 20806}[mpi::rank()] ) ); + EXPECT_EQ( fs2.sizeHalo(), ( std::vector{20604, 20604, 20604, 20604}[mpi::rank()] ) ); + } + + auto ij1 = getIJ( fs1 ); + auto ij2 = getIJ( fs2 ); + + EXPECT_NO_THROW( fs1.haloExchange( ij1 ) ); + EXPECT_NO_THROW( fs2.haloExchange( ij2 ) ); +} + + +//----------------------------------------------------------------------------- + +} // namespace test +} // namespace atlas + +int main( int argc, char** argv ) { + return atlas::test::run( argc, argv ); +} From 2515e7066c1ffaf9715604a88bb9bd4a1f777bc4 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 11 May 2020 16:08:07 +0100 Subject: [PATCH 069/145] Fix warnings with -Wall for Clang C++ / GNU Fortran --- src/apps/atlas-grids.cc | 3 +- .../detail/NodeColumns_FieldStatistics.cc | 20 ++--- src/atlas/util/Rotation.cc | 13 +-- src/atlas_f/field/atlas_FieldSet_module.F90 | 2 + src/atlas_f/field/atlas_Field_module.fypp | 6 +- src/atlas_f/field/atlas_State_module.F90 | 2 + .../atlas_FunctionSpace_module.F90 | 2 + ...atlas_functionspace_EdgeColumns_module.F90 | 2 + ...tlas_functionspace_NodeColumns_module.fypp | 14 +++- .../atlas_functionspace_PointCloud_module.F90 | 10 ++- .../atlas_functionspace_Spectral_module.F90 | 2 + ...functionspace_StructuredColumns_module.F90 | 2 + .../grid/atlas_GridDistribution_module.F90 | 2 + src/atlas_f/grid/atlas_Grid_module.F90 | 82 +++++++++++-------- src/atlas_f/grid/atlas_Partitioner_module.F90 | 2 + src/atlas_f/grid/atlas_Vertical_module.F90 | 2 + .../internals/atlas_write_to_fortran_unit.F90 | 10 ++- .../atlas_Interpolation_module.F90 | 2 + .../mesh/atlas_Connectivity_module.F90 | 4 + src/atlas_f/mesh/atlas_ElementType_module.F90 | 2 + src/atlas_f/mesh/atlas_Elements_module.F90 | 2 + .../mesh/atlas_HybridElements_module.F90 | 2 + .../mesh/atlas_MeshGenerator_module.F90 | 2 + src/atlas_f/mesh/atlas_Mesh_module.F90 | 2 + src/atlas_f/mesh/atlas_mesh_Cells_module.F90 | 2 + src/atlas_f/mesh/atlas_mesh_Edges_module.F90 | 2 + src/atlas_f/mesh/atlas_mesh_Nodes_module.F90 | 2 + src/atlas_f/numerics/atlas_Method_module.F90 | 3 +- src/atlas_f/numerics/atlas_Nabla_module.F90 | 2 + src/atlas_f/numerics/atlas_fvm_module.F90 | 2 + src/atlas_f/output/atlas_output_module.F90 | 2 + .../parallel/atlas_Checksum_module.fypp | 2 + .../parallel/atlas_GatherScatter_module.fypp | 2 + .../parallel/atlas_HaloExchange_module.fypp | 2 + src/atlas_f/runtime/atlas_Trace_module.F90 | 2 + src/atlas_f/trans/atlas_Trans_module.F90 | 2 + src/atlas_f/util/atlas_Config_module.F90 | 2 + src/atlas_f/util/atlas_Metadata_module.F90 | 2 + src/tests/array/test_array_slicer.cc | 1 - src/tests/field/fctest_field_host.F90 | 5 +- .../functionspace/fctest_functionspace.F90 | 1 - src/tests/grid/fctest_griddistribution.F90 | 2 +- src/tests/grid/fctest_grids.F90 | 4 +- src/tests/grid/fctest_unstructuredgrid.F90 | 1 - ...erpolation_structured2D_to_unstructured.cc | 8 -- src/tests/numerics/fctest_fvm_nabla.F90 | 4 +- src/tests/projection/test_rotation.cc | 4 +- src/tests/trans/fctest_trans.F90 | 9 +- src/tests/trans/test_transgeneral.cc | 16 ++-- 49 files changed, 172 insertions(+), 104 deletions(-) diff --git a/src/apps/atlas-grids.cc b/src/apps/atlas-grids.cc index e31e43b8b..cc15faf7a 100644 --- a/src/apps/atlas-grids.cc +++ b/src/apps/atlas-grids.cc @@ -160,8 +160,7 @@ int AtlasGrids::execute( const Args& args ) { } for ( auto b : grid::GridBuilder::nameRegistry() ) { - int nb_names = b.second->names().size(); - int c = 0; + int c = 0; for ( const auto& name : b.second->names() ) { if ( c == 0 ) { Log::info() << " -- " << std::left << std::setw( maxlen + 8 ) << name; diff --git a/src/atlas/functionspace/detail/NodeColumns_FieldStatistics.cc b/src/atlas/functionspace/detail/NodeColumns_FieldStatistics.cc index 64dc0da3a..5b1dc9dab 100644 --- a/src/atlas/functionspace/detail/NodeColumns_FieldStatistics.cc +++ b/src/atlas/functionspace/detail/NodeColumns_FieldStatistics.cc @@ -1556,16 +1556,16 @@ void NodeColumns::FieldStatistics::meanAndStandardDeviationPerLevel( const Field detail::mean_and_standard_deviation_per_level( functionspace, field, mean, stddev, N ); } -template class NodeColumns::FieldStatisticsT; -template class NodeColumns::FieldStatisticsT; -template class NodeColumns::FieldStatisticsT; -template class NodeColumns::FieldStatisticsT; -// template class NodeColumns::FieldStatisticsT; -template class NodeColumns::FieldStatisticsVectorT>; -template class NodeColumns::FieldStatisticsVectorT>; -template class NodeColumns::FieldStatisticsVectorT>; -template class NodeColumns::FieldStatisticsVectorT>; -// template class NodeColumns::FieldStatisticsVectorT< std::vector; +template struct NodeColumns::FieldStatisticsT; +template struct NodeColumns::FieldStatisticsT; +template struct NodeColumns::FieldStatisticsT; +// template struct NodeColumns::FieldStatisticsT; +template struct NodeColumns::FieldStatisticsVectorT>; +template struct NodeColumns::FieldStatisticsVectorT>; +template struct NodeColumns::FieldStatisticsVectorT>; +template struct NodeColumns::FieldStatisticsVectorT>; +// template struct NodeColumns::FieldStatisticsVectorT< std::vector >; } // namespace detail diff --git a/src/atlas/util/Rotation.cc b/src/atlas/util/Rotation.cc index 0873230fb..873dc3edc 100644 --- a/src/atlas/util/Rotation.cc +++ b/src/atlas/util/Rotation.cc @@ -85,7 +85,7 @@ void Rotation::precompute() { rotation_angle_only_ = eq( sin_theta, 0 ) && eq( cos_theta, 1 ) && rotated_; if ( !rotated_ ) { - rotate_ = unrotate_ = {1., 0., 0., 0., 1., 0., 0., 0., 1.}; + rotate_ = unrotate_ = RotationMatrix{{{1., 0., 0.}, {0., 1., 0.}, {0., 0., 1.}}}; return; } @@ -104,9 +104,9 @@ void Rotation::precompute() { // yt = -cos(ϑ) sin(φ) x + cos(φ) y - sin(ϑ) sin(φ) z // zt = -sin(ϑ) x + cos(ϑ) z - rotate_ = {cos_theta * cos_phi, sin_phi, sin_theta * cos_phi, - -cos_theta * sin_phi, cos_phi, -sin_theta * sin_phi, - -sin_theta, 0., cos_theta}; + rotate_ = RotationMatrix{{{cos_theta * cos_phi, sin_phi, sin_theta * cos_phi}, + {-cos_theta * sin_phi, cos_phi, -sin_theta * sin_phi}, + {-sin_theta, 0., cos_theta}}}; // Assume right hand rule, rotate about z axes and then y // P = Rot(y) * Rot(z) * Pt @@ -119,8 +119,9 @@ void Rotation::precompute() { // y = ( sin(φ) , cos(φ) , 0 ).(yt) // z ( sin(ϑ) cos(φ), -sin(ϑ) sin(φ), cos(ϑ)) (zt) - unrotate_ = {cos_theta * cos_phi, -cos_theta * sin_phi, -sin_theta, sin_phi, cos_phi, 0., - sin_theta * cos_phi, -sin_theta * sin_phi, cos_theta}; + unrotate_ = RotationMatrix{{{cos_theta * cos_phi, -cos_theta * sin_phi, -sin_theta}, + {sin_phi, cos_phi, 0.}, + {sin_theta * cos_phi, -sin_theta * sin_phi, cos_theta}}}; } Rotation::Rotation( const PointLonLat& south_pole, double rotation_angle ) { diff --git a/src/atlas_f/field/atlas_FieldSet_module.F90 b/src/atlas_f/field/atlas_FieldSet_module.F90 index 9d9d86ef3..77f7bddb2 100644 --- a/src/atlas_f/field/atlas_FieldSet_module.F90 +++ b/src/atlas_f/field/atlas_FieldSet_module.F90 @@ -194,6 +194,7 @@ subroutine set_dirty(this,value) !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_FieldSet__final_auto(this) type(atlas_FieldSet), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -204,6 +205,7 @@ ATLAS_FINAL subroutine atlas_FieldSet__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif ! ---------------------------------------------------------------------------------------- diff --git a/src/atlas_f/field/atlas_Field_module.fypp b/src/atlas_f/field/atlas_Field_module.fypp index aeaba81ff..dd505295c 100644 --- a/src/atlas_f/field/atlas_Field_module.fypp +++ b/src/atlas_f/field/atlas_Field_module.fypp @@ -66,7 +66,7 @@ contains procedure :: dirty procedure :: set_dirty - procedure :: rename + procedure :: rename => rename_ procedure :: set_levels procedure :: set_functionspace @@ -583,7 +583,7 @@ end subroutine !------------------------------------------------------------------------------- -subroutine rename(this,name) +subroutine rename_(this,name) use atlas_field_c_binding use fckit_c_interop_module, only : c_str class(atlas_Field), intent(inout) :: this @@ -716,6 +716,7 @@ end function !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Field__final_auto(this) type(atlas_Field), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -726,6 +727,7 @@ ATLAS_FINAL subroutine atlas_Field__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif !------------------------------------------------------------------------------- diff --git a/src/atlas_f/field/atlas_State_module.F90 b/src/atlas_f/field/atlas_State_module.F90 index 75042ca89..94cb4c102 100644 --- a/src/atlas_f/field/atlas_State_module.F90 +++ b/src/atlas_f/field/atlas_State_module.F90 @@ -176,6 +176,7 @@ function atlas_State__metadata(this) result(metadata) !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_State__final_auto(this) type(atlas_State), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -186,6 +187,7 @@ ATLAS_FINAL subroutine atlas_State__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif ! ---------------------------------------------------------------------------------------- diff --git a/src/atlas_f/functionspace/atlas_FunctionSpace_module.F90 b/src/atlas_f/functionspace/atlas_FunctionSpace_module.F90 index 4d404a483..2f1121d5a 100644 --- a/src/atlas_f/functionspace/atlas_FunctionSpace_module.F90 +++ b/src/atlas_f/functionspace/atlas_FunctionSpace_module.F90 @@ -235,6 +235,7 @@ function deprecated_create_field_2(this,require_name,kind,levels) result(field) !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_FunctionSpace__final_auto(this) type(atlas_FunctionSpace), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -245,6 +246,7 @@ ATLAS_FINAL subroutine atlas_FunctionSpace__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif !------------------------------------------------------------------------------ diff --git a/src/atlas_f/functionspace/atlas_functionspace_EdgeColumns_module.F90 b/src/atlas_f/functionspace/atlas_functionspace_EdgeColumns_module.F90 index 041d2b0a4..383ed0bdf 100644 --- a/src/atlas_f/functionspace/atlas_functionspace_EdgeColumns_module.F90 +++ b/src/atlas_f/functionspace/atlas_functionspace_EdgeColumns_module.F90 @@ -267,6 +267,7 @@ function checksum_field(this,field) result(checksum) !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_functionspace_EdgeColumns__final_auto(this) type(atlas_functionspace_EdgeColumns), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -277,6 +278,7 @@ ATLAS_FINAL subroutine atlas_functionspace_EdgeColumns__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif !------------------------------------------------------------------------------ diff --git a/src/atlas_f/functionspace/atlas_functionspace_NodeColumns_module.fypp b/src/atlas_f/functionspace/atlas_functionspace_NodeColumns_module.fypp index ed2a0891f..4d3936c6e 100644 --- a/src/atlas_f/functionspace/atlas_functionspace_NodeColumns_module.fypp +++ b/src/atlas_f/functionspace/atlas_functionspace_NodeColumns_module.fypp @@ -593,7 +593,7 @@ subroutine minloclev_${dtype}$_r1(this,field,minimum,location,level) ${ftype}$, pointer :: min_fptr(:) integer(c_long),pointer :: loc_fptr(:) integer(c_long),pointer :: lev_fptr(:) - integer :: min_size + integer :: min_size, jlev call atlas__NodesFunctionSpace__minloclev_arr_${ctype}$(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,min_cptr,loc_cptr,lev_cptr,min_size) call c_f_pointer(min_cptr,min_fptr,(/min_size/)) call c_f_pointer(loc_cptr,loc_fptr,(/min_size/)) @@ -603,7 +603,9 @@ subroutine minloclev_${dtype}$_r1(this,field,minimum,location,level) location(:) = loc_fptr(:) call c_f_pointer(lev_cptr,lev_fptr,(/min_size/)) allocate(level(min_size)) - level(:) = lev_fptr(:) + do jlev=1,min_size + level(jlev) = int(lev_fptr(jlev),c_int) + enddo call c_ptr_free(min_cptr) call c_ptr_free(loc_cptr) call c_ptr_free(lev_cptr) @@ -623,7 +625,7 @@ subroutine maxloclev_${dtype}$_r1(this,field,maximum,location,level) ${ftype}$, pointer :: max_fptr(:) integer(c_long),pointer :: loc_fptr(:) integer(c_long),pointer :: lev_fptr(:) - integer :: max_size + integer :: max_size, jlev call atlas__NodesFunctionSpace__maxloclev_arr_${ctype}$(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,max_cptr,loc_cptr,lev_cptr,max_size) call c_f_pointer(max_cptr,max_fptr,(/max_size/)) call c_f_pointer(loc_cptr,loc_fptr,(/max_size/)) @@ -633,7 +635,9 @@ subroutine maxloclev_${dtype}$_r1(this,field,maximum,location,level) location(:) = loc_fptr(:) call c_f_pointer(lev_cptr,lev_fptr,(/max_size/)) allocate(level(max_size)) - level(:) = lev_fptr(:) + do jlev=1,max_size + level(jlev) = int(lev_fptr(jlev),c_int) + enddo call c_ptr_free(max_cptr) call c_ptr_free(loc_cptr) call c_ptr_free(lev_cptr) @@ -786,6 +790,7 @@ end subroutine !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_functionspace_NodeColumns__final_auto(this) type(atlas_functionspace_NodeColumns), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -796,6 +801,7 @@ ATLAS_FINAL subroutine atlas_functionspace_NodeColumns__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif !------------------------------------------------------------------------------ diff --git a/src/atlas_f/functionspace/atlas_functionspace_PointCloud_module.F90 b/src/atlas_f/functionspace/atlas_functionspace_PointCloud_module.F90 index 17da5bef6..59f46b089 100644 --- a/src/atlas_f/functionspace/atlas_functionspace_PointCloud_module.F90 +++ b/src/atlas_f/functionspace/atlas_functionspace_PointCloud_module.F90 @@ -45,7 +45,7 @@ module atlas_functionspace_PointCloud_module !------------------------------------------------------------------------------ contains - procedure, public :: size + procedure, public :: size => size_ procedure, public :: lonlat END TYPE atlas_functionspace_PointCloud @@ -94,11 +94,11 @@ function ctor_grid(grid) result(this) !------------------------------------------------------------------------------ -function size(this) +function size_(this) use atlas_functionspace_PointCloud_c_binding - integer :: size + integer :: size_ class(atlas_functionspace_PointCloud), intent(in) :: this - size = atlas__fs__PointCloud__size(this%CPTR_PGIBUG_A) + size_ = atlas__fs__PointCloud__size(this%CPTR_PGIBUG_A) end function !------------------------------------------------------------------------------ @@ -113,6 +113,7 @@ function lonlat(this) result(field) !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_functionspace_PointCloud__final_auto(this) type(atlas_functionspace_PointCloud), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -123,6 +124,7 @@ ATLAS_FINAL subroutine atlas_functionspace_PointCloud__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif !------------------------------------------------------------------------------ diff --git a/src/atlas_f/functionspace/atlas_functionspace_Spectral_module.F90 b/src/atlas_f/functionspace/atlas_functionspace_Spectral_module.F90 index d09ae3f5f..ad5f50caf 100644 --- a/src/atlas_f/functionspace/atlas_functionspace_Spectral_module.F90 +++ b/src/atlas_f/functionspace/atlas_functionspace_Spectral_module.F90 @@ -272,6 +272,7 @@ function nvalue(this) !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_functionspace_Spectral__final_auto(this) type(atlas_functionspace_Spectral), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -282,6 +283,7 @@ ATLAS_FINAL subroutine atlas_functionspace_Spectral__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif end module atlas_functionspace_Spectral_module diff --git a/src/atlas_f/functionspace/atlas_functionspace_StructuredColumns_module.F90 b/src/atlas_f/functionspace/atlas_functionspace_StructuredColumns_module.F90 index 2793a745d..d2210ee4b 100644 --- a/src/atlas_f/functionspace/atlas_functionspace_StructuredColumns_module.F90 +++ b/src/atlas_f/functionspace/atlas_functionspace_StructuredColumns_module.F90 @@ -501,6 +501,7 @@ function grid(this) !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine StructuredColumns__final_auto(this) type(atlas_functionspace_StructuredColumns), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -511,6 +512,7 @@ ATLAS_FINAL subroutine StructuredColumns__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif end module atlas_functionspace_StructuredColumns_module diff --git a/src/atlas_f/grid/atlas_GridDistribution_module.F90 b/src/atlas_f/grid/atlas_GridDistribution_module.F90 index a873133b2..e1eb39ce3 100644 --- a/src/atlas_f/grid/atlas_GridDistribution_module.F90 +++ b/src/atlas_f/grid/atlas_GridDistribution_module.F90 @@ -105,6 +105,7 @@ function atlas_GridDistribution__nb_partitions(this) result(nb_partitions) ! ---------------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_GridDistribution__final_auto(this) type(atlas_GridDistribution), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -115,6 +116,7 @@ ATLAS_FINAL subroutine atlas_GridDistribution__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif ! ---------------------------------------------------------------------------------------- diff --git a/src/atlas_f/grid/atlas_Grid_module.F90 b/src/atlas_f/grid/atlas_Grid_module.F90 index 3fee4fdf3..b62573525 100644 --- a/src/atlas_f/grid/atlas_Grid_module.F90 +++ b/src/atlas_f/grid/atlas_Grid_module.F90 @@ -12,7 +12,7 @@ module atlas_Grid_module use fckit_owned_object_module, only: fckit_owned_object use atlas_Config_module, only: atlas_Config -use atlas_kinds_module, only : ATLAS_KIND_IDX +use atlas_kinds_module, only : ATLAS_KIND_IDX, ATLAS_KIND_GIDX use, intrinsic :: iso_c_binding, only : c_ptr implicit none @@ -274,6 +274,12 @@ module atlas_Grid_module module procedure c_idx_64 end interface +interface c_gidx + module procedure c_gidx_32 + module procedure c_gidx_64 +end interface + + !------------------------------------------------------------------------------ !======================================================== contains @@ -293,9 +299,24 @@ pure function c_idx_64(f_idx) result(c_idx) c_idx = int(f_idx,ATLAS_KIND_IDX) - 1_ATLAS_KIND_IDX end function +pure function c_gidx_32(f_gidx) result(c_gidx) + use, intrinsic :: iso_c_binding, only : c_long + integer(ATLAS_KIND_GIDX) :: c_gidx + integer(c_long), intent(in) :: f_gidx + c_gidx = int(f_gidx,ATLAS_KIND_GIDX) - 1_ATLAS_KIND_GIDX +end function + +pure function c_gidx_64(f_gidx) result(c_gidx) + use, intrinsic :: iso_c_binding, only : c_int + integer(ATLAS_KIND_GIDX) :: c_gidx + integer(c_int), intent(in) :: f_gidx + c_gidx = int(f_gidx,ATLAS_KIND_GIDX) - 1_ATLAS_KIND_GIDX +end function + ! ----------------------------------------------------------------------------- ! Destructor +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Grid__final_auto(this) type(atlas_Grid), intent(inout) :: this #if FCKIT_FINAL_NOT_PROPAGATING @@ -351,7 +372,7 @@ ATLAS_FINAL subroutine atlas_RegularGaussianGrid__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine - +#endif ! ----------------------------------------------------------------------------- @@ -524,11 +545,11 @@ function atlas_grid_RegularLonLat__ctor_int64(nlon,nlat) result(this) ! Structured members function atlas_Grid__size(this) result(npts) - use, intrinsic :: iso_c_binding, only: c_long + use, intrinsic :: iso_c_binding, only: c_int use atlas_grid_Grid_c_binding class(atlas_Grid), intent(in) :: this - integer(c_long) :: npts - npts = atlas__grid__Grid__size(this%CPTR_PGIBUG_A) + integer(ATLAS_KIND_IDX) :: npts + npts = int(atlas__grid__Grid__size(this%CPTR_PGIBUG_A),ATLAS_KIND_IDX) end function function atlas_Grid__spec(this) result(spec) @@ -567,74 +588,74 @@ function Structured__ny(this) result(ny) use, intrinsic :: iso_c_binding, only: c_long use atlas_grid_Structured_c_binding class(atlas_StructuredGrid), intent(in) :: this - integer(c_long) :: ny + integer(ATLAS_KIND_IDX) :: ny ny = atlas__grid__Structured__ny(this%CPTR_PGIBUG_A) end function subroutine Structured__index2ij_int32(this, gidx, i, j) - use, intrinsic :: iso_c_binding, only: c_long, c_int + use, intrinsic :: iso_c_binding, only: c_int32_t use atlas_grid_Structured_c_binding - integer(c_int), intent (in) :: gidx + integer(c_int32_t), intent (in) :: gidx class(atlas_StructuredGrid), intent(in) :: this - integer(c_int), intent(out) :: i, j - call atlas__grid__Structured__index2ij(this%CPTR_PGIBUG_A, int (gidx-1, c_long), i, j) + integer(ATLAS_KIND_IDX), intent(out) :: i, j + call atlas__grid__Structured__index2ij(this%CPTR_PGIBUG_A, c_gidx(gidx), i, j) i = i + 1 j = j + 1 end subroutine subroutine Structured__index2ij_int64(this, gidx, i, j) - use, intrinsic :: iso_c_binding, only: c_long, c_int + use, intrinsic :: iso_c_binding, only: c_long, c_int64_t use atlas_grid_Structured_c_binding - integer(c_long), intent (in) :: gidx + integer(c_int64_t), intent (in) :: gidx class(atlas_StructuredGrid), intent(in) :: this - integer(c_int), intent(out) :: i, j - call atlas__grid__Structured__index2ij(this%CPTR_PGIBUG_A, gidx-1, i, j) + integer(ATLAS_KIND_IDX), intent(out) :: i, j + call atlas__grid__Structured__index2ij(this%CPTR_PGIBUG_A, c_gidx(gidx), i, j) i = i + 1 j = j + 1 end subroutine function Structured__ij_int32(this, gidx) result(ij) - use, intrinsic :: iso_c_binding, only: c_long, c_int + use, intrinsic :: iso_c_binding, only: c_int use atlas_grid_Structured_c_binding integer(c_int), intent (in) :: gidx class(atlas_StructuredGrid), intent(in) :: this integer(c_int) :: ij (2) - call atlas__grid__Structured__index2ij(this%CPTR_PGIBUG_A, int (gidx-1, c_long), ij(1), ij(2)) + call atlas__grid__Structured__index2ij(this%CPTR_PGIBUG_A, c_gidx(gidx), ij(1), ij(2)) ij = ij + 1 end function function Structured__ij_int64(this, gidx) result(ij) - use, intrinsic :: iso_c_binding, only: c_long, c_int + use, intrinsic :: iso_c_binding, only: c_int, c_long use atlas_grid_Structured_c_binding integer(c_long), intent (in) :: gidx class(atlas_StructuredGrid), intent(in) :: this integer(c_int) :: ij (2) - call atlas__grid__Structured__index2ij(this%CPTR_PGIBUG_A, gidx-1, ij(1), ij(2)) + call atlas__grid__Structured__index2ij(this%CPTR_PGIBUG_A, c_gidx(gidx), ij(1), ij(2)) ij = ij + 1 end function function Structured__index_int32(this, i, j) result(gidx) - use, intrinsic :: iso_c_binding, only: c_long, c_int + use, intrinsic :: iso_c_binding, only: c_int use atlas_grid_Structured_c_binding - integer(c_long) :: gidx + integer(ATLAS_KIND_IDX) :: gidx class(atlas_StructuredGrid), intent(in) :: this integer(c_int), intent(in) :: i, j - gidx = 1 + atlas__grid__Structured__index(this%CPTR_PGIBUG_A, c_idx(i), c_idx(j) ) + gidx = int(1 + atlas__grid__Structured__index(this%CPTR_PGIBUG_A, c_idx(i), c_idx(j) ), ATLAS_KIND_IDX ) end function function Structured__index_int64(this, i, j) result(gidx) use, intrinsic :: iso_c_binding, only: c_long use atlas_grid_Structured_c_binding - integer(c_long) :: gidx + integer(ATLAS_KIND_IDX) :: gidx class(atlas_StructuredGrid), intent(in) :: this integer(c_long), intent(in) :: i, j - gidx = 1 + atlas__grid__Structured__index(this%CPTR_PGIBUG_A, c_idx(i), c_idx(j) ) + gidx = int( 1 + atlas__grid__Structured__index(this%CPTR_PGIBUG_A, c_idx(i), c_idx(j) ), ATLAS_KIND_IDX ) end function function Structured__nx_int32(this, j) result(nx) - use, intrinsic :: iso_c_binding, only: c_long, c_int + use, intrinsic :: iso_c_binding, only: c_int use atlas_grid_Structured_c_binding - integer(c_long) :: nx + integer(ATLAS_KIND_IDX) :: nx class(atlas_StructuredGrid), intent(in) :: this integer(c_int), intent(in) :: j nx = atlas__grid__Structured__nx(this%CPTR_PGIBUG_A, c_idx(j) ) @@ -643,7 +664,7 @@ function Structured__nx_int32(this, j) result(nx) function Structured__nx_int64(this, j) result(nx) use, intrinsic :: iso_c_binding, only: c_long use atlas_grid_Structured_c_binding - integer(c_long) :: nx + integer(ATLAS_KIND_IDX) :: nx class(atlas_StructuredGrid), intent(in) :: this integer(c_long), intent(in) :: j nx = atlas__grid__Structured__nx(this%CPTR_PGIBUG_A, c_idx(j) ) @@ -687,15 +708,6 @@ function Structured__nxmin(this) result(nxmin) nxmin = atlas__grid__Structured__nxmin(this%CPTR_PGIBUG_A) end function -function Structured__y(this, j) result(y) - use, intrinsic :: iso_c_binding, only: c_double, c_long - use atlas_grid_Structured_c_binding - real(c_double) :: y - class(atlas_StructuredGrid), intent(in) :: this - integer(c_long), intent(in) :: j - y = atlas__grid__Structured__y(this%CPTR_PGIBUG_A, c_idx(j) ) -end function - function Structured__y_32(this, j) result(y) use, intrinsic :: iso_c_binding, only: c_double, c_int use atlas_grid_Structured_c_binding diff --git a/src/atlas_f/grid/atlas_Partitioner_module.F90 b/src/atlas_f/grid/atlas_Partitioner_module.F90 index e7cce8d79..4ecb348d1 100644 --- a/src/atlas_f/grid/atlas_Partitioner_module.F90 +++ b/src/atlas_f/grid/atlas_Partitioner_module.F90 @@ -120,6 +120,7 @@ function partition(this,grid) result(distribution) !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Partitioner__final_auto(this) type(atlas_Partitioner), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -130,6 +131,7 @@ ATLAS_FINAL subroutine atlas_Partitioner__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif ! ---------------------------------------------------------------------------------------- diff --git a/src/atlas_f/grid/atlas_Vertical_module.F90 b/src/atlas_f/grid/atlas_Vertical_module.F90 index ef2287dc8..91c7e832d 100644 --- a/src/atlas_f/grid/atlas_Vertical_module.F90 +++ b/src/atlas_f/grid/atlas_Vertical_module.F90 @@ -133,6 +133,7 @@ function vsize( this ) ! ---------------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Vertical__final_auto(this) type(atlas_Vertical), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -143,6 +144,7 @@ ATLAS_FINAL subroutine atlas_Vertical__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif ! ---------------------------------------------------------------------------------------- diff --git a/src/atlas_f/internals/atlas_write_to_fortran_unit.F90 b/src/atlas_f/internals/atlas_write_to_fortran_unit.F90 index 0de3929b2..5a270c73c 100644 --- a/src/atlas_f/internals/atlas_write_to_fortran_unit.F90 +++ b/src/atlas_f/internals/atlas_write_to_fortran_unit.F90 @@ -7,11 +7,13 @@ ! does it submit to any jurisdiction. subroutine atlas_write_to_fortran_unit(unit,msg_cptr) bind(C) - use, intrinsic :: iso_c_binding, only: c_int, c_ptr - use fckit_c_interop_module, only : c_ptr_to_string + use, intrinsic :: iso_c_binding, only: c_int, c_ptr, c_associated + use fckit_c_interop_module, only : copy_c_ptr_to_string integer(c_int), value, intent(in) :: unit type(c_ptr), value, intent(in) :: msg_cptr character(len=:), allocatable :: msg - msg = c_ptr_to_string(msg_cptr) - write(unit,'(A)',advance='no') msg + if( c_associated( msg_cptr ) ) then + call copy_c_ptr_to_string( msg_cptr, msg ) + write(unit,'(A)',advance='no') msg + endif end subroutine diff --git a/src/atlas_f/interpolation/atlas_Interpolation_module.F90 b/src/atlas_f/interpolation/atlas_Interpolation_module.F90 index 00c81a941..ea3daea32 100644 --- a/src/atlas_f/interpolation/atlas_Interpolation_module.F90 +++ b/src/atlas_f/interpolation/atlas_Interpolation_module.F90 @@ -126,6 +126,7 @@ subroutine execute_fieldset(this,source,target) !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Interpolation__final_auto(this) type(atlas_Interpolation), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -136,6 +137,7 @@ ATLAS_FINAL subroutine atlas_Interpolation__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif ! ----------------------------------------------------------------------------- diff --git a/src/atlas_f/mesh/atlas_Connectivity_module.F90 b/src/atlas_f/mesh/atlas_Connectivity_module.F90 index e476dec79..955c1e2ac 100644 --- a/src/atlas_f/mesh/atlas_Connectivity_module.F90 +++ b/src/atlas_f/mesh/atlas_Connectivity_module.F90 @@ -635,6 +635,7 @@ subroutine delete_access(this) !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Connectivity__final_auto(this) type(atlas_Connectivity), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -645,9 +646,11 @@ ATLAS_FINAL subroutine atlas_Connectivity__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_MultiBlockConnectivity__final_auto(this) type(atlas_MultiBlockConnectivity), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -658,6 +661,7 @@ ATLAS_FINAL subroutine atlas_MultiBlockConnectivity__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif !------------------------------------------------------------------------------- diff --git a/src/atlas_f/mesh/atlas_ElementType_module.F90 b/src/atlas_f/mesh/atlas_ElementType_module.F90 index f5ad2c05b..98dac057a 100644 --- a/src/atlas_f/mesh/atlas_ElementType_module.F90 +++ b/src/atlas_f/mesh/atlas_ElementType_module.F90 @@ -131,6 +131,7 @@ function parametric(this) !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_ElementType__final_auto(this) type(atlas_ElementType), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -141,6 +142,7 @@ ATLAS_FINAL subroutine atlas_ElementType__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif end module atlas_ElementType_module diff --git a/src/atlas_f/mesh/atlas_Elements_module.F90 b/src/atlas_f/mesh/atlas_Elements_module.F90 index 7022bf3d3..aa54589a2 100644 --- a/src/atlas_f/mesh/atlas_Elements_module.F90 +++ b/src/atlas_f/mesh/atlas_Elements_module.F90 @@ -245,6 +245,7 @@ function atlas_Elements__end(this) result(val) !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Elements__final_auto(this) type(atlas_Elements), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -255,5 +256,6 @@ ATLAS_FINAL subroutine atlas_Elements__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif end module atlas_Elements_module diff --git a/src/atlas_f/mesh/atlas_HybridElements_module.F90 b/src/atlas_f/mesh/atlas_HybridElements_module.F90 index f8f312c90..613821dc7 100644 --- a/src/atlas_f/mesh/atlas_HybridElements_module.F90 +++ b/src/atlas_f/mesh/atlas_HybridElements_module.F90 @@ -338,6 +338,7 @@ function elements_int(this,idx) result(elements) !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_HybridElements__final_auto(this) type(atlas_HybridElements), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -348,6 +349,7 @@ ATLAS_FINAL subroutine atlas_HybridElements__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif end module atlas_HybridElements_module diff --git a/src/atlas_f/mesh/atlas_MeshGenerator_module.F90 b/src/atlas_f/mesh/atlas_MeshGenerator_module.F90 index f5db338c3..54c8dae6f 100644 --- a/src/atlas_f/mesh/atlas_MeshGenerator_module.F90 +++ b/src/atlas_f/mesh/atlas_MeshGenerator_module.F90 @@ -107,6 +107,7 @@ function atlas_MeshGenerator__generate(this,grid,distribution) result(mesh) !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_MeshGenerator__final_auto(this) type(atlas_MeshGenerator), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -117,6 +118,7 @@ ATLAS_FINAL subroutine atlas_MeshGenerator__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif ! ---------------------------------------------------------------------------------------- diff --git a/src/atlas_f/mesh/atlas_Mesh_module.F90 b/src/atlas_f/mesh/atlas_Mesh_module.F90 index 82c6b4d8f..692af147a 100644 --- a/src/atlas_f/mesh/atlas_Mesh_module.F90 +++ b/src/atlas_f/mesh/atlas_Mesh_module.F90 @@ -154,6 +154,7 @@ subroutine sync_host_device(this) !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Mesh__final_auto(this) type(atlas_Mesh), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -164,6 +165,7 @@ ATLAS_FINAL subroutine atlas_Mesh__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif !------------------------------------------------------------------------------- diff --git a/src/atlas_f/mesh/atlas_mesh_Cells_module.F90 b/src/atlas_f/mesh/atlas_mesh_Cells_module.F90 index d2c690428..a9875dec8 100644 --- a/src/atlas_f/mesh/atlas_mesh_Cells_module.F90 +++ b/src/atlas_f/mesh/atlas_mesh_Cells_module.F90 @@ -55,6 +55,7 @@ function atlas_mesh_cells__constructor() result(this) !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_mesh_Cells__final_auto(this) type(atlas_mesh_Cells), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -65,6 +66,7 @@ ATLAS_FINAL subroutine atlas_mesh_Cells__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif ! ---------------------------------------------------------------------------------------- diff --git a/src/atlas_f/mesh/atlas_mesh_Edges_module.F90 b/src/atlas_f/mesh/atlas_mesh_Edges_module.F90 index e8c417301..96af28517 100644 --- a/src/atlas_f/mesh/atlas_mesh_Edges_module.F90 +++ b/src/atlas_f/mesh/atlas_mesh_Edges_module.F90 @@ -54,6 +54,7 @@ function atlas_mesh_edges__constructor() result(this) !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_mesh_Edges__final_auto(this) type(atlas_mesh_Edges), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -64,6 +65,7 @@ ATLAS_FINAL subroutine atlas_mesh_Edges__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif ! ---------------------------------------------------------------------------------------- diff --git a/src/atlas_f/mesh/atlas_mesh_Nodes_module.F90 b/src/atlas_f/mesh/atlas_mesh_Nodes_module.F90 index 0464e4337..5cb9f6aa8 100644 --- a/src/atlas_f/mesh/atlas_mesh_Nodes_module.F90 +++ b/src/atlas_f/mesh/atlas_mesh_Nodes_module.F90 @@ -292,6 +292,7 @@ function str(this) !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_mesh_Nodes__final_auto(this) type(atlas_mesh_Nodes), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -302,6 +303,7 @@ ATLAS_FINAL subroutine atlas_mesh_Nodes__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif ! ---------------------------------------------------------------------------------------- diff --git a/src/atlas_f/numerics/atlas_Method_module.F90 b/src/atlas_f/numerics/atlas_Method_module.F90 index 8b3f5ab3a..0c0d4451f 100644 --- a/src/atlas_f/numerics/atlas_Method_module.F90 +++ b/src/atlas_f/numerics/atlas_Method_module.F90 @@ -74,6 +74,7 @@ function atlas_Method__name(this) result(name) !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Method__final_auto(this) type(atlas_Method), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -84,7 +85,7 @@ ATLAS_FINAL subroutine atlas_Method__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine - +#endif end module atlas_Method_module diff --git a/src/atlas_f/numerics/atlas_Nabla_module.F90 b/src/atlas_f/numerics/atlas_Nabla_module.F90 index a6e123e0a..72c90e153 100644 --- a/src/atlas_f/numerics/atlas_Nabla_module.F90 +++ b/src/atlas_f/numerics/atlas_Nabla_module.F90 @@ -131,6 +131,7 @@ function atlas_Nabla__functionspace(this) result(functionspace) !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Nabla__final_auto(this) type(atlas_Nabla), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -141,6 +142,7 @@ ATLAS_FINAL subroutine atlas_Nabla__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif ! ----------------------------------------------------------------------------- diff --git a/src/atlas_f/numerics/atlas_fvm_module.F90 b/src/atlas_f/numerics/atlas_fvm_module.F90 index 5b92f75a8..8c74b3d88 100644 --- a/src/atlas_f/numerics/atlas_fvm_module.F90 +++ b/src/atlas_f/numerics/atlas_fvm_module.F90 @@ -108,6 +108,7 @@ function edge_columns(this) !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_fvm_Method__final_auto(this) type(atlas_fvm_Method), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -118,6 +119,7 @@ ATLAS_FINAL subroutine atlas_fvm_Method__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif end module atlas_fvm_module diff --git a/src/atlas_f/output/atlas_output_module.F90 b/src/atlas_f/output/atlas_output_module.F90 index 008a45776..f46db7f98 100644 --- a/src/atlas_f/output/atlas_output_module.F90 +++ b/src/atlas_f/output/atlas_output_module.F90 @@ -201,6 +201,7 @@ subroutine write_fieldset(this,fieldset,config) !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Output__final_auto(this) type(atlas_Output), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -211,5 +212,6 @@ ATLAS_FINAL subroutine atlas_Output__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif end module atlas_output_module diff --git a/src/atlas_f/parallel/atlas_Checksum_module.fypp b/src/atlas_f/parallel/atlas_Checksum_module.fypp index d17b54002..31e801fa8 100644 --- a/src/atlas_f/parallel/atlas_Checksum_module.fypp +++ b/src/atlas_f/parallel/atlas_Checksum_module.fypp @@ -167,6 +167,7 @@ end function !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Checksum__final_auto(this) type(atlas_Checksum), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -177,6 +178,7 @@ ATLAS_FINAL subroutine atlas_Checksum__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif ! ----------------------------------------------------------------------------- diff --git a/src/atlas_f/parallel/atlas_GatherScatter_module.fypp b/src/atlas_f/parallel/atlas_GatherScatter_module.fypp index 3dc164729..b945e2d8a 100644 --- a/src/atlas_f/parallel/atlas_GatherScatter_module.fypp +++ b/src/atlas_f/parallel/atlas_GatherScatter_module.fypp @@ -263,6 +263,7 @@ end subroutine !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_GatherScatter__final_auto(this) type(atlas_GatherScatter), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -273,6 +274,7 @@ ATLAS_FINAL subroutine atlas_GatherScatter__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif ! ----------------------------------------------------------------------------- diff --git a/src/atlas_f/parallel/atlas_HaloExchange_module.fypp b/src/atlas_f/parallel/atlas_HaloExchange_module.fypp index 1e11bac32..073cf5023 100644 --- a/src/atlas_f/parallel/atlas_HaloExchange_module.fypp +++ b/src/atlas_f/parallel/atlas_HaloExchange_module.fypp @@ -150,6 +150,7 @@ end subroutine !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_HaloExchange__final_auto(this) type(atlas_HaloExchange), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -160,6 +161,7 @@ ATLAS_FINAL subroutine atlas_HaloExchange__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif ! ----------------------------------------------------------------------------- diff --git a/src/atlas_f/runtime/atlas_Trace_module.F90 b/src/atlas_f/runtime/atlas_Trace_module.F90 index 434fc101e..26595d948 100644 --- a/src/atlas_f/runtime/atlas_Trace_module.F90 +++ b/src/atlas_f/runtime/atlas_Trace_module.F90 @@ -193,6 +193,7 @@ subroutine resume( this ) !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Trace__final_auto(this) type(atlas_Trace), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -203,6 +204,7 @@ ATLAS_FINAL subroutine atlas_Trace__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif end module atlas_Trace_module diff --git a/src/atlas_f/trans/atlas_Trans_module.F90 b/src/atlas_f/trans/atlas_Trans_module.F90 index c97335b3f..26d8f46f8 100644 --- a/src/atlas_f/trans/atlas_Trans_module.F90 +++ b/src/atlas_f/trans/atlas_Trans_module.F90 @@ -378,6 +378,7 @@ end subroutine invtrans_grad_field !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Trans__final_auto(this) type(atlas_Trans), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -388,6 +389,7 @@ ATLAS_FINAL subroutine atlas_Trans__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif ! ---------------------------------------------------------------------------------------- diff --git a/src/atlas_f/util/atlas_Config_module.F90 b/src/atlas_f/util/atlas_Config_module.F90 index 528cca359..aee0e3081 100644 --- a/src/atlas_f/util/atlas_Config_module.F90 +++ b/src/atlas_f/util/atlas_Config_module.F90 @@ -94,6 +94,7 @@ module atlas_config_module ! ----------------------------------------------------------------------------- ! Config routines +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Config__final_auto(this) type(atlas_Config), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -104,6 +105,7 @@ ATLAS_FINAL subroutine atlas_Config__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif function atlas_Config__ctor() result(this) use atlas_Config_c_binding diff --git a/src/atlas_f/util/atlas_Metadata_module.F90 b/src/atlas_f/util/atlas_Metadata_module.F90 index 186cec3c6..56caf4fbe 100644 --- a/src/atlas_f/util/atlas_Metadata_module.F90 +++ b/src/atlas_f/util/atlas_Metadata_module.F90 @@ -378,6 +378,7 @@ end function Metadata__json !------------------------------------------------------------------------------- +#if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Metadata__final_auto(this) type(atlas_Metadata), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING @@ -388,6 +389,7 @@ ATLAS_FINAL subroutine atlas_Metadata__final_auto(this) #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine +#endif end module atlas_metadata_module diff --git a/src/tests/array/test_array_slicer.cc b/src/tests/array/test_array_slicer.cc index d89a7663d..d3a88e1d0 100644 --- a/src/tests/array/test_array_slicer.cc +++ b/src/tests/array/test_array_slicer.cc @@ -309,7 +309,6 @@ CASE( "test_arrayview_slice_type" ) { { const auto const_read_write_view = make_view( arr ); - auto read_write_view = make_view( arr ); auto slice1 = const_read_write_view.slice( Range{0, 2}, 2, Range{2, 5} ); diff --git a/src/tests/field/fctest_field_host.F90 b/src/tests/field/fctest_field_host.F90 index cba1673eb..955dd159c 100644 --- a/src/tests/field/fctest_field_host.F90 +++ b/src/tests/field/fctest_field_host.F90 @@ -42,12 +42,11 @@ module fcta_Field_fxt TEST( test_host_data ) type(atlas_Field) :: field -real(8), pointer :: host(:,:) -real(8), pointer :: device(:,:) +real(8), pointer :: view(:,:) field = atlas_Field(kind=atlas_real(8),shape=[10,5]) -call field%data(host) +call field%data(view) FCTEST_CHECK( .not. field%host_needs_update() ) FCTEST_CHECK( .not. field%device_needs_update() ) diff --git a/src/tests/functionspace/fctest_functionspace.F90 b/src/tests/functionspace/fctest_functionspace.F90 index 015c061a1..26bbbae08 100644 --- a/src/tests/functionspace/fctest_functionspace.F90 +++ b/src/tests/functionspace/fctest_functionspace.F90 @@ -516,7 +516,6 @@ module fcta_FunctionSpace_fxt type(atlas_StructuredGrid) :: grid type(atlas_functionspace_StructuredColumns) :: fs type(atlas_functionspace) :: fs_base -type(atlas_functionspace_StructuredColumns) :: fs_struct integer(ATLAS_KIND_IDX) :: i, j character(len=10) str diff --git a/src/tests/grid/fctest_griddistribution.F90 b/src/tests/grid/fctest_griddistribution.F90 index b6c7a4b49..9dc88bec6 100644 --- a/src/tests/grid/fctest_griddistribution.F90 +++ b/src/tests/grid/fctest_griddistribution.F90 @@ -63,7 +63,7 @@ type(atlas_GridDistribution) :: griddistribution integer, allocatable :: part(:) - integer :: jnode + integer(ATLAS_KIND_IDX) :: jnode grid = atlas_StructuredGrid("O16") diff --git a/src/tests/grid/fctest_grids.F90 b/src/tests/grid/fctest_grids.F90 index 00c2b1703..a1c61d257 100644 --- a/src/tests/grid/fctest_grids.F90 +++ b/src/tests/grid/fctest_grids.F90 @@ -31,8 +31,8 @@ use atlas_module implicit none type(atlas_StructuredGrid) :: grid - integer :: jglo, i, j, i1, j1, jglo1 - + integer :: i, j, i1, j1 + integer(ATLAS_KIND_IDX) :: jglo, jglo1 grid = atlas_StructuredGrid ("N16") jglo = 1 diff --git a/src/tests/grid/fctest_unstructuredgrid.F90 b/src/tests/grid/fctest_unstructuredgrid.F90 index 3de694311..48233cccd 100644 --- a/src/tests/grid/fctest_unstructuredgrid.F90 +++ b/src/tests/grid/fctest_unstructuredgrid.F90 @@ -39,7 +39,6 @@ type(atlas_Mesh) :: mesh type(atlas_Output) :: gmsh type(atlas_MeshGenerator) :: meshgenerator - type(atlas_Config) :: config real(c_double) :: xy(2,173) xy(:,1) = [180,0] diff --git a/src/tests/interpolation/test_interpolation_structured2D_to_unstructured.cc b/src/tests/interpolation/test_interpolation_structured2D_to_unstructured.cc index 11fa31eb1..0e0f10bcd 100644 --- a/src/tests/interpolation/test_interpolation_structured2D_to_unstructured.cc +++ b/src/tests/interpolation/test_interpolation_structured2D_to_unstructured.cc @@ -33,14 +33,6 @@ namespace test { //----------------------------------------------------------------------------- -static Config scheme() { - Config scheme; - scheme.set( "type", "structured-linear2D" ); - scheme.set( "halo", 1 ); - scheme.set( "name", "linear" ); - return scheme; -} - std::string input_gridname( const std::string& default_grid ) { return eckit::Resource( "--input-grid", default_grid ); } diff --git a/src/tests/numerics/fctest_fvm_nabla.F90 b/src/tests/numerics/fctest_fvm_nabla.F90 index 092dd5481..2d869d151 100644 --- a/src/tests/numerics/fctest_fvm_nabla.F90 +++ b/src/tests/numerics/fctest_fvm_nabla.F90 @@ -67,9 +67,9 @@ function timer_elapsed(self) result(time) real(c_double) :: time if (.not. self%paused) then call system_clock ( self%clck_counts_stop, self%clck_rate ) - time = (self%counted + self%clck_counts_stop - self%clck_counts_start)/real(self%clck_rate) + time = real(self%counted + self%clck_counts_stop - self%clck_counts_start,c_double)/real(self%clck_rate,c_double) else if (self%counted .ge. 0) then - time = self%counted/real(self%clck_rate) + time = real(self%counted,c_double)/real(self%clck_rate,c_double) else time = 0. end if diff --git a/src/tests/projection/test_rotation.cc b/src/tests/projection/test_rotation.cc index b331a1df9..c48410d71 100644 --- a/src/tests/projection/test_rotation.cc +++ b/src/tests/projection/test_rotation.cc @@ -149,8 +149,8 @@ CASE( "test_rotation_angle" ) { {2.00000, 46.70000}, {-178.00000, -46.70000}, {166.62343, -19.46929}, {156.02366, 8.65459}, {143.57464, 36.43683}, {117.10894, 61.43199}, {43.88245, 68.00825}, {2.00000, 46.70000}, }; - - + + for ( int i = 0, jglo = 0; i < nx; i++ ) { for ( int j = 0; j < ny + 1; j++, jglo++ ) { double lon = static_cast( i ) * 360. / static_cast( nx ); diff --git a/src/tests/trans/fctest_trans.F90 b/src/tests/trans/fctest_trans.F90 index 6e8778222..bd6b488c5 100644 --- a/src/tests/trans/fctest_trans.F90 +++ b/src/tests/trans/fctest_trans.F90 @@ -74,8 +74,7 @@ end module fctest_atlas_trans_fixture type(atlas_FieldSet) :: spectralfields real(c_double), pointer :: scal1(:,:), scal2(:), spec1(:,:), spec2(:), wind(:,:,:), vor(:,:), div(:,:) real(c_double), allocatable :: check(:) - integer :: nlev, truncation, jn, in, jlev - integer, pointer :: nvalue(:) + integer :: nlev, truncation type(atlas_Field) :: glb_vorfield real(c_double) :: tol @@ -252,8 +251,7 @@ end module fctest_atlas_trans_fixture type(atlas_FieldSet) :: spectralfields real(c_double), pointer :: scal1(:,:), scal2(:), spec1(:,:), spec2(:) real(c_double), allocatable :: check(:) - integer :: nlev, truncation, jn, in, jlev - integer, pointer :: nvalue(:) + integer :: nlev, truncation real(c_double) :: tol tol = 1.e-8 @@ -344,7 +342,6 @@ end module fctest_atlas_trans_fixture type(atlas_Field) :: fieldg, field type(atlas_FieldSet) :: gpfields, spfields integer :: jfld, nfld -character(len=10) :: fieldname real(c_double) :: norm real(c_double), pointer :: gvar(:) @@ -406,8 +403,6 @@ end module fctest_atlas_trans_fixture type(atlas_functionspace_Spectral) :: spectral type(atlas_Field) :: field, fieldg integer :: jfld, nfld, T -character(len=10) :: fieldname -real(c_double) :: norm integer, parameter :: RE=0 integer, parameter :: IM=1 integer :: jm, jn, n, m, jc diff --git a/src/tests/trans/test_transgeneral.cc b/src/tests/trans/test_transgeneral.cc index 4fa68dddb..0afbcae2d 100644 --- a/src/tests/trans/test_transgeneral.cc +++ b/src/tests/trans/test_transgeneral.cc @@ -905,8 +905,8 @@ CASE( "test_trans_domain" ) { std::chrono::duration elapsed_seconds = end - start; std::time_t end_time = std::chrono::system_clock::to_time_t( end ); std::string time_str = std::ctime( &end_time ); - //Log::info() << "case " << icase << ", elapsed time: " << elapsed_seconds.count() - // << "s. Now: " << time_str.substr( 0, time_str.length() - 1 ) << std::endl; + Log::debug() << "case " << icase << ", elapsed time: " << elapsed_seconds.count() + << "s. Now: " << time_str.substr( 0, time_str.length() - 1 ) << std::endl; } k++; } @@ -1091,8 +1091,8 @@ CASE( "test_trans_pole" ) { std::chrono::duration elapsed_seconds = end - start; std::time_t end_time = std::chrono::system_clock::to_time_t( end ); std::string time_str = std::ctime( &end_time ); - //Log::info() << "case " << icase << ", elapsed time: " << elapsed_seconds.count() - // << "s. Now: " << time_str.substr( 0, time_str.length() - 1 ) << std::endl; + Log::debug() << "case " << icase << ", elapsed time: " << elapsed_seconds.count() + << "s. Now: " << time_str.substr( 0, time_str.length() - 1 ) << std::endl; } k++; } @@ -1283,8 +1283,8 @@ CASE( "test_trans_southpole" ) { std::chrono::duration elapsed_seconds = end - start; std::time_t end_time = std::chrono::system_clock::to_time_t( end ); std::string time_str = std::ctime( &end_time ); - //Log::info() << "case " << icase << ", elapsed time: " << elapsed_seconds.count() - // << "s. Now: " << time_str.substr( 0, time_str.length() - 1 ) << std::endl; + Log::debug() << "case " << icase << ", elapsed time: " << elapsed_seconds.count() + << "s. Now: " << time_str.substr( 0, time_str.length() - 1 ) << std::endl; } k++; } @@ -1440,8 +1440,8 @@ CASE( "test_trans_unstructured" ) { std::chrono::duration elapsed_seconds = end - start; std::time_t end_time = std::chrono::system_clock::to_time_t( end ); std::string time_str = std::ctime( &end_time ); - //Log::info() << "case " << icase << ", elapsed time: " << elapsed_seconds.count() - // << "s. Now: " << time_str.substr( 0, time_str.length() - 1 ) << std::endl; + Log::debug() << "case " << icase << ", elapsed time: " << elapsed_seconds.count() + << "s. Now: " << time_str.substr( 0, time_str.length() - 1 ) << std::endl; } k++; } From ba652881b31efee2e6cb507434eac48ab3ecf886 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 13 May 2020 15:52:37 +0100 Subject: [PATCH 070/145] Add missing atlas_Config%set( int(c_long) ) overload --- src/atlas_f/util/atlas_Config_module.F90 | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/atlas_f/util/atlas_Config_module.F90 b/src/atlas_f/util/atlas_Config_module.F90 index aee0e3081..32e456a46 100644 --- a/src/atlas_f/util/atlas_Config_module.F90 +++ b/src/atlas_f/util/atlas_Config_module.F90 @@ -41,6 +41,7 @@ module atlas_config_module procedure, private :: set_config_list => atlas_Config__set_config_list procedure, private :: set_logical => atlas_Config__set_logical procedure, private :: set_int32 => atlas_Config__set_int32 + procedure, private :: set_int64 => atlas_Config__set_int64 procedure, private :: set_real32 => atlas_Config__set_real32 procedure, private :: set_real64 => atlas_Config__set_real64 procedure, private :: set_string => atlas_Config__set_string @@ -49,7 +50,7 @@ module atlas_config_module procedure, private :: set_array_real32 => atlas_Config__set_array_real32 procedure, private :: set_array_real64 => atlas_Config__set_array_real64 procedure :: has => atlas_Config__has - generic :: set => set_config, set_config_list, set_logical, set_int32, set_real32, set_real64, & + generic :: set => set_config, set_config_list, set_logical, set_int32, set_int64, set_real32, set_real64, & set_string, set_array_int32, set_array_int64, set_array_real32, set_array_real64 procedure, private :: get_config => atlas_Config__get_config procedure, private :: get_config_list => atlas_Config__get_config_list @@ -212,14 +213,25 @@ subroutine atlas_Config__set_logical(this, name, value) end subroutine atlas_Config__set_logical subroutine atlas_Config__set_int32(this, name, value) + use, intrinsic :: iso_c_binding, only : c_int use fckit_c_interop_module, only : c_str use atlas_Config_c_binding class(atlas_Config), intent(inout) :: this character(len=*), intent(in) :: name - integer, intent(in) :: value + integer(c_int), intent(in) :: value call atlas__Config__set_int(this%CPTR_PGIBUG_B, c_str(name), value) end subroutine atlas_Config__set_int32 +subroutine atlas_Config__set_int64(this, name, value) + use, intrinsic :: iso_c_binding, only : c_long + use fckit_c_interop_module, only : c_str + use atlas_Config_c_binding + class(atlas_Config), intent(inout) :: this + character(len=*), intent(in) :: name + integer(c_long), intent(in) :: value + call atlas__Config__set_long(this%CPTR_PGIBUG_B, c_str(name), value) +end subroutine atlas_Config__set_int64 + subroutine atlas_Config__set_real32(this, name, value) use, intrinsic :: iso_c_binding, only : c_float use fckit_c_interop_module, only : c_str From 9d28808db4b69ea7e6a437c8fe26ccf341440fc9 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 12 May 2020 14:46:34 +0100 Subject: [PATCH 071/145] Fix LambertConformalConicProjection required parameters --- doc/example-grids/regional_lambert_conformal_conic_1.yml | 2 +- doc/example-grids/regional_lambert_conformal_conic_2.yml | 2 +- .../projection/detail/LambertConformalConicProjection.cc | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/example-grids/regional_lambert_conformal_conic_1.yml b/doc/example-grids/regional_lambert_conformal_conic_1.yml index 4d07c0e7f..a05aae2c3 100644 --- a/doc/example-grids/regional_lambert_conformal_conic_1.yml +++ b/doc/example-grids/regional_lambert_conformal_conic_1.yml @@ -8,7 +8,7 @@ lonlat(centre) : [4,50] projection : type : "lambert_conformal_conic" longitude0 : 4 - latitude1 : 50 + latitude0 : 50 y_numbering : +1 diff --git a/doc/example-grids/regional_lambert_conformal_conic_2.yml b/doc/example-grids/regional_lambert_conformal_conic_2.yml index 4f521e113..017e8be0a 100644 --- a/doc/example-grids/regional_lambert_conformal_conic_2.yml +++ b/doc/example-grids/regional_lambert_conformal_conic_2.yml @@ -6,7 +6,7 @@ dy : 13545.0 lonlat(xmin,ymin): [-126.138,16.281] projection : type : "lambert_conformal_conic" - latitude1 : 25.0 + latitude0 : 25.0 longitude0 : -95.0 y_numbering : +1 diff --git a/src/atlas/projection/detail/LambertConformalConicProjection.cc b/src/atlas/projection/detail/LambertConformalConicProjection.cc index 63023710c..3b77a6671 100644 --- a/src/atlas/projection/detail/LambertConformalConicProjection.cc +++ b/src/atlas/projection/detail/LambertConformalConicProjection.cc @@ -59,12 +59,12 @@ static ProjectionBuilder register_1( LambertCon LambertConformalConicProjection::LambertConformalConicProjection( const eckit::Parametrisation& params ) { ATLAS_ASSERT( params.get( "longitude0", lon0_ = 0 ) ); - ATLAS_ASSERT( params.get( "latitude1", lat1_ = 0 ) ); - if ( not params.get( "latitude0", lat0_ ) ) { - lat0_ = lat1_; + ATLAS_ASSERT( params.get( "latitude0", lat0_ = 0 ) ); + if ( not params.get( "latitude1", lat1_ ) ) { + lat1_ = lat0_; } if ( not params.get( "latitude2", lat2_ ) ) { - lat2_ = lat1_; + lat2_ = lat0_; } params.get( "radius", radius_ = util::Earth::radius() ); From 09c93ef3911cbd057c0bb29d39ecb0e8c809f4fe Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 12 May 2020 18:07:32 +0100 Subject: [PATCH 072/145] ATLAS-287 Add minimal Projection Fortran API --- src/atlas/projection/detail/ProjectionImpl.cc | 30 +++ src/atlas/projection/detail/ProjectionImpl.h | 7 + src/atlas_f/CMakeLists.txt | 4 + src/atlas_f/atlas_module.F90 | 5 + .../projection/atlas_Projection_module.F90 | 211 ++++++++++++++++++ src/tests/projection/CMakeLists.txt | 3 + src/tests/projection/fctest_projection.F90 | 113 ++++++++++ 7 files changed, 373 insertions(+) create mode 100644 src/atlas_f/projection/atlas_Projection_module.F90 create mode 100644 src/tests/projection/fctest_projection.F90 diff --git a/src/atlas/projection/detail/ProjectionImpl.cc b/src/atlas/projection/detail/ProjectionImpl.cc index 1ff4d8c13..6a08209a3 100644 --- a/src/atlas/projection/detail/ProjectionImpl.cc +++ b/src/atlas/projection/detail/ProjectionImpl.cc @@ -15,6 +15,7 @@ #include "eckit/types/FloatCompare.h" #include "eckit/utils/Hash.h" +#include "eckit/utils/MD5.h" #include "ProjectionImpl.h" @@ -289,6 +290,35 @@ void Rotated::hash( eckit::Hash& hsh ) const { hsh.add( rotationAngle() ); } +//--------------------------------------------------------------------------------------------------------------------- + +extern "C" { +const ProjectionImpl* atlas__Projection__ctor_config( const eckit::Parametrisation* config ) { + return ProjectionImpl::create( *config ); +} +void atlas__Projection__type( const ProjectionImpl* This, char*& type, int& size ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_Projection" ); + std::string s = This->type(); + size = static_cast( s.size() + 1 ); + type = new char[size]; + strcpy( type, s.c_str() ); +} +void atlas__Projection__hash( const ProjectionImpl* This, char*& hash, int& size ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_Projection" ); + eckit::MD5 md5; + This->hash( md5 ); + std::string s = md5.digest(); + size = static_cast( s.size() + 1 ); + hash = new char[size]; + strcpy( hash, s.c_str() ); +} +ProjectionImpl::Spec* atlas__Projection__spec( const ProjectionImpl* This ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_Projection" ); + return new ProjectionImpl::Spec( This->spec() ); +} + +} // extern "C" + } // namespace detail } // namespace projection } // namespace atlas diff --git a/src/atlas/projection/detail/ProjectionImpl.h b/src/atlas/projection/detail/ProjectionImpl.h index 8218d7ee3..74c0bd710 100644 --- a/src/atlas/projection/detail/ProjectionImpl.h +++ b/src/atlas/projection/detail/ProjectionImpl.h @@ -177,6 +177,13 @@ class NotRotated { void hash( eckit::Hash& ) const {} }; +extern "C" { +const ProjectionImpl* atlas__Projection__ctor_config( const eckit::Parametrisation* config ); +void atlas__Projection__type( const ProjectionImpl* This, char*& type, int& size ); +void atlas__Projection__hash( const ProjectionImpl* This, char*& hash, int& size ); +ProjectionImpl::Spec* atlas__Projection__spec( const ProjectionImpl* This ); +} + } // namespace detail } // namespace projection } // namespace atlas diff --git a/src/atlas_f/CMakeLists.txt b/src/atlas_f/CMakeLists.txt index 4eaee5917..40dac75fb 100644 --- a/src/atlas_f/CMakeLists.txt +++ b/src/atlas_f/CMakeLists.txt @@ -71,6 +71,9 @@ generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/grid/detail/vertical/Vertica MODULE atlas_vertical_c_binding OUTPUT vertical_c_binding.f90 ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/grid/Partitioner.h) +generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/projection/detail/ProjectionImpl.h + MODULE atlas_projection_c_binding + OUTPUT projection_c_binding.f90 ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/mesh/detail/MeshIntf.h MODULE atlas_mesh_c_binding OUTPUT atlas_mesh_c_binding.f90 ) @@ -181,6 +184,7 @@ ecbuild_add_library( TARGET atlas_f parallel/atlas_GatherScatter_module.fypp parallel/atlas_Checksum_module.fypp parallel/atlas_HaloExchange_module.fypp + projection/atlas_Projection_module.F90 trans/atlas_Trans_module.F90 internals/atlas_read_file.h internals/atlas_read_file.cc diff --git a/src/atlas_f/atlas_module.F90 b/src/atlas_f/atlas_module.F90 index 482fc1f64..901ad5233 100644 --- a/src/atlas_f/atlas_module.F90 +++ b/src/atlas_f/atlas_module.F90 @@ -86,6 +86,11 @@ module atlas_module & atlas_functionspace_StructuredColumns use atlas_functionspace_Spectral_module, only: & & atlas_functionspace_Spectral +use atlas_Projection_module, only : & + & atlas_Projection, & + & atlas_RotatedLonLatProjection, & + & atlas_LambertConformalConicProjection, & + & atlas_RotatedSchmidtProjection use atlas_Trans_module, only : & & atlas_Trans use atlas_kinds_module, only: & diff --git a/src/atlas_f/projection/atlas_Projection_module.F90 b/src/atlas_f/projection/atlas_Projection_module.F90 new file mode 100644 index 000000000..5f25fbe29 --- /dev/null +++ b/src/atlas_f/projection/atlas_Projection_module.F90 @@ -0,0 +1,211 @@ +! (C) Copyright 2013 ECMWF. +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +! In applying this licence, ECMWF does not waive the privileges and immunities +! granted to it by virtue of its status as an intergovernmental organisation nor +! does it submit to any jurisdiction. + +#include "atlas/atlas_f.h" + +module atlas_Projection_module + +use fckit_owned_object_module, only : fckit_owned_object +use atlas_config_module, only : atlas_Config + +implicit none + +private :: fckit_owned_object +private :: atlas_Config + +public :: atlas_Projection +public :: atlas_RotatedLonLatProjection +public :: atlas_LambertConformalConicProjection +public :: atlas_RotatedSchmidtProjection + +private + +!------------------------------------------------------------------------------ +TYPE, extends(fckit_owned_object) :: atlas_Projection + +! Purpose : +! ------- +! *atlas_Projection* : + +! Methods : +! ------- +! type: The name or tag this function space was created with + +! Author : +! ------ +! May-2020 Willem Deconinck *ECMWF* + +!------------------------------------------------------------------------------ +contains + procedure, public :: type => atlas_Projection__type + procedure, public :: hash + procedure, public :: spec + +#if FCKIT_FINAL_NOT_INHERITING + final :: atlas_Projection__final_auto +#endif + +END TYPE atlas_Projection + +interface atlas_Projection + module procedure atlas_Projection__ctor_cptr + module procedure atlas_Projection__ctor_config +end interface + +interface atlas_RotatedSchmidtProjection + module procedure atlas_RotatedSchmidtProjection_real64 +end interface + +interface atlas_LambertConformalConicProjection + module procedure atlas_LambertConformalConicProjection_real64 +end interface + +interface atlas_RotatedLonLatProjection + module procedure atlas_RotatedLonLatProjection_NP_real64 +end interface + +!======================================================== +contains +!======================================================== + +function atlas_Projection__ctor_cptr(cptr) result(this) + use, intrinsic :: iso_c_binding, only : c_ptr + type(atlas_Projection) :: this + type(c_ptr), intent(in) :: cptr + call this%reset_c_ptr( cptr ) + call this%return() +end function + +function atlas_Projection__ctor_config(config) result(this) + use atlas_projection_c_binding + use, intrinsic :: iso_c_binding, only : c_ptr + type(atlas_Projection) :: this + type(atlas_Config) :: config + call this%reset_c_ptr( atlas__Projection__ctor_config(config%CPTR_PGIBUG_B) ) + call this%return() +end function + +function atlas_Projection__type(this) result(type_) + use atlas_projection_c_binding + use fckit_c_interop_module, only : c_ptr_to_string, c_ptr_free + use, intrinsic :: iso_c_binding, only : c_ptr + class(atlas_Projection), intent(in) :: this + character(len=:), allocatable :: type_ + type(c_ptr) :: type_c_str + integer :: size + call atlas__Projection__type(this%CPTR_PGIBUG_A, type_c_str, size ) + type_ = c_ptr_to_string(type_c_str) + call c_ptr_free(type_c_str) +end function + +function hash(this) + use atlas_projection_c_binding + use fckit_c_interop_module, only : c_ptr_to_string, c_ptr_free + use, intrinsic :: iso_c_binding, only : c_ptr + class(atlas_Projection), intent(in) :: this + character(len=:), allocatable :: hash + type(c_ptr) :: hash_c_str + integer :: size + call atlas__Projection__hash(this%CPTR_PGIBUG_A, hash_c_str, size ) + hash = c_ptr_to_string(hash_c_str) + call c_ptr_free(hash_c_str) +end function + +function spec(this) + use atlas_projection_c_binding + class(atlas_Projection), intent(in) :: this + type(atlas_Config) :: spec + spec = atlas_Config( atlas__Projection__spec(this%CPTR_PGIBUG_A) ) + call spec%return() +end function + + +!------------------------------------------------------------------------------ + +#if FCKIT_FINAL_NOT_INHERITING +ATLAS_FINAL subroutine atlas_Projection__final_auto(this) + type(atlas_Projection), intent(inout) :: this +#if FCKIT_FINAL_DEBUGGING + write(0,*) "atlas_Projection__final_auto" +#endif +#if FCKIT_FINAL_NOT_PROPAGATING + call this%final() +#endif + FCKIT_SUPPRESS_UNUSED( this ) +end subroutine +#endif + +!------------------------------------------------------------------------------ + + +function atlas_RotatedSchmidtProjection_real64(stretching_factor,north_pole,rotation_angle) result(this) +use, intrinsic :: iso_c_binding, only : c_double +type(atlas_Projection) :: this +real(c_double), intent(in) :: stretching_factor +real(c_double), intent(in), optional :: north_pole(2) +real(c_double), intent(in), optional :: rotation_angle +type(atlas_Config) :: config +config = atlas_Config() +call config%set("type","rotated_schmidt") +call config%set("stretching_factor",stretching_factor) +if( present(rotation_angle) ) then + call config%set("rotation_angle", rotation_angle) +endif +if( present(north_pole) ) then + call config%set("north_pole",north_pole) +endif +this = atlas_Projection(config) +call this%return() +end function + +! ----------------------------------------------------------------------------- + +function atlas_LambertConformalConicProjection_real64(longitude0,latitude0,latitude1,latitude2) result(this) +use, intrinsic :: iso_c_binding, only : c_double +type(atlas_Projection) :: this +real(c_double), intent(in) :: longitude0 +real(c_double), intent(in) :: latitude0 +real(c_double), intent(in), optional :: latitude1 +real(c_double), intent(in), optional :: latitude2 +type(atlas_Config) :: config +config = atlas_Config() +call config%set("type","lambert_conformal_conic") +call config%set("longitude0",longitude0) +call config%set("latitude0",latitude0) +if( present(latitude1) ) then + call config%set("latitude1", latitude1) +endif +if( present(latitude2) ) then + call config%set("latitude2",latitude2) +endif +this = atlas_Projection(config) +call this%return() +end function + +! ----------------------------------------------------------------------------- + +function atlas_RotatedLonLatProjection_NP_real64(north_pole,rotation_angle) result(this) +use, intrinsic :: iso_c_binding, only : c_double +type(atlas_Projection) :: this +real(c_double), intent(in) :: north_pole(2) +real(c_double), intent(in), optional :: rotation_angle +type(atlas_Config) :: config +config = atlas_Config() +call config%set("type","rotated_lonlat") +call config%set("north_pole",north_pole) +if( present(rotation_angle) ) then + call config%set("rotation_angle", rotation_angle) +endif +this = atlas_Projection(config) +call this%return() +end function + +! ----------------------------------------------------------------------------- + +end module atlas_Projection_module + diff --git a/src/tests/projection/CMakeLists.txt b/src/tests/projection/CMakeLists.txt index 84b952e0b..4557c11c2 100644 --- a/src/tests/projection/CMakeLists.txt +++ b/src/tests/projection/CMakeLists.txt @@ -19,3 +19,6 @@ if(atlas_HAVE_PROJ) ecbuild_add_test( TARGET atlas_test_proj SOURCES test_proj.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endif() +if( HAVE_FCTEST ) + add_fctest( TARGET atlas_fctest_projection SOURCES fctest_projection.F90 LINKER_LANGUAGE Fortran LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) +endif() diff --git a/src/tests/projection/fctest_projection.F90 b/src/tests/projection/fctest_projection.F90 new file mode 100644 index 000000000..b67d0a01f --- /dev/null +++ b/src/tests/projection/fctest_projection.F90 @@ -0,0 +1,113 @@ +! (C) Copyright 2013 ECMWF. +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +! In applying this licence, ECMWF does not waive the privileges and immunities +! granted to it by virtue of its status as an intergovernmental organisation nor +! does it submit to any jurisdiction. + +! This File contains Unit Tests for testing the +! C++ / Fortran Interfaces to the State Datastructure +! @author Willem Deconinck + +#include "fckit/fctest.h" + +! ----------------------------------------------------------------------------- + +module projection_fixture +use atlas_module +end module + +! ----------------------------------------------------------------------------- + +TESTSUITE_WITH_FIXTURE(fctest_projection,projection_fixture) + +! ----------------------------------------------------------------------------- + +TESTSUITE_INIT + call atlas_library%initialise() +END_TESTSUITE_INIT + +! ----------------------------------------------------------------------------- + +TESTSUITE_FINALIZE + call atlas_library%finalise() +END_TESTSUITE_FINALIZE + +! ----------------------------------------------------------------------------- + +TEST( test_rotated_schmidt_config ) +type(atlas_Projection) :: projection +type(atlas_Config) :: config +config = atlas_Config() +call config%set("type","rotated_schmidt") +call config%set("stretching_factor",2.0_dp) +call config%set("rotation_angle", 180.0_dp) +call config%set("north_pole", [2.0_dp,46.7_dp] ) +projection = atlas_Projection(config) +FCTEST_CHECK_EQUAL( projection%type(), "rotated_schmidt" ) +FCTEST_CHECK_EQUAL( projection%hash(), "148d7ceb58250c0f48dc6b590941a341" ) +call config%final() +call projection%final() +END_TEST + +TEST( test_rotated_schmidt_specific_constructor ) +type(atlas_Projection) :: projection +projection = atlas_RotatedSchmidtProjection( stretching_factor=2.0_dp, & + north_pole=[2.0_dp,46.7_dp], rotation_angle=180.0_dp) +FCTEST_CHECK_EQUAL( projection%type(), "rotated_schmidt" ) +FCTEST_CHECK_EQUAL( projection%hash(), "148d7ceb58250c0f48dc6b590941a341" ) +call projection%final() +END_TEST + +! ----------------------------------------------------------------------------- + +TEST( test_lambert_conformal_conic_config ) +type(atlas_Projection) :: projection +type(atlas_Config) :: config +config = atlas_Config() +call config%set("type","lambert_conformal_conic") +call config%set("longitude0",4.0_dp) +call config%set("latitude0", 50.0_dp) +projection = atlas_Projection(config) +FCTEST_CHECK_EQUAL( projection%type(), "lambert_conformal_conic" ) +FCTEST_CHECK_EQUAL( projection%hash(), "47244ee950cf8357763a9b5b871b52ab" ) +call config%final() +call projection%final() +END_TEST + +TEST( test_lambert_conformal_conic_specific_constructor ) +type(atlas_Projection) :: projection +projection = atlas_LambertConformalConicProjection(4.0_dp,50._dp) +FCTEST_CHECK_EQUAL( projection%type(), "lambert_conformal_conic" ) +FCTEST_CHECK_EQUAL( projection%hash(), "47244ee950cf8357763a9b5b871b52ab" ) +call projection%final() +END_TEST + +! ----------------------------------------------------------------------------- + +TEST( test_rotated_lonlat_config ) +type(atlas_Projection) :: projection +type(atlas_Config) :: config +config = atlas_Config() +call config%set("type","rotated_lonlat") +call config%set("north_pole", [2.0_dp,46.7_dp] ) +call config%set("rotation_angle", 180.0_dp) +projection = atlas_Projection(config) +FCTEST_CHECK_EQUAL( projection%type(), "rotated_lonlat" ) +FCTEST_CHECK_EQUAL( projection%hash(), "2b6db0e1ccbe7c514dd726f408f92adb" ) +call config%final() +call projection%final() +END_TEST + +TEST( test_rotated_lonlat_specific_constructor ) +type(atlas_Projection) :: projection +projection = atlas_RotatedLonLatProjection([2.0_dp,46.7_dp],180._dp) +FCTEST_CHECK_EQUAL( projection%type(), "rotated_lonlat" ) +FCTEST_CHECK_EQUAL( projection%hash(), "2b6db0e1ccbe7c514dd726f408f92adb" ) +call projection%final() +END_TEST + +! ----------------------------------------------------------------------------- + +END_TESTSUITE + From 69dffe37a08b0dbb71746aefcd46578daf1f9969 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 13 May 2020 14:57:18 +0100 Subject: [PATCH 073/145] ATLAS-289 y_numbering for regional_grid changed to be +1 by default --- src/atlas/grid/detail/grid/Regional.cc | 2 +- src/tests/interpolation/test_interpolation_structured2D.cc | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/atlas/grid/detail/grid/Regional.cc b/src/atlas/grid/detail/grid/Regional.cc index 3e29bc2a8..1fe05e5ad 100644 --- a/src/atlas/grid/detail/grid/Regional.cc +++ b/src/atlas/grid/detail/grid/Regional.cc @@ -306,7 +306,7 @@ static class regional : public GridBuilder { throw_Exception( "Could not parse configuration for RegularRegional grid", Here() ); } - YSpace yspace = config.getInt( "y_numbering", -1 ) < 0 ? LinearSpacing( y.max, y.min, y.N, y.endpoint ) + YSpace yspace = config.getInt( "y_numbering", +1 ) < 0 ? LinearSpacing( y.max, y.min, y.N, y.endpoint ) : LinearSpacing( y.min, y.max, y.N, y.endpoint ); bool with_endpoint = true; diff --git a/src/tests/interpolation/test_interpolation_structured2D.cc b/src/tests/interpolation/test_interpolation_structured2D.cc index 5b83e7422..cb01468c5 100644 --- a/src/tests/interpolation/test_interpolation_structured2D.cc +++ b/src/tests/interpolation/test_interpolation_structured2D.cc @@ -68,6 +68,7 @@ Grid rotated_mercator() { gridspec.set( "ny", 40 ); gridspec.set( "dx", 50000 ); gridspec.set( "dy", 50000 ); + gridspec.set( "y_numbering", -1 ); gridspec.set( "lonlat(centre)", std::vector{4., 50} ); gridspec.set( "projection", [] { Config projection; @@ -85,12 +86,13 @@ Grid lambert() { gridspec.set( "ny", 40 ); gridspec.set( "dx", 50000 ); gridspec.set( "dy", 50000 ); + gridspec.set( "y_numbering", -1 ); gridspec.set( "lonlat(centre)", std::vector{4., 50} ); gridspec.set( "projection", [] { Config projection; projection.set( "type", "lambert_conformal_conic" ); projection.set( "longitude0", 4. ); - projection.set( "latitude1", 50. ); + projection.set( "latitude0", 50. ); return projection; }() ); return Grid{gridspec}; From bfdec9aaeffd09bd1b0041a127a3380821484a66 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 13 May 2020 15:26:27 +0100 Subject: [PATCH 074/145] grid%uid() Fortran binding --- src/atlas/grid/detail/grid/Grid.cc | 11 +++++++++++ src/atlas/grid/detail/grid/Grid.h | 1 + src/atlas_f/grid/atlas_Grid_module.F90 | 15 ++++++++++++++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/atlas/grid/detail/grid/Grid.cc b/src/atlas/grid/detail/grid/Grid.cc index 67081b65a..0083f88cb 100644 --- a/src/atlas/grid/detail/grid/Grid.cc +++ b/src/atlas/grid/detail/grid/Grid.cc @@ -148,6 +148,17 @@ Grid::Spec* atlas__grid__Grid__spec( Grid* This ) { return new Grid::Spec( This->spec() ); } +void atlas__grid__Grid__uid( const Grid* This, char*& uid, int& size ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_Projection" ); + eckit::MD5 md5; + This->hash( md5 ); + std::string s = This->uid(); + size = static_cast( s.size() + 1 ); + uid = new char[size]; + strcpy( uid, s.c_str() ); +} + + } // namespace grid } // namespace detail } // namespace grid diff --git a/src/atlas/grid/detail/grid/Grid.h b/src/atlas/grid/detail/grid/Grid.h index 71bc3a9ef..bc9fb61da 100644 --- a/src/atlas/grid/detail/grid/Grid.h +++ b/src/atlas/grid/detail/grid/Grid.h @@ -180,6 +180,7 @@ class GridObserver { extern "C" { idx_t atlas__grid__Grid__size( Grid* This ); Grid::Spec* atlas__grid__Grid__spec( Grid* This ); +void atlas__grid__Grid__uid( const Grid* This, char*& uid, int& size ); } } // namespace grid diff --git a/src/atlas_f/grid/atlas_Grid_module.F90 b/src/atlas_f/grid/atlas_Grid_module.F90 index b62573525..121451abe 100644 --- a/src/atlas_f/grid/atlas_Grid_module.F90 +++ b/src/atlas_f/grid/atlas_Grid_module.F90 @@ -49,6 +49,7 @@ module atlas_Grid_module contains procedure :: size => atlas_Grid__size procedure :: spec => atlas_Grid__spec + procedure :: uid #if FCKIT_FINAL_NOT_INHERITING final :: atlas_Grid__final_auto @@ -137,7 +138,6 @@ module atlas_Grid_module procedure, private :: lonlat_64 => Structured__lonlat_64 generic :: lonlat => lonlat_32, lonlat_64 procedure :: reduced => Structured__reduced - #if FCKIT_FINAL_NOT_INHERITING final :: atlas_StructuredGrid__final_auto #endif @@ -560,6 +560,19 @@ function atlas_Grid__spec(this) result(spec) call spec%return () end function +function uid(this) + use atlas_grid_Grid_c_binding + use fckit_c_interop_module, only : c_ptr_to_string, c_ptr_free + use, intrinsic :: iso_c_binding, only : c_ptr + class(atlas_Grid), intent(in) :: this + character(len=:), allocatable :: uid + type(c_ptr) :: uid_c_str + integer :: size + call atlas__grid__Grid__uid(this%CPTR_PGIBUG_A, uid_c_str, size ) + uid = c_ptr_to_string(uid_c_str) + call c_ptr_free(uid_c_str) +end function + function Gaussian__N(this) result(N) use, intrinsic :: iso_c_binding, only: c_long use atlas_grid_Structured_c_binding From 44bfec91238b63503f7dbfb18d86baddf650a55f Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 13 May 2020 15:57:16 +0100 Subject: [PATCH 075/145] ATLAS-288 Fortran bindings to create atlas_RegionalGrid --- src/atlas_f/atlas_module.F90 | 3 +- src/atlas_f/grid/atlas_Grid_module.F90 | 197 ++++++++++++++++++++++- src/tests/grid/CMakeLists.txt | 1 + src/tests/grid/fctest_regional_grids.F90 | 145 +++++++++++++++++ 4 files changed, 344 insertions(+), 2 deletions(-) create mode 100644 src/tests/grid/fctest_regional_grids.F90 diff --git a/src/atlas_f/atlas_module.F90 b/src/atlas_f/atlas_module.F90 index 901ad5233..c5207acd1 100644 --- a/src/atlas_f/atlas_module.F90 +++ b/src/atlas_f/atlas_module.F90 @@ -73,7 +73,8 @@ module atlas_module & atlas_GaussianGrid, & & atlas_ReducedGaussianGrid, & & atlas_RegularGaussianGrid, & - & atlas_RegularLonLatGrid + & atlas_RegularLonLatGrid, & + & atlas_RegionalGrid use atlas_Vertical_module, only :& & atlas_Vertical use atlas_functionspace_EdgeColumns_module, only: & diff --git a/src/atlas_f/grid/atlas_Grid_module.F90 b/src/atlas_f/grid/atlas_Grid_module.F90 index 121451abe..8ed4fda7e 100644 --- a/src/atlas_f/grid/atlas_Grid_module.F90 +++ b/src/atlas_f/grid/atlas_Grid_module.F90 @@ -12,6 +12,7 @@ module atlas_Grid_module use fckit_owned_object_module, only: fckit_owned_object use atlas_Config_module, only: atlas_Config +use atlas_Projection_module, only : atlas_Projection use atlas_kinds_module, only : ATLAS_KIND_IDX, ATLAS_KIND_GIDX use, intrinsic :: iso_c_binding, only : c_ptr @@ -19,6 +20,7 @@ module atlas_Grid_module private :: fckit_owned_object private :: atlas_Config +private :: atlas_Projection private :: c_ptr public :: atlas_Grid @@ -28,6 +30,7 @@ module atlas_Grid_module public :: atlas_ReducedGaussianGrid public :: atlas_RegularGaussianGrid public :: atlas_RegularLonLatGrid +public :: atlas_RegionalGrid private @@ -269,6 +272,16 @@ module atlas_Grid_module !------------------------------------------------------------------------------ +interface atlas_RegionalGrid + module procedure atlas_RegionalGrid_ctor_int32 + module procedure atlas_RegionalGrid_ctor_int64 + module procedure atlas_RegionalGrid_ctor_nwse_int32 + module procedure atlas_RegionalGrid_ctor_nwse_int64 + module procedure atlas_RegionalGrid_ctor_increments_int32 + module procedure atlas_RegionalGrid_ctor_increments_int64 +end interface + +!------------------------------------------------------------------------------ interface c_idx module procedure c_idx_32 module procedure c_idx_64 @@ -279,8 +292,8 @@ module atlas_Grid_module module procedure c_gidx_64 end interface - !------------------------------------------------------------------------------ + !======================================================== contains !======================================================== @@ -807,4 +820,186 @@ function Structured__lonlat_64(this, i,j) result(lonlat) ! ---------------------------------------------------------------------------------------- +function atlas_RegionalGrid_ctor_int32( nx, ny, xy_min, xy_max, projection, y_numbering ) result(this) + use, intrinsic :: iso_c_binding, only : c_int, c_double + type(atlas_StructuredGrid) :: this + integer(c_int), intent(in) :: nx, ny + real(c_double), intent(in) :: xy_min(2), xy_max(2) + type(atlas_Projection), intent(in), optional :: projection + integer(c_int), intent(in), optional :: y_numbering + type(atlas_Config) :: config + + config = atlas_Config() + call config%set("type","regional") + call config%set("nx",nx) + call config%set("ny",ny) + call config%set("xmin",xy_min(1)) + call config%set("ymin",xy_min(2)) + call config%set("xmax",xy_max(1)) + call config%set("ymax",xy_max(2)) + if( present(projection) ) then + call config%set("projection",projection%spec()) + endif + if( present(y_numbering) ) then + call config%set("y_numbering",y_numbering) + endif + this = atlas_StructuredGrid(config) + call config%final() + call this%return() +end function + +function atlas_RegionalGrid_ctor_int64( nx, ny, xy_min, xy_max, projection, y_numbering ) result(this) + use, intrinsic :: iso_c_binding, only : c_int, c_long, c_double + type(atlas_StructuredGrid) :: this + integer(c_long), intent(in) :: nx, ny + real(c_double), intent(in) :: xy_min(2), xy_max(2) + type(atlas_Projection), intent(in), optional :: projection + integer(c_int), intent(in), optional :: y_numbering + type(atlas_Config) :: config + + config = atlas_Config() + call config%set("type","regional") + call config%set("nx",nx) + call config%set("ny",ny) + call config%set("xmin",xy_min(1)) + call config%set("ymin",xy_min(2)) + call config%set("xmax",xy_max(1)) + call config%set("ymax",xy_max(2)) + if( present(projection) ) then + call config%set("projection",projection%spec()) + endif + if( present(y_numbering) ) then + call config%set("y_numbering",y_numbering) + endif + this = atlas_StructuredGrid(config) + call config%final() + call this%return() +end function + + +function atlas_RegionalGrid_ctor_nwse_int32( nx, ny, north, west, south, east, projection, y_numbering ) result(this) + use, intrinsic :: iso_c_binding, only : c_int, c_double + type(atlas_StructuredGrid) :: this + integer(c_int), intent(in) :: nx, ny + real(c_double), intent(in) :: north, west, south, east + type(atlas_Projection), intent(in), optional :: projection + integer(c_int), intent(in), optional :: y_numbering + type(atlas_Config) :: config + + config = atlas_Config() + call config%set("type","regional") + call config%set("nx",nx) + call config%set("ny",ny) + call config%set("north",north) + call config%set("west",west) + call config%set("south",south) + call config%set("east",east) + if( present(projection) ) then + call config%set("projection",projection%spec()) + endif + if( present(y_numbering) ) then + call config%set("y_numbering",y_numbering) + endif + this = atlas_StructuredGrid(config) + call config%final() + call this%return() +end function + +function atlas_RegionalGrid_ctor_nwse_int64( nx, ny, north, west, south, east, projection, y_numbering ) result(this) + use, intrinsic :: iso_c_binding, only : c_int, c_long, c_double + type(atlas_Grid) :: this + integer(c_long), intent(in) :: nx, ny + real(c_double), intent(in) :: north, west, south, east + type(atlas_Projection), intent(in), optional :: projection + integer(c_int), intent(in), optional :: y_numbering + type(atlas_Config) :: config + + config = atlas_Config() + call config%set("type","regional") + call config%set("nx",nx) + call config%set("ny",ny) + call config%set("north",north) + call config%set("west",west) + call config%set("south",south) + call config%set("east",east) + if( present(projection) ) then + call config%set("projection",projection%spec()) + endif + if( present(y_numbering) ) then + call config%set("y_numbering",y_numbering) + endif + this = atlas_StructuredGrid(config) + call config%final() + call this%return() +end function + + +function atlas_RegionalGrid_ctor_increments_int32( nx, ny, dx, dy, xy_min, projection, y_numbering ) result(this) + use iso_c_binding, only : c_int, c_double + type(atlas_StructuredGrid) :: this + integer(c_int), intent(in) :: nx + integer(c_int), intent(in) :: ny + real(c_double), intent(in) :: xy_min(2) + real(c_double), intent(in) :: dx + real(c_double), intent(in) :: dy + type(atlas_Projection), intent(in), optional :: projection + integer(c_int), intent(in), optional :: y_numbering + + type(atlas_Config) :: config + + config = atlas_Config() + call config%set("type","regional") + call config%set("nx",nx) + call config%set("ny",ny) + call config%set("xmin",xy_min(1)) + call config%set("ymin",xy_min(2)) + call config%set("dx",dx) + call config%set("dy",dy) + if( present(projection) ) then + call config%set("projection",projection%spec()) + endif + if( present(y_numbering) ) then + call config%set("y_numbering",y_numbering) + endif + + this = atlas_StructuredGrid(config) + call config%final() + call this%return() +end function + +function atlas_RegionalGrid_ctor_increments_int64( nx, ny, dx, dy, xy_min, projection, y_numbering ) result(this) + use iso_c_binding, only : c_int, c_long, c_double + type(atlas_StructuredGrid) :: this + integer(c_long), intent(in) :: nx + integer(c_long), intent(in) :: ny + real(c_double), intent(in) :: xy_min(2) + real(c_double), intent(in) :: dx + real(c_double), intent(in) :: dy + type(atlas_Projection), intent(in), optional :: projection + integer(c_int), intent(in), optional :: y_numbering + + type(atlas_Config) :: config + + config = atlas_Config() + call config%set("type","regional") + call config%set("nx",nx) + call config%set("ny",ny) + call config%set("xmin",xy_min(1)) + call config%set("ymin",xy_min(2)) + call config%set("dx",dx) + call config%set("dy",dy) + if( present(projection) ) then + call config%set("projection",projection%spec()) + endif + if( present(y_numbering) ) then + call config%set("y_numbering",y_numbering) + endif + + this = atlas_StructuredGrid(config) + call config%final() + call this%return() +end function + +! ---------------------------------------------------------------------------------------- + end module atlas_Grid_module diff --git a/src/tests/grid/CMakeLists.txt b/src/tests/grid/CMakeLists.txt index 97e14be36..6b702fa01 100644 --- a/src/tests/grid/CMakeLists.txt +++ b/src/tests/grid/CMakeLists.txt @@ -10,6 +10,7 @@ if( HAVE_FCTEST ) foreach(test fctest_griddistribution fctest_grids + fctest_regional_grids fctest_state ) add_fctest( TARGET atlas_${test} SOURCES ${test}.F90 LINKER_LANGUAGE Fortran LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) diff --git a/src/tests/grid/fctest_regional_grids.F90 b/src/tests/grid/fctest_regional_grids.F90 new file mode 100644 index 000000000..7cb209c20 --- /dev/null +++ b/src/tests/grid/fctest_regional_grids.F90 @@ -0,0 +1,145 @@ +! (C) Copyright 2013 ECMWF. +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +! In applying this licence, ECMWF does not waive the privileges and immunities +! granted to it by virtue of its status as an intergovernmental organisation nor +! does it submit to any jurisdiction. + +! @author Willem Deconinck + +#include "fckit/fctest.h" + +module fixture_reg_grid +use atlas_module +use, intrinsic :: iso_c_binding, only : c_double +implicit none +contains + +subroutine print_spec( grid ) + type(atlas_Grid) :: grid + type(atlas_Config) :: spec + spec = grid%spec() + write(0,*) spec%json() + call spec%final() +end subroutine + +end module + +! ----------------------------------------------------------------------------- + +TESTSUITE_WITH_FIXTURE(fctest_regional_Grid, fixture_reg_grid) + +! ----------------------------------------------------------------------------- + +TESTSUITE_INIT + call atlas_library%initialise() +END_TESTSUITE_INIT + +! ----------------------------------------------------------------------------- + +TESTSUITE_FINALIZE + call atlas_library%finalise() +END_TESTSUITE_FINALIZE + +TEST( simple_regional_grid ) +#if 1 + type(atlas_Grid) :: grid1, grid2 + grid1 = atlas_RegionalGrid( nx=11, ny=11, xy_min=[0._dp,-5._dp], xy_max=[10._dp,5._dp] ) + grid2 = atlas_RegionalGrid( nx=11, ny=11, north=5._dp, west=0._dp, south=-5._dp, east=10._dp ) + call print_spec(grid1) + call print_spec(grid2) + FCTEST_CHECK_EQUAL( grid1%uid() , "0aae0dfb8141e8c70ef63108e6c59ac1" ) + FCTEST_CHECK_EQUAL( grid1%uid() , grid2%uid() ) + call grid1%final() + call grid2%final() +#endif +END_TEST + +TEST( rotated_regional_grid ) +#if 1 + type(atlas_Grid) :: grid1, grid2 + grid1 = atlas_RegionalGrid( nx=11, ny=11, xy_min=[0._dp,-5._dp], xy_max=[10._dp,5._dp], & + & projection=atlas_RotatedLonLatProjection([40._dp,20._dp]) ) + grid2 = atlas_RegionalGrid( nx=11, ny=11, north=5._dp, west=0._dp, south=-5._dp, east=10._dp, & + & projection=atlas_RotatedLonLatProjection([40._dp,20._dp]) ) + call print_spec(grid1) + FCTEST_CHECK_EQUAL( grid1%uid() , grid2%uid() ) + call grid1%final() + call grid2%final() +#endif +END_TEST + +TEST( lambert_grid ) +#if 1 + type(atlas_Grid) :: grid + grid = atlas_RegionalGrid( nx=11, ny=11, dx=10000._dp,dy=10000._dp, & + & xy_min=[-50000._dp,-50000._dp], & + & projection=atlas_LambertConformalConicProjection(4.0_dp,50._dp) ) + call print_spec(grid) + call grid%final() +#endif +END_TEST + +TEST( test_regional_lonlat_grid_MF ) +#if 1 + ! Grid provided by Philippe Marguinaud, Meteo France + type(atlas_StructuredGrid) :: grid + real(c_double), parameter :: zlonw = -20., zlone = +10., zlats = 10., zlatn = 50. + integer(c_int), parameter :: ilons = 30, ilats = 40 + real(c_double), parameter :: tol = 1.e-5_dp + + grid = atlas_RegionalGrid( nx=ilons, ny=ilats, north=zlatn, west=zlonw, south=zlats, east=zlone ) + + FCTEST_CHECK_EQUAL( grid%size(), ilons*ilats ) + + FCTEST_CHECK_CLOSE( grid%lonlat(1,1), ([-0.2000000000E+02_dp, 0.1000000000E+02_dp]), tol) + FCTEST_CHECK_CLOSE( grid%lonlat(2,1), ([-0.1896551724E+02_dp, 0.1000000000E+02_dp]), tol) + FCTEST_CHECK_CLOSE( grid%lonlat(ilons,1), ([ 0.1000000000E+02_dp, 0.1000000000E+02_dp]), tol) + FCTEST_CHECK_CLOSE( grid%lonlat(1,2), ([-0.2000000000E+02_dp, 0.1102564103E+02_dp]), tol) + FCTEST_CHECK_CLOSE( grid%lonlat(ilons,ilats), ([ 0.1000000000E+02_dp, 0.5000000000E+02_dp]), tol) +#endif +END_TEST + + + +TEST( test_regional_lambert_grid_MF ) +#if 1 + ! Grid provided by Philippe Marguinaud, Meteo France + type(atlas_StructuredGrid) :: grid + + integer(c_int), parameter :: ndlon=64 + integer(c_int), parameter :: ndglg=64 + integer(c_int), parameter :: nux=53 + integer(c_int), parameter :: nuy=53 + real(c_double), parameter :: dxinmetres=50000. + real(c_double), parameter :: dyinmetres=50000. + real(c_double), parameter :: xmin = int(real(-nux,c_double)/2.) * dxinmetres + real(c_double), parameter :: ymin = int(real(-nuy,c_double)/2.) * dyinmetres + real(c_double), parameter :: ladindegrees=46.2 + real(c_double), parameter :: latin1indegrees=46.2 + real(c_double), parameter :: latin2indegrees=46.2 + real(c_double), parameter :: lovindegrees=2.0 + real(c_double), parameter :: tol = 1.e-5_dp + + grid = atlas_RegionalGrid( nx=ndlon, ny=ndglg, xy_min=[xmin,ymin], & + & dx=dxinmetres, dy=dyinmetres, & + & projection=atlas_LambertConformalConicProjection(lovindegrees,ladindegrees, & + & latin1indegrees,latin2indegrees) ) + + FCTEST_CHECK_EQUAL( grid%size(), ndglg*ndlon ) + + FCTEST_CHECK_CLOSE( grid%lonlat(1,1), ([-0.1178699484E+02_dp, 0.3358923512E+02_dp]), tol) + FCTEST_CHECK_CLOSE( grid%lonlat(2,1), ([-0.1126673471E+02_dp, 0.3366377557E+02_dp]), tol) + FCTEST_CHECK_CLOSE( grid%lonlat(ndlon,1), ([ 0.2142256121E+02_dp, 0.3258651702E+02_dp]), tol) + FCTEST_CHECK_CLOSE( grid%lonlat(1,2), ([-0.1187876836E+02_dp, 0.3402241700E+02_dp]), tol) + FCTEST_CHECK_CLOSE( grid%lonlat(ndlon,ndglg), ([ 0.3452466369E+02_dp, 0.5925747619E+02_dp]), tol) + + call grid%final() +#endif +END_TEST + + +! ----------------------------------------------------------------------------- + +END_TESTSUITE + From 776f540a629b164663cd0092436e722b4a9d7637 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 15 May 2020 10:49:01 +0100 Subject: [PATCH 076/145] Allow to create set and construct Config with initializer_list --- src/atlas/util/Config.h | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/atlas/util/Config.h b/src/atlas/util/Config.h index 94ae33ae3..12fd2513e 100644 --- a/src/atlas/util/Config.h +++ b/src/atlas/util/Config.h @@ -43,13 +43,17 @@ class Config : public eckit::LocalConfiguration { template Config( const std::string& name, const ValueT& value ); + template + Config( const std::string& name, std::initializer_list&& value ); + // -- Mutators /// @brief Operator that sets a key-value pair. template Config operator()( const std::string& name, const ValueT& value ); - Config operator()( const std::string& name, const std::initializer_list& value ); + template + Config operator()( const std::string& name, std::initializer_list&& value ); // Overload operators to merge two Config objects. Config operator|( const Config& other ) const; @@ -61,6 +65,9 @@ class Config : public eckit::LocalConfiguration { Config& set( const eckit::LocalConfiguration& ); + template + Config& set( const std::string& name, std::initializer_list&& value ); + // -- Accessors, overloaded from eckit::Parametrisation using eckit::LocalConfiguration::get; @@ -74,14 +81,26 @@ inline Config::Config( const std::string& name, const ValueT& value ) : eckit::L set( name, value ); } +template +inline Config::Config( const std::string& name, std::initializer_list&& value ) : eckit::LocalConfiguration() { + set( name, value ); +} + template inline Config Config::operator()( const std::string& name, const ValueT& value ) { set( name, value ); return *this; } -inline Config Config::operator()( const std::string& name, const std::initializer_list& value ) { - set( name, std::vector( value.begin(), value.end() ) ); +template +inline Config& Config::set( const std::string& name, std::initializer_list&& value ) { + set( name, std::vector( value.begin(), value.end() ) ); + return *this; +} + +template +inline Config Config::operator()( const std::string& name, std::initializer_list&& value ) { + set( name, std::vector( value.begin(), value.end() ) ); return *this; } From 77b533a050c668cf0490eed0f1b4daa85702dd2a Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 15 May 2020 10:50:44 +0100 Subject: [PATCH 077/145] Add possibility to create Projection with type as first string argument --- src/atlas/projection/Projection.cc | 3 +++ src/atlas/projection/Projection.h | 1 + src/atlas/projection/detail/ProjectionImpl.cc | 5 +++++ src/atlas/projection/detail/ProjectionImpl.h | 1 + 4 files changed, 10 insertions(+) diff --git a/src/atlas/projection/Projection.cc b/src/atlas/projection/Projection.cc index 632085c25..da45f883b 100644 --- a/src/atlas/projection/Projection.cc +++ b/src/atlas/projection/Projection.cc @@ -22,6 +22,9 @@ Projection::Projection() : Handle( new projection::detail::LonLatProjection() ) Projection::Projection( const eckit::Parametrisation& p ) : Handle( Implementation::create( p ) ) {} +Projection::Projection( const std::string& type, const eckit::Parametrisation& p ) : + Handle( Implementation::create( type, p ) ) {} + atlas::Projection::operator bool() const { return get()->operator bool(); } diff --git a/src/atlas/projection/Projection.h b/src/atlas/projection/Projection.h index 74aee4426..cae33068a 100644 --- a/src/atlas/projection/Projection.h +++ b/src/atlas/projection/Projection.h @@ -53,6 +53,7 @@ class Projection : DOXYGEN_HIDE( public util::ObjectHandle Date: Fri, 15 May 2020 10:57:50 +0100 Subject: [PATCH 078/145] No longer assume that Gaussian grids don't have a projection --- src/atlas/grid/StructuredGrid.h | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/atlas/grid/StructuredGrid.h b/src/atlas/grid/StructuredGrid.h index f7bd8f53a..240d9ab38 100644 --- a/src/atlas/grid/StructuredGrid.h +++ b/src/atlas/grid/StructuredGrid.h @@ -166,15 +166,9 @@ class Gaussian : public Grid { idx_t N() const { return Grid::ny() / 2; } - inline double lon( idx_t i, idx_t j ) const { return Grid::x( i, j ); } - - inline double lat( idx_t j ) const { return Grid::y( j ); } - - PointLonLat lonlat( idx_t i, idx_t j ) const { return Grid::xy( i, j ); } - protected: bool gaussian() const { - return Grid::domain().global() && not Grid::projection() && Grid::yspace().type() == "gaussian"; + return Grid::domain().global() && Grid::yspace().type() == "gaussian"; } }; @@ -222,12 +216,6 @@ class RegularGaussianGrid : public Gaussian { using grid_t::grid_t; RegularGaussianGrid( int N, const Domain& = Domain() ); - inline double lon( idx_t i ) const { return x( i ); } - - inline double lat( idx_t j ) const { return y( j ); } - - PointLonLat lonlat( idx_t i, idx_t j ) const { return xy( i, j ); } - operator bool() const { return valid(); } bool valid() const { return RegularGrid::valid() && gaussian(); } From 7b2dc804981f3ce7bcf808052782c2d0ff9919a2 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 15 May 2020 11:00:10 +0100 Subject: [PATCH 079/145] ATLAS-290 Fortran bindings for ReducedGaussianGrid with Projection This implements an alternative approach to the one proposed in Github #22 Co-authored-by: Philippe Marguinaud --- src/atlas/grid/StructuredGrid.cc | 6 + src/atlas/grid/StructuredGrid.h | 6 +- src/atlas/grid/detail/grid/Gaussian.cc | 60 +- src/atlas/grid/detail/grid/Gaussian.h | 6 +- src/atlas/grid/detail/grid/Structured.h | 4 + src/atlas_f/grid/atlas_Grid_module.F90 | 24 + src/tests/grid/CMakeLists.txt | 1 + .../fctest_stretchedrotatedgaussiangrid.F90 | 935 ++++++++++++++++++ .../grid/test_stretchedrotatedgaussian.cc | 53 +- 9 files changed, 1027 insertions(+), 68 deletions(-) create mode 100644 src/tests/grid/fctest_stretchedrotatedgaussiangrid.F90 diff --git a/src/atlas/grid/StructuredGrid.cc b/src/atlas/grid/StructuredGrid.cc index ae4c26399..57ae502b7 100644 --- a/src/atlas/grid/StructuredGrid.cc +++ b/src/atlas/grid/StructuredGrid.cc @@ -54,6 +54,12 @@ ReducedGaussianGrid::ReducedGaussianGrid( const std::vector& nx, const Dom ReducedGaussianGrid::ReducedGaussianGrid( const std::vector& nx, const Domain& domain ) : ReducedGaussianGrid::grid_t( grid::detail::grid::reduced_gaussian( nx, domain ) ) {} +ReducedGaussianGrid::ReducedGaussianGrid( const std::vector& nx, const Projection& projection ) : + ReducedGaussianGrid::grid_t( grid::detail::grid::reduced_gaussian( nx, projection ) ) {} + +ReducedGaussianGrid::ReducedGaussianGrid( const std::vector& nx, const Projection& projection ) : + ReducedGaussianGrid::grid_t( grid::detail::grid::reduced_gaussian( nx, projection ) ) {} + ReducedGaussianGrid::ReducedGaussianGrid( const std::initializer_list& nx ) : ReducedGaussianGrid( std::vector( nx ) ) {} diff --git a/src/atlas/grid/StructuredGrid.h b/src/atlas/grid/StructuredGrid.h index 240d9ab38..43c0b0afe 100644 --- a/src/atlas/grid/StructuredGrid.h +++ b/src/atlas/grid/StructuredGrid.h @@ -167,9 +167,7 @@ class Gaussian : public Grid { idx_t N() const { return Grid::ny() / 2; } protected: - bool gaussian() const { - return Grid::domain().global() && Grid::yspace().type() == "gaussian"; - } + bool gaussian() const { return Grid::domain().global() && Grid::yspace().type() == "gaussian"; } }; //--------------------------------------------------------------------------------------------------------------------- @@ -199,6 +197,8 @@ class ReducedGaussianGrid : public Gaussian { ReducedGaussianGrid( const std::initializer_list& pl ); ReducedGaussianGrid( const std::vector& pl, const Domain& = Domain() ); ReducedGaussianGrid( const std::vector& pl, const Domain& = Domain() ); + ReducedGaussianGrid( const std::vector& pl, const Projection& ); + ReducedGaussianGrid( const std::vector& pl, const Projection& ); operator bool() const { return valid(); } diff --git a/src/atlas/grid/detail/grid/Gaussian.cc b/src/atlas/grid/detail/grid/Gaussian.cc index 3aee30ca9..1ed5d57f2 100644 --- a/src/atlas/grid/detail/grid/Gaussian.cc +++ b/src/atlas/grid/detail/grid/Gaussian.cc @@ -54,22 +54,9 @@ static Spacing yspace( const Grid::Config& grid ) { return Spacing( config ); } -static StructuredGrid::XSpace xspace( const std::vector& nx ) { +template +static StructuredGrid::XSpace xspace( const vector_t& nx ) { return StructuredGrid::XSpace( {0., 360.}, nx, false ); - // XSpace( const std::array& interval, const std::vector& N, - // bool endpoint=true ); - // - // _xspace->nx = nx; - // _xspace->nxmax = 0; - // _xspace->nxmin = std::numeric_limits::max(); - // for( size_t j=0; j<_xspace->ny; ++j ) { - // _xspace->xmin.push_back( 0. ); - // _xspace->xmax.push_back( 360. ); - // _xspace->dx.push_back( 360./_xspace->nx[j] ); - // _xspace->nxmin = std::min( _xspace->nxmin, size_t(_xspace->nx[j]) ); - // _xspace->nxmax = std::max( _xspace->nxmax, size_t(_xspace->nx[j]) ); - // } - // return _xspace; } //--------------------------------------------------------------------------------------------------------------------- @@ -209,8 +196,7 @@ StructuredGrid::grid_t* reduced_gaussian( const std::vector& nx ) { yspace.set( "end", -90.0 ); yspace.set( "N", nx.size() ); - std::vector _nx( nx.begin(), nx.end() ); - return new StructuredGrid::grid_t( xspace( _nx ), Spacing( yspace ), Projection(), Domain() ); + return new StructuredGrid::grid_t( xspace( nx ), Spacing( yspace ), Projection(), Domain() ); } StructuredGrid::grid_t* reduced_gaussian( const std::vector& nx, const Domain& domain ) { @@ -220,8 +206,17 @@ StructuredGrid::grid_t* reduced_gaussian( const std::vector& nx, const Dom yspace.set( "end", -90.0 ); yspace.set( "N", nx.size() ); - std::vector _nx( nx.begin(), nx.end() ); - return new StructuredGrid::grid_t( xspace( _nx ), Spacing( yspace ), Projection(), domain ); + return new StructuredGrid::grid_t( xspace( nx ), Spacing( yspace ), Projection(), domain ); +} + +StructuredGrid::grid_t* reduced_gaussian( const std::vector& nx, const Projection& projection ) { + Grid::Config yspace; + yspace.set( "type", "gaussian" ); + yspace.set( "start", 90.0 ); + yspace.set( "end", -90.0 ); + yspace.set( "N", nx.size() ); + + return new StructuredGrid::grid_t( xspace( nx ), Spacing( yspace ), projection, Domain() ); } StructuredGrid::grid_t* reduced_gaussian( const std::vector& nx ) { @@ -231,8 +226,7 @@ StructuredGrid::grid_t* reduced_gaussian( const std::vector& nx ) { yspace.set( "end", -90.0 ); yspace.set( "N", nx.size() ); - std::vector _nx( nx.begin(), nx.end() ); - return new StructuredGrid::grid_t( xspace( _nx ), Spacing( yspace ), Projection(), Domain() ); + return new StructuredGrid::grid_t( xspace( nx ), Spacing( yspace ), Projection(), Domain() ); } StructuredGrid::grid_t* reduced_gaussian( const std::vector& nx, const Domain& domain ) { @@ -242,10 +236,20 @@ StructuredGrid::grid_t* reduced_gaussian( const std::vector& nx, const Doma yspace.set( "end", -90.0 ); yspace.set( "N", nx.size() ); - std::vector _nx( nx.begin(), nx.end() ); - return new StructuredGrid::grid_t( xspace( _nx ), Spacing( yspace ), Projection(), domain ); + return new StructuredGrid::grid_t( xspace( nx ), Spacing( yspace ), Projection(), domain ); } +StructuredGrid::grid_t* reduced_gaussian( const std::vector& nx, const Projection& projection ) { + Grid::Config yspace; + yspace.set( "type", "gaussian" ); + yspace.set( "start", 90.0 ); + yspace.set( "end", -90.0 ); + yspace.set( "N", nx.size() ); + + return new StructuredGrid::grid_t( xspace( nx ), Spacing( yspace ), projection, Domain() ); +} + + template inline std::vector idx_vector( Int nx, idx_t ny ) { std::vector _nx( ny ); @@ -264,6 +268,16 @@ StructuredGrid::grid_t* atlas__grid__reduced__ReducedGaussian_int( int nx[], lon StructuredGrid::grid_t* atlas__grid__reduced__ReducedGaussian_long( long nx[], long ny ) { return reduced_gaussian( idx_vector( nx, ny ) ); } + +StructuredGrid::grid_t* atlas__grid__reduced__ReducedGaussian_int_projection( + int nx[], long ny, const Projection::Implementation* projection ) { + return reduced_gaussian( idx_vector( nx, ny ), Projection( projection ) ); +} + +StructuredGrid::grid_t* atlas__grid__reduced__ReducedGaussian_long_projection( + long nx[], long ny, const Projection::Implementation* projection ) { + return reduced_gaussian( idx_vector( nx, ny ), Projection( projection ) ); +} } } // namespace grid diff --git a/src/atlas/grid/detail/grid/Gaussian.h b/src/atlas/grid/detail/grid/Gaussian.h index d287b186d..6e5672f6d 100644 --- a/src/atlas/grid/detail/grid/Gaussian.h +++ b/src/atlas/grid/detail/grid/Gaussian.h @@ -18,10 +18,12 @@ namespace detail { namespace grid { StructuredGrid::grid_t* reduced_gaussian( const std::vector& nx ); -StructuredGrid::grid_t* reduced_gaussian( const std::vector& nx, const Domain& domain ); +StructuredGrid::grid_t* reduced_gaussian( const std::vector& nx, const Projection& ); +StructuredGrid::grid_t* reduced_gaussian( const std::vector& nx, const Domain& ); StructuredGrid::grid_t* reduced_gaussian( const std::vector& nx ); -StructuredGrid::grid_t* reduced_gaussian( const std::vector& nx, const Domain& domain ); +StructuredGrid::grid_t* reduced_gaussian( const std::vector& nx, const Projection& ); +StructuredGrid::grid_t* reduced_gaussian( const std::vector& nx, const Domain& ); } // namespace grid } // namespace detail diff --git a/src/atlas/grid/detail/grid/Structured.h b/src/atlas/grid/detail/grid/Structured.h index 18e16cd43..fdaf620d2 100644 --- a/src/atlas/grid/detail/grid/Structured.h +++ b/src/atlas/grid/detail/grid/Structured.h @@ -424,6 +424,10 @@ const Structured* atlas__grid__Structured__config( util::Config* conf ); Structured* atlas__grid__regular__RegularGaussian( long N ); Structured* atlas__grid__reduced__ReducedGaussian_int( int nx[], long ny ); Structured* atlas__grid__reduced__ReducedGaussian_long( long nx[], long ny ); +Structured* atlas__grid__reduced__ReducedGaussian_int_projection( int nx[], long ny, + const Projection::Implementation* projection ); +Structured* atlas__grid__reduced__ReducedGaussian_long_projection( long nx[], long ny, + const Projection::Implementation* projection ); Structured* atlas__grid__regular__RegularLonLat( long nx, long ny ); Structured* atlas__grid__regular__ShiftedLonLat( long nx, long ny ); Structured* atlas__grid__regular__ShiftedLon( long nx, long ny ); diff --git a/src/atlas_f/grid/atlas_Grid_module.F90 b/src/atlas_f/grid/atlas_Grid_module.F90 index 8ed4fda7e..f62276fbe 100644 --- a/src/atlas_f/grid/atlas_Grid_module.F90 +++ b/src/atlas_f/grid/atlas_Grid_module.F90 @@ -211,6 +211,8 @@ module atlas_Grid_module interface atlas_ReducedGaussianGrid module procedure atlas_ReducedGaussianGrid__ctor_int32 module procedure atlas_ReducedGaussianGrid__ctor_int64 + module procedure atlas_ReducedGaussianGrid__ctor_projection_int32 + module procedure atlas_ReducedGaussianGrid__ctor_projection_int64 end interface !------------------------------------------------------------------------------ @@ -534,6 +536,28 @@ function atlas_ReducedGaussianGrid__ctor_int64(nx) result(this) call this%return() end function +function atlas_ReducedGaussianGrid__ctor_projection_int32(nx, projection) result(this) + use, intrinsic :: iso_c_binding, only: c_int, c_long + use atlas_grid_Structured_c_binding + type(atlas_ReducedGaussianGrid) :: this + integer(c_int), intent(in) :: nx(:) + type(atlas_Projection), intent(in) :: projection + call this%reset_c_ptr( & + & atlas__grid__reduced__ReducedGaussian_int_projection( nx, int(size(nx),c_long), projection%CPTR_PGIBUG_A ) ) + call this%return() +end function + +function atlas_ReducedGaussianGrid__ctor_projection_int64(nx, projection) result(this) + use, intrinsic :: iso_c_binding, only: c_int, c_long + use atlas_grid_Structured_c_binding + type(atlas_ReducedGaussianGrid) :: this + integer(c_long), intent(in) :: nx(:) + type(atlas_Projection), intent(in) :: projection + call this%reset_c_ptr( & + & atlas__grid__reduced__ReducedGaussian_long_projection( nx, int(size(nx),c_long), projection%CPTR_PGIBUG_A ) ) + call this%return() +end function + !----------------------------------------------------------------------------- function atlas_grid_RegularLonLat__ctor_int32(nlon,nlat) result(this) diff --git a/src/tests/grid/CMakeLists.txt b/src/tests/grid/CMakeLists.txt index 6b702fa01..d8dd09afb 100644 --- a/src/tests/grid/CMakeLists.txt +++ b/src/tests/grid/CMakeLists.txt @@ -11,6 +11,7 @@ if( HAVE_FCTEST ) fctest_griddistribution fctest_grids fctest_regional_grids + fctest_stretchedrotatedgaussiangrid fctest_state ) add_fctest( TARGET atlas_${test} SOURCES ${test}.F90 LINKER_LANGUAGE Fortran LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) diff --git a/src/tests/grid/fctest_stretchedrotatedgaussiangrid.F90 b/src/tests/grid/fctest_stretchedrotatedgaussiangrid.F90 new file mode 100644 index 000000000..9ee020a2e --- /dev/null +++ b/src/tests/grid/fctest_stretchedrotatedgaussiangrid.F90 @@ -0,0 +1,935 @@ +! (C) Copyright 2013 ECMWF. +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +! In applying this licence, ECMWF does not waive the privileges and immunities +! granted to it by virtue of its status as an intergovernmental organisation nor +! does it submit to any jurisdiction. + +#include "fckit/fctest.h" + +! ----------------------------------------------------------------------------- + +TESTSUITE(fctest_stretchedrotatedgaussiangrid) + +! ----------------------------------------------------------------------------- + +TESTSUITE_INIT + use atlas_module + call atlas_library%initialise() +END_TESTSUITE_INIT + +! ----------------------------------------------------------------------------- + +TESTSUITE_FINALIZE + use atlas_module + call atlas_library%finalise() +END_TESTSUITE_FINALIZE + +! ---------------------------------------------------------------------------- + +TEST( arpege_t31c24 ) + ! Grid provided by Philippe Marguinaud, Meteo France + use atlas_module + implicit none + type(atlas_ReducedGaussianGrid) :: grid + + real(dp) :: lonlat(3376) + + integer, parameter :: nx(32) = & + [ 20, 27, 32, 40, 45, 48, 60, 60, 64, 64, 64, 64, 64, 64, 64, 64, & + 64, 64, 64, 64, 64, 64, 64, 64, 60, 60, 48, 45, 40, 32, 27, 20 ] + real(dp), parameter :: stretch = 2.4_dp + real(dp), parameter :: centre(2) = [2.0_dp, 46.7_dp] + real(dp), parameter :: angle = 180.0_dp + integer :: i, j, jglo + + lonlat( 1: 120) = [ & + & 2.00000000000000000_dp, 48.46708828700425187_dp, 1.17799068975270704_dp, 48.37772379915030996_dp, & + & 0.44418972240584109_dp, 48.11926090196744354_dp, -0.12512266986032569_dp, 47.71926346185548340_dp, & + & -0.47479391972813606_dp, 47.21956618120344018_dp, -0.57569351104372468_dp, 46.67109320702403608_dp, & + & -0.42528132740345748_dp, 46.12813070368466839_dp, -0.04494770792091035_dp, 45.64287023699112211_dp, & + & 0.52444137510688571_dp, 45.26073815098433784_dp, 1.22762737065264282_dp, 45.01674523281888440_dp, & + & 2.00000000000000000_dp, 44.93291171299576092_dp, 2.77237262934735629_dp, 45.01674523281888440_dp, & + & 3.47555862489311407_dp, 45.26073815098433784_dp, 4.04494770792090996_dp, 45.64287023699112211_dp, & + & 4.42528132740345725_dp, 46.12813070368466839_dp, 4.57569351104372490_dp, 46.67109320702403608_dp, & + & 4.47479391972813634_dp, 47.21956618120344018_dp, 4.12512266986032561_dp, 47.71926346185548340_dp, & + & 3.55581027759415846_dp, 48.11926090196744354_dp, 2.82200931024729318_dp, 48.37772379915030996_dp, & + & 2.00000000000000000_dp, 50.76274729016172671_dp, 0.52357145823104279_dp, 50.64425941434981837_dp, & + & -0.85309402263029532_dp, 50.29690558565773273_dp, -2.04123891610972796_dp, 49.74407157963349846_dp, & + & -2.97152383528954323_dp, 49.02183412771946536_dp, -3.59837298636155323_dp, 48.17535393470686955_dp, & + & -3.90029306374494222_dp, 47.25496955383284359_dp, -3.87730059701688790_dp, 46.31256836954069911_dp, & + & -3.54692456688646551_dp, 45.39855083314470363_dp, -2.93998927128814502_dp, 44.55946699558614910_dp, & + & -2.09688983996385936_dp, 43.83625800397906147_dp, -1.06462721424400342_dp, 43.26298044031561574_dp, & + & 0.10541754825032876_dp, 42.86589673132546352_dp, 1.35912400589531490_dp, 42.66284629701862485_dp, & + & 2.64087599410468910_dp, 42.66284629701862485_dp, 3.89458245174967033_dp, 42.86589673132546352_dp, & + & 5.06462721424400453_dp, 43.26298044031561574_dp, 6.09688983996385936_dp, 43.83625800397906147_dp, & + & 6.93998927128814458_dp, 44.55946699558614910_dp, 7.54692456688646551_dp, 45.39855083314470363_dp, & + & 7.87730059701688656_dp, 46.31256836954069911_dp, 7.90029306374494134_dp, 47.25496955383284359_dp, & + & 7.59837298636155190_dp, 48.17535393470686955_dp, 6.97152383528954367_dp, 49.02183412771946536_dp, & + & 6.04123891610972930_dp, 49.74407157963349846_dp, 4.85309402263029188_dp, 50.29690558565773273_dp, & + & 3.47642854176895577_dp, 50.64425941434981837_dp, 2.00000000000000089_dp, 53.08763788054714894_dp, & + & -0.06435502534210064_dp, 52.94800110445672203_dp, -2.01378910799337474_dp, 52.53707313311167582_dp, & + & -3.74622775989939782_dp, 51.87782553848720113_dp, -5.18197003975669812_dp, 51.00557820511744467_dp, & + & -6.26810804950859701_dp, 49.96440199105025926_dp, -6.97806564551988018_dp, 48.80336562297891589_dp, & + & -7.30784824781774223_dp, 47.57322688923063225_dp, -7.27090590027329053_dp, 46.32385288536406875_dp, & + & -6.89307303713296715_dp, 45.10238649522262477_dp, -6.20837670733706215_dp, 43.95202523375940729_dp, & + & -5.25594876339865014_dp, 42.91123363953519743_dp, -4.07794770476515911_dp, 42.01322983391511201_dp ] + lonlat( 121: 240) = [ & + & -2.71826031811739588_dp, 41.28563184510834105_dp, -1.22173788476928480_dp, 40.75019560382368411_dp, & + & 0.36623707967922525_dp, 40.42261301497919845_dp, 1.99999999999999933_dp, 40.31236211945285675_dp, & + & 3.63376292032077286_dp, 40.42261301497919845_dp, 5.22173788476928369_dp, 40.75019560382368411_dp, & + & 6.71826031811739455_dp, 41.28563184510834105_dp, 8.07794770476515822_dp, 42.01322983391511201_dp, & + & 9.25594876339864925_dp, 42.91123363953519743_dp, 10.20837670733705949_dp, 43.95202523375940729_dp, & + & 10.89307303713296804_dp, 45.10238649522262477_dp, 11.27090590027328965_dp, 46.32385288536406875_dp, & + & 11.30784824781774311_dp, 47.57322688923063225_dp, 10.97806564551988195_dp, 48.80336562297891589_dp, & + & 10.26810804950859790_dp, 49.96440199105025926_dp, 9.18197003975669901_dp, 51.00557820511744467_dp, & + & 7.74622775989939960_dp, 51.87782553848720113_dp, 6.01378910799337607_dp, 52.53707313311167582_dp, & + & 4.06435502534210169_dp, 52.94800110445672203_dp, 2.00000000000000089_dp, 55.44054043583248159_dp, & + & -0.39391037845720578_dp, 55.31115535850048559_dp, -2.68766853851381882_dp, 54.92865613654313961_dp, & + & -4.79116029542177202_dp, 54.30937174533021050_dp, -6.63193584333677855_dp, 53.47856301011842106_dp, & + & -8.15903975007321769_dp, 52.46802323745790630_dp, -9.34306219743110411_dp, 51.31353838046217675_dp, & + & -10.17346129570704782_dp, 50.05261748260089405_dp, -10.65454278958191026_dp, 48.72271255422421632_dp, & + & -10.80126342211737800_dp, 47.35996933207513848_dp, -10.63557388477568999_dp, 45.99843704779454612_dp, & + & -10.18359521918176824_dp, 44.66961909752426152_dp, -9.47363915243209931_dp, 43.40224634648851065_dp, & + & -8.53494002107633598_dp, 42.22217718966415845_dp, -7.39692139773956647_dp, 41.15235690381517486_dp, & + & -6.08882962579569487_dp, 40.21279467492543347_dp, -4.63959771096107954_dp, 39.42053684981195261_dp, & + & -3.07783882564465205_dp, 38.78962916423042628_dp, -1.43190111561111988_dp, 38.33106975566338548_dp, & + & 0.27005804914242593_dp, 38.05275970261258323_dp, 1.99999999999999956_dp, 37.95945956416753120_dp, & + & 3.72994195085757241_dp, 38.05275970261258323_dp, 5.43190111561111788_dp, 38.33106975566338548_dp, & + & 7.07783882564465028_dp, 38.78962916423042628_dp, 8.63959771096107865_dp, 39.42053684981195261_dp, & + & 10.08882962579569309_dp, 40.21279467492543347_dp, 11.39692139773956647_dp, 41.15235690381517486_dp, & + & 12.53494002107633243_dp, 42.22217718966415845_dp, 13.47363915243209576_dp, 43.40224634648851065_dp, & + & 14.18359521918176824_dp, 44.66961909752426152_dp, 14.63557388477568999_dp, 45.99843704779454612_dp, & + & 14.80126342211737445_dp, 47.35996933207513848_dp, 14.65454278958191381_dp, 48.72271255422421632_dp, & + & 14.17346129570704782_dp, 50.05261748260089405_dp, 13.34306219743110766_dp, 51.31353838046217675_dp, & + & 12.15903975007321769_dp, 52.46802323745790630_dp, 10.63193584333677855_dp, 53.47856301011842106_dp, & + & 8.79116029542177380_dp, 54.30937174533021050_dp, 6.68766853851382059_dp, 54.92865613654313961_dp, & + & 4.39391037845720867_dp, 55.31115535850048559_dp, 2.00000000000000222_dp, 57.82915094012270174_dp ] + lonlat( 241: 360) = [ & + & -0.88089731877775757_dp, 57.69078537163564846_dp, -3.64890062321652664_dp, 57.28147795233979167_dp, & + & -6.20331014929099656_dp, 56.61787273415653488_dp, -8.46463908845755597_dp, 55.72551502002679058_dp, & + & -10.37873026754043693_dp, 54.63620962052475960_dp, -11.91616209561276740_dp, 53.38530347804498888_dp, & + & -13.06845153476416321_dp, 52.00935252683229493_dp, -13.84288249522568925_dp, 50.54439040009851425_dp, & + & -14.25738892575702366_dp, 49.02480894996942595_dp, -14.33628744864260440_dp, 47.48274104170999976_dp, & + & -14.10711920596712332_dp, 45.94779768044404733_dp, -13.59853603019004176_dp, 44.44702279967973624_dp, & + & -12.83902541924944174_dp, 43.00496062724707258_dp, -11.85624296375186937_dp, 41.64376435793402464_dp, & + & -10.67674963753560036_dp, 40.38330297070848474_dp, -9.32599718132065902_dp, 39.24124360712294646_dp, & + & -7.82844927012504055_dp, 38.23310085983992224_dp, -6.20776260081896769_dp, 37.37225322309888043_dp, & + & -4.48697963071903061_dp, 36.66993230518373537_dp, -2.68870484500073204_dp, 36.13519327177002793_dp, & + & -0.83525097543211291_dp, 35.77487607444932394_dp, 1.05124791602986001_dp, 35.59356675116022473_dp, & + & 2.94875208397013866_dp, 35.59356675116022473_dp, 4.83525097543211047_dp, 35.77487607444932394_dp, & + & 6.68870484500073026_dp, 36.13519327177002793_dp, 8.48697963071903061_dp, 36.66993230518373537_dp, & + & 10.20776260081896680_dp, 37.37225322309888043_dp, 11.82844927012503966_dp, 38.23310085983992224_dp, & + & 13.32599718132065725_dp, 39.24124360712293225_dp, 14.67674963753560391_dp, 40.38330297070848474_dp, & + & 15.85624296375187114_dp, 41.64376435793402464_dp, 16.83902541924944174_dp, 43.00496062724707258_dp, & + & 17.59853603019003998_dp, 44.44702279967973624_dp, 18.10711920596712687_dp, 45.94779768044404733_dp, & + & 18.33628744864260796_dp, 47.48274104170998555_dp, 18.25738892575703076_dp, 49.02480894996942595_dp, & + & 17.84288249522569458_dp, 50.54439040009851425_dp, 17.06845153476416499_dp, 52.00935252683229493_dp, & + & 15.91616209561277273_dp, 53.38530347804498888_dp, 14.37873026754043870_dp, 54.63620962052475960_dp, & + & 12.46463908845756130_dp, 55.72551502002679058_dp, 10.20331014929099922_dp, 56.61787273415653488_dp, & + & 7.64890062321653019_dp, 57.28147795233979167_dp, 4.88089731877776067_dp, 57.69078537163564846_dp, & + & 2.00000000000000222_dp, 60.26295411645657651_dp, -1.52099396835736300_dp, 60.10440004572244277_dp, & + & -4.89637618755807136_dp, 59.63591573610307250_dp, -7.99912115224313958_dp, 58.87795079119066344_dp, & + & -10.73370655373201643_dp, 57.86135900031617751_dp, -13.04067793585433499_dp, 56.62367212326139310_dp, & + & -14.89384545876526111_dp, 55.20548791890712437_dp, -16.29319722451073105_dp, 53.64760968250063655_dp, & + & -17.25667322722224029_dp, 51.98914605534769606_dp, -17.81285996059914467_dp, 50.26647329190694080_dp, & + & -17.99546453290829007_dp, 48.51282952087984768_dp, -17.83961562141898938_dp, 46.75830290430587155_dp, & + & -17.37966075266355759_dp, 45.03002540480565585_dp, -16.64804001511417297_dp, 43.35244484209054860_dp, & + & -15.67486490585672243_dp, 41.74759899443786537_dp, -14.48792157318536411_dp, 40.23535159216241652_dp ] + lonlat( 361: 480) = [ & + & -13.11290455740948957_dp, 38.83357308646102979_dp, -11.57375501535206475_dp, 37.55826279697890158_dp, & + & -9.89302536639832653_dp, 36.42361680044041350_dp, -8.09222434325374884_dp, 35.44205010622848562_dp, & + & -6.19211732580419838_dp, 34.62418375193039566_dp, -4.21297051898843300_dp, 33.97880825794451454_dp, & + & -2.17473678976558649_dp, 33.51283479810019372_dp, -0.09718755711549092_dp, 33.23124463343540214_dp, & + & 1.99999999999999889_dp, 33.13704588354342917_dp, 4.09718755711548877_dp, 33.23124463343540214_dp, & + & 6.17473678976558382_dp, 33.51283479810019372_dp, 8.21297051898843122_dp, 33.97880825794451454_dp, & + & 10.19211732580419572_dp, 34.62418375193039566_dp, 12.09222434325374707_dp, 35.44205010622848562_dp, & + & 13.89302536639832830_dp, 36.42361680044041350_dp, 15.57375501535206297_dp, 37.55826279697889447_dp, & + & 17.11290455740948602_dp, 38.83357308646102268_dp, 18.48792157318536766_dp, 40.23535159216240942_dp, & + & 19.67486490585671888_dp, 41.74759899443786537_dp, 20.64804001511417653_dp, 43.35244484209054860_dp, & + & 21.37966075266355404_dp, 45.03002540480565585_dp, 21.83961562141898582_dp, 46.75830290430587155_dp, & + & 21.99546453290829007_dp, 48.51282952087984768_dp, 21.81285996059914822_dp, 50.26647329190694080_dp, & + & 21.25667322722224739_dp, 51.98914605534769606_dp, 20.29319722451073105_dp, 53.64760968250062234_dp, & + & 18.89384545876525934_dp, 55.20548791890712437_dp, 17.04067793585434032_dp, 56.62367212326139310_dp, & + & 14.73370655373202354_dp, 57.86135900031617751_dp, 11.99912115224314491_dp, 58.87795079119066344_dp, & + & 8.89637618755807580_dp, 59.63591573610307250_dp, 5.52099396835736744_dp, 60.10440004572244277_dp, & + & 2.00000000000000222_dp, 62.75242980718265784_dp, -1.60371952785031557_dp, 62.62270582496653049_dp, & + & -5.08984714396560278_dp, 62.23829052070698253_dp, -8.35401191716921510_dp, 61.61281120273996237_dp, & + & -11.31471471060360656_dp, 60.76704034614787986_dp, -13.91751559979281083_dp, 59.72658657388572578_dp, & + & -16.13405311103923978_dp, 58.51955862209560166_dp, -17.95764487214750815_dp, 57.17460858105235388_dp, & + & -19.39751805719553701_dp, 55.71953428872550518_dp, -20.47321926655622448_dp, 54.18043392376822709_dp, & + & -21.21002592824931199_dp, 52.58130404832083116_dp, -21.63559425749199150_dp, 50.94394459368972150_dp, & + & -21.77773936992417703_dp, 49.28804930270956675_dp, -21.66310777053267600_dp, 47.63139077670034283_dp, & + & -21.31648826424519072_dp, 45.99003977573201496_dp, -20.76054697060167342_dp, 44.37858250245571412_dp, & + & -20.01582559662803007_dp, 42.81031632956308641_dp, -19.10089088783405842_dp, 41.29741507659190347_dp, & + & -18.03256121225549791_dp, 39.85106125278488065_dp, -16.82616341777592339_dp, 38.48154618161254348_dp, & + & -15.49579145727241247_dp, 37.19834072287546434_dp, -14.05455017155123087_dp, 36.01014014726423795_dp, & + & -12.51477509868480809_dp, 34.92488704934376642_dp, -10.88822378743807739_dp, 33.94977626735131793_dp, & + & -9.18623692372254119_dp, 33.09124574576954103_dp, -7.41986936545087783_dp, 32.35495718584156322_dp, & + & -5.59999239238874669_dp, 31.74577019145995394_dp, -3.73736939611796615_dp, 31.26771342119434749_dp ] + lonlat( 481: 600) = [ & + & -1.84270801296844233_dp, 30.92395598105918353_dp, 0.07330759755401026_dp, 30.71678191817960268_dp, & + & 1.99999999999999889_dp, 30.64757019281734074_dp, 3.92669240244598639_dp, 30.71678191817960268_dp, & + & 5.84270801296843878_dp, 30.92395598105918353_dp, 7.73736939611796526_dp, 31.26771342119434749_dp, & + & 9.59999239238874225_dp, 31.74577019145995394_dp, 11.41986936545087516_dp, 32.35495718584156322_dp, & + & 13.18623692372254119_dp, 33.09124574576954103_dp, 14.88822378743807207_dp, 33.94977626735131793_dp, & + & 16.51477509868480809_dp, 34.92488704934376642_dp, 18.05455017155123087_dp, 36.01014014726423795_dp, & + & 19.49579145727241070_dp, 37.19834072287546434_dp, 20.82616341777592339_dp, 38.48154618161253637_dp, & + & 22.03256121225549791_dp, 39.85106125278488065_dp, 23.10089088783405842_dp, 41.29741507659190347_dp, & + & 24.01582559662803007_dp, 42.81031632956308641_dp, 24.76054697060167342_dp, 44.37858250245571412_dp, & + & 25.31648826424519072_dp, 45.99003977573201496_dp, 25.66310777053267600_dp, 47.63139077670033572_dp, & + & 25.77773936992417703_dp, 49.28804930270955964_dp, 25.63559425749199505_dp, 50.94394459368972150_dp, & + & 25.21002592824931199_dp, 52.58130404832083116_dp, 24.47321926655622448_dp, 54.18043392376822709_dp, & + & 23.39751805719553701_dp, 55.71953428872550518_dp, 21.95764487214750460_dp, 57.17460858105235388_dp, & + & 20.13405311103924333_dp, 58.51955862209560166_dp, 17.91751559979281438_dp, 59.72658657388572578_dp, & + & 15.31471471060361011_dp, 60.76704034614787986_dp, 12.35401191716921865_dp, 61.61281120273996237_dp, & + & 9.08984714396560634_dp, 62.23829052070698253_dp, 5.60371952785031979_dp, 62.62270582496653049_dp, & + & 2.00000000000000311_dp, 65.30899890463146562_dp, -2.55165837938126616_dp, 65.14506774788591770_dp, & + & -6.91821393310037980_dp, 64.66099291349783584_dp, -10.94248311853021427_dp, 63.87850997409253040_dp, & + & -14.51323316925390294_dp, 62.82970044453450953_dp, -17.56939664790982292_dp, 61.55243525845713748_dp, & + & -20.09325961331439458_dp, 60.08625295311978931_dp, -22.09844461997003506_dp, 58.46939890910893922_dp, & + & -23.61769060836897438_dp, 56.73712764036461920_dp, -24.69307609487012556_dp, 54.92100894435518654_dp, & + & -25.36933297612858951_dp, 53.04888396821036167_dp, -25.68983696232584535_dp, 51.14517010976478417_dp, & + & -25.69453569648834801_dp, 49.23130925676766623_dp, -25.41912613625677153_dp, 47.32623888788154431_dp, & + & -24.89496528133907205_dp, 45.44682484300304282_dp, -24.14937074714975296_dp, 43.60823042135866956_dp, & + & -23.20610004359021517_dp, 41.82421587722975431_dp, -22.08588698105332782_dp, 40.10737174655498194_dp, & + & -20.80696969977402944_dp, 38.46929323359992736_dp, -19.38557795842460507_dp, 36.92070387379718710_dp, & + & -17.83636590786354148_dp, 35.47153642700349963_dp, -16.17278641740254130_dp, 34.13097826525054046_dp, & + & -14.40740784814351017_dp, 32.90748779465315721_dp, -12.55217628249286932_dp, 31.80878785141789677_dp, & + & -10.61862699669389443_dp, 30.84184157026976436_dp, -8.61804920168179045_dp, 30.01281590885307438_dp, & + & -6.56160821625925728_dp, 29.32703776203888069_dp, -4.46042949045145676_dp, 28.78894734383437992_dp ] + lonlat( 601: 720) = [ & + & -2.32564933647368921_dp, 28.40205318377053345_dp, -0.16843782661930862_dp, 28.16889262543881500_dp, & + & 1.99999999999999889_dp, 28.09100109536852585_dp, 4.16843782661930629_dp, 28.16889262543881500_dp, & + & 6.32564933647368743_dp, 28.40205318377053345_dp, 8.46042949045145498_dp, 28.78894734383437992_dp, & + & 10.56160821625925372_dp, 29.32703776203888069_dp, 12.61804920168178867_dp, 30.01281590885307438_dp, & + & 14.61862699669389265_dp, 30.84184157026976436_dp, 16.55217628249286577_dp, 31.80878785141789677_dp, & + & 18.40740784814351017_dp, 32.90748779465315721_dp, 20.17278641740253775_dp, 34.13097826525054046_dp, & + & 21.83636590786353793_dp, 35.47153642700349963_dp, 23.38557795842460152_dp, 36.92070387379718710_dp, & + & 24.80696969977402588_dp, 38.46929323359992026_dp, 26.08588698105332426_dp, 40.10737174655498194_dp, & + & 27.20610004359021161_dp, 41.82421587722975431_dp, 28.14937074714974941_dp, 43.60823042135866956_dp, & + & 28.89496528133906494_dp, 45.44682484300304282_dp, 29.41912613625676798_dp, 47.32623888788153721_dp, & + & 29.69453569648834446_dp, 49.23130925676766623_dp, 29.68983696232584180_dp, 51.14517010976478417_dp, & + & 29.36933297612858951_dp, 53.04888396821034746_dp, 28.69307609487012911_dp, 54.92100894435518654_dp, & + & 27.61769060836897438_dp, 56.73712764036461920_dp, 26.09844461997003506_dp, 58.46939890910893922_dp, & + & 24.09325961331439814_dp, 60.08625295311978931_dp, 21.56939664790982292_dp, 61.55243525845713748_dp, & + & 18.51323316925390827_dp, 62.82970044453450953_dp, 14.94248311853021960_dp, 63.87850997409253040_dp, & + & 10.91821393310038779_dp, 64.66099291349783584_dp, 6.55165837938127193_dp, 65.14506774788591770_dp, & + & 2.00000000000000400_dp, 67.94514236453578349_dp, -3.38533547682125890_dp, 67.76325974975715383_dp, & + & -8.52326664933801226_dp, 67.22759281764989225_dp, -13.21138564172178143_dp, 66.36584339331807314_dp, & + & -17.31841683118042496_dp, 65.21781824976642383_dp, -20.78586461017365750_dp, 63.82887077001763032_dp, & + & -23.61278608277912383_dp, 62.24449065808524040_dp, -25.83517004248913196_dp, 60.50690475312734407_dp, & + & -27.50782877801382043_dp, 58.65352526302899605_dp, -28.69170289014789077_dp, 56.71665696607985296_dp, & + & -29.44632120220701665_dp, 54.72388646563504011_dp, -29.82602201650912477_dp, 52.69875031809839783_dp, & + & -29.87852862440772839_dp, 50.66145188613276673_dp, -29.64483525740958925_dp, 48.62951713112133945_dp, & + & -29.15973753507462618_dp, 46.61834908938176625_dp, -28.45262246278107554_dp, 44.64167567492752653_dp, & + & -27.54831327014242959_dp, 42.71190039889389567_dp, -26.46787020033523774_dp, 40.84037031204417190_dp, & + & -25.22930648122764552_dp, 39.03757551632936185_dp, -23.84820859564655038_dp, 37.31329283883029291_dp, & + & -22.33826403031283903_dp, 35.67668409468497970_dp, -20.71170528699355629_dp, 34.13635737784334623_dp, & + & -18.97968026705781242_dp, 32.70039821822412307_dp, -17.15255849246134900_dp, 31.37637626233231813_dp, & + & -15.24018123567453742_dp, 30.17133232687284305_dp, -13.25206214192900944_dp, 29.09175016726920759_dp, & + & -11.19754366142445079_dp, 28.14351701208745027_dp, -9.08591371200913400_dp, 27.33187675649334381_dp ] + lonlat( 721: 840) = [ & + & -6.92648650789847675_dp, 26.66137960320710931_dp, -4.72865139696445702_dp, 26.13583181560235502_dp, & + & -2.50189378344492486_dp, 25.75824904385734371_dp, -0.25579267831605296_dp, 25.53081635683327733_dp, & + & 1.99999999999999889_dp, 25.45485763546421154_dp, 4.25579267831605002_dp, 25.53081635683327733_dp, & + & 6.50189378344492308_dp, 25.75824904385734371_dp, 8.72865139696445347_dp, 26.13583181560235502_dp, & + & 10.92648650789847764_dp, 26.66137960320710931_dp, 13.08591371200913400_dp, 27.33187675649334381_dp, & + & 15.19754366142444724_dp, 28.14351701208745027_dp, 17.25206214192900589_dp, 29.09175016726920759_dp, & + & 19.24018123567453742_dp, 30.17133232687284305_dp, 21.15255849246134545_dp, 31.37637626233231813_dp, & + & 22.97968026705781242_dp, 32.70039821822412307_dp, 24.71170528699354918_dp, 34.13635737784334623_dp, & + & 26.33826403031282837_dp, 35.67668409468497259_dp, 27.84820859564654683_dp, 37.31329283883029291_dp, & + & 29.22930648122764197_dp, 39.03757551632935474_dp, 30.46787020033523419_dp, 40.84037031204417190_dp, & + & 31.54831327014242248_dp, 42.71190039889389567_dp, 32.45262246278107199_dp, 44.64167567492752653_dp, & + & 33.15973753507462618_dp, 46.61834908938176625_dp, 33.64483525740958214_dp, 48.62951713112133945_dp, & + & 33.87852862440772839_dp, 50.66145188613276673_dp, 33.82602201650912122_dp, 52.69875031809839783_dp, & + & 33.44632120220701665_dp, 54.72388646563502590_dp, 32.69170289014788011_dp, 56.71665696607985296_dp, & + & 31.50782877801381687_dp, 58.65352526302899605_dp, 29.83517004248913906_dp, 60.50690475312734407_dp, & + & 27.61278608277913094_dp, 62.24449065808524040_dp, 24.78586461017366460_dp, 63.82887077001763032_dp, & + & 21.31841683118043562_dp, 65.21781824976642383_dp, 17.21138564172179031_dp, 66.36584339331807314_dp, & + & 12.52326664933801936_dp, 67.22759281764989225_dp, 7.38533547682126645_dp, 67.76325974975715383_dp, & + & 2.00000000000000488_dp, 70.67459026401789401_dp, -4.83336207267744200_dp, 70.44359392189112157_dp, & + & -11.25145387811900299_dp, 69.76794994271870110_dp, -16.94303945184942606_dp, 68.69426490502790728_dp, & + & -21.74696599568290978_dp, 67.28571148577093197_dp, -25.63456841105837825_dp, 65.60910965714572285_dp, & + & -28.66225471925319823_dp, 63.72633555655123416_dp, -30.92626834358740595_dp, 61.69055635034530383_dp, & + & -32.53242307522426557_dp, 59.54571651024583190_dp, -33.58017141013023377_dp, 57.32758829402004608_dp, & + & -34.15616674923442986_dp, 55.06530104282313687_dp, -34.33296113360040636_dp, 52.78282385843581892_dp, & + & -34.17004600591189956_dp, 50.50021197832979425_dp, -33.71572773969587900_dp, 48.23458650173874673_dp, & + & -33.00911969333094476_dp, 46.00087796034757304_dp, -32.08195470333168231_dp, 43.81237943576640248_dp, & + & -30.96012666362175025_dp, 41.68115224019865650_dp, -29.66495985223924237_dp, 39.61831917500947498_dp, & + & -28.21423852322426740_dp, 37.63427208010365632_dp, -26.62303747247054986_dp, 35.73881343521247089_dp, & + & -24.90439178940930276_dp, 33.94124647006630369_dp, -23.06983782140640216_dp, 32.25042441190934994_dp, & + & -21.12985059268007149_dp, 30.67476685924403057_dp, -19.09419678103115459_dp, 29.22224954465700009_dp ] + lonlat( 841: 960) = [ & + & -16.97221728412494457_dp, 27.90037270541385794_dp, -14.77304947679039238_dp, 26.71611272875198750_dp, & + & -12.50579641562138100_dp, 25.67586152120415832_dp, -10.17964838758363832_dp, 24.78535803035031648_dp, & + & -7.80396120930039583_dp, 24.04961640006232315_dp, -5.38829543151647705_dp, 23.47285525677743223_dp, & + & -2.94242093509331726_dp, 23.05843251151437201_dp, -0.47629214130109782_dp, 22.80878975038074685_dp, & + & 1.99999999999999800_dp, 22.72540973598210812_dp, 4.47629214130109521_dp, 22.80878975038074685_dp, & + & 6.94242093509331326_dp, 23.05843251151437201_dp, 9.38829543151647350_dp, 23.47285525677743223_dp, & + & 11.80396120930039316_dp, 24.04961640006232315_dp, 14.17964838758363655_dp, 24.78535803035031648_dp, & + & 16.50579641562137567_dp, 25.67586152120415832_dp, 18.77304947679039060_dp, 26.71611272875198750_dp, & + & 20.97221728412493746_dp, 27.90037270541385439_dp, 23.09419678103115103_dp, 29.22224954465700009_dp, & + & 25.12985059268006793_dp, 30.67476685924402702_dp, 27.06983782140639860_dp, 32.25042441190934994_dp, & + & 28.90439178940930631_dp, 33.94124647006630369_dp, 30.62303747247054275_dp, 35.73881343521246379_dp, & + & 32.21423852322426029_dp, 37.63427208010365632_dp, 33.66495985223924237_dp, 39.61831917500946787_dp, & + & 34.96012666362174315_dp, 41.68115224019865650_dp, 36.08195470333167520_dp, 43.81237943576640248_dp, & + & 37.00911969333093765_dp, 46.00087796034757304_dp, 37.71572773969587189_dp, 48.23458650173873252_dp, & + & 38.17004600591188535_dp, 50.50021197832979425_dp, 38.33296113360039925_dp, 52.78282385843581892_dp, & + & 38.15616674923441565_dp, 55.06530104282313687_dp, 37.58017141013023377_dp, 57.32758829402004608_dp, & + & 36.53242307522427268_dp, 59.54571651024581769_dp, 34.92626834358741661_dp, 61.69055635034530383_dp, & + & 32.66225471925319823_dp, 63.72633555655123416_dp, 29.63456841105838535_dp, 65.60910965714572285_dp, & + & 25.74696599568291333_dp, 67.28571148577093197_dp, 20.94303945184943316_dp, 68.69426490502790728_dp, & + & 15.25145387811901188_dp, 69.76794994271870110_dp, 8.83336207267745088_dp, 70.44359392189112157_dp, & + & 2.00000000000000622_dp, 73.51255823132638056_dp, -6.80643587234800940_dp, 73.21444539538327945_dp, & + & -14.87413258170036379_dp, 72.35214877025040892_dp, -21.72462936947633594_dp, 71.00751554077180572_dp, & + & -27.20553396293055215_dp, 69.28213523998356038_dp, -31.39257932570032139_dp, 67.27265449907143591_dp, & + & -34.46283411458643542_dp, 65.05952649321666570_dp, -36.61111913565843423_dp, 62.70556553816149403_dp, & + & -38.01204327449661236_dp, 60.25870541573158334_dp, -38.80901819915838047_dp, 57.75559962131574565_dp, & + & -39.11518954384546731_dp, 55.22475594928482678_dp, -39.01828312042780311_dp, 52.68892136253921876_dp, & + & -38.58594887503603132_dp, 50.16679739569313057_dp, -37.87042151784342536_dp, 47.67424596736638875_dp, & + & -36.91224672041000332_dp, 45.22513042719018728_dp, -35.74315247327934486_dp, 42.83190113242096686_dp, & + & -34.38823113470312620_dp, 40.50600232791481403_dp, -32.86759479425492714_dp, 38.25815259440047811_dp, & + & -31.19763777135731431_dp, 36.09853399317181299_dp, -29.39200856069424006_dp, 34.03691345955112979_dp ] + lonlat( 961: 1080) = [ & + & -27.46236656888655148_dp, 32.08271233019573287_dp, -25.41897786066869003_dp, 30.24503490367625247_dp, & + & -23.27118821325056430_dp, 28.53266378578365448_dp, -21.02780000980340347_dp, 26.95402789495273055_dp, & + & -18.69737092792362887_dp, 25.51714801636046559_dp, -16.28844626261447814_dp, 24.22956442487017625_dp, & + & -13.80973254683687124_dp, 23.09825113914620331_dp, -11.27021753707410845_dp, 22.12952164603582617_dp, & + & -8.67924035399200910_dp, 21.32893128539883065_dp, -6.04651538477557082_dp, 20.70118176192879744_dp, & + & -3.38211423303386649_dp, 20.25003331964319386_dp, -0.69641128035122979_dp, 19.97822987242789239_dp, & + & 1.99999999999999756_dp, 19.88744176867360025_dp, 4.69641128035122613_dp, 19.97822987242789239_dp, & + & 7.38211423303386205_dp, 20.25003331964319386_dp, 10.04651538477556727_dp, 20.70118176192879744_dp, & + & 12.67924035399200378_dp, 21.32893128539883065_dp, 15.27021753707410667_dp, 22.12952164603582617_dp, & + & 17.80973254683686946_dp, 23.09825113914620331_dp, 20.28844626261447814_dp, 24.22956442487017270_dp, & + & 22.69737092792362532_dp, 25.51714801636046559_dp, 25.02780000980339992_dp, 26.95402789495272700_dp, & + & 27.27118821325056786_dp, 28.53266378578365448_dp, 29.41897786066868292_dp, 30.24503490367624536_dp, & + & 31.46236656888655503_dp, 32.08271233019573287_dp, 33.39200856069423651_dp, 34.03691345955112979_dp, & + & 35.19763777135730720_dp, 36.09853399317180589_dp, 36.86759479425493424_dp, 38.25815259440047811_dp, & + & 38.38823113470313331_dp, 40.50600232791481403_dp, 39.74315247327934486_dp, 42.83190113242095975_dp, & + & 40.91224672041000332_dp, 45.22513042719018728_dp, 41.87042151784342536_dp, 47.67424596736638165_dp, & + & 42.58594887503603132_dp, 50.16679739569313057_dp, 43.01828312042781022_dp, 52.68892136253921876_dp, & + & 43.11518954384546731_dp, 55.22475594928482678_dp, 42.80901819915838757_dp, 57.75559962131573144_dp, & + & 42.01204327449661946_dp, 60.25870541573157624_dp, 40.61111913565844134_dp, 62.70556553816149403_dp, & + & 38.46283411458643542_dp, 65.05952649321666570_dp, 35.39257932570031784_dp, 67.27265449907143591_dp, & + & 31.20553396293055926_dp, 69.28213523998356038_dp, 25.72462936947633949_dp, 71.00751554077180572_dp, & + & 18.87413258170037267_dp, 72.35214877025040892_dp, 10.80643587234802361_dp, 73.21444539538327945_dp, & + & 2.00000000000000844_dp, 76.47602837656471308_dp, -9.67376613190775814_dp, 76.07990221077976400_dp, & + & -19.91726594386999949_dp, 74.95625177955801632_dp, -28.02735732207738550_dp, 73.25702648451857613_dp, & + & -34.02194152863378918_dp, 71.14627956201684356_dp, -38.25028545833758642_dp, 68.75766348420627594_dp, & + & -41.10709162054251209_dp, 66.18773615332521842_dp, -42.92334661203305757_dp, 63.50367340927638793_dp, & + & -43.94927569193657035_dp, 60.75248226909388904_dp, -44.36709840828364548_dp, 57.96800639328736793_dp, & + & -44.30826862884845241_dp, 55.17560336970880286_dp, -43.86791757533286074_dp, 52.39514197266055362_dp, & + & -43.11549450115907689_dp, 49.64291142290011294_dp, -42.10226597817306526_dp, 46.93284831506371546_dp, & + & -40.86653894639825779_dp, 44.27733603455973110_dp, -39.43731045663169965_dp, 41.68773239540036002_dp ] + lonlat( 1081: 1200) = [ & + & -37.83684688715485578_dp, 39.17472027603687934_dp, -36.08253581911941410_dp, 36.74853923728758076_dp, & + & -34.18824090805679816_dp, 34.41913389699746517_dp, -32.16531367916817885_dp, 32.19624133969532664_dp, & + & -30.02336509370288198_dp, 30.08943159705432890_dp, -27.77086550998262382_dp, 28.10811023218007776_dp, & + & -25.41561850057673411_dp, 26.26148912292403992_dp, -22.96513811238918379_dp, 24.55852997196868515_dp, & + & -20.42694818201796281_dp, 23.00786444863284785_dp, -17.80881477377849720_dp, 21.61769490666709714_dp, & + & -15.11891780975726896_dp, 20.39568010883170501_dp, -12.36596498789072740_dp, 19.34881113120014717_dp, & + & -9.55924980839402672_dp, 18.48328342761256593_dp, -6.70865569654629645_dp, 17.80437171431731613_dp, & + & -3.82460955068694286_dp, 17.31631470128239414_dp, -0.91799021933643277_dp, 17.02221659443388191_dp, & + & 1.99999999999999756_dp, 16.92397162343524286_dp, 4.91799021933642866_dp, 17.02221659443388191_dp, & + & 7.82460955068693664_dp, 17.31631470128239414_dp, 10.70865569654629290_dp, 17.80437171431731613_dp, & + & 13.55924980839402139_dp, 18.48328342761256593_dp, 16.36596498789072740_dp, 19.34881113120014717_dp, & + & 19.11891780975726363_dp, 20.39568010883170501_dp, 21.80881477377850075_dp, 21.61769490666709714_dp, & + & 24.42694818201795925_dp, 23.00786444863284430_dp, 26.96513811238918024_dp, 24.55852997196867804_dp, & + & 29.41561850057672345_dp, 26.26148912292403637_dp, 31.77086550998261671_dp, 28.10811023218007065_dp, & + & 34.02336509370287132_dp, 30.08943159705432180_dp, 36.16531367916817885_dp, 32.19624133969532664_dp, & + & 38.18824090805679106_dp, 34.41913389699745807_dp, 40.08253581911941410_dp, 36.74853923728758076_dp, & + & 41.83684688715484867_dp, 39.17472027603687934_dp, 43.43731045663169255_dp, 41.68773239540036002_dp, & + & 44.86653894639826490_dp, 44.27733603455973110_dp, 46.10226597817306526_dp, 46.93284831506371546_dp, & + & 47.11549450115907689_dp, 49.64291142290011294_dp, 47.86791757533286784_dp, 52.39514197266055362_dp, & + & 48.30826862884845951_dp, 55.17560336970880286_dp, 48.36709840828365259_dp, 57.96800639328736793_dp, & + & 47.94927569193657746_dp, 60.75248226909387483_dp, 46.92334661203305757_dp, 63.50367340927638793_dp, & + & 45.10709162054252630_dp, 66.18773615332521842_dp, 42.25028545833760063_dp, 68.75766348420627594_dp, & + & 38.02194152863379628_dp, 71.14627956201684356_dp, 32.02735732207738550_dp, 73.25702648451857613_dp, & + & 23.91726594387000659_dp, 74.95625177955801632_dp, 13.67376613190777590_dp, 76.07990221077976400_dp, & + & 2.00000000000001155_dp, 79.58407807962322522_dp, -14.24038894313819092_dp, 79.03039199474196153_dp, & + & -27.34759503822197502_dp, 77.51867325981696411_dp, -36.54693285975560713_dp, 75.34972495190746145_dp, & + & -42.57629367046713753_dp, 72.78039612779681988_dp, -46.37308749001456221_dp, 69.97672254379963874_dp, & + & -48.64383991815974895_dp, 67.03853932314146391_dp, -49.85773173662808233_dp, 64.02688869849654907_dp, & + & -50.31863539857851464_dp, 60.98111330635318694_dp, -50.22511448054331140_dp, 57.92830381592441569_dp, & + & -49.70950811496560107_dp, 54.88845672164920586_dp, -48.86197695521289575_dp, 51.87735585154322138_dp ] + lonlat( 1201: 1320) = [ & + & -47.74524369263553325_dp, 48.90823812634897649_dp, -46.40377244059971673_dp, 45.99279145534946167_dp, & + & -44.86961926381133026_dp, 43.14177213432515856_dp, -43.16626004327478228_dp, 40.36539642075791789_dp, & + & -41.31116510791174079_dp, 37.67359181604234664_dp, -39.31758136998610098_dp, 35.07615645611910082_dp, & + & -37.19580324741198041_dp, 32.58285444844916867_dp, -34.95410730493897233_dp, 30.20346328869343466_dp, & + & -32.59946098845734497_dp, 27.94778271439298933_dp, -30.13807554901531915_dp, 25.82561045671564770_dp, & + & -27.57584735879238025_dp, 23.84668824688734645_dp, -24.91871466466540852_dp, 22.02062053097340311_dp, & + & -22.17294518233925871_dp, 20.35676830949395466_dp, -19.34536199373425802_dp, 18.86412113035556004_dp, & + & -16.44350999615492270_dp, 17.55115134744557182_dp, -13.47576217013061850_dp, 16.42565612762181360_dp, & + & -10.45136393475438830_dp, 15.49459412897927280_dp, -7.38041466390653600_dp, 14.76392503362292707_dp, & + & -4.27378779825515664_dp, 14.23846093632396936_dp, -1.14299451095196525_dp, 13.92173872937387458_dp, & + & 1.99999999999999756_dp, 13.81592192037676980_dp, 5.14299451095195970_dp, 13.92173872937387458_dp, & + & 8.27378779825515132_dp, 14.23846093632396936_dp, 11.38041466390652978_dp, 14.76392503362292707_dp, & + & 14.45136393475438119_dp, 15.49459412897927280_dp, 17.47576217013061495_dp, 16.42565612762181360_dp, & + & 20.44350999615491560_dp, 17.55115134744557182_dp, 23.34536199373425447_dp, 18.86412113035556004_dp, & + & 26.17294518233925515_dp, 20.35676830949395466_dp, 28.91871466466540852_dp, 22.02062053097340311_dp, & + & 31.57584735879237314_dp, 23.84668824688734645_dp, 34.13807554901531205_dp, 25.82561045671564415_dp, & + & 36.59946098845733786_dp, 27.94778271439298933_dp, 38.95410730493897233_dp, 30.20346328869342756_dp, & + & 41.19580324741196620_dp, 32.58285444844916867_dp, 43.31758136998610098_dp, 35.07615645611910082_dp, & + & 45.31116510791174079_dp, 37.67359181604234664_dp, 47.16626004327477517_dp, 40.36539642075791789_dp, & + & 48.86961926381131605_dp, 43.14177213432515856_dp, 50.40377244059970963_dp, 45.99279145534945457_dp, & + & 51.74524369263552614_dp, 48.90823812634896228_dp, 52.86197695521290285_dp, 51.87735585154322138_dp, & + & 53.70950811496559396_dp, 54.88845672164920586_dp, 54.22511448054331140_dp, 57.92830381592441569_dp, & + & 54.31863539857851464_dp, 60.98111330635318694_dp, 53.85773173662808233_dp, 64.02688869849654907_dp, & + & 52.64383991815974895_dp, 67.03853932314146391_dp, 50.37308749001457642_dp, 69.97672254379963874_dp, & + & 46.57629367046714464_dp, 72.78039612779681988_dp, 40.54693285975561423_dp, 75.34972495190746145_dp, & + & 31.34759503822198212_dp, 77.51867325981696411_dp, 18.24038894313820691_dp, 79.03039199474196153_dp, & + & 2.00000000000001998_dp, 82.85826214470431239_dp, -22.58690897303193523_dp, 82.01033616765489853_dp, & + & -38.96575654499159924_dp, 79.88802174923017674_dp, -48.17735288037921038_dp, 77.11452529323827321_dp, & + & -53.18593275925196195_dp, 74.03707888623914357_dp, -55.81801935702030448_dp, 70.81486693953937106_dp, & + & -57.03453256694833584_dp, 67.52503657439315532_dp, -57.35114187047393841_dp, 64.20957103321012482_dp ] + lonlat( 1321: 1440) = [ & + & -57.05957379451162126_dp, 60.89430451319173443_dp, -56.33378767939932885_dp, 57.59709162031844443_dp, & + & -55.28230228211860009_dp, 54.33161333463023368_dp, -53.97533567047437231_dp, 51.10929358481432416_dp, & + & -52.45964870701570248_dp, 47.94033352757975308_dp, -50.76707508825830928_dp, 44.83430401955754974_dp, & + & -48.91964996438991875_dp, 41.80050199367043007_dp, -46.93282539675249154_dp, 38.84817223653459450_dp, & + & -44.81756887288366897_dp, 35.98664677769215103_dp, -42.58178882927894193_dp, 33.22542941501406233_dp, & + & -40.23134383513492196_dp, 30.57423988556289629_dp, -37.77078829775713587_dp, 28.04302503644353806_dp, & + & -35.20394762734520100_dp, 25.64194033091114022_dp, -32.53437968696509586_dp, 23.38130282889364508_dp, & + & -29.76575655354334771_dp, 21.27151577227776258_dp, -26.90218550361062810_dp, 19.32296477964254322_dp, & + & -23.94847769357362566_dp, 17.54588626406437513_dp, -20.91036566952359976_dp, 15.95020993686824795_dp, & + & -17.79466590782826074_dp, 14.54537904344352306_dp, -14.60937983520193661_dp, 13.34015413091371371_dp, & + & -11.36372621835939434_dp, 12.34240842556757833_dp, -8.06809948196382720_dp, 11.55892497220584580_dp, & + & -4.73395228112668587_dp, 10.99520717814031734_dp, -1.37360609917944543_dp, 10.65531494200142504_dp, & + & 1.99999999999999756_dp, 10.54173785529561336_dp, 5.37360609917944032_dp, 10.65531494200142504_dp, & + & 8.73395228112667965_dp, 10.99520717814031734_dp, 12.06809948196382187_dp, 11.55892497220584580_dp, & + & 15.36372621835939078_dp, 12.34240842556757833_dp, 18.60937983520193484_dp, 13.34015413091371371_dp, & + & 21.79466590782825364_dp, 14.54537904344352306_dp, 24.91036566952359621_dp, 15.95020993686824795_dp, & + & 27.94847769357362210_dp, 17.54588626406437513_dp, 30.90218550361061745_dp, 19.32296477964254322_dp, & + & 33.76575655354334060_dp, 21.27151577227776258_dp, 36.53437968696508875_dp, 23.38130282889363798_dp, & + & 39.20394762734519389_dp, 25.64194033091114022_dp, 41.77078829775712165_dp, 28.04302503644353095_dp, & + & 44.23134383513491485_dp, 30.57423988556289629_dp, 46.58178882927893483_dp, 33.22542941501406233_dp, & + & 48.81756887288366187_dp, 35.98664677769215103_dp, 50.93282539675248444_dp, 38.84817223653458029_dp, & + & 52.91964996438991164_dp, 41.80050199367041586_dp, 54.76707508825830928_dp, 44.83430401955754263_dp, & + & 56.45964870701569538_dp, 47.94033352757973887_dp, 57.97533567047437231_dp, 51.10929358481431706_dp, & + & 59.28230228211859298_dp, 54.33161333463023368_dp, 60.33378767939932885_dp, 57.59709162031844443_dp, & + & 61.05957379451161415_dp, 60.89430451319173443_dp, 61.35114187047394552_dp, 64.20957103321012482_dp, & + & 61.03453256694833584_dp, 67.52503657439315532_dp, 59.81801935702030448_dp, 70.81486693953937106_dp, & + & 57.18593275925196906_dp, 74.03707888623914357_dp, 52.17735288037921038_dp, 77.11452529323827321_dp, & + & 42.96575654499162056_dp, 79.88802174923017674_dp, 26.58690897303196721_dp, 82.01033616765489853_dp, & + & 2.00000000000004352_dp, 86.32305404620944955_dp, -41.28583165458979920_dp, 84.76914638163376026_dp, & + & -57.58131858106001744_dp, 81.70467639663135628_dp, -63.56415050352948981_dp, 78.26774990878784877_dp ] + lonlat( 1441: 1560) = [ & + & -65.82714145548160900_dp, 74.71983727667711150_dp, -66.41516373728856593_dp, 71.13755089586129543_dp, & + & -66.11115128919476547_dp, 67.55274433031233627_dp, -65.27043361461275595_dp, 63.98289851135272244_dp, & + & -64.07470870335011170_dp, 60.43992354246546483_dp, -62.62500642980751309_dp, 56.93332642202356197_dp, & + & -60.98089250042357889_dp, 53.47155025248752480_dp, -59.17889520468775544_dp, 50.06261728049957327_dp, & + & -57.24193904387428233_dp, 46.71447239476329827_dp, -55.18452370168171939_dp, 43.43518400478754415_dp, & + & -53.01574257648425004_dp, 40.23307020728445593_dp, -50.74114071516557800_dp, 37.11678138456306897_dp, & + & -48.36392121341754802_dp, 34.09535373993877982_dp, -45.88577372237665486_dp, 31.17824007995576707_dp, & + & -43.30747895739605013_dp, 28.37531978094453677_dp, -40.62937881000767959_dp, 25.69688738457191945_dp, & + & -37.85176517580522670_dp, 23.15361781597290758_dp, -34.97521854780547557_dp, 20.75650546647104377_dp, & + & -32.00091302933286386_dp, 18.51677422677156670_dp, -28.93089420696503922_dp, 16.44575600838861718_dp, & + & -25.76832854644280602_dp, 14.55473640862136619_dp, -22.51771689758312078_dp, 12.85476800444173051_dp, & + & -19.18506021909563941_dp, 11.35645428598744111_dp, -15.77796305903055440_dp, 10.06971032975658353_dp, & + & -12.30566013367807265_dp, 9.00350968307662569_dp, -8.77895396829167218_dp, 8.16563014493928918_dp, & + & -5.21005712455211789_dp, 7.56241362477411272_dp, -1.61234064761579554_dp, 7.19855645524003407_dp, & + & 1.99999999999999734_dp, 7.07694595379053126_dp, 5.61234064761579088_dp, 7.19855645524003407_dp, & + & 9.21005712455211167_dp, 7.56241362477411272_dp, 12.77895396829166685_dp, 8.16563014493928918_dp, & + & 16.30566013367807088_dp, 9.00350968307662569_dp, 19.77796305903055085_dp, 10.06971032975658353_dp, & + & 23.18506021909563231_dp, 11.35645428598744111_dp, 26.51771689758312078_dp, 12.85476800444173051_dp, & + & 29.76832854644280246_dp, 14.55473640862136619_dp, 32.93089420696504277_dp, 16.44575600838861718_dp, & + & 36.00091302933286386_dp, 18.51677422677156670_dp, 38.97521854780546846_dp, 20.75650546647104022_dp, & + & 41.85176517580521960_dp, 23.15361781597290403_dp, 44.62937881000767248_dp, 25.69688738457191590_dp, & + & 47.30747895739604303_dp, 28.37531978094453322_dp, 49.88577372237665486_dp, 31.17824007995575997_dp, & + & 52.36392121341754091_dp, 34.09535373993877982_dp, 54.74114071516557800_dp, 37.11678138456306897_dp, & + & 57.01574257648425004_dp, 40.23307020728444883_dp, 59.18452370168172649_dp, 43.43518400478754415_dp, & + & 61.24193904387428944_dp, 46.71447239476329827_dp, 63.17889520468774833_dp, 50.06261728049956616_dp, & + & 64.98089250042356468_dp, 53.47155025248752480_dp, 66.62500642980749888_dp, 56.93332642202356197_dp, & + & 68.07470870335012592_dp, 60.43992354246546483_dp, 69.27043361461275595_dp, 63.98289851135272244_dp, & + & 70.11115128919477968_dp, 67.55274433031233627_dp, 70.41516373728855172_dp, 71.13755089586129543_dp, & + & 69.82714145548160900_dp, 74.71983727667711150_dp, 67.56415050352947560_dp, 78.26774990878784877_dp, & + & 61.58131858106001744_dp, 81.70467639663135628_dp, 45.28583165458984183_dp, 84.76914638163376026_dp ] + lonlat( 1561: 1680) = [ & + & -178.00000000002495426_dp, 89.99364961382421768_dp, -86.04670840724655534_dp, 86.14286053253547948_dp, & + & -83.94730489861873934_dp, 82.29066165213360762_dp, -81.87003129087820241_dp, 78.44836496333867615_dp, & + & -79.78661098119418682_dp, 74.62102007313750107_dp, -77.68757690134611948_dp, 70.81379178736982283_dp, & + & -75.56653776770212971_dp, 67.03200075627314902_dp, -73.41791965045068480_dp, 63.28116415502336878_dp, & + & -71.23639478559491067_dp, 59.56703711552955127_dp, -69.01668877482984499_dp, 55.89565494125256606_dp, & + & -66.75350545455997064_dp, 52.27337593848611164_dp, -64.44149951448400770_dp, 48.70692450151241104_dp, & + & -62.07527488350018530_dp, 45.20343384586799118_dp, -59.64940188352277772_dp, 41.77048747717444854_dp, & + & -57.15845171105518574_dp, 38.41615810166682365_dp, -54.59704918478396252_dp, 35.14904222145087687_dp, & + & -51.95994579953059400_dp, 31.97828811153562256_dp, -49.24211556220075892_dp, 28.91361425616476666_dp, & + & -46.43887601207384819_dp, 25.96531465470105005_dp, -43.54603622089217652_dp, 23.14424674184217068_dp, & + & -40.56007232189229228_dp, 20.46179708442632972_dp, -37.47832910130670570_dp, 17.92981963683196156_dp, & + & -34.29924330014637945_dp, 15.56054131855884570_dp, -31.02258051120933402_dp, 13.36643021383323848_dp, & + & -27.64967308093982012_dp, 11.36002298851478542_dp, -24.18364165385173337_dp, 9.55371034930798224_dp, & + & -20.62957864939022201_dp, 7.95948261839810733_dp, -16.99466904928961242_dp, 6.58864168091602309_dp, & + & -13.28822357553577405_dp, 5.45149036402020837_dp, -9.52160273595813855_dp, 4.55701513369246669_dp, & + & -5.70801791517022217_dp, 3.91258202080964690_dp, -1.86220744039441199_dp, 3.52366797872773274_dp, & + & 1.99999999999999734_dp, 3.39364961385021413_dp, 5.86220744039440689_dp, 3.52366797872773274_dp, & + & 9.70801791517021861_dp, 3.91258202080964690_dp, 13.52160273595813145_dp, 4.55701513369246669_dp, & + & 17.28822357553576694_dp, 5.45149036402020837_dp, 20.99466904928960886_dp, 6.58864168091602309_dp, & + & 24.62957864939021491_dp, 7.95948261839810733_dp, 28.18364165385172981_dp, 9.55371034930798224_dp, & + & 31.64967308093981302_dp, 11.36002298851478542_dp, 35.02258051120933402_dp, 13.36643021383323848_dp, & + & 38.29924330014637235_dp, 15.56054131855884570_dp, 41.47832910130669859_dp, 17.92981963683195801_dp, & + & 44.56007232189227807_dp, 20.46179708442632261_dp, 47.54603622089216231_dp, 23.14424674184217068_dp, & + & 50.43887601207384819_dp, 25.96531465470104649_dp, 53.24211556220075892_dp, 28.91361425616475955_dp, & + & 55.95994579953058690_dp, 31.97828811153562256_dp, 58.59704918478396252_dp, 35.14904222145087687_dp, & + & 61.15845171105517863_dp, 38.41615810166681655_dp, 63.64940188352278483_dp, 41.77048747717444854_dp, & + & 66.07527488350018530_dp, 45.20343384586799118_dp, 68.44149951448399349_dp, 48.70692450151241104_dp, & + & 70.75350545455997064_dp, 52.27337593848611164_dp, 73.01668877482984499_dp, 55.89565494125256606_dp, & + & 75.23639478559491067_dp, 59.56703711552955127_dp, 77.41791965045068480_dp, 63.28116415502336878_dp, & + & 79.56653776770211550_dp, 67.03200075627314902_dp, 81.68757690134611948_dp, 70.81379178736982283_dp ] + lonlat( 1681: 1800) = [ & + & 83.78661098119418682_dp, 74.62102007313750107_dp, 85.87003129087820241_dp, 78.44836496333867615_dp, & + & 87.94730489861875355_dp, 82.29066165213360762_dp, 90.04670840724658376_dp, 86.14286053253547948_dp, & + & -178.00000000000005684_dp, 86.05996156130565566_dp, -130.58473801020662108_dp, 84.39088722277173815_dp, & + & -110.19717929614465390_dp, 81.10053146183230410_dp, -100.10346048738929881_dp, 77.40995108179684792_dp, & + & -93.69886509454548218_dp, 73.59868574092435267_dp, -88.93011750021230455_dp, 69.74815881341463353_dp, & + & -85.00534896567366161_dp, 65.89187266757556927_dp, -81.55981529780905248_dp, 62.04795929474798299_dp, & + & -78.40205491898922219_dp, 58.22861013398448193_dp, -75.42099130058548440_dp, 54.44347433172315220_dp, & + & -72.54676554128549526_dp, 50.70110112272598712_dp, -69.73234087330638431_dp, 47.00963897352001908_dp, & + & -66.94410839385282941_dp, 43.37721635788606989_dp, -64.15676435641834985_dp, 39.81217237539897269_dp, & + & -61.35037195723166548_dp, 36.32321005821970061_dp, -58.50861449014277582_dp, 32.91950572923986584_dp, & + & -55.61773745881250619_dp, 29.61078975921034129_dp, -52.66591251909912330_dp, 26.40740499964346810_dp, & + & -49.64287529119710030_dp, 23.32034407821754840_dp, -46.53975201683548590_dp, 20.36126354510683711_dp, & + & -43.34902402209520034_dp, 17.54247066454981407_dp, -40.06459700293881809_dp, 14.87687712846902599_dp, & + & -36.68195074563113423_dp, 12.37791309905906445_dp, -33.19834740797973893_dp, 10.05939491571369260_dp, & + & -29.61307490596015057_dp, 7.93534078498541273_dp, -25.92769777693465372_dp, 6.01973109672258389_dp, & + & -22.14628266758476371_dp, 4.32621388981253130_dp, -18.27556116130716646_dp, 2.86776143955401652_dp, & + & -14.32499114491301384_dp, 1.65629062554436479_dp, -10.30668142622437244_dp, 0.70226688878528098_dp, & + & -6.23515434659414680_dp, 0.01431798130227213_dp, -2.12693783758673716_dp, -0.40111214042971233_dp, & + & 1.99999999999999689_dp, -0.54003843869414525_dp, 6.12693783758673050_dp, -0.40111214042971233_dp, & + & 10.23515434659414147_dp, 0.01431798130227213_dp, 14.30668142622436712_dp, 0.70226688878528098_dp, & + & 18.32499114491300318_dp, 1.65629062554436479_dp, 22.27556116130715580_dp, 2.86776143955401652_dp, & + & 26.14628266758476016_dp, 4.32621388981253130_dp, 29.92769777693464306_dp, 6.01973109672258389_dp, & + & 33.61307490596014702_dp, 7.93534078498541273_dp, 37.19834740797973183_dp, 10.05939491571369260_dp, & + & 40.68195074563112001_dp, 12.37791309905905912_dp, 44.06459700293881809_dp, 14.87687712846902421_dp, & + & 47.34902402209520034_dp, 17.54247066454981052_dp, 50.53975201683548590_dp, 20.36126354510683711_dp, & + & 53.64287529119709319_dp, 23.32034407821754485_dp, 56.66591251909911620_dp, 26.40740499964346455_dp, & + & 59.61773745881250619_dp, 29.61078975921033418_dp, 62.50861449014276872_dp, 32.91950572923986584_dp, & + & 65.35037195723165837_dp, 36.32321005821969351_dp, 68.15676435641833564_dp, 39.81217237539895848_dp, & + & 70.94410839385281520_dp, 43.37721635788606989_dp, 73.73234087330637010_dp, 47.00963897352000487_dp, & + & 76.54676554128549526_dp, 50.70110112272598002_dp, 79.42099130058548440_dp, 54.44347433172315220_dp ] + lonlat( 1801: 1920) = [ & + & 82.40205491898922219_dp, 58.22861013398448193_dp, 85.55981529780903827_dp, 62.04795929474798299_dp, & + & 89.00534896567364740_dp, 65.89187266757556927_dp, 92.93011750021230455_dp, 69.74815881341463353_dp, & + & 97.69886509454546797_dp, 73.59868574092435267_dp, 104.10346048738931302_dp, 77.40995108179684792_dp, & + & 114.19717929614462548_dp, 81.10053146183230410_dp, 134.58473801020659266_dp, 84.39088722277173815_dp, & + & -178.00000000000002842_dp, 81.83938189765240168_dp, -149.15668933029422760_dp, 80.85560324742941418_dp, & + & -128.64763601333984866_dp, 78.39753950634394641_dp, -115.34882263320223217_dp, 75.18923730101467129_dp, & + & -106.23509874248557594_dp, 71.62987479878029262_dp, -99.45761459629794388_dp, 67.90061168070624831_dp, & + & -94.04421718085441739_dp, 64.08831694761892095_dp, -89.47013935832345055_dp, 60.23965935256792648_dp, & + & -85.43460651588344490_dp, 56.38292751246400769_dp, -81.75424365182693975_dp, 52.53738247574246856_dp, & + & -78.31071017117777444_dp, 48.71761719224470966_dp, -75.02360369831293951_dp, 44.93576201889581512_dp, & + & -71.83567948061782715_dp, 41.20268880474466755_dp, -68.70439458886511375_dp, 37.52871692222674938_dp, & + & -65.59687011372152199_dp, 33.92405612452550656_dp, -62.48679120890344052_dp, 30.39910192691253599_dp, & + & -59.35245736607464551_dp, 26.96464262473804041_dp, -56.17554747078253996_dp, 23.63200834498021408_dp, & + & -52.94035098918124760_dp, 20.41317688045037926_dp, -49.63331929525543984_dp, 17.32084182287969298_dp, & + & -46.24284889074778704_dp, 14.36844259642658983_dp, -42.75924066894448572_dp, 11.57015196980180605_dp, & + & -39.17479644658766347_dp, 8.94081392759362537_dp, -35.48402100288603123_dp, 6.49582334126627892_dp, & + & -31.68389780238809195_dp, 4.25093893994812166_dp, -27.77420154083882053_dp, 2.22202305439570358_dp, & + & -23.75780275269854158_dp, 0.42470591907018640_dp, -19.64091162595336826_dp, -1.12602081331554005_dp, & + & -15.43320327160518524_dp, -2.41626750988024330_dp, -11.14776873556009384_dp, -3.43374257581262743_dp, & + & -6.80084816537008940_dp, -4.16824654595516098_dp, -2.41132592404073165_dp, -4.61212239418478553_dp, & + & 1.99999999999999689_dp, -4.76061810234754024_dp, 6.41132592404072632_dp, -4.61212239418478553_dp, & + & 10.80084816537008230_dp, -4.16824654595516098_dp, 15.14776873556009029_dp, -3.43374257581262743_dp, & + & 19.43320327160517991_dp, -2.41626750988024330_dp, 23.64091162595336115_dp, -1.12602081331554005_dp, & + & 27.75780275269853803_dp, 0.42470591907018640_dp, 31.77420154083881698_dp, 2.22202305439570358_dp, & + & 35.68389780238808129_dp, 4.25093893994812166_dp, 39.48402100288601702_dp, 6.49582334126627270_dp, & + & 43.17479644658765636_dp, 8.94081392759361826_dp, 46.75924066894447861_dp, 11.57015196980179894_dp, & + & 50.24284889074777283_dp, 14.36844259642658628_dp, 53.63331929525543273_dp, 17.32084182287968943_dp, & + & 56.94035098918124049_dp, 20.41317688045037571_dp, 60.17554747078253286_dp, 23.63200834498020697_dp, & + & 63.35245736607463840_dp, 26.96464262473803331_dp, 66.48679120890342631_dp, 30.39910192691252178_dp, & + & 69.59687011372152199_dp, 33.92405612452549946_dp, 72.70439458886511375_dp, 37.52871692222674227_dp ] + lonlat( 1921: 2040) = [ & + & 75.83567948061782715_dp, 41.20268880474466755_dp, 79.02360369831293951_dp, 44.93576201889580091_dp, & + & 82.31071017117776023_dp, 48.71761719224470255_dp, 85.75424365182693975_dp, 52.53738247574246145_dp, & + & 89.43460651588344490_dp, 56.38292751246400769_dp, 93.47013935832345055_dp, 60.23965935256792648_dp, & + & 98.04421718085441739_dp, 64.08831694761892095_dp, 103.45761459629794388_dp, 67.90061168070624831_dp, & + & 110.23509874248557594_dp, 71.62987479878029262_dp, 119.34882263320223217_dp, 75.18923730101467129_dp, & + & 132.64763601333982024_dp, 78.39753950634394641_dp, 153.15668933029419918_dp, 80.85560324742941418_dp, & + & -178.00000000000002842_dp, 77.29014606148251687_dp, -157.47781378002883912_dp, 76.59572545663588983_dp, & + & -140.18467631053607647_dp, 74.70285264202438213_dp, -126.87442892417496410_dp, 71.99165187875776439_dp, & + & -116.75095597801099245_dp, 68.78228497629521598_dp, -108.83745587761724494_dp, 65.27873175231235336_dp, & + & -102.40541928769302160_dp, 61.60190913800556700_dp, -96.97173315822051620_dp, 57.82466562901673512_dp, & + & -92.22157019495104180_dp, 53.99321139219064491_dp, -87.94629406419838347_dp, 50.13884820321026581_dp, & + & -84.00357763838492531_dp, 46.28434439562811065_dp, -80.29307189810650414_dp, 42.44749809608728697_dp, & + & -76.74158359961857911_dp, 38.64321178012773572_dp, -73.29390254081907585_dp, 34.88475663106463287_dp, & + & -69.90700610430916129_dp, 31.18458087158649406_dp, -66.54632400302145356_dp, 27.55485191826680591_dp, & + & -63.18329478816111333_dp, 24.00783659504740086_dp, -59.79375952890369916_dp, 20.55617726580612015_dp, & + & -56.35691981311008192_dp, 17.21309536720795919_dp, -52.85469423510679832_dp, 13.99253787292509621_dp, & + & -49.27137129744098587_dp, 10.90927185095380558_dp, -45.59349430020986915_dp, 7.97892504367260802_dp, & + & -41.80993456941939712_dp, 5.21796518077428839_dp, -37.91211819209929246_dp, 2.64360721178194558_dp, & + & -33.89437094593881739_dp, 0.27363605581641759_dp, -29.75433817509684786_dp, -1.87386656696368226_dp, & + & -25.49342310484703589_dp, -3.78089833576525525_dp, -21.11717183125961128_dp, -5.43001433583734983_dp, & + & -16.63552097548324937_dp, -6.80489321395614066_dp, -12.06282113870962114_dp, -7.89096231408011839_dp, & + & -7.41756212661334047_dp, -8.67603648837036268_dp, -2.72175805422022021_dp, -9.15091230991673399_dp, & + & 1.99999999999999645_dp, -9.30985393851741172_dp, 6.72175805422021444_dp, -9.15091230991673399_dp, & + & 11.41756212661333691_dp, -8.67603648837036268_dp, 16.06282113870961581_dp, -7.89096231408011839_dp, & + & 20.63552097548324582_dp, -6.80489321395614066_dp, 25.11717183125960418_dp, -5.43001433583734983_dp, & + & 29.49342310484702523_dp, -3.78089833576525525_dp, 33.75433817509684786_dp, -1.87386656696368226_dp, & + & 37.89437094593881028_dp, 0.27363605581641121_dp, 41.91211819209927114_dp, 2.64360721178193936_dp, & + & 45.80993456941938291_dp, 5.21796518077428217_dp, 49.59349430020984784_dp, 7.97892504367260180_dp, & + & 53.27137129744098587_dp, 10.90927185095380203_dp, 56.85469423510678411_dp, 13.99253787292509266_dp, & + & 60.35691981311008192_dp, 17.21309536720795563_dp, 63.79375952890369206_dp, 20.55617726580611659_dp ] + lonlat( 2041: 2160) = [ & + & 67.18329478816110623_dp, 24.00783659504739376_dp, 70.54632400302143935_dp, 27.55485191826679880_dp, & + & 73.90700610430916129_dp, 31.18458087158649050_dp, 77.29390254081907585_dp, 34.88475663106463287_dp, & + & 80.74158359961856490_dp, 38.64321178012773572_dp, 84.29307189810650414_dp, 42.44749809608728697_dp, & + & 88.00357763838491110_dp, 46.28434439562810354_dp, 91.94629406419836926_dp, 50.13884820321025160_dp, & + & 96.22157019495104180_dp, 53.99321139219064491_dp, 100.97173315822050199_dp, 57.82466562901673512_dp, & + & 106.40541928769302160_dp, 61.60190913800556700_dp, 112.83745587761723073_dp, 65.27873175231235336_dp, & + & 120.75095597801100666_dp, 68.78228497629521598_dp, 130.87442892417493567_dp, 71.99165187875776439_dp, & + & 144.18467631053604805_dp, 74.70285264202438213_dp, 161.47781378002881070_dp, 76.59572545663588983_dp, & + & -178.00000000000002842_dp, 72.36459406509962378_dp, -162.05644118521095720_dp, 71.82662636485281382_dp, & + & -147.60742176334812825_dp, 70.30250402254888797_dp, -135.36255278451204731_dp, 68.00113945880528377_dp, & + & -125.26976638159491984_dp, 65.14478524125108549_dp, -116.94158660124730886_dp, 61.91142197433249095_dp, & + & -109.95470750710923369_dp, 58.42738157410047961_dp, -103.95825647893495614_dp, 54.77900423463843538_dp, & + & -98.68725830824647005_dp, 51.02562354013957702_dp, -93.94726867835278483_dp, 47.20919167069192923_dp, & + & -89.59567144235911940_dp, 43.36062114047545890_dp, -85.52649044264862255_dp, 39.50382677214758331_dp, & + & -81.65938939021744147_dp, 35.65830698143167155_dp, -77.93201512996090230_dp, 31.84082402428231973_dp, & + & -74.29472888122153051_dp, 28.06652943785655552_dp, -70.70698212428176532_dp, 24.34974403770184992_dp, & + & -67.13481958525987636_dp, 20.70451849248450316_dp, -63.54916484650964748_dp, 17.14505004500528074_dp, & + & -59.92466443809041010_dp, 13.68599977255222377_dp, -56.23894695811429756_dp, 10.34273467530951152_dp, & + & -52.47220691225792422_dp, 7.13150506907927006_dp, -48.60705694133756083_dp, 4.06955754229704603_dp, & + & -44.62861191461713162_dp, 1.17517574056815666_dp, -40.52477649322946007_dp, -1.53236497867592147_dp, & + & -36.28670514160292981_dp, -4.03294710811187507_dp, -31.90939060170597941_dp, -6.30588392466837533_dp, & + & -27.39231472226267528_dp, -8.33033151194295662_dp, -22.74006765897273397_dp, -10.08584610023313921_dp, & + & -17.96281473836326725_dp, -11.55307649365679090_dp, -13.07647532339788654_dp, -12.71455858250937254_dp, & + & -8.10248720394762678_dp, -13.55555303790299426_dp, -3.06707279862612969_dp, -14.06484423228424063_dp, & + & 1.99999999999999600_dp, -14.23540593490034212_dp, 7.06707279862612125_dp, -14.06484423228424063_dp, & + & 12.10248720394761790_dp, -13.55555303790299426_dp, 17.07647532339788299_dp, -12.71455858250937254_dp, & + & 21.96281473836326015_dp, -11.55307649365679090_dp, 26.74006765897273041_dp, -10.08584610023313921_dp, & + & 31.39231472226266817_dp, -8.33033151194295662_dp, 35.90939060170597230_dp, -6.30588392466837533_dp, & + & 40.28670514160292271_dp, -4.03294710811188128_dp, 44.52477649322944586_dp, -1.53236497867592791_dp, & + & 48.62861191461712451_dp, 1.17517574056815022_dp, 52.60705694133754662_dp, 4.06955754229703981_dp ] + lonlat( 2161: 2280) = [ & + & 56.47220691225791711_dp, 7.13150506907926740_dp, 60.23894695811429756_dp, 10.34273467530950796_dp, & + & 63.92466443809040300_dp, 13.68599977255221667_dp, 67.54916484650964037_dp, 17.14505004500527363_dp, & + & 71.13481958525987636_dp, 20.70451849248449605_dp, 74.70698212428177953_dp, 24.34974403770184992_dp, & + & 78.29472888122153051_dp, 28.06652943785655197_dp, 81.93201512996088809_dp, 31.84082402428231262_dp, & + & 85.65938939021744147_dp, 35.65830698143166444_dp, 89.52649044264860834_dp, 39.50382677214758331_dp, & + & 93.59567144235911940_dp, 43.36062114047545180_dp, 97.94726867835277062_dp, 47.20919167069192213_dp, & + & 102.68725830824645584_dp, 51.02562354013956281_dp, 107.95825647893494192_dp, 54.77900423463843538_dp, & + & 113.95470750710921948_dp, 58.42738157410047961_dp, 120.94158660124729465_dp, 61.91142197433249095_dp, & + & 129.26976638159487720_dp, 65.14478524125108549_dp, 139.36255278451201889_dp, 68.00113945880528377_dp, & + & 151.60742176334809983_dp, 70.30250402254888797_dp, 166.05644118521092878_dp, 71.82662636485281382_dp, & + & -178.00000000000002842_dp, 67.00865712643569339_dp, -164.95645291666266985_dp, 66.56908368553206401_dp, & + & -152.69437932769912436_dp, 65.29863962109095610_dp, -141.70578215046154469_dp, 63.31961305621509695_dp, & + & -132.12712761110685733_dp, 60.78156430751474204_dp, -123.85428534979440940_dp, 57.82417470821774685_dp, & + & -116.68240638414135901_dp, 54.56141374911145192_dp, -110.39390839554111778_dp, 51.08045112569507751_dp, & + & -104.79614728017998004_dp, 47.44644408577498496_dp, -99.73088255981890882_dp, 43.70821480745370025_dp, & + & -95.07197748871266185_dp, 39.90302877816822757_dp, -90.71961138434764393_dp, 36.06017287638244539_dp, & + & -86.59435351833845118_dp, 32.20351875194761959_dp, -82.63216392910496211_dp, 28.35333972343855535_dp, & + & -78.78048521884275601_dp, 24.52760876444716587_dp, -74.99529352424694650_dp, 20.74294398926493699_dp, & + & -71.23891693218340038_dp, 17.01531594444593765_dp, -67.47845003929684538_dp, 13.36059248585088355_dp, & + & -63.68463403089357655_dp, 9.79496963802121101_dp, -59.83111160507219495_dp, 6.33531713186757539_dp, & + & -55.89399913408760057_dp, 2.99945241539763696_dp, -51.85174332801064168_dp, -0.19365520038940248_dp, & + & -47.68524592294902931_dp, -3.22375917751486485_dp, -43.37824652323212860_dp, -6.06930752874206636_dp, & + & -38.91794868987490474_dp, -8.70753794849322027_dp, -34.29585521437643081_dp, -11.11472732454917001_dp, & + & -29.50874372060167872_dp, -13.26662598462659126_dp, -24.55966518490711081_dp, -15.13909773195326558_dp, & + & -19.45879403720245193_dp, -16.70896594558295334_dp, -14.22391622121717880_dp, -17.95503295463868554_dp, & + & -8.88033504279663255_dp, -18.85919797266866027_dp, -3.46002704385166027_dp, -19.40755726556069405_dp, & + & 1.99999999999999645_dp, -19.59134287356426540_dp, 7.46002704385165316_dp, -19.40755726556069405_dp, & + & 12.88033504279662367_dp, -18.85919797266866027_dp, 18.22391622121717347_dp, -17.95503295463868554_dp, & + & 23.45879403720244127_dp, -16.70896594558295334_dp, 28.55966518490710015_dp, -15.13909773195326558_dp, & + & 33.50874372060167161_dp, -13.26662598462659126_dp, 38.29585521437642370_dp, -11.11472732454917534_dp ] + lonlat( 2281: 2400) = [ & + & 42.91794868987490474_dp, -8.70753794849322738_dp, 47.37824652323212149_dp, -6.06930752874207258_dp, & + & 51.68524592294901510_dp, -3.22375917751487107_dp, 55.85174332801063457_dp, -0.19365520038940884_dp, & + & 59.89399913408759346_dp, 2.99945241539763385_dp, 63.83111160507218784_dp, 6.33531713186756917_dp, & + & 67.68463403089357655_dp, 9.79496963802120568_dp, 71.47845003929684538_dp, 13.36059248585087822_dp, & + & 75.23891693218338617_dp, 17.01531594444593054_dp, 78.99529352424693229_dp, 20.74294398926492988_dp, & + & 82.78048521884274180_dp, 24.52760876444715521_dp, 86.63216392910494790_dp, 28.35333972343854825_dp, & + & 90.59435351833845118_dp, 32.20351875194761959_dp, 94.71961138434764393_dp, 36.06017287638243118_dp, & + & 99.07197748871266185_dp, 39.90302877816822047_dp, 103.73088255981890882_dp, 43.70821480745368603_dp, & + & 108.79614728017998004_dp, 47.44644408577497074_dp, 114.39390839554110357_dp, 51.08045112569504909_dp, & + & 120.68240638414135901_dp, 54.56141374911145192_dp, 127.85428534979438098_dp, 57.82417470821774685_dp, & + & 136.12712761110682891_dp, 60.78156430751474204_dp, 145.70578215046154469_dp, 63.31961305621509695_dp, & + & 156.69437932769909594_dp, 65.29863962109095610_dp, 168.95645291666264143_dp, 66.56908368553206401_dp, & + & -178.00000000000002842_dp, 61.16162880179430772_dp, -166.97884079028878546_dp, 60.79043532425956897_dp, & + & -156.40162010671977555_dp, 59.70518607311999659_dp, -146.59283359977425221_dp, 57.98141283097307053_dp, & + & -137.70772458687926587_dp, 55.72003625021811501_dp, -129.75638606304184464_dp, 53.02581189527144545_dp, & + & -122.65952710171549711_dp, 49.99368843662635697_dp, -116.29889149413011751_dp, 46.70342202473146642_dp, & + & -110.54933935400606515_dp, 43.21943287866469774_dp, -105.29473060869931089_dp, 39.59298941537671368_dp, & + & -100.43366587620559471_dp, 35.86497163386139420_dp, -95.88003723653146437_dp, 32.06843215731343832_dp, & + & -91.56137924921046078_dp, 28.23071203179445376_dp, -87.41655400532016529_dp, 24.37510563102819461_dp, & + & -83.39346098727328638_dp, 20.52214967325533124_dp, -79.44703540885105042_dp, 16.69062430422997423_dp, & + & -75.53760403336077900_dp, 12.89834287286322301_dp, -71.62959031828354739_dp, 9.16278970353470967_dp, & + & -67.69053915995506543_dp, 5.50164833296652400_dp, -63.69043463465567356_dp, 1.93324784086672841_dp, & + & -59.60129716846515180_dp, -1.52305829704237294_dp, -55.39706222812606740_dp, -4.84657887125734721_dp, & + & -51.05375641269629483_dp, -8.01504773475606314_dp, -46.54999441522540593_dp, -11.00448645615599474_dp, & + & -41.86781613095839560_dp, -13.78920372951384543_dp, -36.99386004100858116_dp, -16.34197985921976581_dp, & + & -31.92081946357624034_dp, -18.63449057116782015_dp, -26.64904852681421588_dp, -20.63801909702448611_dp, & + & -21.18808138888132930_dp, -22.32448212182582026_dp, -15.55772615655294366_dp, -23.66774871950555692_dp, & + & -9.78834011215971245_dp, -24.64516330804185174_dp, -3.91994134356475854_dp, -25.23910692117754806_dp, & + & 1.99999999999999556_dp, -25.43837119820565107_dp, 7.91994134356474966_dp, -25.23910692117754806_dp, & + & 13.78834011215970534_dp, -24.64516330804185174_dp, 19.55772615655293123_dp, -23.66774871950555692_dp ] + lonlat( 2401: 2520) = [ & + & 25.18808138888131865_dp, -22.32448212182582026_dp, 30.64904852681421232_dp, -20.63801909702448611_dp, & + & 35.92081946357622968_dp, -18.63449057116782015_dp, 40.99386004100855985_dp, -16.34197985921976581_dp, & + & 45.86781613095838850_dp, -13.78920372951385076_dp, 50.54999441522539882_dp, -11.00448645615600007_dp, & + & 55.05375641269628773_dp, -8.01504773475606846_dp, 59.39706222812605318_dp, -4.84657887125735343_dp, & + & 63.60129716846514469_dp, -1.52305829704237916_dp, 67.69043463465565935_dp, 1.93324784086672197_dp, & + & 71.69053915995505122_dp, 5.50164833296651778_dp, 75.62959031828353318_dp, 9.16278970353470434_dp, & + & 79.53760403336076479_dp, 12.89834287286321590_dp, 83.44703540885105042_dp, 16.69062430422996712_dp, & + & 87.39346098727328638_dp, 20.52214967325532413_dp, 91.41655400532015108_dp, 24.37510563102818750_dp, & + & 95.56137924921046078_dp, 28.23071203179444666_dp, 99.88003723653145016_dp, 32.06843215731343832_dp, & + & 104.43366587620558050_dp, 35.86497163386137288_dp, 109.29473060869932510_dp, 39.59298941537669947_dp, & + & 114.54933935400606515_dp, 43.21943287866469774_dp, 120.29889149413010330_dp, 46.70342202473146642_dp, & + & 126.65952710171551132_dp, 49.99368843662635697_dp, 133.75638606304187306_dp, 53.02581189527144545_dp, & + & 141.70772458687926587_dp, 55.72003625021811501_dp, 150.59283359977422379_dp, 57.98141283097307053_dp, & + & 160.40162010671977555_dp, 59.70518607311999659_dp, 170.97884079028878546_dp, 60.79043532425956897_dp, & + & -178.00000000000002842_dp, 54.75649468439711143_dp, -168.49285933765114009_dp, 54.43639475342744305_dp, & + & -159.25249258416440057_dp, 53.49372597518600259_dp, -150.49218436749950456_dp, 51.97706503648645082_dp, & + & -142.34201331309026273_dp, 49.95526128262039123_dp, -134.84908760812987794_dp, 47.50530446205828383_dp, & + & -127.99715883033752561_dp, 44.70273256173389598_dp, -121.73118977168118704_dp, 41.61600262291977259_dp, & + & -115.97779813807071037_dp, 38.30427835916992052_dp, -110.65884380836621403_dp, 34.81740352705595143_dp, & + & -105.69899804029306267_dp, 31.19696754875727152_dp, -101.02917319698202903_dp, 27.47775433026597725_dp, & + & -96.58749981932022877_dp, 23.68920100972959730_dp, -92.31902021078286680_dp, 19.85670886149115688_dp, & + & -88.17480572169651509_dp, 16.00276430383187076_dp, -84.11088800598805904_dp, 12.14788119395578825_dp, & + & -80.08720437104130951_dp, 8.31139512800513991_dp, -76.06665407661381550_dp, 4.51214364124032752_dp, & + & -72.01431282527251199_dp, 0.76906207426875217_dp, -67.89683474738467339_dp, -2.89828246890512542_dp, & + & -63.68207179147747610_dp, -6.46920580966549608_dp, -59.33895182482493880_dp, -9.92146778444055499_dp, & + & -54.83767345094592827_dp, -13.23089104518994574_dp, -50.15029168417838434_dp, -16.37106576006626568_dp, & + & -45.25177543408179304_dp, -19.31318879069117145_dp, -40.12160159745184274_dp, -22.02610835172851722_dp, & + & -34.74589265732009835_dp, -24.47666503668304117_dp, -29.11998552133064422_dp, -26.63042818683534207_dp, & + & -23.25113130544623985_dp, -28.45290874783366064_dp, -17.16079514785620930_dp, -29.91127029421519978_dp, & + & -10.88583393507997776_dp, -30.97645080660139172_dp, -4.47781225763253765_dp, -31.62546289221583962_dp ] + lonlat( 2521: 2640) = [ & + & 1.99999999999999534_dp, -31.84350531560284736_dp, 8.47781225763252877_dp, -31.62546289221583962_dp, & + & 14.88583393507996711_dp, -30.97645080660139172_dp, 21.16079514785619509_dp, -29.91127029421519978_dp, & + & 27.25113130544623274_dp, -28.45290874783366064_dp, 33.11998552133064067_dp, -26.63042818683534207_dp, & + & 38.74589265732009835_dp, -24.47666503668304117_dp, 44.12160159745183563_dp, -22.02610835172851722_dp, & + & 49.25177543408178593_dp, -19.31318879069118211_dp, 54.15029168417837724_dp, -16.37106576006627279_dp, & + & 58.83767345094592827_dp, -13.23089104518995285_dp, 63.33895182482493169_dp, -9.92146778444056032_dp, & + & 67.68207179147748320_dp, -6.46920580966550229_dp, 71.89683474738467339_dp, -2.89828246890513164_dp, & + & 76.01431282527251199_dp, 0.76906207426874584_dp, 80.06665407661380129_dp, 4.51214364124032308_dp, & + & 84.08720437104132372_dp, 8.31139512800513458_dp, 88.11088800598805904_dp, 12.14788119395578470_dp, & + & 92.17480572169650088_dp, 16.00276430383186366_dp, 96.31902021078285259_dp, 19.85670886149114978_dp, & + & 100.58749981932022877_dp, 23.68920100972959020_dp, 105.02917319698201482_dp, 27.47775433026597014_dp, & + & 109.69899804029304846_dp, 31.19696754875725375_dp, 114.65884380836618561_dp, 34.81740352705593722_dp, & + & 119.97779813807069615_dp, 38.30427835916992052_dp, 125.73118977168117283_dp, 41.61600262291977259_dp, & + & 131.99715883033749719_dp, 44.70273256173389598_dp, 138.84908760812987794_dp, 47.50530446205828383_dp, & + & 146.34201331309026273_dp, 49.95526128262039123_dp, 154.49218436749950456_dp, 51.97706503648645082_dp, & + & 163.25249258416440057_dp, 53.49372597518600259_dp, 172.49285933765111167_dp, 54.43639475342744305_dp, & + & -178.00000000000002842_dp, 47.72124472298390430_dp, -169.69217969866491558_dp, 47.44157216781381692_dp, & + & -161.55074086228117380_dp, 46.61395503653132266_dp, -153.71667350566602295_dp, 45.27051130755705799_dp, & + & -146.28885177460344380_dp, 43.45866798082070659_dp, -139.31958252044236701_dp, 41.23439386179803279_dp, & + & -132.82020109922686402_dp, 38.65601141858345358_dp, -126.77168357131760956_dp, 35.77969749489390949_dp, & + & -121.13576891124588997_dp, 32.65687547770472321_dp, -115.86406835852179142_dp, 29.33315055785289260_dp, & + & -110.90442707083994378_dp, 25.84827445710250160_dp, -106.20481280215612685_dp, 22.23668760419472790_dp, & + & -101.71535755141638901_dp, 18.52832281285149207_dp, -97.38917071735673403_dp, 14.74948212031801020_dp, & + & -93.18240447461121789_dp, 10.92369066480776674_dp, -89.05390189983984328_dp, 7.07248905242842607_dp, & + & -84.96463936229969249_dp, 3.21615784383777559_dp, -80.87709425893895343_dp, -0.62561625366165152_dp, & + & -76.75462189677602964_dp, -4.43311662588922051_dp, -72.56090370117536281_dp, -8.18599910867232161_dp, & + & -68.25952713288315010_dp, -11.86270778471483389_dp, -63.81377209114523907_dp, -15.43989911427394368_dp, & + & -59.18670666190919150_dp, -18.89188184637555068_dp, -54.34173321358174746_dp, -22.19009961204913850_dp, & + & -49.24376548995358149_dp, -25.30271165018387691_dp, -43.86123932751760890_dp, -28.19436585459229860_dp, & + & -38.16912769952225659_dp, -30.82630445836841915_dp, -32.15298964123236658_dp, -33.15698491696964112_dp ] + lonlat( 2641: 2760) = [ & + & -25.81377244810833815_dp, -35.14341202945125531_dp, -19.17259140539265516_dp, -36.74332279213026453_dp, & + & -12.27414365511957151_dp, -37.91820250638207312_dp, -5.18708125479775184_dp, -38.63683300066042392_dp, & + & 1.99999999999999489_dp, -38.87875527701606160_dp, 9.18708125479774296_dp, -38.63683300066042392_dp, & + & 16.27414365511955907_dp, -37.91820250638207312_dp, 23.17259140539264095_dp, -36.74332279213026453_dp, & + & 29.81377244810832394_dp, -35.14341202945125531_dp, 36.15298964123235947_dp, -33.15698491696964112_dp, & + & 42.16912769952225659_dp, -30.82630445836841915_dp, 47.86123932751760179_dp, -28.19436585459229860_dp, & + & 53.24376548995357439_dp, -25.30271165018388402_dp, 58.34173321358174746_dp, -22.19009961204914205_dp, & + & 63.18670666190918439_dp, -18.89188184637555779_dp, 67.81377209114522486_dp, -15.43989911427395079_dp, & + & 72.25952713288313589_dp, -11.86270778471484277_dp, 76.56090370117536281_dp, -8.18599910867232872_dp, & + & 80.75462189677602964_dp, -4.43311662588922673_dp, 84.87709425893895343_dp, -0.62561625366165630_dp, & + & 88.96463936229967828_dp, 3.21615784383776981_dp, 93.05390189983984328_dp, 7.07248905242842163_dp, & + & 97.18240447461121789_dp, 10.92369066480776141_dp, 101.38917071735671982_dp, 14.74948212031800310_dp, & + & 105.71535755141637480_dp, 18.52832281285148497_dp, 110.20481280215611264_dp, 22.23668760419472079_dp, & + & 114.90442707083992957_dp, 25.84827445710249449_dp, 119.86406835852177721_dp, 29.33315055785288550_dp, & + & 125.13576891124587576_dp, 32.65687547770470900_dp, 130.77168357131759535_dp, 35.77969749489390949_dp, & + & 136.82020109922683559_dp, 38.65601141858345358_dp, 143.31958252044236701_dp, 41.23439386179803279_dp, & + & 150.28885177460347222_dp, 43.45866798082070659_dp, 157.71667350566602295_dp, 45.27051130755705799_dp, & + & 165.55074086228114538_dp, 46.61395503653132266_dp, 173.69217969866488716_dp, 47.44157216781381692_dp, & + & -178.00000000000002842_dp, 39.98177927524727693_dp, -170.20476659278793363_dp, 39.70189618399165710_dp, & + & -162.53782868216148927_dp, 38.87197455633481979_dp, -155.11038900941949237_dp, 37.51968052984662449_dp, & + & -148.00450116657361832_dp, 35.68659928106227852_dp, -141.26850797118672176_dp, 33.42299645292870736_dp, & + & -134.91921485966395267_dp, 30.78272820361293327_dp, -128.94811747553873715_dp, 27.81921722445741452_dp, & + & -123.32885205682140395_dp, 24.58281725765701253_dp, -118.02393786261990272_dp, 21.11944988981388605_dp, & + & -112.98994385952538266_dp, 17.47019805768939449_dp, -108.18096363521175363_dp, 13.67152082594358653_dp, & + & -103.55065416712271542_dp, 9.75582109859903390_dp, -99.05319914949195947_dp, 5.75218534812640492_dp, & + & -94.64352777118511995_dp, 1.68719008387413827_dp, -90.27704477012913742_dp, -2.41427530523583656_dp, & + & -85.90905356028720519_dp, -6.52818370181667529_dp, -81.49400025392820623_dp, -10.63053585607600837_dp, & + & -76.98463901984250413_dp, -14.69649678201448140_dp, -72.33122188826867216_dp, -18.69947873486281864_dp, & + & -67.48085419487966874_dp, -22.61014977269736193_dp, -62.37723925505212463_dp, -26.39536140379772533_dp, & + & -56.96117255247644096_dp, -30.01702215459737033_dp, -51.17233704527057370_dp, -33.43100825355479344_dp ] + lonlat( 2761: 2880) = [ & + & -44.95316063861177014_dp, -36.58631299796492442_dp, -38.25559890256131723_dp, -39.42480136235259636_dp, & + & -31.05142021090808413_dp, -41.88213387768102791_dp, -23.34546416576216643_dp, -43.89055765093644368_dp, & + & -15.18911080317680451_dp, -45.38412606933154336_dp, -6.68839576675081293_dp, -46.30624543028365991_dp, & + & 1.99999999999999445_dp, -46.61822072475268186_dp, 10.68839576675079961_dp, -46.30624543028365991_dp, & + & 19.18911080317679918_dp, -45.38412606933154336_dp, 27.34546416576215222_dp, -43.89055765093644368_dp, & + & 35.05142021090807702_dp, -41.88213387768102791_dp, 42.25559890256131013_dp, -39.42480136235259636_dp, & + & 48.95316063861175593_dp, -36.58631299796492442_dp, 55.17233704527057370_dp, -33.43100825355479344_dp, & + & 60.96117255247643385_dp, -30.01702215459737744_dp, 66.37723925505210332_dp, -26.39536140379773244_dp, & + & 71.48085419487965453_dp, -22.61014977269736548_dp, 76.33122188826865795_dp, -18.69947873486282575_dp, & + & 80.98463901984250413_dp, -14.69649678201448850_dp, 85.49400025392819202_dp, -10.63053585607601548_dp, & + & 89.90905356028720519_dp, -6.52818370181668062_dp, 94.27704477012913742_dp, -2.41427530523584188_dp, & + & 98.64352777118510573_dp, 1.68719008387413361_dp, 103.05319914949195947_dp, 5.75218534812639781_dp, & + & 107.55065416712271542_dp, 9.75582109859902680_dp, 112.18096363521173942_dp, 13.67152082594358120_dp, & + & 116.98994385952536845_dp, 17.47019805768938738_dp, 122.02393786261990272_dp, 21.11944988981387894_dp, & + & 127.32885205682140395_dp, 24.58281725765700187_dp, 132.94811747553873715_dp, 27.81921722445741452_dp, & + & 138.91921485966392424_dp, 30.78272820361293327_dp, 145.26850797118672176_dp, 33.42299645292870736_dp, & + & 152.00450116657361832_dp, 35.68659928106227852_dp, 159.11038900941949237_dp, 37.51968052984662449_dp, & + & 166.53782868216146085_dp, 38.87197455633481979_dp, 174.20476659278793363_dp, 39.70189618399165710_dp, & + & -178.00000000000002842_dp, 31.46720374958098887_dp, -171.12910064316457692_dp, 31.22051723140710067_dp, & + & -164.34108626834762390_dp, 30.48695101230147841_dp, -157.71017626763574526_dp, 29.28521267182222587_dp, & + & -151.29523174265207786_dp, 27.64411687523926986_dp, -145.13620894054648147_dp, 25.59967735271197498_dp, & + & -139.25381666270027381_dp, 23.19203115841211726_dp, -133.65157520973266969_dp, 20.46271167815037728_dp, & + & -128.31914580979304219_dp, 17.45256126555711873_dp, -123.23592626204657563_dp, 14.20035582641498806_dp, & + & -118.37425008640165913_dp, 10.74206713847307348_dp, -113.70187269530693186_dp, 7.11062322287716331_dp, & + & -109.18367588372132104_dp, 3.33602075627005945_dp, -104.78265670493266271_dp, -0.55433148808729127_dp, & + & -100.46031614728222792_dp, -4.53512373056416163_dp, -96.17656220612794016_dp, -8.58256192749418290_dp, & + & -91.88921878897831164_dp, -12.67371220268469223_dp, -87.55320457280681978_dp, -16.78577161643179849_dp, & + & -83.11942634360052296_dp, -20.89524495725206421_dp, -78.53343020155088539_dp, -24.97699141478169338_dp, & + & -73.73388667252179118_dp, -29.00309226896919412_dp, -68.65107920217621995_dp, -32.94148403257255353_dp, & + & -63.20576633421556068_dp, -36.75430974219704439_dp, -57.30916699148811233_dp, -40.39598469592144880_dp ] + lonlat( 2881: 3000) = [ & + & -50.86545369174167774_dp, -43.81109166541019562_dp, -43.77902429867569367_dp, -46.93247902115529513_dp, & + & -35.96960367385943158_dp, -49.68040408706252720_dp, -27.39769711559288723_dp, -51.96422430511800172_dp, & + & -18.09872704828334378_dp, -53.68864395349498864_dp, -8.21451927262478065_dp, -54.76594013073759015_dp, & + & 1.99999999999999289_dp, -55.13279625041898413_dp, 12.21451927262476822_dp, -54.76594013073759015_dp, & + & 22.09872704828332601_dp, -53.68864395349498864_dp, 31.39769711559288012_dp, -51.96422430511800172_dp, & + & 39.96960367385942448_dp, -49.68040408706252720_dp, 47.77902429867568657_dp, -46.93247902115529513_dp, & + & 54.86545369174167064_dp, -43.81109166541019562_dp, 61.30916699148811233_dp, -40.39598469592146301_dp, & + & 67.20576633421555357_dp, -36.75430974219705149_dp, 72.65107920217620574_dp, -32.94148403257256064_dp, & + & 77.73388667252179118_dp, -29.00309226896920123_dp, 82.53343020155088539_dp, -24.97699141478170048_dp, & + & 87.11942634360052296_dp, -20.89524495725207132_dp, 91.55320457280680557_dp, -16.78577161643180560_dp, & + & 95.88921878897831164_dp, -12.67371220268469756_dp, 100.17656220612792595_dp, -8.58256192749419000_dp, & + & 104.46031614728221371_dp, -4.53512373056416607_dp, 108.78265670493264849_dp, -0.55433148808729760_dp, & + & 113.18367588372132104_dp, 3.33602075627005323_dp, 117.70187269530691765_dp, 7.11062322287715620_dp, & + & 122.37425008640165913_dp, 10.74206713847306816_dp, 127.23592626204656142_dp, 14.20035582641498095_dp, & + & 132.31914580979304219_dp, 17.45256126555711162_dp, 137.65157520973264127_dp, 20.46271167815036662_dp, & + & 143.25381666270027381_dp, 23.19203115841211726_dp, 149.13620894054645305_dp, 25.59967735271197498_dp, & + & 155.29523174265207786_dp, 27.64411687523926986_dp, 161.71017626763577368_dp, 29.28521267182222587_dp, & + & 168.34108626834759548_dp, 30.48695101230147841_dp, 175.12910064316457692_dp, 31.22051723140710067_dp, & + & -178.00000000000002842_dp, 22.11837781753018817_dp, -170.46891148411387462_dp, 21.78042227826199095_dp, & + & -163.04151372648448159_dp, 20.77699151155475477_dp, -155.80845268931921055_dp, 19.13794220572576776_dp, & + & -148.83768595785252842_dp, 16.90865838596958781_dp, -142.17005258660481104_dp, 14.14485028302004999_dp, & + & -135.81978239976487544_dp, 10.90731557455581147_dp, -129.77826339847271697_dp, 7.25759760165333567_dp, & + & -124.01906809075352101_dp, 3.25496397729521814_dp, -118.50268451884778642_dp, -1.04529436283411115_dp, & + & -113.18006743498142441_dp, -5.59247248797413210_dp, -107.99465974304708027_dp, -10.34025377357217934_dp, & + & -102.88281945262124850_dp, -15.24594064653213898_dp, -97.77265218881962028_dp, -20.26925377022462982_dp, & + & -92.58116190590757810_dp, -25.37072818781790673_dp, -87.20945067766288616_dp, -30.50961194071436111_dp, & + & -81.53547443418635510_dp, -35.64102404029979709_dp, -75.40370496549049051_dp, -40.71192873614232610_dp, & + & -68.61131289029849256_dp, -45.65520975111465418_dp, -60.89227178252910733_dp, -50.38082342875217989_dp, & + & -51.90710322947828104_dp, -54.76300160634039571_dp, -41.26325159838054901_dp, -58.62403790832019723_dp, & + & -28.62428016143747200_dp, -61.72174914658255318_dp, -13.98037118830461445_dp, -63.76341508305664263_dp ] + lonlat( 3001: 3120) = [ & + & 1.99999999999999090_dp, -64.48162218246977773_dp, 17.98037118830459491_dp, -63.76341508305664263_dp, & + & 32.62428016143745424_dp, -61.72174914658255318_dp, 45.26325159838053480_dp, -58.62403790832019723_dp, & + & 55.90710322947827393_dp, -54.76300160634039571_dp, 64.89227178252910733_dp, -50.38082342875217989_dp, & + & 72.61131289029847835_dp, -45.65520975111467550_dp, 79.40370496549049051_dp, -40.71192873614232610_dp, & + & 85.53547443418635510_dp, -35.64102404029981841_dp, 91.20945067766287195_dp, -30.50961194071436822_dp, & + & 96.58116190590757810_dp, -25.37072818781791383_dp, 101.77265218881962028_dp, -20.26925377022463692_dp, & + & 106.88281945262124850_dp, -15.24594064653214609_dp, 111.99465974304708027_dp, -10.34025377357218289_dp, & + & 117.18006743498143862_dp, -5.59247248797413921_dp, 122.50268451884780063_dp, -1.04529436283411759_dp, & + & 128.01906809075350679_dp, 3.25496397729521192_dp, 133.77826339847268855_dp, 7.25759760165332857_dp, & + & 139.81978239976487544_dp, 10.90731557455580436_dp, 146.17005258660478262_dp, 14.14485028302004999_dp, & + & 152.83768595785250000_dp, 16.90865838596958781_dp, 159.80845268931921055_dp, 19.13794220572576776_dp, & + & 167.04151372648448159_dp, 20.77699151155475477_dp, 174.46891148411387462_dp, 21.78042227826199095_dp, & + & -178.00000000000002842_dp, 11.90033356154828503_dp, -171.03550378237531504_dp, 11.56695807154089728_dp, & + & -164.15259832130166728_dp, 10.57562338306961180_dp, -157.42382961455143686_dp, 8.95164656051215069_dp, & + & -150.90567761562240889_dp, 6.73398631258200187_dp, -144.63474532961075170_dp, 3.97127289925851068_dp, & + & -138.62718020698264354_dp, 0.71762564121834338_dp, -132.88045216690298389_dp, -2.97103149043460757_dp, & + & -127.37626453526326031_dp, -7.03975713563777195_dp, -122.08348616854385682_dp, -11.43638098615609344_dp, & + & -116.96030366998827787_dp, -16.11238508810905401_dp, -111.95506961066591600_dp, -21.02304593053227677_dp, & + & -107.00543419047215821_dp, -26.12699149209596428_dp, -102.03522828445495918_dp, -31.38525381109429446_dp, & + & -96.94814060146437384_dp, -36.75977477892173084_dp, -91.61632617812561818_dp, -42.21112686087874266_dp, & + & -85.86029859026153588_dp, -47.69485768882572785_dp, -79.41299264536502278_dp, -53.15514845029933610_dp, & + & -71.85455169648275842_dp, -58.51289100414830102_dp, -62.49613000707146426_dp, -63.64173578517077345_dp, & + & -50.20367738593296281_dp, -68.31866526945105988_dp, -33.33636828365784055_dp, -72.13161644510194037_dp, & + & -10.78607194201629582_dp, -74.39304559691805707_dp, 14.78607194201627273_dp, -74.39304559691805707_dp, & + & 37.33636828365781213_dp, -72.13161644510194037_dp, 54.20367738593294149_dp, -68.31866526945105988_dp, & + & 66.49613000707145716_dp, -63.64173578517077345_dp, 75.85455169648274421_dp, -58.51289100414830102_dp, & + & 83.41299264536502278_dp, -53.15514845029933610_dp, 89.86029859026150746_dp, -47.69485768882574916_dp, & + & 95.61632617812561818_dp, -42.21112686087874266_dp, 100.94814060146437384_dp, -36.75977477892173084_dp, & + & 106.03522828445494497_dp, -31.38525381109429446_dp, 111.00543419047215821_dp, -26.12699149209597138_dp, & + & 115.95506961066591600_dp, -21.02304593053228388_dp, 120.96030366998827787_dp, -16.11238508810905756_dp ] + lonlat( 3121: 3240) = [ & + & 126.08348616854387103_dp, -11.43638098615609699_dp, 131.37626453526326031_dp, -7.03975713563777816_dp, & + & 136.88045216690298389_dp, -2.97103149043461379_dp, 142.62718020698264354_dp, 0.71762564121833694_dp, & + & 148.63474532961075170_dp, 3.97127289925851068_dp, 154.90567761562243732_dp, 6.73398631258200187_dp, & + & 161.42382961455140844_dp, 8.95164656051215069_dp, 168.15259832130169571_dp, 10.57562338306961180_dp, & + & 175.03550378237531504_dp, 11.56695807154089728_dp, -178.00000000000002842_dp, 0.81833312891115306_dp, & + & -171.37485237931247184_dp, 0.46152536384328174_dp, -164.82591385569978115_dp, -0.60006016949342578_dp, & + & -158.42170502724641779_dp, -2.34091526057736310_dp, -152.21700460768090579_dp, -4.72161659803607048_dp, & + & -146.24942513902283281_dp, -7.69266569608215889_dp, -140.53869867413175143_dp, -11.19861017149900739_dp, & + & -135.08801440266989857_dp, -15.18177122287804615_dp, -129.88641650799127092_dp, -19.58517424494497305_dp, & + & -124.91129483912591525_dp, -24.35455549606792047_dp, -120.13018318901130499_dp, -29.43951176908901957_dp, & + & -115.50120511177955507_dp, -34.79395188944583595_dp, -110.97138028465980142_dp, -40.37601690822956613_dp, & + & -106.47134737780879732_dp, -46.14758359783259323_dp, -101.90317607822893820_dp, -52.07335345715244301_dp, & + & -97.11263541171206271_dp, -58.11928872485960085_dp, -91.82057104813961246_dp, -64.24948773689963843_dp, & + & -85.42499621356618889_dp, -70.41813086842684299_dp, -76.27768438369074033_dp, -76.54083121758115738_dp, & + & -57.87324357773111672_dp, -82.33459525449163152_dp, 1.99999999999996114_dp, -85.78166687108868871_dp, & + & 61.87324357773107408_dp, -82.33459525449163152_dp, 80.27768438369074033_dp, -76.54083121758115738_dp, & + & 89.42499621356618889_dp, -70.41813086842684299_dp, 95.82057104813959825_dp, -64.24948773689963843_dp, & + & 101.11263541171206271_dp, -58.11928872485960085_dp, 105.90317607822892398_dp, -52.07335345715245722_dp, & + & 110.47134737780879732_dp, -46.14758359783259323_dp, 114.97138028465980142_dp, -40.37601690822956613_dp, & + & 119.50120511177955507_dp, -34.79395188944583595_dp, 124.13018318901127657_dp, -29.43951176908902667_dp, & + & 128.91129483912592946_dp, -24.35455549606792403_dp, 133.88641650799127092_dp, -19.58517424494497661_dp, & + & 139.08801440266989857_dp, -15.18177122287804970_dp, 144.53869867413172301_dp, -11.19861017149901272_dp, & + & 150.24942513902280439_dp, -7.69266569608215889_dp, 156.21700460768090579_dp, -4.72161659803607048_dp, & + & 162.42170502724644621_dp, -2.34091526057736310_dp, 168.82591385569978115_dp, -0.60006016949342578_dp, & + & 175.37485237931247184_dp, 0.46152536384328174_dp, -178.00000000000000000_dp, -11.06408152044379101_dp, & + & -171.33870321353637678_dp, -11.51266748991093891_dp, -164.78020742076523675_dp, -12.84550987892848539_dp, & + & -158.41822389716838870_dp, -15.02545489467462403_dp, -152.33085313434585828_dp, -17.99542592709287803_dp, & + & -146.57821873782722832_dp, -21.68429473238734673_dp, -141.20455296082769792_dp, -26.01294410398721979_dp, & + & -136.24424010430539056_dp, -30.89942669541638764_dp, -131.73150447900647464_dp, -36.26259289105208694_dp, & + & -127.71480673751507595_dp, -42.02394287044848653_dp, -124.28024682035200499_dp, -48.10755262562803125_dp ] + lonlat( 3241: 3360) = [ & + & -121.59604777627559713_dp, -54.43751136022502379_dp, -120.01228809564838684_dp, -60.93070105520797597_dp, & + & -120.32470998353322500_dp, -67.47696540683870126_dp, -124.60898756414846389_dp, -73.87349687490876704_dp, & + & -139.22778135121401988_dp, -79.54230031515007227_dp, -178.00000000000000000_dp, -82.33591847955618448_dp, & + & 143.22778135121404830_dp, -79.54230031515007227_dp, 128.60898756414846389_dp, -73.87349687490876704_dp, & + & 124.32470998353322500_dp, -67.47696540683870126_dp, 124.01228809564837263_dp, -60.93070105520797597_dp, & + & 125.59604777627561134_dp, -54.43751136022503090_dp, 128.28024682035200499_dp, -48.10755262562803125_dp, & + & 131.71480673751509016_dp, -42.02394287044850074_dp, 135.73150447900647464_dp, -36.26259289105208694_dp, & + & 140.24424010430539056_dp, -30.89942669541638764_dp, 145.20455296082769792_dp, -26.01294410398722334_dp, & + & 150.57821873782722832_dp, -21.68429473238734673_dp, 156.33085313434585828_dp, -17.99542592709287803_dp, & + & 162.41822389716838870_dp, -15.02545489467462403_dp, 168.78020742076520833_dp, -12.84550987892848539_dp, & + & 175.33870321353637678_dp, -11.51266748991093891_dp, -178.00000000000000000_dp, -23.60656536858034116_dp, & + & -172.31484199740026497_dp, -24.06074499849094650_dp, -166.76206145225543764_dp, -25.40835290264588053_dp, & + & -161.47033176340409000_dp, -27.60594661847371611_dp, -156.56387974300261590_dp, -30.58514629912493277_dp, & + & -152.16715015384193066_dp, -34.25717258827405942_dp, -148.41689072479888978_dp, -38.51667916926085411_dp, & + & -145.48413942807309240_dp, -43.24305393544465659_dp, -143.61056018478930696_dp, -48.29703252527734492_dp, & + & -143.16669319413330186_dp, -53.50931894776815057_dp, -144.73891790293430404_dp, -58.65522080377154168_dp, & + & -149.21912766680259210_dp, -63.40582055258879279_dp, -157.69810487935359333_dp, -67.25441721659638006_dp, & + & -170.53118545218148938_dp, -69.49384617858525814_dp, 174.53118545218146096_dp, -69.49384617858525814_dp, & + & 161.69810487935359333_dp, -67.25441721659638006_dp, 153.21912766680256368_dp, -63.40582055258877858_dp, & + & 148.73891790293430404_dp, -58.65522080377152747_dp, 147.16669319413330186_dp, -53.50931894776815767_dp, & + & 147.61056018478930696_dp, -48.29703252527734492_dp, 149.48413942807309240_dp, -43.24305393544464948_dp, & + & 152.41689072479886136_dp, -38.51667916926086122_dp, 156.16715015384193066_dp, -34.25717258827403811_dp, & + & 160.56387974300261590_dp, -30.58514629912493277_dp, 165.47033176340409000_dp, -27.60594661847371611_dp, & + & 170.76206145225546607_dp, -25.40835290264587698_dp, 176.31484199740026497_dp, -24.06074499849094295_dp, & + & -178.00000000000000000_dp, -36.54741287921620341_dp, -174.09065200901946469_dp, -36.97055608025468132_dp, & + & -170.42266827604018431_dp, -38.21219158951615213_dp, -167.24101930549508666_dp, -40.18968456371464271_dp, & + & -164.79950790329061761_dp, -42.76713193279059766_dp, -163.36621335583259906_dp, -45.75617589753742465_dp, & + & -163.22080918295719698_dp, -48.91487419033276751_dp, -164.62326642581021474_dp, -51.94637057143930292_dp, & + & -167.72066876086483944_dp, -54.50623527875517738_dp, -172.37525674045562596_dp, -56.23764348052387874_dp, & + & -178.00000000000000000_dp, -56.85258712078380228_dp, 176.37525674045562596_dp, -56.23764348052387874_dp ] + lonlat( 3361: 3376) = [ & + & 171.72066876086483944_dp, -54.50623527875517738_dp, 168.62326642581021474_dp, -51.94637057143930292_dp, & + & 167.22080918295719698_dp, -48.91487419033276751_dp, 167.36621335583259906_dp, -45.75617589753742465_dp, & + & 168.79950790329058918_dp, -42.76713193279060476_dp, 171.24101930549508666_dp, -40.18968456371464271_dp, & + & 174.42266827604018431_dp, -38.21219158951615213_dp, 178.09065200901943626_dp, -36.97055608025468132_dp ] + + grid = atlas_ReducedGaussianGrid(nx, projection=atlas_RotatedSchmidtProjection(stretch,centre,angle) ) + + jglo = 0 + do j = 1, grid%ny() + do i = 1, grid%nx(j) + FCTEST_CHECK_CLOSE( grid%lonlat(i,j), ([lonlat(2*jglo+1),lonlat(2*jglo+2)]), 1.e-10_dp ); + jglo = jglo + 1 + enddo + enddo + + call grid%final() +END_TEST + +! ----------------------------------------------------------------------------- + +END_TESTSUITE diff --git a/src/tests/grid/test_stretchedrotatedgaussian.cc b/src/tests/grid/test_stretchedrotatedgaussian.cc index c35ea1ef4..ee6399de1 100644 --- a/src/tests/grid/test_stretchedrotatedgaussian.cc +++ b/src/tests/grid/test_stretchedrotatedgaussian.cc @@ -2,19 +2,13 @@ #include #include "atlas/array.h" -#include "atlas/functionspace.h" #include "atlas/grid.h" -#include "atlas/library/Library.h" -#include "atlas/parallel/mpi/mpi.h" +#include "atlas/option.h" #include "atlas/util/Config.h" -#include "atlas/util/UnitSphere.h" -#include "eckit/types/FloatCompare.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::util; using namespace atlas::grid; -using namespace atlas; - namespace { @@ -869,46 +863,25 @@ const double lonlat_arp_t32c24[] = { namespace atlas { namespace test { CASE( "t31c2.4" ) { - atlas::StructuredGrid grid; - atlas::Projection proj; - - - const std::vector nx = {20, 27, 32, 40, 45, 48, 60, 60, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 60, 60, 48, 45, 40, 32, 27, 20}; + auto nx = std::vector{20, 27, 32, 40, 45, 48, 60, 60, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 60, 60, 48, 45, 40, 32, 27, 20}; - const double stretch = 2.4, centre[2] = {2.0, 46.7}; - - std::vector spacings( nx.size() ); - - for ( int i = 0; i < nx.size(); i++ ) { - double lonmax = 360.0 * double( nx[i] - 1 ) / double( nx[i] ); - spacings[i] = - atlas::grid::Spacing( atlas::util::Config( "type", "linear" ) | atlas::util::Config( "N", nx[i] ) | - atlas::util::Config( "start", 0 ) | atlas::util::Config( "end", lonmax ) ); - } + auto proj = Projection( "rotated_schmidt", Config( "stretching_factor", 2.4 ) | Config( "rotation_angle", 180.0 ) | + Config( "north_pole", {2.0, 46.7} ) ); - atlas::StructuredGrid::XSpace xspace( spacings ); - atlas::StructuredGrid::YSpace yspace( atlas::util::Config( "type", "gaussian" ) | - atlas::util::Config( "N", nx.size() ) ); + auto grid = ReducedGaussianGrid( nx, proj ); - proj = atlas::Projection( atlas::util::Config( "type", "rotated_schmidt" ) | - atlas::util::Config( "stretching_factor", stretch ) | - atlas::util::Config( "rotation_angle", 180.0 ) | - atlas::util::Config( "north_pole", std::vector{centre[0], centre[1]} ) ); - - grid = atlas::StructuredGrid( xspace, yspace, proj, atlas::Domain( atlas::util::Config( "type", "global" ) ) ); - - for ( int j = 0, jglo = 0; j < grid.ny(); j++ ) + for ( int j = 0, jglo = 0; j < grid.ny(); j++ ) { for ( int i = 0; i < grid.nx( j ); i++, jglo++ ) { + auto ll1 = PointLonLat( lonlat_arp_t32c24[2 * jglo + 0], lonlat_arp_t32c24[2 * jglo + 1] ); auto ll2 = grid.lonlat( i, j ); - PointXYZ p1, p2; - PointLonLat ll1 = PointLonLat( lonlat_arp_t32c24[2 * jglo + 0], lonlat_arp_t32c24[2 * jglo + 1] ); - atlas::util::UnitSphere::convertSphericalToCartesian( ll1, p1 ); - atlas::util::UnitSphere::convertSphericalToCartesian( ll2, p2 ); - EXPECT( is_approximately_equal( ll1.lon(), ll2.lon(), 0.001 ) ); - EXPECT( is_approximately_equal( ll1.lat(), ll2.lat(), 0.001 ) ); + EXPECT_APPROX_EQ( ll1.lon(), ll2.lon(), 1.e-10 ); + EXPECT_APPROX_EQ( ll1.lat(), ll2.lat(), 1.e-10 ); } + } } + + } // namespace test } // namespace atlas From 68a08b5e99ce45eafc577f684aad29fd015a0bc8 Mon Sep 17 00:00:00 2001 From: Philippe Marguinaud Date: Fri, 17 Apr 2020 08:58:49 +0000 Subject: [PATCH 080/145] Add Fortran bindings to gather/scatter fieldsets using a StructuredColumns object. --- .../detail/StructuredColumnsInterface.cc | 24 ++++++++++++-- .../detail/StructuredColumnsInterface.h | 8 +++-- ...functionspace_StructuredColumns_module.F90 | 33 +++++++++++++++---- 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/src/atlas/functionspace/detail/StructuredColumnsInterface.cc b/src/atlas/functionspace/detail/StructuredColumnsInterface.cc index b544462df..dbceb1359 100644 --- a/src/atlas/functionspace/detail/StructuredColumnsInterface.cc +++ b/src/atlas/functionspace/detail/StructuredColumnsInterface.cc @@ -69,7 +69,7 @@ const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__g return new detail::StructuredColumns( Grid( grid ), *vert, grid::Partitioner( partitioner ), *config ); } -void atlas__functionspace__StructuredColumns__gather( const detail::StructuredColumns* This, +void atlas__functionspace__StructuredColumns__gather_field( const detail::StructuredColumns* This, const field::FieldImpl* local, field::FieldImpl* global ) { ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_functionspace_StructuredColumns" ); ATLAS_ASSERT( global != nullptr, "Cannot access uninitialised atlas_Field" ); @@ -79,7 +79,7 @@ void atlas__functionspace__StructuredColumns__gather( const detail::StructuredCo This->gather( l, g ); } -void atlas__functionspace__StructuredColumns__scatter( const detail::StructuredColumns* This, +void atlas__functionspace__StructuredColumns__scatter_field( const detail::StructuredColumns* This, const field::FieldImpl* global, field::FieldImpl* local ) { ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_functionspace_StructuredColumns" ); ATLAS_ASSERT( global != nullptr, "Cannot access uninitialised atlas_Field" ); @@ -89,6 +89,26 @@ void atlas__functionspace__StructuredColumns__scatter( const detail::StructuredC This->scatter( g, l ); } +void atlas__functionspace__StructuredColumns__gather_fieldset( const detail::StructuredColumns* This, + const field::FieldSetImpl* local, field::FieldSetImpl* global ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_functionspace_StructuredColumns" ); + ATLAS_ASSERT( global != nullptr, "Cannot access uninitialised atlas_FieldSet" ); + ATLAS_ASSERT( local != nullptr, "Cannot access uninitialised atlas_FieldSet" ); + const FieldSet l (local); + FieldSet g (global); + This->gather (l, g); +} + +void atlas__functionspace__StructuredColumns__scatter_fieldset( const detail::StructuredColumns* This, + const field::FieldSetImpl* global, field::FieldSetImpl* local ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_functionspace_StructuredColumns" ); + ATLAS_ASSERT( global != nullptr, "Cannot access uninitialised atlas_FieldSet" ); + ATLAS_ASSERT( local != nullptr, "Cannot access uninitialised atlas_FieldSet" ); + const FieldSet g (global); + FieldSet l (local); + This->scatter (g, l); +} + void atlas__fs__StructuredColumns__checksum_fieldset( const detail::StructuredColumns* This, const field::FieldSetImpl* fieldset, char*& checksum, idx_t& size, int& allocated ) { diff --git a/src/atlas/functionspace/detail/StructuredColumnsInterface.h b/src/atlas/functionspace/detail/StructuredColumnsInterface.h index 31b4abc0b..565cad5b5 100644 --- a/src/atlas/functionspace/detail/StructuredColumnsInterface.h +++ b/src/atlas/functionspace/detail/StructuredColumnsInterface.h @@ -73,10 +73,14 @@ void atlas__functionspace__StructuredColumns__delete( detail::StructuredColumns* field::FieldImpl* atlas__fs__StructuredColumns__create_field( const detail::StructuredColumns* This, const eckit::Configuration* options ); -void atlas__functionspace__StructuredColumns__gather( const detail::StructuredColumns* This, +void atlas__functionspace__StructuredColumns__gather_field( const detail::StructuredColumns* This, const field::FieldImpl* local, field::FieldImpl* global ); -void atlas__functionspace__StructuredColumns__scatter( const detail::StructuredColumns* This, +void atlas__functionspace__StructuredColumns__scatter_field( const detail::StructuredColumns* This, const field::FieldImpl* global, field::FieldImpl* local ); +void atlas__functionspace__StructuredColumns__gather_fieldset( const detail::StructuredColumns* This, + const field::FieldSetImpl* local, field::FieldSetImpl* global ); +void atlas__functionspace__StructuredColumns__scatter_fieldset( const detail::StructuredColumns* This, + const field::FieldSetImpl* global, field::FieldSetImpl* local ); void atlas__fs__StructuredColumns__checksum_fieldset( const detail::StructuredColumns* This, const field::FieldSetImpl* fieldset, char*& checksum, idx_t& size, int& allocated ); diff --git a/src/atlas_f/functionspace/atlas_functionspace_StructuredColumns_module.F90 b/src/atlas_f/functionspace/atlas_functionspace_StructuredColumns_module.F90 index d2210ee4b..5af4184e8 100644 --- a/src/atlas_f/functionspace/atlas_functionspace_StructuredColumns_module.F90 +++ b/src/atlas_f/functionspace/atlas_functionspace_StructuredColumns_module.F90 @@ -62,8 +62,13 @@ module atlas_functionspace_StructuredColumns_module procedure, public :: assignment_operator_hook - procedure, public :: gather - procedure, public :: scatter + procedure, private :: gather_fieldset + procedure, private :: gather_field + generic, public :: gather => gather_fieldset, gather_field + + procedure, private :: scatter_fieldset + procedure, private :: scatter_field + generic, public :: scatter => scatter_fieldset, scatter_field procedure, private :: checksum_fieldset procedure, private :: checksum_field @@ -296,20 +301,36 @@ function ctor_grid_part_vertical(grid, partitioner, vertical, halo) result(this) end function -subroutine gather(this,local,global) +subroutine gather_field(this,local,global) use atlas_functionspace_StructuredColumns_c_binding class(atlas_functionspace_StructuredColumns), intent(in) :: this type(atlas_Field), intent(in) :: local type(atlas_Field), intent(inout) :: global - call atlas__functionspace__StructuredColumns__gather(this%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A) + call atlas__functionspace__StructuredColumns__gather_field(this%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A) +end subroutine + +subroutine gather_fieldset(this,local,global) + use atlas_functionspace_StructuredColumns_c_binding + class(atlas_functionspace_StructuredColumns), intent(in) :: this + type(atlas_FieldSet), intent(in) :: local + type(atlas_FieldSet), intent(inout) :: global + call atlas__functionspace__StructuredColumns__gather_fieldset(this%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A) end subroutine -subroutine scatter(this,global,local) +subroutine scatter_field(this,global,local) use atlas_functionspace_StructuredColumns_c_binding class(atlas_functionspace_StructuredColumns), intent(in) :: this type(atlas_Field), intent(in) :: global type(atlas_Field), intent(inout) :: local - call atlas__functionspace__StructuredColumns__scatter(this%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A) + call atlas__functionspace__StructuredColumns__scatter_field(this%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A) +end subroutine + +subroutine scatter_fieldset(this,global,local) + use atlas_functionspace_StructuredColumns_c_binding + class(atlas_functionspace_StructuredColumns), intent(in) :: this + type(atlas_FieldSet), intent(in) :: global + type(atlas_FieldSet), intent(inout) :: local + call atlas__functionspace__StructuredColumns__scatter_fieldset(this%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A) end subroutine function checksum_fieldset(this,fieldset) result(checksum) From 149a224d88b6d42e82c7bf4393293260708decf1 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 15 May 2020 11:58:28 +0100 Subject: [PATCH 081/145] Apply clang-format --- .../detail/StructuredColumnsInterface.cc | 22 ++++++++++--------- .../detail/StructuredColumnsInterface.h | 10 +++++---- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/atlas/functionspace/detail/StructuredColumnsInterface.cc b/src/atlas/functionspace/detail/StructuredColumnsInterface.cc index dbceb1359..c9f045d65 100644 --- a/src/atlas/functionspace/detail/StructuredColumnsInterface.cc +++ b/src/atlas/functionspace/detail/StructuredColumnsInterface.cc @@ -70,7 +70,7 @@ const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__g } void atlas__functionspace__StructuredColumns__gather_field( const detail::StructuredColumns* This, - const field::FieldImpl* local, field::FieldImpl* global ) { + const field::FieldImpl* local, field::FieldImpl* global ) { ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_functionspace_StructuredColumns" ); ATLAS_ASSERT( global != nullptr, "Cannot access uninitialised atlas_Field" ); ATLAS_ASSERT( local != nullptr, "Cannot access uninitialised atlas_Field" ); @@ -80,7 +80,7 @@ void atlas__functionspace__StructuredColumns__gather_field( const detail::Struct } void atlas__functionspace__StructuredColumns__scatter_field( const detail::StructuredColumns* This, - const field::FieldImpl* global, field::FieldImpl* local ) { + const field::FieldImpl* global, field::FieldImpl* local ) { ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_functionspace_StructuredColumns" ); ATLAS_ASSERT( global != nullptr, "Cannot access uninitialised atlas_Field" ); ATLAS_ASSERT( local != nullptr, "Cannot access uninitialised atlas_Field" ); @@ -90,23 +90,25 @@ void atlas__functionspace__StructuredColumns__scatter_field( const detail::Struc } void atlas__functionspace__StructuredColumns__gather_fieldset( const detail::StructuredColumns* This, - const field::FieldSetImpl* local, field::FieldSetImpl* global ) { + const field::FieldSetImpl* local, + field::FieldSetImpl* global ) { ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_functionspace_StructuredColumns" ); ATLAS_ASSERT( global != nullptr, "Cannot access uninitialised atlas_FieldSet" ); ATLAS_ASSERT( local != nullptr, "Cannot access uninitialised atlas_FieldSet" ); - const FieldSet l (local); - FieldSet g (global); - This->gather (l, g); + const FieldSet l( local ); + FieldSet g( global ); + This->gather( l, g ); } void atlas__functionspace__StructuredColumns__scatter_fieldset( const detail::StructuredColumns* This, - const field::FieldSetImpl* global, field::FieldSetImpl* local ) { + const field::FieldSetImpl* global, + field::FieldSetImpl* local ) { ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_functionspace_StructuredColumns" ); ATLAS_ASSERT( global != nullptr, "Cannot access uninitialised atlas_FieldSet" ); ATLAS_ASSERT( local != nullptr, "Cannot access uninitialised atlas_FieldSet" ); - const FieldSet g (global); - FieldSet l (local); - This->scatter (g, l); + const FieldSet g( global ); + FieldSet l( local ); + This->scatter( g, l ); } void atlas__fs__StructuredColumns__checksum_fieldset( const detail::StructuredColumns* This, diff --git a/src/atlas/functionspace/detail/StructuredColumnsInterface.h b/src/atlas/functionspace/detail/StructuredColumnsInterface.h index 565cad5b5..b16e9bd68 100644 --- a/src/atlas/functionspace/detail/StructuredColumnsInterface.h +++ b/src/atlas/functionspace/detail/StructuredColumnsInterface.h @@ -74,13 +74,15 @@ field::FieldImpl* atlas__fs__StructuredColumns__create_field( const detail::Stru const eckit::Configuration* options ); void atlas__functionspace__StructuredColumns__gather_field( const detail::StructuredColumns* This, - const field::FieldImpl* local, field::FieldImpl* global ); + const field::FieldImpl* local, field::FieldImpl* global ); void atlas__functionspace__StructuredColumns__scatter_field( const detail::StructuredColumns* This, - const field::FieldImpl* global, field::FieldImpl* local ); + const field::FieldImpl* global, field::FieldImpl* local ); void atlas__functionspace__StructuredColumns__gather_fieldset( const detail::StructuredColumns* This, - const field::FieldSetImpl* local, field::FieldSetImpl* global ); + const field::FieldSetImpl* local, + field::FieldSetImpl* global ); void atlas__functionspace__StructuredColumns__scatter_fieldset( const detail::StructuredColumns* This, - const field::FieldSetImpl* global, field::FieldSetImpl* local ); + const field::FieldSetImpl* global, + field::FieldSetImpl* local ); void atlas__fs__StructuredColumns__checksum_fieldset( const detail::StructuredColumns* This, const field::FieldSetImpl* fieldset, char*& checksum, idx_t& size, int& allocated ); From ffb31ef693cfc956b00c71159d51d6c7148d3f43 Mon Sep 17 00:00:00 2001 From: Benjamin Menetrier Date: Fri, 15 May 2020 13:29:32 +0100 Subject: [PATCH 082/145] ATLAS-291 (Github #47) Fortran bindings for Geometry and IndexKDTree Co-authored-by: Willem Deconinck --- src/atlas/CMakeLists.txt | 2 + src/atlas/util/Geometry.cc | 84 ++++ src/atlas/util/Geometry.h | 25 +- src/atlas/util/KDTree.cc | 125 ++++++ src/atlas/util/KDTree.h | 22 + src/atlas_f/CMakeLists.txt | 4 + src/atlas_f/atlas_module.F90 | 4 + src/atlas_f/util/atlas_Geometry_module.F90 | 222 ++++++++++ src/atlas_f/util/atlas_KDTree_module.F90 | 447 +++++++++++++++++++++ src/tests/util/CMakeLists.txt | 13 + src/tests/util/fctest_geometry.F90 | 203 ++++++++++ src/tests/util/fctest_kdtree.F90 | 316 +++++++++++++++ 12 files changed, 1465 insertions(+), 2 deletions(-) create mode 100644 src/atlas/util/Geometry.cc create mode 100644 src/atlas/util/KDTree.cc create mode 100644 src/atlas_f/util/atlas_Geometry_module.F90 create mode 100644 src/atlas_f/util/atlas_KDTree_module.F90 create mode 100644 src/tests/util/fctest_geometry.F90 create mode 100644 src/tests/util/fctest_kdtree.F90 diff --git a/src/atlas/CMakeLists.txt b/src/atlas/CMakeLists.txt index 8f1156123..6cc70d816 100644 --- a/src/atlas/CMakeLists.txt +++ b/src/atlas/CMakeLists.txt @@ -585,7 +585,9 @@ util/Constants.h util/Earth.h util/GaussianLatitudes.cc util/GaussianLatitudes.h +util/Geometry.cc util/Geometry.h +util/KDTree.cc util/KDTree.h util/PolygonXY.cc util/PolygonXY.h diff --git a/src/atlas/util/Geometry.cc b/src/atlas/util/Geometry.cc new file mode 100644 index 000000000..8f08711a9 --- /dev/null +++ b/src/atlas/util/Geometry.cc @@ -0,0 +1,84 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immGeometryies + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include "eckit/geometry/Point2.h" +#include "eckit/geometry/Point3.h" + +#include "atlas/runtime/Exception.h" +#include "atlas/util/Geometry.h" + +namespace atlas { + +extern "C" { +// ------------------------------------------------------------------ +// C wrapper interfaces to C++ routines +Geometry::Implementation* atlas__Geometry__new_name( const char* name ) { + Geometry::Implementation* geometry; + { + Geometry handle{std::string{name}}; + geometry = handle.get(); + geometry->attach(); + } + geometry->detach(); + return geometry; +} +geometry::detail::GeometryBase* atlas__Geometry__new_radius( const double radius ) { + Geometry::Implementation* geometry; + { + Geometry handle{radius}; + geometry = handle.get(); + geometry->attach(); + } + geometry->detach(); + return geometry; +} +void atlas__Geometry__delete( Geometry::Implementation* This ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_Geometry" ); + delete This; +} +void atlas__Geometry__xyz2lonlat( Geometry::Implementation* This, const double x, const double y, const double z, + double& lon, double& lat ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_Geometry" ); + PointLonLat lonlat; + This->xyz2lonlat( PointXYZ{x, y, z}, lonlat ); + lon = lonlat.lon(); + lat = lonlat.lat(); +} +void atlas__Geometry__lonlat2xyz( Geometry::Implementation* This, const double lon, const double lat, double& x, + double& y, double& z ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_Geometry" ); + PointXYZ xyz; + This->lonlat2xyz( PointLonLat{lon, lat}, xyz ); + x = xyz.x(); + y = xyz.y(); + z = xyz.z(); +} +double atlas__Geometry__distance_lonlat( Geometry::Implementation* This, const double lon1, const double lat1, + const double lon2, const double lat2 ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_Geometry" ); + return This->distance( PointLonLat{lon1, lat1}, PointLonLat{lon2, lat2} ); +} +double atlas__Geometry__distance_xyz( Geometry::Implementation* This, const double x1, const double y1, const double z1, + const double x2, const double y2, const double z2 ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_Geometry" ); + return This->distance( PointXYZ{x1, y1, z1}, PointXYZ{x2, y2, z2} ); +} +double atlas__Geometry__radius( Geometry::Implementation* This ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_Geometry" ); + return This->radius(); +} +double atlas__Geometry__area( Geometry::Implementation* This ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_Geometry" ); + return This->area(); +} +} +// ------------------------------------------------------------------ + +} // namespace atlas diff --git a/src/atlas/util/Geometry.h b/src/atlas/util/Geometry.h index 3bd70b992..4054a2462 100644 --- a/src/atlas/util/Geometry.h +++ b/src/atlas/util/Geometry.h @@ -27,7 +27,7 @@ namespace geometry { namespace detail { -class GeometryBase : util::Object { +class GeometryBase : public util::Object { public: virtual ~GeometryBase() = default; virtual void lonlat2xyz( const Point2&, Point3& ) const = 0; @@ -91,7 +91,7 @@ class GeometrySphere : public GeometryBase { //------------------------------------------------------------------------------------------------------ -class Geometry : util::ObjectHandle { +class Geometry : public util::ObjectHandle { public: using Handle::Handle; @@ -136,8 +136,29 @@ class Geometry : util::ObjectHandle { namespace geometry { using Earth = Geometry; // Sphere with util::Earth radius by default using UnitSphere = Geometry( eckit::geometry::UnitSphere() ); + } // namespace geometry + +// ------------------------------------------------------------------ +// C wrapper interfaces to C++ routines + +extern "C" { +Geometry::Implementation* atlas__Geometry__new_name( const char* name ); +Geometry::Implementation* atlas__Geometry__new_radius( const double radius ); +void atlas__Geometry__delete( Geometry::Implementation* This ); +void atlas__Geometry__xyz2lonlat( Geometry::Implementation* This, const double x, const double y, const double z, + double& lon, double& lat ); +void atlas__Geometry__lonlat2xyz( Geometry::Implementation* This, const double lon, const double lat, double& x, + double& y, double& z ); +double atlas__Geometry__distance_lonlat( Geometry::Implementation* This, const double lon1, const double lat1, + const double lon2, const double lat2 ); +double atlas__Geometry__distance_xyz( Geometry::Implementation* This, const double x1, const double y1, const double z1, + const double x2, const double y2, const double z2 ); +double atlas__Geometry__radius( Geometry::Implementation* This ); +double atlas__Geometry__area( Geometry::Implementation* This ); +} + //------------------------------------------------------------------------------------------------------ } // namespace atlas diff --git a/src/atlas/util/KDTree.cc b/src/atlas/util/KDTree.cc new file mode 100644 index 000000000..d819219a4 --- /dev/null +++ b/src/atlas/util/KDTree.cc @@ -0,0 +1,125 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include "eckit/geometry/Point3.h" + +#include "atlas/grid.h" +#include "atlas/runtime/Exception.h" +#include "atlas/util/KDTree.h" +#include "atlas/util/detail/KDTree.h" + +namespace atlas { +namespace util { + +using Handle = typename ObjectHandle>::Handle; +using Implementation = typename Handle::Implementation; +using Value = typename Implementation::Value; +using ValueList = typename Implementation::ValueList; + +// C wrapper interfaces to C++ routines +IndexKDTree::Implementation* atlas__IndexKDTree__new() { + IndexKDTree::Implementation* tree; + { + IndexKDTree handle; + tree = handle.get(); + tree->attach(); + } + tree->detach(); + return tree; +} + +IndexKDTree::Implementation* atlas__IndexKDTree__new_geometry( const Geometry::Implementation* geometry ) { + IndexKDTree::Implementation* tree; + { + IndexKDTree handle( Geometry{geometry} ); + tree = handle.get(); + tree->attach(); + } + tree->detach(); + return tree; +} + +void atlas__IndexKDTree__delete( IndexKDTree::Implementation* This ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_IndexKDTree" ); + delete This; +} +void atlas__IndexKDTree__reserve( IndexKDTree::Implementation* This, const idx_t size ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_IndexKDTree" ); + return This->reserve( size ); +} +void atlas__IndexKDTree__insert( IndexKDTree::Implementation* This, const double lon, const double lat, + const idx_t index ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_IndexKDTree" ); + return This->insert( PointLonLat{lon, lat}, index ); +} +void atlas__IndexKDTree__build( IndexKDTree::Implementation* This ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_IndexKDTree" ); + return This->build(); +} +void atlas__IndexKDTree__closestPoints( const IndexKDTree::Implementation* This, const double plon, const double plat, + const size_t k, double*& lons, double*& lats, idx_t*& indices, + double*& distances ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_IndexKDTree" ); + IndexKDTree::ValueList vl( This->closestPoints( PointLonLat{plon, plat}, k ) ); + lons = new double[k]; + lats = new double[k]; + indices = new idx_t[k]; + distances = new double[k]; + for ( size_t i = 0; i < k; ++i ) { + PointLonLat lonlat; + This->geometry().xyz2lonlat( vl[i].point(), lonlat ); + lonlat.normalise(); + lons[i] = lonlat.lon(); + lats[i] = lonlat.lat(); + indices[i] = vl[i].payload(); + distances[i] = vl[i].distance(); + } +} +void atlas__IndexKDTree__closestPoint( const IndexKDTree::Implementation* This, const double plon, const double plat, + double& lon, double& lat, idx_t& index, double& distance ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_IndexKDTree" ); + IndexKDTree::Value v( This->closestPoint( PointLonLat{plon, plat} ) ); + PointLonLat lonlat; + This->geometry().xyz2lonlat( v.point(), lonlat ); + lonlat.normalise(); + lon = lonlat.lon(); + lat = lonlat.lat(); + index = v.payload(); + distance = v.distance(); +} +void atlas__IndexKDTree__closestPointsWithinRadius( const IndexKDTree::Implementation* This, const double plon, + const double plat, double radius, size_t& k, double*& lons, + double*& lats, idx_t*& indices, double*& distances ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_IndexKDTree" ); + IndexKDTree::ValueList vl = This->closestPointsWithinRadius( PointLonLat{plon, plat}, radius ); + k = vl.size(); + lons = new double[k]; + lats = new double[k]; + indices = new idx_t[k]; + distances = new double[k]; + for ( size_t i = 0; i < k; ++i ) { + PointLonLat lonlat; + This->geometry().xyz2lonlat( vl[i].point(), lonlat ); + lonlat.normalise(); + lons[i] = lonlat.lon(); + lats[i] = lonlat.lat(); + indices[i] = vl[i].payload(); + distances[i] = vl[i].distance(); + } +} +const Geometry::Implementation* atlas__IndexKDTree__geometry( const IndexKDTree::Implementation* This ) { + ATLAS_ASSERT( This != nullptr, "Cannot access uninitialised atlas_IndexKDTree" ); + return This->geometry().get(); +} + +// ------------------------------------------------------------------ + +} // namespace util +} // namespace atlas diff --git a/src/atlas/util/KDTree.h b/src/atlas/util/KDTree.h index 8e19cde42..c8e0c35ab 100644 --- a/src/atlas/util/KDTree.h +++ b/src/atlas/util/KDTree.h @@ -174,6 +174,28 @@ using IndexKDTree2D = KDTree; // 2D search: implementation is us using IndexKDTree3D = KDTree; // 3D search: lonlat (2D) to xyz (3D) conversion is done internally using IndexKDTree = IndexKDTree3D; +// ------------------------------------------------------------------ +// C wrapper interfaces to C++ routines + +extern "C" { +IndexKDTree::Implementation* atlas__IndexKDTree__new(); +IndexKDTree::Implementation* atlas__IndexKDTree__new_geometry( const Geometry::Implementation* geometry ); +void atlas__IndexKDTree__delete( IndexKDTree::Implementation* This ); +void atlas__IndexKDTree__reserve( IndexKDTree::Implementation* This, const idx_t size ); +void atlas__IndexKDTree__insert( IndexKDTree::Implementation* This, const double lon, const double lat, + const idx_t index ); +void atlas__IndexKDTree__build( IndexKDTree::Implementation* This ); +void atlas__IndexKDTree__closestPoints( const IndexKDTree::Implementation* This, const double plon, const double plat, + const size_t k, double*& lon, double*& lat, idx_t*& indices, + double*& distances ); +void atlas__IndexKDTree__closestPoint( const IndexKDTree::Implementation* This, const double plon, const double plat, + double& lon, double& lat, idx_t& index, double& distance ); +void atlas__IndexKDTree__closestPointsWithinRadius( const IndexKDTree::Implementation* This, const double plon, + const double plat, const double radius, size_t& k, double*& lon, + double*& lat, idx_t*& indices, double*& distances ); +const Geometry::Implementation* atlas__IndexKDTree__geometry( const IndexKDTree::Implementation* This ); +} + //------------------------------------------------------------------------------------------------------ } // namespace util diff --git a/src/atlas_f/CMakeLists.txt b/src/atlas_f/CMakeLists.txt index 40dac75fb..b282927bd 100644 --- a/src/atlas_f/CMakeLists.txt +++ b/src/atlas_f/CMakeLists.txt @@ -133,6 +133,8 @@ generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/trans/detail/TransInterface. generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/util/Allocate.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/util/Metadata.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/util/Config.h) +generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/util/Geometry.h) +generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/util/KDTree.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/output/detail/GmshIO.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/parallel/HaloExchange.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/parallel/GatherScatter.h) @@ -151,6 +153,8 @@ ecbuild_add_library( TARGET atlas_f util/atlas_kinds_module.F90 util/atlas_JSON_module.F90 util/atlas_Config_module.F90 + util/atlas_Geometry_module.F90 + util/atlas_KDTree_module.F90 util/atlas_Metadata_module.F90 util/atlas_allocate_module.F90 output/atlas_output_module.F90 diff --git a/src/atlas_f/atlas_module.F90 b/src/atlas_f/atlas_module.F90 index c5207acd1..ce7ec777c 100644 --- a/src/atlas_f/atlas_module.F90 +++ b/src/atlas_f/atlas_module.F90 @@ -35,6 +35,10 @@ module atlas_module & atlas_config use atlas_Metadata_module, only: & & atlas_Metadata +use atlas_Geometry_module, only: & + & atlas_Geometry +use atlas_KDTree_module, only: & + & atlas_IndexKDTree use atlas_HybridElements_module, only: & & atlas_HybridElements use atlas_mesh_Edges_module, only: & diff --git a/src/atlas_f/util/atlas_Geometry_module.F90 b/src/atlas_f/util/atlas_Geometry_module.F90 new file mode 100644 index 000000000..747984a3c --- /dev/null +++ b/src/atlas_f/util/atlas_Geometry_module.F90 @@ -0,0 +1,222 @@ +! (C) Copyright 2013 ECMWF. +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +! In applying this licence, ECMWF does not waive the privileges and immunities +! granted to it by virtue of its status as an intergovernmental organisation nor +! does it submit to any jurisdiction. + +#include "atlas/atlas_f.h" + +module atlas_Geometry_module + +use, intrinsic :: iso_c_binding +use fckit_exception_module, only : fckit_exception +use fckit_owned_object_module, only : fckit_owned_object + +implicit none + +private :: fckit_owned_object + +public :: atlas_Geometry + +private + +!------------------------------------------------------------------------------ +TYPE, extends(fckit_owned_object) :: atlas_Geometry + +! Purpose : +! ------- +! *Geometry* : Container of Geometry + +! Methods : +! ------- + +! Author : +! ------ +! April-2020 Benjamin Menetrier *IRIT-JCSDA* + +!------------------------------------------------------------------------------ +contains + procedure, public :: delete => atlas_Geometry__delete + procedure :: xyz2lonlat_separate_coords => Geometry__xyz2lonlat_separate_coords + procedure :: xyz2lonlat_vectorized_coords => Geometry__xyz2lonlat_vectorized_coords + generic :: xyz2lonlat => xyz2lonlat_separate_coords, xyz2lonlat_vectorized_coords + procedure :: lonlat2xyz_separate_coords => Geometry__lonlat2xyz_separate_coords + procedure :: lonlat2xyz_vectorized_coords => Geometry__lonlat2xyz_vectorized_coords + generic :: lonlat2xyz => lonlat2xyz_separate_coords, lonlat2xyz_vectorized_coords + procedure :: distance_lonlat_separate_coords => Geometry__distance_lonlat_separate_coords + procedure :: distance_xyz_separate_coords => Geometry__distance_xyz_separate_coords + procedure :: distance_vectorized_coords => Geometry__distance_vectorized_coords + generic :: distance => distance_lonlat_separate_coords, distance_xyz_separate_coords, distance_vectorized_coords + procedure :: radius => Geometry__radius + procedure :: area => Geometry__area +#if FCKIT_FINAL_NOT_INHERITING + final :: atlas_Geometry__final_auto +#endif + +END TYPE atlas_Geometry + +!------------------------------------------------------------------------------ + +interface atlas_Geometry + module procedure atlas_Geometry__ctor_name + module procedure atlas_Geometry__ctor_radius +end interface + +!------------------------------------------------------------------------------ + + +!======================================================== +contains +!======================================================== + + +! ----------------------------------------------------------------------------- +! Geometry routines + +function atlas_Geometry__ctor_name(name) result(this) + use atlas_Geometry_c_binding + use fckit_c_interop_module + character(kind=c_char,len=*), intent(in) :: name + type(atlas_Geometry) :: this + call this%reset_c_ptr( atlas__Geometry__new_name( c_str(name) ) ) + call this%return() +end function atlas_Geometry__ctor_name + +function atlas_Geometry__ctor_radius(radius) result(this) + use atlas_Geometry_c_binding + use fckit_c_interop_module + real(c_double), intent(in) :: radius + type(atlas_Geometry) :: this + call this%reset_c_ptr( atlas__Geometry__new_radius( radius ) ) + call this%return() +end function atlas_Geometry__ctor_radius + +subroutine atlas_Geometry__delete(this) + use atlas_Geometry_c_binding + class(atlas_Geometry), intent(inout) :: this + if ( .not. this%is_null() ) then + call atlas__Geometry__delete(this%CPTR_PGIBUG_A) + end if + call this%reset_c_ptr() +end subroutine atlas_Geometry__delete + +subroutine Geometry__xyz2lonlat_separate_coords(this, x, y, z, lon, lat) + use atlas_Geometry_c_binding + use fckit_c_interop_module + class(atlas_Geometry), intent(in) :: this + real(c_double), intent(in) :: x + real(c_double), intent(in) :: y + real(c_double), intent(in) :: z + real(c_double), intent(out) :: lon + real(c_double), intent(out) :: lat + call atlas__Geometry__xyz2lonlat(this%CPTR_PGIBUG_A, x, y, z, lon, lat) +end subroutine Geometry__xyz2lonlat_separate_coords + +subroutine Geometry__xyz2lonlat_vectorized_coords(this, xyz, lonlat) + use atlas_Geometry_c_binding + use fckit_c_interop_module + class(atlas_Geometry), intent(in) :: this + real(c_double), intent(in) :: xyz(3) + real(c_double), intent(out) :: lonlat(2) + call atlas__Geometry__xyz2lonlat(this%CPTR_PGIBUG_A, xyz(1), xyz(2), xyz(3), lonlat(1), lonlat(2)) +end subroutine Geometry__xyz2lonlat_vectorized_coords + +subroutine Geometry__lonlat2xyz_separate_coords(this, lon, lat, x, y, z) + use atlas_Geometry_c_binding + use fckit_c_interop_module + class(atlas_Geometry), intent(in) :: this + real(c_double), intent(in) :: lon + real(c_double), intent(in) :: lat + real(c_double), intent(out) :: x + real(c_double), intent(out) :: y + real(c_double), intent(out) :: z + call atlas__Geometry__lonlat2xyz(this%CPTR_PGIBUG_A, lon, lat, x, y, z) +end subroutine Geometry__lonlat2xyz_separate_coords + +subroutine Geometry__lonlat2xyz_vectorized_coords(this, lonlat, xyz) + use atlas_Geometry_c_binding + use fckit_c_interop_module + class(atlas_Geometry), intent(in) :: this + real(c_double), intent(in) :: lonlat(2) + real(c_double), intent(out) :: xyz(3) + call atlas__Geometry__lonlat2xyz(this%CPTR_PGIBUG_A, lonlat(1), lonlat(2), xyz(1), xyz(2), xyz(3)) +end subroutine Geometry__lonlat2xyz_vectorized_coords + +function Geometry__distance_lonlat_separate_coords(this, lon1, lat1, lon2, lat2) result(distance_lonlat) + use atlas_Geometry_c_binding + use fckit_c_interop_module + class(atlas_Geometry), intent(in) :: this + real(c_double), intent(in) :: lon1 + real(c_double), intent(in) :: lat1 + real(c_double), intent(in) :: lon2 + real(c_double), intent(in) :: lat2 + real(c_double) :: distance_lonlat + distance_lonlat = atlas__Geometry__distance_lonlat(this%CPTR_PGIBUG_A, lon1, lat1, lon2, lat2) +end function Geometry__distance_lonlat_separate_coords + +function Geometry__distance_xyz_separate_coords(this, x1, y1, z1, x2, y2, z2) result(distance_xyz) + use atlas_Geometry_c_binding + use fckit_c_interop_module + class(atlas_Geometry), intent(in) :: this + real(c_double), intent(in) :: x1 + real(c_double), intent(in) :: y1 + real(c_double), intent(in) :: z1 + real(c_double), intent(in) :: x2 + real(c_double), intent(in) :: y2 + real(c_double), intent(in) :: z2 + real(c_double) :: distance_xyz + distance_xyz = atlas__Geometry__distance_xyz(this%CPTR_PGIBUG_A, x1, y1, z1, x2, y2, z2) +end function Geometry__distance_xyz_separate_coords + +function Geometry__distance_vectorized_coords(this, point1, point2) result(distance_point) + use atlas_Geometry_c_binding + use fckit_c_interop_module + class(atlas_Geometry), intent(in) :: this + real(c_double), intent(in) :: point1(:) + real(c_double), intent(in) :: point2(:) + real(c_double) :: distance_point + if ((size(point1)==2).and.(size(point2)==2)) then + ! Lon/lat distance + distance_point = atlas__Geometry__distance_lonlat(this%CPTR_PGIBUG_A, point1(1), point1(2), point2(1), point2(2)) + elseif ((size(point1)==3).and.(size(point2)==3)) then + ! XYZ distance + distance_point = atlas__Geometry__distance_xyz(this%CPTR_PGIBUG_A, point1(1), point1(2), point1(3), point2(1), point2(2), & + & point2(3)) + else + ! Abort + call fckit_exception%abort('wrong inputs for geometry distance with vectorized coordinates', 'altas_Geometry_module', 190) + end if +end function Geometry__distance_vectorized_coords + +function Geometry__radius(this) result(radius) + use atlas_Geometry_c_binding + use fckit_c_interop_module + class(atlas_Geometry), intent(in) :: this + real(c_double) :: radius + radius = atlas__Geometry__radius(this%CPTR_PGIBUG_A) +end function Geometry__radius + +function Geometry__area(this) result(area) + use atlas_Geometry_c_binding + use fckit_c_interop_module + class(atlas_Geometry), intent(in) :: this + real(c_double) :: area + area = atlas__Geometry__area(this%CPTR_PGIBUG_A) +end function Geometry__area + +!------------------------------------------------------------------------------- + +ATLAS_FINAL subroutine atlas_Geometry__final_auto(this) + type(atlas_Geometry), intent(inout) :: this +#if FCKIT_FINAL_DEBUGGING + write(0,*) "atlas_Geometry__final_auto" +#endif +#if FCKIT_FINAL_NOT_PROPAGATING + call this%final() +#endif + FCKIT_SUPPRESS_UNUSED( this ) +end subroutine + +end module atlas_Geometry_module diff --git a/src/atlas_f/util/atlas_KDTree_module.F90 b/src/atlas_f/util/atlas_KDTree_module.F90 new file mode 100644 index 000000000..327d8f622 --- /dev/null +++ b/src/atlas_f/util/atlas_KDTree_module.F90 @@ -0,0 +1,447 @@ +! (C) Copyright 2013 ECMWF. +! +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +! In applying this licence, ECMWF does not waive the privileges and immunities +! granted to it by virtue of its status as an intergovernmental organisation nor +! does it submit to any jurisdiction. + +#include "atlas/atlas_f.h" + +module atlas_KDTree_module + +use, intrinsic :: iso_c_binding +use fckit_owned_object_module, only : fckit_owned_object +use atlas_geometry_module + +implicit none + +private :: fckit_owned_object + +public :: atlas_IndexKDTree + +private + +!------------------------------------------------------------------------------ +TYPE, extends(fckit_owned_object) :: atlas_IndexKDTree + +! Purpose : +! ------- +! *IndexKDTree* : Container of IndexKDTree + +! Methods : +! ------- + +! Author : +! ------ +! April-2020 Benjamin Menetrier *IRIT-JCSDA* + +!------------------------------------------------------------------------------ +contains + procedure, public :: delete => atlas_IndexKDTree__delete + procedure :: reserve => IndexKDTree__reserve + procedure :: insert_separate_coords => IndexKDTree__insert_separate_coords + procedure :: insert_vectorized_coords => IndexKDTree__insert_vectorized_coords + generic :: insert => insert_separate_coords, insert_vectorized_coords + procedure :: build_only => IndexKDTree__build_only + procedure :: build_list_separate_coords => IndexKDTree__build_list_separate_coords + procedure :: build_list_vectorized_coords => IndexKDTree__build_list_vectorized_coords + generic :: build => build_only, build_list_separate_coords, build_list_vectorized_coords + procedure :: closestPoints_separate_coords => IndexKDTree__closestPoints_separate_coords + procedure :: closestPoints_vectorized_coords => IndexKDTree__closestPoints_vectorized_coords + generic :: closestPoints => closestPoints_separate_coords, closestPoints_vectorized_coords + procedure :: closestPoint_separate_coords => IndexKDTree__closestPoint_separate_coords + procedure :: closestPoint_vectorized_coords => IndexKDTree__closestPoint_vectorized_coords + generic :: closestPoint => closestPoint_separate_coords, closestPoint_vectorized_coords + procedure :: closestPointsWithinRadius_separate_coords => IndexKDTree__closestPointsWithinRadius_separate_coords + procedure :: closestPointsWithinRadius_vectorized_coords => IndexKDTree__closestPointsWithinRadius_vectorized_coords + generic :: closestPointsWithinRadius => closestPointsWithinRadius_separate_coords, closestPointsWithinRadius_vectorized_coords + procedure :: geometry => IndexKDTree__geometry +#if FCKIT_FINAL_NOT_INHERITING + final :: atlas_IndexKDTree__final_auto +#endif + +END TYPE atlas_IndexKDTree + +!------------------------------------------------------------------------------ + +interface atlas_IndexKDTree + module procedure atlas_IndexKDTree__ctor + module procedure atlas_IndexKDTree__ctor_geometry +end interface + +!------------------------------------------------------------------------------ + + +!======================================================== +contains +!======================================================== + + +! ----------------------------------------------------------------------------- +! IndexKDTree routines + +function atlas_IndexKDTree__ctor() result(this) + use atlas_KDTree_c_binding + use fckit_c_interop_module + type(atlas_IndexKDTree) :: this + call this%reset_c_ptr( atlas__IndexKDTree__new() ) + call this%return() +end function atlas_IndexKDTree__ctor + +function atlas_IndexKDTree__ctor_geometry(geometry) result(this) + use atlas_KDTree_c_binding + use fckit_c_interop_module + type(atlas_Geometry), intent(in) :: geometry + type(atlas_IndexKDTree) :: this + call this%reset_c_ptr( atlas__IndexKDTree__new_geometry( geometry%CPTR_PGIBUG_A ) ) + call this%return() +end function atlas_IndexKDTree__ctor_geometry + +subroutine atlas_IndexKDTree__delete(this) + use atlas_KDTree_c_binding + class(atlas_IndexKDTree), intent(inout) :: this + if ( .not. this%is_null() ) then + call atlas__IndexKDTree__delete(this%CPTR_PGIBUG_A) + end if + call this%reset_c_ptr() +end subroutine atlas_IndexKDTree__delete + +subroutine IndexKDTree__reserve(this, size) + use atlas_KDTree_c_binding + use fckit_c_interop_module + class(atlas_IndexKDTree), intent(in) :: this + integer(c_int), intent(in) :: size + call atlas__IndexKDTree__reserve(this%CPTR_PGIBUG_A, size) +end subroutine IndexKDTree__reserve + +subroutine IndexKDTree__insert_separate_coords(this, lon, lat, index) + use atlas_KDTree_c_binding + use fckit_c_interop_module + class(atlas_IndexKDTree), intent(in) :: this + real(c_double), intent(in) :: lon + real(c_double), intent(in) :: lat + integer(c_int), intent(in) :: index + call atlas__IndexKDTree__insert(this%CPTR_PGIBUG_A, lon, lat, index) +end subroutine IndexKDTree__insert_separate_coords + +subroutine IndexKDTree__insert_vectorized_coords(this, lonlat, index) + use atlas_KDTree_c_binding + use fckit_c_interop_module + class(atlas_IndexKDTree), intent(in) :: this + real(c_double), intent(in) :: lonlat(2) + integer(c_int), intent(in) :: index + call atlas__IndexKDTree__insert(this%CPTR_PGIBUG_A, lonlat(1), lonlat(2), index) +end subroutine IndexKDTree__insert_vectorized_coords + +subroutine IndexKDTree__build_only(this) + use atlas_KDTree_c_binding + use fckit_c_interop_module + class(atlas_IndexKDTree), intent(in) :: this + call atlas__IndexKDTree__build(this%CPTR_PGIBUG_A) +end subroutine IndexKDTree__build_only + +subroutine IndexKDTree__build_list_separate_coords(this, k, lons, lats, indices) + use atlas_KDTree_c_binding + use fckit_c_interop_module + class(atlas_IndexKDTree), intent(in) :: this + integer(c_int), intent(in) :: k + real(c_double), intent(in) :: lons(k) + real(c_double), intent(in) :: lats(k) + integer(c_int), intent(in), optional :: indices(k) + integer(c_int) :: i, index + do i = 1, k + if (present(indices)) then + index = indices(i) + else + index = i + end if + call this%insert(lons(i), lats(i), index) + end do + call this%build() +end subroutine IndexKDTree__build_list_separate_coords + +subroutine IndexKDTree__build_list_vectorized_coords(this, k, lonlats, indices) + use atlas_KDTree_c_binding + use fckit_c_interop_module + class(atlas_IndexKDTree), intent(in) :: this + integer(c_int), intent(in) :: k + real(c_double), intent(in) :: lonlats(k,2) + integer(c_int), intent(in), optional :: indices(k) + integer(c_int) :: i, index + do i = 1, k + if (present(indices)) then + index = indices(i) + else + index = i + end if + call this%insert(lonlats(i,:), index) + end do + call this%build() +end subroutine IndexKDTree__build_list_vectorized_coords + +subroutine IndexKDTree__closestPoints_separate_coords(this, plon, plat, k, indices, distances, lons, lats) + use atlas_KDTree_c_binding + use, intrinsic :: iso_c_binding, only : c_f_pointer + use fckit_c_interop_module + class(atlas_IndexKDTree), intent(in) :: this + real(c_double), intent(in) :: plon + real(c_double), intent(in) :: plat + integer(c_int), intent(in) :: k + integer(c_int), intent(out) :: indices(k) + real(c_double), intent(out), optional :: distances(k) + real(c_double), intent(out), optional :: lons(k) + real(c_double), intent(out), optional :: lats(k) + type(c_ptr) :: indices_cptr + type(c_ptr) :: distances_cptr + type(c_ptr) :: lons_cptr + type(c_ptr) :: lats_cptr + integer(c_int), pointer :: indices_fptr(:) + real(c_double), pointer :: distances_fptr(:) + real(c_double), pointer :: lons_fptr(:) + real(c_double), pointer :: lats_fptr(:) + if ( k > 0 ) then + call atlas__IndexKDTree__closestPoints(this%CPTR_PGIBUG_A, plon, plat, int(k, c_size_t), lons_cptr, lats_cptr, indices_cptr, & + & distances_cptr) + call c_f_pointer(indices_cptr, indices_fptr, (/k/)) + indices(:) = indices_fptr(:) + if (present(distances)) then + call c_f_pointer(distances_cptr, distances_fptr, (/k/)) + distances(:) = distances_fptr(:) + end if + if (present(lons)) then + call c_f_pointer(lons_cptr, lons_fptr, (/k/)) + lons(:) = lons_fptr(:) + end if + if (present(lats)) then + call c_f_pointer(lats_cptr, lats_fptr, (/k/)) + lats(:) = lats_fptr(:) + end if + call c_ptr_free(lons_cptr) + call c_ptr_free(lats_cptr) + call c_ptr_free(indices_cptr) + call c_ptr_free(distances_cptr) + end if +end subroutine IndexKDTree__closestPoints_separate_coords + +subroutine IndexKDTree__closestPoints_vectorized_coords(this, point, k, indices, distances, lonlats) + use atlas_KDTree_c_binding + use, intrinsic :: iso_c_binding, only : c_f_pointer + use fckit_c_interop_module + class(atlas_IndexKDTree), intent(in) :: this + real(c_double), intent(in) :: point(2) + integer(c_int), intent(in) :: k + integer(c_int), intent(out) :: indices(k) + real(c_double), intent(out), optional :: distances(k) + real(c_double), intent(out), optional :: lonlats(k,2) + type(c_ptr) :: indices_cptr + type(c_ptr) :: distances_cptr + type(c_ptr) :: lons_cptr + type(c_ptr) :: lats_cptr + integer(c_int), pointer :: indices_fptr(:) + real(c_double), pointer :: distances_fptr(:) + real(c_double), pointer :: lons_fptr(:) + real(c_double), pointer :: lats_fptr(:) + if ( k > 0 ) then + call atlas__IndexKDTree__closestPoints(this%CPTR_PGIBUG_A, point(1), point(2), int(k, c_size_t), lons_cptr, lats_cptr, & + & indices_cptr, distances_cptr) + call c_f_pointer(indices_cptr, indices_fptr, (/k/)) + indices(:) = indices_fptr(:) + if (present(distances)) then + call c_f_pointer(distances_cptr, distances_fptr, (/k/)) + distances(:) = distances_fptr(:) + end if + if (present(lonlats)) then + call c_f_pointer(lons_cptr, lons_fptr, (/k/)) + call c_f_pointer(lats_cptr, lats_fptr, (/k/)) + lonlats(:,1) = lons_fptr(:) + lonlats(:,2) = lats_fptr(:) + end if + call c_ptr_free(lons_cptr) + call c_ptr_free(lats_cptr) + call c_ptr_free(indices_cptr) + call c_ptr_free(distances_cptr) + end if +end subroutine IndexKDTree__closestPoints_vectorized_coords + +subroutine IndexKDTree__closestPoint_separate_coords(this, plon, plat, index, distance, lon, lat) + use atlas_KDTree_c_binding + use fckit_c_interop_module + class(atlas_IndexKDTree), intent(in) :: this + real(c_double), intent(in) :: plon + real(c_double), intent(in) :: plat + integer(c_int), intent(out) :: index + real(c_double), intent(out), optional :: distance + real(c_double), intent(out), optional :: lon + real(c_double), intent(out), optional :: lat + real(c_double) :: distance_tmp, lon_tmp, lat_tmp + call atlas__IndexKDTree__closestPoint(this%CPTR_PGIBUG_A, plon, plat, lon_tmp, lat_tmp, index, distance_tmp) + if (present(distance)) distance = distance_tmp + if (present(lon)) lon = lon_tmp + if (present(lat)) lat = lat_tmp +end subroutine IndexKDTree__closestPoint_separate_coords + +subroutine IndexKDTree__closestPoint_vectorized_coords(this, point, index, distance, lonlat) + use atlas_KDTree_c_binding + use fckit_c_interop_module + class(atlas_IndexKDTree), intent(in) :: this + real(c_double), intent(in) :: point(2) + integer(c_int), intent(out) :: index + real(c_double), intent(out), optional :: distance + real(c_double), intent(out), optional :: lonlat(2) + real(c_double) :: distance_tmp, lon_tmp, lat_tmp + call atlas__IndexKDTree__closestPoint(this%CPTR_PGIBUG_A, point(1), point(2), lon_tmp, lat_tmp, index, distance_tmp) + if (present(distance)) distance = distance_tmp + if (present(lonlat)) then + lonlat(1) = lon_tmp + lonlat(2) = lat_tmp + end if +end subroutine IndexKDTree__closestPoint_vectorized_coords + +subroutine IndexKDTree__closestPointsWithinRadius_separate_coords(this, plon, plat, radius, k, indices, distances, lons, lats) + use atlas_KDTree_c_binding + use, intrinsic :: iso_c_binding, only : c_f_pointer + use fckit_c_interop_module + class(atlas_IndexKDTree), intent(in) :: this + real(c_double), intent(in) :: plon + real(c_double), intent(in) :: plat + real(c_double), intent(in) :: radius + integer(c_int), intent(out) :: k + integer(c_int), allocatable, intent(inout), optional :: indices(:) + real(c_double), allocatable, intent(inout), optional :: distances(:) + real(c_double), allocatable, intent(inout), optional :: lons(:) + real(c_double), allocatable, intent(inout), optional :: lats(:) + integer(c_size_t) :: k_tmp + type(c_ptr) :: lons_cptr + type(c_ptr) :: lats_cptr + type(c_ptr) :: indices_cptr + type(c_ptr) :: distances_cptr + real(c_double), pointer :: lons_fptr(:) + real(c_double), pointer :: lats_fptr(:) + integer(c_int), pointer :: indices_fptr(:) + real(c_double), pointer :: distances_fptr(:) + call atlas__IndexKDTree__closestPointsWithinRadius(this%CPTR_PGIBUG_A, plon, plat, radius, & + & k_tmp, lons_cptr, lats_cptr, indices_cptr, distances_cptr) + k = int(k_tmp, c_int) + if (present(indices)) then + if (allocated(indices)) deallocate(indices) + allocate(indices(k)) + end if + if (present(distances)) then + if (allocated(distances)) deallocate(distances) + allocate(distances(k)) + end if + if (present(lons)) then + if (allocated(lons)) deallocate(lons) + allocate(lons(k)) + end if + if (present(lats)) then + if (allocated(lats)) deallocate(lats) + allocate(lats(k)) + end if + if ( k > 0 ) then + if (present(indices)) then + call c_f_pointer(indices_cptr, indices_fptr, (/k/)) + indices(:) = indices_fptr(:) + end if + if (present(distances)) then + call c_f_pointer(distances_cptr, distances_fptr, (/k/)) + distances(:) = distances_fptr(:) + end if + if (present(lons)) then + call c_f_pointer(lons_cptr, lons_fptr, (/k/)) + lons(:) = lons_fptr(:) + end if + if (present(lats)) then + call c_f_pointer(lats_cptr, lats_fptr, (/k/)) + lats(:) = lats_fptr(:) + end if + end if + call c_ptr_free(lons_cptr) + call c_ptr_free(lats_cptr) + call c_ptr_free(indices_cptr) + call c_ptr_free(distances_cptr) +end subroutine IndexKDTree__closestPointsWithinRadius_separate_coords + +subroutine IndexKDTree__closestPointsWithinRadius_vectorized_coords(this, point, radius, k, indices, distances, lonlats) + use atlas_KDTree_c_binding + use, intrinsic :: iso_c_binding, only : c_f_pointer + use fckit_c_interop_module + class(atlas_IndexKDTree), intent(in) :: this + real(c_double), intent(in) :: point(2) + real(c_double), intent(in) :: radius + integer(c_int), intent(out) :: k + integer(c_int), allocatable, intent(inout), optional :: indices(:) + real(c_double), allocatable, intent(inout), optional :: distances(:) + real(c_double), allocatable, intent(inout), optional :: lonlats(:,:) + integer(c_size_t) :: k_tmp + type(c_ptr) :: lons_cptr + type(c_ptr) :: lats_cptr + type(c_ptr) :: indices_cptr + type(c_ptr) :: distances_cptr + real(c_double), pointer :: lons_fptr(:) + real(c_double), pointer :: lats_fptr(:) + integer(c_int), pointer :: indices_fptr(:) + real(c_double), pointer :: distances_fptr(:) + call atlas__IndexKDTree__closestPointsWithinRadius(this%CPTR_PGIBUG_A, point(1), point(2), radius, & + & k_tmp, lons_cptr, lats_cptr, indices_cptr, distances_cptr) + k = int(k_tmp, c_int) + if (present(indices)) then + if (allocated(indices)) deallocate(indices) + allocate(indices(k)) + end if + if (present(distances)) then + if (allocated(distances)) deallocate(distances) + allocate(distances(k)) + end if + if (present(lonlats)) then + if (allocated(lonlats)) deallocate(lonlats) + allocate(lonlats(k,2)) + end if + if ( k > 0 ) then + if (present(indices)) then + call c_f_pointer(indices_cptr, indices_fptr, (/k/)) + indices(:) = indices_fptr(:) + end if + if (present(distances)) then + call c_f_pointer(distances_cptr, distances_fptr, (/k/)) + distances(:) = distances_fptr(:) + end if + if (present(lonlats)) then + call c_f_pointer(lons_cptr, lons_fptr, (/k/)) + call c_f_pointer(lats_cptr, lats_fptr, (/k/)) + lonlats(:,1) = lons_fptr(:) + lonlats(:,2) = lats_fptr(:) + end if + end if + call c_ptr_free(lons_cptr) + call c_ptr_free(lats_cptr) + call c_ptr_free(indices_cptr) + call c_ptr_free(distances_cptr) +end subroutine IndexKDTree__closestPointsWithinRadius_vectorized_coords + +function IndexKDTree__geometry(this) result(geometry) + use atlas_KDTree_c_binding + use fckit_c_interop_module + class(atlas_IndexKDTree), intent(in) :: this + type(atlas_Geometry) :: geometry + call geometry%reset_c_ptr( atlas__IndexKDTree__geometry( this%CPTR_PGIBUG_A ) ) + call geometry%return() +end function IndexKDTree__geometry + +!------------------------------------------------------------------------------- + +#if FCKIT_FINAL_NOT_INHERITING +ATLAS_FINAL subroutine atlas_IndexKDTree__final_auto(this) + type(atlas_IndexKDTree), intent(inout) :: this +#if FCKIT_FINAL_DEBUGGING + write(0,*) "atlas_IndexKDTree__final_auto" +#endif +#if FCKIT_FINAL_NOT_PROPAGATING + call this%final() +#endif + FCKIT_SUPPRESS_UNUSED( this ) +end subroutine +#endif + +end module atlas_KDTree_module diff --git a/src/tests/util/CMakeLists.txt b/src/tests/util/CMakeLists.txt index c3e9f236b..3631e3193 100644 --- a/src/tests/util/CMakeLists.txt +++ b/src/tests/util/CMakeLists.txt @@ -31,6 +31,19 @@ if( HAVE_FCTEST ) ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) + add_fctest( TARGET atlas_fctest_geometry + LINKER_LANGUAGE Fortran + SOURCES fctest_geometry.F90 + LIBS atlas_f + ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} + ) + + add_fctest( TARGET atlas_fctest_kdtree + LINKER_LANGUAGE Fortran + SOURCES fctest_kdtree.F90 + LIBS atlas_f + ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} + ) endif() foreach( test earth flags footprint indexview polygon point ) diff --git a/src/tests/util/fctest_geometry.F90 b/src/tests/util/fctest_geometry.F90 new file mode 100644 index 000000000..4a1063ee8 --- /dev/null +++ b/src/tests/util/fctest_geometry.F90 @@ -0,0 +1,203 @@ +! (C) Copyright 2013 ECMWF. +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +! In applying this licence, ECMWF does not waive the privileges and immunities +! granted to it by virtue of its status as an intergovernmental organisation nor +! does it submit to any jurisdiction. + +! This File contains Unit Tests for testing the +! C++ / Fortran Interfaces to the Geometry Datastructure +! @author Benjamin Menetrier + +#include "fckit/fctest.h" +#include "atlas/atlas_f.h" + +! ----------------------------------------------------------------------------- + +module fcta_Geometry_fixture +use atlas_module +use, intrinsic :: iso_c_binding +implicit none +end module fcta_Geometry_fixture + +! ----------------------------------------------------------------------------- + +TESTSUITE_WITH_FIXTURE(fctest_atlas_Geometry,fcta_Geometry_fixture) + +! ----------------------------------------------------------------------------- + +TESTSUITE_INIT + call atlas_library%initialise() +END_TESTSUITE_INIT + +! ----------------------------------------------------------------------------- + +TESTSUITE_FINALIZE + use fckit_main_module + call atlas_library%finalise() +END_TESTSUITE_FINALIZE + +! ----------------------------------------------------------------------------- + +TEST( test_geometry ) +use fckit_log_module +use fckit_c_interop_module +implicit none + + type(atlas_Geometry) :: geometry + real(c_double) :: lonlat1(2), lonlat2(2), lonlat(2) + real(c_double) :: xyz1(3), xyz2(3), xyz(3) + real(c_double) :: distance + + write(*,*) "test_geometry for UnitSphere starting" + + ! Check constructor + geometry = atlas_Geometry("UnitSphere") + write(0,*) "geometry%c_ptr() = ", c_ptr_to_loc(geometry%CPTR_PGIBUG_A) + + ! Define points + lonlat1 = (/-71.6_c_double, -33._c_double/) + lonlat2 = (/121.8_c_double, 31.4_c_double/) + xyz1 = (/2.647e-1_c_double, -7.958e-1_c_double, -5.446e-1_c_double/) + xyz2 = (/-4.498e-1_c_double, 7.254e-1_c_double, 5.210e-1_c_double/) + + ! Check conversion from cartesian to spherical + call geometry%xyz2lonlat(xyz1(1), xyz1(2), xyz1(3), lonlat(1), lonlat(2)) + FCTEST_CHECK_CLOSE( lonlat(1) , lonlat1(1), 1.e-2_c_double ) + FCTEST_CHECK_CLOSE( lonlat(2) , lonlat1(2), 1.e-2_c_double ) + call geometry%xyz2lonlat(xyz1, lonlat) + FCTEST_CHECK_CLOSE( lonlat(1) , lonlat1(1), 1.e-2_c_double ) + FCTEST_CHECK_CLOSE( lonlat(2) , lonlat1(2), 1.e-2_c_double ) + + ! Check conversion from spherical to cartesian + call geometry%lonlat2xyz(lonlat1(1), lonlat1(2), xyz(1), xyz(2), xyz(3)) + FCTEST_CHECK_CLOSE( xyz(1) , xyz1(1) , 1.e3_c_double ) + FCTEST_CHECK_CLOSE( xyz(2) , xyz1(2) , 1.e3_c_double ) + FCTEST_CHECK_CLOSE( xyz(3) , xyz1(3) , 1.e3_c_double ) + call geometry%lonlat2xyz(lonlat1, xyz) + FCTEST_CHECK_CLOSE( xyz(1) , xyz1(1) , 1.e3_c_double ) + FCTEST_CHECK_CLOSE( xyz(2) , xyz1(2) , 1.e3_c_double ) + FCTEST_CHECK_CLOSE( xyz(3) , xyz1(3) , 1.e3_c_double ) + + ! Check distance + distance = geometry%distance(lonlat1(1), lonlat1(2), lonlat2(1), lonlat2(2)) + FCTEST_CHECK_CLOSE( distance , 2.942_c_double , 1.e-3_c_double ) + distance = geometry%distance(lonlat1, lonlat2) + FCTEST_CHECK_CLOSE( distance , 2.942_c_double , 1.e-3_c_double ) + distance = geometry%distance(xyz1(1), xyz1(2), xyz1(3), xyz2(1), xyz2(2), xyz2(3)) + FCTEST_CHECK_CLOSE( distance , 2.942_c_double , 1.e-3_c_double ) + distance = geometry%distance(xyz1, xyz2) + FCTEST_CHECK_CLOSE( distance , 2.942_c_double , 1.e-3_c_double ) + + ! Check radius + FCTEST_CHECK_EQUAL( geometry%radius() , 1.0_c_double ) + + ! Check area + FCTEST_CHECK_CLOSE( geometry%area() , 1.257e1_c_double , 1.e-2_c_double ) + + ! Finalization + call geometry%final() + + write(*,*) "test_geometry for Earth starting" + + ! Check constructor for Earth + geometry = atlas_Geometry("Earth") + write(0,*) "geometry%c_ptr() = ", c_ptr_to_loc(geometry%CPTR_PGIBUG_A) + + ! Define points + lonlat1 = (/-71.6_c_double, -33._c_double/) + lonlat2 = (/121.8_c_double, 31.4_c_double/) + xyz1 = (/1.687e6_c_double, -5.070e6_c_double, -3.470e6_c_double/) + xyz2 = (/-2.866e6_c_double, 4.622e6_c_double, 3.319e6_c_double/) + + ! Check conversion from cartesian to spherical + call geometry%xyz2lonlat(xyz1(1), xyz1(2), xyz1(3), lonlat(1), lonlat(2)) + FCTEST_CHECK_CLOSE( lonlat(1) , lonlat1(1), 1.e-2_c_double ) + FCTEST_CHECK_CLOSE( lonlat(2) , lonlat1(2), 1.e-2_c_double ) + call geometry%xyz2lonlat(xyz1, lonlat) + FCTEST_CHECK_CLOSE( lonlat(1) , lonlat1(1), 1.e-2_c_double ) + FCTEST_CHECK_CLOSE( lonlat(2) , lonlat1(2), 1.e-2_c_double ) + + ! Check conversion from spherical to cartesian + call geometry%lonlat2xyz(lonlat1(1), lonlat1(2), xyz(1), xyz(2), xyz(3)) + FCTEST_CHECK_CLOSE( xyz(1) , xyz1(1) , 1.e3_c_double ) + FCTEST_CHECK_CLOSE( xyz(2) , xyz1(2) , 1.e3_c_double ) + FCTEST_CHECK_CLOSE( xyz(3) , xyz1(3) , 1.e3_c_double ) + call geometry%lonlat2xyz(lonlat1, xyz) + FCTEST_CHECK_CLOSE( xyz(1) , xyz1(1) , 1.e3_c_double ) + FCTEST_CHECK_CLOSE( xyz(2) , xyz1(2) , 1.e3_c_double ) + FCTEST_CHECK_CLOSE( xyz(3) , xyz1(3) , 1.e3_c_double ) + + ! Check distance + distance = geometry%distance(lonlat1(1), lonlat1(2), lonlat2(1), lonlat2(2)) + FCTEST_CHECK_CLOSE( distance , 1.874e7_c_double , 1.e4_c_double ) + distance = geometry%distance(lonlat1, lonlat2) + FCTEST_CHECK_CLOSE( distance , 1.874e7_c_double , 1.e4_c_double ) + distance = geometry%distance(xyz1(1), xyz1(2), xyz1(3), xyz2(1), xyz2(2), xyz2(3)) + FCTEST_CHECK_CLOSE( distance , 1.874e7_c_double , 1.e4_c_double ) + distance = geometry%distance(xyz1, xyz2) + FCTEST_CHECK_CLOSE( distance , 1.874e7_c_double , 1.e4_c_double ) + + ! Check radius + FCTEST_CHECK_EQUAL( geometry%radius() , 6371229.0_c_double ) + + ! Check area + FCTEST_CHECK_CLOSE( geometry%area() , 5.101e14_c_double , 1.e11_c_double ) + + ! Finalization + call geometry%final() + + write(*,*) "test_geometry for another planet (Mars here) starting" + + ! Check constructor for another planet (Mars here) + geometry = atlas_Geometry( 3389500.0_c_double ) + write(0,*) "geometry%c_ptr() = ", c_ptr_to_loc(geometry%CPTR_PGIBUG_A) + + ! Define points + lonlat1 = (/-71.6_c_double, -33._c_double/) + lonlat2 = (/121.8_c_double, 31.4_c_double/) + xyz1 = (/8.973e5_c_double, -2.697e6_c_double, -1.846e6_c_double/) + xyz2 = (/-1.525e6_c_double, 2.459e6_c_double, 1.766e6_c_double/) + + ! Check conversion from cartesian to spherical + call geometry%xyz2lonlat(xyz1(1), xyz1(2), xyz1(3), lonlat(1), lonlat(2)) + FCTEST_CHECK_CLOSE( lonlat(1) , lonlat1(1), 1.e-2_c_double ) + FCTEST_CHECK_CLOSE( lonlat(2) , lonlat1(2), 1.e-2_c_double ) + call geometry%xyz2lonlat(xyz1, lonlat) + FCTEST_CHECK_CLOSE( lonlat(1) , lonlat1(1), 1.e-2_c_double ) + FCTEST_CHECK_CLOSE( lonlat(2) , lonlat1(2), 1.e-2_c_double ) + + ! Check conversion from spherical to cartesian + call geometry%lonlat2xyz(lonlat1(1), lonlat1(2), xyz(1), xyz(2), xyz(3)) + FCTEST_CHECK_CLOSE( xyz(1) , xyz1(1) , 1.e3_c_double ) + FCTEST_CHECK_CLOSE( xyz(2) , xyz1(2) , 1.e3_c_double ) + FCTEST_CHECK_CLOSE( xyz(3) , xyz1(3) , 1.e3_c_double ) + call geometry%lonlat2xyz(lonlat1, xyz) + FCTEST_CHECK_CLOSE( xyz(1) , xyz1(1) , 1.e3_c_double ) + FCTEST_CHECK_CLOSE( xyz(2) , xyz1(2) , 1.e3_c_double ) + FCTEST_CHECK_CLOSE( xyz(3) , xyz1(3) , 1.e3_c_double ) + + ! Check distance + distance = geometry%distance(lonlat1(1), lonlat1(2), lonlat2(1), lonlat2(2)) + FCTEST_CHECK_CLOSE( distance , 9.971e6_c_double , 1.e3_c_double ) + distance = geometry%distance(lonlat1, lonlat2) + FCTEST_CHECK_CLOSE( distance , 9.971e6_c_double , 1.e3_c_double ) + distance = geometry%distance(xyz1(1), xyz1(2), xyz1(3), xyz2(1), xyz2(2), xyz2(3)) + FCTEST_CHECK_CLOSE( distance , 9.971e6_c_double , 1.e3_c_double ) + distance = geometry%distance(xyz1, xyz2) + FCTEST_CHECK_CLOSE( distance , 9.971e6_c_double , 1.e3_c_double ) + + ! Check radius + FCTEST_CHECK_EQUAL( geometry%radius() , 3389500.0_c_double ) + + ! Check area + FCTEST_CHECK_CLOSE( geometry%area() , 1.444e14_c_double , 1.e11_c_double ) + + ! Finalization + call geometry%final() + +END_TEST +! ----------------------------------------------------------------------------- + +END_TESTSUITE + diff --git a/src/tests/util/fctest_kdtree.F90 b/src/tests/util/fctest_kdtree.F90 new file mode 100644 index 000000000..980d3a8fd --- /dev/null +++ b/src/tests/util/fctest_kdtree.F90 @@ -0,0 +1,316 @@ +! (C) Copyright 2013 ECMWF. +! This software is licensed under the terms of the Apache Licence Version 2.0 +! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +! In applying this licence, ECMWF does not waive the privileges and immunities +! granted to it by virtue of its status as an intergovernmental organisation nor +! does it submit to any jurisdiction. + +! This File contains Unit Tests for testing the +! C++ / Fortran Interfaces to the KDTree Datastructure +! @author Benjamin Menetrier + +#include "fckit/fctest.h" +#include "atlas/atlas_f.h" + +! ----------------------------------------------------------------------------- + +module fcta_KDTree_fixture +use atlas_module +use, intrinsic :: iso_c_binding +implicit none +end module fcta_KDTree_fixture + +! ----------------------------------------------------------------------------- + +TESTSUITE_WITH_FIXTURE(fctest_atlas_KDTree,fcta_KDTree_fixture) + +! ----------------------------------------------------------------------------- + +TESTSUITE_INIT + call atlas_library%initialise() +END_TESTSUITE_INIT + +! ----------------------------------------------------------------------------- + +TESTSUITE_FINALIZE + use fckit_main_module + call atlas_library%finalise() +END_TESTSUITE_FINALIZE + +! ----------------------------------------------------------------------------- + +TEST( test_kdtree ) +use fckit_log_module +use fckit_c_interop_module +implicit none + + type(atlas_Geometry) :: geometry + type(atlas_IndexKDTree) :: kdtree + type(atlas_StructuredGrid) :: grid + integer(c_int) :: n, i, ix, iy, k, kk + integer(c_int), allocatable :: tree_indices(:), indices(:), indices_rad(:), result_indices(:) + real(c_double) :: lonlat(2), xyz(3), plonlat(2), pxyz(3) + real(c_double), allocatable :: tree_lonlats(:,:), tree_distances(:) + real(c_double), allocatable :: lonlats(:,:), distances(:) + real(c_double), allocatable :: lons_rad(:), lats_rad(:), lonlats_rad(:,:), distances_rad(:) + real(c_double), allocatable :: result_lonlats(:,:), result_distances(:) + + write(*,*) "test_kdtree for UnitSphere starting" + + ! Define grid + grid = atlas_StructuredGrid("O32") + + ! Allocation + n = grid%size() + allocate(tree_lonlats(n,2)) + allocate(tree_indices(n)) + allocate(tree_distances(n)) + k = 6 + allocate(result_lonlats(k,2)) + allocate(result_indices(k)) + allocate(result_distances(k)) + allocate(lonlats(k,2)) + allocate(indices(k)) + allocate(distances(k)) + + ! Define tree points + i = 0 + do iy = 1, int(grid%ny(), c_int) + do ix = 1, int(grid%nx(iy), c_int) + i = i+1 + tree_indices(i) = i + tree_lonlats(i,:) = grid%lonlat(ix, iy) + end do + end do + + ! Define test point + plonlat = (/-76.1_c_double, -33._c_double/) + + ! Build geometry + geometry = atlas_Geometry("UnitSphere") + + ! Define result points + call geometry%lonlat2xyz(plonlat, pxyz) + tree_distances = 0.0 + do i = 1, n + call geometry%lonlat2xyz(tree_lonlats(i,:), xyz) + tree_distances(i) = sqrt(sum((xyz-pxyz)**2)) + end do + do i = 1, k + result_indices(i:i) = minloc(tree_distances) + result_distances(i) = tree_distances(result_indices(i)) + tree_distances(result_indices(i)) = huge(c_double) + result_lonlats(i,:) = tree_lonlats(result_indices(i),:) + end do + + ! Check interfaces with separate coordinates + + ! Check constructor with specific geometry + kdtree = atlas_IndexKDTree(geometry) + write(0,*) "kdtree%c_ptr() = ", c_ptr_to_loc(kdtree%CPTR_PGIBUG_A) + + ! Check reserve + call kdtree%reserve(n) + + ! Check insert + do i = 1, n + call kdtree%insert(tree_lonlats(i,1), tree_lonlats(i,2), tree_indices(i)) + end do + + ! Check build only + call kdtree%build() + + ! Check closestPoints + call kdtree%closestPoints(plonlat(1), plonlat(2), k, indices, distances, lonlats(:,1), lonlats(:,2)) + do i = 1, k + FCTEST_CHECK_EQUAL( indices(i) , result_indices(i) ) + FCTEST_CHECK_CLOSE( lonlats(i,1) , result_lonlats(i,1) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( lonlats(i,2) , result_lonlats(i,2) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( distances(i) , result_distances(i) , 1.e-12_c_double ) + end do + + ! Check closestPoint + call kdtree%closestPoint(plonlat(1), plonlat(2), indices(1), distances(1), lonlats(1,1), lonlats(1,2)) + FCTEST_CHECK_EQUAL( indices(1) , result_indices(1) ) + FCTEST_CHECK_CLOSE( lonlats(1,1) , result_lonlats(1,1) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( lonlats(1,2) , result_lonlats(1,2) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( distances(1) , result_distances(1) , 1.e-12_c_double ) + + ! Check closestPoints + call kdtree%closestPointsWithinRadius(plonlat(1), plonlat(2), 5.e-2_c_double, kk, indices_rad, distances_rad, lons_rad, lats_rad) + + FCTEST_CHECK_EQUAL( kk , 3 ) + do i = 1, kk + FCTEST_CHECK_EQUAL( indices_rad(i) , result_indices(i) ) + FCTEST_CHECK_CLOSE( lons_rad(i), result_lonlats(i,1) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( lats_rad(i) , result_lonlats(i,2) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( distances_rad(i) , result_distances(i) , 1.e-12_c_double ) + end do + + ! Finalization + call kdtree%final() + + ! Check interfaces with vectorized coordinates + + ! Check constructor with specific geometry + kdtree = atlas_IndexKDTree(geometry) + write(0,*) "kdtree%c_ptr() = ", c_ptr_to_loc(kdtree%CPTR_PGIBUG_A) + + ! Check reserve + call kdtree%reserve(n) + + ! Check insert + do i = 1, n + call kdtree%insert(tree_lonlats(i,:), tree_indices(i)) + end do + + ! Check build only + call kdtree%build() + + ! Check closestPoints + call kdtree%closestPoints(plonlat, k, indices, distances, lonlats) + do i = 1, k + FCTEST_CHECK_EQUAL( indices(i) , result_indices(i) ) + FCTEST_CHECK_CLOSE( lonlats(i,1) , result_lonlats(i,1) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( lonlats(i,2) , result_lonlats(i,2) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( distances(i) , result_distances(i) , 1.e-12_c_double ) + end do + + ! Check closestPoint + call kdtree%closestPoint(plonlat, indices(1), distances(1), lonlats(1,:)) + FCTEST_CHECK_EQUAL( indices(1) , result_indices(1) ) + FCTEST_CHECK_CLOSE( lonlats(1,1) , result_lonlats(1,1) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( lonlats(1,2) , result_lonlats(1,2) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( distances(1) , result_distances(1) , 1.e-12_c_double ) + + ! Check closestPoints + call kdtree%closestPointsWithinRadius(plonlat, 5.e-2_c_double, kk, indices_rad, distances_rad, lonlats_rad) + FCTEST_CHECK_EQUAL( kk , 3 ) + do i = 1, kk + FCTEST_CHECK_EQUAL( indices_rad(i) , result_indices(i) ) + FCTEST_CHECK_CLOSE( lonlats_rad(i,1) , result_lonlats(i,1) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( lonlats_rad(i,2) , result_lonlats(i,2) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( distances_rad(i) , result_distances(i) , 1.e-12_c_double ) + end do + + ! Finalization + call kdtree%final() + call geometry%final() + + ! Check constructor without geometry (Earth) + kdtree = atlas_IndexKDTree() + write(0,*) "kdtree%c_ptr() = ", c_ptr_to_loc(kdtree%CPTR_PGIBUG_A) + + ! Check geometry accessor + geometry = kdtree%geometry() + FCTEST_CHECK_EQUAL( geometry%radius() , 6371229._c_double ) + + ! Define result points + call geometry%lonlat2xyz(plonlat(1), plonlat(2), pxyz(1), pxyz(2), pxyz(3)) + tree_distances = 0.0 + do i = 1, n + call geometry%lonlat2xyz(tree_lonlats(i,1), tree_lonlats(i,2), xyz(1), xyz(2), xyz(3)) + tree_distances(i) = sqrt(sum((xyz-pxyz)**2)) + end do + do i = 1, k + result_indices(i:i) = minloc(tree_distances) + result_distances(i) = tree_distances(result_indices(i)) + tree_distances(result_indices(i)) = huge(c_double) + result_lonlats(i,:) = tree_lonlats(result_indices(i),:) + end do + + ! Check interfaces with separate coordinates + + ! Check build with list + call kdtree%build(n, tree_lonlats(:,1), tree_lonlats(:,2), tree_indices) + + ! Check closestPoints + call kdtree%closestPoints(plonlat(1), plonlat(2), k, indices, distances, lonlats(:,1), lonlats(:,2)) + do i = 1, k + FCTEST_CHECK_EQUAL( indices(i) , result_indices(i) ) + FCTEST_CHECK_CLOSE( lonlats(i,1) , result_lonlats(i,1) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( lonlats(i,2) , result_lonlats(i,2) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( distances(i) , result_distances(i) , 1.e-12_c_double ) + end do + + ! Check closestPoint + call kdtree%closestPoint(plonlat(1), plonlat(2), indices(1), distances(1), lonlats(1,1), lonlats(1,2)) + FCTEST_CHECK_EQUAL( indices(1) , result_indices(1) ) + FCTEST_CHECK_CLOSE( lonlats(1,1) , result_lonlats(1,1) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( lonlats(1,2) , result_lonlats(1,2) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( distances(1) , result_distances(1) , 1.e-12_c_double ) + + ! Check closestPoints + call kdtree%closestPointsWithinRadius(plonlat(1), plonlat(2), 3.5e5_c_double, kk, indices_rad, distances_rad, lons_rad, lats_rad) + FCTEST_CHECK_EQUAL( kk , 4 ) + do i = 1, kk + FCTEST_CHECK_EQUAL( indices_rad(i) , result_indices(i) ) + FCTEST_CHECK_CLOSE( lons_rad(i) , result_lonlats(i,1) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( lats_rad(i) , result_lonlats(i,2) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( distances_rad(i) , result_distances(i) , 1.e-12_c_double ) + end do + + ! Finalization + call kdtree%final() + call geometry%final() + + ! Check interfaces with vectorized coordinates + + ! Check constructor without geometry (Earth) + kdtree = atlas_IndexKDTree() + write(0,*) "kdtree%c_ptr() = ", c_ptr_to_loc(kdtree%CPTR_PGIBUG_A) + + ! Check build with list + call kdtree%build(n, tree_lonlats, tree_indices) + + ! Check closestPoints + call kdtree%closestPoints(plonlat, k, indices, distances, lonlats) + do i = 1, k + FCTEST_CHECK_EQUAL( indices(i) , result_indices(i) ) + FCTEST_CHECK_CLOSE( lonlats(i,1) , result_lonlats(i,1) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( lonlats(i,2) , result_lonlats(i,2) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( distances(i) , result_distances(i) , 1.e-12_c_double ) + end do + + ! Check closestPoint + call kdtree%closestPoint(plonlat, indices(1), distances(1), lonlats(1,:)) + FCTEST_CHECK_EQUAL( indices(1) , result_indices(1) ) + FCTEST_CHECK_CLOSE( lonlats(1,1) , result_lonlats(1,1) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( lonlats(1,2) , result_lonlats(1,2) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( distances(1) , result_distances(1) , 1.e-12_c_double ) + + ! Check closestPoints + call kdtree%closestPointsWithinRadius(plonlat, 3.5e5_c_double, kk, indices_rad, distances_rad, lonlats_rad) + FCTEST_CHECK_EQUAL( kk , 4 ) + do i = 1, kk + FCTEST_CHECK_EQUAL( indices_rad(i) , result_indices(i) ) + FCTEST_CHECK_CLOSE( lonlats_rad(i,1) , result_lonlats(i,1) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( lonlats_rad(i,2) , result_lonlats(i,2) , 1.e-12_c_double ) + FCTEST_CHECK_CLOSE( distances_rad(i) , result_distances(i) , 1.e-12_c_double ) + end do + + ! Finalization + call kdtree%final() + + ! Release memory + call grid%final() + deallocate(tree_lonlats) + deallocate(tree_indices) + deallocate(tree_distances) + deallocate(lonlats) + deallocate(indices) + deallocate(distances) + deallocate(lons_rad) + deallocate(lats_rad) + deallocate(lonlats_rad) + deallocate(indices_rad) + deallocate(distances_rad) + deallocate(result_lonlats) + deallocate(result_indices) + deallocate(result_distances) + +END_TEST +! ----------------------------------------------------------------------------- + +END_TESTSUITE + From 20e4ded767693ca6ac921851ed7f8a92b2b3b78f Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 15 May 2020 17:44:32 +0100 Subject: [PATCH 083/145] ATLAS-292 Add scaling to Mercator projection --- src/atlas/projection/detail/MercatorProjection.cc | 14 +++++++++++++- src/atlas/projection/detail/MercatorProjection.h | 3 ++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/atlas/projection/detail/MercatorProjection.cc b/src/atlas/projection/detail/MercatorProjection.cc index 3f7f813e5..0734b2041 100644 --- a/src/atlas/projection/detail/MercatorProjection.cc +++ b/src/atlas/projection/detail/MercatorProjection.cc @@ -20,6 +20,8 @@ #include "atlas/util/Constants.h" #include "atlas/util/Earth.h" +#include "atlas/runtime/Log.h" + /* Projection formula's for Mercator projection from "Map Projections: A Working Manual" @@ -53,7 +55,13 @@ MercatorProjectionT::MercatorProjectionT( const eckit::Parametrisation if ( !params.get( "longitude0", lon0_ ) ) { lon0_ = 0.0; } - + if ( !params.get( "latitude1", lat1_ ) ) { + lat1_ = 0.0; + } + if ( lat1_ != 0.0 ) { + double k = std::cos( D2R( lat1_ ) ); + radius_ *= k; + } inv_radius_ = 1. / radius_; } @@ -83,6 +91,7 @@ typename MercatorProjectionT::Spec MercatorProjectionT::spec Spec proj_spec; proj_spec.set( "type", static_type() ); proj_spec.set( "longitude0", lon0_ ); + proj_spec.set( "latitude1", lat1_ ); if ( std::not_equal_to()( radius_, util::Earth::radius() ) ) { proj_spec.set( "radius", radius_ ); } @@ -96,6 +105,9 @@ void MercatorProjectionT::hash( eckit::Hash& hsh ) const { rotation_.hash( hsh ); hsh.add( lon0_ ); hsh.add( radius_ ); + if ( lat1_ != 0. ) { + hsh.add( lat1_ ); + } } template class MercatorProjectionT; diff --git a/src/atlas/projection/detail/MercatorProjection.h b/src/atlas/projection/detail/MercatorProjection.h index 62ee6082b..1830e70a2 100644 --- a/src/atlas/projection/detail/MercatorProjection.h +++ b/src/atlas/projection/detail/MercatorProjection.h @@ -44,7 +44,8 @@ class MercatorProjectionT final : public ProjectionImpl { void hash( eckit::Hash& ) const override; protected: - double lon0_; // central longitude + double lon0_; // central longitude (default = 0 ) + double lat1_; // latitude where cylinder cuts sphere (default = 0 ) double radius_; // sphere radius double inv_radius_; // 1/(sphere radius) From 188309884d92ae8387b19dfcadab83ac8f9263c8 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 15 May 2020 18:18:14 +0100 Subject: [PATCH 084/145] ATLAS-292 Add tests for mercator grids --- doc/example-grids/regional_mercator_1.yml | 20 ++++++++++++++++++++ doc/example-grids/regional_mercator_2.yml | 20 ++++++++++++++++++++ doc/example-grids/regional_mercator_3.yml | 20 ++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 doc/example-grids/regional_mercator_1.yml create mode 100644 doc/example-grids/regional_mercator_2.yml create mode 100644 doc/example-grids/regional_mercator_3.yml diff --git a/doc/example-grids/regional_mercator_1.yml b/doc/example-grids/regional_mercator_1.yml new file mode 100644 index 000000000..41ccf8a35 --- /dev/null +++ b/doc/example-grids/regional_mercator_1.yml @@ -0,0 +1,20 @@ +# test_mercator.temp + +type : "regional" +nx : 56 +ny : 44 +dx : 45000 +dy : 45000 +lonlat(xmin,ymin) : [262.036,14.7365] +projection: + type : "mercator" + latitude1 : 14.0 + +check : + uid : 7df03d15d5ac5e356d4bead27a95856a + size : 2464 + lonlat(first) : [262.036,14.7365] + lonlat(last) : [284.975,31.1731] + bounding_box(n,w,s,e) : [31.1741,262.035,14.7355,284.976] + xmin : 28272594.38 + ymin : 1607833.03 diff --git a/doc/example-grids/regional_mercator_2.yml b/doc/example-grids/regional_mercator_2.yml new file mode 100644 index 000000000..f6a00ac6d --- /dev/null +++ b/doc/example-grids/regional_mercator_2.yml @@ -0,0 +1,20 @@ +# prmsl_mercator + +type : "regional" +nx : 942 +ny : 882 +dx : 1500 +dy : 1500 +lonlat(xmin,ymin) : [3.61,35.505] +projection: + type : "mercator" + latitude1 : 45.5 + +check : + uid : 50cee0ba70062383166f3e0e0b2667a7 + size : 830844 + lonlat(first) : [3.61,35.505] + lonlat(last) : [21.72,48.08] + bounding_box(n,w,s,e) : [48.081,3.609,35.504,21.721] + xmin : 281364.68 + ymin : 2963540.96 diff --git a/doc/example-grids/regional_mercator_3.yml b/doc/example-grids/regional_mercator_3.yml new file mode 100644 index 000000000..b5be768ef --- /dev/null +++ b/doc/example-grids/regional_mercator_3.yml @@ -0,0 +1,20 @@ +# blend.t04z.master.f004.pr.grib2_001 + +type : "regional" +nx : 339 +ny : 225 +dx : 1250 +dy : 1250 +lonlat(xmin,ymin) : [291.972,16.9775] +projection: + type : "mercator" + latitude1 : 20.0 + +check : + uid : 31512a0712286a325fc37e6596fdf327 + size : 76275 + lonlat(first) : [291.972,16.9775] + lonlat(last) : [296.015,19.5221] + bounding_box(n,w,s,e) : [19.5231,291.971,16.9765,296.016] + xmin : 30508974.08 + ymin : 1800571.82 From b8b0e99cd43508c7bf437c421670790cb2aa0ce2 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 15 May 2020 18:24:43 +0100 Subject: [PATCH 085/145] ATLAS-292 Updated hash for MercatorProjection --- doc/example-grids/regional_mercator_1.yml | 2 +- doc/example-grids/regional_mercator_2.yml | 2 +- doc/example-grids/regional_mercator_3.yml | 2 +- doc/example-grids/regional_rotated_mercator_1.yml | 2 +- src/atlas/projection/detail/MercatorProjection.cc | 4 +--- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/doc/example-grids/regional_mercator_1.yml b/doc/example-grids/regional_mercator_1.yml index 41ccf8a35..e43903d27 100644 --- a/doc/example-grids/regional_mercator_1.yml +++ b/doc/example-grids/regional_mercator_1.yml @@ -11,7 +11,7 @@ projection: latitude1 : 14.0 check : - uid : 7df03d15d5ac5e356d4bead27a95856a + uid : 69156467c3cedc86bb7ffbb6e68ac0f6 size : 2464 lonlat(first) : [262.036,14.7365] lonlat(last) : [284.975,31.1731] diff --git a/doc/example-grids/regional_mercator_2.yml b/doc/example-grids/regional_mercator_2.yml index f6a00ac6d..4bac7c072 100644 --- a/doc/example-grids/regional_mercator_2.yml +++ b/doc/example-grids/regional_mercator_2.yml @@ -11,7 +11,7 @@ projection: latitude1 : 45.5 check : - uid : 50cee0ba70062383166f3e0e0b2667a7 + uid : 3a0c302b075cb92980a13f73acb797f3 size : 830844 lonlat(first) : [3.61,35.505] lonlat(last) : [21.72,48.08] diff --git a/doc/example-grids/regional_mercator_3.yml b/doc/example-grids/regional_mercator_3.yml index b5be768ef..6006653f8 100644 --- a/doc/example-grids/regional_mercator_3.yml +++ b/doc/example-grids/regional_mercator_3.yml @@ -11,7 +11,7 @@ projection: latitude1 : 20.0 check : - uid : 31512a0712286a325fc37e6596fdf327 + uid : 0c97c418451c6450be5297fa395375c7 size : 76275 lonlat(first) : [291.972,16.9775] lonlat(last) : [296.015,19.5221] diff --git a/doc/example-grids/regional_rotated_mercator_1.yml b/doc/example-grids/regional_rotated_mercator_1.yml index 57bb6bc2c..0215e04c9 100644 --- a/doc/example-grids/regional_rotated_mercator_1.yml +++ b/doc/example-grids/regional_rotated_mercator_1.yml @@ -14,5 +14,5 @@ check : size : 2000 lonlat(first) : [-10.319,40.2109] lonlat(last) : [24.4206,57.2263] - uid : 340321ee9855b47bbaadb581d2c86238 + uid : ed39d23003e1262ef8eb6e7b33689cd4 bounding_box(n,w,s,e) : [58.735,-16.4216,40.2099,24.4216] diff --git a/src/atlas/projection/detail/MercatorProjection.cc b/src/atlas/projection/detail/MercatorProjection.cc index 0734b2041..ad3af39e6 100644 --- a/src/atlas/projection/detail/MercatorProjection.cc +++ b/src/atlas/projection/detail/MercatorProjection.cc @@ -104,10 +104,8 @@ void MercatorProjectionT::hash( eckit::Hash& hsh ) const { hsh.add( static_type() ); rotation_.hash( hsh ); hsh.add( lon0_ ); + hsh.add( lat1_ ); hsh.add( radius_ ); - if ( lat1_ != 0. ) { - hsh.add( lat1_ ); - } } template class MercatorProjectionT; From c45b7386be0d410f06e89376ac68f51e633a99b0 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 15 May 2020 22:23:25 +0100 Subject: [PATCH 086/145] ATLAS-292 Fix hash for MercatorProjection with latitude1 parameter --- doc/example-grids/regional_mercator_1.yml | 2 +- doc/example-grids/regional_mercator_2.yml | 2 +- doc/example-grids/regional_mercator_3.yml | 2 +- .../projection/detail/MercatorProjection.cc | 31 ++++++++----------- .../projection/detail/MercatorProjection.h | 9 +++--- 5 files changed, 21 insertions(+), 25 deletions(-) diff --git a/doc/example-grids/regional_mercator_1.yml b/doc/example-grids/regional_mercator_1.yml index e43903d27..0d1585f63 100644 --- a/doc/example-grids/regional_mercator_1.yml +++ b/doc/example-grids/regional_mercator_1.yml @@ -11,7 +11,7 @@ projection: latitude1 : 14.0 check : - uid : 69156467c3cedc86bb7ffbb6e68ac0f6 + uid : d0ea12a60648863d9f5af8054d156bc0 size : 2464 lonlat(first) : [262.036,14.7365] lonlat(last) : [284.975,31.1731] diff --git a/doc/example-grids/regional_mercator_2.yml b/doc/example-grids/regional_mercator_2.yml index 4bac7c072..812d12451 100644 --- a/doc/example-grids/regional_mercator_2.yml +++ b/doc/example-grids/regional_mercator_2.yml @@ -11,7 +11,7 @@ projection: latitude1 : 45.5 check : - uid : 3a0c302b075cb92980a13f73acb797f3 + uid : c1318d1f29a86b8a97e4434a095bb220 size : 830844 lonlat(first) : [3.61,35.505] lonlat(last) : [21.72,48.08] diff --git a/doc/example-grids/regional_mercator_3.yml b/doc/example-grids/regional_mercator_3.yml index 6006653f8..fede4dd58 100644 --- a/doc/example-grids/regional_mercator_3.yml +++ b/doc/example-grids/regional_mercator_3.yml @@ -11,7 +11,7 @@ projection: latitude1 : 20.0 check : - uid : 0c97c418451c6450be5297fa395375c7 + uid : d5e6735264fef7c5d5cf5839a4a96ae3 size : 76275 lonlat(first) : [291.972,16.9775] lonlat(last) : [296.015,19.5221] diff --git a/src/atlas/projection/detail/MercatorProjection.cc b/src/atlas/projection/detail/MercatorProjection.cc index ad3af39e6..02f899b01 100644 --- a/src/atlas/projection/detail/MercatorProjection.cc +++ b/src/atlas/projection/detail/MercatorProjection.cc @@ -47,22 +47,17 @@ namespace detail { template MercatorProjectionT::MercatorProjectionT( const eckit::Parametrisation& params ) : ProjectionImpl(), rotation_( params ) { - // check presence of radius - if ( !params.get( "radius", radius_ ) ) { - radius_ = util::Earth::radius(); - } - // check presence of lon0 - if ( !params.get( "longitude0", lon0_ ) ) { - lon0_ = 0.0; - } - if ( !params.get( "latitude1", lat1_ ) ) { - lat1_ = 0.0; - } - if ( lat1_ != 0.0 ) { + params.get( "radius", radius_ = util::Earth::radius() ); + k_radius_ = radius_; + + params.get( "longitude0", lon0_ = 0.0 ); + + if ( params.get( "latitude1", lat1_ = 0.0 ) ) { double k = std::cos( D2R( lat1_ ) ); - radius_ *= k; + k_radius_ *= k; } - inv_radius_ = 1. / radius_; + + inv_k_radius_ = 1. / k_radius_; } template @@ -71,15 +66,15 @@ void MercatorProjectionT::lonlat2xy( double crd[] ) const { rotation_.unrotate( crd ); // then project - crd[0] = radius_ * ( D2R( crd[0] - lon0_ ) ); - crd[1] = radius_ * std::log( std::tan( D2R( 45. + crd[1] * 0.5 ) ) ); + crd[0] = k_radius_ * ( D2R( crd[0] - lon0_ ) ); + crd[1] = k_radius_ * std::log( std::tan( D2R( 45. + crd[1] * 0.5 ) ) ); } template void MercatorProjectionT::xy2lonlat( double crd[] ) const { // first projection - crd[0] = lon0_ + R2D( crd[0] * inv_radius_ ); - crd[1] = 2. * R2D( std::atan( std::exp( crd[1] * inv_radius_ ) ) ) - 90.; + crd[0] = lon0_ + R2D( crd[0] * inv_k_radius_ ); + crd[1] = 2. * R2D( std::atan( std::exp( crd[1] * inv_k_radius_ ) ) ) - 90.; // then rotate rotation_.rotate( crd ); diff --git a/src/atlas/projection/detail/MercatorProjection.h b/src/atlas/projection/detail/MercatorProjection.h index 1830e70a2..27d96e329 100644 --- a/src/atlas/projection/detail/MercatorProjection.h +++ b/src/atlas/projection/detail/MercatorProjection.h @@ -44,10 +44,11 @@ class MercatorProjectionT final : public ProjectionImpl { void hash( eckit::Hash& ) const override; protected: - double lon0_; // central longitude (default = 0 ) - double lat1_; // latitude where cylinder cuts sphere (default = 0 ) - double radius_; // sphere radius - double inv_radius_; // 1/(sphere radius) + double lon0_; // central longitude (default = 0 ) + double lat1_; // latitude where cylinder cuts sphere (default = 0 ) + double radius_; // sphere radius + double k_radius_; // sphere radius + double inv_k_radius_; // 1/(sphere radius) void setup( const eckit::Parametrisation& p ); From fad08a0caae32931991f5f65d0243c9636fec9a6 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 20 May 2020 10:56:55 +0100 Subject: [PATCH 087/145] ATLAS-292 More improvements to Projections (read on) - Support formulation for MercatorProjection for ellipsoids - Normalisation for ProjProjection and MercatorProjection --- .gitignore | 1 + ...egional_lambert_azimuthal_equal_area_1.yml | 2 +- ...egional_lambert_azimuthal_equal_area_2.yml | 2 +- ...egional_lambert_azimuthal_equal_area_3.yml | 4 +- ...egional_lambert_azimuthal_equal_area_4.yml | 4 +- .../regional_lambert_conformal_conic_1.yml | 2 +- .../regional_lambert_conformal_conic_2.yml | 2 +- doc/example-grids/regional_mercator_1.yml | 6 +- doc/example-grids/regional_mercator_2.yml | 2 +- doc/example-grids/regional_mercator_3.yml | 7 +- .../regional_rotated_mercator_1.yml | 2 +- src/apps/atlas-grids.cc | 105 +++++++++------- src/atlas/grid/detail/grid/Regional.cc | 113 ++++++++++++++--- .../projection/detail/MercatorProjection.cc | 119 ++++++++++++++---- .../projection/detail/MercatorProjection.h | 11 ++ src/atlas/projection/detail/ProjProjection.cc | 13 +- src/atlas/projection/detail/ProjProjection.h | 1 + src/atlas/projection/detail/ProjectionImpl.cc | 44 ++++++- src/atlas/projection/detail/ProjectionImpl.h | 19 +++ src/atlas/util/NormaliseLongitude.h | 6 +- src/tests/projection/test_projection_LAEA.cc | 8 +- 21 files changed, 369 insertions(+), 104 deletions(-) diff --git a/.gitignore b/.gitignore index 8ad3a5a4b..f085ab025 100755 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ doc/latex build/* install/* env.sh +*.DS_Store diff --git a/doc/example-grids/regional_lambert_azimuthal_equal_area_1.yml b/doc/example-grids/regional_lambert_azimuthal_equal_area_1.yml index cc2fe5da2..8b3c40a84 100644 --- a/doc/example-grids/regional_lambert_azimuthal_equal_area_1.yml +++ b/doc/example-grids/regional_lambert_azimuthal_equal_area_1.yml @@ -16,4 +16,4 @@ check : lonlat(first) : [-10.4643,40.1877] lonlat(last) : [24.6228,57.1968] uid : 16663e1f4f3deb724712dd2698008915 - bounding_box(n,w,s,e) : [58.7776,-16.6238,40.1867,24.6238] + bounding_box(n,w,s,e) : [58.7766,-16.6228,40.1877,24.6228] diff --git a/doc/example-grids/regional_lambert_azimuthal_equal_area_2.yml b/doc/example-grids/regional_lambert_azimuthal_equal_area_2.yml index b06b9681d..241def2c0 100644 --- a/doc/example-grids/regional_lambert_azimuthal_equal_area_2.yml +++ b/doc/example-grids/regional_lambert_azimuthal_equal_area_2.yml @@ -22,7 +22,7 @@ check : size : 950000 lonlat(first) : [-35.034,66.9821] lonlat(last) : [41.2436,23.8962] - bounding_box(n,w,s,e) : [72.647, -35.035, 23.895, 74.144] + bounding_box(n,w,s,e) : [72.6459, -35.034, 23.8962, 74.1431] uid : 2ef4f1bb005cf8e24ecc9464f8b7e089 xmin : 2510375.79 ymin : 748404.45 diff --git a/doc/example-grids/regional_lambert_azimuthal_equal_area_3.yml b/doc/example-grids/regional_lambert_azimuthal_equal_area_3.yml index 06736945d..58209741d 100644 --- a/doc/example-grids/regional_lambert_azimuthal_equal_area_3.yml +++ b/doc/example-grids/regional_lambert_azimuthal_equal_area_3.yml @@ -20,7 +20,7 @@ check : size : 950000 lonlat(first) : [-35.034,66.9821] lonlat(last) : [41.2436,23.8962] - bounding_box(n,w,s,e) : [72.647, -35.035, 23.895, 74.144] - uid : cc74ce2b099e3ae15f2f9f9caf52ddda + bounding_box(n,w,s,e) : [72.6459, -35.034, 23.8962, 74.1431] + uid : 855ead571170aaef37d72286e0984a3a xmin : 2510375.79 ymin : 748404.45 diff --git a/doc/example-grids/regional_lambert_azimuthal_equal_area_4.yml b/doc/example-grids/regional_lambert_azimuthal_equal_area_4.yml index 60626d3eb..a4d8f15d6 100644 --- a/doc/example-grids/regional_lambert_azimuthal_equal_area_4.yml +++ b/doc/example-grids/regional_lambert_azimuthal_equal_area_4.yml @@ -20,7 +20,7 @@ check : size : 950000 lonlat(first) : [-35.034,66.9821] lonlat(last) : [41.1397,23.9423] - bounding_box(n,w,s,e) : [72.6426, -35.035, 23.9413, 73.9386] - uid : bd42a9d7bbf089251667a4cc553a664c + bounding_box(n,w,s,e) : [72.6416, -35.034, 23.9423, 73.9376] + uid : 55039a4493fa9e045dd40ffda0eba2b5 xmin : 2502497.60 ymin : 752495.56 diff --git a/doc/example-grids/regional_lambert_conformal_conic_1.yml b/doc/example-grids/regional_lambert_conformal_conic_1.yml index a05aae2c3..a0245085c 100644 --- a/doc/example-grids/regional_lambert_conformal_conic_1.yml +++ b/doc/example-grids/regional_lambert_conformal_conic_1.yml @@ -17,6 +17,6 @@ check : lonlat(first) : [-10.3173,40.22] lonlat(last) : [24.4368,57.2335] uid : 2e01d66aa6df870b0ad346da4e80c4ff - bounding_box(n,w,s,e) : [58.7333,-16.4378,40.219,24.4378] + bounding_box(n,w,s,e) : [58.7320,-16.4368,40.2200,24.4368] xmin : -1225000.00 ymin : -975000.00 diff --git a/doc/example-grids/regional_lambert_conformal_conic_2.yml b/doc/example-grids/regional_lambert_conformal_conic_2.yml index 017e8be0a..17ed6d72b 100644 --- a/doc/example-grids/regional_lambert_conformal_conic_2.yml +++ b/doc/example-grids/regional_lambert_conformal_conic_2.yml @@ -16,6 +16,6 @@ check : lonlat(first) : [-126.138,16.281] lonlat(last) : [-57.3811,55.4813] uid : 5c9a0f242e68ff6dd7711ba3ab6f5c7b - bounding_box(n,w,s,e) : [58.3664,-139.857,16.28,-57.3801] + bounding_box(n,w,s,e) : [58.3654,-139.856,16.281,-57.3811] xmin : -3332155.29 ymin : -588892.76 diff --git a/doc/example-grids/regional_mercator_1.yml b/doc/example-grids/regional_mercator_1.yml index 0d1585f63..6b6313a28 100644 --- a/doc/example-grids/regional_mercator_1.yml +++ b/doc/example-grids/regional_mercator_1.yml @@ -11,10 +11,10 @@ projection: latitude1 : 14.0 check : - uid : d0ea12a60648863d9f5af8054d156bc0 + uid : b3a286f00887968240b93342e8a1e1a3 size : 2464 lonlat(first) : [262.036,14.7365] lonlat(last) : [284.975,31.1731] - bounding_box(n,w,s,e) : [31.1741,262.035,14.7355,284.976] - xmin : 28272594.38 + bounding_box(n,w,s,e) : [31.1731,262.036,14.7365,284.975] + xmin : -10569908.09 ymin : 1607833.03 diff --git a/doc/example-grids/regional_mercator_2.yml b/doc/example-grids/regional_mercator_2.yml index 812d12451..24daf992b 100644 --- a/doc/example-grids/regional_mercator_2.yml +++ b/doc/example-grids/regional_mercator_2.yml @@ -15,6 +15,6 @@ check : size : 830844 lonlat(first) : [3.61,35.505] lonlat(last) : [21.72,48.08] - bounding_box(n,w,s,e) : [48.081,3.609,35.504,21.721] + bounding_box(n,w,s,e) : [48.08,3.61,35.505,21.72] xmin : 281364.68 ymin : 2963540.96 diff --git a/doc/example-grids/regional_mercator_3.yml b/doc/example-grids/regional_mercator_3.yml index fede4dd58..dbf415d2a 100644 --- a/doc/example-grids/regional_mercator_3.yml +++ b/doc/example-grids/regional_mercator_3.yml @@ -9,12 +9,13 @@ lonlat(xmin,ymin) : [291.972,16.9775] projection: type : "mercator" latitude1 : 20.0 + normalise : [0,360] check : - uid : d5e6735264fef7c5d5cf5839a4a96ae3 + uid : 7e02e412ef4aa72f8e86aa281596ff1c size : 76275 lonlat(first) : [291.972,16.9775] lonlat(last) : [296.015,19.5221] - bounding_box(n,w,s,e) : [19.5231,291.971,16.9765,296.016] - xmin : 30508974.08 + bounding_box(n,w,s,e) : [19.5221,291.9720,16.9775,296.0153] + xmin : -7108436.73 ymin : 1800571.82 diff --git a/doc/example-grids/regional_rotated_mercator_1.yml b/doc/example-grids/regional_rotated_mercator_1.yml index 0215e04c9..3023898b3 100644 --- a/doc/example-grids/regional_rotated_mercator_1.yml +++ b/doc/example-grids/regional_rotated_mercator_1.yml @@ -15,4 +15,4 @@ check : lonlat(first) : [-10.319,40.2109] lonlat(last) : [24.4206,57.2263] uid : ed39d23003e1262ef8eb6e7b33689cd4 - bounding_box(n,w,s,e) : [58.735,-16.4216,40.2099,24.4216] + bounding_box(n,w,s,e) : [58.734,-16.4206,40.2109,24.4206] diff --git a/src/apps/atlas-grids.cc b/src/apps/atlas-grids.cc index cc15faf7a..14e836661 100644 --- a/src/apps/atlas-grids.cc +++ b/src/apps/atlas-grids.cc @@ -28,6 +28,7 @@ #include "atlas/grid/detail/grid/GridBuilder.h" #include "atlas/grid/detail/grid/GridFactory.h" #include "atlas/runtime/AtlasTool.h" +#include "atlas/util/NormaliseLongitude.h" namespace atlas { @@ -286,6 +287,12 @@ int AtlasGrids::execute( const Args& args ) { Log::info() << " lonlat(first) : " << *it << std::endl; it += grid.size() - 1; Log::info() << " lonlat(last) : " << *it << std::endl; + + if ( auto bb = grid.lonlatBoundingBox() ) { + Log::info() << " bounding_box(n,w,s,e) : { " << bb.north() << ", " << bb.west() << ", " + << bb.south() << ", " << bb.east() << " }" << std::endl; + } + Log::info().precision( precision ); } } @@ -373,35 +380,36 @@ int AtlasGrids::execute( const Args& args ) { } - auto equal = []( double a, double b ) { return eckit::types::is_approximately_equal( a, b, 5.e-4 ); }; - - std::vector first_point_lonlat; - if ( config_check.get( "lonlat(first)", first_point_lonlat ) ) { - PointLonLat first_point = *grid.lonlat().begin(); - if ( not equal( first_point.lon(), first_point_lonlat[0] ) or - not equal( first_point.lat(), first_point_lonlat[1] ) ) { - out << "Check failed: lonlat(first) " << first_point << " expected to be " - << PointLonLat( first_point_lonlat.data() ) << std::endl; - check_failed = true; + auto equal = []( double a, double b ) { return eckit::types::is_approximately_equal( a, b, 5.e-4 ); }; + auto point_equal = [&equal]( const PointLonLat& a, const PointLonLat& b ) -> bool { + return equal( a.lon(), b.lon() ) && equal( a.lat(), b.lat() ); + }; + auto difference_normalised = []( double a, double b ) { + util::NormaliseLongitude normalised( std::min( a, b ) ); + return normalised( b ) - normalised( a ); + }; + + auto check_lonlat = [&]( const std::string& key, std::function&& get_lonlat ) { + std::vector lonlat_config; + if ( config_check.get( key, lonlat_config ) ) { + PointLonLat lonlat_check = {lonlat_config.data()}; + PointLonLat lonlat = get_lonlat(); + if ( not point_equal( lonlat, lonlat_check ) ) { + out << std::setprecision( 4 ) << std::fixed << "Check failed: " << key << " " << lonlat + << " expected to be " << lonlat_check; + out << " ( normalised difference: {" + << difference_normalised( lonlat.lon(), lonlat_check.lon() ) << "," + << lonlat.lat() - lonlat_check.lat() << "} )" << std::endl; + check_failed = true; + } } - } - else { - Log::warning() << "Check for lonlat(first) skipped" << std::endl; - } - - std::vector last_point_lonlat; - if ( config_check.get( "lonlat(last)", last_point_lonlat ) ) { - PointLonLat last_point = *( grid.lonlat().begin() + ( grid.size() - 1 ) ); - if ( not equal( last_point.lon(), last_point_lonlat[0] ) or - not equal( last_point.lat(), last_point_lonlat[1] ) ) { - out << "Check failed: lonlat(last) " << last_point << " expected to be " - << PointLonLat( last_point_lonlat.data() ) << std::endl; - check_failed = true; + else { + Log::warning() << "Check for " << key << " skipped" << std::endl; } - } - else { - Log::warning() << "Check for lonlat(last) skipped" << std::endl; - } + }; + + check_lonlat( "lonlat(first)", [&]() { return *grid.lonlat().begin(); } ); + check_lonlat( "lonlat(last)", [&]() { return *( grid.lonlat().begin() + ( grid.size() - 1 ) ); } ); std::vector bbox; if ( config_check.get( "bounding_box(n,w,s,e)", bbox ) && bbox.size() == 4 ) { @@ -410,21 +418,34 @@ int AtlasGrids::execute( const Args& args ) { check_failed = true; out << "Check failed: cannot calculate bounding box for " << grid.spec() << std::endl; } - else if ( !equal( bb.north(), bbox[0] ) ) { - check_failed = true; - out << "Check failed: n=" << bb.north() << " expected to be " << bbox[0] << std::endl; - } - else if ( !equal( bb.west(), bbox[1] ) ) { - check_failed = true; - out << "Check failed: w=" << bb.west() << " expected to be " << bbox[1] << std::endl; - } - else if ( !equal( bb.south(), bbox[2] ) ) { - check_failed = true; - out << "Check failed: s=" << bb.south() << " expected to be " << bbox[2] << std::endl; - } - else if ( !equal( bb.east(), bbox[3] ) ) { - check_failed = true; - out << "Check failed: e=" << bb.east() << " expected to be " << bbox[3] << std::endl; + else { + bool any_value_failed = false; + if ( !equal( bb.north(), bbox[0] ) ) { + any_value_failed = true; + out << "Check failed: n=" << bb.north() << " expected to be " << bbox[0] << std::endl; + } + if ( !equal( bb.west(), bbox[1] ) ) { + any_value_failed = true; + out << "Check failed: w=" << bb.west() << " expected to be " << bbox[1] + << " ( normalised difference : " << difference_normalised( bb.west(), bbox[1] ) << " )" + << std::endl; + } + if ( !equal( bb.south(), bbox[2] ) ) { + any_value_failed = true; + out << "Check failed: s=" << bb.south() << " expected to be " << bbox[2] << std::endl; + } + if ( !equal( bb.east(), bbox[3] ) ) { + any_value_failed = true; + out << "Check failed: e=" << bb.east() << " expected to be " << bbox[3] + << " ( normalised difference : " << difference_normalised( bb.east(), bbox[3] ) << " )" + << std::endl; + } + if ( any_value_failed ) { + check_failed = true; + out << "Check failed: bounding_box(n,w,s,e) [" << bb.north() << ", " << bb.west() << ", " + << bb.south() << ", " << bb.east() << "] expected to be [" << bbox[0] << ", " << bbox[1] + << ", " << bbox[2] << ", " << bbox[3] << "]" << std::endl; + } } } else if ( check_bbox && bbox.size() != 4 ) { diff --git a/src/atlas/grid/detail/grid/Regional.cc b/src/atlas/grid/detail/grid/Regional.cc index 1fe05e5ad..234a87b8e 100644 --- a/src/atlas/grid/detail/grid/Regional.cc +++ b/src/atlas/grid/detail/grid/Regional.cc @@ -10,10 +10,14 @@ #include "Regional.h" +#include "eckit/types/FloatCompare.h" + #include "atlas/grid/StructuredGrid.h" #include "atlas/grid/detail/grid/GridBuilder.h" +#include "atlas/projection/detail/ProjectionImpl.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" +#include "atlas/util/NormaliseLongitude.h" using atlas::grid::LinearSpacing; using XSpace = atlas::StructuredGrid::XSpace; @@ -31,6 +35,93 @@ static Domain domain( const Grid::Config& grid ) { return Domain(); } +bool check_normalised_roundtrip( const Projection& projection, const PointLonLat& point ) { + using eckit::types::is_approximately_equal; + PointLonLat point_roundtrip = projection.lonlat( projection.xy( point ) ); + + constexpr util::NormaliseLongitude normalised; + return is_approximately_equal( point_roundtrip.lat(), point.lat(), 1.e-6 ) && + is_approximately_equal( normalised( point_roundtrip.lon() ), normalised( point.lon() ) ); +} + + +bool check_roundtrip( const Projection& projection, const PointLonLat& point ) { + using eckit::types::is_approximately_equal; + PointLonLat point_roundtrip = projection.lonlat( projection.xy( point ) ); + + return is_approximately_equal( point_roundtrip.lat(), point.lat(), 1.e-6 ) && + is_approximately_equal( point_roundtrip.lon(), point.lon(), 1.e-6 ); +} + + +static Projection projection( const Grid::Config& grid ) { + // Get the projection from the Grid::Config. + // Also try to infer "west" value that can be used for normalisation, + // and add it to the grid. + + struct reference_t { + std::string key; + PointLonLat value; + bool found() { return key.size(); } + }; + + auto get_reference = [&]() -> reference_t { + reference_t ref; + auto lonlat_strings = std::vector{"lonlat(xmin,ymin)", "lonlat(xmin,ymax)", "lonlat(centre)"}; + for ( auto& lonlat_string : lonlat_strings ) { + std::vector lonlat( 2 ); + if ( grid.get( lonlat_string, lonlat ) ) { + ref.key = lonlat_string; + ref.value = PointLonLat{lonlat.data()}; + break; + } + } + return ref; + }; + + Grid::Config proj_config; + if ( grid.get( "projection", proj_config ) ) { + auto ref = get_reference(); + + bool retry = false; + double west; + do { + bool retried = retry; + retry = false; + auto proj = Projection{proj_config}; + if ( ref.found() ) { + Log::warning().indent( "WARNING: " ); + if ( not check_roundtrip( proj, ref.value ) ) { + ATLAS_ASSERT( check_normalised_roundtrip( proj, ref.value ) ); + if ( retried ) { + Log::warning() << "Projection roundtrip failed for point \"" << ref.value << "\"" << std::endl; + } + if ( not Projection::Implementation::Normalise( proj_config ) ) { + west = std::floor( ref.value.lon() - 180. ); + Projection::Implementation::Normalise( west ).spec( proj_config ); + retry = true; + } + } + else if ( retried ) { + Log::warning() + << "Projection roundtrip failed for reference point \"" << ref.key << " : " << ref.value + << "\",\n" + "but was succesful when retrying using estimated normalisation parameter \"normalise : [" + << west << "," << west + 360. << "]\".\n" + << "Continuing with new Projection, but it is advised to provide a suitable normalisation for " + "this grid." + << std::endl; + } + Log::warning().unindent(); + } + if ( not retry || retried ) { + return proj; + } + } while ( retry ); + } + return Projection(); +} + struct ConfigParser { struct Parsed { Parsed() = default; @@ -86,7 +177,7 @@ struct Parse_llc_step : ConfigParser { }; struct Parse_bounds_xy : ConfigParser { - Parse_bounds_xy( const Projection& p, const Grid::Config& config ) { + Parse_bounds_xy( const Projection& /*p*/, const Grid::Config& config ) { valid = config.get( "nx", x.N ) && config.get( "ny", y.N ) && config.get( "xmin", x.min ) && config.get( "xmax", x.max ) && config.get( "ymin", y.min ) && config.get( "ymax", y.max ); @@ -280,29 +371,23 @@ static class regional : public GridBuilder { public: regional() : GridBuilder( "regional" ) {} - void print( std::ostream& os ) const override { + void print( std::ostream& ) const override { // os << std::left << std::setw(20) << "O" << "Octahedral Gaussian // grid"; } - const Grid::Implementation* create( const std::string& name, const Grid::Config& config ) const override { + const Grid::Implementation* create( const std::string& /*name*/, const Grid::Config& /*config*/ ) const override { throw_NotImplemented( "There are no named regional grids implemented.", Here() ); return nullptr; } const Grid::Implementation* create( const Grid::Config& config ) const override { // read projection subconfiguration - Projection projection; - { - util::Config config_proj; - if ( config.get( "projection", config_proj ) ) { - projection = Projection( config_proj ); - } - } + Projection proj = projection( config ); // Read grid configuration ConfigParser::Parsed x, y; - if ( not ConfigParser::parse( projection, config, x, y ) ) { + if ( not ConfigParser::parse( proj, config, x, y ) ) { throw_Exception( "Could not parse configuration for RegularRegional grid", Here() ); } @@ -311,7 +396,7 @@ static class regional : public GridBuilder { bool with_endpoint = true; XSpace xspace( {x.min, x.max}, std::vector( y.N, x.N ), with_endpoint ); - return new StructuredGrid::grid_t( xspace, yspace, projection, domain( config ) ); + return new StructuredGrid::grid_t( xspace, yspace, proj, domain( config ) ); } void force_link() {} @@ -322,12 +407,12 @@ static class zonal_band : public GridBuilder { public: zonal_band() : GridBuilder( "zonal_band" ) {} - void print( std::ostream& os ) const override { + void print( std::ostream& ) const override { // os << std::left << std::setw(20) << "O" << "Octahedral Gaussian // grid"; } - const Grid::Implementation* create( const std::string& name, const Grid::Config& config ) const override { + const Grid::Implementation* create( const std::string& /*name*/, const Grid::Config& ) const override { throw_NotImplemented( "There are no named zonal_band grids implemented.", Here() ); return nullptr; } diff --git a/src/atlas/projection/detail/MercatorProjection.cc b/src/atlas/projection/detail/MercatorProjection.cc index 02f899b01..4dd4c6f0a 100644 --- a/src/atlas/projection/detail/MercatorProjection.cc +++ b/src/atlas/projection/detail/MercatorProjection.cc @@ -10,17 +10,19 @@ #include #include +#include #include "eckit/config/Parametrisation.h" #include "eckit/utils/Hash.h" #include "atlas/projection/detail/MercatorProjection.h" #include "atlas/projection/detail/ProjectionFactory.h" +#include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" #include "atlas/util/Constants.h" +#include "atlas/util/CoordinateEnums.h" #include "atlas/util/Earth.h" - -#include "atlas/runtime/Log.h" +#include "atlas/util/NormaliseLongitude.h" /* Projection formula's for Mercator projection from "Map Projections: A Working @@ -31,10 +33,10 @@ The origin of the xy-system is at (lon0,0) */ namespace { -static double D2R( const double x ) { +static constexpr double D2R( const double x ) { return atlas::util::Constants::degreesToRadians() * x; } -static double R2D( const double x ) { +static constexpr double R2D( const double x ) { return atlas::util::Constants::radiansToDegrees() * x; } } // namespace @@ -46,15 +48,32 @@ namespace detail { // constructors template MercatorProjectionT::MercatorProjectionT( const eckit::Parametrisation& params ) : - ProjectionImpl(), rotation_( params ) { - params.get( "radius", radius_ = util::Earth::radius() ); - k_radius_ = radius_; + ProjectionImpl(), normalise_( params ), rotation_( params ) { + bool radius_provided = params.get( "radius", radius_ = util::Earth::radius() ); + k_radius_ = radius_; params.get( "longitude0", lon0_ = 0.0 ); + normalise_mercator_ = util::NormaliseLongitude( lon0_ - 180., lon0_ + 180. ); if ( params.get( "latitude1", lat1_ = 0.0 ) ) { - double k = std::cos( D2R( lat1_ ) ); - k_radius_ *= k; + k_radius_ *= std::cos( D2R( lat1_ ) ); + } + + params.get( "false_northing", false_northing_ = 0. ); + params.get( "false_easting", false_easting_ = 0. ); + + eccentricity_ = 0.; + auto squared = []( double x ) { return x * x; }; + if ( params.get( "semi_major_axis", semi_major_axis_ = radius_ ) && + params.get( "semi_minor_axis", semi_minor_axis_ = radius_ ) ) { + ATLAS_ASSERT( + not radius_provided, + "Ambiguous parameters provided to MercatorProjection: {radius} and {semi_major_axis,semi_minor_axis}" ); + eccentricity_ = std::sqrt( 1. - squared( semi_minor_axis_ / semi_major_axis_ ) ); + } + + if ( eccentricity_ != 0. ) { + k_radius_ /= std::sqrt( 1. - squared( eccentricity_ * std::sin( D2R( lat1_ ) ) ) ); } inv_k_radius_ = 1. / k_radius_; @@ -62,42 +81,98 @@ MercatorProjectionT::MercatorProjectionT( const eckit::Parametrisation template void MercatorProjectionT::lonlat2xy( double crd[] ) const { + auto t = [&]( double& lat ) -> double { + double sinlat = std::sin( D2R( lat ) ); + double t = ( 1. + sinlat ) / ( 1. - sinlat ); + if ( eccentricity_ > 0 ) { // --> ellipsoidal correction + double e = eccentricity_; + double e_sinlat = e * sinlat; + t *= std::pow( ( 1. - e_sinlat ) / ( 1. + e_sinlat ), e ); + } + return t; + }; + // first unrotate rotation_.unrotate( crd ); // then project - crd[0] = k_radius_ * ( D2R( crd[0] - lon0_ ) ); - crd[1] = k_radius_ * std::log( std::tan( D2R( 45. + crd[1] * 0.5 ) ) ); + crd[XX] = k_radius_ * ( D2R( normalise_mercator_( crd[LON] ) - lon0_ ) ); + crd[YY] = k_radius_ * 0.5 * std::log( t( crd[LAT] ) ); + crd[XX] += false_easting_; + crd[YY] += false_northing_; } template void MercatorProjectionT::xy2lonlat( double crd[] ) const { + auto compute_lat = [&]( double y ) -> double { + if ( eccentricity_ == 0. ) { + return 90. - 2. * R2D( std::atan( std::exp( -y * inv_k_radius_ ) ) ); + } + else { // eccentricity > 0 --> ellipsoidal correction + double e = eccentricity_; + /* Iterative procedure to compute the latitude for the inverse projection + * From the book "Map Projections-A Working Manual-John P. Snyder (1987)" + * Equation (7–9) involves rapidly converging iteration: Calculate t from (15-11) + * Then, assuming an initial trial phi equal to (pi/2 - 2*arctan t) in the right side of equation (7–9), + * calculate phi on the left side. Substitute the calculated phi into the right side, + * calculate a new phi, etc., until phi does not change significantly from the preceding trial value of phi + */ + constexpr double EPSILON = 1.e-12; + constexpr int MAX_ITER = 15; + + const double t = std::exp( -y * inv_k_radius_ ); + const double e_half = 0.5 * e; + + double lat = 90. - 2 * R2D( std::atan( t ) ); + for ( int i = 0; i < MAX_ITER; ++i ) { + double e_sinlat = e * std::sin( D2R( lat ) ); + double dlat = + 90. - + 2. * R2D( std::atan( t * ( std::pow( ( ( 1.0 - e_sinlat ) / ( 1.0 + e_sinlat ) ), e_half ) ) ) ) - + lat; + lat += dlat; + if ( std::abs( dlat ) < EPSILON ) { + return lat; + } + } + ATLAS_THROW_EXCEPTION( "Convergence failed in computing latitude in MercatorProjection" ); + } + }; + + const double x = crd[XX] - false_easting_; + const double y = crd[YY] - false_northing_; + // first projection - crd[0] = lon0_ + R2D( crd[0] * inv_k_radius_ ); - crd[1] = 2. * R2D( std::atan( std::exp( crd[1] * inv_k_radius_ ) ) ) - 90.; + crd[LON] = lon0_ + R2D( x * inv_k_radius_ ); + crd[LAT] = compute_lat( y ); // then rotate rotation_.rotate( crd ); + + // then normalise + normalise_( crd ); } // specification template typename MercatorProjectionT::Spec MercatorProjectionT::spec() const { - Spec proj_spec; - proj_spec.set( "type", static_type() ); - proj_spec.set( "longitude0", lon0_ ); - proj_spec.set( "latitude1", lat1_ ); - if ( std::not_equal_to()( radius_, util::Earth::radius() ) ) { - proj_spec.set( "radius", radius_ ); - } - rotation_.spec( proj_spec ); - return proj_spec; + Spec proj; + proj.set( "type", static_type() ); + proj.set( "longitude0", lon0_ ); + proj.set( "latitude1", lat1_ ); + proj.set( "radius", radius_ ); + proj.set( "false_easting", false_easting_ ); + proj.set( "false_northing", false_northing_ ); + normalise_.spec( proj ); + rotation_.spec( proj ); + return proj; } template void MercatorProjectionT::hash( eckit::Hash& hsh ) const { hsh.add( static_type() ); rotation_.hash( hsh ); + normalise_.hash( hsh ); hsh.add( lon0_ ); hsh.add( lat1_ ); hsh.add( radius_ ); diff --git a/src/atlas/projection/detail/MercatorProjection.h b/src/atlas/projection/detail/MercatorProjection.h index 27d96e329..503dc1797 100644 --- a/src/atlas/projection/detail/MercatorProjection.h +++ b/src/atlas/projection/detail/MercatorProjection.h @@ -12,6 +12,7 @@ #include "atlas/domain.h" #include "atlas/projection/detail/ProjectionImpl.h" +#include "atlas/util/NormaliseLongitude.h" namespace atlas { namespace projection { @@ -44,12 +45,22 @@ class MercatorProjectionT final : public ProjectionImpl { void hash( eckit::Hash& ) const override; protected: + Normalise normalise_; + util::NormaliseLongitude normalise_mercator_; double lon0_; // central longitude (default = 0 ) double lat1_; // latitude where cylinder cuts sphere (default = 0 ) double radius_; // sphere radius double k_radius_; // sphere radius double inv_k_radius_; // 1/(sphere radius) + double eccentricity_; + double semi_major_axis_; + double semi_minor_axis_; + + double false_easting_; + double false_northing_; + + void setup( const eckit::Parametrisation& p ); private: diff --git a/src/atlas/projection/detail/ProjProjection.cc b/src/atlas/projection/detail/ProjProjection.cc index abb05e166..c326e5730 100644 --- a/src/atlas/projection/detail/ProjProjection.cc +++ b/src/atlas/projection/detail/ProjProjection.cc @@ -44,8 +44,8 @@ bool proj_ellipsoid_params( PJ_CONTEXT* ctxt, const std::string& proj_str, doubl std::string source_str( PJ_CONTEXT* ctxt, const std::string& proj_str ) { double a, b; if ( proj_ellipsoid_params( ctxt, proj_str, a, b ) ) { - return b < a ? "+proj=lonlat +a=" + std::to_string( a ) + " +b=" + std::to_string( b ) - : "+proj=lonlat +R=" + std::to_string( a ); + auto axes = b < a ? " +a=" + std::to_string( a ) + " +b=" + std::to_string( b ) : " +R=" + std::to_string( a ); + return "+proj=lonlat" + axes; } else { return "EPSG:4326"; // WGS84 (lat,lon) @@ -67,7 +67,7 @@ std::string geocentric_str( PJ_CONTEXT* ctxt, const std::string& proj_str ) { } // namespace ProjProjection::ProjProjection( const eckit::Parametrisation& param ) : - sourceToTarget_( nullptr ), sourceToGeocentric_( nullptr ), context_( PJ_DEFAULT_CTX ) { + normalise_( param ), sourceToTarget_( nullptr ), sourceToGeocentric_( nullptr ), context_( PJ_DEFAULT_CTX ) { ATLAS_ASSERT( param.get( "proj", proj_ ) && !proj_.empty() ); source_encoded_ = param.get( "proj_source", source_ = source_str( context_, proj_ ) ); geocentric_encoded_ = param.get( "proj_geocentric", geocentric_ = geocentric_str( context_, proj_ ) ); @@ -112,6 +112,9 @@ ProjProjection::ProjProjection( const eckit::Parametrisation& param ) : nullptr ) ) { std::string units( units_c_str ); if ( !units.empty() ) { + if ( units == "metre" ) { + units = "meters"; + } extraSpec_.set( "units", units ); } } @@ -126,6 +129,8 @@ void ProjProjection::xy2lonlat( double crd[] ) const { // std::memcpy(crd, &P, 2 * sizeof(double)); crd[LON] = P.enu.e; crd[LAT] = P.enu.n; + + normalise_( crd ); } @@ -161,6 +166,7 @@ ProjProjection::Spec ProjProjection::spec() const { if ( geocentric_encoded_ ) { spec.set( "proj_geocentric", geocentric_ ); } + normalise_.spec( spec ); return spec | extraSpec_; } @@ -179,6 +185,7 @@ void ProjProjection::hash( eckit::Hash& h ) const { if ( geocentric_encoded_ ) { h.add( geocentric_ ); } + normalise_.hash( h ); } diff --git a/src/atlas/projection/detail/ProjProjection.h b/src/atlas/projection/detail/ProjProjection.h index 17bcd522d..4e61884da 100644 --- a/src/atlas/projection/detail/ProjProjection.h +++ b/src/atlas/projection/detail/ProjProjection.h @@ -129,6 +129,7 @@ class ProjProjection final : public ProjectionImpl { // -- Members private: + Normalise normalise_; std::string proj_; std::string source_; std::string geocentric_; diff --git a/src/atlas/projection/detail/ProjectionImpl.cc b/src/atlas/projection/detail/ProjectionImpl.cc index 9a9e29303..e867f38ed 100644 --- a/src/atlas/projection/detail/ProjectionImpl.cc +++ b/src/atlas/projection/detail/ProjectionImpl.cc @@ -148,6 +148,48 @@ void ProjectionImpl::BoundLonLat::extend( PointLonLat p, PointLonLat eps ) { crossesDateLine( eckit::types::is_approximately_equal( max_.lon() - min_.lon(), 360. ) ); } +// -------------------------------------------------------------------------------------------------------------------- + +ProjectionImpl::Normalise::Normalise( const eckit::Parametrisation& p ) { + values_.resize( 2 ); + bool provided = false; + if ( p.get( "normalise", values_ ) ) { + provided = true; + } + else if ( p.get( "normalize", values_ ) ) { + provided = true; + } + else if ( p.get( "west", values_[0] ) ) { + values_[1] = values_[0] + 360.; + provided = true; + } + if ( provided ) { + normalise_.reset( new util::NormaliseLongitude( values_[0], values_[1] ) ); + } +} + +ProjectionImpl::Normalise::Normalise( double west ) { + values_.resize( 2 ); + values_[0] = west; + values_[1] = values_[0] + 360.; + normalise_.reset( new util::NormaliseLongitude( values_[0], values_[1] ) ); +} + + +void ProjectionImpl::Normalise::hash( eckit::Hash& hash ) const { + if ( normalise_ ) { + hash.add( values_[0] ); + hash.add( values_[1] ); + } +} + +void ProjectionImpl::Normalise::spec( Spec& s ) const { + if ( normalise_ ) { + s.set( "normalise", values_ ); + } +} + + // -------------------------------------------------------------------------------------------------------------------- const ProjectionImpl* ProjectionImpl::create( const eckit::Parametrisation& p ) { @@ -183,7 +225,7 @@ RectangularLonLatDomain ProjectionImpl::lonlatBoundingBox( const Domain& domain RectangularDomain rect( domain ); ATLAS_ASSERT( rect ); - constexpr double h = 0.001; + constexpr double h = 0.5e-6; // precision to microdegrees; constexpr size_t Niter = 100; diff --git a/src/atlas/projection/detail/ProjectionImpl.h b/src/atlas/projection/detail/ProjectionImpl.h index 03abf45bc..6dbb76250 100644 --- a/src/atlas/projection/detail/ProjectionImpl.h +++ b/src/atlas/projection/detail/ProjectionImpl.h @@ -10,9 +10,11 @@ #pragma once +#include #include #include "atlas/util/Factory.h" +#include "atlas/util/NormaliseLongitude.h" #include "atlas/util/Object.h" #include "atlas/util/Point.h" #include "atlas/util/Rotation.h" @@ -89,6 +91,23 @@ class ProjectionImpl : public util::Object { bool first_ = true; }; + struct Normalise { + Normalise( const eckit::Parametrisation& ); + Normalise( double west ); + void hash( eckit::Hash& ) const; + void spec( Spec& ) const; + void operator()( double crd[] ) const { + if ( normalise_ ) { + crd[0] = ( *normalise_ )( crd[0] ); + } + } + operator bool() const { return bool( normalise_ ); } + + private: + std::unique_ptr normalise_; + std::vector values_; + }; + struct Derivate { Derivate( const ProjectionImpl& p, PointXY A, PointXY B, double h ); virtual ~Derivate(); diff --git a/src/atlas/util/NormaliseLongitude.h b/src/atlas/util/NormaliseLongitude.h index e044fe49d..43603cc9d 100644 --- a/src/atlas/util/NormaliseLongitude.h +++ b/src/atlas/util/NormaliseLongitude.h @@ -26,6 +26,8 @@ class NormaliseLongitude { // Normalise longitude between ( west-eps, east+eps ) constexpr NormaliseLongitude( double west, double east ) : west_( west - eps_ ), east_( east + eps_ ) {} + constexpr NormaliseLongitude( const NormaliseLongitude& other ) : west_( other.west_ ), east_( other.east_ ) {} + double operator()( double lon ) const { while ( lon < west_ ) { lon += 360.; @@ -37,8 +39,8 @@ class NormaliseLongitude { } private: - const double west_; - const double east_; + double west_; + double east_; public: static constexpr double eps_ = 1.e-11; diff --git a/src/tests/projection/test_projection_LAEA.cc b/src/tests/projection/test_projection_LAEA.cc index d9d569e0c..695cc3bda 100644 --- a/src/tests/projection/test_projection_LAEA.cc +++ b/src/tests/projection/test_projection_LAEA.cc @@ -145,10 +145,10 @@ CASE( "test_grid_creation_from_GRIB" ) { { RectangularLonLatDomain bb{g.lonlatBoundingBox()}; const double tolerance = 1.e-6; - EXPECT( is_approximately_equal( bb.west(), -90.001, tolerance ) ); - EXPECT( is_approximately_equal( bb.east(), 41.883045, tolerance ) ); - EXPECT( is_approximately_equal( bb.south(), 3.817356, tolerance ) ); - EXPECT( is_approximately_equal( bb.north(), 76.041386, tolerance ) ); + EXPECT_APPROX_EQ( bb.west(), -90., tolerance ); + EXPECT_APPROX_EQ( bb.east(), 41.882046, tolerance ); + EXPECT_APPROX_EQ( bb.south(), 3.818356, tolerance ); + EXPECT_APPROX_EQ( bb.north(), 76.040387, tolerance ); for ( PointLonLat p : g.lonlat() ) { EXPECT( bb.contains( p ) ); } From b3e42503e99b2e8545f1427dbaa71005dd45b160 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 20 May 2020 11:17:47 +0100 Subject: [PATCH 088/145] Added possibility to disable the atlas Log::warning channel with environment variable ATLAS_WARNING=0 --- src/atlas/library/Library.cc | 22 ++++++++++++++++++++++ src/atlas/library/Library.h | 3 +++ src/atlas/runtime/Log.cc | 4 ++++ src/atlas/runtime/Log.h | 1 + src/tests/AtlasTestEnvironment.h | 21 +++++++++++---------- 5 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/atlas/library/Library.cc b/src/atlas/library/Library.cc index e9ec725be..8bb748866 100644 --- a/src/atlas/library/Library.cc +++ b/src/atlas/library/Library.cc @@ -99,6 +99,7 @@ Library::Library() : eckit::system::Library( std::string( "atlas" ) ), debug_( eckit::system::Library::debug() ), info_( getEnv( "ATLAS_INFO", true ) ), + warning_( getEnv( "ATLAS_WARNING", true ) ), trace_( getEnv( "ATLAS_TRACE", false ) ), trace_barriers_( getEnv( "ATLAS_TRACE_BARRIERS", false ) ), trace_report_( getEnv( "ATLAS_TRACE_REPORT", false ) ) {} @@ -144,6 +145,7 @@ void Library::initialise( const eckit::Parametrisation& config ) { if ( config.has( "log" ) ) { config.get( "log.info", info_ ); config.get( "log.trace", trace_ ); + config.get( "log.warning", warning_ ); config.get( "log.debug", debug_ ); } if ( config.has( "trace" ) ) { @@ -160,6 +162,10 @@ void Library::initialise( const eckit::Parametrisation& config ) { if ( not info_ ) { info_channel_.reset(); } + if ( not warning_ ) { + warning_channel_.reset(); + } + // Summary if ( getEnv( "ATLAS_LOG_RANK", 0 ) == int( mpi::rank() ) ) { @@ -215,6 +221,10 @@ void Library::finalise() { info_ = false; info_channel_.reset( new eckit::Channel( new eckit::PrefixTarget( "ATLAS_INFO" ) ) ); } + if ( warningChannel() ) { + warning_ = false; + warning_channel_.reset( new eckit::Channel( new eckit::PrefixTarget( "ATLAS_WARNING" ) ) ); + } } eckit::Channel& Library::infoChannel() const { @@ -227,6 +237,18 @@ eckit::Channel& Library::infoChannel() const { return *info_channel_; } + +eckit::Channel& Library::warningChannel() const { + if ( warning_ ) { + return eckit::Log::warning(); + } + else if ( !warning_channel_ ) { + warning_channel_.reset( new eckit::Channel() ); + } + return *warning_channel_; +} + + eckit::Channel& Library::traceChannel() const { if ( trace_channel_ ) { return *trace_channel_; diff --git a/src/atlas/library/Library.h b/src/atlas/library/Library.h index 09bdb23b2..f1e6943a8 100644 --- a/src/atlas/library/Library.h +++ b/src/atlas/library/Library.h @@ -60,6 +60,7 @@ class Library : public eckit::system::Library { Information information() const { return Information(); } virtual eckit::Channel& infoChannel() const; + virtual eckit::Channel& warningChannel() const; virtual eckit::Channel& traceChannel() const; virtual eckit::Channel& debugChannel() const override; bool trace() const { return trace_; } @@ -74,10 +75,12 @@ class Library : public eckit::system::Library { bool debug_{false}; bool info_{true}; + bool warning_{true}; bool trace_{false}; bool trace_barriers_{false}; bool trace_report_{false}; mutable std::unique_ptr info_channel_; + mutable std::unique_ptr warning_channel_; mutable std::unique_ptr trace_channel_; mutable std::unique_ptr debug_channel_; }; diff --git a/src/atlas/runtime/Log.cc b/src/atlas/runtime/Log.cc index 110ed08e2..4d8c2f61c 100644 --- a/src/atlas/runtime/Log.cc +++ b/src/atlas/runtime/Log.cc @@ -44,6 +44,10 @@ Log::Channel& Log::info() { return atlas::Library::instance().infoChannel(); } +Log::Channel& Log::warning() { + return atlas::Library::instance().warningChannel(); +} + Log::Channel& Log::trace() { return atlas::Library::instance().traceChannel(); } diff --git a/src/atlas/runtime/Log.h b/src/atlas/runtime/Log.h index 71937a4c2..d0db9d5f0 100644 --- a/src/atlas/runtime/Log.h +++ b/src/atlas/runtime/Log.h @@ -11,6 +11,7 @@ class Log : public eckit::Log { using Channel = eckit::Channel; // derives from std::ostream static Channel& info(); + static Channel& warning(); static Channel& trace(); static Channel& debug(); diff --git a/src/tests/AtlasTestEnvironment.h b/src/tests/AtlasTestEnvironment.h index 3254d1df4..20a144d5e 100644 --- a/src/tests/AtlasTestEnvironment.h +++ b/src/tests/AtlasTestEnvironment.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -112,16 +113,16 @@ using eckit::types::is_approximately_equal; } \ } while ( false ) -#define __EXPECT_APPROX_EQ_TOL( lhs, rhs, tol ) \ - do { \ - if ( !( is_approximately_equal( lhs, rhs, tol ) ) ) { \ - std::stringstream err; \ - err << "EXPECT condition failed: " #lhs " ~= " #rhs \ - "\n" \ - " --> " \ - << lhs << " != " << rhs; \ - throw eckit::testing::TestException( err.str(), Here() ); \ - } \ +#define __EXPECT_APPROX_EQ_TOL( lhs, rhs, tol ) \ + do { \ + if ( !( is_approximately_equal( lhs, rhs, tol ) ) ) { \ + std::stringstream err; \ + err << "EXPECT condition failed: " #lhs " ~= " #rhs \ + "\n" \ + " --> " \ + << std::fixed << std::setprecision( 12 ) << lhs << " != " << rhs; \ + throw eckit::testing::TestException( err.str(), Here() ); \ + } \ } while ( false ) #define EXPECT_APPROX_EQ( ... ) __ATLAS_SPLICE( __EXPECT_APPROX_EQ__, __ATLAS_NARG( __VA_ARGS__ ) )( __VA_ARGS__ ) From e46f9e3ce8ca24f984917e197bf0f8b1cdd63570 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 20 May 2020 14:32:05 +0100 Subject: [PATCH 089/145] Add front() and back() functions to grid.xy() and grid.lonlat() to access first and last points quickly --- src/atlas/grid/Iterator.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/atlas/grid/Iterator.h b/src/atlas/grid/Iterator.h index 178a84387..a8e79d42f 100644 --- a/src/atlas/grid/Iterator.h +++ b/src/atlas/grid/Iterator.h @@ -139,6 +139,8 @@ class IterateXY { iterator begin() const; iterator end() const; void access( size_t i, PointXY& ); + PointXY front() { return *begin(); } + PointXY back() { return *( begin() + ( grid_.size() - 1 ) ); } private: const Grid& grid_; @@ -154,6 +156,8 @@ class IterateLonLat { IterateLonLat( const Grid& grid ) : grid_( grid ) {} iterator begin() const; iterator end() const; + PointLonLat front() { return *begin(); } + PointLonLat back() { return *( begin() + ( grid_.size() - 1 ) ); } private: const Grid& grid_; From 9fba0cbff4cb88f1dd7a989eb1812f0295cdc9ba Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 20 May 2020 14:33:39 +0100 Subject: [PATCH 090/145] ATLAS-292 Add comparison with 'proj' string (normalisation errors are permitted) --- ...egional_lambert_azimuthal_equal_area_1.yml | 4 ++ ...egional_lambert_azimuthal_equal_area_2.yml | 2 + doc/example-grids/regional_mercator_3.yml | 1 + src/apps/atlas-grids.cc | 40 +++++++++++++++++-- 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/doc/example-grids/regional_lambert_azimuthal_equal_area_1.yml b/doc/example-grids/regional_lambert_azimuthal_equal_area_1.yml index 8b3c40a84..4aeee39f9 100644 --- a/doc/example-grids/regional_lambert_azimuthal_equal_area_1.yml +++ b/doc/example-grids/regional_lambert_azimuthal_equal_area_1.yml @@ -17,3 +17,7 @@ check : lonlat(last) : [24.6228,57.1968] uid : 16663e1f4f3deb724712dd2698008915 bounding_box(n,w,s,e) : [58.7766,-16.6228,40.1877,24.6228] + xmin : -1225000.00 + ymin : -975000.00 + proj : "+proj=laea +lat_0=50 +lon_0=4 +R=6371229.0" + diff --git a/doc/example-grids/regional_lambert_azimuthal_equal_area_2.yml b/doc/example-grids/regional_lambert_azimuthal_equal_area_2.yml index 241def2c0..bf12a437b 100644 --- a/doc/example-grids/regional_lambert_azimuthal_equal_area_2.yml +++ b/doc/example-grids/regional_lambert_azimuthal_equal_area_2.yml @@ -26,3 +26,5 @@ check : uid : 2ef4f1bb005cf8e24ecc9464f8b7e089 xmin : 2510375.79 ymin : 748404.45 + proj : "+proj=laea +lat_0=52 +lon_0=10 +x_0=4321000 +y_0=3210000 +R=6371229.0" + diff --git a/doc/example-grids/regional_mercator_3.yml b/doc/example-grids/regional_mercator_3.yml index dbf415d2a..7581f9a5a 100644 --- a/doc/example-grids/regional_mercator_3.yml +++ b/doc/example-grids/regional_mercator_3.yml @@ -19,3 +19,4 @@ check : bounding_box(n,w,s,e) : [19.5221,291.9720,16.9775,296.0153] xmin : -7108436.73 ymin : 1800571.82 + proj : "+proj=merc +lat_ts=20 +R=6371229.0 +units=m +no_defs" diff --git a/src/apps/atlas-grids.cc b/src/apps/atlas-grids.cc index 14e836661..069716ca8 100644 --- a/src/apps/atlas-grids.cc +++ b/src/apps/atlas-grids.cc @@ -27,6 +27,7 @@ #include "atlas/grid.h" #include "atlas/grid/detail/grid/GridBuilder.h" #include "atlas/grid/detail/grid/GridFactory.h" +#include "atlas/projection/detail/ProjectionFactory.h" #include "atlas/runtime/AtlasTool.h" #include "atlas/util/NormaliseLongitude.h" @@ -385,8 +386,11 @@ int AtlasGrids::execute( const Args& args ) { return equal( a.lon(), b.lon() ) && equal( a.lat(), b.lat() ); }; auto difference_normalised = []( double a, double b ) { - util::NormaliseLongitude normalised( std::min( a, b ) ); - return normalised( b ) - normalised( a ); + constexpr util::NormaliseLongitude normalised( -180. ); + return normalised( b - a ); + }; + auto point_equal_normalised = [&]( const PointLonLat& a, const PointLonLat& b ) -> bool { + return equal( a.lat(), b.lat() ) && equal( difference_normalised( a.lon(), b.lon() ), 0. ); }; auto check_lonlat = [&]( const std::string& key, std::function&& get_lonlat ) { @@ -408,8 +412,8 @@ int AtlasGrids::execute( const Args& args ) { } }; - check_lonlat( "lonlat(first)", [&]() { return *grid.lonlat().begin(); } ); - check_lonlat( "lonlat(last)", [&]() { return *( grid.lonlat().begin() + ( grid.size() - 1 ) ); } ); + check_lonlat( "lonlat(first)", [&]() { return grid.lonlat().front(); } ); + check_lonlat( "lonlat(last)", [&]() { return grid.lonlat().back(); } ); std::vector bbox; if ( config_check.get( "bounding_box(n,w,s,e)", bbox ) && bbox.size() == 4 ) { @@ -487,6 +491,34 @@ int AtlasGrids::execute( const Args& args ) { Log::warning() << "Check for ymin skipped" << std::endl; } + if ( projection::ProjectionFactory::has( "proj" ) ) { + std::string proj_str; + if ( config_check.get( "proj", proj_str ) ) { + Projection proj( util::Config( "type", "proj" ) | util::Config( "proj", proj_str ) ); + auto check_proj = [&]( const PointLonLat& lonlat, const PointLonLat& proj_lonlat, + const std::string& point = "" ) { + if ( not point_equal( lonlat, proj_lonlat ) ) { + if ( point_equal_normalised( lonlat, proj_lonlat ) ) { + Log::warning() + << "WARNING: Projection of " << point + << " grid point is different from Proj only due to different normalisation: " + << lonlat << " != " << proj_lonlat << std::endl; + } + else { + Log::info() << "Check failed: Projection of " << point + << " grid point is different from Proj: " << lonlat << " != " << proj_lonlat + << " normalised difference = {" << std::fixed << std::setprecision( 12 ) + << difference_normalised( lonlat.lon(), proj_lonlat.lon() ) << "," + << lonlat.lat() - proj_lonlat.lat() << "}" << std::endl; + check_failed = true; + } + } + }; + check_proj( grid.lonlat().front(), proj.lonlat( grid.xy().front() ), "first" ); + check_proj( grid.lonlat().back(), proj.lonlat( grid.xy().back() ), "last" ); + } + } + if ( check_failed ) { return failed(); From 1da7356cb79a90c3914c58ad461f3a560fc35eb9 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 20 May 2020 16:45:38 +0100 Subject: [PATCH 091/145] ATLAS-292 Make grid.uid() for projected grids in meters more robust --- doc/example-grids/regional_lambert_azimuthal_equal_area_1.yml | 2 +- doc/example-grids/regional_lambert_azimuthal_equal_area_2.yml | 2 +- doc/example-grids/regional_lambert_azimuthal_equal_area_3.yml | 2 +- doc/example-grids/regional_lambert_azimuthal_equal_area_4.yml | 2 +- doc/example-grids/regional_lambert_conformal_conic_1.yml | 2 +- doc/example-grids/regional_lambert_conformal_conic_2.yml | 2 +- doc/example-grids/regional_mercator_1.yml | 2 +- doc/example-grids/regional_mercator_2.yml | 2 +- doc/example-grids/regional_mercator_3.yml | 2 +- doc/example-grids/regional_rotated_mercator_1.yml | 2 +- src/atlas/domain/detail/RectangularDomain.cc | 3 ++- src/atlas/grid/detail/grid/Structured.cc | 3 ++- 12 files changed, 14 insertions(+), 12 deletions(-) diff --git a/doc/example-grids/regional_lambert_azimuthal_equal_area_1.yml b/doc/example-grids/regional_lambert_azimuthal_equal_area_1.yml index 4aeee39f9..b7f69a791 100644 --- a/doc/example-grids/regional_lambert_azimuthal_equal_area_1.yml +++ b/doc/example-grids/regional_lambert_azimuthal_equal_area_1.yml @@ -15,7 +15,7 @@ check : size : 2000 lonlat(first) : [-10.4643,40.1877] lonlat(last) : [24.6228,57.1968] - uid : 16663e1f4f3deb724712dd2698008915 + uid : 0ee521e7f37c21d50b2f66655f178b65 bounding_box(n,w,s,e) : [58.7766,-16.6228,40.1877,24.6228] xmin : -1225000.00 ymin : -975000.00 diff --git a/doc/example-grids/regional_lambert_azimuthal_equal_area_2.yml b/doc/example-grids/regional_lambert_azimuthal_equal_area_2.yml index bf12a437b..df1573ed3 100644 --- a/doc/example-grids/regional_lambert_azimuthal_equal_area_2.yml +++ b/doc/example-grids/regional_lambert_azimuthal_equal_area_2.yml @@ -23,7 +23,7 @@ check : lonlat(first) : [-35.034,66.9821] lonlat(last) : [41.2436,23.8962] bounding_box(n,w,s,e) : [72.6459, -35.034, 23.8962, 74.1431] - uid : 2ef4f1bb005cf8e24ecc9464f8b7e089 + uid : 92fd7b766b66cce3829ce1b0af3bac15 xmin : 2510375.79 ymin : 748404.45 proj : "+proj=laea +lat_0=52 +lon_0=10 +x_0=4321000 +y_0=3210000 +R=6371229.0" diff --git a/doc/example-grids/regional_lambert_azimuthal_equal_area_3.yml b/doc/example-grids/regional_lambert_azimuthal_equal_area_3.yml index 58209741d..25cd19c31 100644 --- a/doc/example-grids/regional_lambert_azimuthal_equal_area_3.yml +++ b/doc/example-grids/regional_lambert_azimuthal_equal_area_3.yml @@ -21,6 +21,6 @@ check : lonlat(first) : [-35.034,66.9821] lonlat(last) : [41.2436,23.8962] bounding_box(n,w,s,e) : [72.6459, -35.034, 23.8962, 74.1431] - uid : 855ead571170aaef37d72286e0984a3a + uid : 7e489e060265f549856481f44d001c69 xmin : 2510375.79 ymin : 748404.45 diff --git a/doc/example-grids/regional_lambert_azimuthal_equal_area_4.yml b/doc/example-grids/regional_lambert_azimuthal_equal_area_4.yml index a4d8f15d6..aa061e037 100644 --- a/doc/example-grids/regional_lambert_azimuthal_equal_area_4.yml +++ b/doc/example-grids/regional_lambert_azimuthal_equal_area_4.yml @@ -21,6 +21,6 @@ check : lonlat(first) : [-35.034,66.9821] lonlat(last) : [41.1397,23.9423] bounding_box(n,w,s,e) : [72.6416, -35.034, 23.9423, 73.9376] - uid : 55039a4493fa9e045dd40ffda0eba2b5 + uid : dba9a7e48d9a5274ece3aea98335b6f0 xmin : 2502497.60 ymin : 752495.56 diff --git a/doc/example-grids/regional_lambert_conformal_conic_1.yml b/doc/example-grids/regional_lambert_conformal_conic_1.yml index a0245085c..9b8b185a0 100644 --- a/doc/example-grids/regional_lambert_conformal_conic_1.yml +++ b/doc/example-grids/regional_lambert_conformal_conic_1.yml @@ -16,7 +16,7 @@ check : size : 2000 lonlat(first) : [-10.3173,40.22] lonlat(last) : [24.4368,57.2335] - uid : 2e01d66aa6df870b0ad346da4e80c4ff + uid : 0914f1a75b5c5226e2b09ceed834a822 bounding_box(n,w,s,e) : [58.7320,-16.4368,40.2200,24.4368] xmin : -1225000.00 ymin : -975000.00 diff --git a/doc/example-grids/regional_lambert_conformal_conic_2.yml b/doc/example-grids/regional_lambert_conformal_conic_2.yml index 17ed6d72b..422375cd5 100644 --- a/doc/example-grids/regional_lambert_conformal_conic_2.yml +++ b/doc/example-grids/regional_lambert_conformal_conic_2.yml @@ -15,7 +15,7 @@ check : size : 151987 lonlat(first) : [-126.138,16.281] lonlat(last) : [-57.3811,55.4813] - uid : 5c9a0f242e68ff6dd7711ba3ab6f5c7b + uid : b185c7c56b3a1abaa5bf23b35ed12661 bounding_box(n,w,s,e) : [58.3654,-139.856,16.281,-57.3811] xmin : -3332155.29 ymin : -588892.76 diff --git a/doc/example-grids/regional_mercator_1.yml b/doc/example-grids/regional_mercator_1.yml index 6b6313a28..00df8b1df 100644 --- a/doc/example-grids/regional_mercator_1.yml +++ b/doc/example-grids/regional_mercator_1.yml @@ -11,7 +11,7 @@ projection: latitude1 : 14.0 check : - uid : b3a286f00887968240b93342e8a1e1a3 + uid : 533cccd1092166ec17c1799a03abc674 size : 2464 lonlat(first) : [262.036,14.7365] lonlat(last) : [284.975,31.1731] diff --git a/doc/example-grids/regional_mercator_2.yml b/doc/example-grids/regional_mercator_2.yml index 24daf992b..c1fc8e6c7 100644 --- a/doc/example-grids/regional_mercator_2.yml +++ b/doc/example-grids/regional_mercator_2.yml @@ -11,7 +11,7 @@ projection: latitude1 : 45.5 check : - uid : c1318d1f29a86b8a97e4434a095bb220 + uid : 92e781679f64b2589a970ea31a9aec0f size : 830844 lonlat(first) : [3.61,35.505] lonlat(last) : [21.72,48.08] diff --git a/doc/example-grids/regional_mercator_3.yml b/doc/example-grids/regional_mercator_3.yml index 7581f9a5a..1018297ba 100644 --- a/doc/example-grids/regional_mercator_3.yml +++ b/doc/example-grids/regional_mercator_3.yml @@ -12,7 +12,7 @@ projection: normalise : [0,360] check : - uid : 7e02e412ef4aa72f8e86aa281596ff1c + uid : 9a76e31630682f7f13cd38d540e45270 size : 76275 lonlat(first) : [291.972,16.9775] lonlat(last) : [296.015,19.5221] diff --git a/doc/example-grids/regional_rotated_mercator_1.yml b/doc/example-grids/regional_rotated_mercator_1.yml index 3023898b3..8ea84ecfc 100644 --- a/doc/example-grids/regional_rotated_mercator_1.yml +++ b/doc/example-grids/regional_rotated_mercator_1.yml @@ -14,5 +14,5 @@ check : size : 2000 lonlat(first) : [-10.319,40.2109] lonlat(last) : [24.4206,57.2263] - uid : ed39d23003e1262ef8eb6e7b33689cd4 + uid : 9d4b8c38db08c6f3fe3221c8770d8d57 bounding_box(n,w,s,e) : [58.734,-16.4206,40.2109,24.4206] diff --git a/src/atlas/domain/detail/RectangularDomain.cc b/src/atlas/domain/detail/RectangularDomain.cc index 6f5094ce6..d075ec96a 100644 --- a/src/atlas/domain/detail/RectangularDomain.cc +++ b/src/atlas/domain/detail/RectangularDomain.cc @@ -125,7 +125,8 @@ void RectangularDomain::print( std::ostream& os ) const { } void RectangularDomain::hash( eckit::Hash& h ) const { - auto add_double = [&]( const double& x ) { h.add( std::round( x * 1.e8 ) ); }; + double multiplier = units() == "meters" ? 1e2 : 1e8; + auto add_double = [&]( const double& x ) { h.add( std::round( x * multiplier ) ); }; h.add( type() ); h.add( units() ); add_double( xmin() ); diff --git a/src/atlas/grid/detail/grid/Structured.cc b/src/atlas/grid/detail/grid/Structured.cc index e529a1aac..679069ef4 100644 --- a/src/atlas/grid/detail/grid/Structured.cc +++ b/src/atlas/grid/detail/grid/Structured.cc @@ -489,7 +489,8 @@ std::string Structured::type() const { } void Structured::hash( eckit::Hash& h ) const { - auto add_double = [&]( const double& x ) { h.add( std::round( x * 1.e8 ) ); }; + double multiplier = projection().units() == "meters" ? 1e2 : 1e8; + auto add_double = [&]( const double& x ) { h.add( std::round( x * multiplier ) ); }; auto add_double_vector = [&]( const std::vector& xvec ) { for ( auto& x : xvec ) { add_double( x ); From d6a27bbb31ff98d85b8831aaf152e227d8373710 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 20 May 2020 17:06:19 +0100 Subject: [PATCH 092/145] ATLAS-292 Also compare regional_mercatotor_1 and regional_mercator_2 with Proj --- doc/example-grids/regional_mercator_1.yml | 1 + doc/example-grids/regional_mercator_2.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/doc/example-grids/regional_mercator_1.yml b/doc/example-grids/regional_mercator_1.yml index 00df8b1df..4e172f54b 100644 --- a/doc/example-grids/regional_mercator_1.yml +++ b/doc/example-grids/regional_mercator_1.yml @@ -18,3 +18,4 @@ check : bounding_box(n,w,s,e) : [31.1731,262.036,14.7365,284.975] xmin : -10569908.09 ymin : 1607833.03 + proj : "+proj=merc +lat_ts=14.0 +R=6371229.0 +units=m +no_defs" diff --git a/doc/example-grids/regional_mercator_2.yml b/doc/example-grids/regional_mercator_2.yml index c1fc8e6c7..ddd623938 100644 --- a/doc/example-grids/regional_mercator_2.yml +++ b/doc/example-grids/regional_mercator_2.yml @@ -18,3 +18,4 @@ check : bounding_box(n,w,s,e) : [48.08,3.61,35.505,21.72] xmin : 281364.68 ymin : 2963540.96 + proj : "+proj=merc +lat_ts=45.5 +R=6371229.0 +units=m +no_defs" From a4d42d926753d5a0ad5b31feabdba5bfcd7d5c2a Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 20 May 2020 17:13:25 +0100 Subject: [PATCH 093/145] ATLAS-292 atlas_test_regional_mercator_1 --> explicitly add normalisation between [0,360] --- doc/example-grids/regional_mercator_1.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/example-grids/regional_mercator_1.yml b/doc/example-grids/regional_mercator_1.yml index 4e172f54b..4761c4abb 100644 --- a/doc/example-grids/regional_mercator_1.yml +++ b/doc/example-grids/regional_mercator_1.yml @@ -9,6 +9,7 @@ lonlat(xmin,ymin) : [262.036,14.7365] projection: type : "mercator" latitude1 : 14.0 + normalise : [0,360] check : uid : 533cccd1092166ec17c1799a03abc674 From e5a66773f84c8ba26feb759b905d3ce00e64df85 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 20 May 2020 21:06:19 +0100 Subject: [PATCH 094/145] ATLAS-292 fixup last commit --- doc/example-grids/regional_mercator_1.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/example-grids/regional_mercator_1.yml b/doc/example-grids/regional_mercator_1.yml index 4761c4abb..642fc38e0 100644 --- a/doc/example-grids/regional_mercator_1.yml +++ b/doc/example-grids/regional_mercator_1.yml @@ -12,7 +12,7 @@ projection: normalise : [0,360] check : - uid : 533cccd1092166ec17c1799a03abc674 + uid : 632ee8b7bec231b64fbc34a78a5ff2ff size : 2464 lonlat(first) : [262.036,14.7365] lonlat(last) : [284.975,31.1731] From f66e43914337552d5b78ed0404fc48f4e39d7c0c Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 21 May 2020 17:20:02 +0100 Subject: [PATCH 095/145] ATLAS-292 No longer warn on normalisation errors, and introduce strict keyword --- src/apps/atlas-grids.cc | 81 +++++++++++++++++++++++--- src/atlas/grid/detail/grid/Regional.cc | 80 +------------------------ src/tests/grid/CMakeLists.txt | 3 + 3 files changed, 78 insertions(+), 86 deletions(-) diff --git a/src/apps/atlas-grids.cc b/src/apps/atlas-grids.cc index 069716ca8..ab5f8a38d 100644 --- a/src/apps/atlas-grids.cc +++ b/src/apps/atlas-grids.cc @@ -354,6 +354,8 @@ int AtlasGrids::execute( const Args& args ) { return failed(); } + bool strict = config_check.getBool( "strict", true ); + idx_t size; if ( config_check.get( "size", size ) ) { if ( grid.size() != size ) { @@ -367,7 +369,10 @@ int AtlasGrids::execute( const Args& args ) { std::string uid; if ( config_check.get( "uid", uid ) ) { - if ( grid.uid() != uid ) { + if ( uid == "ignore" ) { + out << "WARNING: ignoring uid explicitly" << std::endl; + } + else if ( grid.uid() != uid ) { out << "Check failed: grid uid " << grid.uid() << " expected to be " << uid << std::endl; check_failed = true; } @@ -385,20 +390,29 @@ int AtlasGrids::execute( const Args& args ) { auto point_equal = [&equal]( const PointLonLat& a, const PointLonLat& b ) -> bool { return equal( a.lon(), b.lon() ) && equal( a.lat(), b.lat() ); }; - auto difference_normalised = []( double a, double b ) { - constexpr util::NormaliseLongitude normalised( -180. ); - return normalised( b - a ); + auto difference_normalised = []( double a, double ref ) { + util::NormaliseLongitude normalised{ref - 180}; + return normalised( a ) - ref; }; auto point_equal_normalised = [&]( const PointLonLat& a, const PointLonLat& b ) -> bool { return equal( a.lat(), b.lat() ) && equal( difference_normalised( a.lon(), b.lon() ), 0. ); }; + auto equal_normalised = [&]( double a, double b ) { return equal( difference_normalised( a, b ), 0. ); }; auto check_lonlat = [&]( const std::string& key, std::function&& get_lonlat ) { std::vector lonlat_config; if ( config_check.get( key, lonlat_config ) ) { PointLonLat lonlat_check = {lonlat_config.data()}; PointLonLat lonlat = get_lonlat(); - if ( not point_equal( lonlat, lonlat_check ) ) { + if ( not point_equal_normalised( lonlat, lonlat_check ) ) { + out << std::setprecision( 4 ) << std::fixed << "Check failed: " << key << " " << lonlat + << " expected to be " << lonlat_check; + out << " ( normalised difference: {" + << difference_normalised( lonlat.lon(), lonlat_check.lon() ) << "," + << lonlat.lat() - lonlat_check.lat() << "} )" << std::endl; + check_failed = true; + } + else if ( strict && not point_equal( lonlat, lonlat_check ) ) { out << std::setprecision( 4 ) << std::fixed << "Check failed: " << key << " " << lonlat << " expected to be " << lonlat_check; out << " ( normalised difference: {" @@ -428,17 +442,28 @@ int AtlasGrids::execute( const Args& args ) { any_value_failed = true; out << "Check failed: n=" << bb.north() << " expected to be " << bbox[0] << std::endl; } - if ( !equal( bb.west(), bbox[1] ) ) { + if ( !equal_normalised( bb.west(), bbox[1] ) ) { any_value_failed = true; out << "Check failed: w=" << bb.west() << " expected to be " << bbox[1] << " ( normalised difference : " << difference_normalised( bb.west(), bbox[1] ) << " )" << std::endl; } + else if ( strict && not equal( bb.west(), bbox[1] ) ) { + out << "Check failed: w=" << bb.west() << " expected to be " << bbox[1] + << " ( normalised difference : " << difference_normalised( bb.west(), bbox[1] ) << " )" + << std::endl; + } if ( !equal( bb.south(), bbox[2] ) ) { any_value_failed = true; out << "Check failed: s=" << bb.south() << " expected to be " << bbox[2] << std::endl; } - if ( !equal( bb.east(), bbox[3] ) ) { + if ( !equal_normalised( bb.east(), bbox[3] ) ) { + any_value_failed = true; + out << "Check failed: e=" << bb.east() << " expected to be " << bbox[3] + << " ( normalised difference : " << difference_normalised( bb.east(), bbox[3] ) << " )" + << std::endl; + } + else if ( strict && not equal( bb.east(), bbox[3] ) ) { any_value_failed = true; out << "Check failed: e=" << bb.east() << " expected to be " << bbox[3] << " ( normalised difference : " << difference_normalised( bb.east(), bbox[3] ) << " )" @@ -519,6 +544,48 @@ int AtlasGrids::execute( const Args& args ) { } } + auto check_roundtrip = [&]( const PointLonLat& point, bool strict = false ) { + using eckit::types::is_approximately_equal; + const auto projection = grid.projection(); + PointLonLat point_roundtrip = projection.lonlat( projection.xy( point ) ); + + bool roundtrip = is_approximately_equal( point_roundtrip.lat(), point.lat(), 1.e-6 ) && + is_approximately_equal( point_roundtrip.lon(), point.lon(), 1.e-6 ); + constexpr util::NormaliseLongitude normalised; + bool normalised_roundtrip = + is_approximately_equal( point_roundtrip.lat(), point.lat(), 1.e-6 ) && + is_approximately_equal( normalised( point_roundtrip.lon() ), normalised( point.lon() ) ); + + if ( not normalised_roundtrip ) { + check_failed = true; + Log::info() << "Check failed: Roundtrip of point " << point + << " failed, even with normalisation: " << point_roundtrip << " != " << point + << " normalised difference = {" << std::fixed << std::setprecision( 12 ) + << difference_normalised( point_roundtrip.lon(), point.lon() ) << "," + << point_roundtrip.lat() - point.lat() << "}" << std::endl; + } + if ( strict && not roundtrip ) { + check_failed = true; + Log::info() << "Check failed: Roundtrip of point " << point << " failed"; + if ( normalised_roundtrip ) { + Log::info() << " but was OK with normalisation"; + } + Log::info() << ": " << point_roundtrip << " != " << point << " normalised difference = {" + << std::fixed << std::setprecision( 12 ) + << difference_normalised( point_roundtrip.lon(), point.lon() ) << "," + << point_roundtrip.lat() - point.lat() << "}" << std::endl; + } + }; + + std::vector roundtrip; + if ( config_check.get( "roundtrip", roundtrip ) ) { + for ( auto& entry : roundtrip ) { + std::vector lonlat; + entry.get( "lonlat", lonlat ); + check_roundtrip( {lonlat[0], lonlat[1]}, strict ); + } + } + if ( check_failed ) { return failed(); diff --git a/src/atlas/grid/detail/grid/Regional.cc b/src/atlas/grid/detail/grid/Regional.cc index 234a87b8e..cf0e5fa3d 100644 --- a/src/atlas/grid/detail/grid/Regional.cc +++ b/src/atlas/grid/detail/grid/Regional.cc @@ -35,89 +35,11 @@ static Domain domain( const Grid::Config& grid ) { return Domain(); } -bool check_normalised_roundtrip( const Projection& projection, const PointLonLat& point ) { - using eckit::types::is_approximately_equal; - PointLonLat point_roundtrip = projection.lonlat( projection.xy( point ) ); - - constexpr util::NormaliseLongitude normalised; - return is_approximately_equal( point_roundtrip.lat(), point.lat(), 1.e-6 ) && - is_approximately_equal( normalised( point_roundtrip.lon() ), normalised( point.lon() ) ); -} - - -bool check_roundtrip( const Projection& projection, const PointLonLat& point ) { - using eckit::types::is_approximately_equal; - PointLonLat point_roundtrip = projection.lonlat( projection.xy( point ) ); - - return is_approximately_equal( point_roundtrip.lat(), point.lat(), 1.e-6 ) && - is_approximately_equal( point_roundtrip.lon(), point.lon(), 1.e-6 ); -} - - static Projection projection( const Grid::Config& grid ) { // Get the projection from the Grid::Config. - // Also try to infer "west" value that can be used for normalisation, - // and add it to the grid. - - struct reference_t { - std::string key; - PointLonLat value; - bool found() { return key.size(); } - }; - - auto get_reference = [&]() -> reference_t { - reference_t ref; - auto lonlat_strings = std::vector{"lonlat(xmin,ymin)", "lonlat(xmin,ymax)", "lonlat(centre)"}; - for ( auto& lonlat_string : lonlat_strings ) { - std::vector lonlat( 2 ); - if ( grid.get( lonlat_string, lonlat ) ) { - ref.key = lonlat_string; - ref.value = PointLonLat{lonlat.data()}; - break; - } - } - return ref; - }; - Grid::Config proj_config; if ( grid.get( "projection", proj_config ) ) { - auto ref = get_reference(); - - bool retry = false; - double west; - do { - bool retried = retry; - retry = false; - auto proj = Projection{proj_config}; - if ( ref.found() ) { - Log::warning().indent( "WARNING: " ); - if ( not check_roundtrip( proj, ref.value ) ) { - ATLAS_ASSERT( check_normalised_roundtrip( proj, ref.value ) ); - if ( retried ) { - Log::warning() << "Projection roundtrip failed for point \"" << ref.value << "\"" << std::endl; - } - if ( not Projection::Implementation::Normalise( proj_config ) ) { - west = std::floor( ref.value.lon() - 180. ); - Projection::Implementation::Normalise( west ).spec( proj_config ); - retry = true; - } - } - else if ( retried ) { - Log::warning() - << "Projection roundtrip failed for reference point \"" << ref.key << " : " << ref.value - << "\",\n" - "but was succesful when retrying using estimated normalisation parameter \"normalise : [" - << west << "," << west + 360. << "]\".\n" - << "Continuing with new Projection, but it is advised to provide a suitable normalisation for " - "this grid." - << std::endl; - } - Log::warning().unindent(); - } - if ( not retry || retried ) { - return proj; - } - } while ( retry ); + return Projection{proj_config}; } return Projection(); } diff --git a/src/tests/grid/CMakeLists.txt b/src/tests/grid/CMakeLists.txt index d8dd09afb..fe0cfd719 100644 --- a/src/tests/grid/CMakeLists.txt +++ b/src/tests/grid/CMakeLists.txt @@ -41,6 +41,9 @@ if( NOT HAVE_PROJ ) ecbuild_list_exclude_pattern( LIST grids REGEX regional_lambert_azimuthal_equal_area_[3|4].yml ) + ecbuild_list_exclude_pattern( + LIST grids + REGEX regional_polar_stereographic_1.yml ) endif() foreach( grid ${grids} ) From d15b78f43cb25f9c506e3c9267ba27290cb631ff Mon Sep 17 00:00:00 2001 From: "Mark J. Olah" Date: Thu, 21 May 2020 10:28:45 -0600 Subject: [PATCH 096/145] Missing string header is an error under gcc-10.1.0 --- src/atlas/grid/Spacing.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/atlas/grid/Spacing.h b/src/atlas/grid/Spacing.h index 8112a4908..55d32f7a3 100644 --- a/src/atlas/grid/Spacing.h +++ b/src/atlas/grid/Spacing.h @@ -11,6 +11,7 @@ #pragma once #include +#include #include #include "atlas/library/config.h" From 8be30b3f3ec1c675755752ecbcb65a8b7caef442 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 21 May 2020 17:56:24 +0100 Subject: [PATCH 097/145] ATLAS-293 Add unit-test that is able to demonstrate bug when removing normalise option --- .../regional_polar_stereographic_1.yml | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 doc/example-grids/regional_polar_stereographic_1.yml diff --git a/doc/example-grids/regional_polar_stereographic_1.yml b/doc/example-grids/regional_polar_stereographic_1.yml new file mode 100644 index 000000000..f7b23bf4d --- /dev/null +++ b/doc/example-grids/regional_polar_stereographic_1.yml @@ -0,0 +1,26 @@ +type : "regional" +nx : 553 +ny : 425 +dx : 11250 +dy : 11250 +lonlat(xmin,ymin) : [187,30] +projection: + type : "proj" + proj : "+proj=stere +lat_ts=60 +lat_0=90 +lon_0=225 +k_0=1 +R=6371229.0" + normalise : [0,360] # no longer necessary after bugfix of ATLAS-293 + +check : # optional section, intended for unit-testing + + strict: false # false -> allow longitude values to be different due to normalisation only + uid : ignore # -> special keyword that ignores uid check, but issues warning + size : 235025 + lonlat(first) : [187,30] + lonlat(last) : [297.1496,70.1114] + bounding_box(n,w,s,e) : [83.8474, 143.5977, 30.0000, 297.1496] + xmin : -4225928.24 + ymin : -5408941.49 + + roundtrip: # check that projection can do roundtrip for following values + - lonlat: [ 187,30 ] + - lonlat: [ 297.1496,70.1114 ] + From 93f38022d14388edc52ba9506b8cb1de6a743304 Mon Sep 17 00:00:00 2001 From: Philippe Marguinaud Date: Fri, 17 Apr 2020 08:15:11 +0000 Subject: [PATCH 098/145] ATLAS-294 Fix work sharing for StructuredColumns::setup (Github #34) The problem is not a race condition nor is it related to OpenMP but rather to the distribution of the work to the threads. --- .../detail/StructuredColumns_setup.cc | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/atlas/functionspace/detail/StructuredColumns_setup.cc b/src/atlas/functionspace/detail/StructuredColumns_setup.cc index 1bdd7f972..4ac370986 100644 --- a/src/atlas/functionspace/detail/StructuredColumns_setup.cc +++ b/src/atlas/functionspace/detail/StructuredColumns_setup.cc @@ -462,17 +462,26 @@ void StructuredColumns::setup( const grid::Distribution& distribution, const eck if ( j > thread_j_begin ) { thread_i_begin[j] = i_begin_[j]; } + idx_t j_size = ( i_end_[j] - i_begin_[j] ); + idx_t remaining = static_cast( end - n ); - idx_t j_size = ( i_end_[j] - i_begin_[j] ); - if ( remaining > j_size ) { + if ( j_size <= remaining ) { thread_i_end[j] = i_end_[j]; n += j_size; + if ( n == end ) { + goto stop; + } } else { - thread_i_end[j] = thread_i_begin[j] + remaining; - thread_j_end = j + 1; - break; + thread_i_end[j] = i_begin_[j] + remaining; + goto stop; } + + continue; + + stop: + thread_j_end = j + 1; + break; } idx_t r = idx_t( begin ); From 063be796fdaeef280d376bfe097801f417552445 Mon Sep 17 00:00:00 2001 From: Philippe Marguinaud Date: Fri, 17 Apr 2020 07:56:01 +0000 Subject: [PATCH 099/145] ATLAS-295 (Github #32) Create failing demonstrator for wrongly created StructuredColumns of grid shifted by -180 degrees --- .../functionspace/test_structuredcolumns.cc | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/tests/functionspace/test_structuredcolumns.cc b/src/tests/functionspace/test_structuredcolumns.cc index e519a1e79..d5f603d0f 100644 --- a/src/tests/functionspace/test_structuredcolumns.cc +++ b/src/tests/functionspace/test_structuredcolumns.cc @@ -36,6 +36,31 @@ namespace test { //----------------------------------------------------------------------------- + +CASE ("lonlat -180/+180/-90/+90") { + + StructuredGrid grid1 (Config ("nx", 80) | Config ("ny", 40) | Config ("type", "shifted_lonlat") + | Config ("domain", Config ("type", "rectangular") | Config ("units", "degrees") + | Config ("xmin", -180.0) | Config ("xmax", +180.0) + | Config ("ymin", -90.0) | Config ("ymax", +90.0))); + + functionspace::StructuredColumns fs1 (grid1, grid::Partitioner ("checkerboard"), Config ("halo", 1) | Config ("periodic_points", true)); + + StructuredGrid grid2 (Config ("nx", 80) | Config ("ny", 40) | Config ("type", "shifted_lonlat") + | Config ("domain", Config ("type", "rectangular") | Config ("units", "degrees") + | Config ("xmin", 0.0) | Config ("xmax", 360.0) + | Config ("ymin", -90.0) | Config ("ymax", +90.0))); + + functionspace::StructuredColumns fs2 (grid2, grid::Partitioner ("checkerboard"), Config ("halo", 1) | Config ("periodic_points", true)); + + printf (" grid1.size (), fs1.sizeOwned () fs1.size () = %8d, %8d, %8d\n", grid1.size (), fs1.sizeOwned (), fs1.size ()); + printf (" grid2.size (), fs2.sizeOwned () fs2.size () = %8d, %8d, %8d\n", grid2.size (), fs2.sizeOwned (), fs2.size ()); + + +} + + +#ifdef UNDEF CASE( "test_functionspace_StructuredColumns_no_halo" ) { size_t root = 0; std::string gridname = eckit::Resource( "--grid", "O8" ); @@ -327,6 +352,8 @@ CASE( "test_functionspace_StructuredColumns halo exchange registration" ) { } } +#endif + //----------------------------------------------------------------------------- long innerproductwithhalo( const atlas::Field& f1, const atlas::Field& f2 ) { From f7dd2f01255dcc0fbec5ce36b97807d16ef56857 Mon Sep 17 00:00:00 2001 From: Philippe Marguinaud Date: Fri, 17 Apr 2020 08:01:25 +0000 Subject: [PATCH 100/145] ATLAS-295 (Github #32) Fix this issue --- .../functionspace/detail/StructuredColumns_setup.cc | 2 +- src/tests/functionspace/test_structuredcolumns.cc | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/atlas/functionspace/detail/StructuredColumns_setup.cc b/src/atlas/functionspace/detail/StructuredColumns_setup.cc index 4ac370986..9317de222 100644 --- a/src/atlas/functionspace/detail/StructuredColumns_setup.cc +++ b/src/atlas/functionspace/detail/StructuredColumns_setup.cc @@ -270,7 +270,7 @@ void StructuredColumns::setup( const grid::Distribution& distribution, const eck auto compute_x_fast = [this, &compute_i_fast]( idx_t i, idx_t jj, idx_t nx ) -> double { const idx_t ii = compute_i_fast( i, nx ); // guaranteed between 0 and nx(jj) const double a = ( ii - i ) / nx; - const double x = grid_->x( ii, jj ) - a * grid_->x( nx, jj ); + const double x = grid_->x( ii, jj ) - a * ( grid_->x( nx, jj ) - grid_->x( 0, jj ) ); return x; }; diff --git a/src/tests/functionspace/test_structuredcolumns.cc b/src/tests/functionspace/test_structuredcolumns.cc index d5f603d0f..0ac4a70e7 100644 --- a/src/tests/functionspace/test_structuredcolumns.cc +++ b/src/tests/functionspace/test_structuredcolumns.cc @@ -53,14 +53,12 @@ CASE ("lonlat -180/+180/-90/+90") { functionspace::StructuredColumns fs2 (grid2, grid::Partitioner ("checkerboard"), Config ("halo", 1) | Config ("periodic_points", true)); - printf (" grid1.size (), fs1.sizeOwned () fs1.size () = %8d, %8d, %8d\n", grid1.size (), fs1.sizeOwned (), fs1.size ()); - printf (" grid2.size (), fs2.sizeOwned () fs2.size () = %8d, %8d, %8d\n", grid2.size (), fs2.sizeOwned (), fs2.size ()); - - + EXPECT (grid1.size () == grid2.size ()); + EXPECT (fs1.size () == fs2.size ()); + EXPECT (fs1.sizeOwned () == fs2.sizeOwned ()); } -#ifdef UNDEF CASE( "test_functionspace_StructuredColumns_no_halo" ) { size_t root = 0; std::string gridname = eckit::Resource( "--grid", "O8" ); @@ -352,7 +350,6 @@ CASE( "test_functionspace_StructuredColumns halo exchange registration" ) { } } -#endif //----------------------------------------------------------------------------- From 3ec60b43e2948fe43f5934f95665cc920d62dabf Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 27 May 2020 18:02:04 +0100 Subject: [PATCH 101/145] ATLAS-295 (Github #32) cleanup --- .../functionspace/test_structuredcolumns.cc | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/tests/functionspace/test_structuredcolumns.cc b/src/tests/functionspace/test_structuredcolumns.cc index 0ac4a70e7..d086de7d2 100644 --- a/src/tests/functionspace/test_structuredcolumns.cc +++ b/src/tests/functionspace/test_structuredcolumns.cc @@ -37,25 +37,20 @@ namespace test { //----------------------------------------------------------------------------- -CASE ("lonlat -180/+180/-90/+90") { +CASE( "ATLAS-295 (Github PR 32): Fix StructuredColumns when domain is shifted by 180 degrees (Github PR 32)" ) { + auto grid1 = Grid( "S80x40", GlobalDomain{-180} ); - StructuredGrid grid1 (Config ("nx", 80) | Config ("ny", 40) | Config ("type", "shifted_lonlat") - | Config ("domain", Config ("type", "rectangular") | Config ("units", "degrees") - | Config ("xmin", -180.0) | Config ("xmax", +180.0) - | Config ("ymin", -90.0) | Config ("ymax", +90.0))); + functionspace::StructuredColumns fs1( grid1, grid::Partitioner( "checkerboard" ), + Config( "halo", 1 ) | Config( "periodic_points", true ) ); - functionspace::StructuredColumns fs1 (grid1, grid::Partitioner ("checkerboard"), Config ("halo", 1) | Config ("periodic_points", true)); + auto grid2 = Grid{"S80x40"}; - StructuredGrid grid2 (Config ("nx", 80) | Config ("ny", 40) | Config ("type", "shifted_lonlat") - | Config ("domain", Config ("type", "rectangular") | Config ("units", "degrees") - | Config ("xmin", 0.0) | Config ("xmax", 360.0) - | Config ("ymin", -90.0) | Config ("ymax", +90.0))); + functionspace::StructuredColumns fs2( grid2, grid::Partitioner( "checkerboard" ), + Config( "halo", 1 ) | Config( "periodic_points", true ) ); - functionspace::StructuredColumns fs2 (grid2, grid::Partitioner ("checkerboard"), Config ("halo", 1) | Config ("periodic_points", true)); - - EXPECT (grid1.size () == grid2.size ()); - EXPECT (fs1.size () == fs2.size ()); - EXPECT (fs1.sizeOwned () == fs2.sizeOwned ()); + EXPECT_EQ( grid1.size(), grid2.size() ); + EXPECT_EQ( fs1.size(), fs2.size() ); + EXPECT_EQ( fs1.sizeOwned(), fs2.sizeOwned() ); } From 10d8d4959ddbdf0085634b8db2d175de097475d8 Mon Sep 17 00:00:00 2001 From: Pedro Maciel Date: Thu, 28 May 2020 08:34:52 +0100 Subject: [PATCH 102/145] ATLAS-293: semantic fixes --- .../projection/detail/LonLatProjection.cc | 15 +++++---- src/atlas/projection/detail/ProjectionImpl.cc | 32 ++++++++++--------- src/atlas/projection/detail/ProjectionImpl.h | 2 +- src/tests/projection/test_projection_LAEA.cc | 2 +- 4 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/atlas/projection/detail/LonLatProjection.cc b/src/atlas/projection/detail/LonLatProjection.cc index a88859023..d8cc16136 100644 --- a/src/atlas/projection/detail/LonLatProjection.cc +++ b/src/atlas/projection/detail/LonLatProjection.cc @@ -27,7 +27,8 @@ namespace detail { template LonLatProjectionT::LonLatProjectionT( const eckit::Parametrisation& config ) : - ProjectionImpl(), rotation_( config ) {} + ProjectionImpl(), + rotation_( config ) {} template <> void LonLatProjectionT::xy2lonlat( double[] ) const {} @@ -53,8 +54,10 @@ RectangularLonLatDomain LonLatProjectionT::lonlatBoundingBox( const Do RectangularDomain rect( domain ); ATLAS_ASSERT( rect ); - constexpr double h = 0.001; - constexpr size_t Niter = 100; + + const std::string derivative = "central"; + constexpr double h = 0.001; // precision to millidegrees + constexpr size_t Niter = 100; // 1. determine box from projected corners @@ -82,7 +85,7 @@ RectangularLonLatDomain LonLatProjectionT::lonlatBoundingBox( const Do PointXY A = corners[i]; PointXY B = corners[( i + 1 ) % corners.size()]; - std::unique_ptr derivate( DerivateFactory::build( "central", *this, A, B ) ); + std::unique_ptr derivate( DerivateFactory::build( derivative, *this, A, B, h ) ); double dAdy = derivate->d( A ).lat(); double dBdy = derivate->d( B ).lat(); @@ -110,7 +113,7 @@ RectangularLonLatDomain LonLatProjectionT::lonlatBoundingBox( const Do } - // 3. locate latitude extrema by checking if date line is crossed (in the un-projected frame), in which case we + // 3. locate longitude extrema by checking if date line is crossed (in the un-projected frame), in which case we // assume periodicity and if not, find extrema not at the corners by refining iteratively if ( !bounds.crossesDateLine() ) { @@ -144,7 +147,7 @@ RectangularLonLatDomain LonLatProjectionT::lonlatBoundingBox( const Do PointXY A = corners[i]; PointXY B = corners[( i + 1 ) % corners.size()]; - std::unique_ptr derivate( DerivateFactory::build( "central", *this, A, B ) ); + std::unique_ptr derivate( DerivateFactory::build( derivative, *this, A, B, h ) ); double dAdx = derivate->d( A ).lon(); double dBdx = derivate->d( B ).lon(); diff --git a/src/atlas/projection/detail/ProjectionImpl.cc b/src/atlas/projection/detail/ProjectionImpl.cc index e867f38ed..141e95152 100644 --- a/src/atlas/projection/detail/ProjectionImpl.cc +++ b/src/atlas/projection/detail/ProjectionImpl.cc @@ -42,29 +42,30 @@ struct DerivateBuilder : public ProjectionImpl::DerivateFactory { struct DerivateForwards final : ProjectionImpl::Derivate { using Derivate::Derivate; PointLonLat d( PointXY P ) const override { - PointXY A( xy2lonlat( P ) ); - PointXY B( xy2lonlat( PointXY::add( P, H_ ) ) ); - return PointXY::div( PointXY::sub( B, A ), normH_ ); + PointLonLat A( xy2lonlat( P ) ); + PointLonLat B( xy2lonlat( PointXY::add( P, H_ ) ) ); + return PointLonLat::div( PointLonLat::sub( B, A ), normH_ ); } }; struct DerivateBackwards final : ProjectionImpl::Derivate { using Derivate::Derivate; PointLonLat d( PointXY P ) const override { - PointXY A( xy2lonlat( PointXY::sub( P, H_ ) ) ); - PointXY B( xy2lonlat( P ) ); - return PointXY::div( PointXY::sub( B, A ), normH_ ); + PointLonLat A( xy2lonlat( PointXY::sub( P, H_ ) ) ); + PointLonLat B( xy2lonlat( P ) ); + return PointLonLat::div( PointLonLat::sub( B, A ), normH_ ); } }; struct DerivateCentral final : ProjectionImpl::Derivate { DerivateCentral( const ProjectionImpl& p, PointXY A, PointXY B, double h ) : - Derivate( p, A, B, h ), H2_{PointXY::mul( H_, 0.5 )} {} + Derivate( p, A, B, h ), + H2_{PointXY::mul( H_, 0.5 )} {} const PointXY H2_; PointLonLat d( PointXY P ) const override { - PointXY A( xy2lonlat( PointXY::sub( P, H2_ ) ) ); - PointXY B( xy2lonlat( PointXY::add( P, H2_ ) ) ); - return PointXY::div( PointXY::sub( B, A ), normH_ ); + PointLonLat A( xy2lonlat( PointXY::sub( P, H2_ ) ) ); + PointLonLat B( xy2lonlat( PointXY::add( P, H2_ ) ) ); + return PointLonLat::div( PointLonLat::sub( B, A ), normH_ ); } }; @@ -225,8 +226,9 @@ RectangularLonLatDomain ProjectionImpl::lonlatBoundingBox( const Domain& domain RectangularDomain rect( domain ); ATLAS_ASSERT( rect ); - constexpr double h = 0.5e-6; // precision to microdegrees; - constexpr size_t Niter = 100; + const std::string derivative = "central"; + constexpr double h = 0.5e-6; // precision to microdegrees + constexpr size_t Niter = 100; // 1. determine box from projected corners @@ -248,7 +250,7 @@ RectangularLonLatDomain ProjectionImpl::lonlatBoundingBox( const Domain& domain PointXY A = corners[i]; PointXY B = corners[( i + 1 ) % corners.size()]; - std::unique_ptr derivate( DerivateFactory::build( "central", *this, A, B ) ); + std::unique_ptr derivate( DerivateFactory::build( derivative, *this, A, B, h ) ); double dAdy = derivate->d( A ).lat(); double dBdy = derivate->d( B ).lat(); @@ -276,14 +278,14 @@ RectangularLonLatDomain ProjectionImpl::lonlatBoundingBox( const Domain& domain } - // 3. locate latitude extrema not at the corners by refining iteratively + // 3. locate longitude extrema not at the corners by refining iteratively for ( size_t i = 0; i < corners.size(); ++i ) { if ( !bounds.crossesDateLine() ) { PointXY A = corners[i]; PointXY B = corners[( i + 1 ) % corners.size()]; - std::unique_ptr derivate( DerivateFactory::build( "central", *this, A, B ) ); + std::unique_ptr derivate( DerivateFactory::build( derivative, *this, A, B, h) ); double dAdx = derivate->d( A ).lon(); double dBdx = derivate->d( B ).lon(); diff --git a/src/atlas/projection/detail/ProjectionImpl.h b/src/atlas/projection/detail/ProjectionImpl.h index 6dbb76250..14d17a80a 100644 --- a/src/atlas/projection/detail/ProjectionImpl.h +++ b/src/atlas/projection/detail/ProjectionImpl.h @@ -127,7 +127,7 @@ class ProjectionImpl : public util::Object { struct DerivateFactory : public util::Factory { static std::string className() { return "DerivateFactory"; } static ProjectionImpl::Derivate* build( const std::string& type, const ProjectionImpl& p, PointXY A, PointXY B, - double h = 0.001 ); + double h ); protected: using Factory::Factory; diff --git a/src/tests/projection/test_projection_LAEA.cc b/src/tests/projection/test_projection_LAEA.cc index 695cc3bda..a9c9a62c1 100644 --- a/src/tests/projection/test_projection_LAEA.cc +++ b/src/tests/projection/test_projection_LAEA.cc @@ -148,7 +148,7 @@ CASE( "test_grid_creation_from_GRIB" ) { EXPECT_APPROX_EQ( bb.west(), -90., tolerance ); EXPECT_APPROX_EQ( bb.east(), 41.882046, tolerance ); EXPECT_APPROX_EQ( bb.south(), 3.818356, tolerance ); - EXPECT_APPROX_EQ( bb.north(), 76.040387, tolerance ); + EXPECT_APPROX_EQ( bb.north(), 76.040329, tolerance ); for ( PointLonLat p : g.lonlat() ) { EXPECT( bb.contains( p ) ); } From 42a246f3e2d7f9d6fdccc0f5f885efb7a84a2c93 Mon Sep 17 00:00:00 2001 From: Pedro Maciel Date: Thu, 28 May 2020 15:57:57 +0100 Subject: [PATCH 103/145] ATLAS-293: fix using a central longitude, for which projected/calculated longitude are corrected within +-180 degree range --- .../regional_polar_stereographic_1.yml | 20 ++++---- src/atlas/projection/detail/ProjectionImpl.cc | 50 ++++++++++++++----- src/atlas/projection/detail/ProjectionImpl.h | 14 +++--- 3 files changed, 54 insertions(+), 30 deletions(-) diff --git a/doc/example-grids/regional_polar_stereographic_1.yml b/doc/example-grids/regional_polar_stereographic_1.yml index f7b23bf4d..030d7cae7 100644 --- a/doc/example-grids/regional_polar_stereographic_1.yml +++ b/doc/example-grids/regional_polar_stereographic_1.yml @@ -1,26 +1,26 @@ type : "regional" nx : 553 ny : 425 -dx : 11250 -dy : 11250 -lonlat(xmin,ymin) : [187,30] +dx : 11250. +dy : 11250. +lonlat(xmin,ymin) : [187., 30.] projection: type : "proj" proj : "+proj=stere +lat_ts=60 +lat_0=90 +lon_0=225 +k_0=1 +R=6371229.0" - normalise : [0,360] # no longer necessary after bugfix of ATLAS-293 check : # optional section, intended for unit-testing - strict: false # false -> allow longitude values to be different due to normalisation only + strict: false # -> allow longitude values to be different due to normalisation only uid : ignore # -> special keyword that ignores uid check, but issues warning size : 235025 - lonlat(first) : [187,30] - lonlat(last) : [297.1496,70.1114] - bounding_box(n,w,s,e) : [83.8474, 143.5977, 30.0000, 297.1496] + lonlat(first) : [ 187., 30. ] + lonlat(last) : [ 297.1496, 70.1114 ] + bounding_box(n,w,s,e) : [ 83.84744, -216.402255, 30., -62.8504 ] xmin : -4225928.24 ymin : -5408941.49 roundtrip: # check that projection can do roundtrip for following values - - lonlat: [ 187,30 ] - - lonlat: [ 297.1496,70.1114 ] + - lonlat: [ 187., 30. ] + - lonlat: [ 297.1496, 70.1114 ] + diff --git a/src/atlas/projection/detail/ProjectionImpl.cc b/src/atlas/projection/detail/ProjectionImpl.cc index 141e95152..c23bb0cb9 100644 --- a/src/atlas/projection/detail/ProjectionImpl.cc +++ b/src/atlas/projection/detail/ProjectionImpl.cc @@ -17,7 +17,7 @@ #include "eckit/utils/Hash.h" #include "eckit/utils/MD5.h" -#include "ProjectionImpl.h" +#include "atlas/projection/detail/ProjectionImpl.h" #include "atlas/projection/detail/ProjectionFactory.h" #include "atlas/runtime/Exception.h" @@ -31,11 +31,22 @@ namespace detail { namespace { +void longitude_in_range( double reference, double& lon ) { + // keep longitude difference (to reference) range below +-180 degree + while ( lon > reference + 180. ) { + lon -= 360.; + } + while ( lon <= reference - 180. ) { + lon += 360.; + } +} + template struct DerivateBuilder : public ProjectionImpl::DerivateFactory { using DerivateFactory::DerivateFactory; - ProjectionImpl::Derivate* make( const ProjectionImpl& p, PointXY A, PointXY B, double h ) override { - return new T( p, A, B, h ); + ProjectionImpl::Derivate* make( const ProjectionImpl& p, PointXY A, PointXY B, double h, + double refLongitude ) override { + return new T( p, A, B, h, refLongitude ); } }; @@ -58,8 +69,8 @@ struct DerivateBackwards final : ProjectionImpl::Derivate { }; struct DerivateCentral final : ProjectionImpl::Derivate { - DerivateCentral( const ProjectionImpl& p, PointXY A, PointXY B, double h ) : - Derivate( p, A, B, h ), + DerivateCentral( const ProjectionImpl& p, PointXY A, PointXY B, double h, double refLongitude ) : + Derivate( p, A, B, h, refLongitude ), H2_{PointXY::mul( H_, 0.5 )} {} const PointXY H2_; PointLonLat d( PointXY P ) const override { @@ -71,15 +82,17 @@ struct DerivateCentral final : ProjectionImpl::Derivate { } // namespace -ProjectionImpl::Derivate::Derivate( const ProjectionImpl& p, PointXY A, PointXY B, double h ) : +ProjectionImpl::Derivate::Derivate( const ProjectionImpl& p, PointXY A, PointXY B, double h, double refLongitude ) : projection_( p ), H_{PointXY::mul( PointXY::normalize( PointXY::sub( B, A ) ), h )}, - normH_( PointXY::norm( H_ ) ) {} + normH_( PointXY::norm( H_ ) ), + refLongitude_( refLongitude ) {} ProjectionImpl::Derivate::~Derivate() = default; ProjectionImpl::Derivate* ProjectionImpl::DerivateFactory::build( const std::string& type, const ProjectionImpl& p, - PointXY A, PointXY B, double h ) { + PointXY A, PointXY B, double h, + double refLongitude ) { ATLAS_ASSERT( 0. < h ); // force_link @@ -92,7 +105,7 @@ ProjectionImpl::Derivate* ProjectionImpl::DerivateFactory::build( const std::str using Derivate::Derivate; PointLonLat d( PointXY ) const override { return {}; } }; - return new DerivateDegenerate( p, A, B, h ); + return new DerivateDegenerate( p, A, B, h, refLongitude ); } auto factory = get( type ); @@ -101,6 +114,13 @@ ProjectionImpl::Derivate* ProjectionImpl::DerivateFactory::build( const std::str ProjectionImpl::DerivateFactory::~DerivateFactory() = default; +PointLonLat ProjectionImpl::Derivate::xy2lonlat( const PointXY& p ) const { + PointLonLat q( p ); + projection_.xy2lonlat( q.data() ); + longitude_in_range( refLongitude_, q.lon() ); + return q; +} + // -------------------------------------------------------------------------------------------------------------------- ProjectionImpl::BoundLonLat::operator RectangularLonLatDomain() const { @@ -226,6 +246,10 @@ RectangularLonLatDomain ProjectionImpl::lonlatBoundingBox( const Domain& domain RectangularDomain rect( domain ); ATLAS_ASSERT( rect ); + // use central longitude as absolute reference (keep points within +-180 longitude range) + const auto centre = lonlat( {( rect.xmin() + rect.xmax() ) / 2., ( rect.ymin() + rect.ymax() ) / 2.} ); + + const std::string derivative = "central"; constexpr double h = 0.5e-6; // precision to microdegrees constexpr size_t Niter = 100; @@ -238,7 +262,9 @@ RectangularLonLatDomain ProjectionImpl::lonlatBoundingBox( const Domain& domain BoundLonLat bounds; for ( auto& p : corners ) { - bounds.extend( lonlat( p ), PointLonLat{h, h} ); + auto q = lonlat( p ); + longitude_in_range( centre.lon(), q.lon() ); + bounds.extend( q, PointLonLat{h, h} ); } @@ -250,7 +276,7 @@ RectangularLonLatDomain ProjectionImpl::lonlatBoundingBox( const Domain& domain PointXY A = corners[i]; PointXY B = corners[( i + 1 ) % corners.size()]; - std::unique_ptr derivate( DerivateFactory::build( derivative, *this, A, B, h ) ); + std::unique_ptr derivate( DerivateFactory::build( derivative, *this, A, B, h, centre.lon() ) ); double dAdy = derivate->d( A ).lat(); double dBdy = derivate->d( B ).lat(); @@ -285,7 +311,7 @@ RectangularLonLatDomain ProjectionImpl::lonlatBoundingBox( const Domain& domain PointXY A = corners[i]; PointXY B = corners[( i + 1 ) % corners.size()]; - std::unique_ptr derivate( DerivateFactory::build( derivative, *this, A, B, h) ); + std::unique_ptr derivate( DerivateFactory::build( derivative, *this, A, B, h, centre.lon() ) ); double dAdx = derivate->d( A ).lon(); double dBdx = derivate->d( B ).lon(); diff --git a/src/atlas/projection/detail/ProjectionImpl.h b/src/atlas/projection/detail/ProjectionImpl.h index 14d17a80a..45b3fa38f 100644 --- a/src/atlas/projection/detail/ProjectionImpl.h +++ b/src/atlas/projection/detail/ProjectionImpl.h @@ -109,7 +109,7 @@ class ProjectionImpl : public util::Object { }; struct Derivate { - Derivate( const ProjectionImpl& p, PointXY A, PointXY B, double h ); + Derivate( const ProjectionImpl& p, PointXY A, PointXY B, double h, double refLongitude = 0. ); virtual ~Derivate(); virtual PointLonLat d( PointXY ) const = 0; @@ -117,22 +117,20 @@ class ProjectionImpl : public util::Object { const ProjectionImpl& projection_; const PointXY H_; const double normH_; - PointLonLat xy2lonlat( const PointXY& p ) const { - PointLonLat q( p ); - projection_.xy2lonlat( q.data() ); - return q; - } + const double refLongitude_; + PointLonLat xy2lonlat( const PointXY& p ) const; }; struct DerivateFactory : public util::Factory { static std::string className() { return "DerivateFactory"; } static ProjectionImpl::Derivate* build( const std::string& type, const ProjectionImpl& p, PointXY A, PointXY B, - double h ); + double h, double refLongitude = 0. ); protected: using Factory::Factory; virtual ~DerivateFactory(); - virtual ProjectionImpl::Derivate* make( const ProjectionImpl& p, PointXY A, PointXY B, double h ) = 0; + virtual ProjectionImpl::Derivate* make( const ProjectionImpl& p, PointXY A, PointXY B, double h, + double refLongitude = 0. ) = 0; }; }; From b860c916ac374c824d2d0a0bc49831cea932e542 Mon Sep 17 00:00:00 2001 From: Pedro Maciel Date: Thu, 28 May 2020 20:51:00 +0100 Subject: [PATCH 104/145] ProjectionImpl::lonlatBoundingBox perturbation updated to h=0.5e-6 degree (from 1e-3), related change --- doc/example-grids/regional_lambert_conformal_conic_2.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/example-grids/regional_lambert_conformal_conic_2.yml b/doc/example-grids/regional_lambert_conformal_conic_2.yml index 422375cd5..b7cdc8673 100644 --- a/doc/example-grids/regional_lambert_conformal_conic_2.yml +++ b/doc/example-grids/regional_lambert_conformal_conic_2.yml @@ -16,6 +16,6 @@ check : lonlat(first) : [-126.138,16.281] lonlat(last) : [-57.3811,55.4813] uid : b185c7c56b3a1abaa5bf23b35ed12661 - bounding_box(n,w,s,e) : [58.3654,-139.856,16.281,-57.3811] + bounding_box(n,w,s,e) : [58.365355,-139.856122,16.281,-57.381070] xmin : -3332155.29 ymin : -588892.76 From f6b4a1674ae874241b54743e541096961069e817 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 1 Jun 2020 17:27:11 +0100 Subject: [PATCH 105/145] Projection::lonlatBoundingBox: change h=micrometers to h=decimeters for projections in meters --- src/atlas/projection/detail/LonLatProjection.cc | 3 +-- src/atlas/projection/detail/ProjectionImpl.cc | 15 ++++++++------- src/tests/projection/test_projection_LAEA.cc | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/atlas/projection/detail/LonLatProjection.cc b/src/atlas/projection/detail/LonLatProjection.cc index d8cc16136..1220da862 100644 --- a/src/atlas/projection/detail/LonLatProjection.cc +++ b/src/atlas/projection/detail/LonLatProjection.cc @@ -27,8 +27,7 @@ namespace detail { template LonLatProjectionT::LonLatProjectionT( const eckit::Parametrisation& config ) : - ProjectionImpl(), - rotation_( config ) {} + ProjectionImpl(), rotation_( config ) {} template <> void LonLatProjectionT::xy2lonlat( double[] ) const {} diff --git a/src/atlas/projection/detail/ProjectionImpl.cc b/src/atlas/projection/detail/ProjectionImpl.cc index c23bb0cb9..ff77ffeea 100644 --- a/src/atlas/projection/detail/ProjectionImpl.cc +++ b/src/atlas/projection/detail/ProjectionImpl.cc @@ -70,8 +70,7 @@ struct DerivateBackwards final : ProjectionImpl::Derivate { struct DerivateCentral final : ProjectionImpl::Derivate { DerivateCentral( const ProjectionImpl& p, PointXY A, PointXY B, double h, double refLongitude ) : - Derivate( p, A, B, h, refLongitude ), - H2_{PointXY::mul( H_, 0.5 )} {} + Derivate( p, A, B, h, refLongitude ), H2_{PointXY::mul( H_, 0.5 )} {} const PointXY H2_; PointLonLat d( PointXY P ) const override { PointLonLat A( xy2lonlat( PointXY::sub( P, H2_ ) ) ); @@ -249,11 +248,13 @@ RectangularLonLatDomain ProjectionImpl::lonlatBoundingBox( const Domain& domain // use central longitude as absolute reference (keep points within +-180 longitude range) const auto centre = lonlat( {( rect.xmin() + rect.xmax() ) / 2., ( rect.ymin() + rect.ymax() ) / 2.} ); - + const std::string derivative = "central"; - constexpr double h = 0.5e-6; // precision to microdegrees + constexpr double h_deg = 0.5e-6; // precision to microdegrees + constexpr double h_meters = 0.5e-1; // precision to decimeters constexpr size_t Niter = 100; + const double h = units() == "degrees" ? h_deg : h_meters; // 1. determine box from projected corners @@ -264,7 +265,7 @@ RectangularLonLatDomain ProjectionImpl::lonlatBoundingBox( const Domain& domain for ( auto& p : corners ) { auto q = lonlat( p ); longitude_in_range( centre.lon(), q.lon() ); - bounds.extend( q, PointLonLat{h, h} ); + bounds.extend( q, PointLonLat{h_deg, h_deg} ); } @@ -298,7 +299,7 @@ RectangularLonLatDomain ProjectionImpl::lonlatBoundingBox( const Domain& domain } // update extrema, extended by 'a small amount' (arbitrary) - bounds.extend( lonlat( PointXY::middle( A, B ) ), PointLonLat{0, h} ); + bounds.extend( lonlat( PointXY::middle( A, B ) ), PointLonLat{0, h_deg} ); } } } @@ -333,7 +334,7 @@ RectangularLonLatDomain ProjectionImpl::lonlatBoundingBox( const Domain& domain } // update extrema, extended by 'a small amount' (arbitrary) - bounds.extend( lonlat( PointXY::middle( A, B ) ), PointLonLat{h, 0} ); + bounds.extend( lonlat( PointXY::middle( A, B ) ), PointLonLat{h_deg, 0} ); } } } diff --git a/src/tests/projection/test_projection_LAEA.cc b/src/tests/projection/test_projection_LAEA.cc index a9c9a62c1..695cc3bda 100644 --- a/src/tests/projection/test_projection_LAEA.cc +++ b/src/tests/projection/test_projection_LAEA.cc @@ -148,7 +148,7 @@ CASE( "test_grid_creation_from_GRIB" ) { EXPECT_APPROX_EQ( bb.west(), -90., tolerance ); EXPECT_APPROX_EQ( bb.east(), 41.882046, tolerance ); EXPECT_APPROX_EQ( bb.south(), 3.818356, tolerance ); - EXPECT_APPROX_EQ( bb.north(), 76.040329, tolerance ); + EXPECT_APPROX_EQ( bb.north(), 76.040387, tolerance ); for ( PointLonLat p : g.lonlat() ) { EXPECT( bb.contains( p ) ); } From ddf4a705a1f20518914436ac98ce390109767017 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 1 Jun 2020 12:32:05 +0100 Subject: [PATCH 106/145] Improve apply-clang-format.sh script --- tools/apply-clang-format.sh | 89 +++++++++++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 4 deletions(-) diff --git a/tools/apply-clang-format.sh b/tools/apply-clang-format.sh index 68e10cd40..a86527b57 100755 --- a/tools/apply-clang-format.sh +++ b/tools/apply-clang-format.sh @@ -10,6 +10,21 @@ _REQUIRED_CLANG_VERSION='9.0.1' +function help() { + cat < $(which clang-format) --version" echo " $(clang-format --version)" - if [[ $1 =~ --no-version-check ]]; then - echo "--no-version-check --> Continue anyway" + if [[ $force =~ "yes" ]]; then + echo "--force --> Continue anyway" else exit 1 fi @@ -31,9 +95,26 @@ fi SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" cd $SCRIPTDIR/../src -echo "Applying $(clang-format --version) ..." -find . -iname *.h -o -iname *.cc | xargs clang-format -i -style=file +if [[ $all =~ "yes" ]]; then + echo "Applying $(clang-format --version) to all files ..." + if [[ $dryrun =~ "yes" ]]; then + echo "+ find . -iname *.h -o -iname *.cc | xargs clang-format -i -style=file" + else + find . -iname *.h -o -iname *.cc | xargs clang-format -i -style=file + fi + +else + echo "Applying $(clang-format --version) to files ..." + while test $# -gt 0; do + if [[ $dryrun =~ "yes" ]]; then + echo "+ clang-format -i -style=file $1" + else + clang-format -i -style=file $1 + fi + shift + done +fi echo "Applying $(clang-format --version) ... done" From c6d9bc6ba1d77615b619291c482196c10d4d4bb0 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Sat, 30 May 2020 15:08:46 +0100 Subject: [PATCH 107/145] atlas-grids: use grid.y(j) instead of grid.yspace()[j] --- src/apps/atlas-grids.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/apps/atlas-grids.cc b/src/apps/atlas-grids.cc index ab5f8a38d..930ef648c 100644 --- a/src/apps/atlas-grids.cc +++ b/src/apps/atlas-grids.cc @@ -251,9 +251,8 @@ int AtlasGrids::execute( const Args& args ) { if ( structuredgrid.xspace().nxmax() == structuredgrid.xspace().nxmin() ) { Log::info() << " dx : " << structuredgrid.xspace().dx()[0] / 1000. << " km" << std::endl; } - Log::info() << " dy : " - << std::abs( structuredgrid.yspace()[1] - structuredgrid.yspace()[0] ) / 1000. << " km" - << std::endl; + Log::info() << " dy : " << std::abs( structuredgrid.y( 1 ) - structuredgrid.y( 0 ) ) / 1000. + << " km" << std::endl; Log::info() << " lonlat(centre) : " << grid.projection().lonlat( {0.5 * ( structuredgrid.xspace().max() + structuredgrid.xspace().min() ), From 9775e46d8feb1bf3af41d8499a9b2c6aec8d8693 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 1 Jun 2020 12:29:28 +0100 Subject: [PATCH 108/145] ATLAS-297 Trace computations of Gaussian latitudes and quadrature --- src/atlas/grid/detail/spacing/gaussian/Latitudes.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/atlas/grid/detail/spacing/gaussian/Latitudes.cc b/src/atlas/grid/detail/spacing/gaussian/Latitudes.cc index ad4d317e8..f0b61b643 100644 --- a/src/atlas/grid/detail/spacing/gaussian/Latitudes.cc +++ b/src/atlas/grid/detail/spacing/gaussian/Latitudes.cc @@ -15,12 +15,15 @@ #include #include +#include "eckit/log/Bytes.h" + #include "atlas/array.h" #include "atlas/grid/detail/spacing/gaussian/Latitudes.h" #include "atlas/grid/detail/spacing/gaussian/N.h" #include "atlas/library/config.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" +#include "atlas/runtime/Trace.h" #include "atlas/util/Constants.h" #include "atlas/util/CoordinateEnums.h" @@ -239,7 +242,9 @@ void legpol_quadrature( const int kn, const double pfn[], double& pl, double& pw //----------------------------------------------------------------------------- void compute_gaussian_quadrature_npole_equator( const size_t N, double lats[], double weights[] ) { - Log::debug() << "Atlas computing Gaussian latitudes for N " << N << "\n"; + Log::debug() << "Atlas computing Gaussian latitudes for N " << N << " which requires temporary memory of " + << eckit::Bytes( sizeof( double ) * ( 2 * N + 1 ) * ( 2 * N + 1 ) ) << std::endl; + ATLAS_TRACE(); // Compute first guess for colatitudes in radians double z; @@ -250,6 +255,8 @@ void compute_gaussian_quadrature_npole_equator( const size_t N, double lats[], d int kdgl = 2 * N; array::ArrayT zfn_( kdgl + 1, kdgl + 1 ); + // WARNING: potentially HUGE allocation ( N=16000 --> 7.6 Gbytes ) + ArrayView zfn = make_view( zfn_ ); int iodd; From 88b9c47aa7a670c401c3e43931d039fc11532b19 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 1 Jun 2020 13:25:59 +0100 Subject: [PATCH 109/145] ATLAS-297 Create test that demonstrates heavy resource usage of large StructuredGrid with cropping Co-authored-by: Philippe Marguinaud --- src/tests/grid/CMakeLists.txt | 1 + src/tests/grid/test_largegrid.cc | 70 ++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 src/tests/grid/test_largegrid.cc diff --git a/src/tests/grid/CMakeLists.txt b/src/tests/grid/CMakeLists.txt index fe0cfd719..d23f9ea5d 100644 --- a/src/tests/grid/CMakeLists.txt +++ b/src/tests/grid/CMakeLists.txt @@ -30,6 +30,7 @@ foreach(test test_vertical test_spacing test_state + test_largegrid test_grid_hash) ecbuild_add_test( TARGET atlas_${test} SOURCES ${test}.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) diff --git a/src/tests/grid/test_largegrid.cc b/src/tests/grid/test_largegrid.cc new file mode 100644 index 000000000..5ca5032ad --- /dev/null +++ b/src/tests/grid/test_largegrid.cc @@ -0,0 +1,70 @@ +#include +#include + +#include "eckit/log/Bytes.h" +#include "eckit/log/ResourceUsage.h" +#include "eckit/system/ResourceUsage.h" + +#include "atlas/domain.h" +#include "atlas/grid.h" +#include "atlas/runtime/Trace.h" +#include "atlas/runtime/trace/StopWatch.h" +#include "tests/AtlasTestEnvironment.h" + +namespace atlas { +namespace test { + +// Computation of gaussian latitudes for large grids can be quite costly in terms of +// time and memory. +// Atlas has a number of commonly used resolutions precomputed which can speed up Gaussian grids +// and prevents large peaks in memory usage. See gaussian Spacing. +// +// If more high resolution Gaussian latitudes need to be precomputed they can be added there, or in +// a dynamic library with self-registration. + +struct Trace { + struct Indentor { + Indentor() { Log::info().indent(); } + ~Indentor() { Log::info().unindent(); } + } indentor; + eckit::ResourceUsage resource_usage; + atlas::Trace trace; + + Trace( const eckit::CodeLocation& where, const std::string& what ) : + indentor{}, resource_usage{what, Log::debug()}, trace{where, what} {} + + double elapsed() const { return trace.elapsed(); } + static size_t peakMemory() { return eckit::system::ResourceUsage().maxResidentSetSize(); } + void stopAndReport() { + trace.stop(); + Log::info() << "Time taken: " << elapsed() << " sec" << std::endl; + Log::info() << "Peak memory usage: " << eckit::Bytes( peakMemory() ) << std::endl; + } +}; + +constexpr size_t Mbytes = 1000000; + +CASE( "test resources for cropping large grids" ) { + std::vector gridnames{"L40000x20000", "N8000", "O8000"}; + + for ( auto& gridname : gridnames ) { + // EXPECT( Trace::peakMemory() < 100 * Mbytes ); + + SECTION( std::string( "section" ) + gridname ) { + Trace trace( Here(), "Grid{" + gridname + ", GlobalDomain{-180}}" ); + auto grid = Grid{gridname, GlobalDomain{-180}}; + trace.stopAndReport(); + // EXPECT( trace.elapsed() < 10. ); + // EXPECT( trace.peakMemory() < 100 * Mbytes ); + } + } +} + + +} // namespace test +} // namespace atlas + + +int main( int argc, char* argv[] ) { + return atlas::test::run( argc, argv ); +} From aac63d4eef4084e74148971741fdbbbc2fd2f15a Mon Sep 17 00:00:00 2001 From: Philippe Marguinaud Date: Mon, 1 Jun 2020 13:26:14 +0100 Subject: [PATCH 110/145] ATLAS-297 First implementation for heavy resource usage of cropped regular StructuredGrid --- src/atlas/grid/detail/grid/Structured.cc | 44 ++++++++++++++++++------ 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/src/atlas/grid/detail/grid/Structured.cc b/src/atlas/grid/detail/grid/Structured.cc index 679069ef4..a2a9e26f1 100644 --- a/src/atlas/grid/detail/grid/Structured.cc +++ b/src/atlas/grid/detail/grid/Structured.cc @@ -354,8 +354,14 @@ void Structured::crop( const Domain& dom ) { } ATLAS_ASSERT( dom.units() == projection().units() ); + auto rect_domain = RectangularDomain( dom ); + bool samerows = true; + + for (idx_t j = 0; samerows && (j < ny ()); j++) + samerows = samerows && (xmin (j) == xmin (0)) && (dx (j) == dx (0)); + if ( !rect_domain ) { std::stringstream errmsg; errmsg << "Cannot crop the grid with domain " << dom; @@ -389,17 +395,27 @@ void Structured::crop( const Domain& dom ) { // Cropping in X Normalise normalise( rect_domain ); for ( idx_t j = jmin, jcropped = 0; j <= jmax; ++j, ++jcropped ) { - idx_t n = 0; - for ( idx_t i = 0; i < nx( j ); ++i ) { - const double _x = normalise( x( i, j ) ); - if ( rect_domain.contains_x( _x ) ) { - cropped_xmin[jcropped] = std::min( cropped_xmin[jcropped], _x ); - cropped_xmax[jcropped] = std::max( cropped_xmax[jcropped], _x ); - ++n; + if ((j == jmin) || (! samerows)) + { + idx_t n = 0; + for ( idx_t i = 0; i < nx( j ); ++i ) { + const double _x = normalise( x( i, j ) ); + if ( rect_domain.contains_x( _x ) ) { + cropped_xmin[jcropped] = std::min( cropped_xmin[jcropped], _x ); + cropped_xmax[jcropped] = std::max( cropped_xmax[jcropped], _x ); + ++n; + } } - } - cropped_nx[jcropped] = n; + cropped_nx[jcropped] = n; + } + else + { + cropped_xmin[jcropped] = cropped_xmin[jcropped-1]; + cropped_xmax[jcropped] = cropped_xmax[jcropped-1]; + cropped_nx[jcropped] = cropped_nx[jcropped-1]; + } } + bool endpoint = true; if ( ZonalBandDomain( rect_domain ) ) { for ( idx_t j = 0; j < cropped_ny; ++j ) { @@ -416,6 +432,7 @@ void Structured::crop( const Domain& dom ) { idx_t cropped_nxmin, cropped_nxmax; cropped_nxmin = cropped_nxmax = cropped_nx.front(); + for ( idx_t j = 1; j < cropped_ny; ++j ) { cropped_nxmin = std::min( cropped_nx[j], cropped_nxmin ); cropped_nxmax = std::max( cropped_nx[j], cropped_nxmax ); @@ -424,8 +441,15 @@ void Structured::crop( const Domain& dom ) { std::vector cropped_xspacings( cropped_ny ); for ( idx_t j = 0; j < cropped_ny; ++j ) { - cropped_xspacings[j] = new spacing::LinearSpacing( cropped_xmin[j], cropped_xmax[j], cropped_nx[j], endpoint ); + spacing::LinearSpacing * sp = nullptr; + idx_t i = j - 1; + if ((j > 0) && samerows) + sp = dynamic_cast (cropped_xspacings[i].get ()); + else + sp = new spacing::LinearSpacing( cropped_xmin[j], cropped_xmax[j], cropped_nx[j], endpoint ); + cropped_xspacings[j] = sp; } + XSpace cropped_xspace( cropped_xspacings ); for ( idx_t j = 0; j < cropped_ny; ++j ) { From 1ec5ebe0ae7cdc920c74023ef783d6b9ba2e1821 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 1 Jun 2020 12:30:04 +0100 Subject: [PATCH 111/145] ATLAS-297 Generalize heavy resource usage of large reduced StructuredGrid with cropping --- src/atlas/grid/detail/grid/Structured.cc | 234 ++++++++++++++---- src/atlas/grid/detail/grid/Structured.h | 9 +- .../grid/detail/spacing/LinearSpacing.cc | 11 + src/atlas/grid/detail/spacing/LinearSpacing.h | 2 + 4 files changed, 203 insertions(+), 53 deletions(-) diff --git a/src/atlas/grid/detail/grid/Structured.cc b/src/atlas/grid/detail/grid/Structured.cc index a2a9e26f1..04a3d16d7 100644 --- a/src/atlas/grid/detail/grid/Structured.cc +++ b/src/atlas/grid/detail/grid/Structured.cc @@ -26,6 +26,7 @@ #include "atlas/grid/detail/spacing/LinearSpacing.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" +#include "atlas/runtime/Trace.h" #include "atlas/util/NormaliseLongitude.h" #include "atlas/util/Point.h" #include "atlas/util/UnitSphere.h" @@ -110,10 +111,13 @@ template Structured::XSpace::XSpace( const std::array& interval, cons Structured::XSpace::XSpace( const std::array& interval, std::initializer_list&& N, bool endpoint ) : XSpace( interval, std::vector{N}, endpoint ) {} -Structured::XSpace::XSpace( const Spacing& spacing ) : impl_( new Implementation( spacing ) ) {} +Structured::XSpace::XSpace( const Spacing& spacing, idx_t ny ) : impl_( new Implementation( spacing, ny ) ) {} Structured::XSpace::XSpace( const std::vector& spacings ) : impl_( new Implementation( spacings ) ) {} +Structured::XSpace::XSpace( const std::vector& spacings ) : + impl_( new Implementation( spacings ) ) {} + Structured::XSpace::XSpace( const Config& config ) : impl_( new Implementation( config ) ) {} Structured::XSpace::XSpace( const std::vector& config ) : impl_( new Implementation( config ) ) {} @@ -209,6 +213,28 @@ Structured::XSpace::Implementation::Implementation( const std::vector& c } } +Structured::XSpace::Implementation::Implementation( const std::vector& spacings ) { + reserve( spacings.size() ); + + nxmin_ = std::numeric_limits::max(); + nxmax_ = 0; + min_ = std::numeric_limits::max(); + max_ = -std::numeric_limits::max(); + + for ( idx_t j = 0; j < ny(); ++j ) { + const auto& xspace = spacings[j]; + xmin_.push_back( xspace.start ); + xmax_.push_back( xspace.end ); + nx_.push_back( xspace.N ); + dx_.push_back( xspace.step ); + nxmin_ = std::min( nxmin_, nx_[j] ); + nxmax_ = std::max( nxmax_, nx_[j] ); + min_ = std::min( min_, xspace.start ); + max_ = std::max( max_, xspace.end ); + } +} + + void Structured::XSpace::Implementation::Implementation::reserve( idx_t ny ) { ny_ = ny; nx_.reserve( ny ); @@ -244,14 +270,17 @@ Structured::XSpace::Implementation::Implementation( const std::array& Implementation( interval, std::vector{N}, endpoint ) {} -Structured::XSpace::Implementation::Implementation( const Spacing& spacing ) : - ny_( 1 ), nx_( ny_, spacing.size() ), xmin_( ny_, spacing.min() ), xmax_( ny_, spacing.max() ), dx_( ny_ ) { +Structured::XSpace::Implementation::Implementation( const Spacing& spacing, idx_t ny ) : + ny_( ny ), nx_( ny_, spacing.size() ), xmin_( ny_, spacing.min() ), xmax_( ny_, spacing.max() ), dx_( ny_ ) { const spacing::LinearSpacing& linspace = dynamic_cast( *spacing.get() ); dx_[0] = linspace.step(); - nxmax_ = nx_[0]; - nxmin_ = nx_[0]; - min_ = spacing.min(); - max_ = spacing.max(); + for ( idx_t j = 1; j < ny_; ++j ) { + dx_[j] = dx_[0]; + } + nxmax_ = nx_[0]; + nxmin_ = nx_[0]; + min_ = spacing.min(); + max_ = spacing.max(); } Structured::XSpace::Implementation::Implementation( const std::vector& spacings ) : @@ -347,6 +376,8 @@ class Normalise { } // namespace void Structured::crop( const Domain& dom ) { + using eckit::types::is_approximately_equal; + if ( !dom ) { domain_ = RectangularDomain( {xspace_.min(), xspace_.max()}, {yspace_.min(), yspace_.max()}, projection_.units() ); @@ -354,14 +385,8 @@ void Structured::crop( const Domain& dom ) { } ATLAS_ASSERT( dom.units() == projection().units() ); - auto rect_domain = RectangularDomain( dom ); - bool samerows = true; - - for (idx_t j = 0; samerows && (j < ny ()); j++) - samerows = samerows && (xmin (j) == xmin (0)) && (dx (j) == dx (0)); - if ( !rect_domain ) { std::stringstream errmsg; errmsg << "Cannot crop the grid with domain " << dom; @@ -392,37 +417,141 @@ void Structured::crop( const Domain& dom ) { std::vector cropped_xmax( cropped_ny, -std::numeric_limits::max() ); std::vector cropped_nx( cropped_ny ); - // Cropping in X Normalise normalise( rect_domain ); - for ( idx_t j = jmin, jcropped = 0; j <= jmax; ++j, ++jcropped ) { - if ((j == jmin) || (! samerows)) - { - idx_t n = 0; - for ( idx_t i = 0; i < nx( j ); ++i ) { - const double _x = normalise( x( i, j ) ); - if ( rect_domain.contains_x( _x ) ) { - cropped_xmin[jcropped] = std::min( cropped_xmin[jcropped], _x ); - cropped_xmax[jcropped] = std::max( cropped_xmax[jcropped], _x ); - ++n; - } + + auto index_of_smallest_normalised_x = [&]( idx_t j ) { + // Compute point of normalisation with bisection method + idx_t imin = 0; + idx_t imax = nx( j ) - 1; + auto normalised_x = [&]( idx_t i ) { return normalise( x( i, j ) ); }; + double xmin = normalised_x( imin ); + double xmax = normalised_x( imax ); + idx_t max_iter = nx( j ); + idx_t iter = 0; + while ( xmax < xmin && imax != imin + 1 ) { + idx_t imid = ( imin + imax ) / 2; + if ( normalised_x( imid ) > xmin ) { + imin = imid; + xmin = normalised_x( imin ); } - cropped_nx[jcropped] = n; - } - else - { - cropped_xmin[jcropped] = cropped_xmin[jcropped-1]; - cropped_xmax[jcropped] = cropped_xmax[jcropped-1]; - cropped_nx[jcropped] = cropped_nx[jcropped-1]; - } - } + else { + imax = imid; + xmax = normalised_x( imax ); + } + if ( iter == max_iter ) { + ATLAS_THROW_EXCEPTION( "Could not converge" ); + } + ++iter; + } + return ( xmin < xmax ? imin : imax ); + }; + + bool is_regular = [&]() { + if ( reduced() ) { + return false; + } + long _nx = nx( jmin ); + double _dx = _nx > 0 ? dx( jmin ) : std::numeric_limits::max(); + double _xmin = _nx > 0 ? xmin( jmin ) : std::numeric_limits::max(); + + for ( idx_t j = jmin; j <= jmax; ++j ) { + if ( nx( j ) != _nx ) { + return false; + } + if ( dx( j ) != _dx ) { + return false; + } + if ( xmin( j ) != _xmin ) { + return false; + } + } + return true; + }(); + // Cropping in X bool endpoint = true; - if ( ZonalBandDomain( rect_domain ) ) { - for ( idx_t j = 0; j < cropped_ny; ++j ) { - if ( eckit::types::is_approximately_equal( cropped_xmax[j] + cropped_dx[j], cropped_xmin[j] + 360., - 1.e-10 ) ) { - cropped_xmax[j] = cropped_xmin[j] + 360.; - endpoint = false; + { + bool do_normalise = false; + for ( idx_t j = jmin, jcropped = 0; j <= jmax; ++j, ++jcropped ) { + double x_first = x( 0, j ); + double x_last = x( nx( j ) - 1, j ); + if ( normalise( x_first ) != x_first || normalise( x_last ) != x_last ) { + do_normalise = true; + } + } + + if ( rect_domain.zonal_band() ) { + for ( idx_t j = jmin, jcropped = 0; j <= jmax; ++j, ++jcropped ) { + if ( is_regular && jcropped > 0 ) { + cropped_xmin[jcropped] = cropped_xmin[0]; + cropped_xmax[jcropped] = cropped_xmax[0]; + cropped_nx[jcropped] = cropped_nx[0]; + continue; + } + idx_t i_xmin = index_of_smallest_normalised_x( j ); + idx_t i_xmax = i_xmin == 0 ? nx( j ) - 1 : i_xmin - 1; + cropped_xmin[jcropped] = normalise( x( i_xmin, j ) ); + cropped_xmax[jcropped] = normalise( x( i_xmax, j ) ); + cropped_nx[jcropped] = nx( j ); + if ( is_approximately_equal( cropped_xmax[jcropped] + cropped_dx[jcropped], + cropped_xmin[jcropped] + 360., 1.e-10 ) ) { + cropped_xmax[jcropped] = cropped_xmin[jcropped] + 360.; + endpoint = false; + } + } + } + else { + for ( idx_t j = jmin, jcropped = 0; j <= jmax; ++j, ++jcropped ) { + if ( is_regular && jcropped > 0 ) { + cropped_xmin[jcropped] = cropped_xmin[0]; + cropped_xmax[jcropped] = cropped_xmax[0]; + cropped_nx[jcropped] = cropped_nx[0]; + continue; + } + + idx_t i_xmin = index_of_smallest_normalised_x( j ); + + for ( idx_t n = 0; n < nx( j ); ++n ) { + idx_t i = i_xmin + n; + if ( i >= nx( j ) ) { + i -= nx( j ); + } + double xmin = normalise( x( i, j ) ); + if ( rect_domain.contains_x( xmin ) ) { + i_xmin = i; + cropped_xmin[jcropped] = xmin; + break; + } + } + double xmin = normalise( x( i_xmin, j ) ); + idx_t i_xmax = i_xmin; + idx_t max_iter = nx( j ); + idx_t n = 1; + ATLAS_ASSERT( rect_domain.contains_x( xmin ) ); + + while ( true ) { + idx_t i_xmax_next = i_xmax + 1; + if ( i_xmax_next == nx( j ) ) { + i_xmax_next -= nx( j ); + } + if ( i_xmax_next == i_xmin ) { + break; + } + double xmax_next = normalise( x( i_xmax_next, j ) ); + + if ( not rect_domain.contains_x( xmax_next ) ) { + break; + } + if ( n == max_iter ) { + ATLAS_THROW_EXCEPTION( "Could not converge" ); + } + i_xmax++; + n++; + } + + cropped_xmin[jcropped] = normalise( x( i_xmin, j ) ); + cropped_xmax[jcropped] = normalise( x( i_xmax, j ) ); + cropped_nx[jcropped] = std::max( 1, n ); } } } @@ -432,25 +561,28 @@ void Structured::crop( const Domain& dom ) { idx_t cropped_nxmin, cropped_nxmax; cropped_nxmin = cropped_nxmax = cropped_nx.front(); - for ( idx_t j = 1; j < cropped_ny; ++j ) { cropped_nxmin = std::min( cropped_nx[j], cropped_nxmin ); cropped_nxmax = std::max( cropped_nx[j], cropped_nxmax ); + if ( is_regular ) { + break; + } } idx_t cropped_npts = std::accumulate( cropped_nx.begin(), cropped_nx.end(), idx_t{0} ); - std::vector cropped_xspacings( cropped_ny ); - for ( idx_t j = 0; j < cropped_ny; ++j ) { - spacing::LinearSpacing * sp = nullptr; - idx_t i = j - 1; - if ((j > 0) && samerows) - sp = dynamic_cast (cropped_xspacings[i].get ()); - else - sp = new spacing::LinearSpacing( cropped_xmin[j], cropped_xmax[j], cropped_nx[j], endpoint ); - cropped_xspacings[j] = sp; + + XSpace cropped_xspace; + if ( is_regular ) { + cropped_xspace = XSpace( LinearSpacing{cropped_xmin[0], cropped_xmax[0], cropped_nx[0], endpoint}, cropped_ny ); + } + else { + std::vector cropped_xspacing_params( cropped_ny ); + for ( idx_t j = 0; j < cropped_ny; ++j ) { + cropped_xspacing_params[j] = {cropped_xmin[j], cropped_xmax[j], cropped_nx[j], endpoint}; + } + cropped_xspace = XSpace( cropped_xspacing_params ); } - XSpace cropped_xspace( cropped_xspacings ); for ( idx_t j = 0; j < cropped_ny; ++j ) { ATLAS_ASSERT( eckit::types::is_approximately_equal( cropped_xspace.xmin()[j], cropped_xmin[j] ) ); diff --git a/src/atlas/grid/detail/grid/Structured.h b/src/atlas/grid/detail/grid/Structured.h index fdaf620d2..b5c5431a7 100644 --- a/src/atlas/grid/detail/grid/Structured.h +++ b/src/atlas/grid/detail/grid/Structured.h @@ -15,6 +15,7 @@ #include "atlas/grid/Spacing.h" #include "atlas/grid/detail/grid/Grid.h" +#include "atlas/grid/detail/spacing/LinearSpacing.h" #include "atlas/library/config.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Object.h" @@ -160,7 +161,7 @@ class Structured : public Grid { Implementation( const std::array& interval, std::initializer_list&& N, bool endpoint = true ); - Implementation( const Spacing& ); + Implementation( const Spacing&, idx_t ny = 1 ); Implementation( const std::vector& ); @@ -168,6 +169,8 @@ class Structured : public Grid { Implementation( const std::vector& ); + Implementation( const std::vector& ); + idx_t ny() const { return ny_; } // Minimum number of points across parallels (constant y) @@ -218,10 +221,12 @@ class Structured : public Grid { XSpace( const XSpace& ); - XSpace( const Spacing& ); + XSpace( const Spacing&, idx_t ny = 1 ); XSpace( const std::vector& ); + XSpace( const std::vector& ); + // Constructor NVector can be either std::vector or std::vector or initializer list template XSpace( const std::array& interval, const NVector& N, bool endpoint = true ); diff --git a/src/atlas/grid/detail/spacing/LinearSpacing.cc b/src/atlas/grid/detail/spacing/LinearSpacing.cc index 3926b2d9a..8409356cd 100644 --- a/src/atlas/grid/detail/spacing/LinearSpacing.cc +++ b/src/atlas/grid/detail/spacing/LinearSpacing.cc @@ -56,6 +56,17 @@ LinearSpacing::Params::Params( const eckit::Parametrisation& params ) { } } +LinearSpacing::Params::Params( double _start, double _end, long _N, bool _endpoint ) : + N( _N ), start( _start ), end( _end ), endpoint( _endpoint ) { + length = end - start; + if ( endpoint && N > 1 ) { + step = length / double( N - 1 ); + } + else { + step = length / double( N ); + } +} + LinearSpacing::LinearSpacing( const eckit::Parametrisation& params ) { Params p( params ); setup( p.start, p.end, p.N, p.endpoint ); diff --git a/src/atlas/grid/detail/spacing/LinearSpacing.h b/src/atlas/grid/detail/spacing/LinearSpacing.h index 58a1001b9..24088281b 100644 --- a/src/atlas/grid/detail/spacing/LinearSpacing.h +++ b/src/atlas/grid/detail/spacing/LinearSpacing.h @@ -72,7 +72,9 @@ class LinearSpacing : public Spacing { double length; bool endpoint; double step; + Params() = default; Params( const eckit::Parametrisation& p ); + Params( double start, double end, long N, bool endpoint ); }; protected: From f2597ce18bf3969d7aee65f86b231335068be513 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 1 Jun 2020 13:24:41 +0100 Subject: [PATCH 112/145] ATLAS-297 Set resource usage limits to the test Co-authored-by: Philippe Marguinaud --- src/tests/grid/test_largegrid.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tests/grid/test_largegrid.cc b/src/tests/grid/test_largegrid.cc index 5ca5032ad..6707dc39a 100644 --- a/src/tests/grid/test_largegrid.cc +++ b/src/tests/grid/test_largegrid.cc @@ -48,14 +48,14 @@ CASE( "test resources for cropping large grids" ) { std::vector gridnames{"L40000x20000", "N8000", "O8000"}; for ( auto& gridname : gridnames ) { - // EXPECT( Trace::peakMemory() < 100 * Mbytes ); + EXPECT( Trace::peakMemory() < 100 * Mbytes ); SECTION( std::string( "section" ) + gridname ) { Trace trace( Here(), "Grid{" + gridname + ", GlobalDomain{-180}}" ); auto grid = Grid{gridname, GlobalDomain{-180}}; trace.stopAndReport(); - // EXPECT( trace.elapsed() < 10. ); - // EXPECT( trace.peakMemory() < 100 * Mbytes ); + EXPECT( trace.elapsed() < 10. ); + EXPECT( trace.peakMemory() < 100 * Mbytes ); } } } From 68bafb996457bbfdd820a95541ea3e2a0a989d49 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 2 Jun 2020 12:16:30 +0100 Subject: [PATCH 113/145] ATLAS-298 Fix conversion of negative degrees to micro degrees --- src/atlas/util/MicroDeg.h | 2 +- src/tests/mesh/test_halo.cc | 106 ++++++++++++++++++++---------------- 2 files changed, 61 insertions(+), 47 deletions(-) diff --git a/src/atlas/util/MicroDeg.h b/src/atlas/util/MicroDeg.h index 7645ff9b1..951f88a92 100644 --- a/src/atlas/util/MicroDeg.h +++ b/src/atlas/util/MicroDeg.h @@ -16,7 +16,7 @@ namespace util { inline int microdeg( const double& deg ) { assert( deg < 2145. ); // Since INT_MAX == 2147483647 assert( deg > -2145. ); // Since INT_MIN == –2147483648 - return static_cast( deg * 1.e6 + 0.5 ); + return static_cast( deg < 0 ? deg * 1.e6 - 0.5 : deg * 1.e6 + 0.5 ); } } // namespace util diff --git a/src/tests/mesh/test_halo.cc b/src/tests/mesh/test_halo.cc index bff82541a..93d798d37 100644 --- a/src/tests/mesh/test_halo.cc +++ b/src/tests/mesh/test_halo.cc @@ -139,68 +139,68 @@ CASE( "test_custom" ) { 754708008265885696, 754708008288385696, 754708008310885696, 754708008333385696, 754708008355885696, 754708008378385696, 754708008400885696, 717939789834742368, 717939789857242368, 717939789879742368, 717939789902242368, 754708008423385696, 681187136410079744, 717939789924742368, 717939789947242368, - 717939789969742368, 717939789992242368, 717939790014742368, 607990293310953217, 644481443565331585, - 681187136024365459, 717939789654742369, 754708008243385697, 607990293742953216, 644481443985331584, + 717939789969742368, 717939789992242368, 717939790014742368, 607990293310953216, 644481443565331584, + 681187136024365458, 717939789654742368, 754708008243385696, 607990293742953216, 644481443985331584, 681187136435794030}; break; case 1: check = {717939789677242368, 717939789699742368, 717939789722242368, 717939789744742368, 717939789767242368, 717939789789742368, 754708008265885696, 754708008288385696, 754708008310885696, 754708008333385696, - 754708008355885696, 754708008378385696, 791480219026630656, 791480219049130656, 791480219071630656, - 791480219094130656, 791480219116630656, 828248437615273984, 828248437637773984, 828248437660273984, - 828248437682773984, 828248437705273984, 865001091242436608, 865001091268150894, 865001091293865179, - 865001091319579465, 865001091345293751, 717939789812242368, 754708008400885696, 791480219139130656, - 828248437727773984, 865001091371008037, 901706783697184768, 901706783727184768, 901706783757184768, - 901706783787184768, 901706783817184768, 681187136050079744, 681187136075794030, 681187136101508315, + 754708008355885696, 754708008378385696, 791480221174114304, 791480221196614304, 791480221219114304, + 791480221241614304, 791480221264114304, 828248439762757632, 828248439785257632, 828248439807757632, + 828248439830257632, 828248439852757632, 865001093389920256, 865001093415634542, 865001093441348827, + 865001093467063113, 865001093492777399, 717939789812242368, 754708008400885696, 791480221286614304, + 828248439875257632, 865001093518491685, 901706785844668416, 901706785874668416, 901706785904668416, + 901706785934668416, 901706785964668416, 681187136050079744, 681187136075794030, 681187136101508315, 681187136127222601, 681187136152936887, 681187136178651173, 681187136204365458, 717939789834742368, - 754708008423385696, 791480219161630656, 791480219184130656, 828248437750273984, 865001091396722322, - 901706783847184768, 938197933945563136, 938197933981563136, 938197934017563136, 938197934053563136, - 938197934089563136, 717939789654742369, 754708008243385697, 791480219004130657, 828248437592773985, - 865001091216722323, 901706783667184769}; + 754708008423385696, 791480221309114304, 791480221331614304, 828248439897757632, 865001093544205970, + 901706785994668416, 938197936093046784, 938197936129046784, 938197936165046784, 938197936201046784, + 938197936237046784, 717939789654742368, 754708008243385696, 791480221151614304, 828248439740257632, + 865001093364205970, 901706785814668416}; break; case 2: check = {681187136204365458, 681187136230079744, 681187136255794030, 717939789812242368, 717939789834742368, 717939789857242368, 717939789879742368, 717939789902242368, 754708008400885696, 754708008423385696, - 754708008445885696, 754708008468385696, 754708008490885696, 791480219139130656, 791480219161630656, - 791480219184130656, 791480219206630656, 791480219229130656, 828248437727773984, 828248437750273984, - 828248437772773984, 828248437795273984, 828248437817773984, 865001091371008037, 865001091396722322, - 865001091422436608, 865001091448150894, 681187136281508315, 717939789924742368, 754708008378385696, - 754708008513385696, 791480219251630656, 828248437840273984, 865001091345293751, 865001091473865179, - 901706783817184768, 901706783847184768, 901706783877184768, 901706783907184768, 644481443745331584, + 754708008445885696, 754708008468385696, 754708008490885696, 791480221286614304, 791480221309114304, + 791480221331614304, 791480221354114304, 791480221376614304, 828248439875257632, 828248439897757632, + 828248439920257632, 828248439942757632, 828248439965257632, 865001093518491685, 865001093544205970, + 865001093569920256, 865001093595634542, 681187136281508315, 717939789924742368, 754708008378385696, + 754708008513385696, 791480221399114304, 828248439987757632, 865001093492777399, 865001093621348827, + 901706785964668416, 901706785994668416, 901706786024668416, 901706786054668416, 644481443745331584, 644481443775331584, 644481443805331584, 644481443835331584, 681187136178651173, 681187136307222601, - 717939789789742368, 717939789767242368, 754708008355885696, 791480219116630656, 828248437705273984, - 865001091319579465, 901706783787184768, 717939789947242368, 754708008535885696, 791480219274130656, - 791480219296630656, 828248437862773984, 865001091499579465, 901706783937184768, 938197934053563136, - 938197934089563136, 938197934125563136, 938197934161563136}; + 717939789789742368, 717939789767242368, 754708008355885696, 791480221264114304, 828248439852757632, + 865001093467063113, 901706785934668416, 717939789947242368, 754708008535885696, 791480221421614304, + 791480221444114304, 828248440010257632, 865001093647063113, 901706786084668416, 938197936201046784, + 938197936237046784, 938197936273046784, 938197936309046784}; break; case 3: check = {681187136281508315, 681187136307222601, 681187136332936887, 681187136358651173, 681187136384365458, 717939789924742368, 717939789947242368, 717939789969742368, 717939789992242368, 717939790014742368, 754708008513385696, 754708008535885696, 754708008558385696, 754708008580885696, 754708008603385696, - 791480219251630656, 791480219274130656, 791480219296630656, 791480219319130656, 791480219341630656, - 791480219364130656, 828248437840273984, 828248437862773984, 828248437885273984, 828248437907773984, - 828248437930273984, 828248437952773984, 644481443955331584, 681187136410079744, 717939789902242368, - 717939790037242368, 754708008490885696, 754708008625885696, 791480219386630656, 828248437975273984, - 865001091473865179, 865001091499579465, 865001091525293751, 865001091551008037, 865001091576722322, - 865001091602436608, 607990293706953216, 644481443805331584, 644481443835331584, 644481443865331584, + 791480221399114304, 791480221421614304, 791480221444114304, 791480221466614304, 791480221489114304, + 791480221511614304, 828248439987757632, 828248440010257632, 828248440032757632, 828248440055257632, + 828248440077757632, 828248440100257632, 644481443955331584, 681187136410079744, 717939789902242368, + 717939790037242368, 754708008490885696, 754708008625885696, 791480221534114304, 828248440122757632, + 865001093621348827, 865001093647063113, 865001093672777399, 865001093698491685, 865001093724205970, + 865001093749920256, 607990293706953216, 644481443805331584, 644481443835331584, 644481443865331584, 644481443895331584, 644481443925331584, 681187136255794030, 717939789879742368, 754708008468385696, - 791480219229130656, 828248437817773984, 865001091448150894, 901706783907184768, 901706783937184768, - 901706783967184768, 901706783997184768, 901706784027184768, 901706784057184768, 644481443985331584, - 681187136435794030, 717939790059742368, 754708008648385696, 791480219409130656, 828248437997773984, - 865001091628150894}; + 791480221376614304, 828248439965257632, 865001093595634542, 901706786054668416, 901706786084668416, + 901706786114668416, 901706786144668416, 901706786174668416, 901706786204668416, 644481443985331584, + 681187136435794030, 717939790059742368, 754708008648385696, 791480221556614304, 828248440145257632, + 865001093775634542}; break; case 4: - check = {865001091473865179, 865001091499579465, 865001091525293751, 865001091551008037, 865001091576722322, - 901706783697184768, 901706783727184768, 901706783757184768, 901706783787184768, 901706783817184768, - 901706783847184768, 901706783877184768, 901706783907184768, 901706783937184768, 901706783967184768, - 901706783997184768, 901706784027184768, 938197933945563136, 938197933981563136, 938197934017563136, - 938197934053563136, 938197934089563136, 938197934125563136, 938197934161563136, 938197934197563136, - 938197934233563136, 938197934269563136, 865001091602436608, 901706784057184768, 938197934305563136, - 865001091242436608, 865001091268150894, 865001091293865179, 865001091319579465, 865001091345293751, - 865001091371008037, 828248437840273984, 865001091396722322, 865001091422436608, 865001091448150894, - 828248437862773984, 828248437885273984, 828248437907773984, 828248437930273984, 828248437952773984, - 828248437975273984, 865001091216722323, 901706783667184769, 938197933909563137, 828248437997773984, - 865001091628150894, 901706784087184768, 938197934341563136}; + check = {865001093621348827, 865001093647063113, 865001093672777399, 865001093698491685, 865001093724205970, + 901706785844668416, 901706785874668416, 901706785904668416, 901706785934668416, 901706785964668416, + 901706785994668416, 901706786024668416, 901706786054668416, 901706786084668416, 901706786114668416, + 901706786144668416, 901706786174668416, 938197936093046784, 938197936129046784, 938197936165046784, + 938197936201046784, 938197936237046784, 938197936273046784, 938197936309046784, 938197936345046784, + 938197936381046784, 938197936417046784, 865001093749920256, 901706786204668416, 938197936453046784, + 865001093389920256, 865001093415634542, 865001093441348827, 865001093467063113, 865001093492777399, + 865001093518491685, 828248439987757632, 865001093544205970, 865001093569920256, 865001093595634542, + 828248440010257632, 828248440032757632, 828248440055257632, 828248440077757632, 828248440100257632, + 828248440122757632, 865001093364205970, 901706785814668416, 938197936057046784, 828248440145257632, + 865001093775634542, 901706786234668416, 938197936489046784}; break; default: check.clear(); @@ -210,9 +210,23 @@ CASE( "test_custom" ) { uid[j] = util::unique_lonlat( lonlat( j, 0 ), lonlat( j, 1 ) ); } if ( check.size() && mpi::comm().size() == 5 ) { - ATLAS_DEBUG_VAR( uid.size() ); - ATLAS_DEBUG_VAR( check.size() ); - EXPECT( uid.size() == check.size() ); + EXPECT_EQ( uid.size(), check.size() ); + + if ( uid != check ) { + for ( idx_t i = 0; i < uid.size(); ++i ) { + if ( uid[i] != check[i] ) { + Log::warning() << "uid[" << i << "] != check[" << i << "] : " << uid[i] << " expected to be " + << check[i] << " point = " << std::setprecision( 7 ) << std::fixed + << PointLonLat{lonlat( i, LON ), lonlat( i, LAT )} + << " microdeg(lon) = " << microdeg( lonlat( i, LON ) ) << std::endl; + } + } + Log::info() << "uid = { "; + for ( idx_t i = 0; i < uid.size(); ++i ) { + Log::info() << uid[i] << std::string( i < uid.size() - 1 ? ", " : " " ); + } + Log::info() << "};" << std::endl; + } EXPECT( uid == check ); } #endif From 8a5623924f85131b265e39a92def829dbc34c867 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 2 Jun 2020 23:31:41 +0100 Subject: [PATCH 114/145] ATLAS-298 Added tests for microdeg function --- src/atlas/util/MicroDeg.h | 2 ++ src/tests/util/CMakeLists.txt | 2 +- src/tests/util/test_util.cc | 44 +++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/tests/util/test_util.cc diff --git a/src/atlas/util/MicroDeg.h b/src/atlas/util/MicroDeg.h index 951f88a92..00daa0ad4 100644 --- a/src/atlas/util/MicroDeg.h +++ b/src/atlas/util/MicroDeg.h @@ -10,6 +10,8 @@ #pragma once +#include + namespace atlas { namespace util { diff --git a/src/tests/util/CMakeLists.txt b/src/tests/util/CMakeLists.txt index 3631e3193..10014cfb2 100644 --- a/src/tests/util/CMakeLists.txt +++ b/src/tests/util/CMakeLists.txt @@ -46,7 +46,7 @@ if( HAVE_FCTEST ) ) endif() -foreach( test earth flags footprint indexview polygon point ) +foreach( test util earth flags footprint indexview polygon point ) ecbuild_add_test( TARGET atlas_test_${test} SOURCES test_${test}.cc LIBS atlas diff --git a/src/tests/util/test_util.cc b/src/tests/util/test_util.cc new file mode 100644 index 000000000..8bbc40f22 --- /dev/null +++ b/src/tests/util/test_util.cc @@ -0,0 +1,44 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include "atlas/util/MicroDeg.h" + +#include "tests/AtlasTestEnvironment.h" + +using namespace atlas::util; + +namespace atlas { +namespace test { + +//----------------------------------------------------------------------------- + +CASE( "convert to microdegrees" ) { + EXPECT_EQ( microdeg( -179.999999999999 ), -180000000 ); + EXPECT_EQ( microdeg( +179.999999999999 ), +180000000 ); + EXPECT_EQ( microdeg( -180.000000000001 ), -180000000 ); + EXPECT_EQ( microdeg( +180.000000000001 ), +180000000 ); + EXPECT_EQ( microdeg( -0.000000000001 ), 0 ); + EXPECT_EQ( microdeg( +0.000000000001 ), 0 ); + EXPECT_EQ( microdeg( -0.000001 ), -1 ); + EXPECT_EQ( microdeg( +0.000001 ), +1 ); + EXPECT_EQ( microdeg( -0.000001499999 ), -1 ); + EXPECT_EQ( microdeg( +0.000001499999 ), +1 ); + EXPECT_EQ( microdeg( -0.000001500001 ), -2 ); + EXPECT_EQ( microdeg( +0.000001500001 ), +2 ); +} + +//----------------------------------------------------------------------------- + +} // namespace test +} // namespace atlas + +int main( int argc, char** argv ) { + return atlas::test::run( argc, argv ); +} From c99ebec2e78c2d43134493fccffd998b21970c1d Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 8 Jun 2020 14:40:48 +0100 Subject: [PATCH 115/145] Improve testing: EXPECT is no longer throwing, unless maximum amount is reached. REQUIRE will throw immediately --- src/tests/AtlasTestEnvironment.h | 121 +++++++++++++++++++++++++------ 1 file changed, 97 insertions(+), 24 deletions(-) diff --git a/src/tests/AtlasTestEnvironment.h b/src/tests/AtlasTestEnvironment.h index 20a144d5e..f31c32e8e 100644 --- a/src/tests/AtlasTestEnvironment.h +++ b/src/tests/AtlasTestEnvironment.h @@ -39,6 +39,58 @@ namespace test { using eckit::types::is_approximately_equal; +class Test; +static Test* current_test_{nullptr}; + +static size_t ATLAS_MAX_FAILED_EXPECTS() { + static size_t v = size_t( eckit::Resource( "$ATLAS_MAX_FAILED_EXPECTS", 100 ) ); + return v; +} + +class Test { + struct Failure { + std::string message; + eckit::CodeLocation location; + }; + +public: + Test( const std::string& description, const eckit::CodeLocation& location ) : + description_( description ), location_( location ) { + current_test_ = this; + } + ~Test() { current_test_ = nullptr; } + void expect_failed( const std::string& message, const eckit::CodeLocation& location ) { + failures_.emplace_back( Failure{message, location} ); + eckit::Log::error() << eckit::Colour::red << message << eckit::Colour::reset << " @ " + << eckit::PathName{location.file()}.baseName() << " +" << location.line() << std::endl; + if ( failures_.size() == ATLAS_MAX_FAILED_EXPECTS() ) { + std::stringstream msg; + msg << "Maximum number of allowed EXPECTS have failed (${ATLAS_MAX_FAILED_EXPECTS}=" + << ATLAS_MAX_FAILED_EXPECTS() << ")."; + throw eckit::testing::TestException( msg.str(), location_ ); + } + } + bool failed() const { return failures_.size() > 0; } + void throw_on_failed_expects() { + if ( failed() ) { + std::stringstream msg; + msg << failures_.size() << " EXPECTS have failed"; + throw eckit::testing::TestException( msg.str(), location_ ); + } + } + const std::string& description() const { return description_; } + +private: + std::vector failures_; + std::string description_; + eckit::CodeLocation location_; +}; + +Test& current_test() { + ATLAS_ASSERT( current_test_ ); + return *current_test_; +} + //---------------------------------------------------------------------------------------------------------------------- #ifdef MAYBE_UNUSED @@ -58,9 +110,11 @@ using eckit::types::is_approximately_equal; void UNIQUE_NAME2( traced_test_, __LINE__ )( std::string & _test_subsection, int& _num_subsections, \ int _subsection ); \ void UNIQUE_NAME2( test_, __LINE__ )( std::string & _test_subsection, int& _num_subsections, int _subsection ) { \ + Test UNIQUE_NAME2( testobj_, __LINE__ )( description, Here() ); \ ATLAS_TRACE( description ); \ UNIQUE_NAME2( traced_test_, __LINE__ ) \ ( _test_subsection, _num_subsections, _subsection ); \ + current_test().throw_on_failed_expects(); \ if ( atlas::test::barrier_timeout( atlas::test::ATLAS_MPI_BARRIER_TIMEOUT() ) ) { \ atlas::Log::warning() << "\nWARNING: Test \"" << description \ << "\" failed with MPI deadlock. (${ATLAS_MPI_BARRIER_TIMEOUT}=" \ @@ -89,28 +143,47 @@ using eckit::types::is_approximately_equal; #undef EXPECT_APPROX_EQ #endif -#define EXPECT_EQ( lhs, rhs ) \ - do { \ - if ( !( lhs == rhs ) ) { \ - using namespace std; \ - throw eckit::testing::TestException( "EXPECT condition failed: " #lhs " == " #rhs \ - "\n" \ - " --> " + \ - to_string( lhs ) + " != " + to_string( rhs ), \ - Here() ); \ - } \ +#ifdef EXPECT +#undef EXPECT +#endif + +#define REQUIRE( expr ) \ + do { \ + if ( !( expr ) ) { \ + throw eckit::testing::TestException( "EXPECT condition failed: " #expr, Here() ); \ + } \ + } while ( false ) + +#define EXPECT( expr ) \ + do { \ + if ( !( expr ) ) { \ + current_test().expect_failed( "EXPECT condition failed: " #expr, Here() ); \ + } \ + } while ( false ) + + +#define EXPECT_EQ( lhs, rhs ) \ + do { \ + if ( !( lhs == rhs ) ) { \ + using namespace std; \ + current_test().expect_failed( "EXPECT condition failed: " #lhs " == " #rhs \ + "\n" \ + " --> " + \ + to_string( lhs ) + " != " + to_string( rhs ), \ + Here() ); \ + } \ } while ( false ) -#define __EXPECT_APPROX_EQ( lhs, rhs ) \ - do { \ - if ( !( is_approximately_equal( lhs, rhs ) ) ) { \ - std::stringstream err; \ - err << "EXPECT condition failed: " #lhs " ~= " #rhs \ - "\n" \ - " --> " \ - << lhs << " != " << rhs; \ - throw eckit::testing::TestException( err.str(), Here() ); \ - } \ +#define __EXPECT_APPROX_EQ( lhs, rhs ) \ + do { \ + if ( !( is_approximately_equal( lhs, rhs ) ) ) { \ + std::stringstream err; \ + err << "EXPECT condition failed: " #lhs " ~= " #rhs \ + "\n" \ + " --> " \ + << std::fixed << std::setprecision( 12 ) << lhs << " != " << rhs; \ + current_test().expect_failed( err.str(), Here() ); \ + } \ } while ( false ) #define __EXPECT_APPROX_EQ_TOL( lhs, rhs, tol ) \ @@ -121,7 +194,7 @@ using eckit::types::is_approximately_equal; "\n" \ " --> " \ << std::fixed << std::setprecision( 12 ) << lhs << " != " << rhs; \ - throw eckit::testing::TestException( err.str(), Here() ); \ + current_test().expect_failed( err.str(), Here() ); \ } \ } while ( false ) @@ -133,7 +206,7 @@ using eckit::types::is_approximately_equal; //---------------------------------------------------------------------------------------------------------------------- static double ATLAS_MPI_BARRIER_TIMEOUT() { - static double v = eckit::Resource( "${ATLAS_MPI_BARRIER_TIMEOUT", 3. ); + static double v = eckit::Resource( "$ATLAS_MPI_BARRIER_TIMEOUT", 3. ); return v; } @@ -303,5 +376,5 @@ int run( int argc, char* argv[] ) { //---------------------------------------------------------------------------------------------------------------------- -} // end namespace test -} // end namespace atlas +} // namespace test +} // namespace atlas From b01aff9252e91b52272a28a837da1d15256ba613 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 8 Jun 2020 14:45:41 +0100 Subject: [PATCH 116/145] Add fvm::Nabla validation case used in METV-2657 and MIR-459 --- src/atlas/numerics/fvm/Nabla.cc | 119 ++++-- src/atlas/numerics/fvm/Nabla.h | 1 + src/tests/numerics/CMakeLists.txt | 6 + .../numerics/test_fvm_nabla_validation.cc | 342 ++++++++++++++++++ 4 files changed, 430 insertions(+), 38 deletions(-) create mode 100644 src/tests/numerics/test_fvm_nabla_validation.cc diff --git a/src/atlas/numerics/fvm/Nabla.cc b/src/atlas/numerics/fvm/Nabla.cc index 852743ee3..f0b4a17ec 100644 --- a/src/atlas/numerics/fvm/Nabla.cc +++ b/src/atlas/numerics/fvm/Nabla.cc @@ -45,6 +45,18 @@ Nabla::Nabla( const numerics::Method& method, const eckit::Parametrisation& p ) Log::debug() << "Nabla constructed for method " << fvm_->name() << " with " << fvm_->node_columns().nb_nodes_global() << " nodes total" << std::endl; fvm_->attach(); + + p.get( "metric_approach", metric_approach_ ); + // Experimental, only affects divergence and curl !!! + // metric_approach = 0 ORIGINAL, DEFAULT + // metric_term cos(y) is multiplied with each wind component + // --> cell-interface: avg = 0.5*( cos(y1)*u1 + cos(y2)*u2 ) + // metric_approach = 1: + // metric_term cos(0.5*(y1+y2)) is used at cell interface + // --> cell-interface: avg = 0.5*(u1+u2)*cos(0.5*(y1+y2)) + // Results seem to indicate that approach=0 is overall better, although approach=1 + // seems to handle pole slightly better (error factor 2 to 4 times lower) + setup(); } @@ -305,30 +317,43 @@ void Nabla::divergence( const Field& vector_field, Field& div_field ) const { const double scale = deg2rad * deg2rad * radius; + enum + { + LONdLON = 0, + LATdLAT = 1 + }; atlas_omp_parallel { atlas_omp_for( idx_t jedge = 0; jedge < nedges; ++jedge ) { - idx_t ip1 = edge2node( jedge, 0 ); - idx_t ip2 = edge2node( jedge, 1 ); - double y1 = lonlat_deg( ip1, LAT ) * deg2rad; - double y2 = lonlat_deg( ip2, LAT ) * deg2rad; - double cosy1 = std::cos( y1 ); - double cosy2 = std::cos( y2 ); + double pbc = 1 - is_pole_edge( jedge ); - double pbc = 1. - is_pole_edge( jedge ); + idx_t ip1 = edge2node( jedge, 0 ); + idx_t ip2 = edge2node( jedge, 1 ); + double y1 = lonlat_deg( ip1, LAT ) * deg2rad; + double y2 = lonlat_deg( ip2, LAT ) * deg2rad; + + double cosy1, cosy2; + if ( metric_approach_ == 0 ) { + cosy1 = std::cos( y1 ) * pbc; + cosy2 = std::cos( y2 ) * pbc; + } + else { + cosy1 = cosy2 = std::cos( 0.5 * ( y1 + y2 ) ) * pbc; + } + + double S[2] = {dual_normals( jedge, LON ) * deg2rad, dual_normals( jedge, LAT ) * deg2rad}; + double avg[2]; for ( idx_t jlev = 0; jlev < nlev; ++jlev ) { - double avg[2] = { - ( vector( ip1, jlev, LON ) + vector( ip2, jlev, LON ) ) * 0.5, - ( cosy1 * vector( ip1, jlev, LAT ) + cosy2 * vector( ip2, jlev, LAT ) ) * 0.5 * - pbc // (force cos(y)=0 at pole) - }; - avgS( jedge, jlev, LON ) = dual_normals( jedge, LON ) * deg2rad * avg[LON]; - // above = 0 at pole by construction of S - avgS( jedge, jlev, LAT ) = dual_normals( jedge, LAT ) * deg2rad * avg[LAT]; - // above = 0 at pole by construction of pbc - // We don't need the cross terms for divergence, - // i.e. dual_normals(jedge,LON)*deg2rad*avg[LAT] - // and dual_normals(jedge,LAT)*deg2rad*avg[LON] + double u1 = vector( ip1, jlev, LON ); + double u2 = vector( ip2, jlev, LON ); + double v1 = vector( ip1, jlev, LAT ) * cosy1; + double v2 = vector( ip2, jlev, LAT ) * cosy2; + + avg[LON] = ( u1 + u2 ) * 0.5; + avg[LAT] = ( v1 + v2 ) * 0.5; + + avgS( jedge, jlev, LONdLON ) = avg[LON] * S[LON]; + avgS( jedge, jlev, LATdLAT ) = avg[LAT] * S[LAT]; } } @@ -341,7 +366,7 @@ void Nabla::divergence( const Field& vector_field, Field& div_field ) const { if ( iedge < nedges ) { double add = node2edge_sign( jnode, jedge ); for ( idx_t jlev = 0; jlev < nlev; ++jlev ) { - div( jnode, jlev ) += add * ( avgS( iedge, jlev, LON ) + avgS( iedge, jlev, LAT ) ); + div( jnode, jlev ) += add * ( avgS( iedge, jlev, LONdLON ) + avgS( iedge, jlev, LATdLAT ) ); } } } @@ -383,36 +408,54 @@ void Nabla::curl( const Field& vector_field, Field& curl_field ) const { const auto edge_flags = array::make_view( edges.flags() ); auto is_pole_edge = [&]( idx_t e ) { return Topology::check( edge_flags( e ), Topology::POLE ); }; + const mesh::Connectivity& node2edge = nodes.edge_connectivity(); const mesh::MultiBlockConnectivity& edge2node = edges.node_connectivity(); array::ArrayT avgS_arr( nedges, nlev, 2ul ); array::ArrayView avgS = array::make_view( avgS_arr ); - const double scale = deg2rad * deg2rad * radius * radius; + const double scale = deg2rad * deg2rad * radius; + + enum + { + LONdLAT = 0, + LATdLON = 1 + }; + atlas_omp_parallel { atlas_omp_for( idx_t jedge = 0; jedge < nedges; ++jedge ) { - idx_t ip1 = edge2node( jedge, 0 ); - idx_t ip2 = edge2node( jedge, 1 ); - double y1 = lonlat_deg( ip1, LAT ) * deg2rad; - double y2 = lonlat_deg( ip2, LAT ) * deg2rad; - double rcosy1 = radius * std::cos( y1 ); - double rcosy2 = radius * std::cos( y2 ); + idx_t ip1 = edge2node( jedge, 0 ); + idx_t ip2 = edge2node( jedge, 1 ); + double y1 = lonlat_deg( ip1, LAT ) * deg2rad; + double y2 = lonlat_deg( ip2, LAT ) * deg2rad; double pbc = 1 - is_pole_edge( jedge ); + double cosy1; + double cosy2; + if ( metric_approach_ == 0 ) { + cosy1 = std::cos( y1 ) * pbc; + cosy2 = std::cos( y2 ) * pbc; + } + else { + cosy1 = cosy2 = std::cos( 0.5 * ( y1 + y2 ) ) * pbc; + } + + double S[2] = {dual_normals( jedge, LON ) * deg2rad, dual_normals( jedge, LAT ) * deg2rad}; + double avg[2]; for ( idx_t jlev = 0; jlev < nlev; ++jlev ) { - double avg[2] = {( rcosy1 * vector( ip1, jlev, LON ) + rcosy2 * vector( ip2, jlev, LON ) ) * 0.5 * - pbc, // (force R*cos(y)=0 at pole) - ( radius * vector( ip1, jlev, LAT ) + radius * vector( ip2, jlev, LAT ) ) * 0.5}; - avgS( jedge, jlev, LON ) = dual_normals( jedge, LAT ) * deg2rad * avg[LON]; - // above = 0 at pole by construction of pbc - avgS( jedge, jlev, LAT ) = dual_normals( jedge, LON ) * deg2rad * avg[LAT]; - // above = 0 at pole by construction of S - // We don't need the non-cross terms for curl, i.e. - // dual_normals(jedge,LON)*deg2rad*avg[LON] - // and dual_normals(jedge,LAT)*deg2rad*avg[LAT] + double u1 = vector( ip1, jlev, LON ) * cosy1; + double u2 = vector( ip2, jlev, LON ) * cosy2; + double v1 = vector( ip1, jlev, LAT ); + double v2 = vector( ip2, jlev, LAT ); + + avg[LON] = ( u1 + u2 ) * 0.5; + avg[LAT] = ( v1 + v2 ) * 0.5; + + avgS( jedge, jlev, LONdLAT ) = avg[LON] * S[LAT]; + avgS( jedge, jlev, LATdLON ) = avg[LAT] * S[LON]; } } @@ -425,7 +468,7 @@ void Nabla::curl( const Field& vector_field, Field& curl_field ) const { if ( iedge < nedges ) { double add = node2edge_sign( jnode, jedge ); for ( idx_t jlev = 0; jlev < nlev; ++jlev ) { - curl( jnode, jlev ) += add * ( avgS( iedge, jlev, LAT ) - avgS( iedge, jlev, LON ) ); + curl( jnode, jlev ) += add * ( avgS( iedge, jlev, LATdLON ) - avgS( iedge, jlev, LONdLAT ) ); } } } diff --git a/src/atlas/numerics/fvm/Nabla.h b/src/atlas/numerics/fvm/Nabla.h index c73c08137..715b1d4b9 100644 --- a/src/atlas/numerics/fvm/Nabla.h +++ b/src/atlas/numerics/fvm/Nabla.h @@ -53,6 +53,7 @@ class Nabla : public atlas::numerics::NablaImpl { private: fvm::Method const* fvm_; std::vector pole_edges_; + int metric_approach_{0}; }; #endif // ------------------------------------------------------------------ diff --git a/src/tests/numerics/CMakeLists.txt b/src/tests/numerics/CMakeLists.txt index 15e9f1860..47bd2d168 100644 --- a/src/tests/numerics/CMakeLists.txt +++ b/src/tests/numerics/CMakeLists.txt @@ -24,6 +24,12 @@ ecbuild_add_test( TARGET atlas_test_fvm_nabla_L5 ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) +ecbuild_add_test( TARGET atlas_test_fvm_nabla_validation + SOURCES test_fvm_nabla_validation.cc + LIBS atlas + ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} +) + if( HAVE_FCTEST) add_fctest( TARGET atlas_fctest_fvm_nabla LINKER_LANGUAGE Fortran diff --git a/src/tests/numerics/test_fvm_nabla_validation.cc b/src/tests/numerics/test_fvm_nabla_validation.cc new file mode 100644 index 000000000..da79afd42 --- /dev/null +++ b/src/tests/numerics/test_fvm_nabla_validation.cc @@ -0,0 +1,342 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include +#include +#include + +#include "eckit/config/Resource.h" + +#include "atlas/array/MakeView.h" +#include "atlas/field/Field.h" +#include "atlas/field/FieldSet.h" +#include "atlas/grid/Distribution.h" +#include "atlas/grid/Grid.h" +#include "atlas/grid/Partitioner.h" +#include "atlas/mesh/Mesh.h" +#include "atlas/mesh/Nodes.h" +#include "atlas/meshgenerator.h" +#include "atlas/numerics/Nabla.h" +#include "atlas/numerics/fvm/Method.h" +#include "atlas/option.h" +#include "atlas/output/Gmsh.h" +#include "atlas/parallel/mpi/mpi.h" +#include "atlas/util/Config.h" +#include "atlas/util/Constants.h" +#include "atlas/util/CoordinateEnums.h" +#include "atlas/util/Earth.h" + +#include "tests/AtlasTestEnvironment.h" + +using namespace eckit; +using namespace atlas::numerics; +using namespace atlas::meshgenerator; +using namespace atlas::grid; + +namespace atlas { +namespace test { + + +// This test relates to JIRA issues METV-2657 , MIR-459 + +static std::string griduid() { + //return "O80"; + //return "Slat80"; + return "Slat720x360"; +} +static double radius() { + return util::Earth::radius(); +} +static double beta_in_degrees() { + return 90.; +} + +static bool output_gmsh() { + return false; +} + +static bool mask_polar_values() { + return false; +} + +static int metric_approach() { + // Experimental!!! + // approach = 0 ORIGINAL, DEFAULT + // metric_term cos(y) is multiplied with each wind component + // --> cell-interface: avg = 0.5*( cos(y1)*u1 + cos(y2)*u2 ) + // approach = 1: + // metric_term cos(0.5*(y1+y2)) is used at cell interface + // --> cell-interface: avg = 0.5*(u1+u2)*cos(0.5*(y1+y2)) + // Results seem to indicate that approach=0 is overall better, although approach=1 + // seems to handle pole slightly better (error factor 2 to 4 times lower) + return 0; +} + +class SolidBodyRotation { + double radius; + double beta; + double sin_beta; + double cos_beta; + +public: + SolidBodyRotation( const double _radius, const double _beta ) : radius{_radius}, beta{_beta} { + sin_beta = std::sin( beta ); + cos_beta = std::cos( beta ); + } + void wind( const double x, const double y, double& u, double& v ) { + using namespace std; + double cos_x = std::cos( x ); + double cos_y = std::cos( y ); + double sin_x = std::sin( x ); + double sin_y = std::sin( y ); + u = cos_y * cos_beta + cos_x * sin_y * sin_beta; + v = -sin_x * sin_beta; + + u = cos( y ) * cos( beta ) + cos( x ) * sin( y ) * sin( beta ); + v = -sin( x ) * sin( beta ); + } + + void vordiv( const double x, const double y, double& vor, double& div ) { + double cos_x = std::cos( x ); + double cos_y = std::cos( y ); + double sin_x = std::sin( x ); + double sin_y = std::sin( y ); + + // Divergence = 1./(R*cos(y)) * ( d/dx( u ) + d/dy( v * cos(y) ) ) + // Vorticity = 1./(R*cos(y)) * ( d/dx( v ) - d/dy( u * cos(y) ) ) + double ddx_u = -sin_x * sin_y * sin_beta; + double ddy_cosy_v = ( -sin_x * sin_beta ) * ( -sin_y ); + double ddx_v = -cos_x * sin_beta; + double ddy_cosy_u = 2 * cos_y * ( -sin_y ) * cos_beta + ( -sin_y ) * cos_x * sin_y * sin_beta + + cos_y * cos_x * cos_y * sin_beta; + + double metric = 1. / ( radius * cos_y ); + + div = metric * ( ddx_u + ddy_cosy_v ); + vor = metric * ( ddx_v - ddy_cosy_u ); + } + + void wind_magnitude_squared( const double x, const double y, double& f ) { + double u, v; + wind( x, y, u, v ); + f = u * u + v * v; + } + + void wind_magnitude_squared_gradient( const double x, const double y, double& dfdx, double& dfdy ) { + using namespace std; + double cos_x = std::cos( x ); + double cos_y = std::cos( y ); + double sin_x = std::sin( x ); + double sin_y = std::sin( y ); + + double metric_y = 1. / radius; + double metric_x = metric_y / cos_y; + + double u = cos_y * cos_beta + cos_x * sin_y * sin_beta; + double v = -sin_x * sin_beta; + double dudx = metric_x * ( -sin_x * sin_y * sin_beta ); + double dudy = metric_y * ( -sin_y * cos_beta + cos_x * cos_y * sin_beta ); + double dvdx = metric_x * ( -cos_x * sin_beta ); + double dvdy = metric_y * ( 0. ); + dfdx = 2 * u * dudx + 2 * v * dvdx; + dfdy = 2 * u * dudy + 2 * v * dvdy; + } +}; + +FieldSet analytical_fields( const fvm::Method& fvm ) { + constexpr double deg2rad = M_PI / 180.; + const double radius = fvm.radius(); + + auto lonlat_deg = array::make_view( fvm.mesh().nodes().lonlat() ); + + FieldSet fields; + auto add_scalar_field = [&]( const std::string& name ) { + return array::make_view( + fields.add( fvm.node_columns().createField( option::name( name ) ) ) ); + }; + auto add_vector_field = [&]( const std::string& name ) { + return array::make_view( fields.add( fvm.node_columns().createField( + option::name( name ) | option::type( "vector" ) | option::variables( 2 ) ) ) ); + }; + auto f = add_scalar_field( "f" ); + auto uv = add_vector_field( "uv" ); + auto u = add_scalar_field( "u" ); + auto v = add_scalar_field( "v" ); + auto grad_f = add_vector_field( "ref_grad_f" ); + auto dfdx = add_scalar_field( "ref_dfdx" ); + auto dfdy = add_scalar_field( "ref_dfdy" ); + auto div = add_scalar_field( "ref_div" ); + auto vor = add_scalar_field( "ref_vor" ); + + auto flow = SolidBodyRotation{radius, beta_in_degrees() * deg2rad}; + auto is_ghost = array::make_view( fvm.mesh().nodes().ghost() ); + const idx_t nnodes = fvm.mesh().nodes().size(); + for ( idx_t jnode = 0; jnode < nnodes; ++jnode ) { + if ( is_ghost( jnode ) ) { + continue; + } + double x = lonlat_deg( jnode, LON ) * deg2rad; + double y = lonlat_deg( jnode, LAT ) * deg2rad; + + flow.wind( x, y, u( jnode ), v( jnode ) ); + flow.vordiv( x, y, vor( jnode ), div( jnode ) ); + flow.wind_magnitude_squared( x, y, f( jnode ) ); + flow.wind_magnitude_squared_gradient( x, y, dfdx( jnode ), dfdy( jnode ) ); + + uv( jnode, XX ) = u( jnode ); + uv( jnode, YY ) = v( jnode ); + grad_f( jnode, XX ) = dfdx( jnode ); + grad_f( jnode, YY ) = dfdy( jnode ); + } + fields.set_dirty(); + fields.haloExchange(); + + return fields; +} + +//----------------------------------------------------------------------------- + +CASE( "test_analytical" ) { + Grid grid( griduid(), GlobalDomain( -180. ) ); + + Mesh mesh = MeshGenerator{"structured"}.generate( grid ); + fvm::Method fvm( mesh, option::radius( radius() ) ); + Nabla nabla( fvm, util::Config( "metric_approach", metric_approach() ) ); + FieldSet fields = analytical_fields( fvm ); + + Field div = fields.add( fvm.node_columns().createField( option::name( "div" ) ) ); + Field vor = fields.add( fvm.node_columns().createField( option::name( "vor" ) ) ); + Field grad_f = + fields.add( fvm.node_columns().createField( option::name( "grad_f" ) | option::variables( 2 ) ) ); + Field dfdx = fields.add( fvm.node_columns().createField( option::name( "dfdx" ) ) ); + Field dfdy = fields.add( fvm.node_columns().createField( option::name( "dfdy" ) ) ); + + auto split = []( const Field& vector, Field& component_x, Field& component_y ) { + auto v = array::make_view( vector ); + auto x = array::make_view( component_x ); + auto y = array::make_view( component_y ); + for ( idx_t j = 0; j < v.shape( 0 ); ++j ) { + x( j ) = v( j, XX ); + y( j ) = v( j, YY ); + } + }; + + + ATLAS_TRACE_SCOPE( "gradient" ) nabla.gradient( fields["f"], grad_f ); + split( grad_f, dfdx, dfdy ); + ATLAS_TRACE_SCOPE( "divergence" ) nabla.divergence( fields["uv"], div ); + ATLAS_TRACE_SCOPE( "vorticity" ) nabla.curl( fields["uv"], vor ); + + auto do_mask_polar_values = [&]( Field& field, double mask ) { + using Topology = atlas::mesh::Nodes::Topology; + using Range = atlas::array::Range; + auto node_flags = array::make_view( fvm.node_columns().nodes().flags() ); + auto is_polar = [&]( idx_t j ) { + return Topology::check( node_flags( j ), Topology::BC | Topology::NORTH ) || + Topology::check( node_flags( j ), Topology::BC | Topology::SOUTH ); + }; + auto apply = [&]( array::LocalView&& view ) { + for ( idx_t j = 0; j < view.shape( 0 ); ++j ) { + if ( is_polar( j ) ) { + for ( idx_t v = 0; v < view.shape( 1 ); ++v ) { + view( j, v ) = mask; + } + } + } + }; + if ( field.rank() == 1 ) { + apply( array::make_view( field ).slice( Range::all(), Range::dummy() ) ); + } + else if ( field.rank() == 2 ) { + apply( array::make_view( field ).slice( Range::all(), Range::all() ) ); + } + }; + + for ( auto fieldname : std::vector{"dfdx", "dfdy", "div", "vor"} ) { + auto err_field = fields.add( fvm.node_columns().createField( option::name( "err_" + fieldname ) ) ); + auto err2_field = fields.add( fvm.node_columns().createField( option::name( "err2_" + fieldname ) ) ); + auto fld = array::make_view( fields[fieldname] ); + auto ref = array::make_view( fields["ref_" + fieldname] ); + auto err = array::make_view( fields["err_" + fieldname] ); + auto err2 = array::make_view( fields["err2_" + fieldname] ); + for ( idx_t j = 0; j < fld.shape( 0 ); ++j ) { + err( j ) = fld( j ) - ref( j ); + err2( j ) = err( j ) * err( j ); + } + + if ( mask_polar_values() ) { + do_mask_polar_values( fields["err_" + fieldname], 0. ); + do_mask_polar_values( fields["err2_" + fieldname], 0. ); + } + } + + fields.haloExchange(); + + + // output to gmsh + if ( output_gmsh() ) { + output::Gmsh{"mesh_2d.msh", util::Config( "coordinates", "lonlat" )}.write( mesh ); + output::Gmsh{"mesh_3d.msh", util::Config( "coordinates", "xyz" )}.write( mesh ); + output::Gmsh{"fields.msh"}.write( fields ); + } + + auto minmax_within_error = [&]( const std::string& name, double error ) { + Field field = fields["err_" + name]; + error = std::abs( error ); + double min, max; + fvm.node_columns().minimum( field, min ); + fvm.node_columns().maximum( field, max ); + bool success = true; + if ( min < -error ) { + Log::warning() << "minumum " << min << " smaller than error " << -error << std::endl; + success = false; + } + if ( max > error ) { + Log::warning() << "maximum " << max << " greater than error " << error << std::endl; + success = false; + } + Log::info() << name << "\t: minmax error between { " << min << " , " << max << " }" << std::endl; + return success; + }; + EXPECT( minmax_within_error( "dfdx", 1.e-11 ) ); + EXPECT( minmax_within_error( "dfdy", 1.e-11 ) ); + EXPECT( minmax_within_error( "div", 1.e-16 ) ); + EXPECT( minmax_within_error( "vor", 1.5e-9 ) ); + + auto rms_within_error = [&]( const std::string& name, double error ) { + Field field = fields["err2_" + name]; + double mean; + idx_t N; + fvm.node_columns().mean( field, mean, N ); + double rms = std::sqrt( mean / double( N ) ); + bool success = true; + if ( rms > error ) { + Log::warning() << "rms " << rms << " greater than error " << error << std::endl; + success = false; + } + Log::info() << name << "\t: rms error = " << rms << std::endl; + return success; + }; + EXPECT( rms_within_error( "dfdx", 1.e-14 ) ); + EXPECT( rms_within_error( "dfdy", 1.e-14 ) ); + EXPECT( rms_within_error( "div", 5.e-20 ) ); + EXPECT( rms_within_error( "vor", 5.e-13 ) ); + + // error for vorticity seems too high ? +} + +//----------------------------------------------------------------------------- + +} // namespace test +} // namespace atlas + +int main( int argc, char** argv ) { + return atlas::test::run( argc, argv ); +} From 5749368e00f5b0da12a787e28943b84a4279c32f Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 9 Jun 2020 00:01:20 +0100 Subject: [PATCH 117/145] Clean --- src/tests/numerics/test_fvm_nabla_validation.cc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/tests/numerics/test_fvm_nabla_validation.cc b/src/tests/numerics/test_fvm_nabla_validation.cc index da79afd42..c0e664b82 100644 --- a/src/tests/numerics/test_fvm_nabla_validation.cc +++ b/src/tests/numerics/test_fvm_nabla_validation.cc @@ -91,16 +91,12 @@ class SolidBodyRotation { cos_beta = std::cos( beta ); } void wind( const double x, const double y, double& u, double& v ) { - using namespace std; double cos_x = std::cos( x ); double cos_y = std::cos( y ); double sin_x = std::sin( x ); double sin_y = std::sin( y ); u = cos_y * cos_beta + cos_x * sin_y * sin_beta; v = -sin_x * sin_beta; - - u = cos( y ) * cos( beta ) + cos( x ) * sin( y ) * sin( beta ); - v = -sin( x ) * sin( beta ); } void vordiv( const double x, const double y, double& vor, double& div ) { @@ -130,7 +126,6 @@ class SolidBodyRotation { } void wind_magnitude_squared_gradient( const double x, const double y, double& dfdx, double& dfdy ) { - using namespace std; double cos_x = std::cos( x ); double cos_y = std::cos( y ); double sin_x = std::sin( x ); From 736fff9ea3988a83893c91a94cdb6cf3c3c169af Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 11 Jun 2020 17:33:42 +0100 Subject: [PATCH 118/145] atlas-meshgen: fix effect of --patch-pole option --- src/apps/atlas-meshgen.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/apps/atlas-meshgen.cc b/src/apps/atlas-meshgen.cc index 44d6f1439..cbd66c00f 100644 --- a/src/apps/atlas-meshgen.cc +++ b/src/apps/atlas-meshgen.cc @@ -237,6 +237,9 @@ int Meshgen2Gmsh::execute( const Args& args ) { if ( mpi::comm().size() > 1 || edges ) { meshgenerator_config.set( "3d", false ); } + bool patch_pole = false; + args.get( "patch_pole", patch_pole ); + meshgenerator_config.set( "patch_pole", patch_pole ); MeshGenerator meshgenerator( generator, meshgenerator_config ); From ac351aae1a19d8c0d8c5fa1e830b12a0d00ef568 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 12 Jun 2020 19:06:19 +0100 Subject: [PATCH 119/145] Throw exception when grid.size() cannot be represented with idx_t (Needs solution in future) --- src/atlas/grid/detail/grid/Structured.cc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/atlas/grid/detail/grid/Structured.cc b/src/atlas/grid/detail/grid/Structured.cc index 04a3d16d7..d17d7f7f5 100644 --- a/src/atlas/grid/detail/grid/Structured.cc +++ b/src/atlas/grid/detail/grid/Structured.cc @@ -76,8 +76,18 @@ Structured::Structured( const std::string& name, XSpace xspace, YSpace yspace, P nxmin_ = std::min( nx_[j], nxmin_ ); nxmax_ = std::max( nx_[j], nxmax_ ); } + size_t npts_size_t = 0; + for ( auto& nx : nx_ ) { + npts_size_t += size_t( nx ); + } npts_ = std::accumulate( nx_.begin(), nx_.end(), idx_t{0} ); - + if ( size_t( npts_ ) != npts_size_t ) { + ATLAS_THROW_EXCEPTION( + "Cannot represent grid.size() with idx_t of " + << ATLAS_BITS_LOCAL + << " bits.\n" + "Recompile atlas with idx_t 64bits, or wait for pending development where grid index to become gidx_t" ); + } crop( domain ); computeTruePeriodicity(); From 5603a17c9eb85712ca57c4d95ec553335051765b Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 16 Jun 2020 13:20:18 +0100 Subject: [PATCH 120/145] ATLAS-286 Fix yet again the StructuredColumnsHaloExchangeCache, for when different distribution is used --- .../functionspace/detail/StructuredColumns.cc | 50 +++++++++++++++---- .../functionspace/detail/StructuredColumns.h | 5 ++ 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/atlas/functionspace/detail/StructuredColumns.cc b/src/atlas/functionspace/detail/StructuredColumns.cc index 12d037b23..3e70875cc 100644 --- a/src/atlas/functionspace/detail/StructuredColumns.cc +++ b/src/atlas/functionspace/detail/StructuredColumns.cc @@ -105,19 +105,23 @@ class StructuredColumnsHaloExchangeCache : public util::Cache get_or_create( const detail::StructuredColumns& funcspace ) { registerGrid( *funcspace.grid().get() ); creator_type creator = std::bind( &StructuredColumnsGatherScatterCache::create, &funcspace ); - return Base::get_or_create( key( *funcspace.grid().get() ), creator ); + return Base::get_or_create( key( funcspace ), remove_key( funcspace ), creator ); } - void onGridDestruction( grid::detail::grid::Grid& grid ) override { remove( key( grid ) ); } + void onGridDestruction( grid::detail::grid::Grid& grid ) override { remove( remove_key( grid ) ); } private: - static Base::key_type key( const grid::detail::grid::Grid& grid ) { + static Base::key_type key( const detail::StructuredColumns& funcspace ) { + std::ostringstream key; + key << "grid[address=" << funcspace.grid().get() << ",halo=" << funcspace.halo() + << ",periodic_points=" << std::boolalpha << funcspace.periodic_points_ + << ",distribution=" << funcspace.distribution() << "]"; + return key.str(); + } + + static Base::key_type remove_key( const detail::StructuredColumns& funcspace ) { + return remove_key( *funcspace.grid().get() ); + } + + static Base::key_type remove_key( const grid::detail::grid::Grid& grid ) { std::ostringstream key; key << "grid[address=" << &grid << "]"; return key.str(); @@ -185,12 +201,24 @@ class StructuredColumnsChecksumCache : public util::Cache get_or_create( const detail::StructuredColumns& funcspace ) { registerGrid( *funcspace.grid().get() ); creator_type creator = std::bind( &StructuredColumnsChecksumCache::create, &funcspace ); - return Base::get_or_create( key( *funcspace.grid().get() ), creator ); + return Base::get_or_create( key( funcspace ), remove_key( funcspace ), creator ); } - void onGridDestruction( grid::detail::grid::Grid& grid ) override { remove( key( grid ) ); } + void onGridDestruction( grid::detail::grid::Grid& grid ) override { remove( remove_key( grid ) ); } private: - static Base::key_type key( const grid::detail::grid::Grid& grid ) { + static Base::key_type key( const detail::StructuredColumns& funcspace ) { + std::ostringstream key; + key << "grid[address=" << funcspace.grid().get() << ",halo=" << funcspace.halo() + << ",periodic_points=" << std::boolalpha << funcspace.periodic_points_ + << ",distribution=" << funcspace.distribution() << "]"; + return key.str(); + } + + static Base::key_type remove_key( const detail::StructuredColumns& funcspace ) { + return remove_key( *funcspace.grid().get() ); + } + + static Base::key_type remove_key( const grid::detail::grid::Grid& grid ) { std::ostringstream key; key << "grid[address=" << &grid << "]"; return key.str(); diff --git a/src/atlas/functionspace/detail/StructuredColumns.h b/src/atlas/functionspace/detail/StructuredColumns.h index d34808c39..dd81fb1e0 100644 --- a/src/atlas/functionspace/detail/StructuredColumns.h +++ b/src/atlas/functionspace/detail/StructuredColumns.h @@ -56,6 +56,9 @@ namespace functionspace { namespace detail { class StructuredColumnsHaloExchangeCache; +class StructuredColumnsGatherScatterCache; +class StructuredColumnsChecksumCache; + // ------------------------------------------------------------------- @@ -205,6 +208,8 @@ class StructuredColumns : public FunctionSpaceImpl { idx_t halo_; friend class StructuredColumnsHaloExchangeCache; + friend class StructuredColumnsGatherScatterCache; + friend class StructuredColumnsChecksumCache; bool periodic_points_{false}; const StructuredGrid* grid_; From 41fe032c5e705b3a950d5ce936bc87dcf9a63e42 Mon Sep 17 00:00:00 2001 From: Philippe Marguinaud Date: Tue, 16 Jun 2020 13:30:56 +0100 Subject: [PATCH 121/145] ATLAS-299 (Github #42) Add config to Partitioner ctor, and "bands" config option to CheckerboardPartitioner --- src/atlas/grid/Partitioner.cc | 2 +- .../partitioner/CheckerboardPartitioner.cc | 6 ++++++ .../partitioner/CheckerboardPartitioner.h | 1 + .../partitioner/EqualRegionsPartitioner.cc | 18 +++++++++++------- .../partitioner/EqualRegionsPartitioner.h | 2 ++ .../MatchingMeshPartitionerBruteForce.h | 1 + .../MatchingMeshPartitionerLonLatPolygon.h | 1 + .../MatchingMeshPartitionerSphericalPolygon.h | 1 + .../grid/detail/partitioner/Partitioner.cc | 8 +++++++- .../grid/detail/partitioner/Partitioner.h | 9 ++++++--- .../detail/partitioner/TransPartitioner.cc | 2 +- .../grid/detail/partitioner/TransPartitioner.h | 2 +- 12 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/atlas/grid/Partitioner.cc b/src/atlas/grid/Partitioner.cc index 6b3d330ed..bfa385849 100644 --- a/src/atlas/grid/Partitioner.cc +++ b/src/atlas/grid/Partitioner.cc @@ -43,7 +43,7 @@ detail::partitioner::Partitioner* partitioner_from_config( const Partitioner::Co throw_Exception( "'type' missing in configuration for Partitioner", Here() ); } config.get( "partitions", partitions ); - return Factory::build( type, partitions ); + return Factory::build( type, partitions, config ); } } // namespace diff --git a/src/atlas/grid/detail/partitioner/CheckerboardPartitioner.cc b/src/atlas/grid/detail/partitioner/CheckerboardPartitioner.cc index 6e84a15c9..6b0ffe07e 100644 --- a/src/atlas/grid/detail/partitioner/CheckerboardPartitioner.cc +++ b/src/atlas/grid/detail/partitioner/CheckerboardPartitioner.cc @@ -41,6 +41,12 @@ CheckerboardPartitioner::CheckerboardPartitioner( int N ) : Partitioner( N ) { checkerboard_ = true; // default } +CheckerboardPartitioner::CheckerboardPartitioner( int N, const eckit::Parametrisation & config ) : Partitioner( N ) { + nbands_ = 0; // to be computed later + config.get ("bands", nbands_); + checkerboard_ = true; // default +} + CheckerboardPartitioner::CheckerboardPartitioner( int N, int nbands ) : Partitioner( N ) { nbands_ = nbands; checkerboard_ = true; // default diff --git a/src/atlas/grid/detail/partitioner/CheckerboardPartitioner.h b/src/atlas/grid/detail/partitioner/CheckerboardPartitioner.h index 69051a34e..ffc50006d 100644 --- a/src/atlas/grid/detail/partitioner/CheckerboardPartitioner.h +++ b/src/atlas/grid/detail/partitioner/CheckerboardPartitioner.h @@ -24,6 +24,7 @@ class CheckerboardPartitioner : public Partitioner { CheckerboardPartitioner(); CheckerboardPartitioner( int N ); // N is the number of parts (aka MPI tasks) + CheckerboardPartitioner( int N, const eckit::Parametrisation & ); CheckerboardPartitioner( int N, int nbands ); CheckerboardPartitioner( int N, int nbands, bool checkerboard ); diff --git a/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.cc b/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.cc index 1caacbeba..916e18c4c 100644 --- a/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.cc +++ b/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.cc @@ -434,7 +434,8 @@ void eq_regions( int N, double xmin[], double xmax[], double ymin[], double ymax ymax[N - 1] = 0.5 * M_PI - s_cap[s_cap.size() - 2]; } -EqualRegionsPartitioner::EqualRegionsPartitioner() : Partitioner(), N_( nb_partitions() ) { +void EqualRegionsPartitioner:: init () +{ std::vector s_cap; eq_caps( N_, sectors_, s_cap ); bands_.resize( s_cap.size() ); @@ -443,13 +444,16 @@ EqualRegionsPartitioner::EqualRegionsPartitioner() : Partitioner(), N_( nb_parti } } +EqualRegionsPartitioner::EqualRegionsPartitioner() : Partitioner(), N_( nb_partitions() ) { + init (); +} + EqualRegionsPartitioner::EqualRegionsPartitioner( int N ) : Partitioner( N ), N_( N ) { - std::vector s_cap; - eq_caps( N_, sectors_, s_cap ); - bands_.resize( s_cap.size() ); - for ( size_t n = 0; n < s_cap.size(); ++n ) { - bands_[n] = 0.5 * M_PI - s_cap[n]; - } + init (); +} + +EqualRegionsPartitioner::EqualRegionsPartitioner( int N, const eckit::Parametrisation & config ) : Partitioner( N ), N_( N ) { + init (); } int EqualRegionsPartitioner::partition( const double& x, const double& y ) const { diff --git a/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.h b/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.h index 1a03ce992..8970571f9 100644 --- a/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.h +++ b/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.h @@ -80,6 +80,7 @@ class EqualRegionsPartitioner : public Partitioner { EqualRegionsPartitioner(); EqualRegionsPartitioner( int N ); + EqualRegionsPartitioner( int N, const eckit::Parametrisation & config ); void where( int partition, int& band, int& sector ) const; int nb_bands() const { return bands_.size(); } @@ -113,6 +114,7 @@ class EqualRegionsPartitioner : public Partitioner { }; private: + void init (); // Doesn't matter if nodes[] is in degrees or radians, as a sorting // algorithm is used internally void partition( int nb_nodes, NodeInt nodes[], int part[] ) const; diff --git a/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerBruteForce.h b/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerBruteForce.h index 1c3a2406c..44c7fea36 100644 --- a/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerBruteForce.h +++ b/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerBruteForce.h @@ -24,6 +24,7 @@ class MatchingMeshPartitionerBruteForce : public MatchingMeshPartitioner { public: MatchingMeshPartitionerBruteForce() : MatchingMeshPartitioner() {} MatchingMeshPartitionerBruteForce( const idx_t nb_partitions ) : MatchingMeshPartitioner( nb_partitions ) {} + MatchingMeshPartitionerBruteForce( const idx_t nb_partitions, const eckit::Parametrisation & ) : MatchingMeshPartitioner( nb_partitions ) {} MatchingMeshPartitionerBruteForce( const Mesh& mesh ) : MatchingMeshPartitioner( mesh ) {} virtual void partition( const Grid& grid, int partitioning[] ) const; diff --git a/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.h b/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.h index 7f0370aa2..65ce82baa 100644 --- a/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.h +++ b/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.h @@ -24,6 +24,7 @@ class MatchingMeshPartitionerLonLatPolygon : public MatchingMeshPartitioner { public: MatchingMeshPartitionerLonLatPolygon() : MatchingMeshPartitioner() {} MatchingMeshPartitionerLonLatPolygon( const size_t nb_partitions ) : MatchingMeshPartitioner( nb_partitions ) {} + MatchingMeshPartitionerLonLatPolygon( const size_t nb_partitions, const eckit::Parametrisation & config ) : MatchingMeshPartitioner( nb_partitions ) {} MatchingMeshPartitionerLonLatPolygon( const Mesh& mesh ) : MatchingMeshPartitioner( mesh ) {} /** diff --git a/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerSphericalPolygon.h b/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerSphericalPolygon.h index c0317d76a..72e2e18e8 100644 --- a/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerSphericalPolygon.h +++ b/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerSphericalPolygon.h @@ -24,6 +24,7 @@ class MatchingMeshPartitionerSphericalPolygon : public MatchingMeshPartitioner { public: MatchingMeshPartitionerSphericalPolygon() : MatchingMeshPartitioner() {} MatchingMeshPartitionerSphericalPolygon( const idx_t nb_partitions ) : MatchingMeshPartitioner( nb_partitions ) {} + MatchingMeshPartitionerSphericalPolygon( const idx_t nb_partitions, const eckit::Parametrisation & config ) : MatchingMeshPartitioner( nb_partitions ) {} MatchingMeshPartitionerSphericalPolygon( const Mesh& mesh ) : MatchingMeshPartitioner( mesh ) {} /** diff --git a/src/atlas/grid/detail/partitioner/Partitioner.cc b/src/atlas/grid/detail/partitioner/Partitioner.cc index 106355c55..24097c30d 100644 --- a/src/atlas/grid/detail/partitioner/Partitioner.cc +++ b/src/atlas/grid/detail/partitioner/Partitioner.cc @@ -16,6 +16,7 @@ #include "eckit/thread/AutoLock.h" #include "eckit/thread/Mutex.h" +#include "atlas/util/Config.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/detail/partitioner/CheckerboardPartitioner.h" @@ -148,6 +149,11 @@ Partitioner* PartitionerFactory::build( const std::string& name ) { } Partitioner* PartitionerFactory::build( const std::string& name, const idx_t nb_partitions ) { + atlas::util::Config config; + return build (name, nb_partitions, config); +} + +Partitioner* PartitionerFactory::build( const std::string& name, const idx_t nb_partitions, const eckit::Parametrisation & config ) { pthread_once( &once, init ); eckit::AutoLock lock( local_mutex ); @@ -167,7 +173,7 @@ Partitioner* PartitionerFactory::build( const std::string& name, const idx_t nb_ throw_Exception( std::string( "No PartitionerFactory called " ) + name ); } - return ( *j ).second->make( nb_partitions ); + return ( *j ).second->make( nb_partitions, config ); } diff --git a/src/atlas/grid/detail/partitioner/Partitioner.h b/src/atlas/grid/detail/partitioner/Partitioner.h index d817d9425..d125e9a46 100644 --- a/src/atlas/grid/detail/partitioner/Partitioner.h +++ b/src/atlas/grid/detail/partitioner/Partitioner.h @@ -13,7 +13,7 @@ #include #include "atlas/util/Object.h" - +#include "atlas/util/Config.h" #include "atlas/library/config.h" namespace atlas { @@ -64,6 +64,7 @@ class PartitionerFactory { */ static Partitioner* build( const std::string& ); static Partitioner* build( const std::string&, const idx_t nb_partitions ); + static Partitioner* build( const std::string&, const idx_t nb_partitions, const eckit::Parametrisation & ); /*! * \brief list all registered partioner builders @@ -73,8 +74,9 @@ class PartitionerFactory { private: std::string name_; - virtual Partitioner* make() = 0; - virtual Partitioner* make( const idx_t nb_partitions ) = 0; + virtual Partitioner* make() = 0; + virtual Partitioner* make( const idx_t nb_partitions ) = 0; + virtual Partitioner* make( const idx_t nb_partitions, const eckit::Parametrisation & ) = 0; protected: PartitionerFactory( const std::string& ); @@ -88,6 +90,7 @@ class PartitionerBuilder : public PartitionerFactory { virtual Partitioner* make() { return new T(); } virtual Partitioner* make( const idx_t nb_partitions ) { return new T( nb_partitions ); } + virtual Partitioner* make( const idx_t nb_partitions, const eckit::Parametrisation & config ) { return new T( nb_partitions, config ); } public: PartitionerBuilder( const std::string& name ) : PartitionerFactory( name ) {} diff --git a/src/atlas/grid/detail/partitioner/TransPartitioner.cc b/src/atlas/grid/detail/partitioner/TransPartitioner.cc index 4820747f9..14875a5e8 100644 --- a/src/atlas/grid/detail/partitioner/TransPartitioner.cc +++ b/src/atlas/grid/detail/partitioner/TransPartitioner.cc @@ -33,7 +33,7 @@ TransPartitioner::TransPartitioner() : Partitioner() { } } -TransPartitioner::TransPartitioner( const idx_t N ) : Partitioner( N ) { +TransPartitioner::TransPartitioner( const idx_t N, const eckit::Parametrisation& ) : Partitioner( N ) { EqualRegionsPartitioner eqreg( nb_partitions() ); nbands_ = eqreg.nb_bands(); nregions_.resize( nbands_ ); diff --git a/src/atlas/grid/detail/partitioner/TransPartitioner.h b/src/atlas/grid/detail/partitioner/TransPartitioner.h index 4d1ccb5c1..fe6220693 100644 --- a/src/atlas/grid/detail/partitioner/TransPartitioner.h +++ b/src/atlas/grid/detail/partitioner/TransPartitioner.h @@ -30,7 +30,7 @@ class TransPartitioner : public Partitioner { /// @brief Constructor TransPartitioner(); - TransPartitioner( const idx_t nb_partitions ); + TransPartitioner( const idx_t nb_partitions, const eckit::Parametrisation& = util::NoConfig() ); virtual ~TransPartitioner(); From f56718426604feee7659d6d107d7efbc953ec5e3 Mon Sep 17 00:00:00 2001 From: Philippe Marguinaud Date: Tue, 16 Jun 2020 14:08:05 +0100 Subject: [PATCH 122/145] ATLAS-299 (Github #42) Prototype memory-free Distribution via a "light" option --- src/atlas/grid/Distribution.cc | 5 +- src/atlas/grid/Distribution.h | 15 +- .../detail/distribution/DistributionImpl.cc | 141 +++++++++++++----- .../detail/distribution/DistributionImpl.h | 43 +++++- src/tests/grid/CMakeLists.txt | 1 + src/tests/grid/test_distribution.cc | 75 ++++++++++ 6 files changed, 232 insertions(+), 48 deletions(-) create mode 100644 src/tests/grid/test_distribution.cc diff --git a/src/atlas/grid/Distribution.cc b/src/atlas/grid/Distribution.cc index 55f1df449..6f1394dab 100644 --- a/src/atlas/grid/Distribution.cc +++ b/src/atlas/grid/Distribution.cc @@ -22,6 +22,7 @@ namespace atlas { namespace grid { Distribution::Distribution( const Grid& grid ) : Handle( new Implementation( grid ) ) {} +Distribution::Distribution( const Grid& grid, const Config & config ) : Handle( new Implementation( grid, config ) ) {} Distribution::Distribution( const Grid& grid, const Partitioner& partitioner ) : Handle( new Implementation( grid, partitioner ) ) {} @@ -34,10 +35,6 @@ Distribution::Distribution( int nb_partitions, partition_t&& part ) : Distribution::~Distribution() = default; -int Distribution::partition( const gidx_t gidx ) const { - return get()->partition( gidx ); -} - const Distribution::partition_t& Distribution::partition() const { return get()->partition(); } diff --git a/src/atlas/grid/Distribution.h b/src/atlas/grid/Distribution.h index 7a91230d8..89d6d8467 100644 --- a/src/atlas/grid/Distribution.h +++ b/src/atlas/grid/Distribution.h @@ -15,6 +15,7 @@ #include "atlas/grid/detail/distribution/DistributionImpl.h" #include "atlas/library/config.h" #include "atlas/util/ObjectHandle.h" +#include "atlas/util/Config.h" namespace atlas { class Grid; @@ -33,12 +34,15 @@ class Distribution : DOXYGEN_HIDE( public util::ObjectHandle ) public: using partition_t = DistributionImpl::partition_t; + using Config = DistributionImpl::Config; using Handle::Handle; Distribution() = default; Distribution( const Grid& ); + Distribution( const Grid&, const Config& ); + Distribution( const Grid&, const Partitioner& ); Distribution( int nb_partitions, idx_t npts, int partition[], int part0 = 0 ); @@ -47,7 +51,16 @@ class Distribution : DOXYGEN_HIDE( public util::ObjectHandle ) ~Distribution(); - int partition( const gidx_t gidx ) const; + // This method has to be inlined + int partition( const gidx_t gidx ) const + { + return get()->partition( gidx ); + } + + size_t footprint () const + { + return get()->footprint (); + } const partition_t& partition() const; diff --git a/src/atlas/grid/detail/distribution/DistributionImpl.cc b/src/atlas/grid/detail/distribution/DistributionImpl.cc index d6d85e1a2..3a650fa16 100644 --- a/src/atlas/grid/detail/distribution/DistributionImpl.cc +++ b/src/atlas/grid/detail/distribution/DistributionImpl.cc @@ -15,6 +15,7 @@ #include "DistributionImpl.h" #include "atlas/grid/Grid.h" +#include "atlas/util/Config.h" #include "atlas/grid/Partitioner.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/parallel/omp/omp.h" @@ -34,6 +35,66 @@ std::string distribution_type( int N, const Partitioner& p = Partitioner() ) { } } // namespace +DistributionImpl::DistributionImpl( const Grid & grid, const eckit::Parametrisation & config ) +{ + bool light = false; + + config.get ("light", light); + + if (light) + { + gridsize_ = grid.size (); + nb_partitions_ = atlas::mpi::comm ().size (); + blocksize_ = 1; + + config.get ("blocksize", blocksize_); + + nb_blocks_ = gridsize_ / blocksize_; + + if (gridsize_ % blocksize_) + nb_blocks_++; + + nb_pts_.reserve (nb_partitions_); + + for (idx_t iproc = 0; iproc < nb_partitions_; iproc++) + { + // Approximate values + gidx_t imin = blocksize_ * (((iproc + 0) * nb_blocks_) / nb_partitions_); + gidx_t imax = blocksize_ * (((iproc + 1) * nb_blocks_) / nb_partitions_); + + while (imin > 0) + if (partition (imin-blocksize_) == iproc) + imin -= blocksize_; + else + break; + + while (partition (imin) < iproc) + imin += blocksize_; + + while (partition (imax-1) == iproc + 1) + imax -= blocksize_; + + while (imax + blocksize_ <= gridsize_) + if (partition (imax) == iproc) + imax += blocksize_; + else + break; + + imax = std::min (imax, (gidx_t)gridsize_); + nb_pts_.push_back (imax-imin); + } + + max_pts_ = *std::max_element( nb_pts_.begin(), nb_pts_.end() ); + min_pts_ = *std::min_element( nb_pts_.begin(), nb_pts_.end() ); + } + else + { + Partitioner partitioner (config); + setupWithPartitioner (grid, partitioner); + } + +} + DistributionImpl::DistributionImpl( const Grid& grid ) : nb_partitions_( 1 ), part_( grid.size(), 0 ), @@ -42,44 +103,50 @@ DistributionImpl::DistributionImpl( const Grid& grid ) : min_pts_( grid.size() ), type_( distribution_type( nb_partitions_ ) ) {} -DistributionImpl::DistributionImpl( const Grid& grid, const Partitioner& partitioner ) : part_( grid.size() ) { - partitioner.partition( grid, part_.data() ); - nb_partitions_ = partitioner.nb_partitions(); - - // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - // new - size_t size = part_.size(); - int num_threads = atlas_omp_get_max_threads(); - - std::vector > nb_pts_per_thread( num_threads, std::vector( nb_partitions_ ) ); - atlas_omp_parallel { - int thread = atlas_omp_get_thread_num(); - auto& nb_pts = nb_pts_per_thread[thread]; - atlas_omp_for( size_t j = 0; j < size; ++j ) { - int p = part_[j]; - ++nb_pts[p]; - } - } - - nb_pts_.resize( nb_partitions_, 0 ); - for ( int thread = 0; thread < num_threads; ++thread ) { - for ( int p = 0; p < nb_partitions_; ++p ) { - nb_pts_[p] += nb_pts_per_thread[thread][p]; - } - } - +void DistributionImpl::setupWithPartitioner (const Grid & grid, const Partitioner & partitioner) +{ + part_.resize (grid.size ()); + partitioner.partition( grid, part_.data() ); + nb_partitions_ = partitioner.nb_partitions(); + + // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + // new + size_t size = part_.size(); + int num_threads = atlas_omp_get_max_threads(); + + std::vector > nb_pts_per_thread( num_threads, std::vector( nb_partitions_ ) ); + atlas_omp_parallel { + int thread = atlas_omp_get_thread_num(); + auto& nb_pts = nb_pts_per_thread[thread]; + atlas_omp_for( size_t j = 0; j < size; ++j ) { + int p = part_[j]; + ++nb_pts[p]; + } + } + + nb_pts_.resize( nb_partitions_, 0 ); + for ( int thread = 0; thread < num_threads; ++thread ) { + for ( int p = 0; p < nb_partitions_; ++p ) { + nb_pts_[p] += nb_pts_per_thread[thread][p]; + } + } + + + // ============================================== + // previous + // + // nb_pts_.resize( nb_partitions_, 0 ); + // for ( idx_t j = 0, size = static_cast( part_.size() ); j < size; ++j ) { + // ++nb_pts_[part_[j]]; + // } + // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + max_pts_ = *std::max_element( nb_pts_.begin(), nb_pts_.end() ); + min_pts_ = *std::min_element( nb_pts_.begin(), nb_pts_.end() ); + type_ = distribution_type( nb_partitions_, partitioner ); +} - // ============================================== - // previous - // - // nb_pts_.resize( nb_partitions_, 0 ); - // for ( idx_t j = 0, size = static_cast( part_.size() ); j < size; ++j ) { - // ++nb_pts_[part_[j]]; - // } - // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< - max_pts_ = *std::max_element( nb_pts_.begin(), nb_pts_.end() ); - min_pts_ = *std::min_element( nb_pts_.begin(), nb_pts_.end() ); - type_ = distribution_type( nb_partitions_, partitioner ); +DistributionImpl::DistributionImpl( const Grid& grid, const Partitioner& partitioner ) { + setupWithPartitioner (grid, partitioner); } DistributionImpl::DistributionImpl( int nb_partitions, idx_t npts, int part[], int part0 ) { diff --git a/src/atlas/grid/detail/distribution/DistributionImpl.h b/src/atlas/grid/detail/distribution/DistributionImpl.h index 187b13e68..51541dc40 100644 --- a/src/atlas/grid/detail/distribution/DistributionImpl.h +++ b/src/atlas/grid/detail/distribution/DistributionImpl.h @@ -15,16 +15,20 @@ #include "atlas/util/Object.h" #include "atlas/util/vector.h" - +#include "atlas/util/Config.h" #include "atlas/library/config.h" + +namespace eckit { +class Parametrisation; +} + namespace atlas { class Grid; namespace grid { class Partitioner; } - } // namespace atlas namespace atlas { @@ -32,9 +36,11 @@ namespace grid { class DistributionImpl : public util::Object { public: + using Config = atlas::util::Config; using partition_t = atlas::vector; DistributionImpl( const Grid& ); + DistributionImpl( const Grid&, const eckit::Parametrisation & ); DistributionImpl( const Grid&, const Partitioner& ); @@ -44,15 +50,21 @@ class DistributionImpl : public util::Object { virtual ~DistributionImpl(); - int partition( const gidx_t gidx ) const { return part_[gidx]; } + int partition( const gidx_t gidx ) const + { + if (part_.size () > 0) + return part_[gidx]; + idx_t iblock = gidx / blocksize_; + return (iblock * nb_partitions_) / nb_blocks_; + } - const partition_t& partition() const { return part_; } + const partition_t& partition() const { checkPartition (); return part_; } idx_t nb_partitions() const { return nb_partitions_; } operator const partition_t&() const { return part_; } - const int* data() const { return part_.data(); } + const int* data() const { checkPartition (); return part_.data(); } const std::vector& nb_pts() const { return nb_pts_; } @@ -63,8 +75,27 @@ class DistributionImpl : public util::Object { void print( std::ostream& ) const; + size_t footprint () const + { + return nb_pts_.size () * sizeof (nb_pts_[0]) + part_.size () * sizeof (part_[0]); + } + + private: - idx_t nb_partitions_; + + void setupWithPartitioner (const Grid &, const Partitioner &); + + void checkPartition () const + { + if (part_.size () == 0) + throw_Exception ("partition array of distribution is empty"); + } + // For trivial partitionning + size_t gridsize_ = 0; + idx_t nb_partitions_ = 0; + size_t blocksize_ = 0; + idx_t nb_blocks_ = 0; + partition_t part_; std::vector nb_pts_; idx_t max_pts_; diff --git a/src/tests/grid/CMakeLists.txt b/src/tests/grid/CMakeLists.txt index d23f9ea5d..110e0de70 100644 --- a/src/tests/grid/CMakeLists.txt +++ b/src/tests/grid/CMakeLists.txt @@ -31,6 +31,7 @@ foreach(test test_spacing test_state test_largegrid + test_distribution test_grid_hash) ecbuild_add_test( TARGET atlas_${test} SOURCES ${test}.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) diff --git a/src/tests/grid/test_distribution.cc b/src/tests/grid/test_distribution.cc new file mode 100644 index 000000000..a581bf69d --- /dev/null +++ b/src/tests/grid/test_distribution.cc @@ -0,0 +1,75 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include +#include +#include + +#include "atlas/grid.h" +#include "atlas/parallel/mpi/mpi.h" +#include "atlas/util/Config.h" + +#include "tests/AtlasTestEnvironment.h" + +using Grid = atlas::Grid; +using Config = atlas::util::Config; + +namespace atlas { +namespace test { + +//----------------------------------------------------------------------------- + +CASE( "test_nbands" ) +{ + auto & comm = atlas::mpi::comm (); + int nproc = comm.size (); + + StructuredGrid grid = Grid ("L200x100"); + atlas::grid::Distribution dist (grid, atlas::util::Config ("type", "checkerboard") | Config ("bands", nproc)); + + for (int i = 1; i < grid.size (); i++) + EXPECT (dist.partition (i-1) <= dist.partition (i)); +} + +CASE( "test_light" ) +{ + auto & comm = atlas::mpi::comm (); + int nproc = comm.size (); + + const int nx = 400, ny = 200; + + StructuredGrid grid = Grid (std::string ("L") + std::to_string (nx) + "x" + std::to_string (ny)); + atlas::grid::Distribution dist1 (grid, atlas::util::Config ("type", "checkerboard") | Config ("bands", nproc)); + + atlas::grid::Distribution dist2 (grid, Config ("light", true) | Config ("blocksize", nx)); + + EXPECT (dist2.footprint () < 100); + + if ((ny % nproc) == 0) + for (int i = 0; i < grid.size (); i++) + EXPECT (dist1.partition (i) == dist2.partition (i)); + + for (int iy = 0, jglo = 0; iy < ny; iy++) + { + int jglo0 = jglo; + for (int ix = 0; ix < nx; ix++, jglo++) + EXPECT (dist2.partition (jglo) == dist2.partition (jglo0)); + } + +} + +//----------------------------------------------------------------------------- + +} // namespace test +} // namespace atlas + +int main( int argc, char** argv ) { + return atlas::test::run( argc, argv ); +} From af7c1e7ca24b52b0f7d061de7074c4c5e1181d30 Mon Sep 17 00:00:00 2001 From: Philippe Marguinaud Date: Mon, 20 Apr 2020 07:14:53 +0000 Subject: [PATCH 123/145] ATLAS-299 (Github #42) Use partition accessor in StructuredColumns_setup.cc; extends test --- .../detail/StructuredColumns_setup.cc | 15 ++--- src/tests/grid/test_distribution.cc | 65 ++++++++++++++++++- 2 files changed, 71 insertions(+), 9 deletions(-) diff --git a/src/atlas/functionspace/detail/StructuredColumns_setup.cc b/src/atlas/functionspace/detail/StructuredColumns_setup.cc index 9317de222..a7f1e8c74 100644 --- a/src/atlas/functionspace/detail/StructuredColumns_setup.cc +++ b/src/atlas/functionspace/detail/StructuredColumns_setup.cc @@ -177,17 +177,17 @@ void StructuredColumns::setup( const grid::Distribution& distribution, const eck break; } } - auto& __j_begin = thread_reduce_j_begin[thread_num]; - auto& __j_end = thread_reduce_j_end[thread_num]; - auto& __owned = thread_reduce_owned[thread_num]; - idx_t c = begin; - const auto& partition = distribution.partition(); + auto& __j_begin = thread_reduce_j_begin[thread_num]; + auto& __j_end = thread_reduce_j_end[thread_num]; + auto& __owned = thread_reduce_owned[thread_num]; + idx_t c = begin; + for ( idx_t j = thread_j_begin; j < thread_j_end; ++j ) { auto& __i_begin = thread_reduce_i_begin[j][thread_num]; auto& __i_end = thread_reduce_i_end[j][thread_num]; bool j_in_partition{false}; for ( idx_t i = thread_i_begin[j]; i < thread_i_end[j]; ++i, ++c ) { - if ( partition[c] == mpi_rank ) { + if ( distribution.partition( c ) == mpi_rank ) { j_in_partition = true; __i_begin = std::min( __i_begin, i ); __i_end = std::max( __i_end, i + 1 ); @@ -552,7 +552,6 @@ void StructuredColumns::setup( const grid::Distribution& distribution, const eck auto index_i = array::make_indexview( field_index_i_ ); auto index_j = array::make_indexview( field_index_j_ ); - const auto& partition = distribution.partition(); atlas_omp_parallel_for( idx_t n = 0; n < gridpoints.size(); ++n ) { const GridPoint& gp = gridpoints[n]; if ( gp.j >= 0 && gp.j < grid_->ny() ) { @@ -569,7 +568,7 @@ void StructuredColumns::setup( const grid::Distribution& distribution, const eck if ( gp.i >= 0 && gp.i < grid_->nx( gp.j ) ) { in_domain = true; gidx_t k = global_offsets[gp.j] + gp.i; - part( gp.r ) = partition[k]; + part( gp.r ) = distribution.partition( k ); global_idx( gp.r ) = k + 1; } } diff --git a/src/tests/grid/test_distribution.cc b/src/tests/grid/test_distribution.cc index a581bf69d..056d7f186 100644 --- a/src/tests/grid/test_distribution.cc +++ b/src/tests/grid/test_distribution.cc @@ -15,6 +15,9 @@ #include "atlas/grid.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/Config.h" +#include "atlas/array.h" +#include "atlas/field.h" +#include "atlas/functionspace.h" #include "tests/AtlasTestEnvironment.h" @@ -24,6 +27,21 @@ using Config = atlas::util::Config; namespace atlas { namespace test { +atlas::FieldSet +getIJ (const atlas::functionspace::StructuredColumns & fs) +{ + atlas::FieldSet ij; + + auto i = atlas::array::make_view (fs.index_i ()); + auto j = atlas::array::make_view (fs.index_j ()); + + ij.add (fs.index_i ()); + ij.add (fs.index_j ()); + + return ij; +} + + //----------------------------------------------------------------------------- CASE( "test_nbands" ) @@ -52,7 +70,18 @@ CASE( "test_light" ) EXPECT (dist2.footprint () < 100); - if ((ny % nproc) == 0) + const auto & nb_pts2 = dist2.nb_pts (); + + int count = 0; + for (int i = 0; i < grid.size (); i++) + if (dist2.partition (i) == comm.rank ()) + count++; + + EXPECT (count == nb_pts2[comm.rank ()]); + + bool same = (ny % nproc) == 0; // Compare light & regular distributions when possible + + if (same) for (int i = 0; i < grid.size (); i++) EXPECT (dist1.partition (i) == dist2.partition (i)); @@ -63,6 +92,40 @@ CASE( "test_light" ) EXPECT (dist2.partition (jglo) == dist2.partition (jglo0)); } + + atlas::functionspace::StructuredColumns fs1 (grid, dist1, atlas::util::Config ("halo", 1) | Config ("periodic_points", true)); + atlas::functionspace::StructuredColumns fs2 (grid, dist2, atlas::util::Config ("halo", 1) | Config ("periodic_points", true)); + + auto ij1 = getIJ (fs1); + auto ij2 = getIJ (fs2); + + fs1.haloExchange (ij1); + fs2.haloExchange (ij2); + + if (same) + { + EXPECT (fs1.size () == fs1.size ()); + EXPECT (fs1.sizeOwned () == fs1.sizeOwned ()); + + auto i1 = atlas::array::make_view (ij1[0]); + auto j1 = atlas::array::make_view (ij1[1]); + auto i2 = atlas::array::make_view (ij2[0]); + auto j2 = atlas::array::make_view (ij2[1]); + + for (int k = 0; k < fs1.sizeOwned (); k++) + { + EXPECT (i1[k] == i2[k]); + EXPECT (j1[k] == j2[k]); + } + } + + + for (int j = fs2.j_begin_halo (); j < fs2.j_end_halo (); j++) + { + EXPECT (fs2.i_begin_halo (j) == -1); + EXPECT (fs2.i_end_halo (j) == nx + 2); + } + } //----------------------------------------------------------------------------- From 558f95fabdd66287da3e9d113074af870eadfd47 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 11 Jun 2020 17:51:39 +0100 Subject: [PATCH 124/145] ATLAS-299 (Github #42) Rework Distribution class to become polymorphic with 2 implementations: DistrubutionArray & DistributionFunction (with 2 new implementations: equal_bands and regular_bands) --- src/atlas/CMakeLists.txt | 20 ++ src/atlas/grid/Distribution.cc | 45 ++-- src/atlas/grid/Distribution.h | 38 ++-- src/atlas/grid/Partitioner.cc | 15 +- src/atlas/grid/Partitioner.h | 1 + .../detail/distribution/BandsDistribution.cc | 78 +++++++ .../detail/distribution/BandsDistribution.h | 41 ++++ .../detail/distribution/DistributionArray.cc | 143 +++++++++++++ .../detail/distribution/DistributionArray.h | 84 ++++++++ .../distribution/DistributionFunction.cc | 47 +++++ .../distribution/DistributionFunction.h | 64 ++++++ .../detail/distribution/DistributionImpl.cc | 199 +----------------- .../detail/distribution/DistributionImpl.h | 78 ++----- .../detail/distribution/SerialDistribution.cc | 35 +++ .../detail/distribution/SerialDistribution.h | 31 +++ .../detail/partitioner/BandsPartitioner.cc | 58 +++++ .../detail/partitioner/BandsPartitioner.h | 55 +++++ .../partitioner/CheckerboardPartitioner.cc | 6 +- .../partitioner/CheckerboardPartitioner.h | 2 +- .../partitioner/EqualBandsPartitioner.cc | 31 +++ .../partitioner/EqualBandsPartitioner.h | 34 +++ .../partitioner/EqualRegionsPartitioner.cc | 12 +- .../partitioner/EqualRegionsPartitioner.h | 4 +- .../MatchingMeshPartitionerBruteForce.h | 3 +- .../MatchingMeshPartitionerLonLatPolygon.h | 3 +- .../MatchingMeshPartitionerSphericalPolygon.h | 3 +- .../grid/detail/partitioner/Partitioner.cc | 10 +- .../grid/detail/partitioner/Partitioner.h | 18 +- .../partitioner/RegularBandsPartitioner.cc | 31 +++ .../partitioner/RegularBandsPartitioner.h | 33 +++ .../detail/partitioner/SerialPartitioner.cc | 17 ++ .../detail/partitioner/SerialPartitioner.h | 49 +++++ .../detail/RegularMeshGenerator.cc | 22 +- .../detail/RegularMeshGenerator.h | 2 +- .../detail/StructuredMeshGenerator.cc | 41 ++-- .../detail/StructuredMeshGenerator.h | 8 +- .../test_structuredcolumns_haloexchange.cc | 30 +++ src/tests/grid/CMakeLists.txt | 16 +- src/tests/grid/test_distribution.cc | 138 ------------ .../grid/test_distribution_regular_bands.cc | 145 +++++++++++++ src/tests/trans/test_trans.cc | 2 +- 41 files changed, 1186 insertions(+), 506 deletions(-) create mode 100644 src/atlas/grid/detail/distribution/BandsDistribution.cc create mode 100644 src/atlas/grid/detail/distribution/BandsDistribution.h create mode 100644 src/atlas/grid/detail/distribution/DistributionArray.cc create mode 100644 src/atlas/grid/detail/distribution/DistributionArray.h create mode 100644 src/atlas/grid/detail/distribution/DistributionFunction.cc create mode 100644 src/atlas/grid/detail/distribution/DistributionFunction.h create mode 100644 src/atlas/grid/detail/distribution/SerialDistribution.cc create mode 100644 src/atlas/grid/detail/distribution/SerialDistribution.h create mode 100644 src/atlas/grid/detail/partitioner/BandsPartitioner.cc create mode 100644 src/atlas/grid/detail/partitioner/BandsPartitioner.h create mode 100644 src/atlas/grid/detail/partitioner/EqualBandsPartitioner.cc create mode 100644 src/atlas/grid/detail/partitioner/EqualBandsPartitioner.h create mode 100644 src/atlas/grid/detail/partitioner/RegularBandsPartitioner.cc create mode 100644 src/atlas/grid/detail/partitioner/RegularBandsPartitioner.h create mode 100644 src/atlas/grid/detail/partitioner/SerialPartitioner.cc create mode 100644 src/atlas/grid/detail/partitioner/SerialPartitioner.h delete mode 100644 src/tests/grid/test_distribution.cc create mode 100644 src/tests/grid/test_distribution_regular_bands.cc diff --git a/src/atlas/CMakeLists.txt b/src/atlas/CMakeLists.txt index 6cc70d816..26635776b 100644 --- a/src/atlas/CMakeLists.txt +++ b/src/atlas/CMakeLists.txt @@ -142,12 +142,27 @@ grid/detail/grid/Regional.cc grid/detail/distribution/DistributionImpl.h grid/detail/distribution/DistributionImpl.cc +grid/detail/distribution/DistributionArray.cc +grid/detail/distribution/DistributionArray.h +grid/detail/distribution/DistributionFunction.cc +grid/detail/distribution/DistributionFunction.h + +grid/detail/distribution/BandsDistribution.cc +grid/detail/distribution/BandsDistribution.h +grid/detail/distribution/SerialDistribution.cc +grid/detail/distribution/SerialDistribution.h + grid/detail/vertical/VerticalInterface.h grid/detail/vertical/VerticalInterface.cc + +grid/detail/partitioner/BandsPartitioner.cc +grid/detail/partitioner/BandsPartitioner.h grid/detail/partitioner/CheckerboardPartitioner.cc grid/detail/partitioner/CheckerboardPartitioner.h +grid/detail/partitioner/EqualBandsPartitioner.cc +grid/detail/partitioner/EqualBandsPartitioner.h grid/detail/partitioner/EqualRegionsPartitioner.cc grid/detail/partitioner/EqualRegionsPartitioner.h grid/detail/partitioner/MatchingMeshPartitioner.h @@ -164,6 +179,11 @@ grid/detail/partitioner/MatchingFunctionSpacePartitionerLonLatPolygon.cc grid/detail/partitioner/MatchingFunctionSpacePartitionerLonLatPolygon.h grid/detail/partitioner/Partitioner.cc grid/detail/partitioner/Partitioner.h +grid/detail/partitioner/RegularBandsPartitioner.cc +grid/detail/partitioner/RegularBandsPartitioner.h +grid/detail/partitioner/SerialPartitioner.cc +grid/detail/partitioner/SerialPartitioner.h + grid/detail/spacing/Spacing.cc grid/detail/spacing/Spacing.h diff --git a/src/atlas/grid/Distribution.cc b/src/atlas/grid/Distribution.cc index 6f1394dab..86e0217d3 100644 --- a/src/atlas/grid/Distribution.cc +++ b/src/atlas/grid/Distribution.cc @@ -8,45 +8,36 @@ * nor does it submit to any jurisdiction. */ -#include - #include "Distribution.h" +#include "eckit/utils/MD5.h" + #include "atlas/grid/Grid.h" #include "atlas/grid/Partitioner.h" -#include "atlas/grid/detail/distribution/DistributionImpl.h" -#include "atlas/parallel/mpi/mpi.h" -#include "atlas/runtime/Log.h" +#include "atlas/grid/detail/distribution/DistributionArray.h" +#include "atlas/grid/detail/distribution/SerialDistribution.h" namespace atlas { namespace grid { -Distribution::Distribution( const Grid& grid ) : Handle( new Implementation( grid ) ) {} -Distribution::Distribution( const Grid& grid, const Config & config ) : Handle( new Implementation( grid, config ) ) {} +using namespace detail::distribution; + +Distribution::Distribution( const Grid& grid ) : Handle( new SerialDistribution{grid} ) {} + +Distribution::Distribution( const Grid& grid, const Config& config ) : + Handle( Partitioner( config ).partition( grid ).get() ) {} Distribution::Distribution( const Grid& grid, const Partitioner& partitioner ) : - Handle( new Implementation( grid, partitioner ) ) {} + Handle( partitioner.partition( grid ) ) {} Distribution::Distribution( int nb_partitions, idx_t npts, int part[], int part0 ) : - Handle( new Implementation( nb_partitions, npts, part, part0 ) ) {} + Handle( new DistributionArray( nb_partitions, npts, part, part0 ) ) {} Distribution::Distribution( int nb_partitions, partition_t&& part ) : - Handle( new Implementation( nb_partitions, std::move( part ) ) ) {} + Handle( new DistributionArray( nb_partitions, std::move( part ) ) ) {} Distribution::~Distribution() = default; -const Distribution::partition_t& Distribution::partition() const { - return get()->partition(); -} - -idx_t Distribution::nb_partitions() const { - return get()->nb_partitions(); -} - -const int* Distribution::data() const { - return get()->data(); -} - const std::vector& Distribution::nb_pts() const { return get()->nb_pts(); } @@ -68,8 +59,14 @@ std::ostream& operator<<( std::ostream& os, const Distribution& distribution ) { return os; } -Distribution::operator const partition_t&() const { - return *get(); +void Distribution::hash( eckit::Hash& hash ) const { + get()->hash( hash ); +} + +std::string Distribution::hash() const { + eckit::MD5 h; + hash( h ); + return h.digest(); } } // namespace grid diff --git a/src/atlas/grid/Distribution.h b/src/atlas/grid/Distribution.h index 89d6d8467..9287c24c8 100644 --- a/src/atlas/grid/Distribution.h +++ b/src/atlas/grid/Distribution.h @@ -14,15 +14,14 @@ #include "atlas/grid/detail/distribution/DistributionImpl.h" #include "atlas/library/config.h" -#include "atlas/util/ObjectHandle.h" #include "atlas/util/Config.h" +#include "atlas/util/ObjectHandle.h" +#include "atlas/util/vector.h" namespace atlas { class Grid; namespace grid { class Partitioner; -class DistributionImpl; -class Partitioner; } // namespace grid } // namespace atlas @@ -33,51 +32,50 @@ class Distribution : DOXYGEN_HIDE( public util::ObjectHandle ) friend class Partitioner; public: - using partition_t = DistributionImpl::partition_t; - using Config = DistributionImpl::Config; + using Config = DistributionImpl::Config; + using partition_t = atlas::vector; using Handle::Handle; Distribution() = default; + /// @brief Create a serial distribution Distribution( const Grid& ); + /// @brief Create a distribution specified by a configuration Distribution( const Grid&, const Config& ); + /// @brief Create a distribution using a given partitioner Distribution( const Grid&, const Partitioner& ); + /// @brief Create a distribution by given array, and make internal copy Distribution( int nb_partitions, idx_t npts, int partition[], int part0 = 0 ); + /// @brief Create a distribution by given array, and take ownership (move) Distribution( int nb_partitions, partition_t&& partition ); ~Distribution(); - // This method has to be inlined - int partition( const gidx_t gidx ) const - { - return get()->partition( gidx ); - } - - size_t footprint () const - { - return get()->footprint (); - } + int partition( const gidx_t gidx ) const { return get()->partition( gidx ); } - const partition_t& partition() const; + size_t footprint() const { return get()->footprint(); } - idx_t nb_partitions() const; + idx_t nb_partitions() const { return get()->nb_partitions(); } - operator const partition_t&() const; - - const int* data() const; + gidx_t size() const { return get()->size(); } const std::vector& nb_pts() const; idx_t max_pts() const; + idx_t min_pts() const; const std::string& type() const; friend std::ostream& operator<<( std::ostream& os, const Distribution& distribution ); + + std::string hash() const; + + void hash( eckit::Hash& ) const; }; } // namespace grid diff --git a/src/atlas/grid/Partitioner.cc b/src/atlas/grid/Partitioner.cc index bfa385849..207d47b25 100644 --- a/src/atlas/grid/Partitioner.cc +++ b/src/atlas/grid/Partitioner.cc @@ -36,17 +36,24 @@ Partitioner::Partitioner( const std::string& type, const idx_t nb_partitions ) : Handle( Factory::build( type, nb_partitions ) ) {} namespace { +detail::partitioner::Partitioner* partitioner_from_config( const std::string& type, + const Partitioner::Config& config ) { + long partitions = mpi::size(); + config.get( "partitions", partitions ); + return Factory::build( type, partitions, config ); +} detail::partitioner::Partitioner* partitioner_from_config( const Partitioner::Config& config ) { std::string type; - long partitions = mpi::size(); if ( not config.get( "type", type ) ) { throw_Exception( "'type' missing in configuration for Partitioner", Here() ); } - config.get( "partitions", partitions ); - return Factory::build( type, partitions, config ); + return partitioner_from_config( type, config ); } } // namespace +Partitioner::Partitioner( const std::string& type, const Config& config ) : + Handle( partitioner_from_config( type, config ) ) {} + Partitioner::Partitioner( const Config& config ) : Handle( partitioner_from_config( config ) ) {} void Partitioner::partition( const Grid& grid, int part[] ) const { @@ -55,7 +62,7 @@ void Partitioner::partition( const Grid& grid, int part[] ) const { } Distribution Partitioner::partition( const Grid& grid ) const { - return Distribution( grid, *this ); + return get()->partition( grid ); } idx_t Partitioner::nb_partitions() const { diff --git a/src/atlas/grid/Partitioner.h b/src/atlas/grid/Partitioner.h index a09fc4f65..ec1669a66 100644 --- a/src/atlas/grid/Partitioner.h +++ b/src/atlas/grid/Partitioner.h @@ -79,6 +79,7 @@ class Partitioner : DOXYGEN_HIDE( public util::ObjectHandle + +#include "atlas/grid/Grid.h" + +namespace atlas { +namespace grid { +namespace detail { +namespace distribution { + +BandsDistribution::BandsDistribution( const atlas::Grid& grid, atlas::idx_t nb_partitions, const std::string& type, + size_t blocksize ) : + DistributionFunctionT( grid ) { + type_ = type; + size_t gridsize = grid.size(); + size_ = gridsize; + nb_partitions_ = nb_partitions; + blocksize_ = blocksize; + + nb_blocks_ = gridsize / blocksize_; + + if ( gridsize % blocksize_ ) + nb_blocks_++; + + nb_pts_.reserve( nb_partitions_ ); + + for ( idx_t iproc = 0; iproc < nb_partitions_; iproc++ ) { + // Approximate values + gidx_t imin = blocksize_ * ( ( ( iproc + 0 ) * nb_blocks_ ) / nb_partitions_ ); + gidx_t imax = blocksize_ * ( ( ( iproc + 1 ) * nb_blocks_ ) / nb_partitions_ ); + + while ( imin > 0 ) { + if ( function( imin - blocksize_ ) == iproc ) + imin -= blocksize_; + else + break; + } + + while ( function( imin ) < iproc ) { + imin += blocksize_; + } + + while ( function( imax - 1 ) == iproc + 1 ) { + imax -= blocksize_; + } + + while ( imax + blocksize_ <= gridsize ) { + if ( function( imax ) == iproc ) { + imax += blocksize_; + } + else { + break; + } + } + + imax = std::min( imax, (gidx_t)gridsize ); + nb_pts_.push_back( imax - imin ); + } + + max_pts_ = *std::max_element( nb_pts_.begin(), nb_pts_.end() ); + min_pts_ = *std::min_element( nb_pts_.begin(), nb_pts_.end() ); +} + +} // namespace distribution +} // namespace detail +} // namespace grid +} // namespace atlas diff --git a/src/atlas/grid/detail/distribution/BandsDistribution.h b/src/atlas/grid/detail/distribution/BandsDistribution.h new file mode 100644 index 000000000..b210fb45d --- /dev/null +++ b/src/atlas/grid/detail/distribution/BandsDistribution.h @@ -0,0 +1,41 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#pragma once + +#include + +#include "atlas/grid/detail/distribution/DistributionFunction.h" + +namespace atlas { +namespace grid { +namespace detail { +namespace distribution { + + +class BandsDistribution : public DistributionFunctionT { +private: + size_t blocksize_; + idx_t nb_blocks_; + +public: + BandsDistribution( const Grid& grid, idx_t nb_partitions, const std::string& type, size_t blocksize = 1 ); + + int function( gidx_t gidx ) const { + idx_t iblock = gidx / blocksize_; + return ( iblock * nb_partitions_ ) / nb_blocks_; + } +}; + + +} // namespace distribution +} // namespace detail +} // namespace grid +} // namespace atlas diff --git a/src/atlas/grid/detail/distribution/DistributionArray.cc b/src/atlas/grid/detail/distribution/DistributionArray.cc new file mode 100644 index 000000000..3fb55fb28 --- /dev/null +++ b/src/atlas/grid/detail/distribution/DistributionArray.cc @@ -0,0 +1,143 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include "DistributionArray.h" + +#include +#include +#include + +#include "eckit/types/Types.h" +#include "eckit/utils/Hash.h" + +#include "atlas/grid/Grid.h" +#include "atlas/grid/Partitioner.h" +#include "atlas/parallel/omp/omp.h" +#include "atlas/util/Config.h" + +namespace atlas { +namespace grid { +namespace detail { +namespace distribution { + +namespace { +std::string distribution_type( int N, const Partitioner& p = Partitioner() ) { + if ( N == 1 ) { + return "serial"; + } + if ( not p ) { + return "custom"; + } + return p.type(); +} +} // namespace + +DistributionArray::DistributionArray( const Grid& grid, const eckit::Parametrisation& config ) : + DistributionArray( grid, Partitioner{config} ) {} + + +DistributionArray::DistributionArray( const Grid& grid, const Partitioner& partitioner ) { + part_.resize( grid.size() ); + partitioner.partition( grid, part_.data() ); + nb_partitions_ = partitioner.nb_partitions(); + + size_t size = part_.size(); + int num_threads = atlas_omp_get_max_threads(); + + std::vector > nb_pts_per_thread( num_threads, std::vector( nb_partitions_ ) ); + atlas_omp_parallel { + int thread = atlas_omp_get_thread_num(); + auto& nb_pts = nb_pts_per_thread[thread]; + atlas_omp_for( size_t j = 0; j < size; ++j ) { + int p = part_[j]; + ++nb_pts[p]; + } + } + + nb_pts_.resize( nb_partitions_, 0 ); + for ( int thread = 0; thread < num_threads; ++thread ) { + for ( int p = 0; p < nb_partitions_; ++p ) { + nb_pts_[p] += nb_pts_per_thread[thread][p]; + } + } + + max_pts_ = *std::max_element( nb_pts_.begin(), nb_pts_.end() ); + min_pts_ = *std::min_element( nb_pts_.begin(), nb_pts_.end() ); + type_ = distribution_type( nb_partitions_, partitioner ); +} + +DistributionArray::DistributionArray( int nb_partitions, idx_t npts, int part[], int part0 ) { + part_.assign( part, part + npts ); + if ( nb_partitions == 0 ) { + std::set partset( part_.begin(), part_.end() ); + nb_partitions_ = static_cast( partset.size() ); + } + else { + nb_partitions_ = nb_partitions; + } + nb_pts_.resize( nb_partitions_, 0 ); + for ( idx_t j = 0, size = static_cast( part_.size() ); j < size; ++j ) { + part_[j] -= part0; + ++nb_pts_[part_[j]]; + } + max_pts_ = *std::max_element( nb_pts_.begin(), nb_pts_.end() ); + min_pts_ = *std::min_element( nb_pts_.begin(), nb_pts_.end() ); + type_ = distribution_type( nb_partitions_ ); +} + +DistributionArray::DistributionArray( int nb_partitions, partition_t&& part ) : + nb_partitions_( nb_partitions ), part_( std::move( part ) ), nb_pts_( nb_partitions_, 0 ) { + size_t size = part_.size(); + int num_threads = atlas_omp_get_max_threads(); + std::vector > nb_pts_per_thread( num_threads, std::vector( nb_partitions_ ) ); + atlas_omp_parallel { + int thread = atlas_omp_get_thread_num(); + auto& nb_pts = nb_pts_per_thread[thread]; + atlas_omp_for( size_t j = 0; j < size; ++j ) { + int p = part_[j]; + ++nb_pts[p]; + } + } + for ( int thread = 0; thread < num_threads; ++thread ) { + for ( int p = 0; p < nb_partitions_; ++p ) { + nb_pts_[p] += nb_pts_per_thread[thread][p]; + } + } + + max_pts_ = *std::max_element( nb_pts_.begin(), nb_pts_.end() ); + min_pts_ = *std::min_element( nb_pts_.begin(), nb_pts_.end() ); + type_ = distribution_type( nb_partitions_ ); +} + +DistributionArray::~DistributionArray() = default; + +void DistributionArray::print( std::ostream& s ) const { + auto print_partition = [&]( std::ostream& s ) { + eckit::output_list list_printer( s ); + for ( size_t i = 0, size = part_.size(); i < size; i++ ) { + list_printer.push_back( part_[i] ); + } + }; + s << "Distribution( " + << "type: " << type_ << ", nb_points: " << size() << ", nb_partitions: " << nb_pts_.size() << ", parts : "; + print_partition( s ); +} + +void DistributionArray::hash( eckit::Hash& hash ) const { + for ( size_t i = 0; i < part_.size(); i++ ) { + hash.add( part_[i] ); + } +} + + +} // namespace distribution +} // namespace detail +} // namespace grid +} // namespace atlas diff --git a/src/atlas/grid/detail/distribution/DistributionArray.h b/src/atlas/grid/detail/distribution/DistributionArray.h new file mode 100644 index 000000000..942db6bbe --- /dev/null +++ b/src/atlas/grid/detail/distribution/DistributionArray.h @@ -0,0 +1,84 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#pragma once + +#include +#include + + +#include "atlas/grid/detail/distribution/DistributionImpl.h" +#include "atlas/util/vector.h" + + +namespace atlas { +namespace grid { +namespace detail { +namespace distribution { + +class DistributionArray : public DistributionImpl { +public: + using partition_t = atlas::vector; + + DistributionArray() = default; + + DistributionArray( const Grid&, const eckit::Parametrisation& ); + + DistributionArray( const Grid&, const Partitioner& ); + + DistributionArray( int nb_partitions, idx_t npts, int partition[], int part0 = 0 ); + + DistributionArray( int nb_partitions, partition_t&& partition ); + + virtual ~DistributionArray(); + + int partition( const gidx_t gidx ) const override { return part_[gidx]; } + + const partition_t& partition() const { return part_; } + + idx_t nb_partitions() const override { return nb_partitions_; } + + operator const partition_t&() const { return part_; } + + const int* data() const { return part_.data(); } + + const std::vector& nb_pts() const override { return nb_pts_; } + + idx_t max_pts() const override { return max_pts_; } + idx_t min_pts() const override { return min_pts_; } + + const std::string& type() const override { return type_; } + + void print( std::ostream& ) const override; + + size_t footprint() const override { + return nb_pts_.size() * sizeof( nb_pts_[0] ) + part_.size() * sizeof( part_[0] ); + } + + bool functional() const override { return false; } + + virtual gidx_t size() const { return part_.size(); } + + void hash( eckit::Hash& ) const override; + +protected: + idx_t nb_partitions_ = 0; + + partition_t part_; + std::vector nb_pts_; + idx_t max_pts_; + idx_t min_pts_; + std::string type_; +}; + +} // namespace distribution +} // namespace detail +} // namespace grid +} // namespace atlas diff --git a/src/atlas/grid/detail/distribution/DistributionFunction.cc b/src/atlas/grid/detail/distribution/DistributionFunction.cc new file mode 100644 index 000000000..3a9aad44f --- /dev/null +++ b/src/atlas/grid/detail/distribution/DistributionFunction.cc @@ -0,0 +1,47 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include "DistributionFunction.h" + +#include +#include + +#include "eckit/types/Types.h" +#include "eckit/utils/Hash.h" + +#include "atlas/grid/Grid.h" + +namespace atlas { +namespace grid { +namespace detail { +namespace distribution { + +void DistributionFunction::print( std::ostream& s ) const { + auto print_partition = [&]( std::ostream& s ) { + eckit::output_list list_printer( s ); + for ( gidx_t i = 0; i < size_; i++ ) { + list_printer.push_back( partition( i ) ); + } + }; + s << "Distribution( " + << "type: " << type_ << ", nb_points: " << size_ << ", nb_partitions: " << nb_pts_.size() << ", parts : "; + print_partition( s ); +} + +void DistributionFunction::hash( eckit::Hash& hash ) const { + for ( gidx_t i = 0; i < size_; i++ ) { + hash.add( partition( i ) ); + } +} + +} // namespace distribution +} // namespace detail +} // namespace grid +} // namespace atlas diff --git a/src/atlas/grid/detail/distribution/DistributionFunction.h b/src/atlas/grid/detail/distribution/DistributionFunction.h new file mode 100644 index 000000000..9998d32cf --- /dev/null +++ b/src/atlas/grid/detail/distribution/DistributionFunction.h @@ -0,0 +1,64 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#pragma once + +#include +#include + +#include "atlas/grid/detail/distribution/DistributionImpl.h" + + +namespace atlas { +namespace grid { +namespace detail { +namespace distribution { + +class DistributionFunction : public DistributionImpl { +public: + DistributionFunction( const Grid& ) : DistributionImpl() {} + bool functional() const override { return true; } + size_t footprint() const override { return nb_pts_.size() * sizeof( nb_pts_[0] ); } + const std::string& type() const override { return nb_partitions_ == 1 ? serial : type_; } + idx_t nb_partitions() const { return nb_partitions_; } + + virtual const std::vector& nb_pts() const { return nb_pts_; } + + virtual idx_t max_pts() const { return max_pts_; } + virtual idx_t min_pts() const { return min_pts_; } + + virtual void print( std::ostream& ) const; + + virtual gidx_t size() const { return size_; } + + void hash( eckit::Hash& ) const override; + +protected: + gidx_t size_; + idx_t nb_partitions_; + std::vector nb_pts_; + idx_t max_pts_; + idx_t min_pts_; + std::string type_{"functional"}; + std::string serial{"serial"}; +}; + + +template +class DistributionFunctionT : public DistributionFunction { +public: + DistributionFunctionT( const Grid& grid ) : DistributionFunction( grid ) {} + int partition( gidx_t gidx ) const override { return static_cast( this )->function( gidx ); } +}; + +} // namespace distribution +} // namespace detail +} // namespace grid +} // namespace atlas diff --git a/src/atlas/grid/detail/distribution/DistributionImpl.cc b/src/atlas/grid/detail/distribution/DistributionImpl.cc index 3a650fa16..4f93ab8a5 100644 --- a/src/atlas/grid/detail/distribution/DistributionImpl.cc +++ b/src/atlas/grid/detail/distribution/DistributionImpl.cc @@ -8,207 +8,18 @@ * nor does it submit to any jurisdiction. */ +#include "DistributionImpl.h" + #include -#include -#include -#include "DistributionImpl.h" +#include "atlas/grid/detail/distribution/DistributionArray.h" -#include "atlas/grid/Grid.h" -#include "atlas/util/Config.h" -#include "atlas/grid/Partitioner.h" -#include "atlas/parallel/mpi/mpi.h" -#include "atlas/parallel/omp/omp.h" namespace atlas { namespace grid { -namespace { -std::string distribution_type( int N, const Partitioner& p = Partitioner() ) { - if ( N == 1 ) { - return "serial"; - } - if ( not p ) { - return "custom"; - } - return p.type(); -} -} // namespace - -DistributionImpl::DistributionImpl( const Grid & grid, const eckit::Parametrisation & config ) -{ - bool light = false; - - config.get ("light", light); - - if (light) - { - gridsize_ = grid.size (); - nb_partitions_ = atlas::mpi::comm ().size (); - blocksize_ = 1; - - config.get ("blocksize", blocksize_); - - nb_blocks_ = gridsize_ / blocksize_; - - if (gridsize_ % blocksize_) - nb_blocks_++; - - nb_pts_.reserve (nb_partitions_); - - for (idx_t iproc = 0; iproc < nb_partitions_; iproc++) - { - // Approximate values - gidx_t imin = blocksize_ * (((iproc + 0) * nb_blocks_) / nb_partitions_); - gidx_t imax = blocksize_ * (((iproc + 1) * nb_blocks_) / nb_partitions_); - - while (imin > 0) - if (partition (imin-blocksize_) == iproc) - imin -= blocksize_; - else - break; - - while (partition (imin) < iproc) - imin += blocksize_; - - while (partition (imax-1) == iproc + 1) - imax -= blocksize_; - - while (imax + blocksize_ <= gridsize_) - if (partition (imax) == iproc) - imax += blocksize_; - else - break; - - imax = std::min (imax, (gidx_t)gridsize_); - nb_pts_.push_back (imax-imin); - } - - max_pts_ = *std::max_element( nb_pts_.begin(), nb_pts_.end() ); - min_pts_ = *std::min_element( nb_pts_.begin(), nb_pts_.end() ); - } - else - { - Partitioner partitioner (config); - setupWithPartitioner (grid, partitioner); - } - -} - -DistributionImpl::DistributionImpl( const Grid& grid ) : - nb_partitions_( 1 ), - part_( grid.size(), 0 ), - nb_pts_( nb_partitions_, grid.size() ), - max_pts_( grid.size() ), - min_pts_( grid.size() ), - type_( distribution_type( nb_partitions_ ) ) {} - -void DistributionImpl::setupWithPartitioner (const Grid & grid, const Partitioner & partitioner) -{ - part_.resize (grid.size ()); - partitioner.partition( grid, part_.data() ); - nb_partitions_ = partitioner.nb_partitions(); - - // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - // new - size_t size = part_.size(); - int num_threads = atlas_omp_get_max_threads(); - - std::vector > nb_pts_per_thread( num_threads, std::vector( nb_partitions_ ) ); - atlas_omp_parallel { - int thread = atlas_omp_get_thread_num(); - auto& nb_pts = nb_pts_per_thread[thread]; - atlas_omp_for( size_t j = 0; j < size; ++j ) { - int p = part_[j]; - ++nb_pts[p]; - } - } - - nb_pts_.resize( nb_partitions_, 0 ); - for ( int thread = 0; thread < num_threads; ++thread ) { - for ( int p = 0; p < nb_partitions_; ++p ) { - nb_pts_[p] += nb_pts_per_thread[thread][p]; - } - } - - - // ============================================== - // previous - // - // nb_pts_.resize( nb_partitions_, 0 ); - // for ( idx_t j = 0, size = static_cast( part_.size() ); j < size; ++j ) { - // ++nb_pts_[part_[j]]; - // } - // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< - max_pts_ = *std::max_element( nb_pts_.begin(), nb_pts_.end() ); - min_pts_ = *std::min_element( nb_pts_.begin(), nb_pts_.end() ); - type_ = distribution_type( nb_partitions_, partitioner ); -} - -DistributionImpl::DistributionImpl( const Grid& grid, const Partitioner& partitioner ) { - setupWithPartitioner (grid, partitioner); -} - -DistributionImpl::DistributionImpl( int nb_partitions, idx_t npts, int part[], int part0 ) { - part_.assign( part, part + npts ); - if ( nb_partitions == 0 ) { - std::set partset( part_.begin(), part_.end() ); - nb_partitions_ = static_cast( partset.size() ); - } - else { - nb_partitions_ = nb_partitions; - } - nb_pts_.resize( nb_partitions_, 0 ); - for ( idx_t j = 0, size = static_cast( part_.size() ); j < size; ++j ) { - part_[j] -= part0; - ++nb_pts_[part_[j]]; - } - max_pts_ = *std::max_element( nb_pts_.begin(), nb_pts_.end() ); - min_pts_ = *std::min_element( nb_pts_.begin(), nb_pts_.end() ); - type_ = distribution_type( nb_partitions_ ); -} - -DistributionImpl::DistributionImpl( int nb_partitions, partition_t&& part ) : - nb_partitions_( nb_partitions ), part_( std::move( part ) ), nb_pts_( nb_partitions_, 0 ) { - size_t size = part_.size(); - int num_threads = atlas_omp_get_max_threads(); - std::vector > nb_pts_per_thread( num_threads, std::vector( nb_partitions_ ) ); - atlas_omp_parallel { - int thread = atlas_omp_get_thread_num(); - auto& nb_pts = nb_pts_per_thread[thread]; - atlas_omp_for( size_t j = 0; j < size; ++j ) { - int p = part_[j]; - ++nb_pts[p]; - } - } - for ( int thread = 0; thread < num_threads; ++thread ) { - for ( int p = 0; p < nb_partitions_; ++p ) { - nb_pts_[p] += nb_pts_per_thread[thread][p]; - } - } - - max_pts_ = *std::max_element( nb_pts_.begin(), nb_pts_.end() ); - min_pts_ = *std::min_element( nb_pts_.begin(), nb_pts_.end() ); - type_ = distribution_type( nb_partitions_ ); -} - -DistributionImpl::~DistributionImpl() = default; - -void DistributionImpl::print( std::ostream& s ) const { - s << "Distribution( " - << "type: " << type_ << ", nb_points: " << part_.size() << ", nb_partitions: " << nb_pts_.size() << ", parts : ["; - for ( idx_t i = 0, size = static_cast( part_.size() ); i < size; i++ ) { - if ( i != 0 ) { - s << ','; - } - s << part_[i]; - } - s << ']'; -} - - -DistributionImpl* atlas__GridDistribution__new( idx_t npts, int part[], int part0 ) { - return new DistributionImpl( 0, npts, part, part0 ); +DistributionImpl* atlas__GridDistribution__new( idx_t size, int part[], int part0 ) { + return new detail::distribution::DistributionArray( 0, size, part, part0 ); } void atlas__GridDistribution__delete( DistributionImpl* This ) { diff --git a/src/atlas/grid/detail/distribution/DistributionImpl.h b/src/atlas/grid/detail/distribution/DistributionImpl.h index 51541dc40..5f08bb7fe 100644 --- a/src/atlas/grid/detail/distribution/DistributionImpl.h +++ b/src/atlas/grid/detail/distribution/DistributionImpl.h @@ -13,10 +13,9 @@ #include #include -#include "atlas/util/Object.h" -#include "atlas/util/vector.h" -#include "atlas/util/Config.h" #include "atlas/library/config.h" +#include "atlas/util/Config.h" +#include "atlas/util/Object.h" namespace eckit { @@ -37,74 +36,29 @@ namespace grid { class DistributionImpl : public util::Object { public: using Config = atlas::util::Config; - using partition_t = atlas::vector; - - DistributionImpl( const Grid& ); - DistributionImpl( const Grid&, const eckit::Parametrisation & ); - - DistributionImpl( const Grid&, const Partitioner& ); - - DistributionImpl( int nb_partitions, idx_t npts, int partition[], int part0 = 0 ); - - DistributionImpl( int nb_partitions, partition_t&& partition ); - - virtual ~DistributionImpl(); - - int partition( const gidx_t gidx ) const - { - if (part_.size () > 0) - return part_[gidx]; - idx_t iblock = gidx / blocksize_; - return (iblock * nb_partitions_) / nb_blocks_; - } + virtual ~DistributionImpl() {} + virtual int partition( const gidx_t gidx ) const = 0; + virtual bool functional() const = 0; + virtual idx_t nb_partitions() const = 0; + virtual gidx_t size() const = 0; - const partition_t& partition() const { checkPartition (); return part_; } + virtual const std::vector& nb_pts() const = 0; - idx_t nb_partitions() const { return nb_partitions_; } + virtual idx_t max_pts() const = 0; + virtual idx_t min_pts() const = 0; - operator const partition_t&() const { return part_; } + virtual const std::string& type() const = 0; - const int* data() const { checkPartition (); return part_.data(); } + virtual void print( std::ostream& ) const = 0; - const std::vector& nb_pts() const { return nb_pts_; } + virtual size_t footprint() const = 0; - idx_t max_pts() const { return max_pts_; } - idx_t min_pts() const { return min_pts_; } - - const std::string& type() const { return type_; } - - void print( std::ostream& ) const; - - size_t footprint () const - { - return nb_pts_.size () * sizeof (nb_pts_[0]) + part_.size () * sizeof (part_[0]); - } - - -private: - - void setupWithPartitioner (const Grid &, const Partitioner &); - - void checkPartition () const - { - if (part_.size () == 0) - throw_Exception ("partition array of distribution is empty"); - } - // For trivial partitionning - size_t gridsize_ = 0; - idx_t nb_partitions_ = 0; - size_t blocksize_ = 0; - idx_t nb_blocks_ = 0; - - partition_t part_; - std::vector nb_pts_; - idx_t max_pts_; - idx_t min_pts_; - std::string type_; + virtual void hash( eckit::Hash& ) const = 0; }; + extern "C" { -DistributionImpl* atlas__GridDistribution__new( idx_t npts, int part[], int part0 ); +DistributionImpl* atlas__GridDistribution__new( idx_t size, int part[], int part0 ); void atlas__GridDistribution__delete( DistributionImpl* This ); void atlas__GridDistribution__nb_pts( DistributionImpl* This, idx_t nb_pts[] ); idx_t atlas__atlas__GridDistribution__nb_partitions( DistributionImpl* This ); diff --git a/src/atlas/grid/detail/distribution/SerialDistribution.cc b/src/atlas/grid/detail/distribution/SerialDistribution.cc new file mode 100644 index 000000000..f37864005 --- /dev/null +++ b/src/atlas/grid/detail/distribution/SerialDistribution.cc @@ -0,0 +1,35 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include "SerialDistribution.h" + +#include +#include + +#include "atlas/grid/Grid.h" + +namespace atlas { +namespace grid { +namespace detail { +namespace distribution { + +SerialDistribution::SerialDistribution( const Grid& grid ) : DistributionFunctionT( grid ) { + type_ = "serial"; + nb_partitions_ = 1; + size_ = grid.size(); + nb_pts_.resize( nb_partitions_, grid.size() ); + max_pts_ = *std::max_element( nb_pts_.begin(), nb_pts_.end() ); + min_pts_ = *std::min_element( nb_pts_.begin(), nb_pts_.end() ); +} + +} // namespace distribution +} // namespace detail +} // namespace grid +} // namespace atlas diff --git a/src/atlas/grid/detail/distribution/SerialDistribution.h b/src/atlas/grid/detail/distribution/SerialDistribution.h new file mode 100644 index 000000000..7326cd546 --- /dev/null +++ b/src/atlas/grid/detail/distribution/SerialDistribution.h @@ -0,0 +1,31 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#pragma once + + +#include "atlas/grid/detail/distribution/DistributionFunction.h" + +namespace atlas { +namespace grid { +namespace detail { +namespace distribution { + +class SerialDistribution : public DistributionFunctionT { +public: + SerialDistribution( const Grid& grid ); + + int function( gidx_t gidx ) const { return 0; } +}; + +} // namespace distribution +} // namespace detail +} // namespace grid +} // namespace atlas diff --git a/src/atlas/grid/detail/partitioner/BandsPartitioner.cc b/src/atlas/grid/detail/partitioner/BandsPartitioner.cc new file mode 100644 index 000000000..7483f9902 --- /dev/null +++ b/src/atlas/grid/detail/partitioner/BandsPartitioner.cc @@ -0,0 +1,58 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + + +#include "BandsPartitioner.h" + +#include "atlas/grid/StructuredGrid.h" +#include "atlas/grid/detail/distribution/BandsDistribution.h" +#include "atlas/runtime/Exception.h" + +namespace atlas { +namespace grid { +namespace detail { +namespace partitioner { + +size_t BandsPartitioner::blocksize( const atlas::grid::detail::partitioner::Partitioner::Grid& grid ) const { + if ( blocksize_ == BLOCKSIZE_NX ) { + if ( auto regular = RegularGrid( grid ) ) { + return regular.nx(); + } + return 1; + } + ATLAS_ASSERT( blocksize_ > 0 ); + return size_t( blocksize_ ); +} + +BandsPartitioner::BandsPartitioner() : Partitioner(), blocksize_{1} {} + +BandsPartitioner::BandsPartitioner( int N, int blocksize ) : Partitioner( N ), blocksize_( blocksize ) {} + +Distribution BandsPartitioner::partition( const Partitioner::Grid& grid ) const { + return Distribution{new distribution::BandsDistribution{grid, nb_partitions(), type(), blocksize( grid )}}; +} + +void BandsPartitioner::partition( const Partitioner::Grid& grid, int part[] ) const { + distribution::BandsDistribution distribution{grid, nb_partitions(), type(), blocksize( grid )}; + gidx_t gridsize = grid.size(); + for ( gidx_t n = 0; n < gridsize; ++n ) { + part[n] = distribution.function( n ); + } +} + +} // namespace partitioner +} // namespace detail +} // namespace grid +} // namespace atlas + +namespace { +atlas::grid::detail::partitioner::PartitionerBuilder __Bands( + atlas::grid::detail::partitioner::BandsPartitioner::static_type() ); +} diff --git a/src/atlas/grid/detail/partitioner/BandsPartitioner.h b/src/atlas/grid/detail/partitioner/BandsPartitioner.h new file mode 100644 index 000000000..7bbc384ec --- /dev/null +++ b/src/atlas/grid/detail/partitioner/BandsPartitioner.h @@ -0,0 +1,55 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#pragma once + +#include "atlas/grid/detail/partitioner/Partitioner.h" + +#include "atlas/grid/Distribution.h" +#include "atlas/grid/Grid.h" + +namespace atlas { +namespace grid { +namespace detail { +namespace partitioner { + +class BandsPartitioner : public Partitioner { +private: + int blocksize_; + static size_t extract_blocksize( const eckit::Parametrisation& config ) { + size_t blocksize{1}; + config.get( "blocksize", blocksize ); + return blocksize; + } + + size_t blocksize( const Grid& grid ) const; + +protected: + static constexpr int BLOCKSIZE_NX{-1}; + +public: + BandsPartitioner(); + BandsPartitioner( int N ) : BandsPartitioner( N, 1 ) {} + BandsPartitioner( int N, int blocksize ); + BandsPartitioner( int N, const eckit::Parametrisation& config ) : + BandsPartitioner( N, extract_blocksize( config ) ) {} + + std::string type() const override { return static_type(); } + static std::string static_type() { return "bands"; } + + virtual Distribution partition( const Grid& grid ) const override; + + virtual void partition( const Grid& grid, int part[] ) const; +}; + +} // namespace partitioner +} // namespace detail +} // namespace grid +} // namespace atlas diff --git a/src/atlas/grid/detail/partitioner/CheckerboardPartitioner.cc b/src/atlas/grid/detail/partitioner/CheckerboardPartitioner.cc index 6b0ffe07e..31d98bc2e 100644 --- a/src/atlas/grid/detail/partitioner/CheckerboardPartitioner.cc +++ b/src/atlas/grid/detail/partitioner/CheckerboardPartitioner.cc @@ -41,9 +41,9 @@ CheckerboardPartitioner::CheckerboardPartitioner( int N ) : Partitioner( N ) { checkerboard_ = true; // default } -CheckerboardPartitioner::CheckerboardPartitioner( int N, const eckit::Parametrisation & config ) : Partitioner( N ) { - nbands_ = 0; // to be computed later - config.get ("bands", nbands_); +CheckerboardPartitioner::CheckerboardPartitioner( int N, const eckit::Parametrisation& config ) : Partitioner( N ) { + nbands_ = 0; // to be computed later + config.get( "bands", nbands_ ); checkerboard_ = true; // default } diff --git a/src/atlas/grid/detail/partitioner/CheckerboardPartitioner.h b/src/atlas/grid/detail/partitioner/CheckerboardPartitioner.h index ffc50006d..567355602 100644 --- a/src/atlas/grid/detail/partitioner/CheckerboardPartitioner.h +++ b/src/atlas/grid/detail/partitioner/CheckerboardPartitioner.h @@ -24,7 +24,7 @@ class CheckerboardPartitioner : public Partitioner { CheckerboardPartitioner(); CheckerboardPartitioner( int N ); // N is the number of parts (aka MPI tasks) - CheckerboardPartitioner( int N, const eckit::Parametrisation & ); + CheckerboardPartitioner( int N, const eckit::Parametrisation& ); CheckerboardPartitioner( int N, int nbands ); CheckerboardPartitioner( int N, int nbands, bool checkerboard ); diff --git a/src/atlas/grid/detail/partitioner/EqualBandsPartitioner.cc b/src/atlas/grid/detail/partitioner/EqualBandsPartitioner.cc new file mode 100644 index 000000000..bf6784387 --- /dev/null +++ b/src/atlas/grid/detail/partitioner/EqualBandsPartitioner.cc @@ -0,0 +1,31 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + + +#include "EqualBandsPartitioner.h" + +#include "atlas/parallel/mpi/mpi.h" + +namespace atlas { +namespace grid { +namespace detail { +namespace partitioner { + +EqualBandsPartitioner::EqualBandsPartitioner() : EqualBandsPartitioner( mpi::size() ) {} + +} // namespace partitioner +} // namespace detail +} // namespace grid +} // namespace atlas + +namespace { +atlas::grid::detail::partitioner::PartitionerBuilder + __EqualBands( atlas::grid::detail::partitioner::EqualBandsPartitioner::static_type() ); +} diff --git a/src/atlas/grid/detail/partitioner/EqualBandsPartitioner.h b/src/atlas/grid/detail/partitioner/EqualBandsPartitioner.h new file mode 100644 index 000000000..37fdba298 --- /dev/null +++ b/src/atlas/grid/detail/partitioner/EqualBandsPartitioner.h @@ -0,0 +1,34 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#pragma once + +#include + +#include "atlas/grid/detail/partitioner/BandsPartitioner.h" + +namespace atlas { +namespace grid { +namespace detail { +namespace partitioner { + +class EqualBandsPartitioner : public BandsPartitioner { +public: + EqualBandsPartitioner(); + EqualBandsPartitioner( int N, const eckit::Parametrisation& config = util::NoConfig() ) : + BandsPartitioner( N, 1 ) {} + std::string type() const override { return static_type(); } + static std::string static_type() { return "equal_bands"; } +}; + +} // namespace partitioner +} // namespace detail +} // namespace grid +} // namespace atlas diff --git a/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.cc b/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.cc index 916e18c4c..1144b75da 100644 --- a/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.cc +++ b/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.cc @@ -434,8 +434,7 @@ void eq_regions( int N, double xmin[], double xmax[], double ymin[], double ymax ymax[N - 1] = 0.5 * M_PI - s_cap[s_cap.size() - 2]; } -void EqualRegionsPartitioner:: init () -{ +void EqualRegionsPartitioner::init() { std::vector s_cap; eq_caps( N_, sectors_, s_cap ); bands_.resize( s_cap.size() ); @@ -445,15 +444,16 @@ void EqualRegionsPartitioner:: init () } EqualRegionsPartitioner::EqualRegionsPartitioner() : Partitioner(), N_( nb_partitions() ) { - init (); + init(); } EqualRegionsPartitioner::EqualRegionsPartitioner( int N ) : Partitioner( N ), N_( N ) { - init (); + init(); } -EqualRegionsPartitioner::EqualRegionsPartitioner( int N, const eckit::Parametrisation & config ) : Partitioner( N ), N_( N ) { - init (); +EqualRegionsPartitioner::EqualRegionsPartitioner( int N, const eckit::Parametrisation& config ) : + Partitioner( N ), N_( N ) { + init(); } int EqualRegionsPartitioner::partition( const double& x, const double& y ) const { diff --git a/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.h b/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.h index 8970571f9..c95e06579 100644 --- a/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.h +++ b/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.h @@ -80,7 +80,7 @@ class EqualRegionsPartitioner : public Partitioner { EqualRegionsPartitioner(); EqualRegionsPartitioner( int N ); - EqualRegionsPartitioner( int N, const eckit::Parametrisation & config ); + EqualRegionsPartitioner( int N, const eckit::Parametrisation& config ); void where( int partition, int& band, int& sector ) const; int nb_bands() const { return bands_.size(); } @@ -114,7 +114,7 @@ class EqualRegionsPartitioner : public Partitioner { }; private: - void init (); + void init(); // Doesn't matter if nodes[] is in degrees or radians, as a sorting // algorithm is used internally void partition( int nb_nodes, NodeInt nodes[], int part[] ) const; diff --git a/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerBruteForce.h b/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerBruteForce.h index 44c7fea36..cd8cec44e 100644 --- a/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerBruteForce.h +++ b/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerBruteForce.h @@ -24,7 +24,8 @@ class MatchingMeshPartitionerBruteForce : public MatchingMeshPartitioner { public: MatchingMeshPartitionerBruteForce() : MatchingMeshPartitioner() {} MatchingMeshPartitionerBruteForce( const idx_t nb_partitions ) : MatchingMeshPartitioner( nb_partitions ) {} - MatchingMeshPartitionerBruteForce( const idx_t nb_partitions, const eckit::Parametrisation & ) : MatchingMeshPartitioner( nb_partitions ) {} + MatchingMeshPartitionerBruteForce( const idx_t nb_partitions, const eckit::Parametrisation& ) : + MatchingMeshPartitioner( nb_partitions ) {} MatchingMeshPartitionerBruteForce( const Mesh& mesh ) : MatchingMeshPartitioner( mesh ) {} virtual void partition( const Grid& grid, int partitioning[] ) const; diff --git a/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.h b/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.h index 65ce82baa..53e4419b1 100644 --- a/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.h +++ b/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.h @@ -24,7 +24,8 @@ class MatchingMeshPartitionerLonLatPolygon : public MatchingMeshPartitioner { public: MatchingMeshPartitionerLonLatPolygon() : MatchingMeshPartitioner() {} MatchingMeshPartitionerLonLatPolygon( const size_t nb_partitions ) : MatchingMeshPartitioner( nb_partitions ) {} - MatchingMeshPartitionerLonLatPolygon( const size_t nb_partitions, const eckit::Parametrisation & config ) : MatchingMeshPartitioner( nb_partitions ) {} + MatchingMeshPartitionerLonLatPolygon( const size_t nb_partitions, const eckit::Parametrisation& config ) : + MatchingMeshPartitioner( nb_partitions ) {} MatchingMeshPartitionerLonLatPolygon( const Mesh& mesh ) : MatchingMeshPartitioner( mesh ) {} /** diff --git a/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerSphericalPolygon.h b/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerSphericalPolygon.h index 72e2e18e8..275b454ee 100644 --- a/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerSphericalPolygon.h +++ b/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerSphericalPolygon.h @@ -24,7 +24,8 @@ class MatchingMeshPartitionerSphericalPolygon : public MatchingMeshPartitioner { public: MatchingMeshPartitionerSphericalPolygon() : MatchingMeshPartitioner() {} MatchingMeshPartitionerSphericalPolygon( const idx_t nb_partitions ) : MatchingMeshPartitioner( nb_partitions ) {} - MatchingMeshPartitionerSphericalPolygon( const idx_t nb_partitions, const eckit::Parametrisation & config ) : MatchingMeshPartitioner( nb_partitions ) {} + MatchingMeshPartitionerSphericalPolygon( const idx_t nb_partitions, const eckit::Parametrisation& config ) : + MatchingMeshPartitioner( nb_partitions ) {} MatchingMeshPartitionerSphericalPolygon( const Mesh& mesh ) : MatchingMeshPartitioner( mesh ) {} /** diff --git a/src/atlas/grid/detail/partitioner/Partitioner.cc b/src/atlas/grid/detail/partitioner/Partitioner.cc index 24097c30d..6803b96f3 100644 --- a/src/atlas/grid/detail/partitioner/Partitioner.cc +++ b/src/atlas/grid/detail/partitioner/Partitioner.cc @@ -16,9 +16,9 @@ #include "eckit/thread/AutoLock.h" #include "eckit/thread/Mutex.h" -#include "atlas/util/Config.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Partitioner.h" +#include "atlas/grid/detail/distribution/DistributionArray.h" #include "atlas/grid/detail/partitioner/CheckerboardPartitioner.h" #include "atlas/grid/detail/partitioner/EqualRegionsPartitioner.h" #include "atlas/grid/detail/partitioner/MatchingFunctionSpacePartitionerLonLatPolygon.h" @@ -30,6 +30,7 @@ #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" +#include "atlas/util/Config.h" #if ATLAS_HAVE_TRANS #include "atlas/grid/detail/partitioner/TransPartitioner.h" @@ -63,7 +64,7 @@ idx_t Partitioner::nb_partitions() const { } Distribution Partitioner::partition( const Grid& grid ) const { - return Distribution( grid, atlas::grid::Partitioner( this ) ); + return new distribution::DistributionArray{grid, atlas::grid::Partitioner( this )}; } namespace { @@ -150,10 +151,11 @@ Partitioner* PartitionerFactory::build( const std::string& name ) { Partitioner* PartitionerFactory::build( const std::string& name, const idx_t nb_partitions ) { atlas::util::Config config; - return build (name, nb_partitions, config); + return build( name, nb_partitions, config ); } -Partitioner* PartitionerFactory::build( const std::string& name, const idx_t nb_partitions, const eckit::Parametrisation & config ) { +Partitioner* PartitionerFactory::build( const std::string& name, const idx_t nb_partitions, + const eckit::Parametrisation& config ) { pthread_once( &once, init ); eckit::AutoLock lock( local_mutex ); diff --git a/src/atlas/grid/detail/partitioner/Partitioner.h b/src/atlas/grid/detail/partitioner/Partitioner.h index d125e9a46..01fb5674d 100644 --- a/src/atlas/grid/detail/partitioner/Partitioner.h +++ b/src/atlas/grid/detail/partitioner/Partitioner.h @@ -12,9 +12,9 @@ #include -#include "atlas/util/Object.h" -#include "atlas/util/Config.h" #include "atlas/library/config.h" +#include "atlas/util/Config.h" +#include "atlas/util/Object.h" namespace atlas { class Grid; @@ -41,7 +41,7 @@ class Partitioner : public util::Object { virtual void partition( const Grid& grid, int part[] ) const = 0; - Distribution partition( const Grid& grid ) const; + virtual Distribution partition( const Grid& grid ) const; idx_t nb_partitions() const; @@ -64,7 +64,7 @@ class PartitionerFactory { */ static Partitioner* build( const std::string& ); static Partitioner* build( const std::string&, const idx_t nb_partitions ); - static Partitioner* build( const std::string&, const idx_t nb_partitions, const eckit::Parametrisation & ); + static Partitioner* build( const std::string&, const idx_t nb_partitions, const eckit::Parametrisation& ); /*! * \brief list all registered partioner builders @@ -74,9 +74,9 @@ class PartitionerFactory { private: std::string name_; - virtual Partitioner* make() = 0; - virtual Partitioner* make( const idx_t nb_partitions ) = 0; - virtual Partitioner* make( const idx_t nb_partitions, const eckit::Parametrisation & ) = 0; + virtual Partitioner* make() = 0; + virtual Partitioner* make( const idx_t nb_partitions ) = 0; + virtual Partitioner* make( const idx_t nb_partitions, const eckit::Parametrisation& ) = 0; protected: PartitionerFactory( const std::string& ); @@ -90,7 +90,9 @@ class PartitionerBuilder : public PartitionerFactory { virtual Partitioner* make() { return new T(); } virtual Partitioner* make( const idx_t nb_partitions ) { return new T( nb_partitions ); } - virtual Partitioner* make( const idx_t nb_partitions, const eckit::Parametrisation & config ) { return new T( nb_partitions, config ); } + virtual Partitioner* make( const idx_t nb_partitions, const eckit::Parametrisation& config ) { + return new T( nb_partitions, config ); + } public: PartitionerBuilder( const std::string& name ) : PartitionerFactory( name ) {} diff --git a/src/atlas/grid/detail/partitioner/RegularBandsPartitioner.cc b/src/atlas/grid/detail/partitioner/RegularBandsPartitioner.cc new file mode 100644 index 000000000..562178b95 --- /dev/null +++ b/src/atlas/grid/detail/partitioner/RegularBandsPartitioner.cc @@ -0,0 +1,31 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + + +#include "RegularBandsPartitioner.h" + +#include "atlas/parallel/mpi/mpi.h" + +namespace atlas { +namespace grid { +namespace detail { +namespace partitioner { + +RegularBandsPartitioner::RegularBandsPartitioner() : RegularBandsPartitioner( mpi::size() ) {} + +} // namespace partitioner +} // namespace detail +} // namespace grid +} // namespace atlas + +namespace { +atlas::grid::detail::partitioner::PartitionerBuilder + __RegularBands( atlas::grid::detail::partitioner::RegularBandsPartitioner::static_type() ); +} diff --git a/src/atlas/grid/detail/partitioner/RegularBandsPartitioner.h b/src/atlas/grid/detail/partitioner/RegularBandsPartitioner.h new file mode 100644 index 000000000..82bb48b21 --- /dev/null +++ b/src/atlas/grid/detail/partitioner/RegularBandsPartitioner.h @@ -0,0 +1,33 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#pragma once + +#include "atlas/grid/detail/partitioner/BandsPartitioner.h" + +namespace atlas { +namespace grid { +namespace detail { +namespace partitioner { + + +class RegularBandsPartitioner : public BandsPartitioner { +public: + RegularBandsPartitioner(); + RegularBandsPartitioner( int N, const eckit::Parametrisation& config = util::NoConfig() ) : + BandsPartitioner( N, BLOCKSIZE_NX ) {} + std::string type() const override { return static_type(); } + static std::string static_type() { return "regular_bands"; } +}; + +} // namespace partitioner +} // namespace detail +} // namespace grid +} // namespace atlas diff --git a/src/atlas/grid/detail/partitioner/SerialPartitioner.cc b/src/atlas/grid/detail/partitioner/SerialPartitioner.cc new file mode 100644 index 000000000..51c8a1fff --- /dev/null +++ b/src/atlas/grid/detail/partitioner/SerialPartitioner.cc @@ -0,0 +1,17 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + + +#include "SerialPartitioner.h" + +namespace { +atlas::grid::detail::partitioner::PartitionerBuilder __Serial( + atlas::grid::detail::partitioner::SerialPartitioner::static_type() ); +} diff --git a/src/atlas/grid/detail/partitioner/SerialPartitioner.h b/src/atlas/grid/detail/partitioner/SerialPartitioner.h new file mode 100644 index 000000000..9b999af62 --- /dev/null +++ b/src/atlas/grid/detail/partitioner/SerialPartitioner.h @@ -0,0 +1,49 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#pragma once + +#include "atlas/grid/Distribution.h" +#include "atlas/grid/Grid.h" +#include "atlas/grid/detail/distribution/SerialDistribution.h" +#include "atlas/grid/detail/partitioner/Partitioner.h" + +namespace atlas { +namespace grid { +namespace detail { +namespace partitioner { + +class SerialPartitioner : public Partitioner { +public: + SerialPartitioner() : Partitioner( 1 ) {} + SerialPartitioner( int N, const eckit::Parametrisation& config ) : Partitioner( 1 ) {} + + SerialPartitioner( int N ) : Partitioner( N ) {} + + std::string type() const override { return static_type(); } + static std::string static_type() { return "serial"; } + + virtual Distribution partition( const Grid& grid ) const override { + return Distribution{new distribution::SerialDistribution{grid}}; + } + + virtual void partition( const Grid& grid, int part[] ) const { + gidx_t gridsize = grid.size(); + for ( gidx_t n = 0; n < gridsize; ++n ) { + part[n] = 0.; + } + } +}; + + +} // namespace partitioner +} // namespace detail +} // namespace grid +} // namespace atlas diff --git a/src/atlas/meshgenerator/detail/RegularMeshGenerator.cc b/src/atlas/meshgenerator/detail/RegularMeshGenerator.cc index bb7c31cec..cbfda74ce 100644 --- a/src/atlas/meshgenerator/detail/RegularMeshGenerator.cc +++ b/src/atlas/meshgenerator/detail/RegularMeshGenerator.cc @@ -144,12 +144,12 @@ void RegularMeshGenerator::generate( const Grid& grid, const grid::Distribution& ATLAS_ASSERT( !mesh.generated() ); - if ( grid.size() != static_cast( distribution.partition().size() ) ) { + if ( grid.size() != static_cast( distribution.size() ) ) { std::stringstream msg; msg << "Number of points in grid (" << grid.size() << ") different from " "number of points in grid distribution (" - << distribution.partition().size() << ")"; + << distribution.size() << ")"; throw_AssertionFailed( msg.str(), Here() ); } @@ -159,7 +159,7 @@ void RegularMeshGenerator::generate( const Grid& grid, const grid::Distribution& generate_mesh( rg, distribution, mesh ); } -void RegularMeshGenerator::generate_mesh( const RegularGrid& rg, const grid::Distribution::partition_t& parts, +void RegularMeshGenerator::generate_mesh( const RegularGrid& rg, const grid::Distribution& distribution, // const Region& region, Mesh& mesh ) const { int mypart = options.get( "part" ); @@ -230,10 +230,10 @@ void RegularMeshGenerator::generate_mesh( const RegularGrid& rg, const grid::Dis ii_glb = 0; for ( iy = 0; iy < ny; iy++ ) { for ( ix = 0; ix < nx; ix++ ) { - local_idx[ii_glb] = current_idx[parts[ii_glb]]++; // store local index on - // the local proc of - // this point - if ( parts[ii_glb] == mypart ) { + local_idx[ii_glb] = current_idx[distribution.partition( ii_glb )]++; // store local index on + // the local proc of + // this point + if ( distribution.partition( ii_glb ) == mypart ) { ++nnodes_nonghost; // non-ghost node: belongs to this part ix_min = std::min( ix_min, ix ); ix_max = std::max( ix_max, ix ); @@ -272,25 +272,25 @@ void RegularMeshGenerator::generate_mesh( const RegularGrid& rg, const grid::Dis is_ghost_SR[ii] = !( ( parts_SR[ii] == mypart ) && ix < nxl - 1 && iy < nyl - 1 ); if ( ix_glb < nx && iy_glb < ny ) { ii_glb = (iy_glb)*nx + ix_glb; // global index - parts_SR[ii] = parts[ii_glb]; + parts_SR[ii] = distribution.partition( ii_glb ); local_idx_SR[ii] = local_idx[ii_glb]; is_ghost_SR[ii] = !( ( parts_SR[ii] == mypart ) && ix < nxl - 1 && iy < nyl - 1 ); } else if ( ix_glb == nx && iy_glb < ny ) { // take properties from the point to the left - parts_SR[ii] = parts[iy_glb * nx + ix_glb - 1]; + parts_SR[ii] = distribution.partition( iy_glb * nx + ix_glb - 1 ); local_idx_SR[ii] = -1; is_ghost_SR[ii] = true; } else if ( iy_glb == ny && ix_glb < nx ) { // take properties from the point below - parts_SR[ii] = parts[( iy_glb - 1 ) * nx + ix_glb]; + parts_SR[ii] = distribution.partition( ( iy_glb - 1 ) * nx + ix_glb ); local_idx_SR[ii] = -1; is_ghost_SR[ii] = true; } else { // take properties from the point belowleft - parts_SR[ii] = parts[( iy_glb - 1 ) * nx + ix_glb - 1]; + parts_SR[ii] = distribution.partition( ( iy_glb - 1 ) * nx + ix_glb - 1 ); local_idx_SR[ii] = -1; is_ghost_SR[ii] = true; } diff --git a/src/atlas/meshgenerator/detail/RegularMeshGenerator.h b/src/atlas/meshgenerator/detail/RegularMeshGenerator.h index 11917cbbd..4d340f297 100644 --- a/src/atlas/meshgenerator/detail/RegularMeshGenerator.h +++ b/src/atlas/meshgenerator/detail/RegularMeshGenerator.h @@ -54,7 +54,7 @@ class RegularMeshGenerator : public MeshGenerator::Implementation { void configure_defaults(); - void generate_mesh( const RegularGrid&, const atlas::vector& parts, Mesh& m ) const; + void generate_mesh( const RegularGrid&, const grid::Distribution& distribution, Mesh& m ) const; private: util::Metadata options; diff --git a/src/atlas/meshgenerator/detail/StructuredMeshGenerator.cc b/src/atlas/meshgenerator/detail/StructuredMeshGenerator.cc index 594a371f3..d5055cfcc 100644 --- a/src/atlas/meshgenerator/detail/StructuredMeshGenerator.cc +++ b/src/atlas/meshgenerator/detail/StructuredMeshGenerator.cc @@ -197,11 +197,12 @@ void StructuredMeshGenerator::generate( const Grid& grid, Mesh& mesh ) const { partitioner_type = "equal_regions"; // Odd number of latitudes } if ( nb_parts == 1 || mpi::size() == 1 ) { - partitioner_type = "equal_regions"; // Only one part --> Trans is slower + partitioner_type = "equal_regions"; } grid::Partitioner partitioner( partitioner_type, nb_parts ); grid::Distribution distribution( partitioner.partition( grid ) ); + ATLAS_DEBUG_VAR( distribution ); generate( grid, distribution, mesh ); } @@ -220,12 +221,12 @@ void StructuredMeshGenerator::generate( const Grid& grid, const grid::Distributi ATLAS_ASSERT( !mesh.generated() ); - if ( grid.size() != idx_t( distribution.partition().size() ) ) { + if ( grid.size() != idx_t( distribution.size() ) ) { std::stringstream msg; msg << "Number of points in grid (" << grid.size() << ") different from " "number of points in grid distribution (" - << distribution.partition().size() << ")"; + << distribution.size() << ")"; throw_AssertionFailed( msg.str(), Here() ); } @@ -254,8 +255,8 @@ void StructuredMeshGenerator::generate( const Grid& grid, const grid::Distributi generate_mesh( rg, distribution, region, mesh ); } -void StructuredMeshGenerator::generate_region( const StructuredGrid& rg, const atlas::vector& parts, int mypart, - Region& region ) const { +void StructuredMeshGenerator::generate_region( const StructuredGrid& rg, const grid::Distribution& distribution, + int mypart, Region& region ) const { ATLAS_TRACE(); double max_angle = options.getDouble( "angle" ); @@ -274,7 +275,7 @@ Find min and max latitudes used by this part. idx_t lat_north = -1; for ( idx_t jlat = 0; jlat < rg.ny(); ++jlat ) { for ( idx_t jlon = 0; jlon < rg.nx( jlat ); ++jlon ) { - if ( parts.at( n ) == mypart ) { + if ( distribution.partition( n ) == mypart ) { lat_north = jlat; goto end_north; } @@ -287,7 +288,7 @@ Find min and max latitudes used by this part. idx_t lat_south = -1; for ( idx_t jlat = rg.ny() - 1; jlat >= 0; --jlat ) { for ( idx_t jlon = rg.nx( jlat ) - 1; jlon >= 0; --jlon ) { - if ( parts.at( n ) == mypart ) { + if ( distribution.partition( n ) == mypart ) { lat_south = jlat; goto end_south; } @@ -370,7 +371,7 @@ We need to connect to next region ipS2 = std::min( ipS1 + 1, endS ); idx_t jelem = 0; - int pE = parts.at( offset.at( latN ) ); + int pE = distribution.partition( offset.at( latN ) ); #if DEBUG_OUTPUT Log::info() << "=================\n"; @@ -391,29 +392,29 @@ We need to connect to next region int pN1, pS1, pN2, pS2; if ( ipN1 != rg.nx( latN ) ) { - pN1 = parts.at( offset.at( latN ) + ipN1 ); + pN1 = distribution.partition( offset.at( latN ) + ipN1 ); } else { - pN1 = parts.at( offset.at( latN ) ); + pN1 = distribution.partition( offset.at( latN ) ); } if ( ipS1 != rg.nx( latS ) ) { - pS1 = parts.at( offset.at( latS ) + ipS1 ); + pS1 = distribution.partition( offset.at( latS ) + ipS1 ); } else { - pS1 = parts.at( offset.at( latS ) ); + pS1 = distribution.partition( offset.at( latS ) ); } if ( ipN2 == rg.nx( latN ) ) { - pN2 = parts.at( offset.at( latN ) ); + pN2 = distribution.partition( offset.at( latN ) ); } else { - pN2 = parts.at( offset.at( latN ) + ipN2 ); + pN2 = distribution.partition( offset.at( latN ) + ipN2 ); } if ( ipS2 == rg.nx( latS ) ) { - pS2 = parts.at( offset.at( latS ) ); + pS2 = distribution.partition( offset.at( latS ) ); } else { - pS2 = parts.at( offset.at( latS ) + ipS2 ); + pS2 = distribution.partition( offset.at( latS ) + ipS2 ); } // Log::info() << ipN1 << "("<& parts, +void StructuredMeshGenerator::generate_mesh( const StructuredGrid& rg, const grid::Distribution& distribution, const Region& region, Mesh& mesh ) const { ATLAS_TRACE(); @@ -938,7 +939,7 @@ void StructuredMeshGenerator::generate_mesh( const StructuredGrid& rg, const atl for ( idx_t jlon = region.lat_begin.at( jlat ); jlon <= region.lat_end.at( jlat ); ++jlon ) { if ( jlon < rg.nx( jlat ) ) { n = offset_glb.at( jlat ) + jlon; - if ( parts.at( n ) == mypart ) { + if ( distribution.partition( n ) == mypart ) { node_numbering.at( jnode ) = node_number; ++node_number; } @@ -1016,7 +1017,7 @@ void StructuredMeshGenerator::generate_mesh( const StructuredGrid& rg, const atl lonlat( inode, LAT ) = crd[LAT]; glb_idx( inode ) = n + 1; - part( inode ) = parts.at( n ); + part( inode ) = distribution.partition( n ); ghost( inode ) = 0; halo( inode ) = 0; Topology::reset( flags( inode ) ); diff --git a/src/atlas/meshgenerator/detail/StructuredMeshGenerator.h b/src/atlas/meshgenerator/detail/StructuredMeshGenerator.h index b07db702c..1672180a3 100644 --- a/src/atlas/meshgenerator/detail/StructuredMeshGenerator.h +++ b/src/atlas/meshgenerator/detail/StructuredMeshGenerator.h @@ -55,12 +55,14 @@ class StructuredMeshGenerator : public MeshGenerator::Implementation { void configure_defaults(); - void generate_region( const StructuredGrid&, const atlas::vector& parts, int mypart, Region& region ) const; + void generate_region( const StructuredGrid&, const grid::Distribution& distribution, int mypart, + Region& region ) const; - void generate_mesh_new( const StructuredGrid&, const atlas::vector& parts, const Region& region, + void generate_mesh_new( const StructuredGrid&, const grid::Distribution& distribution, const Region& region, Mesh& m ) const; - void generate_mesh( const StructuredGrid&, const atlas::vector& parts, const Region& region, Mesh& m ) const; + void generate_mesh( const StructuredGrid&, const grid::Distribution& distribution, const Region& region, + Mesh& m ) const; private: util::Config options; diff --git a/src/tests/functionspace/test_structuredcolumns_haloexchange.cc b/src/tests/functionspace/test_structuredcolumns_haloexchange.cc index 19c638bb9..8a2862d45 100644 --- a/src/tests/functionspace/test_structuredcolumns_haloexchange.cc +++ b/src/tests/functionspace/test_structuredcolumns_haloexchange.cc @@ -89,6 +89,36 @@ CASE( "Two haloexchanges for StructuredColumns differing only by 'periodic_point EXPECT_NO_THROW( fs2.haloExchange( ij2 ) ); } +//----------------------------------------------------------------------------- + +CASE( "Two haloexchanges for StructuredColumns differing only by 'distribution'" ) { + Grid grid( "L400x201" ); + + grid::Distribution dist1( grid, grid::Partitioner( "checkerboard" ) ); + grid::Distribution dist2( grid, grid::Partitioner( "regular_bands" ) ); + + functionspace::StructuredColumns fs1( grid, dist1, Config( "halo", 1 ) ); + functionspace::StructuredColumns fs2( grid, dist2, Config( "halo", 1 ) ); + + if ( mpi::size() == 1 ) { + EXPECT_EQ( fs1.sizeOwned(), 80400 ); + EXPECT_EQ( fs2.sizeOwned(), 80400 ); + EXPECT_EQ( fs1.sizeHalo(), 81606 ); + EXPECT_EQ( fs2.sizeHalo(), 81606 ); + } + if ( mpi::size() == 4 ) { + EXPECT_EQ( fs1.sizeOwned(), 20100 ); + EXPECT_EQ( fs2.sizeOwned(), ( std::vector{20400, 20000, 20000, 20000}[mpi::rank()] ) ); + EXPECT_EQ( fs1.sizeHalo(), ( std::vector{20706, 20706, 20706, 20706}[mpi::rank()] ) ); + EXPECT_EQ( fs2.sizeHalo(), ( std::vector{21306, 20904, 20904, 20904}[mpi::rank()] ) ); + } + + auto ij1 = getIJ( fs1 ); + auto ij2 = getIJ( fs2 ); + + EXPECT_NO_THROW( fs1.haloExchange( ij1 ) ); + EXPECT_NO_THROW( fs2.haloExchange( ij2 ) ); +} //----------------------------------------------------------------------------- diff --git a/src/tests/grid/CMakeLists.txt b/src/tests/grid/CMakeLists.txt index 110e0de70..8fe0697c0 100644 --- a/src/tests/grid/CMakeLists.txt +++ b/src/tests/grid/CMakeLists.txt @@ -31,13 +31,27 @@ foreach(test test_spacing test_state test_largegrid - test_distribution + test_grid_hash) ecbuild_add_test( TARGET atlas_${test} SOURCES ${test}.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endforeach() +set( _WITH_MPI ) +if( eckit_HAVE_MPI ) + set( _WITH_MPI MPI 4 ) +endif() + +ecbuild_add_test( TARGET atlas_test_distribution_regular_bands + ${_WITH_MPI} + SOURCES test_distribution_regular_bands.cc + LIBS atlas + ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} +) + + + file( GLOB grids ${PROJECT_SOURCE_DIR}/doc/example-grids/*.yml ) if( NOT HAVE_PROJ ) ecbuild_list_exclude_pattern( diff --git a/src/tests/grid/test_distribution.cc b/src/tests/grid/test_distribution.cc deleted file mode 100644 index 056d7f186..000000000 --- a/src/tests/grid/test_distribution.cc +++ /dev/null @@ -1,138 +0,0 @@ -/* - * (C) Copyright 2013 ECMWF. - * - * This software is licensed under the terms of the Apache Licence Version 2.0 - * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. - * In applying this licence, ECMWF does not waive the privileges and immunities - * granted to it by virtue of its status as an intergovernmental organisation - * nor does it submit to any jurisdiction. - */ - -#include -#include -#include - -#include "atlas/grid.h" -#include "atlas/parallel/mpi/mpi.h" -#include "atlas/util/Config.h" -#include "atlas/array.h" -#include "atlas/field.h" -#include "atlas/functionspace.h" - -#include "tests/AtlasTestEnvironment.h" - -using Grid = atlas::Grid; -using Config = atlas::util::Config; - -namespace atlas { -namespace test { - -atlas::FieldSet -getIJ (const atlas::functionspace::StructuredColumns & fs) -{ - atlas::FieldSet ij; - - auto i = atlas::array::make_view (fs.index_i ()); - auto j = atlas::array::make_view (fs.index_j ()); - - ij.add (fs.index_i ()); - ij.add (fs.index_j ()); - - return ij; -} - - -//----------------------------------------------------------------------------- - -CASE( "test_nbands" ) -{ - auto & comm = atlas::mpi::comm (); - int nproc = comm.size (); - - StructuredGrid grid = Grid ("L200x100"); - atlas::grid::Distribution dist (grid, atlas::util::Config ("type", "checkerboard") | Config ("bands", nproc)); - - for (int i = 1; i < grid.size (); i++) - EXPECT (dist.partition (i-1) <= dist.partition (i)); -} - -CASE( "test_light" ) -{ - auto & comm = atlas::mpi::comm (); - int nproc = comm.size (); - - const int nx = 400, ny = 200; - - StructuredGrid grid = Grid (std::string ("L") + std::to_string (nx) + "x" + std::to_string (ny)); - atlas::grid::Distribution dist1 (grid, atlas::util::Config ("type", "checkerboard") | Config ("bands", nproc)); - - atlas::grid::Distribution dist2 (grid, Config ("light", true) | Config ("blocksize", nx)); - - EXPECT (dist2.footprint () < 100); - - const auto & nb_pts2 = dist2.nb_pts (); - - int count = 0; - for (int i = 0; i < grid.size (); i++) - if (dist2.partition (i) == comm.rank ()) - count++; - - EXPECT (count == nb_pts2[comm.rank ()]); - - bool same = (ny % nproc) == 0; // Compare light & regular distributions when possible - - if (same) - for (int i = 0; i < grid.size (); i++) - EXPECT (dist1.partition (i) == dist2.partition (i)); - - for (int iy = 0, jglo = 0; iy < ny; iy++) - { - int jglo0 = jglo; - for (int ix = 0; ix < nx; ix++, jglo++) - EXPECT (dist2.partition (jglo) == dist2.partition (jglo0)); - } - - - atlas::functionspace::StructuredColumns fs1 (grid, dist1, atlas::util::Config ("halo", 1) | Config ("periodic_points", true)); - atlas::functionspace::StructuredColumns fs2 (grid, dist2, atlas::util::Config ("halo", 1) | Config ("periodic_points", true)); - - auto ij1 = getIJ (fs1); - auto ij2 = getIJ (fs2); - - fs1.haloExchange (ij1); - fs2.haloExchange (ij2); - - if (same) - { - EXPECT (fs1.size () == fs1.size ()); - EXPECT (fs1.sizeOwned () == fs1.sizeOwned ()); - - auto i1 = atlas::array::make_view (ij1[0]); - auto j1 = atlas::array::make_view (ij1[1]); - auto i2 = atlas::array::make_view (ij2[0]); - auto j2 = atlas::array::make_view (ij2[1]); - - for (int k = 0; k < fs1.sizeOwned (); k++) - { - EXPECT (i1[k] == i2[k]); - EXPECT (j1[k] == j2[k]); - } - } - - - for (int j = fs2.j_begin_halo (); j < fs2.j_end_halo (); j++) - { - EXPECT (fs2.i_begin_halo (j) == -1); - EXPECT (fs2.i_end_halo (j) == nx + 2); - } - -} - -//----------------------------------------------------------------------------- - -} // namespace test -} // namespace atlas - -int main( int argc, char** argv ) { - return atlas::test::run( argc, argv ); -} diff --git a/src/tests/grid/test_distribution_regular_bands.cc b/src/tests/grid/test_distribution_regular_bands.cc new file mode 100644 index 000000000..d81189e6a --- /dev/null +++ b/src/tests/grid/test_distribution_regular_bands.cc @@ -0,0 +1,145 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include +#include +#include + +#include "atlas/array.h" +#include "atlas/field.h" +#include "atlas/functionspace.h" +#include "atlas/grid.h" +#include "atlas/parallel/mpi/mpi.h" +#include "atlas/util/Config.h" + +#include "tests/AtlasTestEnvironment.h" + +using Grid = atlas::Grid; +using Config = atlas::util::Config; + +namespace atlas { +namespace test { + +atlas::FieldSet getIJ( const atlas::functionspace::StructuredColumns& fs ) { + atlas::FieldSet ij; + + // auto i = atlas::array::make_view( fs.index_i() ); + // auto j = atlas::array::make_view( fs.index_j() ); + + ij.add( fs.index_i() ); + ij.add( fs.index_j() ); + + return ij; +} + + +//----------------------------------------------------------------------------- + +CASE( "test_bands" ) { + int nproc = mpi::size(); + + StructuredGrid grid = Grid( "L200x101" ); + grid::Distribution dist( grid, atlas::util::Config( "type", "checkerboard" ) | Config( "bands", nproc ) ); + + for ( int i = 1; i < grid.size(); i++ ) { + EXPECT( dist.partition( i - 1 ) <= dist.partition( i ) ); + } +} + +CASE( "test_regular_bands" ) { + int nproc = mpi::size(); + + std::vector gridnames = {"L40x21", "L40x20", "Slat100x50"}; + + for ( auto gridname : gridnames ) { + SECTION( gridname ) { + auto grid = RegularGrid( gridname ); + const int nx = grid.nx(); + const int ny = grid.ny(); + + bool equivalent_with_checkerboard = + ( ny % nproc ) == 0; // Compare regular band & checkerboard distributions when possible + + grid::Distribution checkerboard( grid, grid::Partitioner( "checkerboard", Config( "bands", nproc ) ) ); + grid::Distribution regularbands( grid, grid::Partitioner( "regular_bands" ) ); + + EXPECT( regularbands.footprint() < 100 ); + + const auto& nb_pts2 = regularbands.nb_pts(); + + int count = 0; + for ( int i = 0; i < grid.size(); i++ ) { + if ( regularbands.partition( i ) == mpi::rank() ) { + count++; + } + } + + EXPECT_EQ( count, nb_pts2[mpi::rank()] ); + + + if ( equivalent_with_checkerboard ) { + for ( int i = 0; i < grid.size(); i++ ) { + EXPECT_EQ( checkerboard.partition( i ), regularbands.partition( i ) ); + } + } + else { + Log::warning() << "WARNING: checkerboard not expected to be equal!" << std::endl; + } + + // Expect each points with constant latitude to be on the same partition as the first on each latitude. + for ( int iy = 0, jglo = 0; iy < ny; iy++ ) { + int jglo0 = jglo; + for ( int ix = 0; ix < nx; ix++, jglo++ ) { + EXPECT_EQ( regularbands.partition( jglo ), regularbands.partition( jglo0 ) ); + } + } + + functionspace::StructuredColumns fs_checkerboard( grid, checkerboard, + Config( "halo", 1 ) | Config( "periodic_points", true ) ); + functionspace::StructuredColumns fs_regularbands( grid, regularbands, + Config( "halo", 1 ) | Config( "periodic_points", true ) ); + + auto ij1 = getIJ( fs_checkerboard ); + auto ij2 = getIJ( fs_regularbands ); + + fs_checkerboard.haloExchange( ij1 ); + fs_regularbands.haloExchange( ij2 ); + + if ( equivalent_with_checkerboard ) { + EXPECT_EQ( fs_regularbands.size(), fs_checkerboard.size() ); + EXPECT_EQ( fs_regularbands.sizeOwned(), fs_checkerboard.sizeOwned() ); + + auto i1 = array::make_view( ij1[0] ); + auto j1 = array::make_view( ij1[1] ); + auto i2 = array::make_view( ij2[0] ); + auto j2 = array::make_view( ij2[1] ); + + for ( int k = 0; k < fs_checkerboard.sizeOwned(); k++ ) { + EXPECT_EQ( i1[k], i2[k] ); + EXPECT_EQ( j1[k], j2[k] ); + } + } + + for ( int j = fs_regularbands.j_begin_halo(); j < fs_regularbands.j_end_halo(); j++ ) { + EXPECT_EQ( fs_regularbands.i_begin_halo( j ), -1 ); + EXPECT_EQ( fs_regularbands.i_end_halo( j ), nx + 2 ); + } + } + } +} + +//----------------------------------------------------------------------------- + +} // namespace test +} // namespace atlas + +int main( int argc, char** argv ) { + return atlas::test::run( argc, argv ); +} diff --git a/src/tests/trans/test_trans.cc b/src/tests/trans/test_trans.cc index f1130b425..95df2850c 100644 --- a/src/tests/trans/test_trans.cc +++ b/src/tests/trans/test_trans.cc @@ -137,7 +137,7 @@ CASE( "test_trans_distribution_matches_atlas" ) { EXPECT( t->n_regions_EW == max_nb_regions_EW ); EXPECT( distribution.nb_partitions() == idx_t( mpi::comm().size() ) ); - EXPECT( idx_t( distribution.partition().size() ) == g.size() ); + EXPECT( idx_t( distribution.size() ) == g.size() ); std::vector npts( distribution.nb_partitions(), 0 ); From a7821c06efd4d799ba674bcbb85df3fac934ed96 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 12 Jun 2020 19:17:22 +0100 Subject: [PATCH 125/145] ATLAS-299 (Github #42) Add capability to Distribution to copy a range between indices --- src/atlas/grid/Distribution.h | 7 ++++++- src/atlas/grid/detail/distribution/DistributionArray.h | 7 +++++++ .../grid/detail/distribution/DistributionFunction.h | 9 ++++++++- src/atlas/grid/detail/distribution/DistributionImpl.h | 2 ++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/atlas/grid/Distribution.h b/src/atlas/grid/Distribution.h index 9287c24c8..1a0886e6a 100644 --- a/src/atlas/grid/Distribution.h +++ b/src/atlas/grid/Distribution.h @@ -55,7 +55,12 @@ class Distribution : DOXYGEN_HIDE( public util::ObjectHandle ) ~Distribution(); - int partition( const gidx_t gidx ) const { return get()->partition( gidx ); } + int partition( gidx_t index ) const { return get()->partition( index ); } + + template + void partition( gidx_t begin, gidx_t end, PartitionContainer& partitions ) const { + return get()->partition( begin, end, partitions.data() ); + } size_t footprint() const { return get()->footprint(); } diff --git a/src/atlas/grid/detail/distribution/DistributionArray.h b/src/atlas/grid/detail/distribution/DistributionArray.h index 942db6bbe..d37026aac 100644 --- a/src/atlas/grid/detail/distribution/DistributionArray.h +++ b/src/atlas/grid/detail/distribution/DistributionArray.h @@ -68,6 +68,13 @@ class DistributionArray : public DistributionImpl { void hash( eckit::Hash& ) const override; + void partition( gidx_t begin, gidx_t end, int partitions[] ) const override { + size_t i = 0; + for ( gidx_t n = begin; n < end; ++n, ++i ) { + partitions[i] = part_[n]; + } + } + protected: idx_t nb_partitions_ = 0; diff --git a/src/atlas/grid/detail/distribution/DistributionFunction.h b/src/atlas/grid/detail/distribution/DistributionFunction.h index 9998d32cf..3bf367fa4 100644 --- a/src/atlas/grid/detail/distribution/DistributionFunction.h +++ b/src/atlas/grid/detail/distribution/DistributionFunction.h @@ -55,7 +55,14 @@ template class DistributionFunctionT : public DistributionFunction { public: DistributionFunctionT( const Grid& grid ) : DistributionFunction( grid ) {} - int partition( gidx_t gidx ) const override { return static_cast( this )->function( gidx ); } + int partition( gidx_t index ) const override { return static_cast( this )->function( index ); } + + void partition( gidx_t begin, gidx_t end, int partitions[] ) const override { + size_t i = 0; + for ( gidx_t n = begin; n < end; ++n, ++i ) { + partitions[i] = static_cast( this )->function( n ); + } + } }; } // namespace distribution diff --git a/src/atlas/grid/detail/distribution/DistributionImpl.h b/src/atlas/grid/detail/distribution/DistributionImpl.h index 5f08bb7fe..9502d7d64 100644 --- a/src/atlas/grid/detail/distribution/DistributionImpl.h +++ b/src/atlas/grid/detail/distribution/DistributionImpl.h @@ -54,6 +54,8 @@ class DistributionImpl : public util::Object { virtual size_t footprint() const = 0; virtual void hash( eckit::Hash& ) const = 0; + + virtual void partition( gidx_t begin, gidx_t end, int partitions[] ) const = 0; }; From af652b102b687e54cf857004a8cde55658780eee Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 12 Jun 2020 19:20:26 +0100 Subject: [PATCH 126/145] ATLAS-299 (Github #42) Factor 2 speedup in BandsDistribution by using int32_t for its arithmetics if we are sure no overflow will occur. This helps L40000x20000 with 'regular_bands' partitioner --- .../detail/distribution/BandsDistribution.cc | 52 ++++++++++++----- .../detail/distribution/BandsDistribution.h | 17 +++--- .../detail/partitioner/BandsPartitioner.cc | 27 +++++++-- .../grid/test_distribution_regular_bands.cc | 58 +++++++++++++++++++ 4 files changed, 128 insertions(+), 26 deletions(-) diff --git a/src/atlas/grid/detail/distribution/BandsDistribution.cc b/src/atlas/grid/detail/distribution/BandsDistribution.cc index 7166fd6c8..b531bd520 100644 --- a/src/atlas/grid/detail/distribution/BandsDistribution.cc +++ b/src/atlas/grid/detail/distribution/BandsDistribution.cc @@ -11,34 +11,38 @@ #include "BandsDistribution.h" #include +#include #include "atlas/grid/Grid.h" +#include "atlas/runtime/Exception.h" namespace atlas { namespace grid { namespace detail { namespace distribution { -BandsDistribution::BandsDistribution( const atlas::Grid& grid, atlas::idx_t nb_partitions, const std::string& type, - size_t blocksize ) : - DistributionFunctionT( grid ) { - type_ = type; - size_t gridsize = grid.size(); - size_ = gridsize; - nb_partitions_ = nb_partitions; - blocksize_ = blocksize; +template +BandsDistribution::BandsDistribution( const atlas::Grid& grid, atlas::idx_t nb_partitions, const std::string& type, + size_t blocksize ) : + DistributionFunctionT>( grid ) { + this->type_ = type; + size_t gridsize = grid.size(); + this->size_ = gridsize; + this->nb_partitions_ = nb_partitions; + nb_partitions_Int_ = nb_partitions; + blocksize_ = blocksize; nb_blocks_ = gridsize / blocksize_; if ( gridsize % blocksize_ ) nb_blocks_++; - nb_pts_.reserve( nb_partitions_ ); + this->nb_pts_.reserve( nb_partitions_Int_ ); - for ( idx_t iproc = 0; iproc < nb_partitions_; iproc++ ) { + for ( idx_t iproc = 0; iproc < nb_partitions; iproc++ ) { // Approximate values - gidx_t imin = blocksize_ * ( ( ( iproc + 0 ) * nb_blocks_ ) / nb_partitions_ ); - gidx_t imax = blocksize_ * ( ( ( iproc + 1 ) * nb_blocks_ ) / nb_partitions_ ); + gidx_t imin = blocksize_ * ( ( ( iproc + 0 ) * nb_blocks_ ) / nb_partitions_Int_ ); + gidx_t imax = blocksize_ * ( ( ( iproc + 1 ) * nb_blocks_ ) / nb_partitions_Int_ ); while ( imin > 0 ) { if ( function( imin - blocksize_ ) == iproc ) @@ -65,13 +69,31 @@ BandsDistribution::BandsDistribution( const atlas::Grid& grid, atlas::idx_t nb_p } imax = std::min( imax, (gidx_t)gridsize ); - nb_pts_.push_back( imax - imin ); + this->nb_pts_.push_back( imax - imin ); } - max_pts_ = *std::max_element( nb_pts_.begin(), nb_pts_.end() ); - min_pts_ = *std::min_element( nb_pts_.begin(), nb_pts_.end() ); + this->max_pts_ = *std::max_element( this->nb_pts_.begin(), this->nb_pts_.end() ); + this->min_pts_ = *std::min_element( this->nb_pts_.begin(), this->nb_pts_.end() ); + + ATLAS_ASSERT( detectOverflow( gridsize, nb_partitions_Int_, blocksize_ ) == false ); +} + +template +bool BandsDistribution::detectOverflow( size_t gridsize, size_t nb_partitions, size_t blocksize ) { + int64_t size = gridsize; + int64_t iblock = ( size - 1 ) / int64_t( blocksize ); + int64_t intermediate_product = iblock * int64_t( nb_partitions ); + ATLAS_ASSERT( intermediate_product > 0, "Even 64 bits is insufficient to prevent overflow." ); + if ( intermediate_product > std::numeric_limits::max() ) { + return true; + } + return false; } + +template class BandsDistribution; +template class BandsDistribution; + } // namespace distribution } // namespace detail } // namespace grid diff --git a/src/atlas/grid/detail/distribution/BandsDistribution.h b/src/atlas/grid/detail/distribution/BandsDistribution.h index b210fb45d..f4cfe20df 100644 --- a/src/atlas/grid/detail/distribution/BandsDistribution.h +++ b/src/atlas/grid/detail/distribution/BandsDistribution.h @@ -19,19 +19,22 @@ namespace grid { namespace detail { namespace distribution { - -class BandsDistribution : public DistributionFunctionT { +template +class BandsDistribution : public DistributionFunctionT> { private: - size_t blocksize_; - idx_t nb_blocks_; + Int blocksize_; + Int nb_blocks_; + Int nb_partitions_Int_; public: BandsDistribution( const Grid& grid, idx_t nb_partitions, const std::string& type, size_t blocksize = 1 ); - int function( gidx_t gidx ) const { - idx_t iblock = gidx / blocksize_; - return ( iblock * nb_partitions_ ) / nb_blocks_; + int function( gidx_t index ) const { + Int iblock = Int( index ) / blocksize_; + return ( iblock * nb_partitions_Int_ ) / nb_blocks_; } + + static bool detectOverflow( size_t gridsize, size_t nb_partitions, size_t blocksize ); }; diff --git a/src/atlas/grid/detail/partitioner/BandsPartitioner.cc b/src/atlas/grid/detail/partitioner/BandsPartitioner.cc index 7483f9902..498dbed9a 100644 --- a/src/atlas/grid/detail/partitioner/BandsPartitioner.cc +++ b/src/atlas/grid/detail/partitioner/BandsPartitioner.cc @@ -11,6 +11,8 @@ #include "BandsPartitioner.h" +#include + #include "atlas/grid/StructuredGrid.h" #include "atlas/grid/detail/distribution/BandsDistribution.h" #include "atlas/runtime/Exception.h" @@ -36,14 +38,31 @@ BandsPartitioner::BandsPartitioner() : Partitioner(), blocksize_{1} {} BandsPartitioner::BandsPartitioner( int N, int blocksize ) : Partitioner( N ), blocksize_( blocksize ) {} Distribution BandsPartitioner::partition( const Partitioner::Grid& grid ) const { - return Distribution{new distribution::BandsDistribution{grid, nb_partitions(), type(), blocksize( grid )}}; + if ( not distribution::BandsDistribution::detectOverflow( grid.size(), nb_partitions(), + blocksize( ( grid ) ) ) ) { + return Distribution{ + new distribution::BandsDistribution{grid, nb_partitions(), type(), blocksize( grid )}}; + } + else { + return Distribution{ + new distribution::BandsDistribution{grid, nb_partitions(), type(), blocksize( grid )}}; + } } void BandsPartitioner::partition( const Partitioner::Grid& grid, int part[] ) const { - distribution::BandsDistribution distribution{grid, nb_partitions(), type(), blocksize( grid )}; gidx_t gridsize = grid.size(); - for ( gidx_t n = 0; n < gridsize; ++n ) { - part[n] = distribution.function( n ); + if ( not distribution::BandsDistribution::detectOverflow( grid.size(), nb_partitions(), + blocksize( ( grid ) ) ) ) { + distribution::BandsDistribution distribution{grid, nb_partitions(), type(), blocksize( grid )}; + for ( gidx_t n = 0; n < gridsize; ++n ) { + part[n] = distribution.function( n ); + } + } + else { + distribution::BandsDistribution distribution{grid, nb_partitions(), type(), blocksize( grid )}; + for ( gidx_t n = 0; n < gridsize; ++n ) { + part[n] = distribution.function( n ); + } } } diff --git a/src/tests/grid/test_distribution_regular_bands.cc b/src/tests/grid/test_distribution_regular_bands.cc index d81189e6a..47dbd5fc7 100644 --- a/src/tests/grid/test_distribution_regular_bands.cc +++ b/src/tests/grid/test_distribution_regular_bands.cc @@ -9,6 +9,7 @@ */ #include +#include #include #include @@ -16,7 +17,10 @@ #include "atlas/field.h" #include "atlas/functionspace.h" #include "atlas/grid.h" +#include "atlas/grid/detail/distribution/BandsDistribution.h" +#include "atlas/grid/detail/distribution/SerialDistribution.h" #include "atlas/parallel/mpi/mpi.h" +#include "atlas/parallel/omp/omp.h" #include "atlas/util/Config.h" #include "tests/AtlasTestEnvironment.h" @@ -135,6 +139,60 @@ CASE( "test_regular_bands" ) { } } +CASE( "test regular_bands performance test" ) { + // auto grid = StructuredGrid( "L40000x20000" ); //-- > test takes too long( less than 15 seconds ) + // Example timings for L40000x20000: + // └─test regular_bands performance test │ 1 │ 11.90776s + // ├─inline access │ 1 │ 3.52238s + // ├─virtual access │ 1 │ 4.11881s + // └─virtual cached access │ 1 │ 4.02159s + + auto grid = StructuredGrid( "L5000x2500" ); // -> negligible time. + + auto dist = grid::Distribution( grid, grid::Partitioner( "regular_bands" ) ); + auto& dist_direct = dynamic_cast&>( *dist.get() ); + + + // Trick to prevent the compiler from compiling out a loop if it sees the result was not used. + struct DoNotCompileOut { + int counter = 0; + ~DoNotCompileOut() { Log::debug() << counter << std::endl; } + void operator()( int p ) { + counter += p; + // + } + } do_not_compile_out; + + gidx_t size = grid.size(); + + ATLAS_TRACE_SCOPE( "inline access" ) { + for ( gidx_t n = 0; n < size; ++n ) { + // inline function call + do_not_compile_out( dist_direct.function( n ) ); + } + } + + ATLAS_TRACE_SCOPE( "virtual access" ) { + for ( gidx_t n = 0; n < size; ++n ) { + // virtual function call + do_not_compile_out( dist.partition( n ) ); + } + } + + ATLAS_TRACE_SCOPE( "virtual cached access" ) { + gidx_t n = 0; + grid::Distribution::partition_t part( grid.nxmax() ); + for ( idx_t j = 0; j < grid.ny(); ++j, n += grid.nx( j ) ) { + dist.partition( n, n + grid.nx( j ), part ); + // this is one virtual call, which in turn has inline-access for nx(j) evaluations + for ( idx_t i = 0; i < grid.nx( j ); ++i ) { + do_not_compile_out( part[i] ); + } + } + } +} + + //----------------------------------------------------------------------------- } // namespace test From 8d933a3d5670572d17f245e93b168bf9b8e1850e Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 16 Jun 2020 15:37:08 +0100 Subject: [PATCH 127/145] Remove unused subroutine atlas_write_to_fortran_unit --- src/atlas_f/CMakeLists.txt | 1 - .../internals/atlas_write_to_fortran_unit.F90 | 19 ------------------- 2 files changed, 20 deletions(-) delete mode 100644 src/atlas_f/internals/atlas_write_to_fortran_unit.F90 diff --git a/src/atlas_f/CMakeLists.txt b/src/atlas_f/CMakeLists.txt index b282927bd..a329419c3 100644 --- a/src/atlas_f/CMakeLists.txt +++ b/src/atlas_f/CMakeLists.txt @@ -192,7 +192,6 @@ ecbuild_add_library( TARGET atlas_f trans/atlas_Trans_module.F90 internals/atlas_read_file.h internals/atlas_read_file.cc - internals/atlas_write_to_fortran_unit.F90 internals/Library.h internals/Library.cc runtime/atlas_trace.cc diff --git a/src/atlas_f/internals/atlas_write_to_fortran_unit.F90 b/src/atlas_f/internals/atlas_write_to_fortran_unit.F90 deleted file mode 100644 index 5a270c73c..000000000 --- a/src/atlas_f/internals/atlas_write_to_fortran_unit.F90 +++ /dev/null @@ -1,19 +0,0 @@ -! (C) Copyright 2013 ECMWF. -! -! This software is licensed under the terms of the Apache Licence Version 2.0 -! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -! In applying this licence, ECMWF does not waive the privileges and immunities -! granted to it by virtue of its status as an intergovernmental organisation nor -! does it submit to any jurisdiction. - -subroutine atlas_write_to_fortran_unit(unit,msg_cptr) bind(C) - use, intrinsic :: iso_c_binding, only: c_int, c_ptr, c_associated - use fckit_c_interop_module, only : copy_c_ptr_to_string - integer(c_int), value, intent(in) :: unit - type(c_ptr), value, intent(in) :: msg_cptr - character(len=:), allocatable :: msg - if( c_associated( msg_cptr ) ) then - call copy_c_ptr_to_string( msg_cptr, msg ) - write(unit,'(A)',advance='no') msg - endif -end subroutine From e07e9e2fdd1a22f54556c6cc88607b936aafdc8e Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 16 Jun 2020 22:38:20 +0100 Subject: [PATCH 128/145] Fix warnings --- .../grid/detail/distribution/DistributionArray.h | 2 +- .../grid/detail/distribution/DistributionFunction.h | 12 ++++++------ src/atlas/grid/detail/partitioner/BandsPartitioner.h | 4 ++-- .../grid/detail/partitioner/SerialPartitioner.h | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/atlas/grid/detail/distribution/DistributionArray.h b/src/atlas/grid/detail/distribution/DistributionArray.h index d37026aac..57f663757 100644 --- a/src/atlas/grid/detail/distribution/DistributionArray.h +++ b/src/atlas/grid/detail/distribution/DistributionArray.h @@ -64,7 +64,7 @@ class DistributionArray : public DistributionImpl { bool functional() const override { return false; } - virtual gidx_t size() const { return part_.size(); } + gidx_t size() const override { return part_.size(); } void hash( eckit::Hash& ) const override; diff --git a/src/atlas/grid/detail/distribution/DistributionFunction.h b/src/atlas/grid/detail/distribution/DistributionFunction.h index 3bf367fa4..2e36d6a83 100644 --- a/src/atlas/grid/detail/distribution/DistributionFunction.h +++ b/src/atlas/grid/detail/distribution/DistributionFunction.h @@ -27,16 +27,16 @@ class DistributionFunction : public DistributionImpl { bool functional() const override { return true; } size_t footprint() const override { return nb_pts_.size() * sizeof( nb_pts_[0] ); } const std::string& type() const override { return nb_partitions_ == 1 ? serial : type_; } - idx_t nb_partitions() const { return nb_partitions_; } + idx_t nb_partitions() const override { return nb_partitions_; } - virtual const std::vector& nb_pts() const { return nb_pts_; } + const std::vector& nb_pts() const override { return nb_pts_; } - virtual idx_t max_pts() const { return max_pts_; } - virtual idx_t min_pts() const { return min_pts_; } + idx_t max_pts() const override { return max_pts_; } + idx_t min_pts() const override { return min_pts_; } - virtual void print( std::ostream& ) const; + void print( std::ostream& ) const override; - virtual gidx_t size() const { return size_; } + gidx_t size() const override { return size_; } void hash( eckit::Hash& ) const override; diff --git a/src/atlas/grid/detail/partitioner/BandsPartitioner.h b/src/atlas/grid/detail/partitioner/BandsPartitioner.h index 7bbc384ec..f905e1ae3 100644 --- a/src/atlas/grid/detail/partitioner/BandsPartitioner.h +++ b/src/atlas/grid/detail/partitioner/BandsPartitioner.h @@ -44,9 +44,9 @@ class BandsPartitioner : public Partitioner { std::string type() const override { return static_type(); } static std::string static_type() { return "bands"; } - virtual Distribution partition( const Grid& grid ) const override; + Distribution partition( const Grid& grid ) const override; - virtual void partition( const Grid& grid, int part[] ) const; + void partition( const Grid& grid, int part[] ) const override; }; } // namespace partitioner diff --git a/src/atlas/grid/detail/partitioner/SerialPartitioner.h b/src/atlas/grid/detail/partitioner/SerialPartitioner.h index 9b999af62..637abb10a 100644 --- a/src/atlas/grid/detail/partitioner/SerialPartitioner.h +++ b/src/atlas/grid/detail/partitioner/SerialPartitioner.h @@ -30,11 +30,11 @@ class SerialPartitioner : public Partitioner { std::string type() const override { return static_type(); } static std::string static_type() { return "serial"; } - virtual Distribution partition( const Grid& grid ) const override { + Distribution partition( const Grid& grid ) const override { return Distribution{new distribution::SerialDistribution{grid}}; } - virtual void partition( const Grid& grid, int part[] ) const { + void partition( const Grid& grid, int part[] ) const override { gidx_t gridsize = grid.size(); for ( gidx_t n = 0; n < gridsize; ++n ) { part[n] = 0.; From 724b4b34327ce2ef94ed53aea3924bba7df8d2c9 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 16 Jun 2020 22:42:47 +0100 Subject: [PATCH 129/145] travis: 60s timeout for tests --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3c1ad5f08..93598a2d4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -233,7 +233,7 @@ script: ################################################################# # Test Atlas ################################################################# - - ctest ${ATLAS_CTEST_OPTIONS} + - ctest --timeout 60 ${ATLAS_CTEST_OPTIONS} after_success: @@ -251,5 +251,5 @@ after_success: after_failure: - cd ${ATLAS_BUILD_DIR} - - ctest -VV --rerun-failed ${ATLAS_CTEST_OPTIONS} + - ctest -VV --rerun-failed --timeout 60 ${ATLAS_CTEST_OPTIONS} - cat ecbuild.log From 5270ecdd662df83fb554cf9bd2d64848fad57eb2 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 17 Jun 2020 12:08:46 +0100 Subject: [PATCH 130/145] Use Serial partitioner in StructuredMeshGenerator if possible. --- .../detail/StructuredMeshGenerator.cc | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/atlas/meshgenerator/detail/StructuredMeshGenerator.cc b/src/atlas/meshgenerator/detail/StructuredMeshGenerator.cc index d5055cfcc..77ec1005b 100644 --- a/src/atlas/meshgenerator/detail/StructuredMeshGenerator.cc +++ b/src/atlas/meshgenerator/detail/StructuredMeshGenerator.cc @@ -193,16 +193,20 @@ void StructuredMeshGenerator::generate( const Grid& grid, Mesh& mesh ) const { std::string partitioner_type = "equal_regions"; options.get( "partitioner", partitioner_type ); - if ( partitioner_type == "trans" && rg.ny() % 2 == 1 ) { - partitioner_type = "equal_regions"; // Odd number of latitudes + if ( partitioner_type == "trans" ) { + if ( rg.ny() % 2 == 1 ) { + partitioner_type = "equal_regions"; // Odd number of latitudes + } + if ( nb_parts != mpi::size() ) { + partitioner_type = "equal_regions"; + } } - if ( nb_parts == 1 || mpi::size() == 1 ) { - partitioner_type = "equal_regions"; + if ( nb_parts == 1 ) { + partitioner_type = "serial"; } grid::Partitioner partitioner( partitioner_type, nb_parts ); grid::Distribution distribution( partitioner.partition( grid ) ); - ATLAS_DEBUG_VAR( distribution ); generate( grid, distribution, mesh ); } From ad2f8186215ab519070c11d10e3081dc5c9e0caf Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 18 Jun 2020 14:19:47 +0100 Subject: [PATCH 131/145] Fix clang-tidy warnings --- src/atlas/grid/detail/distribution/BandsDistribution.cc | 9 ++++++--- src/tests/util/test_vector.cc | 6 ++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/atlas/grid/detail/distribution/BandsDistribution.cc b/src/atlas/grid/detail/distribution/BandsDistribution.cc index b531bd520..13725117a 100644 --- a/src/atlas/grid/detail/distribution/BandsDistribution.cc +++ b/src/atlas/grid/detail/distribution/BandsDistribution.cc @@ -34,8 +34,9 @@ BandsDistribution::BandsDistribution( const atlas::Grid& grid, atlas::idx_t nb_blocks_ = gridsize / blocksize_; - if ( gridsize % blocksize_ ) + if ( gridsize % blocksize_ ) { nb_blocks_++; + } this->nb_pts_.reserve( nb_partitions_Int_ ); @@ -45,10 +46,12 @@ BandsDistribution::BandsDistribution( const atlas::Grid& grid, atlas::idx_t gidx_t imax = blocksize_ * ( ( ( iproc + 1 ) * nb_blocks_ ) / nb_partitions_Int_ ); while ( imin > 0 ) { - if ( function( imin - blocksize_ ) == iproc ) + if ( function( imin - blocksize_ ) == iproc ) { imin -= blocksize_; - else + } + else { break; + } } while ( function( imin ) < iproc ) { diff --git a/src/tests/util/test_vector.cc b/src/tests/util/test_vector.cc index 85fc2101a..acef5a4a3 100644 --- a/src/tests/util/test_vector.cc +++ b/src/tests/util/test_vector.cc @@ -24,8 +24,9 @@ namespace test { template atlas::vector square( const int n ) { atlas::vector x( n ); - for ( int i = 0; i < n; i++ ) + for ( int i = 0; i < n; i++ ) { x[i] = static_cast( i ) * static_cast( i ); + } return x; } @@ -34,8 +35,9 @@ template void pp( const std::string& t, T& v ) { Log::info() << t << " = " << std::endl; Log::info() << v.size() << std::endl; - for ( int i = 0; i < v.size(); i++ ) + for ( int i = 0; i < v.size(); i++ ) { Log::info() << i << " > " << v[i] << std::endl; + } } From 2e0d2e29afda05349b4298bdd72b400810df43d9 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 17 Jun 2020 12:11:44 +0100 Subject: [PATCH 132/145] travis: ATLAS_TRACE=1 --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 93598a2d4..a0cf91448 100644 --- a/.travis.yml +++ b/.travis.yml @@ -233,7 +233,7 @@ script: ################################################################# # Test Atlas ################################################################# - - ctest --timeout 60 ${ATLAS_CTEST_OPTIONS} + - ATLAS_TRACE=1 ATLAS_DEBUG=1 ctest --output-on-failure --timeout 60 ${ATLAS_CTEST_OPTIONS} after_success: @@ -251,5 +251,5 @@ after_success: after_failure: - cd ${ATLAS_BUILD_DIR} - - ctest -VV --rerun-failed --timeout 60 ${ATLAS_CTEST_OPTIONS} + - ATLAS_TRACE=1 ctest -VV --rerun-failed --timeout 60 ${ATLAS_CTEST_OPTIONS} - cat ecbuild.log From 13177c511f39f161ec5cd39ec0adcf5a902f0434 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 18 Jun 2020 14:40:17 +0100 Subject: [PATCH 133/145] travis: Remove addons for osx --- .travis.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index a0cf91448..82db188b0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -69,12 +69,6 @@ matrix: - ATLAS_CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=DEBUG" - ATLAS_CTEST_OPTIONS="-E atlas_test_stencil_parallel_mpi16" osx_image: xcode10.1 - addons: - homebrew: - packages: - - openmpi - - cgal - - fftw - os: osx env: From 7a5dc896ba41779aa80dba77c0b13a1ab6cbea4c Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 18 Jun 2020 13:27:59 +0100 Subject: [PATCH 134/145] Improve performance for debug builds with new ATLAS_ALWAYS_INLINE macro --- src/atlas/grid/Distribution.h | 6 +++--- .../detail/distribution/BandsDistribution.h | 2 +- .../detail/distribution/DistributionFunction.h | 9 ++++++--- .../detail/distribution/SerialDistribution.h | 2 +- src/atlas/library/defines.h.in | 4 +++- src/atlas/util/ObjectHandle.h | 18 +++++++++++------- .../grid/test_distribution_regular_bands.cc | 17 ++++++++--------- 7 files changed, 33 insertions(+), 25 deletions(-) diff --git a/src/atlas/grid/Distribution.h b/src/atlas/grid/Distribution.h index 1a0886e6a..62cfd7a2f 100644 --- a/src/atlas/grid/Distribution.h +++ b/src/atlas/grid/Distribution.h @@ -55,7 +55,7 @@ class Distribution : DOXYGEN_HIDE( public util::ObjectHandle ) ~Distribution(); - int partition( gidx_t index ) const { return get()->partition( index ); } + ATLAS_ALWAYS_INLINE int partition( gidx_t index ) const { return get()->partition( index ); } template void partition( gidx_t begin, gidx_t end, PartitionContainer& partitions ) const { @@ -64,9 +64,9 @@ class Distribution : DOXYGEN_HIDE( public util::ObjectHandle ) size_t footprint() const { return get()->footprint(); } - idx_t nb_partitions() const { return get()->nb_partitions(); } + ATLAS_ALWAYS_INLINE idx_t nb_partitions() const { return get()->nb_partitions(); } - gidx_t size() const { return get()->size(); } + ATLAS_ALWAYS_INLINE gidx_t size() const { return get()->size(); } const std::vector& nb_pts() const; diff --git a/src/atlas/grid/detail/distribution/BandsDistribution.h b/src/atlas/grid/detail/distribution/BandsDistribution.h index f4cfe20df..f064a6bb9 100644 --- a/src/atlas/grid/detail/distribution/BandsDistribution.h +++ b/src/atlas/grid/detail/distribution/BandsDistribution.h @@ -29,7 +29,7 @@ class BandsDistribution : public DistributionFunctionT> { public: BandsDistribution( const Grid& grid, idx_t nb_partitions, const std::string& type, size_t blocksize = 1 ); - int function( gidx_t index ) const { + ATLAS_ALWAYS_INLINE int function( gidx_t index ) const { Int iblock = Int( index ) / blocksize_; return ( iblock * nb_partitions_Int_ ) / nb_blocks_; } diff --git a/src/atlas/grid/detail/distribution/DistributionFunction.h b/src/atlas/grid/detail/distribution/DistributionFunction.h index 2e36d6a83..8e414aa96 100644 --- a/src/atlas/grid/detail/distribution/DistributionFunction.h +++ b/src/atlas/grid/detail/distribution/DistributionFunction.h @@ -55,12 +55,15 @@ template class DistributionFunctionT : public DistributionFunction { public: DistributionFunctionT( const Grid& grid ) : DistributionFunction( grid ) {} - int partition( gidx_t index ) const override { return static_cast( this )->function( index ); } + ATLAS_ALWAYS_INLINE int partition( gidx_t index ) const override { + return static_cast( this )->function( index ); + } - void partition( gidx_t begin, gidx_t end, int partitions[] ) const override { + ATLAS_ALWAYS_INLINE void partition( gidx_t begin, gidx_t end, int partitions[] ) const override { + const Derived& derived = *static_cast(this); size_t i = 0; for ( gidx_t n = begin; n < end; ++n, ++i ) { - partitions[i] = static_cast( this )->function( n ); + partitions[i] = derived.function( n ); } } }; diff --git a/src/atlas/grid/detail/distribution/SerialDistribution.h b/src/atlas/grid/detail/distribution/SerialDistribution.h index 7326cd546..1006f365c 100644 --- a/src/atlas/grid/detail/distribution/SerialDistribution.h +++ b/src/atlas/grid/detail/distribution/SerialDistribution.h @@ -22,7 +22,7 @@ class SerialDistribution : public DistributionFunctionT { public: SerialDistribution( const Grid& grid ); - int function( gidx_t gidx ) const { return 0; } + ATLAS_ALWAYS_INLINE int function( gidx_t gidx ) const { return 0; } }; } // namespace distribution diff --git a/src/atlas/library/defines.h.in b/src/atlas/library/defines.h.in index a1aaaeb22..6ce6200f1 100644 --- a/src/atlas/library/defines.h.in +++ b/src/atlas/library/defines.h.in @@ -43,10 +43,12 @@ #define ATLAS_BITS_LOCAL @ATLAS_BITS_LOCAL@ -#if defined( __GNUC__ ) +#if defined( __GNUC__ ) || defined( __clang__ ) #define ATLAS_MAYBE_UNUSED __attribute__( ( unused ) ) +#define ATLAS_ALWAYS_INLINE __attribute__( ( always_inline ) ) inline #else #define ATLAS_MAYBE_UNUSED +#define ATLAS_ALWAYS_INLINE inline #endif #endif diff --git a/src/atlas/util/ObjectHandle.h b/src/atlas/util/ObjectHandle.h index c6309a268..bb9242cf5 100644 --- a/src/atlas/util/ObjectHandle.h +++ b/src/atlas/util/ObjectHandle.h @@ -10,6 +10,8 @@ #pragma once +#include "atlas/library/config.h" + namespace atlas { namespace util { @@ -65,16 +67,18 @@ class ObjectHandle : public ObjectHandleBase { using Handle = ObjectHandle; public: - T* get() { return reinterpret_cast( object_ ); } - const T* get() const { return reinterpret_cast( object_ ); } ObjectHandle() = default; ObjectHandle( const T* object ) : ObjectHandleBase( reinterpret_cast( object ) ) {} ObjectHandle( const ObjectHandle& handle ) : ObjectHandleBase( reinterpret_cast( handle.get() ) ) {} - const T* operator->() const { return get(); } - T* operator->() { return get(); } - const T& operator*() const { return *get(); } - T& operator*() { return *get(); } - void reset( const T* object ) { ObjectHandleBase::reset( reinterpret_cast( object ) ); } + ATLAS_ALWAYS_INLINE T* get() { return reinterpret_cast( object_ ); } + ATLAS_ALWAYS_INLINE const T* get() const { return reinterpret_cast( object_ ); } + ATLAS_ALWAYS_INLINE const T* operator->() const { return get(); } + ATLAS_ALWAYS_INLINE T* operator->() { return get(); } + ATLAS_ALWAYS_INLINE const T& operator*() const { return *get(); } + ATLAS_ALWAYS_INLINE T& operator*() { return *get(); } + ATLAS_ALWAYS_INLINE void reset( const T* object ) { + ObjectHandleBase::reset( reinterpret_cast( object ) ); + } }; } // namespace util diff --git a/src/tests/grid/test_distribution_regular_bands.cc b/src/tests/grid/test_distribution_regular_bands.cc index 47dbd5fc7..b48327d13 100644 --- a/src/tests/grid/test_distribution_regular_bands.cc +++ b/src/tests/grid/test_distribution_regular_bands.cc @@ -155,12 +155,9 @@ CASE( "test regular_bands performance test" ) { // Trick to prevent the compiler from compiling out a loop if it sees the result was not used. struct DoNotCompileOut { - int counter = 0; - ~DoNotCompileOut() { Log::debug() << counter << std::endl; } - void operator()( int p ) { - counter += p; - // - } + int x = 0; + ~DoNotCompileOut() { Log::debug() << x << std::endl; } + ATLAS_ALWAYS_INLINE void operator()( int p ) { x = p; } } do_not_compile_out; gidx_t size = grid.size(); @@ -183,10 +180,12 @@ CASE( "test regular_bands performance test" ) { gidx_t n = 0; grid::Distribution::partition_t part( grid.nxmax() ); for ( idx_t j = 0; j < grid.ny(); ++j, n += grid.nx( j ) ) { - dist.partition( n, n + grid.nx( j ), part ); + const idx_t nx = grid.nx( j ); + dist.partition( n, n + nx, part ); // this is one virtual call, which in turn has inline-access for nx(j) evaluations - for ( idx_t i = 0; i < grid.nx( j ); ++i ) { - do_not_compile_out( part[i] ); + int* P = part.data(); + for ( idx_t i = 0; i < nx; ++i ) { + do_not_compile_out( P[i] ); } } } From 2fbed8e7c9ebd0e93be330bd64c26e9c2ddeb7e1 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 18 Jun 2020 14:21:41 +0100 Subject: [PATCH 135/145] Increase timeout for test --- src/tests/numerics/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tests/numerics/CMakeLists.txt b/src/tests/numerics/CMakeLists.txt index 47bd2d168..7f1e1c1eb 100644 --- a/src/tests/numerics/CMakeLists.txt +++ b/src/tests/numerics/CMakeLists.txt @@ -29,6 +29,9 @@ ecbuild_add_test( TARGET atlas_test_fvm_nabla_validation LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) +if( TEST atlas_test_fvm_nabla_validation ) + set_tests_properties( atlas_test_fvm_nabla_validation PROPERTIES TIMEOUT 120 ) # For certain debug builds (exceeds 60s on travis) +endif() if( HAVE_FCTEST) add_fctest( TARGET atlas_fctest_fvm_nabla From 97722eec0fdaa2ae6b6a768964928fc4325c5641 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 19 Jun 2020 23:25:04 +0100 Subject: [PATCH 136/145] Assertions --- src/atlas/grid/Distribution.h | 1 + src/atlas/runtime/Exception.h | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/atlas/grid/Distribution.h b/src/atlas/grid/Distribution.h index 62cfd7a2f..44fa11d9e 100644 --- a/src/atlas/grid/Distribution.h +++ b/src/atlas/grid/Distribution.h @@ -59,6 +59,7 @@ class Distribution : DOXYGEN_HIDE( public util::ObjectHandle ) template void partition( gidx_t begin, gidx_t end, PartitionContainer& partitions ) const { + ATLAS_ASSERT( end - begin <= partitions.size() ); return get()->partition( begin, end, partitions.data() ); } diff --git a/src/atlas/runtime/Exception.h b/src/atlas/runtime/Exception.h index f4046bc38..b6c581ca9 100644 --- a/src/atlas/runtime/Exception.h +++ b/src/atlas/runtime/Exception.h @@ -53,8 +53,8 @@ inline void Assert( bool success, const char* code, const char* msg, const char* #define ATLAS_NOTIMPLEMENTED ::atlas::throw_NotImplemented( Here() ) -#define ATLAS_ASSERT_NOMSG( code ) ::atlas::detail::Assert( ( code ), #code, __FILE__, __LINE__, __func__ ) -#define ATLAS_ASSERT_MSG( code, msg ) ::atlas::detail::Assert( ( code ), #code, msg, __FILE__, __LINE__, __func__ ) +#define ATLAS_ASSERT_NOMSG( code ) ::atlas::detail::Assert( bool( code ), #code, __FILE__, __LINE__, __func__ ) +#define ATLAS_ASSERT_MSG( code, msg ) ::atlas::detail::Assert( bool( code ), #code, msg, __FILE__, __LINE__, __func__ ) #define ATLAS_ASSERT( ... ) __ATLAS_SPLICE( __ATLAS_ASSERT_, __ATLAS_NARG( __VA_ARGS__ ) )( __VA_ARGS__ ) #define __ATLAS_ASSERT_1 ATLAS_ASSERT_NOMSG From 6e1c647023e846cf316d564321bf8dbbbe82be92 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 19 Jun 2020 23:45:33 +0100 Subject: [PATCH 137/145] travis: 300s timeout for tests --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 82db188b0..db94b6ba7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -227,7 +227,7 @@ script: ################################################################# # Test Atlas ################################################################# - - ATLAS_TRACE=1 ATLAS_DEBUG=1 ctest --output-on-failure --timeout 60 ${ATLAS_CTEST_OPTIONS} + - ATLAS_TRACE=1 ATLAS_DEBUG=1 ctest --output-on-failure --timeout 300 ${ATLAS_CTEST_OPTIONS} after_success: From 25747e8145a83155ecb329b1b242b6c90ff90cb7 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 19 Jun 2020 23:46:19 +0100 Subject: [PATCH 138/145] Increase timeout for atlas_test_fvm_nabla_validation --- src/tests/numerics/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tests/numerics/CMakeLists.txt b/src/tests/numerics/CMakeLists.txt index 7f1e1c1eb..87d10a262 100644 --- a/src/tests/numerics/CMakeLists.txt +++ b/src/tests/numerics/CMakeLists.txt @@ -30,7 +30,8 @@ ecbuild_add_test( TARGET atlas_test_fvm_nabla_validation ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) if( TEST atlas_test_fvm_nabla_validation ) - set_tests_properties( atlas_test_fvm_nabla_validation PROPERTIES TIMEOUT 120 ) # For certain debug builds (exceeds 60s on travis) + set_tests_properties( atlas_test_fvm_nabla_validation PROPERTIES TIMEOUT 450 ) + # For certain debug builds (e.g. with address-sanitizer) endif() if( HAVE_FCTEST) From 4a0f2168dacdace8ff10b0617e79d131bf5ecb0e Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 19 Jun 2020 23:47:00 +0100 Subject: [PATCH 139/145] Fix minor memory issue in test --- src/tests/grid/test_distribution_regular_bands.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tests/grid/test_distribution_regular_bands.cc b/src/tests/grid/test_distribution_regular_bands.cc index b48327d13..23c0a5f61 100644 --- a/src/tests/grid/test_distribution_regular_bands.cc +++ b/src/tests/grid/test_distribution_regular_bands.cc @@ -179,7 +179,7 @@ CASE( "test regular_bands performance test" ) { ATLAS_TRACE_SCOPE( "virtual cached access" ) { gidx_t n = 0; grid::Distribution::partition_t part( grid.nxmax() ); - for ( idx_t j = 0; j < grid.ny(); ++j, n += grid.nx( j ) ) { + for ( idx_t j = 0; j < grid.ny(); ++j ) { const idx_t nx = grid.nx( j ); dist.partition( n, n + nx, part ); // this is one virtual call, which in turn has inline-access for nx(j) evaluations @@ -187,6 +187,7 @@ CASE( "test regular_bands performance test" ) { for ( idx_t i = 0; i < nx; ++i ) { do_not_compile_out( P[i] ); } + n += grid.nx( j ); } } } From 81f6b5c46756610b88459c3653c8c638347028b0 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Sat, 20 Jun 2020 00:06:20 +0100 Subject: [PATCH 140/145] Compatibility with eckit < 1.12 --- src/tests/AtlasTestEnvironment.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/tests/AtlasTestEnvironment.h b/src/tests/AtlasTestEnvironment.h index f31c32e8e..1bf237023 100644 --- a/src/tests/AtlasTestEnvironment.h +++ b/src/tests/AtlasTestEnvironment.h @@ -61,8 +61,13 @@ class Test { ~Test() { current_test_ = nullptr; } void expect_failed( const std::string& message, const eckit::CodeLocation& location ) { failures_.emplace_back( Failure{message, location} ); +#if ECKIT_MAJOR_VERSION * 100 + ECKIT_MINOR_VERSION >= 112 eckit::Log::error() << eckit::Colour::red << message << eckit::Colour::reset << " @ " << eckit::PathName{location.file()}.baseName() << " +" << location.line() << std::endl; +#else + eckit::Log::error() << message << " @ " << eckit::PathName{location.file()}.baseName() << " +" + << location.line() << std::endl; +#endif if ( failures_.size() == ATLAS_MAX_FAILED_EXPECTS() ) { std::stringstream msg; msg << "Maximum number of allowed EXPECTS have failed (${ATLAS_MAX_FAILED_EXPECTS}=" From 17e64bb83bfc628ebe9bf442b0ddb12d422cbae1 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 19 Jun 2020 23:25:53 +0100 Subject: [PATCH 141/145] Grid::footprint() API (unimplemented) --- src/atlas/grid/Grid.cc | 4 ++++ src/atlas/grid/Grid.h | 2 ++ src/atlas/grid/detail/grid/Grid.h | 2 ++ 3 files changed, 8 insertions(+) diff --git a/src/atlas/grid/Grid.cc b/src/atlas/grid/Grid.cc index e27218bdc..2580f2560 100644 --- a/src/atlas/grid/Grid.cc +++ b/src/atlas/grid/Grid.cc @@ -53,6 +53,10 @@ idx_t Grid::size() const { return get()->size(); } +size_t Grid::footprint() const { + return get()->footprint(); +} + const Grid::Projection& Grid::projection() const { return get()->projection(); } diff --git a/src/atlas/grid/Grid.h b/src/atlas/grid/Grid.h index 151f0aa2b..62fb0fb48 100644 --- a/src/atlas/grid/Grid.h +++ b/src/atlas/grid/Grid.h @@ -96,6 +96,8 @@ class Grid : DOXYGEN_HIDE( public util::ObjectHandle ) /// Adds to the hash the information that makes this Grid unique void hash( eckit::Hash& h ) const; + size_t footprint() const; + Spec spec() const; }; diff --git a/src/atlas/grid/detail/grid/Grid.h b/src/atlas/grid/detail/grid/Grid.h index bc9fb61da..a0e3354ad 100644 --- a/src/atlas/grid/detail/grid/Grid.h +++ b/src/atlas/grid/detail/grid/Grid.h @@ -106,6 +106,8 @@ class Grid : public util::Object { /// @return parallel/meridian limits containing the grid virtual RectangularLonLatDomain lonlatBoundingBox() const = 0; + virtual size_t footprint() const { return 0; } + /// @return projection (mapping between geographic coordinates and grid /// coordinates) const Projection& projection() const { return projection_; } From 90c5fc14e808b5abbb2db306e05104aa4d72de47 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 23 Jun 2020 00:18:09 +0100 Subject: [PATCH 142/145] ATLAS-300 Atlas plugin support --- atlas-import.cmake.in | 28 +++++++ src/atlas/CMakeLists.txt | 2 + src/atlas/library/Library.cc | 138 ++++++++++++++++++++++++++++++++++- src/atlas/library/Library.h | 12 ++- src/atlas/library/Plugin.cc | 28 +++++++ src/atlas/library/Plugin.h | 34 +++++++++ 6 files changed, 237 insertions(+), 5 deletions(-) create mode 100644 src/atlas/library/Plugin.cc create mode 100644 src/atlas/library/Plugin.h diff --git a/atlas-import.cmake.in b/atlas-import.cmake.in index 2c83d830f..3b7d60080 100644 --- a/atlas-import.cmake.in +++ b/atlas-import.cmake.in @@ -80,3 +80,31 @@ elseif( atlas_FIND_REQUIRED_FORTRAN ) endif() set( @PROJECT_NAME@_DIR ${CMAKE_CURRENT_LIST_DIR} CACHE STRING "" ) + + +function( atlas_create_plugin name ) + + set( options ) + set( single_value_args VERSION ) + set( multi_value_args LIBRARY ) + cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}" ${_FIRST_ARG} ${ARGN} ) + + set( _plugin_file share/atlas/plugins/${name}.yml ) + + if( NOT DEFINED _PAR_VERSION ) + set( _version ${${PROJECT_NAME}_VERSION} ) + else() + set( _version ${_PAR_VERSION} ) + endif() + if( NOT DEFINED _PAR_LIBRARY ) + set( _library "${name}" ) + else() + set( _library "${_PAR_LIBRARY}" ) + endif() + file( WRITE ${CMAKE_BINARY_DIR}/${_plugin_file} "name: ${name}\n" ) + file( APPEND ${CMAKE_BINARY_DIR}/${_plugin_file} "version: ${_version}\n" ) + file( APPEND ${CMAKE_BINARY_DIR}/${_plugin_file} "library: ${_library}" ) + install( FILES ${CMAKE_BINARY_DIR}/${_plugin_file} DESTINATION ${_plugin_file} ) + +endfunction() + diff --git a/src/atlas/CMakeLists.txt b/src/atlas/CMakeLists.txt index 26635776b..5a3359929 100644 --- a/src/atlas/CMakeLists.txt +++ b/src/atlas/CMakeLists.txt @@ -29,6 +29,8 @@ library.h library/config.h library/Library.h library/Library.cc +library/Plugin.h +library/Plugin.cc runtime/AtlasTool.h runtime/AtlasTool.cc runtime/Log.h diff --git a/src/atlas/library/Library.cc b/src/atlas/library/Library.cc index 8bb748866..2ee2213d1 100644 --- a/src/atlas/library/Library.cc +++ b/src/atlas/library/Library.cc @@ -11,6 +11,9 @@ #include #include +#include // for dynamic loading (should be delegated to eckit) + +#include "eckit/config/Resource.h" #include "eckit/eckit.h" #include "eckit/filesystem/LocalPathName.h" #include "eckit/filesystem/PathName.h" @@ -18,13 +21,17 @@ #include "eckit/log/OStreamTarget.h" #include "eckit/log/PrefixTarget.h" #include "eckit/runtime/Main.h" +#include "eckit/system/SystemInfo.h" +#include "eckit/types/Types.h" #include "eckit/utils/Translator.h" #include "atlas/library/Library.h" +#include "atlas/library/Plugin.h" #include "atlas/library/config.h" #include "atlas/library/git_sha1.h" #include "atlas/library/version.h" #include "atlas/parallel/mpi/mpi.h" +#include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" #include "atlas/util/Config.h" @@ -56,6 +63,16 @@ std::string str( const eckit::system::Library& lib ) { return ss.str(); } +std::string str( const Plugin& lib ) { + std::string gitsha1 = lib.gitsha1(); + std::stringstream ss; + ss << lib.name() << " version (" << lib.version() << "),"; + if ( lib.gitsha1() != "not available" ) { + ss << " git-sha1 " << lib.gitsha1( 7 ); + } + return ss.str(); +} + bool getEnv( const std::string& env, bool default_value ) { if ( ::getenv( env.c_str() ) ) { return eckit::Translator()( ::getenv( env.c_str() ) ); @@ -70,6 +87,53 @@ int getEnv( const std::string& env, int default_value ) { return default_value; } + +bool library_exists( const eckit::PathName& library_dir, const std::string library_name, + eckit::PathName& library_path ) { + // WARNING: Mostly copy-paste from eckit develop before 1.13 release + std::vector library_file_names; + library_file_names.push_back( "lib" + library_name + ".so" ); + library_file_names.push_back( "lib" + library_name + ".dylib" ); + for ( const auto& library_file_name : library_file_names ) { + library_path = library_dir / library_file_name; + if ( library_path.exists() ) { + return true; + } + } + return false; +} + + +void load_library( const eckit::PathName& library_dir, const std::string library_name ) { + // WARNING: Mostly copy-paste from eckit develop before 1.13 release + std::vector library_file_names; + library_file_names.push_back( "lib" + library_name + ".so" ); + library_file_names.push_back( "lib" + library_name + ".dylib" ); + + eckit::PathName library_path; + for ( const auto& library_file_name : library_file_names ) { + library_path = library_dir / library_file_name; + if ( library_path.exists() ) { + break; + } + } + void* plib; + if ( ( plib = ::dlopen( library_path.localPath(), RTLD_NOW | RTLD_GLOBAL ) ) == nullptr ) { + std::ostringstream ss; + ss << "dlopen(" << library_path << ", ...)"; + throw eckit::FailedSystemCall( ss.str().c_str(), Here(), errno ); + } + + // If the library still doesn't exist after a successful call of dlopen, then + // we have managed to load something other than a (self-registering) eckit library + if ( !eckit::system::Library::exists( library_name ) ) { + std::ostringstream ss; + ss << "Shared library " << library_path << " loaded but Library " << library_name << " not registered"; + throw eckit::UnexpectedState( ss.str(), Here() ); + } +} + + } // namespace //---------------------------------------------------------------------------------------------------------------------- @@ -104,6 +168,10 @@ Library::Library() : trace_barriers_( getEnv( "ATLAS_TRACE_BARRIERS", false ) ), trace_report_( getEnv( "ATLAS_TRACE_REPORT", false ) ) {} +void Library::registerPlugin( Plugin& plugin ) { + plugins_.push_back( &plugin ); +} + Library& Library::instance() { return libatlas; } @@ -167,6 +235,62 @@ void Library::initialise( const eckit::Parametrisation& config ) { } + std::vector plugin_names = + eckit::Resource>( "atlasPlugins;$ATLAS_PLUGINS", {} ); + + std::vector plugins_search_paths = + eckit::Resource>( "atlasPluginsSearchPaths;$ATLAS_PLUGINS_SEARCH_PATHS", {} ); + plugins_search_paths.push_back( "~atlas" ); + + for ( eckit::PathName p : plugins_search_paths ) { + Log::info() << "plugins search path " << p.dirName() << std::endl; + } + + auto is_plugin_loaded = [&]( const std::string& name ) { + for ( auto& plugin : plugins() ) { + if ( name == plugin->name() ) { + return true; + } + } + return false; + }; + + for ( auto& plugin_name : plugin_names ) { + if ( is_plugin_loaded( plugin_name ) ) { + continue; + } + bool plugin_loaded = false; + eckit::PathName library_path; + + std::string plugin_file_path = "share/atlas/plugins/" + plugin_name + ".yml"; + for ( eckit::PathName plugins_search_path : plugins_search_paths ) { + eckit::PathName plugin_file = plugins_search_path / plugin_file_path; + if ( plugin_file.exists() ) { + eckit::PathName plugin_dir = plugins_search_path; + util::Config plugin_config{plugin_file}; + std::string library_name; + ATLAS_ASSERT( plugin_config.get( "library", library_name ) ); + bool library_loaded = eckit::system::Library::exists( library_name ); + if ( not library_loaded ) { + eckit::PathName library_dir = plugin_dir / "lib"; + ATLAS_ASSERT( library_exists( library_dir, library_name, library_path ) ); + Log::debug() << "Loading plugin [" << plugin_name << "] library " << library_path << std::endl; + load_library( library_dir, library_name ); + library_loaded = true; + } + ATLAS_ASSERT( is_plugin_loaded( plugin_name ) ); + plugin_loaded = true; + } + } + if ( not plugin_loaded ) { + std::ostringstream ss; + ss << "Plugin " << plugin_name << " could not be loaded as file " << plugin_file_path + << " could not be located within any of the ${ATLAS_PLUGINS_SEARCH_PATHS}"; + throw_Exception( ss.str() ); + } + } + + // Summary if ( getEnv( "ATLAS_LOG_RANK", 0 ) == int( mpi::rank() ) ) { std::ostream& out = Log::debug(); @@ -335,11 +459,17 @@ void Library::Information::print( std::ostream& out ) const { << " Tesselation : " << str( feature_Tesselation ) << '\n' << " ArrayDataStore : " << array_data_store << '\n' << " idx_t : " << ATLAS_BITS_LOCAL << " bit integer" << '\n' - << " gidx_t : " << ATLAS_BITS_GLOBAL << " bit integer" << '\n' - << " \n"; + << " gidx_t : " << ATLAS_BITS_GLOBAL << " bit integer" << '\n'; + + auto& plugins = Library::instance().plugins(); + if ( !plugins.empty() ) { + out << " \n Plugins: \n"; + for ( auto& plugin : plugins ) { + out << " " << str( *plugin ) << '\n'; + } + } - out << " Dependencies: " - << "\n"; + out << " \n Dependencies: \n"; if ( Library::exists( "eckit" ) ) { out << " " << str( Library::lookup( "eckit" ) ) << '\n'; diff --git a/src/atlas/library/Library.h b/src/atlas/library/Library.h index f1e6943a8..ca8e9d4fd 100644 --- a/src/atlas/library/Library.h +++ b/src/atlas/library/Library.h @@ -14,11 +14,13 @@ #include #include +#include "eckit/filesystem/PathName.h" #include "eckit/system/Library.h" namespace eckit { class Parametrisation; -} +class PathName; +} // namespace eckit namespace atlas { @@ -35,6 +37,7 @@ void finalise(); void finalize(); //---------------------------------------------------------------------------------------------------------------------- +class Plugin; class Library : public eckit::system::Library { public: @@ -83,6 +86,13 @@ class Library : public eckit::system::Library { mutable std::unique_ptr warning_channel_; mutable std::unique_ptr trace_channel_; mutable std::unique_ptr debug_channel_; + +private: + const std::vector& plugins() { return plugins_; } + + friend class Plugin; + void registerPlugin( Plugin& ); + std::vector plugins_; }; using Atlas = Library; diff --git a/src/atlas/library/Plugin.cc b/src/atlas/library/Plugin.cc new file mode 100644 index 000000000..306daab76 --- /dev/null +++ b/src/atlas/library/Plugin.cc @@ -0,0 +1,28 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include "Plugin.h" + +#include "atlas/library/Library.h" + +namespace atlas { + +//---------------------------------------------------------------------------------------------------------------------- + +Plugin::Plugin( const std::string& name, const std::string& libname ) : + eckit::system::Library( libname.size() ? libname : name ), + name_( name ), + libname_( libname.size() ? libname : name ) { + atlas::Library::instance().registerPlugin( *this ); +} + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace atlas diff --git a/src/atlas/library/Plugin.h b/src/atlas/library/Plugin.h new file mode 100644 index 000000000..eaed192e8 --- /dev/null +++ b/src/atlas/library/Plugin.h @@ -0,0 +1,34 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#pragma once + +#include + +#include "eckit/system/Library.h" + +namespace atlas { + +//---------------------------------------------------------------------------------------------------------------------- + +class Plugin : public eckit::system::Library { +public: + Plugin( const std::string& name, const std::string& libname = "" ); + const std::string& name() const { return name_; } + const std::string& libraryName() const { return libname_; } + +private: + std::string name_; + std::string libname_; +}; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace atlas From f66bd738084a69b138fa5005b0ffa3d2c8dcdc5a Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 19 Jun 2020 23:44:49 +0100 Subject: [PATCH 143/145] ATLAS-301 Support for ORCA grid patches at pole using quads --- .../detail/StructuredMeshGenerator.cc | 302 +++++++++++------- 1 file changed, 191 insertions(+), 111 deletions(-) diff --git a/src/atlas/meshgenerator/detail/StructuredMeshGenerator.cc b/src/atlas/meshgenerator/detail/StructuredMeshGenerator.cc index 77ec1005b..0b626aad4 100644 --- a/src/atlas/meshgenerator/detail/StructuredMeshGenerator.cc +++ b/src/atlas/meshgenerator/detail/StructuredMeshGenerator.cc @@ -7,7 +7,6 @@ * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ - #include #include #include @@ -78,6 +77,11 @@ StructuredMeshGenerator::StructuredMeshGenerator( const eckit::Parametrisation& options.set( "patch_pole", patch_pole ); } + bool patch_quads; + if ( p.get( "patch_quads", patch_quads ) ) { + options.set( "patch_quads", patch_quads ); + } + bool unique_pole; if ( p.get( "unique_pole", unique_pole ) ) { options.set( "unique_pole", unique_pole ); @@ -166,9 +170,8 @@ void StructuredMeshGenerator::configure_defaults() { // This option sets the part that will be generated options.set( "part", mpi::rank() ); - // Experimental option. The result is a non-standard Reduced Gaussian Grid, - // with a ragged Greenwich line - options.set( "stagger", false ); + // Experimental option. This is what makes tripolar grid patched with quads + options.set( "patch_quads", false ); // This option sets the maximum angle deviation for a quadrilateral element // angle = 30 --> minimises number of triangles @@ -335,7 +338,6 @@ We need to connect to next region array::ArrayView elemview = array::make_view( *region.elems ); elemview.assign( -1 ); - bool stagger = options.getBool( "stagger" ); for ( idx_t jlat = lat_north; jlat < lat_south; ++jlat ) { // std::stringstream filename; filename << "/tmp/debug/"<( rg.nx( latN ) ); - } - if ( stagger && ( latN + 1 ) % 2 == 0 ) { - xN2 += M_PI / static_cast( rg.nx( latN ) ); - } - if ( stagger && ( latS + 1 ) % 2 == 0 ) { - xS1 += M_PI / static_cast( rg.nx( latS ) ); - } - if ( stagger && ( latS + 1 ) % 2 == 0 ) { - xS2 += M_PI / static_cast( rg.nx( latS ) ); - } - #if DEBUG_OUTPUT Log::info() << "-------\n"; #endif @@ -811,16 +800,34 @@ void StructuredMeshGenerator::generate_mesh( const StructuredGrid& rg, const gri int mypart = options.getInt( "part" ); int nparts = options.getInt( "nb_parts" ); int n, l; + const int y_numbering = ( rg.y().front() < rg.y().back() ) ? +1 : -1; + double y_north = y_numbering < 0 ? rg.y().front() : rg.y().back(); + double y_south = y_numbering < 0 ? rg.y().back() : rg.y().front(); + idx_t nx_north = y_numbering < 0 ? rg.nx().front() : rg.nx().back(); + idx_t nx_south = y_numbering < 0 ? rg.nx().back() : rg.nx().front(); + + std::vector part_north( nx_north ); + std::vector part_south( nx_south ); + if ( y_numbering < 0 ) { + distribution.partition( 0, nx_north, part_north ); + distribution.partition( rg.size() - nx_south, rg.size(), part_south ); + } + else { + distribution.partition( 0, nx_south, part_south ); + distribution.partition( rg.size() - nx_north, rg.size(), part_north ); + } + bool mypart_at_north = std::any_of( part_north.begin(), part_north.end(), [&]( int p ) { return p == mypart; } ); + bool mypart_at_south = std::any_of( part_south.begin(), part_south.end(), [&]( int p ) { return p == mypart; } ); bool three_dimensional = options.getBool( "3d" ); bool periodic_east_west = rg.periodic(); bool include_periodic_ghost_points = periodic_east_west && !three_dimensional; bool remove_periodic_ghost_points = periodic_east_west && three_dimensional; - bool has_point_at_north_pole = rg.y().front() == 90 && rg.nx().front() > 0; - bool has_point_at_south_pole = rg.y().back() == -90 && rg.nx().back() > 0; - bool possible_north_pole = !has_point_at_north_pole && rg.domain().containsNorthPole() && ( mypart == 0 ); - bool possible_south_pole = !has_point_at_south_pole && rg.domain().containsSouthPole() && ( mypart == nparts - 1 ); + bool has_point_at_north_pole = y_north == 90 && nx_north > 0; + bool has_point_at_south_pole = y_south == -90 && nx_south > 0; + bool possible_north_pole = !has_point_at_north_pole && rg.domain().containsNorthPole() && mypart_at_north; + bool possible_south_pole = !has_point_at_south_pole && rg.domain().containsSouthPole() && mypart_at_south; bool force_include_north_pole( options.has( "force_include_north_pole" ) && options.getBool( "force_include_north_pole" ) ); @@ -829,8 +836,9 @@ void StructuredMeshGenerator::generate_mesh( const StructuredGrid& rg, const gri bool include_north_pole = ( possible_north_pole && options.getBool( "include_pole" ) ) || force_include_north_pole; bool include_south_pole = ( possible_south_pole && options.getBool( "include_pole" ) ) || force_include_south_pole; - bool patch_north_pole = possible_north_pole && options.getBool( "patch_pole" ) && rg.nx( 1 ) > 0; - bool patch_south_pole = possible_south_pole && options.getBool( "patch_pole" ) && rg.nx( rg.ny() - 2 ) > 0; + bool patch_north_pole = possible_north_pole && options.getBool( "patch_pole" ) && nx_north > 0; + bool patch_south_pole = possible_south_pole && options.getBool( "patch_pole" ) && nx_south > 0; + bool patch_quads = options.getBool( "patch_quads" ); int nnewnodes = ( !has_point_at_north_pole && include_north_pole ? 1 : 0 ) + ( !has_point_at_south_pole && include_south_pole ? 1 : 0 ); @@ -844,17 +852,27 @@ void StructuredMeshGenerator::generate_mesh( const StructuredGrid& rg, const gri if ( include_north_pole ) { ++nnodes; - ntriags += rg.nx( 0 ) - ( periodic_east_west ? 0 : 1 ); + ntriags += nx_north - ( periodic_east_west ? 0 : 1 ); } else if ( patch_north_pole ) { - ntriags += rg.nx( 0 ) - 2; + if ( patch_quads ) { + nquads += ( nx_north + 1 ) / 2; + } + else { + ntriags += nx_north - 2; + } } if ( include_south_pole ) { ++nnodes; - ntriags += rg.nx( rg.ny() - 1 ) - ( periodic_east_west ? 0 : 1 ); + ntriags += nx_south - ( periodic_east_west ? 0 : 1 ); } else if ( patch_south_pole ) { - ntriags += rg.nx( rg.ny() - 1 ) - 2; + if ( patch_quads ) { + nquads += ( nx_south + 1 ) / 2; + } + else { + ntriags += nx_south - 2; + } } size_t node_numbering_size = nnodes; @@ -920,7 +938,6 @@ void StructuredMeshGenerator::generate_mesh( const StructuredGrid& rg, const gri auto flags = array::make_view( nodes.flags() ); auto halo = array::make_view( nodes.halo() ); - bool stagger = options.getBool( "stagger" ); std::vector node_numbering( node_numbering_size, -1 ); if ( options.getBool( "ghost_at_end" ) ) { @@ -988,9 +1005,6 @@ void StructuredMeshGenerator::generate_mesh( const StructuredGrid& rg, const gri } } - const int y_numbering = ( rg.y().front() < rg.y().back() ) ? +1 : -1; - - idx_t jnode = 0; l = 0; for ( idx_t jlat = region.north; jlat <= region.south; ++jlat ) { @@ -1007,9 +1021,6 @@ void StructuredMeshGenerator::generate_mesh( const StructuredGrid& rg, const gri double x = rg.x( jlon, jlat ); // std::cout << "jlat = " << jlat << "; jlon = " << jlon << "; x = " << // x << std::endl; - if ( stagger && ( jlat + 1 ) % 2 == 0 ) { - x += 180. / static_cast( rg.nx( jlat ) ); - } xy( inode, XX ) = x; xy( inode, YY ) = y; @@ -1056,9 +1067,6 @@ void StructuredMeshGenerator::generate_mesh( const StructuredGrid& rg, const gri idx_t inode = node_numbering.at( jnode ); // int inode_left = node_numbering.at(jnode-1); double x = rg.x( rg.nx( jlat ), jlat ); - if ( stagger && ( jlat + 1 ) % 2 == 0 ) { - x += 180. / static_cast( rg.nx( jlat ) ); - } xy( inode, XX ) = x; xy( inode, YY ) = y; @@ -1307,58 +1315,94 @@ void StructuredMeshGenerator::generate_mesh( const StructuredGrid& rg, const gri } } else if ( patch_north_pole ) { - idx_t jlat = 0; - idx_t ilat = 0; - idx_t ip1, ip2, ip3; + idx_t jlat = y_numbering < 0 ? 0 : rg.ny() - 1; + idx_t ilat = y_numbering < 0 ? 0 : std::abs( region.south - region.north ); - idx_t jforward = 0; - idx_t jbackward = rg.nx( jlat ) - 1; - bool forward = true; + if ( patch_quads ) { + idx_t ip1, ip2, ip3, ip4; - while ( true ) { - if ( forward ) { + idx_t jforward = 0; + idx_t jbackward = rg.nx( jlat ) - 1; + + while ( true ) { ip1 = jforward; ip2 = jforward + 1; - ip3 = jbackward; - } - else { - ip1 = jforward; - ip2 = jbackward - 1; - ip3 = jbackward; - } - - triag_nodes[0] = node_numbering.at( offset_loc.at( ilat ) + ip1 ); - triag_nodes[1] = node_numbering.at( offset_loc.at( ilat ) + ip2 ); - triag_nodes[2] = node_numbering.at( offset_loc.at( ilat ) + ip3 ); - if ( y_numbering > 0 ) { - fix_triag_orientation( triag_nodes ); - } + ip3 = jbackward - 1; + ip4 = jbackward; - jcell = triag_begin + jtriag++; - node_connectivity.set( jcell, triag_nodes ); + quad_nodes[0] = node_numbering.at( offset_loc.at( ilat ) + ip1 ); + quad_nodes[1] = node_numbering.at( offset_loc.at( ilat ) + ip2 ); + quad_nodes[2] = node_numbering.at( offset_loc.at( ilat ) + ip3 ); + quad_nodes[3] = node_numbering.at( offset_loc.at( ilat ) + ip4 ); - cells_glb_idx( jcell ) = jcell + 1; - cells_part( jcell ) = mypart; - Topology::set( cells_flags( jcell ), Topology::PATCH ); + jcell = quad_begin + jquad++; + node_connectivity.set( jcell, quad_nodes ); - if ( jbackward == jforward + 2 ) { - break; - } + cells_glb_idx( jcell ) = jcell + 1; + cells_part( jcell ) = mypart; + Topology::set( cells_flags( jcell ), Topology::PATCH ); - if ( forward ) { + if ( jcell == cells_glb_idx.size() - 1 ) { + } + if ( jforward + 1 == jbackward ) { + break; + } ++jforward; - forward = false; - } - else { --jbackward; - forward = true; + } + } + else { + idx_t ip1, ip2, ip3; + + idx_t jforward = 0; + idx_t jbackward = rg.nx( jlat ) - 1; + bool forward = true; + + while ( true ) { + if ( forward ) { + ip1 = jforward; + ip2 = jforward + 1; + ip3 = jbackward; + } + else { + ip1 = jforward; + ip2 = jbackward - 1; + ip3 = jbackward; + } + + triag_nodes[0] = node_numbering.at( offset_loc.at( ilat ) + ip1 ); + triag_nodes[1] = node_numbering.at( offset_loc.at( ilat ) + ip2 ); + triag_nodes[2] = node_numbering.at( offset_loc.at( ilat ) + ip3 ); + // if ( y_numbering > 0 ) { + // fix_triag_orientation( triag_nodes ); + // } + + jcell = triag_begin + jtriag++; + node_connectivity.set( jcell, triag_nodes ); + + cells_glb_idx( jcell ) = jcell + 1; + cells_part( jcell ) = mypart; + Topology::set( cells_flags( jcell ), Topology::PATCH ); + + if ( jbackward == jforward + 2 ) { + break; + } + + if ( forward ) { + ++jforward; + forward = false; + } + else { + --jbackward; + forward = true; + } } } } if ( include_south_pole ) { - idx_t jlat = rg.ny() - 1; - idx_t ilat = region.south - region.north; + idx_t jlat = y_numbering > 0 ? 0 : rg.ny() - 1; + idx_t ilat = y_numbering > 0 ? 0 : std::abs( region.south - region.north ); idx_t ip1 = 0; idx_t nlon = rg.nx( jlat ) + 1 - ( periodic_east_west ? 0 : 1 ); for ( idx_t ip2 = 1; ip2 < nlon; ++ip2 ) { @@ -1380,51 +1424,87 @@ void StructuredMeshGenerator::generate_mesh( const StructuredGrid& rg, const gri } } else if ( patch_south_pole ) { - idx_t jlat = rg.ny() - 1; - idx_t ilat = region.south - region.north; - idx_t ip1, ip2, ip3; + idx_t jlat = y_numbering > 0 ? 0 : rg.ny() - 1; + idx_t ilat = y_numbering > 0 ? 0 : std::abs( region.south - region.north ); - idx_t jforward = 0; - idx_t jbackward = rg.nx( jlat ) - 1; - bool forward = true; + if ( patch_quads ) { + idx_t ip1, ip2, ip3, ip4; - while ( true ) { - if ( forward ) { - ip1 = jforward; - ip2 = jforward + 1; - ip3 = jbackward; - } - else { - ip1 = jforward; - ip2 = jbackward - 1; - ip3 = jbackward; - } + idx_t jforward = 0; + idx_t jbackward = rg.nx( jlat ) - 1; - triag_nodes[0] = node_numbering.at( offset_loc.at( ilat ) + ip1 ); - triag_nodes[1] = node_numbering.at( offset_loc.at( ilat ) + ip2 ); - triag_nodes[2] = node_numbering.at( offset_loc.at( ilat ) + ip3 ); - if ( y_numbering > 0 ) { - fix_triag_orientation( triag_nodes ); - } + while ( true ) { + ip1 = jbackward; + ip2 = jbackward - 1; + ip3 = jforward + 1; + ip4 = jforward; - jcell = triag_begin + jtriag++; - node_connectivity.set( jcell, triag_nodes ); + quad_nodes[0] = node_numbering.at( offset_loc.at( ilat ) + ip1 ); + quad_nodes[1] = node_numbering.at( offset_loc.at( ilat ) + ip2 ); + quad_nodes[2] = node_numbering.at( offset_loc.at( ilat ) + ip3 ); + quad_nodes[3] = node_numbering.at( offset_loc.at( ilat ) + ip4 ); - cells_glb_idx( jcell ) = jcell + 1; - cells_part( jcell ) = mypart; - Topology::set( cells_flags( jcell ), Topology::PATCH ); + jcell = quad_begin + jquad++; + node_connectivity.set( jcell, quad_nodes ); - if ( jbackward == jforward + 2 ) { - break; - } + cells_glb_idx( jcell ) = jcell + 1; + cells_part( jcell ) = mypart; + Topology::set( cells_flags( jcell ), Topology::PATCH ); - if ( forward ) { + if ( jcell == cells_glb_idx.size() - 1 ) { + } + if ( jforward + 1 == jbackward ) { + break; + } ++jforward; - forward = false; - } - else { --jbackward; - forward = true; + } + } + else { + idx_t ip1, ip2, ip3; + + idx_t jforward = 0; + idx_t jbackward = rg.nx( jlat ) - 1; + bool forward = true; + + while ( true ) { + if ( forward ) { + ip1 = jbackward; + ip2 = jforward + 1; + ip3 = jforward; + } + else { + ip1 = jbackward; + ip2 = jbackward - 1; + ip3 = jforward; + } + + triag_nodes[0] = node_numbering.at( offset_loc.at( ilat ) + ip1 ); + triag_nodes[1] = node_numbering.at( offset_loc.at( ilat ) + ip2 ); + triag_nodes[2] = node_numbering.at( offset_loc.at( ilat ) + ip3 ); + // if ( y_numbering > 0 ) { + // fix_triag_orientation( triag_nodes ); + // } + + jcell = triag_begin + jtriag++; + node_connectivity.set( jcell, triag_nodes ); + + cells_glb_idx( jcell ) = jcell + 1; + cells_part( jcell ) = mypart; + Topology::set( cells_flags( jcell ), Topology::PATCH ); + + if ( jbackward == jforward + 2 ) { + break; + } + + if ( forward ) { + ++jforward; + forward = false; + } + else { + --jbackward; + forward = true; + } } } } From 20b5d765a2a9291aca0cc60fb3731ef431d596a7 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 23 Jun 2020 16:27:52 +0100 Subject: [PATCH 144/145] ATLAS-300 Fix plugin install --- atlas-import.cmake.in | 2 +- src/atlas/library/Library.cc | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/atlas-import.cmake.in b/atlas-import.cmake.in index 3b7d60080..a67d91608 100644 --- a/atlas-import.cmake.in +++ b/atlas-import.cmake.in @@ -104,7 +104,7 @@ function( atlas_create_plugin name ) file( WRITE ${CMAKE_BINARY_DIR}/${_plugin_file} "name: ${name}\n" ) file( APPEND ${CMAKE_BINARY_DIR}/${_plugin_file} "version: ${_version}\n" ) file( APPEND ${CMAKE_BINARY_DIR}/${_plugin_file} "library: ${_library}" ) - install( FILES ${CMAKE_BINARY_DIR}/${_plugin_file} DESTINATION ${_plugin_file} ) + install( FILES ${CMAKE_BINARY_DIR}/${_plugin_file} DESTINATION share/atlas/plugins ) endfunction() diff --git a/src/atlas/library/Library.cc b/src/atlas/library/Library.cc index 2ee2213d1..d2fe5f9f2 100644 --- a/src/atlas/library/Library.cc +++ b/src/atlas/library/Library.cc @@ -240,11 +240,7 @@ void Library::initialise( const eckit::Parametrisation& config ) { std::vector plugins_search_paths = eckit::Resource>( "atlasPluginsSearchPaths;$ATLAS_PLUGINS_SEARCH_PATHS", {} ); - plugins_search_paths.push_back( "~atlas" ); - - for ( eckit::PathName p : plugins_search_paths ) { - Log::info() << "plugins search path " << p.dirName() << std::endl; - } + plugins_search_paths.emplace_back( "~atlas" ); auto is_plugin_loaded = [&]( const std::string& name ) { for ( auto& plugin : plugins() ) { From 5f98d112c7430f9f79bf3ca0391d5fdd9b2c583d Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 23 Jun 2020 00:58:39 +0100 Subject: [PATCH 145/145] Version 0.21.0 --- CHANGELOG.md | 41 ++++++++++++++++++++++++++++++++++++++--- VERSION | 2 +- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87cd633a8..ba31ff033 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,11 +7,45 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html ## [Unreleased] -## [0.20.2] - 2019-04-27 +## [0.21.0] - 2020-06-23 +### Fixed +- Fixed Rotation order of applying the rotation angle +- Fix nabla computation of surface vector fields +- Fix registration and destruction issues of halo-exchange caches +- Workaround Clang compiler problem with OpenMP tasking, using introspection +- Fix bug in conversion of negative degrees to microdegrees +- Fix problem in distribution of work amongst OpenMP threads in StructuredColumns::setup +- Fix problem with StructuredColumns creation for grids defined with domains with negative West +- Fix computation of Grid::lonlatBoundingBox for arbitrary projections crossing the dateline. + +### Changed +- Snap LinearSpacing values to start and endpoint to allow exact comparisons +- Improved performance and memory requirement of cropping of large StructuredGrids +- Regional grids by default now have a positive y-numbering (previously negative). + +### Added +- PolygonLocator based on functionspace partition polygons. +- KDTree class which abstracts eckit concrete implementations, including Fortran interface +- Geometry class with factory mechanism to configure which spheroid to use, including Fortran interface +- StrcutredGrid function for i,j to index and inverse +- Fortran interface to create StructuredColumns with custom distribution +- Fortran interface to grid spec +- Fortran interface to Projection +- Adjoint of HaloExchange +- Plugin mechanism to load plugins at runtime. +- Fortran constructors for atlas_RegionalGrid +- Fortran constructors for projected reduced Gaussian grids +- Add copy constructor and assignment operator for atlas::vector +- Mercator projection support for scaling, and operation on ellipsoid. +- Grid Distribution can now also be created as a function, e.g. for Serial or Bands + + + +## [0.20.2] - 2020-04-27 ### Fixed - Avoid 100ds of compilation warnings introduced in version 0.20.0 -## [0.20.1] - 2019-04-08 +## [0.20.1] - 2020-04-08 ### Fixed - Make feature BOUNDSCHECKING work again. It was not turned on for DEBUG builds - Workaround clang OpenMP bug @@ -20,7 +54,7 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html ### Added - atlas-grids tool can now be used to compute approximate North-South grid resolution -## [0.20.0] - 2019-03-06 +## [0.20.0] - 2020-03-06 ### Fixed - Pole edges hould not be created for global regular grids with points at poles - Update compatibility with more recent GridTools @@ -189,6 +223,7 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html ## 0.13.0 - 2018-02-16 [Unreleased]: https://github.com/ecmwf/atlas/compare/master...develop +[0.21.0]: https://github.com/ecmwf/atlas/compare/0.20.2...0.21.0 [0.20.2]: https://github.com/ecmwf/atlas/compare/0.20.1...0.20.2 [0.20.1]: https://github.com/ecmwf/atlas/compare/0.20.0...0.20.1 [0.20.0]: https://github.com/ecmwf/atlas/compare/0.20.0...0.19.2 diff --git a/VERSION b/VERSION index 727d97b9b..885415662 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.20.2 +0.21.0