From 2ea2612395813cbf87472a0c37d3dc0e8fae85d0 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 2 Sep 2019 10:32:06 +0100 Subject: [PATCH 01/40] Increase tolerance in failing test for intel/17 compiler --- .../interpolation/test_interpolation_cubic_prototype.cc | 8 +++++--- tools/apply-clang-format.sh | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/tests/interpolation/test_interpolation_cubic_prototype.cc b/src/tests/interpolation/test_interpolation_cubic_prototype.cc index 37a27b965..0bb532e74 100644 --- a/src/tests/interpolation/test_interpolation_cubic_prototype.cc +++ b/src/tests/interpolation/test_interpolation_cubic_prototype.cc @@ -242,6 +242,8 @@ CASE( "test horizontal cubic interpolation triplets" ) { //----------------------------------------------------------------------------- CASE( "test 3d cubic interpolation" ) { + const double tolerance = 1.e-15; + //if ( mpi::comm().size() == 1 ) { std::string gridname = eckit::Resource( "--grid", "O8" ); idx_t nlev = 11; @@ -314,7 +316,7 @@ CASE( "test 3d cubic interpolation" ) { double interpolated = cubic_interpolation( p, f ); double exact = fp( p ); Log::info() << p << " --> " << interpolated << " [exact] " << exact << std::endl; - EXPECT( is_approximately_equal( interpolated, exact ) ); + EXPECT( is_approximately_equal( interpolated, exact, tolerance ) ); } } @@ -332,7 +334,7 @@ CASE( "test 3d cubic interpolation" ) { double interpolated = output_view( n++ ); double exact = fp( p ); Log::info() << p << " --> " << interpolated << " [exact] " << exact << std::endl; - EXPECT( is_approximately_equal( interpolated, exact ) ); + EXPECT( is_approximately_equal( interpolated, exact, tolerance ) ); } } @@ -379,7 +381,7 @@ CASE( "test 3d cubic interpolation" ) { double interpolated = output_view( n, k ); double exact = fp( p ); Log::info() << p << " --> " << interpolated << " [exact] " << exact << std::endl; - EXPECT( is_approximately_equal( interpolated, exact ) ); + EXPECT( is_approximately_equal( interpolated, exact, tolerance ) ); } } } diff --git a/tools/apply-clang-format.sh b/tools/apply-clang-format.sh index 4b01a8162..f3dfd6a7c 100755 --- a/tools/apply-clang-format.sh +++ b/tools/apply-clang-format.sh @@ -10,6 +10,8 @@ _REQUIRED_CLANG_VERSION='7.0.1' +export PATH=/usr/local/apps/clang/7.0.1/bin:$PATH + if ! [ -x "$(command -v clang-format)" ]; then echo 'Error: clang-format is not installed.' >&2 exit 1 From 2d3294185f9b71fa52d52ebde6eb60be1fad4c6d Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 2 Sep 2019 10:55:38 +0100 Subject: [PATCH 02/40] clang-tidy: Add more readability braces --- cmake/FeatureClangTidy.cmake | 6 +- src/atlas/functionspace/Spectral.cc | 21 ++- .../detail/partitioner/TransPartitioner.cc | 6 +- src/atlas/trans/ifs/TransIFS.cc | 129 ++++++++++++------ src/tests/functionspace/test_functionspace.cc | 9 +- src/tests/trans/test_trans.cc | 24 ++-- src/tests/trans/test_trans_invtrans_grad.cc | 3 +- 7 files changed, 133 insertions(+), 65 deletions(-) diff --git a/cmake/FeatureClangTidy.cmake b/cmake/FeatureClangTidy.cmake index 8a1c1c98c..b744363c1 100644 --- a/cmake/FeatureClangTidy.cmake +++ b/cmake/FeatureClangTidy.cmake @@ -21,6 +21,10 @@ ecbuild_add_option( FEATURE CLANG_TIDY CONDITION CLANG_TIDY_EXE ) if (HAVE_CLANG_TIDY) + + # Uncomment to apply fixes. Make sure to use a clean build, and apply clang-format afterwards! + # set( CLANG_TIDY_FIXIT ";-fix" ) + set(CLANG_TIDY_CHECKS "-*,readability-braces-around-statements,redundant-string-init") - set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXE};-checks=${CLANG_TIDY_CHECKS};-header-filter='${CMAKE_SOURCE_DIR}/*'") + set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXE};-checks=${CLANG_TIDY_CHECKS};-header-filter='${CMAKE_SOURCE_DIR}/*'${CLANG_TIDY_FIXIT}") endif() diff --git a/src/atlas/functionspace/Spectral.cc b/src/atlas/functionspace/Spectral.cc index c176bca21..584f33e92 100644 --- a/src/atlas/functionspace/Spectral.cc +++ b/src/atlas/functionspace/Spectral.cc @@ -64,20 +64,23 @@ class Spectral::Parallelisation { int nump() const { return trans_->nump; } array::LocalView nvalue() const { - if ( trans_->nvalue == nullptr ) + if ( trans_->nvalue == nullptr ) { ::trans_inquire( trans_.get(), "nvalue" ); + } return array::LocalView( trans_->nvalue, array::make_shape( trans_->nspec2 ) ); } array::LocalView nmyms() const { - if ( trans_->nmyms == nullptr ) + if ( trans_->nmyms == nullptr ) { ::trans_inquire( trans_.get(), "nmyms" ); + } return array::LocalView( trans_->nmyms, array::make_shape( nump() ) ); } array::LocalView nasm0() const { - if ( trans_->nasm0 == nullptr ) + if ( trans_->nasm0 == nullptr ) { ::trans_inquire( trans_.get(), "nasm0" ); + } return array::LocalView( trans_->nasm0, array::make_shape( trans_->nsmax + 1 ) ); } @@ -268,13 +271,15 @@ void Spectral::gather( const FieldSet& local_fieldset, FieldSet& global_fieldset idx_t rank = static_cast( mpi::comm().rank() ); glb.metadata().get( "owner", root ); ATLAS_ASSERT( loc.shape( 0 ) == nb_spectral_coefficients() ); - if ( rank == root ) + if ( rank == root ) { ATLAS_ASSERT( glb.shape( 0 ) == nb_spectral_coefficients_global() ); + } std::vector nto( 1, root + 1 ); if ( loc.rank() > 1 ) { nto.resize( loc.stride( 0 ) ); - for ( size_t i = 0; i < nto.size(); ++i ) + for ( size_t i = 0; i < nto.size(); ++i ) { nto[i] = root + 1; + } } if ( not loc.contiguous() ) { @@ -323,13 +328,15 @@ void Spectral::scatter( const FieldSet& global_fieldset, FieldSet& local_fieldse glb.metadata().get( "owner", root ); ATLAS_ASSERT( loc.shape( 0 ) == nb_spectral_coefficients() ); - if ( rank == root ) + if ( rank == root ) { ATLAS_ASSERT( glb.shape( 0 ) == nb_spectral_coefficients_global() ); + } std::vector nfrom( 1, root + 1 ); if ( loc.rank() > 1 ) { nfrom.resize( loc.stride( 0 ) ); - for ( size_t i = 0; i < nfrom.size(); ++i ) + for ( size_t i = 0; i < nfrom.size(); ++i ) { nfrom[i] = root + 1; + } } if ( not loc.contiguous() ) { diff --git a/src/atlas/grid/detail/partitioner/TransPartitioner.cc b/src/atlas/grid/detail/partitioner/TransPartitioner.cc index 40bbc65c4..7bebe1e1f 100644 --- a/src/atlas/grid/detail/partitioner/TransPartitioner.cc +++ b/src/atlas/grid/detail/partitioner/TransPartitioner.cc @@ -48,8 +48,9 @@ void TransPartitioner::partition( const Grid& grid, int part[] ) const { ATLAS_TRACE( "TransPartitioner::partition" ); StructuredGrid g( grid ); - if ( not g ) + if ( not g ) { throw_Exception( "Grid is not a grid::Structured type. Cannot partition using IFS trans", Here() ); + } trans::TransIFS t( grid ); if ( nb_partitions() != idx_t( t.nproc() ) ) { @@ -91,8 +92,9 @@ void TransPartitioner::partition( const Grid& grid, int part[] ) const { int igl = nptrfrstlat( ja ) + jgl - nfrstlat( ja ); for ( int jl = nsta( jb, igl ) - 1; jl < nsta( jb, igl ) + nonl( jb, igl ) - 1; ++jl ) { idx_t ind = iglobal[jgl * nlonmax + jl] - 1; - if ( ind >= grid.size() ) + if ( ind >= grid.size() ) { throw_OutOfRange( "part", ind, grid.size(), Here() ); + } part[ind] = iproc; } } diff --git a/src/atlas/trans/ifs/TransIFS.cc b/src/atlas/trans/ifs/TransIFS.cc index 28b1be16d..dae13cdb5 100644 --- a/src/atlas/trans/ifs/TransIFS.cc +++ b/src/atlas/trans/ifs/TransIFS.cc @@ -90,8 +90,9 @@ namespace { std::string fieldset_functionspace( const FieldSet& fields ) { std::string functionspace( "undefined" ); for ( idx_t jfld = 0; jfld < fields.size(); ++jfld ) { - if ( functionspace == "undefined" ) + if ( functionspace == "undefined" ) { functionspace = fields[jfld].functionspace().type(); + } if ( fields[jfld].functionspace().type() != functionspace ) { throw_Exception( ": fielset has fields with different functionspaces", Here() ); } @@ -369,8 +370,9 @@ struct PackNodeColumns { } void pack_3( const Field& field, idx_t components ) { const ArrayView gpfield = make_view( field ); - if ( not components ) + if ( not components ) { components = gpfield.shape( 2 ); + } for ( idx_t jcomp = 0; jcomp < components; ++jcomp ) { for ( idx_t jlev = 0; jlev < gpfield.shape( 1 ); ++jlev ) { idx_t n = 0; @@ -527,8 +529,9 @@ struct UnpackNodeColumns { } void unpack_3( Field& field, idx_t components ) { ArrayView gpfield = make_view( field ); - if ( not components ) + if ( not components ) { components = gpfield.shape( 2 ); + } for ( idx_t jcomp = 0; jcomp < components; ++jcomp ) { for ( idx_t jlev = 0; jlev < gpfield.shape( 1 ); ++jlev ) { idx_t n = 0; @@ -738,109 +741,125 @@ const int* atlas::trans::TransIFS::n_regions( int& size ) const { const int* atlas::trans::TransIFS::nfrstlat( int& size ) const { size = trans_->n_regions_NS; - if ( trans_->nfrstlat == nullptr ) + if ( trans_->nfrstlat == nullptr ) { ::trans_inquire( trans_.get(), "nfrstlat" ); + } return trans_->nfrstlat; } const int* atlas::trans::TransIFS::nlstlat( int& size ) const { size = trans_->n_regions_NS; - if ( trans_->nlstlat == nullptr ) + if ( trans_->nlstlat == nullptr ) { ::trans_inquire( trans_.get(), "nlstlat" ); + } return trans_->nlstlat; } const int* atlas::trans::TransIFS::nptrfrstlat( int& size ) const { size = trans_->n_regions_NS; - if ( trans_->nptrfrstlat == nullptr ) + if ( trans_->nptrfrstlat == nullptr ) { ::trans_inquire( trans_.get(), "nptrfrstlat" ); + } return trans_->nptrfrstlat; } const int* atlas::trans::TransIFS::nsta( int& sizef2, int& sizef1 ) const { sizef1 = trans_->ndgl + trans_->n_regions_NS - 1; sizef2 = trans_->n_regions_EW; - if ( trans_->nsta == nullptr ) + if ( trans_->nsta == nullptr ) { ::trans_inquire( trans_.get(), "nsta" ); + } return trans_->nsta; } const int* atlas::trans::TransIFS::nonl( int& sizef2, int& sizef1 ) const { sizef1 = trans_->ndgl + trans_->n_regions_NS - 1; sizef2 = trans_->n_regions_EW; - if ( trans_->nonl == nullptr ) + if ( trans_->nonl == nullptr ) { ::trans_inquire( trans_.get(), "nonl" ); + } return trans_->nonl; } const int* atlas::trans::TransIFS::nmyms( int& size ) const { size = trans_->nump; - if ( trans_->nmyms == nullptr ) + if ( trans_->nmyms == nullptr ) { ::trans_inquire( trans_.get(), "nmyms" ); + } return trans_->nmyms; } const int* atlas::trans::TransIFS::nasm0( int& size ) const { size = trans_->nsmax + 1; // +1 because zeroth wave included - if ( trans_->nasm0 == nullptr ) + if ( trans_->nasm0 == nullptr ) { ::trans_inquire( trans_.get(), "nasm0" ); + } return trans_->nasm0; } const int* atlas::trans::TransIFS::nvalue( int& size ) const { size = trans_->nspec2; - if ( trans_->nvalue == nullptr ) + if ( trans_->nvalue == nullptr ) { ::trans_inquire( trans_.get(), "nvalue" ); + } return trans_->nvalue; } array::LocalView atlas::trans::TransIFS::nvalue() const { - if ( trans_->nvalue == nullptr ) + if ( trans_->nvalue == nullptr ) { ::trans_inquire( trans_.get(), "nvalue" ); + } return array::LocalView( trans_->nvalue, array::make_shape( trans_->nspec2 ) ); } array::LocalView atlas::trans::TransIFS::nasm0() const { - if ( trans_->nasm0 == nullptr ) + if ( trans_->nasm0 == nullptr ) { ::trans_inquire( trans_.get(), "nasm0" ); + } return array::LocalView( trans_->nasm0, array::make_shape( trans_->nsmax + 1 ) ); } array::LocalView atlas::trans::TransIFS::nmyms() const { - if ( trans_->nmyms == nullptr ) + if ( trans_->nmyms == nullptr ) { ::trans_inquire( trans_.get(), "nmyms" ); + } return array::LocalView( trans_->nmyms, array::make_shape( trans_->nump ) ); } array::LocalView atlas::trans::TransIFS::nonl() const { - if ( trans_->nonl == nullptr ) + if ( trans_->nonl == nullptr ) { ::trans_inquire( trans_.get(), "nonl" ); + } return array::LocalView( trans_->nonl, array::make_shape( trans_->n_regions_EW, trans_->ndgl + trans_->n_regions_NS - 1 ) ); } array::LocalView atlas::trans::TransIFS::nsta() const { - if ( trans_->nsta == nullptr ) + if ( trans_->nsta == nullptr ) { ::trans_inquire( trans_.get(), "nsta" ); + } return array::LocalView( trans_->nsta, array::make_shape( trans_->n_regions_EW, trans_->ndgl + trans_->n_regions_NS - 1 ) ); } array::LocalView atlas::trans::TransIFS::nptrfrstlat() const { - if ( trans_->nptrfrstlat == nullptr ) + if ( trans_->nptrfrstlat == nullptr ) { ::trans_inquire( trans_.get(), "nptrfrstlat" ); + } return array::LocalView( trans_->nptrfrstlat, array::make_shape( trans_->n_regions_NS ) ); } array::LocalView atlas::trans::TransIFS::nlstlat() const { - if ( trans_->nlstlat == nullptr ) + if ( trans_->nlstlat == nullptr ) { ::trans_inquire( trans_.get(), "nlstlat" ); + } return array::LocalView( trans_->nlstlat, array::make_shape( trans_->n_regions_NS ) ); } array::LocalView atlas::trans::TransIFS::nfrstlat() const { - if ( trans_->nfrstlat == nullptr ) + if ( trans_->nfrstlat == nullptr ) { ::trans_inquire( trans_.get(), "nfrstlat" ); + } return array::LocalView( trans_->nfrstlat, array::make_shape( trans_->n_regions_NS ) ); } @@ -910,13 +929,15 @@ void TransIFS::ctor( const Grid& grid, long truncation, const eckit::Configurati void TransIFS::ctor_rgg( const long nlat, const idx_t pl[], long truncation, const eckit::Configuration& config ) { TransParameters p( *this, config ); std::vector nloen( nlat ); - for ( long jlat = 0; jlat < nlat; ++jlat ) + for ( long jlat = 0; jlat < nlat; ++jlat ) { nloen[jlat] = pl[jlat]; + } TRANS_CHECK( ::trans_new( trans_.get() ) ); TRANS_CHECK( ::trans_use_mpi( mpi::comm().size() > 1 ) ); TRANS_CHECK( ::trans_set_resol( trans_.get(), nlat, nloen.data() ) ); - if ( truncation >= 0 ) + if ( truncation >= 0 ) { TRANS_CHECK( ::trans_set_trunc( trans_.get(), truncation ) ); + } TRANS_CHECK( ::trans_set_cache( trans_.get(), cache_, cachesize_ ) ); @@ -945,8 +966,9 @@ void TransIFS::ctor_lonlat( const long nlon, const long nlat, long truncation, c TRANS_CHECK( ::trans_new( trans_.get() ) ); TRANS_CHECK( ::trans_use_mpi( mpi::comm().size() > 1 ) ); TRANS_CHECK( ::trans_set_resol_lonlat( trans_.get(), nlon, nlat ) ); - if ( truncation >= 0 ) + if ( truncation >= 0 ) { TRANS_CHECK( ::trans_set_trunc( trans_.get(), truncation ) ); + } TRANS_CHECK( ::trans_set_cache( trans_.get(), cache_, cachesize_ ) ); if ( p.read_legendre().size() && mpi::comm().size() == 1 ) { @@ -1004,8 +1026,9 @@ void TransIFS::__dirtrans( const functionspace::NodeColumns& gp, const FieldSet& // Pack gridpoints { PackNodeColumns pack( rgpview, gp ); - for ( idx_t jfld = 0; jfld < gpfields.size(); ++jfld ) + for ( idx_t jfld = 0; jfld < gpfields.size(); ++jfld ) { pack( gpfields[jfld] ); + } } // Do transform @@ -1020,8 +1043,9 @@ void TransIFS::__dirtrans( const functionspace::NodeColumns& gp, const FieldSet& // Unpack the spectral fields { UnpackSpectral unpack( rspview ); - for ( idx_t jfld = 0; jfld < spfields.size(); ++jfld ) + for ( idx_t jfld = 0; jfld < spfields.size(); ++jfld ) { unpack( spfields[jfld] ); + } } } @@ -1097,8 +1121,9 @@ void TransIFS::__dirtrans( const StructuredColumns& gp, const FieldSet& gpfields // Pack gridpoints { PackStructuredColumns pack( rgpview ); - for ( idx_t jfld = 0; jfld < gpfields.size(); ++jfld ) + for ( idx_t jfld = 0; jfld < gpfields.size(); ++jfld ) { pack( gpfields[jfld] ); + } } // Do transform @@ -1114,8 +1139,9 @@ void TransIFS::__dirtrans( const StructuredColumns& gp, const FieldSet& gpfields // Unpack the spectral fields { UnpackSpectral unpack( rspview ); - for ( idx_t jfld = 0; jfld < spfields.size(); ++jfld ) + for ( idx_t jfld = 0; jfld < spfields.size(); ++jfld ) { unpack( spfields[jfld] ); + } } } @@ -1138,11 +1164,12 @@ void TransIFS::__invtrans_grad( const Spectral& sp, const FieldSet& spfields, co const int nb_gridpoint_field = compute_nfld( gradfields ); const int nfld = compute_nfld( spfields ); - if ( nb_gridpoint_field != 2 * nfld ) // factor 2 because N-S and E-W derivatives + if ( nb_gridpoint_field != 2 * nfld ) { // factor 2 because N-S and E-W derivatives throw_Exception( "invtrans_grad: different number of gridpoint " "fields than spectral fields", Here() ); + } // Arrays Trans expects // Allocate space for @@ -1155,8 +1182,9 @@ void TransIFS::__invtrans_grad( const Spectral& sp, const FieldSet& spfields, co // Pack spectral fields { PackSpectral pack( rspview ); - for ( idx_t jfld = 0; jfld < spfields.size(); ++jfld ) + for ( idx_t jfld = 0; jfld < spfields.size(); ++jfld ) { pack( spfields[jfld] ); + } } // Do transform @@ -1230,8 +1258,9 @@ void TransIFS::__invtrans( const Spectral& sp, const FieldSet& spfields, const f const int nfld = compute_nfld( gpfields ); const int nb_spectral_fields = compute_nfld( spfields ); - if ( nfld != nb_spectral_fields ) + if ( nfld != nb_spectral_fields ) { throw_Exception( "invtrans: different number of gridpoint fields than spectral fields", Here() ); + } // Arrays Trans expects std::vector rgp( nfld * ngptot() ); @@ -1242,8 +1271,9 @@ void TransIFS::__invtrans( const Spectral& sp, const FieldSet& spfields, const f // Pack spectral fields { PackSpectral pack( rspview ); - for ( idx_t jfld = 0; jfld < spfields.size(); ++jfld ) + for ( idx_t jfld = 0; jfld < spfields.size(); ++jfld ) { pack( spfields[jfld] ); + } } // Do transform @@ -1258,8 +1288,9 @@ void TransIFS::__invtrans( const Spectral& sp, const FieldSet& spfields, const f // Unpack the gridpoint fields { UnpackNodeColumns unpack( rgpview, gp ); - for ( idx_t jfld = 0; jfld < gpfields.size(); ++jfld ) + for ( idx_t jfld = 0; jfld < gpfields.size(); ++jfld ) { unpack( gpfields[jfld] ); + } } } @@ -1342,8 +1373,9 @@ void TransIFS::__invtrans( const functionspace::Spectral& sp, const FieldSet& sp // Pack spectral fields { PackSpectral pack( rspview ); - for ( idx_t jfld = 0; jfld < spfields.size(); ++jfld ) + for ( idx_t jfld = 0; jfld < spfields.size(); ++jfld ) { pack( spfields[jfld] ); + } } // Do transform @@ -1359,8 +1391,9 @@ void TransIFS::__invtrans( const functionspace::Spectral& sp, const FieldSet& sp // Unpack the gridpoint fields { UnpackStructuredColumns unpack( rgpview ); - for ( idx_t jfld = 0; jfld < gpfields.size(); ++jfld ) + for ( idx_t jfld = 0; jfld < gpfields.size(); ++jfld ) { unpack( gpfields[jfld] ); + } } } @@ -1372,13 +1405,16 @@ void TransIFS::__dirtrans_wind2vordiv( const functionspace::NodeColumns& gp, con // Count total number of fields and do sanity checks const size_t nfld = compute_nfld( spvor ); - if ( spdiv.shape( 0 ) != spvor.shape( 0 ) ) + if ( spdiv.shape( 0 ) != spvor.shape( 0 ) ) { throw_Exception( "invtrans: vorticity not compatible with divergence.", Here() ); - if ( spdiv.shape( 1 ) != spvor.shape( 1 ) ) + } + if ( spdiv.shape( 1 ) != spvor.shape( 1 ) ) { throw_Exception( "invtrans: vorticity not compatible with divergence.", Here() ); + } const size_t nwindfld = compute_nfld( gpwind ); - if ( nwindfld != 2 * nfld && nwindfld != 3 * nfld ) + if ( nwindfld != 2 * nfld && nwindfld != 3 * nfld ) { throw_Exception( "dirtrans: wind field is not compatible with vorticity, divergence.", Here() ); + } if ( spdiv.shape( 0 ) != nspec2() ) { std::stringstream msg; @@ -1388,10 +1424,12 @@ void TransIFS::__dirtrans_wind2vordiv( const functionspace::NodeColumns& gp, con throw_Exception( msg.str(), Here() ); } - if ( spvor.size() == 0 ) + if ( spvor.size() == 0 ) { throw_Exception( "dirtrans: spectral vorticity field is empty." ); - if ( spdiv.size() == 0 ) + } + if ( spdiv.size() == 0 ) { throw_Exception( "dirtrans: spectral divergence field is empty." ); + } // Arrays Trans expects std::vector rgp( 2 * nfld * ngptot() ); @@ -1435,13 +1473,16 @@ void TransIFS::__invtrans_vordiv2wind( const Spectral& sp, const Field& spvor, c // Count total number of fields and do sanity checks const int nfld = compute_nfld( spvor ); - if ( spdiv.shape( 0 ) != spvor.shape( 0 ) ) + if ( spdiv.shape( 0 ) != spvor.shape( 0 ) ) { throw_Exception( "invtrans: vorticity not compatible with divergence.", Here() ); - if ( spdiv.shape( 1 ) != spvor.shape( 1 ) ) + } + if ( spdiv.shape( 1 ) != spvor.shape( 1 ) ) { throw_Exception( "invtrans: vorticity not compatible with divergence.", Here() ); + } const int nwindfld = compute_nfld( gpwind ); - if ( nwindfld != 2 * nfld && nwindfld != 3 * nfld ) + if ( nwindfld != 2 * nfld && nwindfld != 3 * nfld ) { throw_Exception( "invtrans: wind field is not compatible with vorticity, divergence.", Here() ); + } if ( spdiv.shape( 0 ) != nspec2() ) { std::stringstream msg; @@ -1453,10 +1494,12 @@ void TransIFS::__invtrans_vordiv2wind( const Spectral& sp, const Field& spvor, c ATLAS_ASSERT( spvor.rank() == 2 ); ATLAS_ASSERT( spdiv.rank() == 2 ); - if ( spvor.size() == 0 ) + if ( spvor.size() == 0 ) { throw_Exception( "invtrans: spectral vorticity field is empty." ); - if ( spdiv.size() == 0 ) + } + if ( spdiv.size() == 0 ) { throw_Exception( "invtrans: spectral divergence field is empty." ); + } // Arrays Trans expects std::vector rgp( 2 * nfld * ngptot() ); diff --git a/src/tests/functionspace/test_functionspace.cc b/src/tests/functionspace/test_functionspace.cc index 6356d73cb..e110e5c9c 100644 --- a/src/tests/functionspace/test_functionspace.cc +++ b/src/tests/functionspace/test_functionspace.cc @@ -557,8 +557,9 @@ CASE( "test_SpectralFunctionSpace_trans_global" ) { EXPECT( surface_scalar_field.name() == std::string( "scalar" ) ); - if ( eckit::mpi::comm().rank() == 0 ) + if ( eckit::mpi::comm().rank() == 0 ) { EXPECT( surface_scalar_field.size() == nspec2g ); + } EXPECT( surface_scalar_field.rank() == 1 ); @@ -604,14 +605,16 @@ CASE( "test_SpectralFunctionSpace_norm" ) { { auto twoD = array::make_view( twoD_field ); twoD.assign( 0. ); - if ( mpi::comm().rank() == 0 ) + if ( mpi::comm().rank() == 0 ) { twoD( 0 ) = 1.; + } auto threeD = array::make_view( threeD_field ); threeD.assign( 0. ); for ( size_t jlev = 0; jlev < nb_levels; ++jlev ) { - if ( mpi::comm().rank() == 0 ) + if ( mpi::comm().rank() == 0 ) { threeD( 0, jlev ) = jlev; + } } } diff --git a/src/tests/trans/test_trans.cc b/src/tests/trans/test_trans.cc index 75693a9f3..349e9fb2e 100644 --- a/src/tests/trans/test_trans.cc +++ b/src/tests/trans/test_trans.cc @@ -54,8 +54,9 @@ namespace test { struct AtlasTransEnvironment : public AtlasTestEnvironment { AtlasTransEnvironment( int argc, char* argv[] ) : AtlasTestEnvironment( argc, argv ) { - if ( mpi::comm().size() == 1 ) + if ( mpi::comm().size() == 1 ) { trans_use_mpi( false ); + } trans_init(); } @@ -75,8 +76,9 @@ void read_rspecg( const trans::TransImpl& trans, std::vector& rspecg, st } } nfrom.resize( nfld ); - for ( int jfld = 0; jfld < nfld; ++jfld ) + for ( int jfld = 0; jfld < nfld; ++jfld ) { nfrom[jfld] = 1; + } Log::info() << "read_rspecg ... done" << std::endl; } @@ -129,8 +131,9 @@ CASE( "test_trans_distribution_matches_atlas" ) { if ( mpi::comm().rank() == 0 ) // all tasks do the same, so only one needs to check { int max_nb_regions_EW( 0 ); - for ( int j = 0; j < trans_partitioner->nb_bands(); ++j ) + for ( int j = 0; j < trans_partitioner->nb_bands(); ++j ) { max_nb_regions_EW = std::max( max_nb_regions_EW, trans_partitioner->nb_regions( j ) ); + } EXPECT( t->n_regions_NS == trans_partitioner->nb_bands() ); EXPECT( t->n_regions_EW == max_nb_regions_EW ); @@ -140,16 +143,18 @@ CASE( "test_trans_distribution_matches_atlas" ) { std::vector npts( distribution.nb_partitions(), 0 ); - for ( idx_t j = 0; j < g.size(); ++j ) + for ( idx_t j = 0; j < g.size(); ++j ) { ++npts[distribution.partition( j )]; + } EXPECT( t->ngptotg == g.size() ); EXPECT( t->ngptot == npts[mpi::comm().rank()] ); EXPECT( t->ngptotmx == *std::max_element( npts.begin(), npts.end() ) ); // array::LocalView n_regions ( trans.n_regions() ) ; - for ( int j = 0; j < trans_partitioner->nb_bands(); ++j ) + for ( int j = 0; j < trans_partitioner->nb_bands(); ++j ) { EXPECT( t->n_regions[j] == trans_partitioner->nb_regions( j ) ); + } } } @@ -549,8 +554,9 @@ CASE( "test_trans_VorDivToUV" ) { // TODO: initialise field_vor and field_div with something meaningful field_vor[2 * nfld] = 1.; Log::info() << "vor: " << std::endl; - for ( int j = 0; j < nfld * nspec2; j++ ) + for ( int j = 0; j < nfld * nspec2; j++ ) { Log::info() << field_vor[j] << " "; + } Log::info() << std::endl; // With IFS @@ -566,8 +572,9 @@ CASE( "test_trans_VorDivToUV" ) { // TODO: do some meaningful checks Log::info() << "Trans library" << std::endl; Log::info() << "U: " << std::endl; - for ( int j = 0; j < nfld * nspec2; j++ ) + for ( int j = 0; j < nfld * nspec2; j++ ) { Log::info() << field_U[j] << " "; + } Log::info() << std::endl; } @@ -584,8 +591,9 @@ CASE( "test_trans_VorDivToUV" ) { // TODO: do some meaningful checks Log::info() << "Local transform" << std::endl; Log::info() << "U: " << std::endl; - for ( int j = 0; j < nfld * nspec2; j++ ) + for ( int j = 0; j < nfld * nspec2; j++ ) { Log::info() << field_U[j] << " "; + } Log::info() << std::endl; } } diff --git a/src/tests/trans/test_trans_invtrans_grad.cc b/src/tests/trans/test_trans_invtrans_grad.cc index f464ac88b..1d3c52f6c 100644 --- a/src/tests/trans/test_trans_invtrans_grad.cc +++ b/src/tests/trans/test_trans_invtrans_grad.cc @@ -43,8 +43,9 @@ namespace test { struct AtlasTransEnvironment : public AtlasTestEnvironment { AtlasTransEnvironment( int argc, char* argv[] ) : AtlasTestEnvironment( argc, argv ) { - if ( mpi::comm().size() == 1 ) + if ( mpi::comm().size() == 1 ) { trans_use_mpi( false ); + } trans_init(); } From e46b57b71bd14a0a7f5aaa82ff43c1033133f6ad Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 2 Sep 2019 19:19:40 +0100 Subject: [PATCH 03/40] clang-tidy: modernize-use-nullptr --- cmake/FeatureClangTidy.cmake | 2 +- src/atlas/field/FieldCreator.cc | 4 ++-- src/atlas/field/State.cc | 4 ++-- src/atlas/grid/detail/grid/GridBuilder.cc | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cmake/FeatureClangTidy.cmake b/cmake/FeatureClangTidy.cmake index b744363c1..c1698ee6a 100644 --- a/cmake/FeatureClangTidy.cmake +++ b/cmake/FeatureClangTidy.cmake @@ -25,6 +25,6 @@ if (HAVE_CLANG_TIDY) # Uncomment to apply fixes. Make sure to use a clean build, and apply clang-format afterwards! # set( CLANG_TIDY_FIXIT ";-fix" ) - set(CLANG_TIDY_CHECKS "-*,readability-braces-around-statements,redundant-string-init") + set(CLANG_TIDY_CHECKS "-*,readability-braces-around-statements,redundant-string-init,modernize-use-nullptr") set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXE};-checks=${CLANG_TIDY_CHECKS};-header-filter='${CMAKE_SOURCE_DIR}/*'${CLANG_TIDY_FIXIT}") endif() diff --git a/src/atlas/field/FieldCreator.cc b/src/atlas/field/FieldCreator.cc index e5cb95090..dbef986fd 100644 --- a/src/atlas/field/FieldCreator.cc +++ b/src/atlas/field/FieldCreator.cc @@ -25,8 +25,8 @@ #include "atlas/runtime/Log.h" namespace { -static eckit::Mutex* local_mutex = 0; -static std::map* m = 0; +static eckit::Mutex* local_mutex = nullptr; +static std::map* m = nullptr; static pthread_once_t once = PTHREAD_ONCE_INIT; static void init() { diff --git a/src/atlas/field/State.cc b/src/atlas/field/State.cc index 15257fac6..03df809dd 100644 --- a/src/atlas/field/State.cc +++ b/src/atlas/field/State.cc @@ -29,8 +29,8 @@ namespace field { namespace { -static eckit::Mutex* local_mutex = 0; -static std::map* m = 0; +static eckit::Mutex* local_mutex = nullptr; +static std::map* m = nullptr; static pthread_once_t once = PTHREAD_ONCE_INIT; static void init() { diff --git a/src/atlas/grid/detail/grid/GridBuilder.cc b/src/atlas/grid/detail/grid/GridBuilder.cc index 2844e92e8..c8da3d00d 100644 --- a/src/atlas/grid/detail/grid/GridBuilder.cc +++ b/src/atlas/grid/detail/grid/GridBuilder.cc @@ -100,9 +100,9 @@ class Regex { bool use_case_; }; -static eckit::Mutex* local_mutex = 0; -static GridBuilder::Registry* named_grids = 0; -static GridBuilder::Registry* typed_grids = 0; +static eckit::Mutex* local_mutex = nullptr; +static GridBuilder::Registry* named_grids = nullptr; +static GridBuilder::Registry* typed_grids = nullptr; static pthread_once_t once = PTHREAD_ONCE_INIT; From 54ac4476a5b5d7bc7eedbc43ae78543d235efe5d Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 2 Sep 2019 19:29:48 +0100 Subject: [PATCH 04/40] clang-tidy: modernize-use-using --- cmake/FeatureClangTidy.cmake | 11 +++++++++-- src/atlas/mesh/Connectivity.cc | 4 ++-- src/atlas/mesh/actions/BuildConvexHull3D.cc | 12 ++++++------ src/atlas/mesh/actions/BuildHalo.cc | 4 ++-- src/atlas/mesh/actions/BuildParallelFields.cc | 2 +- src/atlas/mesh/actions/BuildPeriodicBoundaries.cc | 2 +- src/tests/mesh/test_elements.cc | 2 +- src/tests/parallel/test_gather.cc | 2 +- src/tests/parallel/test_haloexchange.cc | 2 +- 9 files changed, 24 insertions(+), 17 deletions(-) diff --git a/cmake/FeatureClangTidy.cmake b/cmake/FeatureClangTidy.cmake index c1698ee6a..d997a552a 100644 --- a/cmake/FeatureClangTidy.cmake +++ b/cmake/FeatureClangTidy.cmake @@ -25,6 +25,13 @@ if (HAVE_CLANG_TIDY) # Uncomment to apply fixes. Make sure to use a clean build, and apply clang-format afterwards! # set( CLANG_TIDY_FIXIT ";-fix" ) - set(CLANG_TIDY_CHECKS "-*,readability-braces-around-statements,redundant-string-init,modernize-use-nullptr") - set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXE};-checks=${CLANG_TIDY_CHECKS};-header-filter='${CMAKE_SOURCE_DIR}/*'${CLANG_TIDY_FIXIT}") + set( CLANG_TIDY_CHECKS "-*" ) + foreach( _clang_tidy_check + readability-braces-around-statements + redundant-string-init + modernize-use-nullptr + modernize-use-using ) + set( CLANG_TIDY_CHECKS "${CLANG_TIDY_CHECKS},${_clang_tidy_check}" ) + endforeach() + set( CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXE};-checks=${CLANG_TIDY_CHECKS};-header-filter='${CMAKE_SOURCE_DIR}/*'${CLANG_TIDY_FIXIT}" ) endif() diff --git a/src/atlas/mesh/Connectivity.cc b/src/atlas/mesh/Connectivity.cc index b342eeda3..fe862b653 100644 --- a/src/atlas/mesh/Connectivity.cc +++ b/src/atlas/mesh/Connectivity.cc @@ -775,8 +775,8 @@ size_t BlockConnectivityImpl::footprint() const { class ConnectivityPrivateAccess { private: - typedef Connectivity::ctxt_t ctxt_t; - typedef Connectivity::callback_t callback_t; + using ctxt_t = Connectivity::ctxt_t; + using callback_t = Connectivity::callback_t; public: ConnectivityPrivateAccess( Connectivity& connectivity ) : connectivity_( connectivity ) {} diff --git a/src/atlas/mesh/actions/BuildConvexHull3D.cc b/src/atlas/mesh/actions/BuildConvexHull3D.cc index 67cf37836..80555f961 100644 --- a/src/atlas/mesh/actions/BuildConvexHull3D.cc +++ b/src/atlas/mesh/actions/BuildConvexHull3D.cc @@ -27,13 +27,13 @@ #include #include -typedef CGAL::Exact_predicates_inexact_constructions_kernel K; -typedef CGAL::Polyhedron_3 Polyhedron_3; +using K = CGAL::Exact_predicates_inexact_constructions_kernel; +using Polyhedron_3 = CGAL::Polyhedron_3; -typedef K::Vector_3 Vector_3; -typedef K::FT FT; -typedef K::Segment_3 Segment_3; -typedef K::Point_3 Point_3; +using Vector_3 = K::Vector_3; +using FT = K::FT; +using Segment_3 = K::Segment_3; +using Point_3 = K::Point_3; const Point_3 origin = Point_3( CGAL::ORIGIN ); diff --git a/src/atlas/mesh/actions/BuildHalo.cc b/src/atlas/mesh/actions/BuildHalo.cc index ce58bdcd7..0819dd460 100644 --- a/src/atlas/mesh/actions/BuildHalo.cc +++ b/src/atlas/mesh/actions/BuildHalo.cc @@ -284,7 +284,7 @@ void make_cells_global_index_human_readable( const mesh::actions::BuildHalo& bui // ------------------------------------------------------------------------------------- -typedef gidx_t uid_t; +using uid_t = gidx_t; // ------------------------------------------------------------------ class BuildHaloHelper; @@ -302,7 +302,7 @@ class WestEast : public util::PeriodicTransform { WestEast() { x_translation_ = 360.; } }; -typedef std::vector> Node2Elem; +using Node2Elem = std::vector>; void build_lookup_node2elem( const Mesh& mesh, Node2Elem& node2elem ) { ATLAS_TRACE(); diff --git a/src/atlas/mesh/actions/BuildParallelFields.cc b/src/atlas/mesh/actions/BuildParallelFields.cc index 1f6a814b5..7122b068f 100644 --- a/src/atlas/mesh/actions/BuildParallelFields.cc +++ b/src/atlas/mesh/actions/BuildParallelFields.cc @@ -73,7 +73,7 @@ Field& build_edges_global_idx( Mesh& mesh ); //---------------------------------------------------------------------------------------------------------------------- -typedef gidx_t uid_t; +using uid_t = gidx_t; namespace { diff --git a/src/atlas/mesh/actions/BuildPeriodicBoundaries.cc b/src/atlas/mesh/actions/BuildPeriodicBoundaries.cc index 1594e8b68..6f051905c 100644 --- a/src/atlas/mesh/actions/BuildPeriodicBoundaries.cc +++ b/src/atlas/mesh/actions/BuildPeriodicBoundaries.cc @@ -32,7 +32,7 @@ namespace atlas { namespace mesh { namespace actions { -typedef gidx_t uid_t; +using uid_t = gidx_t; void build_periodic_boundaries( Mesh& mesh ) { ATLAS_TRACE(); diff --git a/src/tests/mesh/test_elements.cc b/src/tests/mesh/test_elements.cc index 784eea839..767bbbec1 100644 --- a/src/tests/mesh/test_elements.cc +++ b/src/tests/mesh/test_elements.cc @@ -247,7 +247,7 @@ CASE( "block_connectivity" ) { CASE( "zero_elements" ) { HybridElements hybrid_elements; - idx_t* nodes = 0; + idx_t* nodes = nullptr; hybrid_elements.add( new Triangle(), 0, nodes ); hybrid_elements.add( new Quadrilateral(), 0, nodes ); diff --git a/src/tests/parallel/test_gather.cc b/src/tests/parallel/test_gather.cc index 4d83335d6..e89de05d9 100644 --- a/src/tests/parallel/test_gather.cc +++ b/src/tests/parallel/test_gather.cc @@ -23,7 +23,7 @@ #include "tests/AtlasTestEnvironment.h" /// POD: Type to test -typedef double POD; +using POD = double; namespace atlas { namespace test { diff --git a/src/tests/parallel/test_haloexchange.cc b/src/tests/parallel/test_haloexchange.cc index 7ee9f472f..e4c945cbf 100644 --- a/src/tests/parallel/test_haloexchange.cc +++ b/src/tests/parallel/test_haloexchange.cc @@ -23,7 +23,7 @@ #include "tests/AtlasTestEnvironment.h" /// POD: Type to test -typedef double POD; +using POD = double; namespace atlas { namespace test { From 6b12128f928fbbc9709f70b55200ca3b8d28ad6f Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 3 Sep 2019 09:36:45 +0100 Subject: [PATCH 05/40] clang-tidy: modernize-use-override --- cmake/FeatureClangTidy.cmake | 4 +++- src/apps/atlas-benchmark.cc | 2 +- src/apps/atlas-gaussian-latitudes.cc | 2 +- src/apps/atlas-gmsh-extract.cc | 2 +- src/apps/atlas-grids.cc | 10 ++++---- src/apps/atlas-loadbalance.cc | 2 +- src/apps/atlas-meshgen.cc | 6 ++--- src/apps/atlas.cc | 4 ++-- src/atlas/functionspace/EdgeColumns.cc | 6 ++--- src/atlas/functionspace/NodeColumns.cc | 6 ++--- src/atlas/functionspace/StructuredColumns.cc | 12 +++++----- src/atlas/grid/detail/grid/Gaussian.cc | 18 +++++++------- src/atlas/grid/detail/grid/LonLat.cc | 24 +++++++++---------- src/atlas/grid/detail/grid/Regional.cc | 12 +++++----- src/atlas/grid/detail/grid/Structured.cc | 6 ++--- src/atlas/grid/detail/grid/Unstructured.cc | 8 +++---- src/atlas/projection/detail/ProjectionImpl.cc | 2 +- .../atlas-benchmark-ifs-setup.cc | 6 ++--- .../atlas-benchmark-sorting.cc | 6 ++--- .../atlas-grid-distribution.cc | 6 ++--- .../atlas-parallel-interpolation.cc | 10 ++++---- src/tests/acceptance_tests/atest_mgrids.cc | 2 +- src/tests/grid/test_state.cc | 4 ++-- 23 files changed, 81 insertions(+), 79 deletions(-) diff --git a/cmake/FeatureClangTidy.cmake b/cmake/FeatureClangTidy.cmake index d997a552a..d02ebce2f 100644 --- a/cmake/FeatureClangTidy.cmake +++ b/cmake/FeatureClangTidy.cmake @@ -30,7 +30,9 @@ if (HAVE_CLANG_TIDY) readability-braces-around-statements redundant-string-init modernize-use-nullptr - modernize-use-using ) + modernize-use-using + modernize-use-override + ) set( CLANG_TIDY_CHECKS "${CLANG_TIDY_CHECKS},${_clang_tidy_check}" ) endforeach() set( CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXE};-checks=${CLANG_TIDY_CHECKS};-header-filter='${CMAKE_SOURCE_DIR}/*'${CLANG_TIDY_FIXIT}" ) diff --git a/src/apps/atlas-benchmark.cc b/src/apps/atlas-benchmark.cc index 6a80de61b..2e9eee4dd 100644 --- a/src/apps/atlas-benchmark.cc +++ b/src/apps/atlas-benchmark.cc @@ -126,7 +126,7 @@ struct TimerStats { //---------------------------------------------------------------------------------------------------------------------- class AtlasBenchmark : public AtlasTool { - virtual int execute( const Args& args ); + int execute( const Args& args ) override; public: AtlasBenchmark( int argc, char** argv ) : AtlasTool( argc, argv ) { diff --git a/src/apps/atlas-gaussian-latitudes.cc b/src/apps/atlas-gaussian-latitudes.cc index 6d18d50b3..1864ea9f3 100644 --- a/src/apps/atlas-gaussian-latitudes.cc +++ b/src/apps/atlas-gaussian-latitudes.cc @@ -35,7 +35,7 @@ using atlas::grid::spacing::GaussianSpacing; //------------------------------------------------------------------------------------------------------ class AtlasGaussianLatitudes : public eckit::Tool { - virtual void run(); + void run() override; public: AtlasGaussianLatitudes( int argc, char** argv ) : eckit::Tool( argc, argv ) { diff --git a/src/apps/atlas-gmsh-extract.cc b/src/apps/atlas-gmsh-extract.cc index ed367fca9..9cda4629b 100644 --- a/src/apps/atlas-gmsh-extract.cc +++ b/src/apps/atlas-gmsh-extract.cc @@ -33,7 +33,7 @@ using namespace atlas; //------------------------------------------------------------------------------------------------------ class gmsh_extract : public eckit::Tool { - virtual void run(); + void run() override; public: gmsh_extract( int argc, char** argv ) : eckit::Tool( argc, argv ) { diff --git a/src/apps/atlas-grids.cc b/src/apps/atlas-grids.cc index 5a76e2483..65d976fcb 100644 --- a/src/apps/atlas-grids.cc +++ b/src/apps/atlas-grids.cc @@ -29,11 +29,11 @@ //---------------------------------------------------------------------------------------------------------------------- struct AtlasGrids : public atlas::AtlasTool { - virtual bool serial() { return true; } - virtual int execute( const Args& args ); - virtual std::string briefDescription() { return "Catalogue of available built-in grids"; } - virtual std::string usage() { return name() + " GRID [OPTION]... [--help,-h]"; } - virtual std::string longDescription() { + bool serial() override { return true; } + int execute( const Args& args ) override; + std::string briefDescription() override { return "Catalogue of available built-in grids"; } + std::string usage() override { return name() + " GRID [OPTION]... [--help,-h]"; } + std::string longDescription() override { return "Catalogue of available built-in grids\n" "\n" " Browse catalogue of grids\n" diff --git a/src/apps/atlas-loadbalance.cc b/src/apps/atlas-loadbalance.cc index 8df6a73b6..596742941 100644 --- a/src/apps/atlas-loadbalance.cc +++ b/src/apps/atlas-loadbalance.cc @@ -37,7 +37,7 @@ using namespace atlas::mesh; //------------------------------------------------------------------------------------------------------ class AtlasLoadbalance : public eckit::Tool { - virtual void run(); + void run() override; public: AtlasLoadbalance( int argc, char** argv ) : eckit::Tool( argc, argv ) { diff --git a/src/apps/atlas-meshgen.cc b/src/apps/atlas-meshgen.cc index ac4a70403..aefc5fbda 100644 --- a/src/apps/atlas-meshgen.cc +++ b/src/apps/atlas-meshgen.cc @@ -57,9 +57,9 @@ using eckit::PathName; //------------------------------------------------------------------------------ class Meshgen2Gmsh : public AtlasTool { - virtual int execute( const Args& args ); - virtual std::string briefDescription() { return "Mesh generator for Structured compatible meshes"; } - virtual std::string usage() { return name() + " (--grid.name=name|--grid.json=path) [OPTION]... OUTPUT [--help]"; } + int execute( const Args& args ) override; + std::string briefDescription() override { return "Mesh generator for Structured compatible meshes"; } + std::string usage() override { return name() + " (--grid.name=name|--grid.json=path) [OPTION]... OUTPUT [--help]"; } public: Meshgen2Gmsh( int argc, char** argv ); diff --git a/src/apps/atlas.cc b/src/apps/atlas.cc index 319ad38d0..93dabd8c7 100644 --- a/src/apps/atlas.cc +++ b/src/apps/atlas.cc @@ -22,9 +22,9 @@ class Version : public Tool { public: Version( int argc, char** argv ) : Tool( argc, argv ) {} - ~Version() {} + ~Version() override {} - virtual void run(); + void run() override; }; void Version::run() { diff --git a/src/atlas/functionspace/EdgeColumns.cc b/src/atlas/functionspace/EdgeColumns.cc index ff8abcae8..4eeb37a73 100644 --- a/src/atlas/functionspace/EdgeColumns.cc +++ b/src/atlas/functionspace/EdgeColumns.cc @@ -83,7 +83,7 @@ class EdgeColumnsHaloExchangeCache : public util::CachesizeHalo() ); return value; } - virtual ~StructuredColumnsHaloExchangeCache() {} + ~StructuredColumnsHaloExchangeCache() override {} }; class StructuredColumnsGatherScatterCache : public util::Cache, @@ -143,7 +143,7 @@ class StructuredColumnsGatherScatterCache : public util::Cache( funcspace->global_index() ).data(), funcspace->sizeOwned() ); return value; } - virtual ~StructuredColumnsGatherScatterCache() {} + ~StructuredColumnsGatherScatterCache() override {} }; class StructuredColumnsChecksumCache : public util::Cache, @@ -180,7 +180,7 @@ class StructuredColumnsChecksumCache : public util::Cachesetup( gather ); return value; } - virtual ~StructuredColumnsChecksumCache() {} + ~StructuredColumnsChecksumCache() override {} }; diff --git a/src/atlas/grid/detail/grid/Gaussian.cc b/src/atlas/grid/detail/grid/Gaussian.cc index 96902c577..8b87ec36b 100644 --- a/src/atlas/grid/detail/grid/Gaussian.cc +++ b/src/atlas/grid/detail/grid/Gaussian.cc @@ -78,12 +78,12 @@ static class classic_gaussian : public GridBuilder { public: classic_gaussian() : GridBuilder( "classic_gaussian", {"^[Nn]([0-9]+)$"} ) {} - virtual void print( std::ostream& os ) const { + void print( std::ostream& os ) const override { os << std::left << std::setw( 20 ) << "N" << "Classic Gaussian grid"; } - virtual const Grid::Implementation* create( const std::string& name, const Grid::Config& config ) const { + const Grid::Implementation* create( const std::string& name, const Grid::Config& config ) const override { int id; std::vector matches; if ( match( name, matches, id ) ) { @@ -96,7 +96,7 @@ static class classic_gaussian : public GridBuilder { return nullptr; } - virtual const Grid::Implementation* create( const Grid::Config& config ) const { + const Grid::Implementation* create( const Grid::Config& config ) const override { long N; config.get( "N", N ); std::vector nx( 2 * N ); @@ -115,12 +115,12 @@ static class octahedral_gaussian : GridBuilder { public: octahedral_gaussian() : GridBuilder( "octahedral_gaussian", {"^[Oo]([0-9]+)$"} ) {} - virtual void print( std::ostream& os ) const { + void print( std::ostream& os ) const override { os << std::left << std::setw( 20 ) << "O" << "Octahedral Gaussian grid"; } - virtual const Grid::Implementation* create( const std::string& name, const Grid::Config& config ) const { + const Grid::Implementation* create( const std::string& name, const Grid::Config& config ) const override { int id; std::vector matches; if ( match( name, matches, id ) ) { @@ -133,7 +133,7 @@ static class octahedral_gaussian : GridBuilder { return nullptr; } - virtual const Grid::Implementation* create( const Grid::Config& config ) const { + const Grid::Implementation* create( const Grid::Config& config ) const override { long N; config.get( "N", N ); @@ -159,12 +159,12 @@ static class regular_gaussian : GridBuilder { public: regular_gaussian() : GridBuilder( "regular_gaussian", {"^[Ff]([0-9]+)$"} ) {} - virtual void print( std::ostream& os ) const { + void print( std::ostream& os ) const override { os << std::left << std::setw( 20 ) << "F" << "Regular Gaussian grid"; } - virtual const Grid::Implementation* create( const std::string& name, const Grid::Config& config ) const { + const Grid::Implementation* create( const std::string& name, const Grid::Config& config ) const override { int id; std::vector matches; if ( match( name, matches, id ) ) { @@ -177,7 +177,7 @@ static class regular_gaussian : GridBuilder { return nullptr; } - virtual const Grid::Implementation* create( const Grid::Config& config ) const { + const Grid::Implementation* create( const Grid::Config& config ) const override { long N; config.get( "N", N ); std::vector nx( 2 * N, 4 * N ); diff --git a/src/atlas/grid/detail/grid/LonLat.cc b/src/atlas/grid/detail/grid/LonLat.cc index ef1649fb3..0cbf716bc 100644 --- a/src/atlas/grid/detail/grid/LonLat.cc +++ b/src/atlas/grid/detail/grid/LonLat.cc @@ -126,12 +126,12 @@ static class regular_lonlat : public GridBuilder { public: regular_lonlat() : GridBuilder( "regular_lonlat", {"^[Ll]([0-9]+)x([0-9]+)$", "^[Ll]([0-9]+)$"} ) {} - virtual void print( std::ostream& os ) const { + void print( std::ostream& os ) const override { os << std::left << std::setw( 20 ) << "Lx / L" << "Regular longitude-latitude grid"; } - virtual const Grid::Implementation* create( const std::string& name, const Grid::Config& config ) const { + const Grid::Implementation* create( const std::string& name, const Grid::Config& config ) const override { int id; std::vector matches; if ( match( name, matches, id ) ) { @@ -152,7 +152,7 @@ static class regular_lonlat : public GridBuilder { return nullptr; } - virtual const Grid::Implementation* create( const Grid::Config& config ) const { + const Grid::Implementation* create( const Grid::Config& config ) const override { return create_lonlat( config, Shift( false, false ) ); } @@ -166,12 +166,12 @@ static class shifted_lonlat : public GridBuilder { public: shifted_lonlat() : GridBuilder( "shifted_lonlat", {"^[Ss]([0-9]+)x([0-9]+)$", "^[Ss]([0-9]+)$"} ) {} - virtual void print( std::ostream& os ) const { + void print( std::ostream& os ) const override { os << std::left << std::setw( 20 ) << "Sx / S" << "Shifted longitude-latitude grid"; } - virtual const Grid::Implementation* create( const std::string& name, const Grid::Config& config ) const { + const Grid::Implementation* create( const std::string& name, const Grid::Config& config ) const override { int id; std::vector matches; if ( match( name, matches, id ) ) { @@ -192,7 +192,7 @@ static class shifted_lonlat : public GridBuilder { return nullptr; } - virtual const Grid::Implementation* create( const Grid::Config& config ) const { + const Grid::Implementation* create( const Grid::Config& config ) const override { return create_lonlat( config, Shift( true, true ) ); } @@ -207,12 +207,12 @@ static class shifted_lon : public GridBuilder { shifted_lon() : GridBuilder( "shifted_lon", {"^[Ss][Ll][Oo][Nn]([0-9]+)x([0-9]+)$", "^[Ss][Ll][Oo][Nn]([0-9]+)$"} ) {} - virtual void print( std::ostream& os ) const { + void print( std::ostream& os ) const override { os << std::left << std::setw( 20 ) << "Slonx / Slon" << "Shifted longitude grid"; } - virtual const Grid::Implementation* create( const std::string& name, const Grid::Config& config ) const { + const Grid::Implementation* create( const std::string& name, const Grid::Config& config ) const override { int id; std::vector matches; if ( match( name, matches, id ) ) { @@ -233,7 +233,7 @@ static class shifted_lon : public GridBuilder { return nullptr; } - virtual const Grid::Implementation* create( const Grid::Config& config ) const { + const Grid::Implementation* create( const Grid::Config& config ) const override { return create_lonlat( config, Shift( true, false ) ); } @@ -248,12 +248,12 @@ static class shifted_lat : public GridBuilder { shifted_lat() : GridBuilder( "shifted_lat", {"^[Ss][Ll][Aa][Tt]([0-9]+)x([0-9]+)$", "^[Ss][Ll][Aa][Tt]([0-9]+)$"} ) {} - virtual void print( std::ostream& os ) const { + void print( std::ostream& os ) const override { os << std::left << std::setw( 20 ) << "Slatx / Slat" << "Shifted latitude grid"; } - virtual const Grid::Implementation* create( const std::string& name, const Grid::Config& config ) const { + const Grid::Implementation* create( const std::string& name, const Grid::Config& config ) const override { int id; std::vector matches; if ( match( name, matches, id ) ) { @@ -274,7 +274,7 @@ static class shifted_lat : public GridBuilder { return nullptr; } - virtual const Grid::Implementation* create( const Grid::Config& config ) const { + const Grid::Implementation* create( const Grid::Config& config ) const override { return create_lonlat( config, Shift( false, true ) ); } diff --git a/src/atlas/grid/detail/grid/Regional.cc b/src/atlas/grid/detail/grid/Regional.cc index 63e111b13..15211e2ca 100644 --- a/src/atlas/grid/detail/grid/Regional.cc +++ b/src/atlas/grid/detail/grid/Regional.cc @@ -280,17 +280,17 @@ static class regional : public GridBuilder { public: regional() : GridBuilder( "regional" ) {} - virtual void print( std::ostream& os ) const { + void print( std::ostream& os ) const override { // os << std::left << std::setw(20) << "O" << "Octahedral Gaussian // grid"; } - virtual const Grid::Implementation* create( const std::string& name, const Grid::Config& config ) const { + 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; } - virtual const Grid::Implementation* create( const Grid::Config& config ) const { + const Grid::Implementation* create( const Grid::Config& config ) const override { // read projection subconfiguration Projection projection; { @@ -322,17 +322,17 @@ static class zonal_band : public GridBuilder { public: zonal_band() : GridBuilder( "zonal_band" ) {} - virtual void print( std::ostream& os ) const { + void print( std::ostream& os ) const override { // os << std::left << std::setw(20) << "O" << "Octahedral Gaussian // grid"; } - virtual const Grid::Implementation* create( const std::string& name, const Grid::Config& config ) const { + const Grid::Implementation* create( const std::string& name, const Grid::Config& config ) const override { throw_NotImplemented( "There are no named zonal_band grids implemented.", Here() ); return nullptr; } - virtual const Grid::Implementation* create( const Grid::Config& config ) const { + const Grid::Implementation* create( const Grid::Config& config ) const override { // read projection subconfiguration Projection projection; { diff --git a/src/atlas/grid/detail/grid/Structured.cc b/src/atlas/grid/detail/grid/Structured.cc index 2094cf5ce..6d66532bb 100644 --- a/src/atlas/grid/detail/grid/Structured.cc +++ b/src/atlas/grid/detail/grid/Structured.cc @@ -559,16 +559,16 @@ static class structured : public GridBuilder { public: structured() : GridBuilder( "structured" ) {} - virtual void print( std::ostream& os ) const { + void print( std::ostream& os ) const override { os << std::left << std::setw( 20 ) << " " << "Structured grid"; } - virtual const Implementation* create( const std::string& /* name */, const Config& ) const { + const Implementation* create( const std::string& /* name */, const Config& ) const override { throw_NotImplemented( "Cannot create structured grid from name", Here() ); } - virtual const Implementation* create( const Config& config ) const { + const Implementation* create( const Config& config ) const override { Projection projection; Spacing yspace; Domain domain; diff --git a/src/atlas/grid/detail/grid/Unstructured.cc b/src/atlas/grid/detail/grid/Unstructured.cc index ff33951c2..ca3bf7a43 100644 --- a/src/atlas/grid/detail/grid/Unstructured.cc +++ b/src/atlas/grid/detail/grid/Unstructured.cc @@ -245,16 +245,16 @@ static class unstructured : public GridBuilder { public: unstructured() : GridBuilder( "unstructured" ) {} - virtual void print( std::ostream& os ) const { + void print( std::ostream& os ) const override { os << std::left << std::setw( 20 ) << " " << "Unstructured grid"; } - virtual const Implementation* create( const std::string& /* name */, const Config& ) const { + const Implementation* create( const std::string& /* name */, const Config& ) const override { throw_NotImplemented( "Cannot create unstructured grid from name", Here() ); } - virtual const Implementation* create( const Config& config ) const { return new Unstructured( config ); } + const Implementation* create( const Config& config ) const override { return new Unstructured( config ); } } unstructured_; @@ -268,7 +268,7 @@ const Unstructured* atlas__grid__Unstructured__points( const double xy[], int sh size_t stride_v = stridesf[0]; std::vector points; points.reserve( nb_points ); - for ( idx_t n = 0; n < nb_points; ++n ) { + for ( size_t n = 0; n < nb_points; ++n ) { points.emplace_back( PointXY{xy[n * stride_n + 0], xy[n * stride_n + 1 * stride_v]} ); } return new Unstructured( std::move( points ) ); diff --git a/src/atlas/projection/detail/ProjectionImpl.cc b/src/atlas/projection/detail/ProjectionImpl.cc index 60b5cb5d5..e987457d7 100644 --- a/src/atlas/projection/detail/ProjectionImpl.cc +++ b/src/atlas/projection/detail/ProjectionImpl.cc @@ -33,7 +33,7 @@ namespace { template struct DerivateBuilder : public ProjectionImpl::DerivateFactory { using DerivateFactory::DerivateFactory; - ProjectionImpl::Derivate* make( const ProjectionImpl& p, PointXY A, PointXY B, double h ) { + ProjectionImpl::Derivate* make( const ProjectionImpl& p, PointXY A, PointXY B, double h ) override { return new T( p, A, B, h ); } }; diff --git a/src/sandbox/benchmark_ifs_setup/atlas-benchmark-ifs-setup.cc b/src/sandbox/benchmark_ifs_setup/atlas-benchmark-ifs-setup.cc index c0f46bc61..1bb866f57 100644 --- a/src/sandbox/benchmark_ifs_setup/atlas-benchmark-ifs-setup.cc +++ b/src/sandbox/benchmark_ifs_setup/atlas-benchmark-ifs-setup.cc @@ -44,12 +44,12 @@ using eckit::PathName; //------------------------------------------------------------------------------ class Tool : public AtlasTool { - virtual int execute( const Args& args ); - virtual std::string briefDescription() { + int execute( const Args& args ) override; + std::string briefDescription() override { return "Tool to generate a python script that plots the grid-distribution " "of a given grid"; } - virtual std::string usage() { return name() + " --grid=name [OPTION]... OUTPUT [--help]"; } + std::string usage() override { return name() + " --grid=name [OPTION]... OUTPUT [--help]"; } public: Tool( int argc, char** argv ); diff --git a/src/sandbox/benchmark_sorting/atlas-benchmark-sorting.cc b/src/sandbox/benchmark_sorting/atlas-benchmark-sorting.cc index b526b2bd9..1e50ef1cf 100644 --- a/src/sandbox/benchmark_sorting/atlas-benchmark-sorting.cc +++ b/src/sandbox/benchmark_sorting/atlas-benchmark-sorting.cc @@ -179,12 +179,12 @@ void make_nodes_global_index_human_readable( const mesh::actions::BuildHalo& bui //----------------------------------------------------------------------------- class Tool : public AtlasTool { - virtual int execute( const Args& args ); - virtual std::string briefDescription() { + int execute( const Args& args ) override; + std::string briefDescription() override { return "Tool to generate a python script that plots the grid-distribution " "of a given grid"; } - virtual std::string usage() { return name() + " (--grid=name) [--help]"; } + std::string usage() override { return name() + " (--grid=name) [--help]"; } public: Tool( int argc, char** argv ); diff --git a/src/sandbox/grid_distribution/atlas-grid-distribution.cc b/src/sandbox/grid_distribution/atlas-grid-distribution.cc index fffe13b6a..b70e6e5a1 100644 --- a/src/sandbox/grid_distribution/atlas-grid-distribution.cc +++ b/src/sandbox/grid_distribution/atlas-grid-distribution.cc @@ -40,12 +40,12 @@ using eckit::PathName; //------------------------------------------------------------------------------ class Tool : public AtlasTool { - virtual int execute( const Args& args ); - virtual std::string briefDescription() { + int execute( const Args& args ) override; + std::string briefDescription() override { return "Tool to generate a python script that plots the grid-distribution " "of a given grid"; } - virtual std::string usage() { return name() + " (--grid.name=name|--grid.json=path) [OPTION]... OUTPUT [--help]"; } + std::string usage() override { return name() + " (--grid.name=name|--grid.json=path) [OPTION]... OUTPUT [--help]"; } public: Tool( int argc, char** argv ); diff --git a/src/sandbox/interpolation/atlas-parallel-interpolation.cc b/src/sandbox/interpolation/atlas-parallel-interpolation.cc index 71a12b6a3..631b312a7 100644 --- a/src/sandbox/interpolation/atlas-parallel-interpolation.cc +++ b/src/sandbox/interpolation/atlas-parallel-interpolation.cc @@ -46,16 +46,16 @@ auto vortex_rollup = []( double lon, double lat, double t ) { }; class AtlasParallelInterpolation : public AtlasTool { - int execute( const AtlasTool::Args& args ); - std::string briefDescription() { return "Demonstration of parallel interpolation"; } - std::string usage() { + int execute( const AtlasTool::Args& args ) override; + std::string briefDescription() override { return "Demonstration of parallel interpolation"; } + std::string usage() override { return name() + " [--source-gridname=gridname] " "[--target-gridname=gridname] [OPTION]... [--help]"; } - int numberOfPositionalArguments() { return -1; } - int minimumPositionalArguments() { return 0; } + int numberOfPositionalArguments() override { return -1; } + int minimumPositionalArguments() override { return 0; } public: AtlasParallelInterpolation( int argc, char* argv[] ) : AtlasTool( argc, argv ) { diff --git a/src/tests/acceptance_tests/atest_mgrids.cc b/src/tests/acceptance_tests/atest_mgrids.cc index 027057d83..1a97c4da7 100644 --- a/src/tests/acceptance_tests/atest_mgrids.cc +++ b/src/tests/acceptance_tests/atest_mgrids.cc @@ -37,7 +37,7 @@ double vortex_rollup( double lon, double lat, double t, double mean ); //------------------------------------------------------------------------------ class Program : public AtlasTool { - virtual int execute( const Args& args ); + int execute( const Args& args ) override; public: Program( int argc, char** argv ); diff --git a/src/tests/grid/test_state.cc b/src/tests/grid/test_state.cc index fa91a235b..8dac10df5 100644 --- a/src/tests/grid/test_state.cc +++ b/src/tests/grid/test_state.cc @@ -41,8 +41,8 @@ namespace test { class MyStateGenerator : public StateGenerator { public: MyStateGenerator( const eckit::Parametrisation& p = util::Config() ) : StateGenerator( p ) {} - ~MyStateGenerator() {} - virtual void generate( State& state, const eckit::Parametrisation& p = util::Config() ) const; + ~MyStateGenerator() override {} + void generate( State& state, const eckit::Parametrisation& p = util::Config() ) const override; }; // --- Implementation (in .cc file) From e04c16a3ac5bcdc8aec6b042b045bac21b985206 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 3 Sep 2019 09:42:52 +0100 Subject: [PATCH 06/40] clang-tidy: modernize-use-emplace --- cmake/FeatureClangTidy.cmake | 1 + src/atlas/interpolation/method/PointSet.cc | 3 +-- .../method/knn/KNearestNeighbours.cc | 2 +- .../method/knn/KNearestNeighboursBase.cc | 2 +- .../interpolation/method/knn/NearestNeighbour.cc | 2 +- src/atlas/mesh/actions/BuildHalo.cc | 4 ++-- src/atlas/mesh/actions/BuildParallelFields.cc | 2 +- .../detail/StructuredMeshGenerator.cc | 4 ++-- .../benchmark_sorting/atlas-benchmark-sorting.cc | 2 +- src/tests/interpolation/test_Quad3D.cc | 16 ++++++++-------- 10 files changed, 19 insertions(+), 19 deletions(-) diff --git a/cmake/FeatureClangTidy.cmake b/cmake/FeatureClangTidy.cmake index d02ebce2f..21c18d5d7 100644 --- a/cmake/FeatureClangTidy.cmake +++ b/cmake/FeatureClangTidy.cmake @@ -32,6 +32,7 @@ if (HAVE_CLANG_TIDY) modernize-use-nullptr modernize-use-using modernize-use-override + modernize-use-emplace ) set( CLANG_TIDY_CHECKS "${CLANG_TIDY_CHECKS},${_clang_tidy_check}" ) endforeach() diff --git a/src/atlas/interpolation/method/PointSet.cc b/src/atlas/interpolation/method/PointSet.cc index 4076d86b7..148fc353c 100644 --- a/src/atlas/interpolation/method/PointSet.cc +++ b/src/atlas/interpolation/method/PointSet.cc @@ -49,8 +49,7 @@ PointSet::PointSet( Mesh& mesh ) { pidx.reserve( npts_ ); for ( size_t ip = 0; ip < npts_; ++ip ) { - pidx.push_back( - PointIndex3::Value( PointIndex3::Point( coords( ip, 0 ), coords( ip, 1 ), coords( ip, 2 ) ), ip ) ); + pidx.emplace_back( PointIndex3::Point( coords( ip, 0 ), coords( ip, 1 ), coords( ip, 2 ) ), ip ); } tree_->build( pidx.begin(), pidx.end() ); diff --git a/src/atlas/interpolation/method/knn/KNearestNeighbours.cc b/src/atlas/interpolation/method/knn/KNearestNeighbours.cc index a2450308c..8b371fbd0 100644 --- a/src/atlas/interpolation/method/knn/KNearestNeighbours.cc +++ b/src/atlas/interpolation/method/knn/KNearestNeighbours.cc @@ -120,7 +120,7 @@ void KNearestNeighbours::setup( const FunctionSpace& source, const FunctionSpace for ( size_t j = 0; j < npts; ++j ) { size_t jp = nn[j].payload(); ATLAS_ASSERT( jp < inp_npts ); - weights_triplets.push_back( Triplet( ip, jp, weights[j] / sum ) ); + weights_triplets.emplace_back( ip, jp, weights[j] / sum ); } } } diff --git a/src/atlas/interpolation/method/knn/KNearestNeighboursBase.cc b/src/atlas/interpolation/method/knn/KNearestNeighboursBase.cc index 9c1e16000..f85a65a2c 100644 --- a/src/atlas/interpolation/method/knn/KNearestNeighboursBase.cc +++ b/src/atlas/interpolation/method/knn/KNearestNeighboursBase.cc @@ -40,7 +40,7 @@ void KNearestNeighboursBase::buildPointSearchTree( Mesh& meshSource ) { pidx.reserve( meshSource.nodes().size() ); for ( idx_t ip = 0; ip < meshSource.nodes().size(); ++ip ) { PointIndex3::Point p{coords( ip, 0 ), coords( ip, 1 ), coords( ip, 2 )}; - pidx.push_back( PointIndex3::Value( p, ip ) ); + pidx.emplace_back( p, ip ); } pTree_->build( pidx.begin(), pidx.end() ); } diff --git a/src/atlas/interpolation/method/knn/NearestNeighbour.cc b/src/atlas/interpolation/method/knn/NearestNeighbour.cc index b2dd2d993..880805063 100644 --- a/src/atlas/interpolation/method/knn/NearestNeighbour.cc +++ b/src/atlas/interpolation/method/knn/NearestNeighbour.cc @@ -91,7 +91,7 @@ void NearestNeighbour::setup( const FunctionSpace& source, const FunctionSpace& // insert the weights into the interpolant matrix ATLAS_ASSERT( jp < inp_npts ); - weights_triplets.push_back( Triplet( ip, jp, 1 ) ); + weights_triplets.emplace_back( ip, jp, 1 ); } } diff --git a/src/atlas/mesh/actions/BuildHalo.cc b/src/atlas/mesh/actions/BuildHalo.cc index 0819dd460..57d996436 100644 --- a/src/atlas/mesh/actions/BuildHalo.cc +++ b/src/atlas/mesh/actions/BuildHalo.cc @@ -153,7 +153,7 @@ void make_nodes_global_index_human_readable( const mesh::actions::BuildHalo& bui node_sort.reserve( glb_nb_nodes ); const idx_t nb_glb_idx_gathered = static_cast( glb_idx_gathered.size() ); for ( idx_t jnode = 0; jnode < nb_glb_idx_gathered; ++jnode ) { - node_sort.push_back( Entity( glb_idx_gathered[jnode], jnode ) ); + node_sort.emplace_back( glb_idx_gathered[jnode], jnode ); } ATLAS_TRACE_SCOPE( "sort on rank 0" ) { std::sort( node_sort.begin(), node_sort.end() ); } @@ -253,7 +253,7 @@ void make_cells_global_index_human_readable( const mesh::actions::BuildHalo& bui std::vector cell_sort; cell_sort.reserve( glb_nb_cells ); for ( idx_t jcell = 0; jcell < glb_nb_cells; ++jcell ) { - cell_sort.push_back( Entity( glb_idx_gathered[jcell], jcell ) ); + cell_sort.emplace_back( glb_idx_gathered[jcell], jcell ); } ATLAS_TRACE_SCOPE( "sort on rank 0" ) { std::sort( cell_sort.begin(), cell_sort.end() ); } diff --git a/src/atlas/mesh/actions/BuildParallelFields.cc b/src/atlas/mesh/actions/BuildParallelFields.cc index 7122b068f..ac535c9c2 100644 --- a/src/atlas/mesh/actions/BuildParallelFields.cc +++ b/src/atlas/mesh/actions/BuildParallelFields.cc @@ -208,7 +208,7 @@ void renumber_nodes_glb_idx( mesh::Nodes& nodes ) { node_sort.reserve( glb_nb_nodes ); ATLAS_TRACE_SCOPE( "sort global indices" ) { for ( idx_t jnode = 0; jnode < glb_id.shape( 0 ); ++jnode ) { - node_sort.push_back( Node( glb_id( jnode ), jnode ) ); + node_sort.emplace_back( glb_id( jnode ), jnode ); } std::sort( node_sort.begin(), node_sort.end() ); } diff --git a/src/atlas/meshgenerator/detail/StructuredMeshGenerator.cc b/src/atlas/meshgenerator/detail/StructuredMeshGenerator.cc index bb859a932..d72053e73 100644 --- a/src/atlas/meshgenerator/detail/StructuredMeshGenerator.cc +++ b/src/atlas/meshgenerator/detail/StructuredMeshGenerator.cc @@ -931,7 +931,7 @@ void StructuredMeshGenerator::generate_mesh( const StructuredGrid& rg, const std ++node_number; } else { - ghost_nodes.push_back( GhostNode( jlat, jlon, jnode ) ); + ghost_nodes.emplace_back( jlat, jlon, jnode ); } ++jnode; } @@ -942,7 +942,7 @@ void StructuredMeshGenerator::generate_mesh( const StructuredGrid& rg, const std // part(jnode) = parts.at( offset_glb.at(jlat) ); ghost( jnode ) = 1; halo( jnode ) = 0; - ghost_nodes.push_back( GhostNode( jlat, rg.nx( jlat ), jnode ) ); + ghost_nodes.emplace_back( jlat, rg.nx( jlat ), jnode ); ++jnode; } else { diff --git a/src/sandbox/benchmark_sorting/atlas-benchmark-sorting.cc b/src/sandbox/benchmark_sorting/atlas-benchmark-sorting.cc index 1e50ef1cf..9049f4206 100644 --- a/src/sandbox/benchmark_sorting/atlas-benchmark-sorting.cc +++ b/src/sandbox/benchmark_sorting/atlas-benchmark-sorting.cc @@ -144,7 +144,7 @@ void make_nodes_global_index_human_readable( const mesh::actions::BuildHalo& bui std::vector node_sort; node_sort.reserve( glb_nb_nodes ); for ( size_t jnode = 0; jnode < glb_idx_gathered.size(); ++jnode ) { - node_sort.push_back( Node( glb_idx_gathered[jnode], jnode ) ); + node_sort.emplace_back( glb_idx_gathered[jnode], jnode ); } ATLAS_TRACE_SCOPE( "local_sort" ) { std::sort( node_sort.begin(), node_sort.end() ); } diff --git a/src/tests/interpolation/test_Quad3D.cc b/src/tests/interpolation/test_Quad3D.cc index e54766d99..f5ed4eb18 100644 --- a/src/tests/interpolation/test_Quad3D.cc +++ b/src/tests/interpolation/test_Quad3D.cc @@ -193,16 +193,16 @@ CASE( "test_quadrilateral_intersection_corners" ) { EXPECT( quad.validate() ); std::vector corners; - corners.push_back( PointXYZ( 0.0, -2.0, 1. ) ); - corners.push_back( PointXYZ( 2.5, 0.0, 1. ) ); - corners.push_back( PointXYZ( 0.0, 3.5, 1. ) ); - corners.push_back( PointXYZ( -1.5, 0.0, 1. ) ); + corners.emplace_back( 0.0, -2.0, 1. ); + corners.emplace_back( 2.5, 0.0, 1. ); + corners.emplace_back( 0.0, 3.5, 1. ); + corners.emplace_back( -1.5, 0.0, 1. ); std::vector> uvs; - uvs.push_back( std::make_pair( 0., 0. ) ); - uvs.push_back( std::make_pair( 1., 0. ) ); - uvs.push_back( std::make_pair( 1., 1. ) ); - uvs.push_back( std::make_pair( 0., 1. ) ); + uvs.emplace_back( 0., 0. ); + uvs.emplace_back( 1., 0. ); + uvs.emplace_back( 1., 1. ); + uvs.emplace_back( 0., 1. ); for ( size_t i = 0; i < 4; ++i ) { PointXYZ orig = corners[i]; From 98e317c14d6afaadce57210d47c9025ff2737996 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 3 Sep 2019 09:48:58 +0100 Subject: [PATCH 07/40] clang-tidy: modernize-use-equals-default --- cmake/FeatureClangTidy.cmake | 2 ++ src/apps/atlas.cc | 2 +- src/atlas/array/native/NativeArray.cc | 2 +- src/atlas/domain/detail/EmptyDomain.cc | 2 +- src/atlas/field/FieldCreator.cc | 4 ++-- src/atlas/field/State.cc | 4 ++-- src/atlas/functionspace/EdgeColumns.cc | 2 +- src/atlas/functionspace/NodeColumns.cc | 2 +- src/atlas/functionspace/Spectral.cc | 2 +- src/atlas/functionspace/StructuredColumns.cc | 6 +++--- src/atlas/grid/Distribution.cc | 2 +- src/atlas/grid/detail/distribution/DistributionImpl.cc | 2 +- src/atlas/grid/detail/grid/Regional.cc | 2 +- src/atlas/grid/detail/grid/Structured.cc | 4 ++-- src/atlas/grid/detail/grid/Unstructured.cc | 2 +- src/atlas/grid/detail/partitioner/Partitioner.cc | 2 +- src/atlas/grid/detail/partitioner/TransPartitioner.cc | 2 +- src/atlas/mesh/Connectivity.cc | 2 +- src/atlas/mesh/ElementType.cc | 4 ++-- src/atlas/mesh/HybridElements.cc | 2 +- src/atlas/mesh/actions/BuildDualMesh.cc | 2 +- src/atlas/mesh/actions/BuildEdges.cc | 2 +- src/atlas/mesh/actions/BuildNode2CellConnectivity.cc | 2 +- src/atlas/mesh/detail/PartitionGraph.cc | 2 +- src/atlas/meshgenerator/detail/DelaunayMeshGenerator.cc | 4 ++-- src/atlas/meshgenerator/detail/MeshGeneratorImpl.cc | 4 ++-- src/atlas/numerics/Nabla.cc | 2 +- src/atlas/numerics/fvm/Nabla.cc | 2 +- src/atlas/output/Gmsh.cc | 2 +- src/atlas/output/Output.cc | 4 ++-- src/atlas/output/detail/GmshIO.cc | 2 +- src/atlas/parallel/GatherScatter.cc | 2 +- src/atlas/parallel/HaloExchange.cc | 2 +- src/atlas/runtime/trace/Timings.cc | 2 +- src/atlas/trans/Cache.cc | 4 ++-- src/atlas/trans/LegendreCacheCreator.cc | 2 +- src/atlas/trans/VorDivToUV.cc | 2 +- src/atlas/trans/detail/TransImpl.cc | 2 +- src/atlas/trans/ifs/LegendreCacheCreatorIFS.cc | 2 +- src/atlas/trans/ifs/TransIFS.cc | 4 ++-- src/atlas/trans/ifs/TransIFSNodeColumns.cc | 2 +- src/atlas/trans/ifs/TransIFSStructuredColumns.cc | 2 +- src/atlas/trans/ifs/VorDivToUVIFS.cc | 2 +- src/atlas/trans/local/LegendreCacheCreatorLocal.cc | 2 +- src/atlas/trans/local/TransLocal.cc | 2 +- src/atlas/trans/local/VorDivToUVLocal.cc | 2 +- src/atlas/util/Polygon.cc | 4 ++-- src/tests/grid/test_state.cc | 2 +- 48 files changed, 61 insertions(+), 59 deletions(-) diff --git a/cmake/FeatureClangTidy.cmake b/cmake/FeatureClangTidy.cmake index 21c18d5d7..6292e4915 100644 --- a/cmake/FeatureClangTidy.cmake +++ b/cmake/FeatureClangTidy.cmake @@ -33,6 +33,8 @@ if (HAVE_CLANG_TIDY) modernize-use-using modernize-use-override modernize-use-emplace + modernize-use-equals-default + modernize-use-equals-delete ) set( CLANG_TIDY_CHECKS "${CLANG_TIDY_CHECKS},${_clang_tidy_check}" ) endforeach() diff --git a/src/apps/atlas.cc b/src/apps/atlas.cc index 93dabd8c7..077e61a67 100644 --- a/src/apps/atlas.cc +++ b/src/apps/atlas.cc @@ -22,7 +22,7 @@ class Version : public Tool { public: Version( int argc, char** argv ) : Tool( argc, argv ) {} - ~Version() override {} + ~Version() override = default; void run() override; }; diff --git a/src/atlas/array/native/NativeArray.cc b/src/atlas/array/native/NativeArray.cc index 8ab381ce5..0adb2208a 100644 --- a/src/atlas/array/native/NativeArray.cc +++ b/src/atlas/array/native/NativeArray.cc @@ -60,7 +60,7 @@ Array* Array::wrap( Value* data, const ArraySpec& spec ) { return new ArrayT( new native::WrappedDataStore( data ), spec ); } -Array::~Array() {} +Array::~Array() = default; Array* Array::create( DataType datatype, const ArrayShape& shape ) { switch ( datatype.kind() ) { diff --git a/src/atlas/domain/detail/EmptyDomain.cc b/src/atlas/domain/detail/EmptyDomain.cc index c7153bd8f..6e0145e18 100644 --- a/src/atlas/domain/detail/EmptyDomain.cc +++ b/src/atlas/domain/detail/EmptyDomain.cc @@ -19,7 +19,7 @@ namespace atlas { namespace domain { -EmptyDomain::EmptyDomain() {} +EmptyDomain::EmptyDomain() = default; EmptyDomain::EmptyDomain( const eckit::Parametrisation& p ) {} diff --git a/src/atlas/field/FieldCreator.cc b/src/atlas/field/FieldCreator.cc index dbef986fd..5eeebbcad 100644 --- a/src/atlas/field/FieldCreator.cc +++ b/src/atlas/field/FieldCreator.cc @@ -55,9 +55,9 @@ struct force_link { // ------------------------------------------------------------------ -FieldCreator::FieldCreator() {} +FieldCreator::FieldCreator() = default; -FieldCreator::~FieldCreator() {} +FieldCreator::~FieldCreator() = default; FieldCreatorFactory::FieldCreatorFactory( const std::string& name ) : name_( name ) { pthread_once( &once, init ); diff --git a/src/atlas/field/State.cc b/src/atlas/field/State.cc index 03df809dd..0fb48e395 100644 --- a/src/atlas/field/State.cc +++ b/src/atlas/field/State.cc @@ -58,7 +58,7 @@ void State::initialize( const std::string& generator, const eckit::Parametrisati //------------------------------------------------------------------------------------------------------ -State::State() {} +State::State() = default; State::State( const std::string& generator, const eckit::Parametrisation& params ) { initialize( generator, params ); @@ -147,7 +147,7 @@ void State::remove( const std::string& name ) { StateGenerator::StateGenerator( const eckit::Parametrisation& ) {} -StateGenerator::~StateGenerator() {} +StateGenerator::~StateGenerator() = default; StateGenerator* StateGeneratorFactory::build( const std::string& name, const eckit::Parametrisation& param ) { pthread_once( &once, init ); diff --git a/src/atlas/functionspace/EdgeColumns.cc b/src/atlas/functionspace/EdgeColumns.cc index 4eeb37a73..d806f4ade 100644 --- a/src/atlas/functionspace/EdgeColumns.cc +++ b/src/atlas/functionspace/EdgeColumns.cc @@ -283,7 +283,7 @@ EdgeColumns::EdgeColumns( const Mesh& mesh, const eckit::Configuration& config ) ATLAS_ASSERT( nb_edges_ ); } -EdgeColumns::~EdgeColumns() {} +EdgeColumns::~EdgeColumns() = default; size_t EdgeColumns::footprint() const { size_t size = sizeof( *this ); diff --git a/src/atlas/functionspace/NodeColumns.cc b/src/atlas/functionspace/NodeColumns.cc index ae58d4b79..81cf4a005 100644 --- a/src/atlas/functionspace/NodeColumns.cc +++ b/src/atlas/functionspace/NodeColumns.cc @@ -231,7 +231,7 @@ NodeColumns::NodeColumns( Mesh mesh, const eckit::Configuration& config ) : } } -NodeColumns::~NodeColumns() {} +NodeColumns::~NodeColumns() = default; std::string NodeColumns::distribution() const { return mesh().metadata().getString( "distribution" ); diff --git a/src/atlas/functionspace/Spectral.cc b/src/atlas/functionspace/Spectral.cc index 584f33e92..d46ea0ce4 100644 --- a/src/atlas/functionspace/Spectral.cc +++ b/src/atlas/functionspace/Spectral.cc @@ -192,7 +192,7 @@ Spectral::Spectral( const trans::Trans& trans, const eckit::Configuration& confi config.get( "levels", nb_levels_ ); } -Spectral::~Spectral() {} +Spectral::~Spectral() = default; std::string Spectral::distribution() const { return parallelisation_->distribution(); diff --git a/src/atlas/functionspace/StructuredColumns.cc b/src/atlas/functionspace/StructuredColumns.cc index 8b7966219..74f8e3375 100644 --- a/src/atlas/functionspace/StructuredColumns.cc +++ b/src/atlas/functionspace/StructuredColumns.cc @@ -125,7 +125,7 @@ class StructuredColumnsHaloExchangeCache : public util::CachesizeHalo() ); return value; } - ~StructuredColumnsHaloExchangeCache() override {} + ~StructuredColumnsHaloExchangeCache() override = default; }; class StructuredColumnsGatherScatterCache : public util::Cache, @@ -162,7 +162,7 @@ class StructuredColumnsGatherScatterCache : public util::Cache( funcspace->global_index() ).data(), funcspace->sizeOwned() ); return value; } - ~StructuredColumnsGatherScatterCache() override {} + ~StructuredColumnsGatherScatterCache() override = default; }; class StructuredColumnsChecksumCache : public util::Cache, @@ -197,7 +197,7 @@ class StructuredColumnsChecksumCache : public util::Cachesetup( gather ); return value; } - ~StructuredColumnsChecksumCache() override {} + ~StructuredColumnsChecksumCache() override = default; }; diff --git a/src/atlas/grid/Distribution.cc b/src/atlas/grid/Distribution.cc index 6fe2b4af0..bd274fd1a 100644 --- a/src/atlas/grid/Distribution.cc +++ b/src/atlas/grid/Distribution.cc @@ -28,7 +28,7 @@ Distribution::Distribution( const Grid& grid, const Partitioner& partitioner ) : Distribution::Distribution( idx_t npts, int part[], int part0 ) : Handle( new Implementation( npts, part, part0 ) ) {} -Distribution::~Distribution() {} +Distribution::~Distribution() = default; int Distribution::partition( const gidx_t gidx ) const { return get()->partition( gidx ); diff --git a/src/atlas/grid/detail/distribution/DistributionImpl.cc b/src/atlas/grid/detail/distribution/DistributionImpl.cc index 2e0663586..3846b561c 100644 --- a/src/atlas/grid/detail/distribution/DistributionImpl.cc +++ b/src/atlas/grid/detail/distribution/DistributionImpl.cc @@ -67,7 +67,7 @@ DistributionImpl::DistributionImpl( idx_t npts, int part[], int part0 ) { type_ = distribution_type( nb_partitions_ ); } -DistributionImpl::~DistributionImpl() {} +DistributionImpl::~DistributionImpl() = default; void DistributionImpl::print( std::ostream& s ) const { s << "Distribution( " diff --git a/src/atlas/grid/detail/grid/Regional.cc b/src/atlas/grid/detail/grid/Regional.cc index 15211e2ca..3e29bc2a8 100644 --- a/src/atlas/grid/detail/grid/Regional.cc +++ b/src/atlas/grid/detail/grid/Regional.cc @@ -33,7 +33,7 @@ static Domain domain( const Grid::Config& grid ) { struct ConfigParser { struct Parsed { - Parsed() {} + Parsed() = default; Parsed( std::initializer_list interval ) : min( *interval.begin() ), max( *( interval.begin() + 1 ) ) {} double min; double max; diff --git a/src/atlas/grid/detail/grid/Structured.cc b/src/atlas/grid/detail/grid/Structured.cc index 6d66532bb..57570ba70 100644 --- a/src/atlas/grid/detail/grid/Structured.cc +++ b/src/atlas/grid/detail/grid/Structured.cc @@ -92,11 +92,11 @@ Domain Structured::computeDomain() const { return RectangularDomain( {xspace().min(), xspace().max()}, {yspace().min(), yspace().max()}, projection_.units() ); } -Structured::~Structured() {} +Structured::~Structured() = default; Structured::XSpace::XSpace() : impl_( nullptr ) {} -Structured::XSpace::XSpace( const XSpace& xspace ) : impl_( xspace.impl_ ) {} +Structured::XSpace::XSpace( const XSpace& xspace ) = default; template Structured::XSpace::XSpace( const std::array& interval, const NVector& N, bool endpoint ) : diff --git a/src/atlas/grid/detail/grid/Unstructured.cc b/src/atlas/grid/detail/grid/Unstructured.cc index ca3bf7a43..d2178f282 100644 --- a/src/atlas/grid/detail/grid/Unstructured.cc +++ b/src/atlas/grid/detail/grid/Unstructured.cc @@ -159,7 +159,7 @@ Unstructured::Unstructured( std::initializer_list initializer_list ) : domain_ = GlobalDomain(); } -Unstructured::~Unstructured() {} +Unstructured::~Unstructured() = default; Grid::uid_t Unstructured::name() const { if ( shortName_.empty() ) { diff --git a/src/atlas/grid/detail/partitioner/Partitioner.cc b/src/atlas/grid/detail/partitioner/Partitioner.cc index 5c5b1dc19..a8c8bcc0d 100644 --- a/src/atlas/grid/detail/partitioner/Partitioner.cc +++ b/src/atlas/grid/detail/partitioner/Partitioner.cc @@ -54,7 +54,7 @@ Partitioner::Partitioner() : nb_partitions_( mpi::comm().size() ) {} Partitioner::Partitioner( const idx_t nb_partitions ) : nb_partitions_( nb_partitions ) {} -Partitioner::~Partitioner() {} +Partitioner::~Partitioner() = default; idx_t Partitioner::nb_partitions() const { return nb_partitions_; diff --git a/src/atlas/grid/detail/partitioner/TransPartitioner.cc b/src/atlas/grid/detail/partitioner/TransPartitioner.cc index 7bebe1e1f..4820747f9 100644 --- a/src/atlas/grid/detail/partitioner/TransPartitioner.cc +++ b/src/atlas/grid/detail/partitioner/TransPartitioner.cc @@ -42,7 +42,7 @@ TransPartitioner::TransPartitioner( const idx_t N ) : Partitioner( N ) { } } -TransPartitioner::~TransPartitioner() {} +TransPartitioner::~TransPartitioner() = default; void TransPartitioner::partition( const Grid& grid, int part[] ) const { ATLAS_TRACE( "TransPartitioner::partition" ); diff --git a/src/atlas/mesh/Connectivity.cc b/src/atlas/mesh/Connectivity.cc index fe862b653..fbdc8d3e5 100644 --- a/src/atlas/mesh/Connectivity.cc +++ b/src/atlas/mesh/Connectivity.cc @@ -452,7 +452,7 @@ MultiBlockConnectivityImpl::MultiBlockConnectivityImpl( eckit::Stream& s ) : Irr //------------------------------------------------------------------------------------------------------ -MultiBlockConnectivityImpl::~MultiBlockConnectivityImpl() {} +MultiBlockConnectivityImpl::~MultiBlockConnectivityImpl() = default; //------------------------------------------------------------------------------------------------------ diff --git a/src/atlas/mesh/ElementType.cc b/src/atlas/mesh/ElementType.cc index fb1bdcb0e..3d54dd620 100644 --- a/src/atlas/mesh/ElementType.cc +++ b/src/atlas/mesh/ElementType.cc @@ -21,8 +21,8 @@ ElementType* ElementType::create( const std::string& ) { ATLAS_NOTIMPLEMENTED; } -ElementType::ElementType() {} -ElementType::~ElementType() {} +ElementType::ElementType() = default; +ElementType::~ElementType() = default; //----------------------------------------------------------------------------- diff --git a/src/atlas/mesh/HybridElements.cc b/src/atlas/mesh/HybridElements.cc index 1da6f5996..981ac4376 100644 --- a/src/atlas/mesh/HybridElements.cc +++ b/src/atlas/mesh/HybridElements.cc @@ -75,7 +75,7 @@ HybridElements::HybridElements() : size_( 0 ), elements_size_(), elements_begin_ cell_connectivity_ = &add( new Connectivity( "cell" ) ); } -HybridElements::~HybridElements() {} +HybridElements::~HybridElements() = default; Field HybridElements::add( const Field& field ) { ATLAS_ASSERT( field ); diff --git a/src/atlas/mesh/actions/BuildDualMesh.cc b/src/atlas/mesh/actions/BuildDualMesh.cc index b1d0900a4..008685c03 100644 --- a/src/atlas/mesh/actions/BuildDualMesh.cc +++ b/src/atlas/mesh/actions/BuildDualMesh.cc @@ -65,7 +65,7 @@ void global_bounding_box( const mesh::Nodes& nodes, double min[2], double max[2] } struct Node { - Node() {} + Node() = default; Node( gidx_t gid, idx_t idx ) { g = gid; i = idx; diff --git a/src/atlas/mesh/actions/BuildEdges.cc b/src/atlas/mesh/actions/BuildEdges.cc index a0d93acb5..7458f50f4 100644 --- a/src/atlas/mesh/actions/BuildEdges.cc +++ b/src/atlas/mesh/actions/BuildEdges.cc @@ -49,7 +49,7 @@ namespace actions { namespace { // anonymous struct Sort { - Sort() {} + Sort() = default; Sort( gidx_t gid, idx_t idx ) { g = gid; i = idx; diff --git a/src/atlas/mesh/actions/BuildNode2CellConnectivity.cc b/src/atlas/mesh/actions/BuildNode2CellConnectivity.cc index 72019c3ec..9cfb8a6a1 100644 --- a/src/atlas/mesh/actions/BuildNode2CellConnectivity.cc +++ b/src/atlas/mesh/actions/BuildNode2CellConnectivity.cc @@ -52,7 +52,7 @@ namespace actions { namespace { // anonymous struct Sort { - Sort() {} + Sort() = default; Sort( gidx_t gid, idx_t idx ) { g = gid; i = idx; diff --git a/src/atlas/mesh/detail/PartitionGraph.cc b/src/atlas/mesh/detail/PartitionGraph.cc index 7037a5a8d..6639f1814 100644 --- a/src/atlas/mesh/detail/PartitionGraph.cc +++ b/src/atlas/mesh/detail/PartitionGraph.cc @@ -121,7 +121,7 @@ PartitionGraph::Neighbours PartitionGraph::nearestNeighbours( const idx_t partit return Neighbours( values_.data() + displs_[partition], values_.data() + displs_[partition] + counts_[partition] ); } -PartitionGraph::PartitionGraph() {} +PartitionGraph::PartitionGraph() = default; PartitionGraph::PartitionGraph( idx_t values[], idx_t rows, idx_t displs[], idx_t counts[] ) { displs_.assign( displs, displs + rows ); diff --git a/src/atlas/meshgenerator/detail/DelaunayMeshGenerator.cc b/src/atlas/meshgenerator/detail/DelaunayMeshGenerator.cc index 64a96c406..e34baedc4 100644 --- a/src/atlas/meshgenerator/detail/DelaunayMeshGenerator.cc +++ b/src/atlas/meshgenerator/detail/DelaunayMeshGenerator.cc @@ -35,11 +35,11 @@ namespace meshgenerator { //---------------------------------------------------------------------------------------------------------------------- -DelaunayMeshGenerator::DelaunayMeshGenerator() {} +DelaunayMeshGenerator::DelaunayMeshGenerator() = default; DelaunayMeshGenerator::DelaunayMeshGenerator( const eckit::Parametrisation& ) {} -DelaunayMeshGenerator::~DelaunayMeshGenerator() {} +DelaunayMeshGenerator::~DelaunayMeshGenerator() = default; void DelaunayMeshGenerator::hash( eckit::Hash& h ) const { h.add( "Delaunay" ); diff --git a/src/atlas/meshgenerator/detail/MeshGeneratorImpl.cc b/src/atlas/meshgenerator/detail/MeshGeneratorImpl.cc index d08b62001..fafe0f103 100644 --- a/src/atlas/meshgenerator/detail/MeshGeneratorImpl.cc +++ b/src/atlas/meshgenerator/detail/MeshGeneratorImpl.cc @@ -29,9 +29,9 @@ namespace meshgenerator { //---------------------------------------------------------------------------------------------------------------------- -MeshGeneratorImpl::MeshGeneratorImpl() {} +MeshGeneratorImpl::MeshGeneratorImpl() = default; -MeshGeneratorImpl::~MeshGeneratorImpl() {} +MeshGeneratorImpl::~MeshGeneratorImpl() = default; Mesh MeshGeneratorImpl::operator()( const Grid& grid ) const { Mesh mesh; diff --git a/src/atlas/numerics/Nabla.cc b/src/atlas/numerics/Nabla.cc index 4423d351f..c9d70eda0 100644 --- a/src/atlas/numerics/Nabla.cc +++ b/src/atlas/numerics/Nabla.cc @@ -40,7 +40,7 @@ namespace numerics { NablaImpl::NablaImpl( const Method& method, const eckit::Parametrisation& ) : method_( &method ) {} -NablaImpl::~NablaImpl() {} +NablaImpl::~NablaImpl() = default; Nabla::Nabla( const Method& method, const eckit::Parametrisation& p ) : Handle( NablaFactory::build( method, p ) ) {} diff --git a/src/atlas/numerics/fvm/Nabla.cc b/src/atlas/numerics/fvm/Nabla.cc index 98c883882..39649faeb 100644 --- a/src/atlas/numerics/fvm/Nabla.cc +++ b/src/atlas/numerics/fvm/Nabla.cc @@ -48,7 +48,7 @@ Nabla::Nabla( const numerics::Method& method, const eckit::Parametrisation& p ) setup(); } -Nabla::~Nabla() {} +Nabla::~Nabla() = default; void Nabla::setup() { const mesh::Edges& edges = fvm_->mesh().edges(); diff --git a/src/atlas/output/Gmsh.cc b/src/atlas/output/Gmsh.cc index f33fe4c4d..d8d8c7e2b 100644 --- a/src/atlas/output/Gmsh.cc +++ b/src/atlas/output/Gmsh.cc @@ -201,7 +201,7 @@ Gmsh::Gmsh( const PathName& file, const eckit::Parametrisation& config ) { // ----------------------------------------------------------------------------- -Gmsh::~Gmsh() {} +Gmsh::~Gmsh() = default; // ----------------------------------------------------------------------------- diff --git a/src/atlas/output/Output.cc b/src/atlas/output/Output.cc index 2b3ee0e27..800adf79e 100644 --- a/src/atlas/output/Output.cc +++ b/src/atlas/output/Output.cc @@ -42,9 +42,9 @@ static void init() { m = new std::map(); } -OutputImpl::OutputImpl() {} +OutputImpl::OutputImpl() = default; -OutputImpl::~OutputImpl() {} +OutputImpl::~OutputImpl() = default; Output::Output( const std::string& key, Stream& stream, const eckit::Parametrisation& params ) : Handle( OutputFactory::build( key, stream, params ) ) {} diff --git a/src/atlas/output/detail/GmshIO.cc b/src/atlas/output/detail/GmshIO.cc index 70dc8ec4b..576297681 100644 --- a/src/atlas/output/detail/GmshIO.cc +++ b/src/atlas/output/detail/GmshIO.cc @@ -517,7 +517,7 @@ GmshIO::GmshIO() { options.set>( "levels", std::vector() ); } -GmshIO::~GmshIO() {} +GmshIO::~GmshIO() = default; Mesh GmshIO::read( const PathName& file_path ) const { Mesh mesh; diff --git a/src/atlas/parallel/GatherScatter.cc b/src/atlas/parallel/GatherScatter.cc index a3016ac49..0b8c642f6 100644 --- a/src/atlas/parallel/GatherScatter.cc +++ b/src/atlas/parallel/GatherScatter.cc @@ -53,7 +53,7 @@ struct Node { idx_t i; gidx_t g; - Node() {} + Node() = default; Node( gidx_t gid, int part, idx_t idx ) { g = gid; p = part; diff --git a/src/atlas/parallel/HaloExchange.cc b/src/atlas/parallel/HaloExchange.cc index 11848785d..904defc65 100644 --- a/src/atlas/parallel/HaloExchange.cc +++ b/src/atlas/parallel/HaloExchange.cc @@ -58,7 +58,7 @@ HaloExchange::HaloExchange( const std::string& name ) : name_( name ), is_setup_ nproc = mpi::comm().size(); } -HaloExchange::~HaloExchange() {} +HaloExchange::~HaloExchange() = default; void HaloExchange::setup( const int part[], const idx_t remote_idx[], const int base, const idx_t parsize ) { ATLAS_TRACE( "HaloExchange::setup" ); diff --git a/src/atlas/runtime/trace/Timings.cc b/src/atlas/runtime/trace/Timings.cc index 9d7778a0b..61410da59 100644 --- a/src/atlas/runtime/trace/Timings.cc +++ b/src/atlas/runtime/trace/Timings.cc @@ -48,7 +48,7 @@ class TimingsRegistry { std::map> labels_; - TimingsRegistry() {} + TimingsRegistry() = default; public: static TimingsRegistry& instance() { diff --git a/src/atlas/trans/Cache.cc b/src/atlas/trans/Cache.cc index 5bd96586e..00a050358 100644 --- a/src/atlas/trans/Cache.cc +++ b/src/atlas/trans/Cache.cc @@ -69,13 +69,13 @@ Cache::Cache( const TransImpl* trans ) : Cache::Cache() : trans_( nullptr ), legendre_( new EmptyCacheEntry() ), fft_( new EmptyCacheEntry() ) {} -Cache::Cache( const Cache& other ) : trans_( other.trans_ ), legendre_( other.legendre_ ), fft_( other.fft_ ) {} +Cache::Cache( const Cache& other ) = default; Cache::operator bool() const { return trans_ || bool( legendre() ); } -Cache::~Cache() {} +Cache::~Cache() = default; TransCache::TransCache( const Trans& trans ) : Cache( trans.get() ) {} diff --git a/src/atlas/trans/LegendreCacheCreator.cc b/src/atlas/trans/LegendreCacheCreator.cc index 4f13a3b33..f93defcf0 100644 --- a/src/atlas/trans/LegendreCacheCreator.cc +++ b/src/atlas/trans/LegendreCacheCreator.cc @@ -26,7 +26,7 @@ namespace atlas { namespace trans { -LegendreCacheCreatorImpl::~LegendreCacheCreatorImpl() {} +LegendreCacheCreatorImpl::~LegendreCacheCreatorImpl() = default; namespace { diff --git a/src/atlas/trans/VorDivToUV.cc b/src/atlas/trans/VorDivToUV.cc index 009cd2422..411619812 100644 --- a/src/atlas/trans/VorDivToUV.cc +++ b/src/atlas/trans/VorDivToUV.cc @@ -31,7 +31,7 @@ namespace atlas { namespace trans { -VorDivToUVImpl::~VorDivToUVImpl() {} +VorDivToUVImpl::~VorDivToUVImpl() = default; namespace { diff --git a/src/atlas/trans/detail/TransImpl.cc b/src/atlas/trans/detail/TransImpl.cc index 33341453b..ce26544d2 100644 --- a/src/atlas/trans/detail/TransImpl.cc +++ b/src/atlas/trans/detail/TransImpl.cc @@ -15,7 +15,7 @@ namespace atlas { namespace trans { -TransImpl::~TransImpl() {} +TransImpl::~TransImpl() = default; int TransImpl::handle() const { ATLAS_NOTIMPLEMENTED; diff --git a/src/atlas/trans/ifs/LegendreCacheCreatorIFS.cc b/src/atlas/trans/ifs/LegendreCacheCreatorIFS.cc index db44fde10..df6774af4 100644 --- a/src/atlas/trans/ifs/LegendreCacheCreatorIFS.cc +++ b/src/atlas/trans/ifs/LegendreCacheCreatorIFS.cc @@ -86,7 +86,7 @@ std::string LegendreCacheCreatorIFS::uid() const { return unique_identifier_; } -LegendreCacheCreatorIFS::~LegendreCacheCreatorIFS() {} +LegendreCacheCreatorIFS::~LegendreCacheCreatorIFS() = default; bool LegendreCacheCreatorIFS::supported() const { if ( GaussianGrid( grid_ ) ) { diff --git a/src/atlas/trans/ifs/TransIFS.cc b/src/atlas/trans/ifs/TransIFS.cc index dae13cdb5..e1783dc5b 100644 --- a/src/atlas/trans/ifs/TransIFS.cc +++ b/src/atlas/trans/ifs/TransIFS.cc @@ -49,7 +49,7 @@ static TransBuilderGrid builder( "ifs", "ifs" ); class TransParameters { public: TransParameters( const TransIFS& trans, const eckit::Configuration& config ) : trans_( trans ), config_( config ) {} - ~TransParameters() {} + ~TransParameters() = default; bool scalar_derivatives() const { return config_.getBool( "scalar_derivatives", false ); } @@ -886,7 +886,7 @@ TransIFS::TransIFS( const Cache& cache, const Grid& grid, const Domain& domain, ATLAS_ASSERT( domain.global() ); } -TransIFS::~TransIFS() {} +TransIFS::~TransIFS() = default; int atlas::trans::TransIFS::truncation() const { return std::max( 0, trans_->nsmax ); diff --git a/src/atlas/trans/ifs/TransIFSNodeColumns.cc b/src/atlas/trans/ifs/TransIFSNodeColumns.cc index 73d75161d..af1d44007 100644 --- a/src/atlas/trans/ifs/TransIFSNodeColumns.cc +++ b/src/atlas/trans/ifs/TransIFSNodeColumns.cc @@ -27,7 +27,7 @@ TransIFSNodeColumns::TransIFSNodeColumns( const Cache& cache, const functionspac spectral_ = sp; } -TransIFSNodeColumns::~TransIFSNodeColumns() {} +TransIFSNodeColumns::~TransIFSNodeColumns() = default; namespace { static TransBuilderFunctionSpace builder( "ifs(NodeColumns,Spectral)", "ifs" ); diff --git a/src/atlas/trans/ifs/TransIFSStructuredColumns.cc b/src/atlas/trans/ifs/TransIFSStructuredColumns.cc index 1da27e1ef..7ab38a280 100644 --- a/src/atlas/trans/ifs/TransIFSStructuredColumns.cc +++ b/src/atlas/trans/ifs/TransIFSStructuredColumns.cc @@ -29,7 +29,7 @@ TransIFSStructuredColumns::TransIFSStructuredColumns( const Cache& cache, const spectral_ = sp; } -TransIFSStructuredColumns::~TransIFSStructuredColumns() {} +TransIFSStructuredColumns::~TransIFSStructuredColumns() = default; namespace { static TransBuilderFunctionSpace builder( "ifs(StructuredColumns,Spectral)", "ifs" ); diff --git a/src/atlas/trans/ifs/VorDivToUVIFS.cc b/src/atlas/trans/ifs/VorDivToUVIFS.cc index 7adcaccf7..0b4e1118a 100644 --- a/src/atlas/trans/ifs/VorDivToUVIFS.cc +++ b/src/atlas/trans/ifs/VorDivToUVIFS.cc @@ -57,7 +57,7 @@ VorDivToUVIFS::VorDivToUVIFS( const int truncation, const eckit::Configuration& VorDivToUVIFS::VorDivToUVIFS( const FunctionSpace& fs, const eckit::Configuration& ) : truncation_( Spectral( fs ).truncation() ) {} -VorDivToUVIFS::~VorDivToUVIFS() {} +VorDivToUVIFS::~VorDivToUVIFS() = default; } // namespace trans } // namespace atlas diff --git a/src/atlas/trans/local/LegendreCacheCreatorLocal.cc b/src/atlas/trans/local/LegendreCacheCreatorLocal.cc index 5cec303e2..4995105aa 100644 --- a/src/atlas/trans/local/LegendreCacheCreatorLocal.cc +++ b/src/atlas/trans/local/LegendreCacheCreatorLocal.cc @@ -111,7 +111,7 @@ std::string LegendreCacheCreatorLocal::uid() const { return unique_identifier_; } -LegendreCacheCreatorLocal::~LegendreCacheCreatorLocal() {} +LegendreCacheCreatorLocal::~LegendreCacheCreatorLocal() = default; LegendreCacheCreatorLocal::LegendreCacheCreatorLocal( const Grid& grid, int truncation, const eckit::Configuration& config ) : diff --git a/src/atlas/trans/local/TransLocal.cc b/src/atlas/trans/local/TransLocal.cc index ddbf815eb..87e9ef100 100644 --- a/src/atlas/trans/local/TransLocal.cc +++ b/src/atlas/trans/local/TransLocal.cc @@ -59,7 +59,7 @@ namespace { class TransParameters { public: TransParameters( const eckit::Configuration& config ) : config_( config ) {} - ~TransParameters() {} + ~TransParameters() = default; /* * For the future diff --git a/src/atlas/trans/local/VorDivToUVLocal.cc b/src/atlas/trans/local/VorDivToUVLocal.cc index 8f2bcbe43..dcd8fbc75 100644 --- a/src/atlas/trans/local/VorDivToUVLocal.cc +++ b/src/atlas/trans/local/VorDivToUVLocal.cc @@ -195,7 +195,7 @@ VorDivToUVLocal::VorDivToUVLocal( const int truncation, const eckit::Configurati VorDivToUVLocal::VorDivToUVLocal( const FunctionSpace& fs, const eckit::Configuration& config ) : truncation_( Spectral( fs ).truncation() ) {} -VorDivToUVLocal::~VorDivToUVLocal() {} +VorDivToUVLocal::~VorDivToUVLocal() = default; } // namespace trans } // namespace atlas diff --git a/src/atlas/util/Polygon.cc b/src/atlas/util/Polygon.cc index 9daa14c14..a44c5cd14 100644 --- a/src/atlas/util/Polygon.cc +++ b/src/atlas/util/Polygon.cc @@ -37,7 +37,7 @@ double cross_product_analog( const PointLonLat& A, const PointLonLat& B, const P //------------------------------------------------------------------------------------------------------ -Polygon::Polygon() {} +Polygon::Polygon() = default; Polygon::Polygon( const Polygon::edge_set_t& edges ) { ATLAS_TRACE(); @@ -162,7 +162,7 @@ PolygonCoordinates::PolygonCoordinates( const std::vector& points ) } } -PolygonCoordinates::~PolygonCoordinates() {} +PolygonCoordinates::~PolygonCoordinates() = default; const PointLonLat& PolygonCoordinates::coordinatesMax() const { return coordinatesMax_; diff --git a/src/tests/grid/test_state.cc b/src/tests/grid/test_state.cc index 8dac10df5..47a44b36e 100644 --- a/src/tests/grid/test_state.cc +++ b/src/tests/grid/test_state.cc @@ -41,7 +41,7 @@ namespace test { class MyStateGenerator : public StateGenerator { public: MyStateGenerator( const eckit::Parametrisation& p = util::Config() ) : StateGenerator( p ) {} - ~MyStateGenerator() override {} + ~MyStateGenerator() override = default; void generate( State& state, const eckit::Parametrisation& p = util::Config() ) const override; }; From 131f75db7d9bfc411d3ba8014630e76eb2f69bd7 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 4 Sep 2019 11:42:08 +0100 Subject: [PATCH 08/40] cosmetics --- src/atlas/util/Polygon.cc | 4 ++-- src/atlas/util/Polygon.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/atlas/util/Polygon.cc b/src/atlas/util/Polygon.cc index 9daa14c14..4f22ca3f4 100644 --- a/src/atlas/util/Polygon.cc +++ b/src/atlas/util/Polygon.cc @@ -55,10 +55,10 @@ Polygon::Polygon( const Polygon::edge_set_t& edges ) { clear(); reserve( extEdges.size() + 1 ); - push_back( extEdges.begin()->first ); + emplace_back( extEdges.begin()->first ); for ( edge_set_t::iterator e = extEdges.begin(); e != extEdges.end() && e->first == back(); e = extEdges.lower_bound( edge_t( back(), std::numeric_limits::min() ) ) ) { - push_back( e->second ); + emplace_back( e->second ); extEdges.erase( *e ); } ATLAS_ASSERT( front() == back() ); diff --git a/src/atlas/util/Polygon.h b/src/atlas/util/Polygon.h index 953233b25..1f5a20aed 100644 --- a/src/atlas/util/Polygon.h +++ b/src/atlas/util/Polygon.h @@ -49,8 +49,8 @@ class Polygon : public std::vector { }; }; - typedef std::set edge_set_t; - typedef std::vector container_t; + using edge_set_t = std::set; + using container_t = std::vector; // -- Constructors From 341e8d0f89386f177c74e7d670d74f0264cee6ae Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 4 Sep 2019 12:05:14 +0100 Subject: [PATCH 09/40] ATLAS-245 Create empty skeleton --- src/atlas/CMakeLists.txt | 2 ++ src/atlas/mesh/actions/ReorderHilbert.cc | 32 +++++++++++++++++ src/atlas/mesh/actions/ReorderHilbert.h | 34 ++++++++++++++++++ src/tests/mesh/CMakeLists.txt | 2 +- src/tests/mesh/test_hilbert_reordering.cc | 44 +++++++++++++++++++++++ 5 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 src/atlas/mesh/actions/ReorderHilbert.cc create mode 100644 src/atlas/mesh/actions/ReorderHilbert.h create mode 100644 src/tests/mesh/test_hilbert_reordering.cc diff --git a/src/atlas/CMakeLists.txt b/src/atlas/CMakeLists.txt index 21fda484d..9ef805a1b 100644 --- a/src/atlas/CMakeLists.txt +++ b/src/atlas/CMakeLists.txt @@ -287,6 +287,8 @@ mesh/actions/BuildXYZField.h mesh/actions/WriteLoadBalanceReport.cc mesh/actions/BuildTorusXYZField.h mesh/actions/BuildTorusXYZField.cc +mesh/actions/ReorderHilbert.h +mesh/actions/ReorderHilbert.cc meshgenerator.h meshgenerator/MeshGenerator.cc diff --git a/src/atlas/mesh/actions/ReorderHilbert.cc b/src/atlas/mesh/actions/ReorderHilbert.cc new file mode 100644 index 000000000..763e1fc19 --- /dev/null +++ b/src/atlas/mesh/actions/ReorderHilbert.cc @@ -0,0 +1,32 @@ +/* + * (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/mesh/actions/ReorderHilbert.h" +#include "atlas/runtime/Exception.h" +#include "atlas/runtime/Log.h" +#include "atlas/runtime/Trace.h" + +namespace atlas { +namespace mesh { +namespace actions { + +// ------------------------------------------------------------------------------------- + +ReorderHilbert::ReorderHilbert( Mesh& mesh ) : mesh_( mesh ) {} + +void ReorderHilbert::operator()() { + ATLAS_TRACE( "ReorderHilbert()" ); +} + +// ------------------------------------------------------------------ + +} // namespace actions +} // namespace mesh +} // namespace atlas diff --git a/src/atlas/mesh/actions/ReorderHilbert.h b/src/atlas/mesh/actions/ReorderHilbert.h new file mode 100644 index 000000000..f9416abcd --- /dev/null +++ b/src/atlas/mesh/actions/ReorderHilbert.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 + +namespace atlas { +class Mesh; + +namespace mesh { +namespace actions { + +// ------------------------------------------------------------------ + +class ReorderHilbert { +public: + ReorderHilbert( Mesh& mesh ); + void operator()(); + +private: + Mesh& mesh_; +}; + +// ------------------------------------------------------------------ + +} // namespace actions +} // namespace mesh +} // namespace atlas diff --git a/src/tests/mesh/CMakeLists.txt b/src/tests/mesh/CMakeLists.txt index 87a3a7778..0746db8fc 100644 --- a/src/tests/mesh/CMakeLists.txt +++ b/src/tests/mesh/CMakeLists.txt @@ -88,7 +88,7 @@ ecbuild_add_test( TARGET atlas_test_mesh_node2cell ) -foreach( test connectivity stream_connectivity elements ll meshgen3d rgg ) +foreach( test connectivity stream_connectivity elements ll meshgen3d rgg hilbert_reordering ) ecbuild_add_test( TARGET atlas_test_${test} SOURCES test_${test}.cc LIBS atlas diff --git a/src/tests/mesh/test_hilbert_reordering.cc b/src/tests/mesh/test_hilbert_reordering.cc new file mode 100644 index 000000000..41e60d908 --- /dev/null +++ b/src/tests/mesh/test_hilbert_reordering.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 + +#include "atlas/grid.h" +#include "atlas/mesh.h" +#include "atlas/meshgenerator.h" + +#include "atlas/runtime/Log.h" +#include "atlas/util/CoordinateEnums.h" +#include "atlas/mesh/actions/ReorderHilbert.h" + +#include "tests/AtlasTestEnvironment.h" + +namespace atlas { +namespace test { + +//----------------------------------------------------------------------------- + +CASE( "test_hilbert_reordering" ) { + Mesh mesh = StructuredMeshGenerator().generate( Grid("O64" ) ); + mesh::actions::ReorderHilbert{mesh}(); + auto lonlat = array::make_view( mesh.nodes().lonlat() ); + for( idx_t n=0; n< std::min(100,lonlat.shape(0)); ++n ) { + Log::info() << n << '\t' << PointLonLat{ lonlat(n,0), lonlat(n,1 ) } << std::endl; + } +} + +//----------------------------------------------------------------------------- + +} // namespace test +} // namespace atlas + +int main( int argc, char** argv ) { + return atlas::test::run( argc, argv ); +} From 63b677dd301f12b0b6e6cc546847c4d36052e9f6 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 5 Sep 2019 15:50:57 +0100 Subject: [PATCH 10/40] ATLAS-245 Hilbert algorithm implemented, python output, memory unchanged --- src/atlas/mesh/actions/ReorderHilbert.cc | 225 +++++++++++++++++++++- src/atlas/mesh/actions/ReorderHilbert.h | 12 +- src/tests/mesh/test_hilbert_reordering.cc | 158 ++++++++++++++- 3 files changed, 387 insertions(+), 8 deletions(-) diff --git a/src/atlas/mesh/actions/ReorderHilbert.cc b/src/atlas/mesh/actions/ReorderHilbert.cc index 763e1fc19..a0ae18e90 100644 --- a/src/atlas/mesh/actions/ReorderHilbert.cc +++ b/src/atlas/mesh/actions/ReorderHilbert.cc @@ -8,10 +8,21 @@ * nor does it submit to any jurisdiction. */ +#include +#include + +#include "atlas/array.h" +#include "atlas/domain/Domain.h" +#include "atlas/grid/Grid.h" +#include "atlas/mesh/Mesh.h" +#include "atlas/mesh/Nodes.h" #include "atlas/mesh/actions/ReorderHilbert.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/CoordinateEnums.h" +#include "atlas/util/Point.h" namespace atlas { namespace mesh { @@ -19,10 +30,222 @@ namespace actions { // ------------------------------------------------------------------------------------- -ReorderHilbert::ReorderHilbert( Mesh& mesh ) : mesh_( mesh ) {} +/// @brief Class to compute a global index given a coordinate, based on the +/// Hilbert Spacefilling Curve. +/// +/// This algorithm is based on: +/// - John J. Bartholdi and Paul Goldsman "Vertex-Labeling Algorithms for the Hilbert Spacefilling Curve"\n +/// It is adapted to return contiguous numbers of the gidx_t type, instead of a double [0,1] +/// +/// Given a bounding box and number of hilbert levels, the bounding box can be divided in +/// 2^(dim*levels) equally spaced cells. A given coordinate falling inside one of these cells, is assigned +/// the 1-dimensional Hilbert-index of this cell. To make sure that 1 coordinate corresponds to only 1 +/// Hilbert index, the number of levels have to be increased. +/// In 2D, the levels cannot be higher than 15, if you want the indices to fit in "unsigned int" type of 32bit. +/// +/// +/// No attempt is made to provide the most efficient algorithm. There exist other open-source +/// libraries with more efficient algorithms, such as libhilbert, but its LGPL license +/// is not compatible with this licence. +/// +/// @author Willem Deconinck +class Hilbert { +public: + /// Constructor + /// Initializes the hilbert space filling curve with a given "space" and "levels" + Hilbert( const Domain& domain, idx_t levels ); + + /// Compute the hilbert code for a given point in 2D + gidx_t operator()( const PointXY& point ); + + /// Compute the hilbert code for a given point in 2D + /// @param [out] relative_tolerance cell-size of smallest level divided by bounding-box size + gidx_t operator()( const PointXY& point, double& relative_tolerance ); + + /// Return the maximum hilbert code possible with the initialized levels + /// + /// Care has to be taken that this number is not larger than the precision of the type storing + /// the hilbert codes. + gidx_t nb_keys() const { return nb_keys_; } + +private: // functions + using box_t = std::array; + + /// @brief Recursive algorithm + gidx_t recursive_algorithm( const PointXY& p, const box_t& box, idx_t level ); + +private: // data + /// Vertex label type (4 vertices in 2D) + enum VertexLabel + { + A = 0, + B = 1, + C = 2, + D = 3 + }; + + /// Bounding box, defining the space to be filled + const RectangularDomain domain_; + + /// maximum recursion level of the Hilbert space filling curve + idx_t max_level_; + + /// maximum number of unique codes, computed by max_level + gidx_t nb_keys_; + gidx_t nb_keys_2_; +}; + +// ------------------------------------------------------------------------------------- + +Hilbert::Hilbert( const Domain& domain, idx_t levels ) : domain_{domain}, max_level_( levels ) { + nb_keys_2_ = gidx_t( std::pow( gidx_t( 4 ), gidx_t( max_level_ ) ) ); + nb_keys_ = nb_keys_2_ * 2; +} + + +gidx_t Hilbert::operator()( const PointXY& point ) { + box_t box; + box[A] = {domain_.xmin(), domain_.ymax()}; + box[B] = {domain_.xmin(), domain_.ymin()}; + box[C] = {domain_.xmax(), domain_.ymin()}; + box[D] = {domain_.xmax(), domain_.ymax()}; + const double xmid = ( domain_.xmin() + domain_.xmax() ) * 0.5; + if ( point.x() < xmid ) { + box[C].x() = xmid; + box[D].x() = xmid; + return recursive_algorithm( point, box, 0 ); + } + else { + box[A].x() = xmid; + box[B].x() = xmid; + return recursive_algorithm( point, box, 0 ) + nb_keys_2_; + } +} + +gidx_t Hilbert::recursive_algorithm( const PointXY& p, const box_t& box, idx_t level ) { + if ( level == max_level_ ) { + return 0; + } + + double min_distance = std::numeric_limits::max(); + + idx_t quadrant; + for ( idx_t idx = 0; idx < 4; ++idx ) { + double distance = box[idx].distance2( p ); + if ( distance < min_distance ) { + quadrant = idx; + min_distance = distance; + } + } + + box_t box_quadrant; + switch ( quadrant ) { + case A: + box_quadrant[A] = box[A]; + box_quadrant[B] = ( box[A] + box[D] ) * 0.5; + box_quadrant[C] = ( box[A] + box[C] ) * 0.5; + box_quadrant[D] = ( box[A] + box[B] ) * 0.5; + break; + case B: + box_quadrant[A] = ( box[B] + box[A] ) * 0.5; + box_quadrant[B] = box[B]; + box_quadrant[C] = ( box[B] + box[C] ) * 0.5; + box_quadrant[D] = ( box[B] + box[D] ) * 0.5; + break; + case C: + box_quadrant[A] = ( box[C] + box[A] ) * 0.5; + box_quadrant[B] = ( box[C] + box[B] ) * 0.5; + box_quadrant[C] = box[C]; + box_quadrant[D] = ( box[C] + box[D] ) * 0.5; + break; + case D: + box_quadrant[A] = ( box[D] + box[C] ) * 0.5; + box_quadrant[B] = ( box[D] + box[B] ) * 0.5; + box_quadrant[C] = ( box[D] + box[A] ) * 0.5; + box_quadrant[D] = box[D]; + break; + } + + // The key has 4 possible values per recursion (1 for each quadrant), + // which can be represented by 2 bits per recursion + // A --> 00 + // B --> 01 + // C --> 10 + // D --> 11 + // Trailing zero-bits are added depending on the level: + // level max_level_-1 --> none + // level max_level_-2 --> 00 + // level max_level_-2 --> 0000 + // level max_level_-3 --> 000000 + gidx_t key = 0; + auto index = ( max_level_ - level ) * 2 - 1; + gidx_t mask; + + // Create a mask value with all trailing bits for leftmost bit (of 2) + mask = gidx_t( 1 ) << index; + + // Add mask to key + if ( quadrant == C || quadrant == D ) { + key |= mask; + } + + // Create a mask value with all trailing bits for rightmost bit (of 2) + mask = gidx_t( 1 ) << ( index - 1 ); + + // Add mask to key + if ( quadrant == B || quadrant == D ) { + key |= mask; + } + + return recursive_algorithm( p, box_quadrant, level + 1 ) + key; +} + +// ------------------------------------------------------------------ + +ReorderHilbert::ReorderHilbert( Mesh& mesh, const eckit::Configuration& config ) : mesh_( mesh ) { + config.get( "recursion", recursion_ ); +} void ReorderHilbert::operator()() { ATLAS_TRACE( "ReorderHilbert()" ); + + auto xy = array::make_view( mesh_.nodes().xy() ); + auto ghost = array::make_view( mesh_.nodes().ghost() ); + + double xmin = std::numeric_limits::max(); + double xmax = -std::numeric_limits::max(); + double ymin = std::numeric_limits::max(); + double ymax = -std::numeric_limits::max(); + for ( idx_t i = 0; i < xy.shape( 0 ); ++i ) { + xmin = std::min( xmin, xy( i, XX ) ); + xmax = std::max( xmax, xy( i, XX ) ); + ymin = std::min( ymin, xy( i, YY ) ); + ymax = std::max( ymax, xy( i, YY ) ); + } + const auto& comm = atlas::mpi::comm(); + + comm.allReduceInPlace( xmin, eckit::mpi::min() ); + comm.allReduceInPlace( xmax, eckit::mpi::max() ); + comm.allReduceInPlace( ymin, eckit::mpi::min() ); + comm.allReduceInPlace( ymax, eckit::mpi::max() ); + auto domain = RectangularDomain( {xmin, xmax}, {ymin, ymax} ); + + Hilbert hilbert{domain, recursion_}; + + idx_t size = xy.shape( 0 ); + hilbert_reordering_.clear(); + hilbert_reordering_.reserve( size ); + for ( idx_t n = 0; n < size; ++n ) { + if ( not ghost( n ) ) { + PointXY p{xy( n, XX ), xy( n, YY )}; + auto hilbert_idx = hilbert( p ); + hilbert_reordering_.emplace_back( hilbert_idx, n ); + } + else { + hilbert_reordering_.emplace_back( hilbert.nb_keys() * 2 + n, n ); + } + } + std::sort( hilbert_reordering_.begin(), hilbert_reordering_.end() ); } // ------------------------------------------------------------------ diff --git a/src/atlas/mesh/actions/ReorderHilbert.h b/src/atlas/mesh/actions/ReorderHilbert.h index f9416abcd..36e2a9e4c 100644 --- a/src/atlas/mesh/actions/ReorderHilbert.h +++ b/src/atlas/mesh/actions/ReorderHilbert.h @@ -10,6 +10,12 @@ #pragma once +#include +#include + +#include "atlas/library/config.h" +#include "atlas/util/Config.h" + namespace atlas { class Mesh; @@ -20,11 +26,15 @@ namespace actions { class ReorderHilbert { public: - ReorderHilbert( Mesh& mesh ); + ReorderHilbert( Mesh& mesh, const eckit::Configuration& config = util::NoConfig() ); void operator()(); private: Mesh& mesh_; + idx_t recursion_{20}; + +public: + std::vector> hilbert_reordering_; }; // ------------------------------------------------------------------ diff --git a/src/tests/mesh/test_hilbert_reordering.cc b/src/tests/mesh/test_hilbert_reordering.cc index 41e60d908..3fbd3a5e6 100644 --- a/src/tests/mesh/test_hilbert_reordering.cc +++ b/src/tests/mesh/test_hilbert_reordering.cc @@ -14,9 +14,9 @@ #include "atlas/mesh.h" #include "atlas/meshgenerator.h" +#include "atlas/mesh/actions/ReorderHilbert.h" #include "atlas/runtime/Log.h" #include "atlas/util/CoordinateEnums.h" -#include "atlas/mesh/actions/ReorderHilbert.h" #include "tests/AtlasTestEnvironment.h" @@ -25,13 +25,159 @@ namespace test { //----------------------------------------------------------------------------- +void outputPythonScript( const eckit::PathName& filepath, const eckit::Configuration& config, + const array::ArrayView& xy, const mesh::actions::ReorderHilbert& reorder ) { + const eckit::mpi::Comm& comm = atlas::mpi::comm(); + int mpi_rank = int( comm.rank() ); + int mpi_size = int( comm.size() ); + + double xmin = std::numeric_limits::max(); + double xmax = -std::numeric_limits::max(); + for ( idx_t i = 0; i < xy.shape( 0 ); ++i ) { + xmin = std::min( xmin, xy( i, XX ) ); + xmax = std::max( xmax, xy( i, XX ) ); + } + comm.allReduceInPlace( xmin, eckit::mpi::min() ); + comm.allReduceInPlace( xmax, eckit::mpi::max() ); + + idx_t count = xy.shape( 0 ); + idx_t count_all = count; + comm.allReduceInPlace( count_all, eckit::mpi::sum() ); + + for ( int r = 0; r < mpi_size; ++r ) { + if ( mpi_rank == r ) { + std::ofstream f( filepath.asString().c_str(), mpi_rank == 0 ? std::ios::trunc : std::ios::app ); + + if ( mpi_rank == 0 ) { + f << "\n" + "# Configuration option to plot nodes" + "\n" + "plot_nodes = " + + std::string( ( config.getBool( "nodes", false ) ? "True" : "False" ) ) + + "\n" + "\n" + "import matplotlib.pyplot as plt" + "\n" + "from matplotlib.path import Path" + "\n" + "import matplotlib.patches as patches" + "\n" + "" + "\n" + "from itertools import cycle" + "\n" + "import matplotlib.cm as cm" + "\n" + "import numpy as np" + "\n" + "cycol = cycle([cm.Paired(i) for i in " + "np.linspace(0,1,12,endpoint=True)]).next" + "\n" + "" + "\n" + "fig = plt.figure()" + "\n" + "ax = fig.add_subplot(111,aspect='equal')" + "\n" + ""; + } + f << "\n" + "verts_" + << r << " = ["; + for ( idx_t n = 0; n < xy.shape( 0 ); ++n ) { + idx_t i = reorder.hilbert_reordering_[n].second; + f << "\n (" << xy( i, XX ) << ", " << xy( i, YY ) << "), "; + } + f << "\n]" + "\n" + "" + "\n" + "codes_" + << r + << " = [Path.MOVETO]" + "\n" + "codes_" + << r << ".extend([Path.LINETO] * " << ( xy.shape( 0 ) - 2 ) + << ")" + "\n" + "codes_" + << r + << ".extend([Path.LINETO])" + "\n" + "" + "\n" + "count_" + << r << " = " << count + << "\n" + "count_all_" + << r << " = " << count_all + << "\n" + "" + //"\n" "x_" << r << " = ["; for (idx_t i=0; i( mesh.nodes().lonlat() ); - for( idx_t n=0; n< std::min(100,lonlat.shape(0)); ++n ) { - Log::info() << n << '\t' << PointLonLat{ lonlat(n,0), lonlat(n,1 ) } << std::endl; + Mesh mesh = StructuredMeshGenerator().generate( Grid( "O16" ) ); + auto reorder = mesh::actions::ReorderHilbert{mesh, util::Config( "recursion", 6 )}; + auto xy = array::make_view( mesh.nodes().xy() ); + + reorder(); + +#if 1 + gidx_t previous_hilbert_idx{-1}; + for ( idx_t n = 0; n < xy.shape( 0 ); ++n ) { + auto hilbert_idx = reorder.hilbert_reordering_[n].first; + idx_t ip = reorder.hilbert_reordering_[n].second; + PointXY p{xy( ip, 0 ), xy( ip, 1 )}; + Log::info() << ip << '\t' << p << "\t" << hilbert_idx << std::endl; + if ( hilbert_idx == previous_hilbert_idx ) { + //throw_Exception("Duplicate hilbert index detected in ReorderHilbert", Here() ); + } + previous_hilbert_idx = hilbert_idx; } + + outputPythonScript( "hilbert.py", util::NoConfig(), xy, reorder ); +#endif } //----------------------------------------------------------------------------- From d436af181d8a0d9b3d87ef94f644600173701973 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Thu, 5 Sep 2019 17:51:23 +0100 Subject: [PATCH 11/40] ATLAS-245 Hilbert reordering implemented for nodes --- src/atlas/functionspace/NodeColumns.cc | 5 - src/atlas/mesh/actions/ReorderHilbert.cc | 118 ++++++++++++++++++++-- src/tests/mesh/test_hilbert_reordering.cc | 20 ++-- 3 files changed, 122 insertions(+), 21 deletions(-) diff --git a/src/atlas/functionspace/NodeColumns.cc b/src/atlas/functionspace/NodeColumns.cc index cdd2e5f2a..08debefda 100644 --- a/src/atlas/functionspace/NodeColumns.cc +++ b/src/atlas/functionspace/NodeColumns.cc @@ -345,11 +345,6 @@ Field NodeColumns::createField( const Field& other, const eckit::Configuration& namespace { -template -array::ArrayView get_field_view( const Field& field, bool on_device ) { - return on_device ? array::make_device_view( field ) : array::make_view( field ); -} - template void dispatch_haloExchange( Field& field, const parallel::HaloExchange& halo_exchange, bool on_device ) { if ( field.datatype() == array::DataType::kind() ) { diff --git a/src/atlas/mesh/actions/ReorderHilbert.cc b/src/atlas/mesh/actions/ReorderHilbert.cc index a0ae18e90..baf62a28f 100644 --- a/src/atlas/mesh/actions/ReorderHilbert.cc +++ b/src/atlas/mesh/actions/ReorderHilbert.cc @@ -14,6 +14,7 @@ #include "atlas/array.h" #include "atlas/domain/Domain.h" #include "atlas/grid/Grid.h" +#include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/actions/ReorderHilbert.h" @@ -206,11 +207,90 @@ ReorderHilbert::ReorderHilbert( Mesh& mesh, const eckit::Configuration& config ) config.get( "recursion", recursion_ ); } -void ReorderHilbert::operator()() { - ATLAS_TRACE( "ReorderHilbert()" ); +template +struct ReorderField {}; + +template +struct ReorderField { + static constexpr int Rank = 1; + static std::string apply( Field& field, const std::vector& order ) { + auto array = array::make_view( field ); + array::ArrayT tmp_array( field.shape() ); + auto tmp = array::make_view( tmp_array ); + for ( idx_t n = 0; n < array.shape( 0 ); ++n ) { + tmp( n ) = array( n ); + } + for ( idx_t n = 0; n < array.shape( 0 ); ++n ) { + array( n ) = tmp( order[n] ); + } + return field.name(); + } +}; - auto xy = array::make_view( mesh_.nodes().xy() ); - auto ghost = array::make_view( mesh_.nodes().ghost() ); +template +struct ReorderField { + static constexpr int Rank = 2; + static std::string apply( Field& field, const std::vector& order ) { + auto array = array::make_view( field ); + array::ArrayT tmp_array( field.shape() ); + auto tmp = array::make_view( tmp_array ); + for ( idx_t n = 0; n < array.shape( 0 ); ++n ) { + tmp( n, 0 ) = array( n, 0 ); + tmp( n, 1 ) = array( n, 1 ); + } + for ( idx_t n = 0; n < array.shape( 0 ); ++n ) { + array( n, 0 ) = tmp( order[n], 0 ); + array( n, 1 ) = tmp( order[n], 1 ); + } + return field.name(); + } +}; + +template +std::string reorder_field_T( Field& field, const std::vector& order ) { + if ( field.rank() == 1 ) { + return ReorderField::apply( field, order ); + } + else if ( field.rank() == 2 ) { + return ReorderField::apply( field, order ); + } + else { + throw_Exception( "rank not supported", Here() ); + } +} + +std::string reorder_field( Field& field, const std::vector& order ) { + if ( field.datatype() == array::DataType::kind() ) { + return reorder_field_T( field, order ); + } + else if ( field.datatype() == array::DataType::kind() ) { + return reorder_field_T( field, order ); + } + else if ( field.datatype() == array::DataType::kind() ) { + return reorder_field_T( field, order ); + } + else if ( field.datatype() == array::DataType::kind() ) { + return reorder_field_T( field, order ); + } + else { + throw_Exception( "datatype not supported", Here() ); + } +} + +void reorder_connectivity( mesh::HybridElements::Connectivity& connectivity, const std::vector& order ) { + for ( idx_t b = 0; b < connectivity.blocks(); ++b ) { + auto& block = connectivity.block( b ); + for ( idx_t r = 0; r < block.rows(); ++r ) { + for ( idx_t c = 0; c < block.cols(); ++c ) { + idx_t n = block( r, c ); + block.set( r, c, order.at( n ) ); + } + } + } +} + +Domain global_bounding_box( const Mesh& mesh ) { + auto xy = array::make_view( mesh.nodes().xy() ); double xmin = std::numeric_limits::max(); double xmax = -std::numeric_limits::max(); @@ -228,9 +308,16 @@ void ReorderHilbert::operator()() { comm.allReduceInPlace( xmax, eckit::mpi::max() ); comm.allReduceInPlace( ymin, eckit::mpi::min() ); comm.allReduceInPlace( ymax, eckit::mpi::max() ); - auto domain = RectangularDomain( {xmin, xmax}, {ymin, ymax} ); + return RectangularDomain( {xmin, xmax}, {ymin, ymax} ); +} - Hilbert hilbert{domain, recursion_}; +void ReorderHilbert::operator()() { + ATLAS_TRACE( "ReorderHilbert()" ); + + Hilbert hilbert{global_bounding_box( mesh_ ), recursion_}; + + auto xy = array::make_view( mesh_.nodes().xy() ); + auto ghost = array::make_view( mesh_.nodes().ghost() ); idx_t size = xy.shape( 0 ); hilbert_reordering_.clear(); @@ -241,11 +328,26 @@ void ReorderHilbert::operator()() { auto hilbert_idx = hilbert( p ); hilbert_reordering_.emplace_back( hilbert_idx, n ); } - else { - hilbert_reordering_.emplace_back( hilbert.nb_keys() * 2 + n, n ); + else { // ghost nodes get a fake "hilbert_idx" at the end + hilbert_reordering_.emplace_back( hilbert.nb_keys() + n, n ); } } std::sort( hilbert_reordering_.begin(), hilbert_reordering_.end() ); + std::vector order; + order.reserve( size ); + std::vector order_inverse; + order_inverse.resize( size ); + idx_t c{0}; + for ( const auto& pair : hilbert_reordering_ ) { + order.emplace_back( pair.second ); + order_inverse[pair.second] = c++; + } + + for ( idx_t ifield = 0; ifield < mesh_.nodes().nb_fields(); ++ifield ) { + reorder_field( mesh_.nodes().field( ifield ), order ); + } + + reorder_connectivity( mesh_.cells().node_connectivity(), order_inverse ); } // ------------------------------------------------------------------ diff --git a/src/tests/mesh/test_hilbert_reordering.cc b/src/tests/mesh/test_hilbert_reordering.cc index 3fbd3a5e6..2b3f326eb 100644 --- a/src/tests/mesh/test_hilbert_reordering.cc +++ b/src/tests/mesh/test_hilbert_reordering.cc @@ -15,6 +15,7 @@ #include "atlas/meshgenerator.h" #include "atlas/mesh/actions/ReorderHilbert.h" +#include "atlas/output/Gmsh.h" #include "atlas/runtime/Log.h" #include "atlas/util/CoordinateEnums.h" @@ -85,7 +86,7 @@ void outputPythonScript( const eckit::PathName& filepath, const eckit::Configura "verts_" << r << " = ["; for ( idx_t n = 0; n < xy.shape( 0 ); ++n ) { - idx_t i = reorder.hilbert_reordering_[n].second; + idx_t i = n; //reorder.hilbert_reordering_[n].second; f << "\n (" << xy( i, XX ) << ", " << xy( i, YY ) << "), "; } f << "\n]" @@ -129,10 +130,10 @@ void outputPythonScript( const eckit::PathName& filepath, const eckit::Configura << r << ",ys_" << r << ", '-', lw=1, color=c )" "\n" - //"for i in range( len(verts_0) ):" - //"\n" - //" plt.text( xs_0[i], ys_0[i], str(i) )" - "\n" + // "for i in range( len(verts_0) ):" + // "\n" + // " plt.text( xs_0[i], ys_0[i], str(i) )" + // "\n" ""; if ( mpi_rank == mpi_size - 1 ) { f << "\n" @@ -157,8 +158,8 @@ void outputPythonScript( const eckit::PathName& filepath, const eckit::Configura CASE( "test_hilbert_reordering" ) { - Mesh mesh = StructuredMeshGenerator().generate( Grid( "O16" ) ); - auto reorder = mesh::actions::ReorderHilbert{mesh, util::Config( "recursion", 6 )}; + Mesh mesh = StructuredMeshGenerator().generate( Grid( "O32" ) ); + auto reorder = mesh::actions::ReorderHilbert{mesh, util::Config( "recursion", 8 )}; auto xy = array::make_view( mesh.nodes().xy() ); reorder(); @@ -169,7 +170,7 @@ CASE( "test_hilbert_reordering" ) { auto hilbert_idx = reorder.hilbert_reordering_[n].first; idx_t ip = reorder.hilbert_reordering_[n].second; PointXY p{xy( ip, 0 ), xy( ip, 1 )}; - Log::info() << ip << '\t' << p << "\t" << hilbert_idx << std::endl; + //Log::info() << ip << '\t' << p << "\t" << hilbert_idx << std::endl; if ( hilbert_idx == previous_hilbert_idx ) { //throw_Exception("Duplicate hilbert index detected in ReorderHilbert", Here() ); } @@ -178,6 +179,9 @@ CASE( "test_hilbert_reordering" ) { outputPythonScript( "hilbert.py", util::NoConfig(), xy, reorder ); #endif + + output::Gmsh gmsh{"hilbert.msh"}; + gmsh.write( mesh ); } //----------------------------------------------------------------------------- From 6782cce30c94718d73c952654ac9d7fe08954efa Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 6 Sep 2019 17:55:26 +0100 Subject: [PATCH 12/40] ATLAS-245 Hilbert reordering for elements implemented --- src/atlas/mesh/Elements.h | 2 - src/atlas/mesh/actions/ReorderHilbert.cc | 215 ++++++++++++++++----- src/atlas/mesh/actions/ReorderHilbert.h | 11 +- src/tests/mesh/test_hilbert_reordering.cc | 220 +++++++++++----------- 4 files changed, 280 insertions(+), 168 deletions(-) diff --git a/src/atlas/mesh/Elements.h b/src/atlas/mesh/Elements.h index 912ca88b5..a212039dc 100644 --- a/src/atlas/mesh/Elements.h +++ b/src/atlas/mesh/Elements.h @@ -34,8 +34,6 @@ namespace mesh { /// @brief Describe elements of a single type class Elements : public util::Object { -public: - // typedef atlas::mesh::BlockConnectivity Connectivity; public: //-- Constructors diff --git a/src/atlas/mesh/actions/ReorderHilbert.cc b/src/atlas/mesh/actions/ReorderHilbert.cc index baf62a28f..857199a04 100644 --- a/src/atlas/mesh/actions/ReorderHilbert.cc +++ b/src/atlas/mesh/actions/ReorderHilbert.cc @@ -10,13 +10,17 @@ #include #include +#include +#include #include "atlas/array.h" #include "atlas/domain/Domain.h" #include "atlas/grid/Grid.h" +#include "atlas/mesh/Elements.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" +#include "atlas/mesh/actions/BuildCellCentres.h" #include "atlas/mesh/actions/ReorderHilbert.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" @@ -203,7 +207,7 @@ gidx_t Hilbert::recursive_algorithm( const PointXY& p, const box_t& box, idx_t l // ------------------------------------------------------------------ -ReorderHilbert::ReorderHilbert( Mesh& mesh, const eckit::Configuration& config ) : mesh_( mesh ) { +ReorderHilbert::ReorderHilbert( const eckit::Configuration& config ) { config.get( "recursion", recursion_ ); } @@ -213,15 +217,17 @@ struct ReorderField {}; template struct ReorderField { static constexpr int Rank = 1; - static std::string apply( Field& field, const std::vector& order ) { + static std::string apply( Field& field, const std::vector& order, idx_t begin, idx_t end ) { auto array = array::make_view( field ); - array::ArrayT tmp_array( field.shape() ); + end = std::min( end, array.shape( 0 ) ); + idx_t size = end - begin; + array::ArrayT tmp_array( size ); auto tmp = array::make_view( tmp_array ); - for ( idx_t n = 0; n < array.shape( 0 ); ++n ) { - tmp( n ) = array( n ); + for ( idx_t n = 0; n < size; ++n ) { + tmp( n ) = array( begin + n ); } - for ( idx_t n = 0; n < array.shape( 0 ); ++n ) { - array( n ) = tmp( order[n] ); + for ( idx_t n = 0; n < size; ++n ) { + array( begin + n ) = tmp( order[n] ); } return field.name(); } @@ -230,54 +236,59 @@ struct ReorderField { template struct ReorderField { static constexpr int Rank = 2; - static std::string apply( Field& field, const std::vector& order ) { + static std::string apply( Field& field, const std::vector& order, idx_t begin, idx_t end ) { auto array = array::make_view( field ); - array::ArrayT tmp_array( field.shape() ); + end = std::min( end, array.shape( 0 ) ); + idx_t size = end - begin; + array::ArrayT tmp_array( size, field.shape( 1 ) ); auto tmp = array::make_view( tmp_array ); - for ( idx_t n = 0; n < array.shape( 0 ); ++n ) { - tmp( n, 0 ) = array( n, 0 ); - tmp( n, 1 ) = array( n, 1 ); + for ( idx_t n = 0; n < size; ++n ) { + for ( idx_t v = 0; v < array.shape( 1 ); ++v ) { + tmp( n, v ) = array( begin + n, v ); + } } - for ( idx_t n = 0; n < array.shape( 0 ); ++n ) { - array( n, 0 ) = tmp( order[n], 0 ); - array( n, 1 ) = tmp( order[n], 1 ); + for ( idx_t n = 0; n < size; ++n ) { + for ( idx_t v = 0; v < array.shape( 1 ); ++v ) { + array( begin + n, v ) = tmp( order[n], v ); + } } return field.name(); } }; template -std::string reorder_field_T( Field& field, const std::vector& order ) { +std::string reorder_field_T( Field& field, const std::vector& order, idx_t begin, idx_t end ) { if ( field.rank() == 1 ) { - return ReorderField::apply( field, order ); + return ReorderField::apply( field, order, begin, end ); } else if ( field.rank() == 2 ) { - return ReorderField::apply( field, order ); + return ReorderField::apply( field, order, begin, end ); } else { throw_Exception( "rank not supported", Here() ); } } -std::string reorder_field( Field& field, const std::vector& order ) { +std::string reorder_field( Field& field, const std::vector& order, idx_t begin = 0, + idx_t end = std::numeric_limits::max() ) { if ( field.datatype() == array::DataType::kind() ) { - return reorder_field_T( field, order ); + return reorder_field_T( field, order, begin, end ); } else if ( field.datatype() == array::DataType::kind() ) { - return reorder_field_T( field, order ); + return reorder_field_T( field, order, begin, end ); } else if ( field.datatype() == array::DataType::kind() ) { - return reorder_field_T( field, order ); + return reorder_field_T( field, order, begin, end ); } else if ( field.datatype() == array::DataType::kind() ) { - return reorder_field_T( field, order ); + return reorder_field_T( field, order, begin, end ); } else { throw_Exception( "datatype not supported", Here() ); } } -void reorder_connectivity( mesh::HybridElements::Connectivity& connectivity, const std::vector& order ) { +void update_connectivity( mesh::HybridElements::Connectivity& connectivity, const std::vector& order ) { for ( idx_t b = 0; b < connectivity.blocks(); ++b ) { auto& block = connectivity.block( b ); for ( idx_t r = 0; r < block.rows(); ++r ) { @@ -289,6 +300,24 @@ void reorder_connectivity( mesh::HybridElements::Connectivity& connectivity, con } } +void reorder_connectivity( BlockConnectivity& connectivity, const std::vector& order ) { + ATLAS_ASSERT( connectivity.rows() == order.size() ); + BlockConnectivity tmp; + tmp.add( connectivity.rows(), connectivity.cols(), connectivity.data(), true ); + for ( idx_t r = 0; r < connectivity.rows(); ++r ) { + for ( idx_t c = 0; c < connectivity.cols(); ++c ) { + if ( connectivity( r, c ) != tmp( r, c ) ) { + ATLAS_DEBUG_VAR( r ); + ATLAS_DEBUG_VAR( c ); + ATLAS_DEBUG_VAR( connectivity( r, c ) ); + ATLAS_DEBUG_VAR( tmp( r, c ) ); + } + ATLAS_ASSERT( connectivity( r, c ) == tmp( r, c ) ); + connectivity.set( r, c, tmp( order.at( r ), c ) ); + } + } +} + Domain global_bounding_box( const Mesh& mesh ) { auto xy = array::make_view( mesh.nodes().xy() ); @@ -311,43 +340,125 @@ Domain global_bounding_box( const Mesh& mesh ) { return RectangularDomain( {xmin, xmax}, {ymin, ymax} ); } -void ReorderHilbert::operator()() { +void ReorderHilbert::operator()( Mesh& mesh ) { ATLAS_TRACE( "ReorderHilbert()" ); - Hilbert hilbert{global_bounding_box( mesh_ ), recursion_}; + using hilbert_reordering_t = std::vector>; - auto xy = array::make_view( mesh_.nodes().xy() ); - auto ghost = array::make_view( mesh_.nodes().ghost() ); + Hilbert hilbert{global_bounding_box( mesh ), recursion_}; - idx_t size = xy.shape( 0 ); - hilbert_reordering_.clear(); - hilbert_reordering_.reserve( size ); - for ( idx_t n = 0; n < size; ++n ) { - if ( not ghost( n ) ) { - PointXY p{xy( n, XX ), xy( n, YY )}; - auto hilbert_idx = hilbert( p ); - hilbert_reordering_.emplace_back( hilbert_idx, n ); + // Reorder nodes + { + auto xy = array::make_view( mesh.nodes().xy() ); + auto ghost = array::make_view( mesh.nodes().ghost() ); + + idx_t size = xy.shape( 0 ); + hilbert_reordering_t hilbert_reordering; + hilbert_reordering.reserve( size ); + ATLAS_TRACE_SCOPE( "hilbert nodes" ) { + for ( idx_t n = 0; n < size; ++n ) { + if ( not ghost( n ) ) { + PointXY p{xy( n, XX ), xy( n, YY )}; + auto hilbert_idx = hilbert( p ); + hilbert_reordering.emplace_back( hilbert_idx, n ); + } + else { // ghost nodes get a fake "hilbert_idx" at the end + hilbert_reordering.emplace_back( hilbert.nb_keys() + n, n ); + } + } } - else { // ghost nodes get a fake "hilbert_idx" at the end - hilbert_reordering_.emplace_back( hilbert.nb_keys() + n, n ); + + std::sort( hilbert_reordering.begin(), hilbert_reordering.end() ); + std::vector order; + order.reserve( size ); + std::vector order_inverse; + order_inverse.resize( size ); + idx_t c{0}; + for ( const auto& pair : hilbert_reordering ) { + order.emplace_back( pair.second ); + order_inverse[pair.second] = c++; } - } - std::sort( hilbert_reordering_.begin(), hilbert_reordering_.end() ); - std::vector order; - order.reserve( size ); - std::vector order_inverse; - order_inverse.resize( size ); - idx_t c{0}; - for ( const auto& pair : hilbert_reordering_ ) { - order.emplace_back( pair.second ); - order_inverse[pair.second] = c++; - } - for ( idx_t ifield = 0; ifield < mesh_.nodes().nb_fields(); ++ifield ) { - reorder_field( mesh_.nodes().field( ifield ), order ); + for ( idx_t ifield = 0; ifield < mesh.nodes().nb_fields(); ++ifield ) { + reorder_field( mesh.nodes().field( ifield ), order ); + } + + update_connectivity( mesh.cells().node_connectivity(), order_inverse ); } - reorder_connectivity( mesh_.cells().node_connectivity(), order_inverse ); + // Reorder elements + if ( 1 ) { + hilbert_reordering_t hilbert_reordering; + std::vector order; + std::vector order_inverse; + + auto cell_centres = + Field( "cell_centres", array::make_datatype(), array::make_shape( mesh.cells().size(), 2 ) ); + auto nodes_xy = array::make_view( mesh.nodes().xy() ); + for ( idx_t t = 0; t < mesh.cells().nb_types(); ++t ) { + auto& cells = mesh.cells().elements( t ); + auto xy = cells.view( cell_centres ); + auto flags = cells.view( mesh.cells().flags() ); + auto halo = cells.view( mesh.cells().halo() ); + + // Compute cell-centres + { + const auto& node_connectivity = cells.node_connectivity(); + const idx_t nb_nodes = cells.nb_nodes(); + const double nb_nodes_double = nb_nodes; + for ( idx_t e = 0; e < cells.size(); ++e ) { + double x{0}; + double y{0}; + for ( idx_t c = 0; c < nb_nodes; ++c ) { + idx_t n = node_connectivity( e, c ); + x += nodes_xy( n, XX ); + y += nodes_xy( n, YY ); + } + xy( e, XX ) = x / nb_nodes_double; + xy( e, YY ) = y / nb_nodes_double; + } + } + + + auto skip = [&]( idx_t n ) { + if ( halo( n ) || mesh::Nodes::Topology::check( flags( n ), mesh::Nodes::Topology::PATCH ) ) { + return true; + } + return false; + }; + idx_t size = xy.shape( 0 ); + hilbert_reordering.clear(); + hilbert_reordering.reserve( size ); + ATLAS_TRACE_SCOPE( "hilbert elements[" + std::to_string( t ) + "]" ) { + for ( idx_t n = 0; n < size; ++n ) { + PointXY p{xy( n, XX ), xy( n, YY )}; + if ( not skip( n ) ) { + hilbert_reordering.emplace_back( hilbert( p ), n ); + } + else { // halo elements get a fake "hilbert_idx" at the end + hilbert_reordering.emplace_back( hilbert.nb_keys() + n, n ); + } + } + } + ATLAS_ASSERT( hilbert_reordering.size() == size ); + std::sort( hilbert_reordering.begin(), hilbert_reordering.end() ); + order.clear(); + order.reserve( size ); + order_inverse.resize( size ); + idx_t c{0}; + for ( const auto& pair : hilbert_reordering ) { + order.emplace_back( pair.second ); + order_inverse[pair.second] = c++; + ATLAS_ASSERT( pair.second < size ); + } + + for ( idx_t ifield = 0; ifield < mesh.cells().nb_fields(); ++ifield ) { + reorder_field( mesh.cells().field( ifield ), order, cells.begin(), cells.end() ); + } + + reorder_connectivity( cells.node_connectivity(), order ); + } + } } // ------------------------------------------------------------------ diff --git a/src/atlas/mesh/actions/ReorderHilbert.h b/src/atlas/mesh/actions/ReorderHilbert.h index 36e2a9e4c..59d1bbc11 100644 --- a/src/atlas/mesh/actions/ReorderHilbert.h +++ b/src/atlas/mesh/actions/ReorderHilbert.h @@ -10,9 +10,6 @@ #pragma once -#include -#include - #include "atlas/library/config.h" #include "atlas/util/Config.h" @@ -26,15 +23,11 @@ namespace actions { class ReorderHilbert { public: - ReorderHilbert( Mesh& mesh, const eckit::Configuration& config = util::NoConfig() ); - void operator()(); + ReorderHilbert( const eckit::Configuration& config = util::NoConfig() ); + void operator()( Mesh& ); private: - Mesh& mesh_; idx_t recursion_{20}; - -public: - std::vector> hilbert_reordering_; }; // ------------------------------------------------------------------ diff --git a/src/tests/mesh/test_hilbert_reordering.cc b/src/tests/mesh/test_hilbert_reordering.cc index 2b3f326eb..a6fd3e90b 100644 --- a/src/tests/mesh/test_hilbert_reordering.cc +++ b/src/tests/mesh/test_hilbert_reordering.cc @@ -10,6 +10,7 @@ #include +#include "atlas/functionspace.h" #include "atlas/grid.h" #include "atlas/mesh.h" #include "atlas/meshgenerator.h" @@ -26,8 +27,7 @@ namespace test { //----------------------------------------------------------------------------- -void outputPythonScript( const eckit::PathName& filepath, const eckit::Configuration& config, - const array::ArrayView& xy, const mesh::actions::ReorderHilbert& reorder ) { +void outputHilbertCurve( const eckit::PathName& filepath, const array::ArrayView& xy ) { const eckit::mpi::Comm& comm = atlas::mpi::comm(); int mpi_rank = int( comm.rank() ); int mpi_size = int( comm.size() ); @@ -51,90 +51,81 @@ void outputPythonScript( const eckit::PathName& filepath, const eckit::Configura if ( mpi_rank == 0 ) { f << "\n" - "# Configuration option to plot nodes" - "\n" - "plot_nodes = " + - std::string( ( config.getBool( "nodes", false ) ? "True" : "False" ) ) + - "\n" - "\n" - "import matplotlib.pyplot as plt" - "\n" - "from matplotlib.path import Path" - "\n" - "import matplotlib.patches as patches" - "\n" - "" - "\n" - "from itertools import cycle" - "\n" - "import matplotlib.cm as cm" - "\n" - "import numpy as np" - "\n" - "cycol = cycle([cm.Paired(i) for i in " - "np.linspace(0,1,12,endpoint=True)]).next" - "\n" - "" - "\n" - "fig = plt.figure()" - "\n" - "ax = fig.add_subplot(111,aspect='equal')" - "\n" - ""; + "\n" + "import matplotlib.pyplot as plt" + "\n" + "from matplotlib.path import Path" + "\n" + "import matplotlib.patches as patches" + "\n" + "" + "\n" + "from itertools import cycle" + "\n" + "import matplotlib.cm as cm" + "\n" + "import numpy as np" + "\n" + "cycol = cycle([cm.Paired(i) for i in " + "np.linspace(0,1,12,endpoint=True)]).next" + "\n" + "" + "\n" + "fig = plt.figure()" + "\n" + "ax = fig.add_subplot(111,aspect='equal')" + "\n" + ""; } - f << "\n" - "verts_" - << r << " = ["; - for ( idx_t n = 0; n < xy.shape( 0 ); ++n ) { - idx_t i = n; //reorder.hilbert_reordering_[n].second; - f << "\n (" << xy( i, XX ) << ", " << xy( i, YY ) << "), "; + + if ( mpi_rank == r ) { // replace "r" with rank you wish to plot only + f << "\n" + "verts_" + << r << " = ["; + for ( idx_t n = 0; n < xy.shape( 0 ); ++n ) { + idx_t i = n; //reorder.hilbert_reordering_[n].second; + f << "\n (" << xy( i, XX ) << ", " << xy( i, YY ) << "), "; + } + f << "\n]" + "\n" + "" + "\n" + "codes_" + << r + << " = [Path.MOVETO]" + "\n" + "codes_" + << r << ".extend([Path.LINETO] * " << ( xy.shape( 0 ) - 2 ) + << ")" + "\n" + "codes_" + << r + << ".extend([Path.LINETO])" + "\n" + "" + "\n" + "count_" + << r << " = " << count + << "\n" + "count_all_" + << r << " = " << count_all + << "\n" + "c = cycol()" + "\n" + "xs_" + << r << ", ys_" << r << " = zip(*verts_" << r + << ")" + "\n" + "ax.plot(xs_" + << r << ",ys_" << r + << ", '-', lw=1, color=c )" + "\n" + // "for i in range( len(verts_0) ):" + // "\n" + // " plt.text( xs_0[i], ys_0[i], str(i) )" + // "\n" + ""; } - f << "\n]" - "\n" - "" - "\n" - "codes_" - << r - << " = [Path.MOVETO]" - "\n" - "codes_" - << r << ".extend([Path.LINETO] * " << ( xy.shape( 0 ) - 2 ) - << ")" - "\n" - "codes_" - << r - << ".extend([Path.LINETO])" - "\n" - "" - "\n" - "count_" - << r << " = " << count - << "\n" - "count_all_" - << r << " = " << count_all - << "\n" - "" - //"\n" "x_" << r << " = ["; for (idx_t i=0; i( mesh.nodes().xy() ); - - reorder(); - -#if 1 - gidx_t previous_hilbert_idx{-1}; - for ( idx_t n = 0; n < xy.shape( 0 ); ++n ) { - auto hilbert_idx = reorder.hilbert_reordering_[n].first; - idx_t ip = reorder.hilbert_reordering_[n].second; - PointXY p{xy( ip, 0 ), xy( ip, 1 )}; - //Log::info() << ip << '\t' << p << "\t" << hilbert_idx << std::endl; - if ( hilbert_idx == previous_hilbert_idx ) { - //throw_Exception("Duplicate hilbert index detected in ReorderHilbert", Here() ); +Field create_cell_centres( Mesh& mesh ) { + auto cell_centres = + Field( "cell_centres", array::make_datatype(), array::make_shape( mesh.cells().size(), 2 ) ); + auto nodes_xy = array::make_view( mesh.nodes().xy() ); + for ( idx_t t = 0; t < mesh.cells().nb_types(); ++t ) { + auto& cells = mesh.cells().elements( t ); + auto xy = cells.view( cell_centres ); + + // Compute cell-centres + { + const auto& node_connectivity = cells.node_connectivity(); + const idx_t nb_nodes = cells.nb_nodes(); + const double nb_nodes_double = nb_nodes; + for ( idx_t e = 0; e < cells.size(); ++e ) { + double x{0}; + double y{0}; + for ( idx_t c = 0; c < nb_nodes; ++c ) { + idx_t n = node_connectivity( e, c ); + x += nodes_xy( n, XX ); + y += nodes_xy( n, YY ); + } + xy( e, XX ) = x / nb_nodes_double; + xy( e, YY ) = y / nb_nodes_double; + } } - previous_hilbert_idx = hilbert_idx; } + return cell_centres; +} - outputPythonScript( "hilbert.py", util::NoConfig(), xy, reorder ); -#endif +CASE( "test_hilbert_reordering" ) { + auto meshgenerator = StructuredMeshGenerator( util::Config( "patch_pole", false )( "triangulate", true ) ); + Mesh mesh = meshgenerator( Grid( "O32" ) ); + auto reorder = mesh::actions::ReorderHilbert{util::Config( "recursion", 30 )}; + reorder( mesh ); + + // functionspace::NodeColumns( mesh, option::halo( 2 ) ); - output::Gmsh gmsh{"hilbert.msh"}; + auto xy = array::make_view( mesh.nodes().xy() ); + outputHilbertCurve( "hilbert_nodes.py", xy ); + output::Gmsh gmsh{"hilbert.msh", util::Config( "coordinates", "xy" )}; gmsh.write( mesh ); + + Field cell_centres = create_cell_centres( mesh ); + auto cell_centres_xy = array::make_view( cell_centres ); + outputHilbertCurve( "hilbert_elements.py", cell_centres_xy ); } //----------------------------------------------------------------------------- From d8d0ac49e601da733f0df9757af06d1fc67fb37c Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 9 Sep 2019 12:23:54 +0100 Subject: [PATCH 13/40] ATLAS-245 Confirm that edges created after element reordering are also reordered --- src/tests/mesh/test_hilbert_reordering.cc | 31 +++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/tests/mesh/test_hilbert_reordering.cc b/src/tests/mesh/test_hilbert_reordering.cc index a6fd3e90b..998b815ba 100644 --- a/src/tests/mesh/test_hilbert_reordering.cc +++ b/src/tests/mesh/test_hilbert_reordering.cc @@ -15,6 +15,7 @@ #include "atlas/mesh.h" #include "atlas/meshgenerator.h" +#include "atlas/mesh/actions/BuildEdges.h" #include "atlas/mesh/actions/ReorderHilbert.h" #include "atlas/output/Gmsh.h" #include "atlas/runtime/Log.h" @@ -176,6 +177,31 @@ Field create_cell_centres( Mesh& mesh ) { return cell_centres; } +Field create_edge_centres( Mesh& mesh ) { + auto edge_centres = + Field( "edge_centres", array::make_datatype(), array::make_shape( mesh.edges().size(), 2 ) ); + auto nodes_xy = array::make_view( mesh.nodes().xy() ); + auto& edges = mesh.edges(); + auto xy = array::make_view( edge_centres ); + + // Compute edge-centres + { + const auto& node_connectivity = edges.node_connectivity(); + for ( idx_t e = 0; e < edges.size(); ++e ) { + double x{0}; + double y{0}; + for ( idx_t c = 0; c < 2; ++c ) { + idx_t n = node_connectivity( e, c ); + x += nodes_xy( n, XX ); + y += nodes_xy( n, YY ); + } + xy( e, XX ) = 0.5 * x; + xy( e, YY ) = 0.5 * y; + } + } + return edge_centres; +} + CASE( "test_hilbert_reordering" ) { auto meshgenerator = StructuredMeshGenerator( util::Config( "patch_pole", false )( "triangulate", true ) ); Mesh mesh = meshgenerator( Grid( "O32" ) ); @@ -192,6 +218,11 @@ CASE( "test_hilbert_reordering" ) { Field cell_centres = create_cell_centres( mesh ); auto cell_centres_xy = array::make_view( cell_centres ); outputHilbertCurve( "hilbert_elements.py", cell_centres_xy ); + + mesh::actions::build_edges( mesh ); + Field edge_centres = create_edge_centres( mesh ); + auto edge_centres_xy = array::make_view( edge_centres ); + outputHilbertCurve( "hilbert_edges.py", edge_centres_xy ); } //----------------------------------------------------------------------------- From 050419fbbe05943ee28e622e93aa2334952075a8 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 13 Sep 2019 10:36:04 +0100 Subject: [PATCH 14/40] ATLAS-245 Reordering with ReverseCuthillMckee --- src/apps/atlas-benchmark.cc | 5 + src/atlas/CMakeLists.txt | 4 + src/atlas/mesh/actions/BuildEdges.cc | 39 +++ src/atlas/mesh/actions/BuildHalo.cc | 2 +- src/atlas/mesh/actions/Reorder.cc | 243 +++++++++++++ src/atlas/mesh/actions/Reorder.h | 108 ++++++ src/atlas/mesh/actions/ReorderHilbert.cc | 302 +++++----------- src/atlas/mesh/actions/ReorderHilbert.h | 33 +- .../actions/ReorderReverseCuthillMckee.cc | 197 +++++++++++ .../mesh/actions/ReorderReverseCuthillMckee.h | 49 +++ src/tests/mesh/CMakeLists.txt | 2 +- src/tests/mesh/test_hilbert_reordering.cc | 235 ------------- src/tests/mesh/test_mesh_reorder.cc | 330 ++++++++++++++++++ 13 files changed, 1097 insertions(+), 452 deletions(-) create mode 100644 src/atlas/mesh/actions/Reorder.cc create mode 100644 src/atlas/mesh/actions/Reorder.h create mode 100644 src/atlas/mesh/actions/ReorderReverseCuthillMckee.cc create mode 100644 src/atlas/mesh/actions/ReorderReverseCuthillMckee.h delete mode 100644 src/tests/mesh/test_hilbert_reordering.cc create mode 100644 src/tests/mesh/test_mesh_reorder.cc diff --git a/src/apps/atlas-benchmark.cc b/src/apps/atlas-benchmark.cc index 6a80de61b..d0b5a1028 100644 --- a/src/apps/atlas-benchmark.cc +++ b/src/apps/atlas-benchmark.cc @@ -47,6 +47,7 @@ #include "atlas/mesh/actions/BuildHalo.h" #include "atlas/mesh/actions/BuildParallelFields.h" #include "atlas/mesh/actions/BuildPeriodicBoundaries.h" +#include "atlas/mesh/actions/Reorder.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/Checksum.h" @@ -138,6 +139,7 @@ class AtlasBenchmark : public AtlasTool { add_option( new SimpleOption( "output", "Write output in gmsh format" ) ); add_option( new SimpleOption( "exclude", "Exclude number of iterations in statistics (default=1)" ) ); add_option( new SimpleOption( "details", "Show detailed timers (default=false)" ) ); + add_option( new SimpleOption( "reorder", "Reorder mesh (default=none)" ) ); } void setup(); @@ -169,6 +171,7 @@ class AtlasBenchmark : public AtlasTool { long omp_threads; double dz; std::string gridname; + std::string reorder{"none"}; TimerStats iteration_timer; TimerStats haloexchange_timer; @@ -196,6 +199,7 @@ int AtlasBenchmark::execute( const Args& args ) { args.get( "exclude", exclude ); output = false; args.get( "output", output ); + args.get( "reorder", reorder ); bool help( false ); args.get( "help", help ); @@ -308,6 +312,7 @@ void AtlasBenchmark::setup() { ATLAS_TRACE_SCOPE( "Create mesh" ) { mesh = MeshGenerator( "structured", util::Config( "partitioner", "equal_regions" ) ).generate( grid ); } + mesh::actions::Reorder{option::type( reorder )}( mesh ); ATLAS_TRACE_SCOPE( "Create node_fs" ) { nodes_fs = functionspace::NodeColumns( mesh, option::halo( halo ) ); } ATLAS_TRACE_SCOPE( "Create edges_fs" ) { edges_fs = functionspace::EdgeColumns( mesh, option::halo( halo ) ); } diff --git a/src/atlas/CMakeLists.txt b/src/atlas/CMakeLists.txt index 9ef805a1b..7f1b758d7 100644 --- a/src/atlas/CMakeLists.txt +++ b/src/atlas/CMakeLists.txt @@ -287,8 +287,12 @@ mesh/actions/BuildXYZField.h mesh/actions/WriteLoadBalanceReport.cc mesh/actions/BuildTorusXYZField.h mesh/actions/BuildTorusXYZField.cc +mesh/actions/Reorder.h +mesh/actions/Reorder.cc mesh/actions/ReorderHilbert.h mesh/actions/ReorderHilbert.cc +mesh/actions/ReorderReverseCuthillMckee.h +mesh/actions/ReorderReverseCuthillMckee.cc meshgenerator.h meshgenerator/MeshGenerator.cc diff --git a/src/atlas/mesh/actions/BuildEdges.cc b/src/atlas/mesh/actions/BuildEdges.cc index a0d93acb5..ea2313bf6 100644 --- a/src/atlas/mesh/actions/BuildEdges.cc +++ b/src/atlas/mesh/actions/BuildEdges.cc @@ -9,6 +9,7 @@ */ #include "atlas/mesh/actions/BuildEdges.h" +#include #include #include #include @@ -450,10 +451,48 @@ void build_edges( Mesh& mesh, const eckit::Configuration& config ) { pole_edge_accumulator = std::make_shared( nodes ); } + std::vector sorted_edge_nodes_data; + std::vector sorted_edge_to_elem_data; + for ( int halo = 0; halo <= mesh_halo; ++halo ) { edge_start = edge_end; edge_end += ( edge_halo_offsets[halo + 1] - edge_halo_offsets[halo] ); + if ( /*sort edges based on lowest node local index =*/false ) { + if ( sorted_edge_nodes_data.empty() ) { + sorted_edge_nodes_data.resize( edge_nodes_data.size() ); + } + if ( sorted_edge_to_elem_data.empty() ) { + sorted_edge_to_elem_data.resize( edge_to_elem_data.size() ); + } + std::vector> sorted_edges_by_lowest_node_index; + sorted_edges_by_lowest_node_index.reserve( edge_end - edge_start ); + for ( idx_t e = edge_start; e < edge_end; ++e ) { + const idx_t iedge = edge_halo_offsets[halo] + ( e - edge_start ); + idx_t lowest_node_idx = + std::min( edge_nodes_data.at( 2 * iedge + 0 ), edge_nodes_data.at( 2 * iedge + 1 ) ); + sorted_edges_by_lowest_node_index.emplace_back( lowest_node_idx, e ); + } + std::sort( sorted_edges_by_lowest_node_index.begin(), sorted_edges_by_lowest_node_index.end() ); + for ( idx_t e = edge_start; e < edge_end; ++e ) { + const idx_t iedge = edge_halo_offsets[halo] + ( e - edge_start ); + const idx_t sedge = + edge_halo_offsets[halo] + ( sorted_edges_by_lowest_node_index[e - edge_start].second - edge_start ); + sorted_edge_nodes_data[2 * iedge + 0] = edge_nodes_data[2 * sedge + 0]; + sorted_edge_nodes_data[2 * iedge + 1] = edge_nodes_data[2 * sedge + 1]; + sorted_edge_to_elem_data[2 * iedge + 0] = edge_to_elem_data[2 * sedge + 0]; + sorted_edge_to_elem_data[2 * iedge + 1] = edge_to_elem_data[2 * sedge + 1]; + } + + for ( idx_t e = edge_start; e < edge_end; ++e ) { + const idx_t iedge = edge_halo_offsets[halo] + ( e - edge_start ); + edge_nodes_data[2 * iedge + 0] = sorted_edge_nodes_data[2 * iedge + 0]; + edge_nodes_data[2 * iedge + 1] = sorted_edge_nodes_data[2 * iedge + 1]; + edge_to_elem_data[2 * iedge + 0] = sorted_edge_to_elem_data[2 * iedge + 0]; + edge_to_elem_data[2 * iedge + 1] = sorted_edge_to_elem_data[2 * iedge + 1]; + } + } + // Build edges mesh.edges().add( new mesh::temporary::Line(), ( edge_end - edge_start ), edge_nodes_data.data() + edge_halo_offsets[halo] * 2 ); diff --git a/src/atlas/mesh/actions/BuildHalo.cc b/src/atlas/mesh/actions/BuildHalo.cc index 0819dd460..6fd5c07e3 100644 --- a/src/atlas/mesh/actions/BuildHalo.cc +++ b/src/atlas/mesh/actions/BuildHalo.cc @@ -428,7 +428,7 @@ class Notification { std::vector notes; }; -typedef std::map Uid2Node; +using Uid2Node = std::map; void build_lookup_uid2node( Mesh& mesh, Uid2Node& uid2node ) { ATLAS_TRACE(); Notification notes; diff --git a/src/atlas/mesh/actions/Reorder.cc b/src/atlas/mesh/actions/Reorder.cc new file mode 100644 index 000000000..3acfd8b45 --- /dev/null +++ b/src/atlas/mesh/actions/Reorder.cc @@ -0,0 +1,243 @@ +/* + * (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/mesh/actions/Reorder.h" + +#include "atlas/array.h" +#include "atlas/mesh/Elements.h" +#include "atlas/mesh/HybridElements.h" +#include "atlas/mesh/Mesh.h" +#include "atlas/mesh/Nodes.h" +#include "atlas/runtime/Exception.h" +#include "atlas/runtime/Log.h" +#include "atlas/runtime/Trace.h" + +namespace atlas { +namespace mesh { +namespace actions { + +// ------------------------------------------------------------------ + +const ReorderImpl* ReorderFactory::build( const eckit::Parametrisation& config ) { + std::string builder{"none"}; + config.get( "type", builder ); + auto factory = get( builder ); + return factory->make( config ); +} + +// ------------------------------------------------------------------ + +template +struct ReorderField {}; + +// ------------------------------------------------------------------ + +template +struct ReorderField { + static constexpr int Rank = 1; + static std::string apply( Field& field, const std::vector& order, idx_t begin, idx_t end ) { + auto array = array::make_view( field ); + end = std::min( end, array.shape( 0 ) ); + idx_t size = end - begin; + array::ArrayT tmp_array( size ); + auto tmp = array::make_view( tmp_array ); + for ( idx_t n = 0; n < size; ++n ) { + tmp( n ) = array( begin + n ); + } + for ( idx_t n = 0; n < size; ++n ) { + array( begin + n ) = tmp( order[n] ); + } + return field.name(); + } +}; + +// ------------------------------------------------------------------ + +template +struct ReorderField { + static constexpr int Rank = 2; + static std::string apply( Field& field, const std::vector& order, idx_t begin, idx_t end ) { + auto array = array::make_view( field ); + end = std::min( end, array.shape( 0 ) ); + idx_t size = end - begin; + array::ArrayT tmp_array( size, field.shape( 1 ) ); + auto tmp = array::make_view( tmp_array ); + for ( idx_t n = 0; n < size; ++n ) { + for ( idx_t v = 0; v < array.shape( 1 ); ++v ) { + tmp( n, v ) = array( begin + n, v ); + } + } + for ( idx_t n = 0; n < size; ++n ) { + for ( idx_t v = 0; v < array.shape( 1 ); ++v ) { + array( begin + n, v ) = tmp( order[n], v ); + } + } + return field.name(); + } +}; + +// ------------------------------------------------------------------ + +template +std::string reorder_field_T( Field& field, const std::vector& order, idx_t begin, idx_t end ) { + if ( field.rank() == 1 ) { + return ReorderField::apply( field, order, begin, end ); + } + else if ( field.rank() == 2 ) { + return ReorderField::apply( field, order, begin, end ); + } + else { + throw_Exception( "rank not supported", Here() ); + } +} + +// ------------------------------------------------------------------ + +std::string reorder_field( Field& field, const std::vector& order, idx_t begin = 0, + idx_t end = std::numeric_limits::max() ) { + if ( field.datatype() == array::DataType::kind() ) { + return reorder_field_T( field, order, begin, end ); + } + else if ( field.datatype() == array::DataType::kind() ) { + return reorder_field_T( field, order, begin, end ); + } + else if ( field.datatype() == array::DataType::kind() ) { + return reorder_field_T( field, order, begin, end ); + } + else if ( field.datatype() == array::DataType::kind() ) { + return reorder_field_T( field, order, begin, end ); + } + else { + throw_Exception( "datatype not supported", Here() ); + } +} + +// ------------------------------------------------------------------ + +void update_connectivity( mesh::HybridElements::Connectivity& connectivity, const std::vector& order ) { + for ( idx_t b = 0; b < connectivity.blocks(); ++b ) { + auto& block = connectivity.block( b ); + for ( idx_t r = 0; r < block.rows(); ++r ) { + for ( idx_t c = 0; c < block.cols(); ++c ) { + idx_t n = block( r, c ); + block.set( r, c, order.at( n ) ); + } + } + } +} + +// ------------------------------------------------------------------ + +void reorder_connectivity( BlockConnectivity& connectivity, const std::vector& order ) { + ATLAS_ASSERT( connectivity.rows() == order.size() ); + BlockConnectivity tmp; + tmp.add( connectivity.rows(), connectivity.cols(), connectivity.data(), true ); + for ( idx_t r = 0; r < connectivity.rows(); ++r ) { + for ( idx_t c = 0; c < connectivity.cols(); ++c ) { + if ( connectivity( r, c ) != tmp( r, c ) ) { + ATLAS_DEBUG_VAR( r ); + ATLAS_DEBUG_VAR( c ); + ATLAS_DEBUG_VAR( connectivity( r, c ) ); + ATLAS_DEBUG_VAR( tmp( r, c ) ); + } + ATLAS_ASSERT( connectivity( r, c ) == tmp( r, c ) ); + connectivity.set( r, c, tmp( order.at( r ), c ) ); + } + } +} + +// ------------------------------------------------------------------ + +void ReorderImpl::reorderNodes( Mesh& mesh, const std::vector& order ) { + std::vector order_inverse( order.size() ); + for ( idx_t i = 0; i < static_cast( order.size() ); ++i ) { + order_inverse[order[i]] = i; + } + + for ( idx_t ifield = 0; ifield < mesh.nodes().nb_fields(); ++ifield ) { + reorder_field( mesh.nodes().field( ifield ), order ); + } + + if ( mesh.cells().size() ) { + update_connectivity( mesh.cells().node_connectivity(), order_inverse ); + } + if ( mesh.edges().size() ) { + update_connectivity( mesh.edges().node_connectivity(), order_inverse ); + } +} + +// ------------------------------------------------------------------ + +void reorder_elements_using_nodes( Mesh& mesh, Mesh::HybridElements& elements ) { + for ( idx_t t = 0; t < elements.nb_types(); ++t ) { + auto& elems = elements.elements( t ); + auto& connectivity = elems.node_connectivity(); + idx_t nb_nodes = elems.nb_nodes(); + idx_t nb_elems = elems.size(); + std::vector> node_lowest_index; + node_lowest_index.reserve( elems.size() ); + for ( idx_t e = 0; e < nb_elems; ++e ) { + idx_t lowest = std::numeric_limits::max(); + for ( idx_t n = 0; n < nb_nodes; ++n ) { + lowest = std::min( lowest, connectivity( e, n ) ); + } + node_lowest_index.emplace_back( lowest, e ); + } + std::sort( node_lowest_index.begin(), node_lowest_index.end() ); + std::vector order; + order.reserve( nb_elems ); + for ( const auto& pair : node_lowest_index ) { + order.emplace_back( pair.second ); + } + for ( idx_t ifield = 0; ifield < mesh.edges().nb_fields(); ++ifield ) { + reorder_field( elements.field( ifield ), order, elems.begin(), elems.end() ); + } + + reorder_connectivity( elems.node_connectivity(), order ); + } +} + +// ------------------------------------------------------------------ + +void ReorderImpl::reorderCellsUsingNodes( Mesh& mesh ) { + reorder_elements_using_nodes( mesh, mesh.cells() ); +} + +// ------------------------------------------------------------------ + +void ReorderImpl::reorderEdgesUsingNodes( Mesh& mesh ) { + reorder_elements_using_nodes( mesh, mesh.edges() ); +} + +// ------------------------------------------------------------------ + +void ReorderImpl::operator()( Mesh& mesh ) { + ATLAS_TRACE( "ReorderImpl(mesh)" ); + + reorderNodes( mesh, computeNodesOrder( mesh ) ); + reorderCellsUsingNodes( mesh ); + reorderEdgesUsingNodes( mesh ); +} + +// ------------------------------------------------------------------ + +namespace { +static ReorderBuilder __ReorderReverseCuthillMckee( "none" ); +} // namespace + +// ------------------------------------------------------------------ + +} // namespace actions +} // namespace mesh +} // namespace atlas diff --git a/src/atlas/mesh/actions/Reorder.h b/src/atlas/mesh/actions/Reorder.h new file mode 100644 index 000000000..cb5b3cf86 --- /dev/null +++ b/src/atlas/mesh/actions/Reorder.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/library/config.h" +#include "atlas/util/Config.h" +#include "atlas/util/Factory.h" +#include "atlas/util/Object.h" +#include "atlas/util/ObjectHandle.h" + +namespace atlas { +class Mesh; + +namespace mesh { +namespace actions { + +// ------------------------------------------------------------------ + +/// Base class for reordering the mesh nodes and elements +class ReorderImpl : public util::Object { +public: + ReorderImpl() = default; + +public: // -- static functions -- + /// Reorder the nodes in the given mesh using a given order + /// - All fields in mesh.nodes() are reordered. + /// - mesh.cells().node_connectivity() gets updated + /// - mesh.edges().node_connectivity() gets updated + static void reorderNodes( Mesh& mesh, const std::vector& order ); + + /// Reorder the cells by lowest node local index within each cell + static void reorderCellsUsingNodes( Mesh& mesh ); + + /// Reorder the edges by lowest node local index within each edge + static void reorderEdgesUsingNodes( Mesh& mesh ); + +public: // -- member functions -- + /// Reorder the nodes in the given mesh using the order computed with the computeNodesOrder function + /// Then apply reorderCellsUsingNodes and reorderEdgesUsingNodes + virtual void operator()( Mesh& ); + + virtual std::vector computeNodesOrder( Mesh& ) = 0; +}; + +//---------------------------------------------------------------------------------------------------------------------- + +class ReorderFactory : public util::Factory { +public: + static std::string className() { return "ReorderFactory"; } + static const ReorderImpl* build( const eckit::Parametrisation& ); + using Factory::Factory; + +private: + virtual const ReorderImpl* make( const eckit::Parametrisation& ) = 0; +}; + +//---------------------------------------------------------------------------------------------------------------------- + +template +class ReorderBuilder : public ReorderFactory { +private: + virtual const ReorderImpl* make( const eckit::Parametrisation& param ) { return new T( param ); } + +public: + using ReorderFactory::ReorderFactory; +}; + +//---------------------------------------------------------------------------------------------------------------------- + +class Reorder : public util::ObjectHandle { +public: + using Parameters = atlas::util::Config; + +public: + using Handle::Handle; + Reorder( const eckit::Parametrisation& config ) : Handle( ReorderFactory::build( config ) ) {} + + /// Reorder the nodes in the given mesh using the order computed with the ReorderImpl::computeNodesOrder function + /// Then apply ReorderImpl::reorderCellsUsingNodes and ReorderImpl::reorderEdgesUsingNodes + void operator()( Mesh& mesh ) { get()->operator()( mesh ); } +}; + +//---------------------------------------------------------------------------------------------------------------------- + +/// Dummy implemenation of ReorderImpl which does nothing +class NoReorder : public ReorderImpl { +public: + NoReorder( const eckit::Parametrisation& ) {} + void operator()( Mesh& ) override {} + std::vector computeNodesOrder( Mesh& ) override { return {}; } +}; + +//---------------------------------------------------------------------------------------------------------------------- + +} // namespace actions +} // namespace mesh +} // namespace atlas diff --git a/src/atlas/mesh/actions/ReorderHilbert.cc b/src/atlas/mesh/actions/ReorderHilbert.cc index 857199a04..a57ea0872 100644 --- a/src/atlas/mesh/actions/ReorderHilbert.cc +++ b/src/atlas/mesh/actions/ReorderHilbert.cc @@ -20,7 +20,6 @@ #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" -#include "atlas/mesh/actions/BuildCellCentres.h" #include "atlas/mesh/actions/ReorderHilbert.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" @@ -42,11 +41,12 @@ namespace actions { /// - John J. Bartholdi and Paul Goldsman "Vertex-Labeling Algorithms for the Hilbert Spacefilling Curve"\n /// It is adapted to return contiguous numbers of the gidx_t type, instead of a double [0,1] /// -/// Given a bounding box and number of hilbert levels, the bounding box can be divided in +/// Given a bounding box and number of hilbert recursions, the bounding box can be divided in /// 2^(dim*levels) equally spaced cells. A given coordinate falling inside one of these cells, is assigned /// the 1-dimensional Hilbert-index of this cell. To make sure that 1 coordinate corresponds to only 1 /// Hilbert index, the number of levels have to be increased. -/// In 2D, the levels cannot be higher than 15, if you want the indices to fit in "unsigned int" type of 32bit. +/// In 2D, the recursion cannot be higher than 15, if you want the indices to fit in "unsigned int" type of 32bit. +/// In 2D, the recursion cannot be higher than 30, if you want the indices to fit in "unsigned int" type of 64bit. /// /// /// No attempt is made to provide the most efficient algorithm. There exist other open-source @@ -207,116 +207,11 @@ gidx_t Hilbert::recursive_algorithm( const PointXY& p, const box_t& box, idx_t l // ------------------------------------------------------------------ -ReorderHilbert::ReorderHilbert( const eckit::Configuration& config ) { +ReorderHilbert::ReorderHilbert( const eckit::Parametrisation& config ) { config.get( "recursion", recursion_ ); + config.get( "ghost_at_end", ghost_at_end_ ); } -template -struct ReorderField {}; - -template -struct ReorderField { - static constexpr int Rank = 1; - static std::string apply( Field& field, const std::vector& order, idx_t begin, idx_t end ) { - auto array = array::make_view( field ); - end = std::min( end, array.shape( 0 ) ); - idx_t size = end - begin; - array::ArrayT tmp_array( size ); - auto tmp = array::make_view( tmp_array ); - for ( idx_t n = 0; n < size; ++n ) { - tmp( n ) = array( begin + n ); - } - for ( idx_t n = 0; n < size; ++n ) { - array( begin + n ) = tmp( order[n] ); - } - return field.name(); - } -}; - -template -struct ReorderField { - static constexpr int Rank = 2; - static std::string apply( Field& field, const std::vector& order, idx_t begin, idx_t end ) { - auto array = array::make_view( field ); - end = std::min( end, array.shape( 0 ) ); - idx_t size = end - begin; - array::ArrayT tmp_array( size, field.shape( 1 ) ); - auto tmp = array::make_view( tmp_array ); - for ( idx_t n = 0; n < size; ++n ) { - for ( idx_t v = 0; v < array.shape( 1 ); ++v ) { - tmp( n, v ) = array( begin + n, v ); - } - } - for ( idx_t n = 0; n < size; ++n ) { - for ( idx_t v = 0; v < array.shape( 1 ); ++v ) { - array( begin + n, v ) = tmp( order[n], v ); - } - } - return field.name(); - } -}; - -template -std::string reorder_field_T( Field& field, const std::vector& order, idx_t begin, idx_t end ) { - if ( field.rank() == 1 ) { - return ReorderField::apply( field, order, begin, end ); - } - else if ( field.rank() == 2 ) { - return ReorderField::apply( field, order, begin, end ); - } - else { - throw_Exception( "rank not supported", Here() ); - } -} - -std::string reorder_field( Field& field, const std::vector& order, idx_t begin = 0, - idx_t end = std::numeric_limits::max() ) { - if ( field.datatype() == array::DataType::kind() ) { - return reorder_field_T( field, order, begin, end ); - } - else if ( field.datatype() == array::DataType::kind() ) { - return reorder_field_T( field, order, begin, end ); - } - else if ( field.datatype() == array::DataType::kind() ) { - return reorder_field_T( field, order, begin, end ); - } - else if ( field.datatype() == array::DataType::kind() ) { - return reorder_field_T( field, order, begin, end ); - } - else { - throw_Exception( "datatype not supported", Here() ); - } -} - -void update_connectivity( mesh::HybridElements::Connectivity& connectivity, const std::vector& order ) { - for ( idx_t b = 0; b < connectivity.blocks(); ++b ) { - auto& block = connectivity.block( b ); - for ( idx_t r = 0; r < block.rows(); ++r ) { - for ( idx_t c = 0; c < block.cols(); ++c ) { - idx_t n = block( r, c ); - block.set( r, c, order.at( n ) ); - } - } - } -} - -void reorder_connectivity( BlockConnectivity& connectivity, const std::vector& order ) { - ATLAS_ASSERT( connectivity.rows() == order.size() ); - BlockConnectivity tmp; - tmp.add( connectivity.rows(), connectivity.cols(), connectivity.data(), true ); - for ( idx_t r = 0; r < connectivity.rows(); ++r ) { - for ( idx_t c = 0; c < connectivity.cols(); ++c ) { - if ( connectivity( r, c ) != tmp( r, c ) ) { - ATLAS_DEBUG_VAR( r ); - ATLAS_DEBUG_VAR( c ); - ATLAS_DEBUG_VAR( connectivity( r, c ) ); - ATLAS_DEBUG_VAR( tmp( r, c ) ); - } - ATLAS_ASSERT( connectivity( r, c ) == tmp( r, c ) ); - connectivity.set( r, c, tmp( order.at( r ), c ) ); - } - } -} Domain global_bounding_box( const Mesh& mesh ) { auto xy = array::make_view( mesh.nodes().xy() ); @@ -340,128 +235,125 @@ Domain global_bounding_box( const Mesh& mesh ) { return RectangularDomain( {xmin, xmax}, {ymin, ymax} ); } -void ReorderHilbert::operator()( Mesh& mesh ) { - ATLAS_TRACE( "ReorderHilbert()" ); - +std::vector ReorderHilbert::computeNodesOrder( Mesh& mesh ) { using hilbert_reordering_t = std::vector>; Hilbert hilbert{global_bounding_box( mesh ), recursion_}; - // Reorder nodes - { - auto xy = array::make_view( mesh.nodes().xy() ); - auto ghost = array::make_view( mesh.nodes().ghost() ); + auto xy = array::make_view( mesh.nodes().xy() ); + auto ghost = array::make_view( mesh.nodes().ghost() ); + + idx_t size = xy.shape( 0 ); + hilbert_reordering_t hilbert_reordering; + hilbert_reordering.reserve( size ); + ATLAS_TRACE_SCOPE( "hilbert nodes" ) { + for ( idx_t n = 0; n < size; ++n ) { + PointXY p{xy( n, XX ), xy( n, YY )}; + if ( not ghost( n ) ) { + hilbert_reordering.emplace_back( hilbert( p ), n ); + } + else { + if ( ghost_at_end_ ) { + // ghost nodes get a fake "hilbert_idx" at the end + hilbert_reordering.emplace_back( hilbert.nb_keys() + n, n ); + } + else { + hilbert_reordering.emplace_back( hilbert( p ), n ); + } + } + } + } + + std::sort( hilbert_reordering.begin(), hilbert_reordering.end() ); + std::vector order; + order.reserve( size ); + for ( const auto& pair : hilbert_reordering ) { + order.emplace_back( pair.second ); + } + return order; +} + + +#if 0 +// Reorder elements +if ( 0 ) { + hilbert_reordering_t hilbert_reordering; + std::vector order; + std::vector order_inverse; + + auto cell_centres = + Field( "cell_centres", array::make_datatype(), array::make_shape( mesh.cells().size(), 2 ) ); + auto nodes_xy = array::make_view( mesh.nodes().xy() ); + for ( idx_t t = 0; t < mesh.cells().nb_types(); ++t ) { + auto& cells = mesh.cells().elements( t ); + auto xy = cells.view( cell_centres ); + auto flags = cells.view( mesh.cells().flags() ); + auto halo = cells.view( mesh.cells().halo() ); + + // Compute cell-centres + { + const auto& node_connectivity = cells.node_connectivity(); + const idx_t nb_nodes = cells.nb_nodes(); + const double nb_nodes_double = nb_nodes; + for ( idx_t e = 0; e < cells.size(); ++e ) { + double x{0}; + double y{0}; + for ( idx_t c = 0; c < nb_nodes; ++c ) { + idx_t n = node_connectivity( e, c ); + x += nodes_xy( n, XX ); + y += nodes_xy( n, YY ); + } + xy( e, XX ) = x / nb_nodes_double; + xy( e, YY ) = y / nb_nodes_double; + } + } + + auto skip = [&]( idx_t n ) { + if ( halo( n ) || mesh::Nodes::Topology::check( flags( n ), mesh::Nodes::Topology::PATCH ) ) { + return true; + } + return false; + }; idx_t size = xy.shape( 0 ); - hilbert_reordering_t hilbert_reordering; + hilbert_reordering.clear(); hilbert_reordering.reserve( size ); - ATLAS_TRACE_SCOPE( "hilbert nodes" ) { + ATLAS_TRACE_SCOPE( "hilbert elements[" + std::to_string( t ) + "]" ) { for ( idx_t n = 0; n < size; ++n ) { - if ( not ghost( n ) ) { - PointXY p{xy( n, XX ), xy( n, YY )}; - auto hilbert_idx = hilbert( p ); - hilbert_reordering.emplace_back( hilbert_idx, n ); + PointXY p{xy( n, XX ), xy( n, YY )}; + if ( not skip( n ) ) { + hilbert_reordering.emplace_back( hilbert( p ), n ); } - else { // ghost nodes get a fake "hilbert_idx" at the end + else { // halo elements get a fake "hilbert_idx" at the end hilbert_reordering.emplace_back( hilbert.nb_keys() + n, n ); } } } - + ATLAS_ASSERT( hilbert_reordering.size() == size ); std::sort( hilbert_reordering.begin(), hilbert_reordering.end() ); - std::vector order; + order.clear(); order.reserve( size ); - std::vector order_inverse; order_inverse.resize( size ); idx_t c{0}; for ( const auto& pair : hilbert_reordering ) { order.emplace_back( pair.second ); order_inverse[pair.second] = c++; + ATLAS_ASSERT( pair.second < size ); } - for ( idx_t ifield = 0; ifield < mesh.nodes().nb_fields(); ++ifield ) { - reorder_field( mesh.nodes().field( ifield ), order ); + for ( idx_t ifield = 0; ifield < mesh.cells().nb_fields(); ++ifield ) { + reorder_field( mesh.cells().field( ifield ), order, cells.begin(), cells.end() ); } - update_connectivity( mesh.cells().node_connectivity(), order_inverse ); - } - - // Reorder elements - if ( 1 ) { - hilbert_reordering_t hilbert_reordering; - std::vector order; - std::vector order_inverse; - - auto cell_centres = - Field( "cell_centres", array::make_datatype(), array::make_shape( mesh.cells().size(), 2 ) ); - auto nodes_xy = array::make_view( mesh.nodes().xy() ); - for ( idx_t t = 0; t < mesh.cells().nb_types(); ++t ) { - auto& cells = mesh.cells().elements( t ); - auto xy = cells.view( cell_centres ); - auto flags = cells.view( mesh.cells().flags() ); - auto halo = cells.view( mesh.cells().halo() ); - - // Compute cell-centres - { - const auto& node_connectivity = cells.node_connectivity(); - const idx_t nb_nodes = cells.nb_nodes(); - const double nb_nodes_double = nb_nodes; - for ( idx_t e = 0; e < cells.size(); ++e ) { - double x{0}; - double y{0}; - for ( idx_t c = 0; c < nb_nodes; ++c ) { - idx_t n = node_connectivity( e, c ); - x += nodes_xy( n, XX ); - y += nodes_xy( n, YY ); - } - xy( e, XX ) = x / nb_nodes_double; - xy( e, YY ) = y / nb_nodes_double; - } - } - - - auto skip = [&]( idx_t n ) { - if ( halo( n ) || mesh::Nodes::Topology::check( flags( n ), mesh::Nodes::Topology::PATCH ) ) { - return true; - } - return false; - }; - idx_t size = xy.shape( 0 ); - hilbert_reordering.clear(); - hilbert_reordering.reserve( size ); - ATLAS_TRACE_SCOPE( "hilbert elements[" + std::to_string( t ) + "]" ) { - for ( idx_t n = 0; n < size; ++n ) { - PointXY p{xy( n, XX ), xy( n, YY )}; - if ( not skip( n ) ) { - hilbert_reordering.emplace_back( hilbert( p ), n ); - } - else { // halo elements get a fake "hilbert_idx" at the end - hilbert_reordering.emplace_back( hilbert.nb_keys() + n, n ); - } - } - } - ATLAS_ASSERT( hilbert_reordering.size() == size ); - std::sort( hilbert_reordering.begin(), hilbert_reordering.end() ); - order.clear(); - order.reserve( size ); - order_inverse.resize( size ); - idx_t c{0}; - for ( const auto& pair : hilbert_reordering ) { - order.emplace_back( pair.second ); - order_inverse[pair.second] = c++; - ATLAS_ASSERT( pair.second < size ); - } - - for ( idx_t ifield = 0; ifield < mesh.cells().nb_fields(); ++ifield ) { - reorder_field( mesh.cells().field( ifield ), order, cells.begin(), cells.end() ); - } - - reorder_connectivity( cells.node_connectivity(), order ); - } + reorder_connectivity( cells.node_connectivity(), order ); } } +#endif + +namespace { +static ReorderBuilder __ReorderHilbert( "hilbert" ); +} // namespace -// ------------------------------------------------------------------ } // namespace actions } // namespace mesh diff --git a/src/atlas/mesh/actions/ReorderHilbert.h b/src/atlas/mesh/actions/ReorderHilbert.h index 59d1bbc11..27bf03a43 100644 --- a/src/atlas/mesh/actions/ReorderHilbert.h +++ b/src/atlas/mesh/actions/ReorderHilbert.h @@ -10,24 +10,37 @@ #pragma once -#include "atlas/library/config.h" -#include "atlas/util/Config.h" +#include "atlas/mesh/actions/Reorder.h" namespace atlas { -class Mesh; - namespace mesh { namespace actions { -// ------------------------------------------------------------------ - -class ReorderHilbert { +//---------------------------------------------------------------------------------------------------------------------- + +/// Reorder implementation that reorders nodes of a mesh following a Hilbert Space-filling curve. +/// Cells and edges are reordered to follow lowest node index. +/// +/// Usage: +/// auto reorder = Reorder{ option::type("hilbert") | config }; +/// reorder( mesh ); +/// +/// The optional extra config can contain: +/// +/// - "recursion" : (default=30) // Recursion of hilbert space-filling curve, +/// // needs to be large enough to provide unique node indices. +/// +/// - "ghost_at_end" : (default=true) // Determines if ghost nodes should be reordered in between +/// // internal nodes or added/remain at the end +class ReorderHilbert : public ReorderImpl { public: - ReorderHilbert( const eckit::Configuration& config = util::NoConfig() ); - void operator()( Mesh& ); + ReorderHilbert( const eckit::Parametrisation& config = util::NoConfig() ); + + std::vector computeNodesOrder( Mesh& ) override; private: - idx_t recursion_{20}; + idx_t recursion_{30}; + bool ghost_at_end_{true}; }; // ------------------------------------------------------------------ diff --git a/src/atlas/mesh/actions/ReorderReverseCuthillMckee.cc b/src/atlas/mesh/actions/ReorderReverseCuthillMckee.cc new file mode 100644 index 000000000..f95678fa2 --- /dev/null +++ b/src/atlas/mesh/actions/ReorderReverseCuthillMckee.cc @@ -0,0 +1,197 @@ +/* + * (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 +#include + + +#include "atlas/mesh/actions/ReorderReverseCuthillMckee.h" + +#include "atlas/array.h" +#include "atlas/mesh/Mesh.h" +#include "atlas/mesh/Nodes.h" +#include "atlas/mesh/detail/AccumulateFacets.h" +#include "atlas/runtime/Trace.h" + +#include "eckit/linalg/SparseMatrix.h" +#include "eckit/linalg/Triplet.h" + +namespace atlas { +namespace mesh { +namespace actions { + +using NotVisited_t = std::list>; + +NotVisited_t::iterator find_index( NotVisited_t& a, int x ) { + return std::find_if( a.begin(), a.end(), [x]( NotVisited_t::value_type& element ) { return element.first == x; } ); +} + +class CuthillMckee { +private: + eckit::linalg::SparseMatrix sparse_; + +public: + CuthillMckee( eckit::linalg::SparseMatrix&& m ) : sparse_( std::move( m ) ) {} + + // Function to generate degree of all the nodes + std::vector computeDegrees( const eckit::linalg::SparseMatrix& m ) { + std::vector degrees; + degrees.reserve( m.rows() ); + const auto outer = m.outer(); + for ( size_t i = 0; i < m.rows(); i++ ) { + degrees.emplace_back( outer[i + 1] - outer[i] ); + } + return degrees; + } + + // Cuthill-Mckee algorithm + std::vector order() { + auto degrees = computeDegrees( sparse_ ); + + std::queue Q; + std::vector R; + R.reserve( degrees.size() ); + + std::vector sorted_row; + sorted_row.reserve( sparse_.cols() ); + + NotVisited_t not_visited; + + for ( int i = 0; i < degrees.size(); i++ ) { + not_visited.emplace_back( i, degrees[i] ); + } + + not_visited.sort( []( const NotVisited_t::value_type& a, const NotVisited_t::value_type& b ) { + return a.second < b.second; + } ); + + // notVisited helps in running Breadth First Search even when there are disjoint graphs + + const auto outer = sparse_.outer(); + const auto index = sparse_.inner(); + + while ( not_visited.size() ) { + Q.emplace( not_visited.front().first ); + not_visited.pop_front(); + + // Simple Breadth First Search + while ( !Q.empty() ) { + int row = Q.front(); + + sorted_row.clear(); + + for ( auto j = outer[row]; j < outer[row + 1]; ++j ) { + if ( index[j] != row ) { + auto found_index = find_index( not_visited, index[j] ); + if ( found_index != not_visited.end() ) { + sorted_row.emplace_back( index[j] ); + not_visited.erase( found_index ); + } + } + } + + std::sort( sorted_row.begin(), sorted_row.end(), + [&]( int i, int j ) { return degrees[i] - degrees[j]; } ); + + for ( size_t i = 0; i < sorted_row.size(); i++ ) { + Q.emplace( sorted_row[i] ); + } + + R.emplace_back( Q.front() ); + Q.pop(); + } + } + + return R; + } +}; + +class ReverseCuthillMckee { +private: + CuthillMckee cuthill_mckee_; + +public: + ReverseCuthillMckee( eckit::linalg::SparseMatrix&& m ) : cuthill_mckee_( std::move( m ) ) {} + + // Reverse Cuthill-Mckee algorithm + std::vector order() { + std::vector cuthill = cuthill_mckee_.order(); + + int size = static_cast( cuthill.size() ); + int n = size; + + if ( n % 2 == 0 ) { + n -= 1; + } + + n = n / 2; + + for ( int i = 0; i <= n; i++ ) { + int j = cuthill[size - 1 - i]; + cuthill[size - 1 - i] = cuthill[i]; + cuthill[i] = j; + } + + return cuthill; + } +}; + + +ReorderReverseCuthillMckee::ReorderReverseCuthillMckee( const eckit::Parametrisation& config ) { + config.get( "ghost_at_end", ghost_at_end_ ); +} + +std::vector ReorderReverseCuthillMckee::computeNodesOrder( Mesh& mesh ) { + ATLAS_TRACE(); + std::vector n2n_triplets; + { + auto ghost_node = array::make_view( mesh.nodes().ghost() ); + + std::vector facet_nodes_data; + std::vector connectivity_facet_to_elem; + idx_t nb_facets; + idx_t nb_inner_facets; + idx_t missing_value; + + detail::accumulate_facets( mesh.cells(), mesh.nodes(), + facet_nodes_data, // shape(nb_facets,nb_nodes_per_facet) + connectivity_facet_to_elem, nb_facets, nb_inner_facets, missing_value ); + + n2n_triplets.reserve( 2 * nb_facets ); + + for ( idx_t e = 0; e < nb_facets; ++e ) { + idx_t node_0 = facet_nodes_data[2 * e + 0]; + idx_t node_1 = facet_nodes_data[2 * e + 1]; + if ( ghost_at_end_ && ( ghost_node( node_0 ) || ghost_node( node_1 ) ) ) { + continue; + } + n2n_triplets.emplace_back( node_0, node_1, 1. ); + n2n_triplets.emplace_back( node_1, node_0, 1. ); + } + } + + std::sort( n2n_triplets.begin(), n2n_triplets.end() ); + + auto nb_nodes = static_cast( mesh.nodes().size() ); + return ReverseCuthillMckee{{nb_nodes, nb_nodes, n2n_triplets}}.order(); +} + + +namespace { +static ReorderBuilder __ReorderReverseCuthillMckee( "reverse_cuthill_mckee" ); +} // namespace + +} // namespace actions +} // namespace mesh +} // namespace atlas diff --git a/src/atlas/mesh/actions/ReorderReverseCuthillMckee.h b/src/atlas/mesh/actions/ReorderReverseCuthillMckee.h new file mode 100644 index 000000000..a65ad2d0f --- /dev/null +++ b/src/atlas/mesh/actions/ReorderReverseCuthillMckee.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/mesh/actions/Reorder.h" + +namespace atlas { +namespace mesh { +namespace actions { + +// ------------------------------------------------------------------ + +/// Reorder implementation that reorders nodes of a mesh following a Reverse Cuthill-Mckee algorithm +/// based on the node-to-node connectivity by mesh edges +/// Cells and edges are reordered to follow lowest node index. +/// +/// Usage: +/// auto reorder = Reorder{ option::type("reverse_cuthill_mckee") | config }; +/// reorder( mesh ); +/// +/// The optional extra config can contain: +/// +/// - "ghost_at_end" : (default=true) // Determines if ghost nodes should be reordered in between +/// // internal nodes or added/remain at the end + +class ReorderReverseCuthillMckee : public ReorderImpl { +public: + ReorderReverseCuthillMckee( const eckit::Parametrisation& = util::NoConfig() ); + + std::vector computeNodesOrder( Mesh& ) override; + +private: + bool ghost_at_end_{true}; +}; + + +// ------------------------------------------------------------------ + +} // namespace actions +} // namespace mesh +} // namespace atlas diff --git a/src/tests/mesh/CMakeLists.txt b/src/tests/mesh/CMakeLists.txt index 0746db8fc..de904dfbb 100644 --- a/src/tests/mesh/CMakeLists.txt +++ b/src/tests/mesh/CMakeLists.txt @@ -88,7 +88,7 @@ ecbuild_add_test( TARGET atlas_test_mesh_node2cell ) -foreach( test connectivity stream_connectivity elements ll meshgen3d rgg hilbert_reordering ) +foreach( test connectivity stream_connectivity elements ll meshgen3d rgg mesh_reorder ) ecbuild_add_test( TARGET atlas_test_${test} SOURCES test_${test}.cc LIBS atlas diff --git a/src/tests/mesh/test_hilbert_reordering.cc b/src/tests/mesh/test_hilbert_reordering.cc deleted file mode 100644 index 998b815ba..000000000 --- a/src/tests/mesh/test_hilbert_reordering.cc +++ /dev/null @@ -1,235 +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 "atlas/functionspace.h" -#include "atlas/grid.h" -#include "atlas/mesh.h" -#include "atlas/meshgenerator.h" - -#include "atlas/mesh/actions/BuildEdges.h" -#include "atlas/mesh/actions/ReorderHilbert.h" -#include "atlas/output/Gmsh.h" -#include "atlas/runtime/Log.h" -#include "atlas/util/CoordinateEnums.h" - -#include "tests/AtlasTestEnvironment.h" - -namespace atlas { -namespace test { - -//----------------------------------------------------------------------------- - -void outputHilbertCurve( const eckit::PathName& filepath, const array::ArrayView& xy ) { - const eckit::mpi::Comm& comm = atlas::mpi::comm(); - int mpi_rank = int( comm.rank() ); - int mpi_size = int( comm.size() ); - - double xmin = std::numeric_limits::max(); - double xmax = -std::numeric_limits::max(); - for ( idx_t i = 0; i < xy.shape( 0 ); ++i ) { - xmin = std::min( xmin, xy( i, XX ) ); - xmax = std::max( xmax, xy( i, XX ) ); - } - comm.allReduceInPlace( xmin, eckit::mpi::min() ); - comm.allReduceInPlace( xmax, eckit::mpi::max() ); - - idx_t count = xy.shape( 0 ); - idx_t count_all = count; - comm.allReduceInPlace( count_all, eckit::mpi::sum() ); - - for ( int r = 0; r < mpi_size; ++r ) { - if ( mpi_rank == r ) { - std::ofstream f( filepath.asString().c_str(), mpi_rank == 0 ? std::ios::trunc : std::ios::app ); - - if ( mpi_rank == 0 ) { - f << "\n" - "\n" - "import matplotlib.pyplot as plt" - "\n" - "from matplotlib.path import Path" - "\n" - "import matplotlib.patches as patches" - "\n" - "" - "\n" - "from itertools import cycle" - "\n" - "import matplotlib.cm as cm" - "\n" - "import numpy as np" - "\n" - "cycol = cycle([cm.Paired(i) for i in " - "np.linspace(0,1,12,endpoint=True)]).next" - "\n" - "" - "\n" - "fig = plt.figure()" - "\n" - "ax = fig.add_subplot(111,aspect='equal')" - "\n" - ""; - } - - if ( mpi_rank == r ) { // replace "r" with rank you wish to plot only - f << "\n" - "verts_" - << r << " = ["; - for ( idx_t n = 0; n < xy.shape( 0 ); ++n ) { - idx_t i = n; //reorder.hilbert_reordering_[n].second; - f << "\n (" << xy( i, XX ) << ", " << xy( i, YY ) << "), "; - } - f << "\n]" - "\n" - "" - "\n" - "codes_" - << r - << " = [Path.MOVETO]" - "\n" - "codes_" - << r << ".extend([Path.LINETO] * " << ( xy.shape( 0 ) - 2 ) - << ")" - "\n" - "codes_" - << r - << ".extend([Path.LINETO])" - "\n" - "" - "\n" - "count_" - << r << " = " << count - << "\n" - "count_all_" - << r << " = " << count_all - << "\n" - "c = cycol()" - "\n" - "xs_" - << r << ", ys_" << r << " = zip(*verts_" << r - << ")" - "\n" - "ax.plot(xs_" - << r << ",ys_" << r - << ", '-', lw=1, color=c )" - "\n" - // "for i in range( len(verts_0) ):" - // "\n" - // " plt.text( xs_0[i], ys_0[i], str(i) )" - // "\n" - ""; - } - if ( mpi_rank == mpi_size - 1 ) { - f << "\n" - "ax.set_xlim( " - << xmin << "-5, " << xmax - << "+5)" - "\n" - "ax.set_ylim(-90-5, 90+5)" - "\n" - "ax.set_xticks([0,45,90,135,180,225,270,315,360])" - "\n" - "ax.set_yticks([-90,-45,0,45,90])" - "\n" - "plt.grid()" - "\n" - "plt.show()"; - } - } - comm.barrier(); - } -} - -Field create_cell_centres( Mesh& mesh ) { - auto cell_centres = - Field( "cell_centres", array::make_datatype(), array::make_shape( mesh.cells().size(), 2 ) ); - auto nodes_xy = array::make_view( mesh.nodes().xy() ); - for ( idx_t t = 0; t < mesh.cells().nb_types(); ++t ) { - auto& cells = mesh.cells().elements( t ); - auto xy = cells.view( cell_centres ); - - // Compute cell-centres - { - const auto& node_connectivity = cells.node_connectivity(); - const idx_t nb_nodes = cells.nb_nodes(); - const double nb_nodes_double = nb_nodes; - for ( idx_t e = 0; e < cells.size(); ++e ) { - double x{0}; - double y{0}; - for ( idx_t c = 0; c < nb_nodes; ++c ) { - idx_t n = node_connectivity( e, c ); - x += nodes_xy( n, XX ); - y += nodes_xy( n, YY ); - } - xy( e, XX ) = x / nb_nodes_double; - xy( e, YY ) = y / nb_nodes_double; - } - } - } - return cell_centres; -} - -Field create_edge_centres( Mesh& mesh ) { - auto edge_centres = - Field( "edge_centres", array::make_datatype(), array::make_shape( mesh.edges().size(), 2 ) ); - auto nodes_xy = array::make_view( mesh.nodes().xy() ); - auto& edges = mesh.edges(); - auto xy = array::make_view( edge_centres ); - - // Compute edge-centres - { - const auto& node_connectivity = edges.node_connectivity(); - for ( idx_t e = 0; e < edges.size(); ++e ) { - double x{0}; - double y{0}; - for ( idx_t c = 0; c < 2; ++c ) { - idx_t n = node_connectivity( e, c ); - x += nodes_xy( n, XX ); - y += nodes_xy( n, YY ); - } - xy( e, XX ) = 0.5 * x; - xy( e, YY ) = 0.5 * y; - } - } - return edge_centres; -} - -CASE( "test_hilbert_reordering" ) { - auto meshgenerator = StructuredMeshGenerator( util::Config( "patch_pole", false )( "triangulate", true ) ); - Mesh mesh = meshgenerator( Grid( "O32" ) ); - auto reorder = mesh::actions::ReorderHilbert{util::Config( "recursion", 30 )}; - reorder( mesh ); - - // functionspace::NodeColumns( mesh, option::halo( 2 ) ); - - auto xy = array::make_view( mesh.nodes().xy() ); - outputHilbertCurve( "hilbert_nodes.py", xy ); - output::Gmsh gmsh{"hilbert.msh", util::Config( "coordinates", "xy" )}; - gmsh.write( mesh ); - - Field cell_centres = create_cell_centres( mesh ); - auto cell_centres_xy = array::make_view( cell_centres ); - outputHilbertCurve( "hilbert_elements.py", cell_centres_xy ); - - mesh::actions::build_edges( mesh ); - Field edge_centres = create_edge_centres( mesh ); - auto edge_centres_xy = array::make_view( edge_centres ); - outputHilbertCurve( "hilbert_edges.py", edge_centres_xy ); -} - -//----------------------------------------------------------------------------- - -} // namespace test -} // namespace atlas - -int main( int argc, char** argv ) { - return atlas::test::run( argc, argv ); -} diff --git a/src/tests/mesh/test_mesh_reorder.cc b/src/tests/mesh/test_mesh_reorder.cc new file mode 100644 index 000000000..eb53a8491 --- /dev/null +++ b/src/tests/mesh/test_mesh_reorder.cc @@ -0,0 +1,330 @@ +/* + * (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 "atlas/functionspace.h" +#include "atlas/grid.h" +#include "atlas/mesh.h" +#include "atlas/meshgenerator.h" + +#include "atlas/mesh/actions/BuildEdges.h" +#include "atlas/mesh/actions/Reorder.h" +#include "atlas/output/Gmsh.h" +#include "atlas/runtime/Log.h" +#include "atlas/util/CoordinateEnums.h" + + +#include "eckit/config/Resource.h" +#include "eckit/linalg/SparseMatrix.h" +#include "eckit/linalg/Triplet.h" + +#include "tests/AtlasTestEnvironment.h" + +//----------------------------------------------------------------- + +namespace atlas { +namespace test { + +//----------------------------------------------------------------------------- + +void outputConnectedCoordinates( const eckit::PathName& filepath, const array::ArrayView& xy ) { + const eckit::mpi::Comm& comm = atlas::mpi::comm(); + int mpi_rank = int( comm.rank() ); + int mpi_size = int( comm.size() ); + + double xmin = std::numeric_limits::max(); + double xmax = -std::numeric_limits::max(); + for ( idx_t i = 0; i < xy.shape( 0 ); ++i ) { + xmin = std::min( xmin, xy( i, XX ) ); + xmax = std::max( xmax, xy( i, XX ) ); + } + comm.allReduceInPlace( xmin, eckit::mpi::min() ); + comm.allReduceInPlace( xmax, eckit::mpi::max() ); + + idx_t count = xy.shape( 0 ); + idx_t count_all = count; + comm.allReduceInPlace( count_all, eckit::mpi::sum() ); + + for ( int r = 0; r < mpi_size; ++r ) { + if ( mpi_rank == r ) { + std::ofstream f( filepath.asString().c_str(), mpi_rank == 0 ? std::ios::trunc : std::ios::app ); + // clang-format off + if ( mpi_rank == 0 ) { + f << "\n" + "\n" "import matplotlib.pyplot as plt" + "\n" "from matplotlib.path import Path" + "\n" "import matplotlib.patches as patches" + "\n" "" + "\n" "from itertools import cycle" + "\n" "import matplotlib.cm as cm" + "\n" "import numpy as np" + "\n" "cycol = cycle([cm.Paired(i) for i in np.linspace(0,1,12,endpoint=True)]).next" + "\n" "" + "\n" "fig = plt.figure()" + "\n" "ax = fig.add_subplot(111,aspect='equal')" + "\n" ""; + } + + if ( mpi_rank == r ) { // replace "r" with rank you wish to plot only + f << "\n" "verts_" + << r << " = ["; + for ( idx_t n = 0; n < xy.shape( 0 ); ++n ) { + idx_t i = n; //reorder.hilbert_reordering_[n].second; + f << "\n (" << xy( i, XX ) << ", " << xy( i, YY ) << "), "; + } + f << "\n]" + "\n" "" + "\n" "codes_" << r << " = [Path.MOVETO]" + "\n" "codes_" << r << ".extend([Path.LINETO] * " << ( xy.shape( 0 ) - 2 ) << ")" + "\n" "codes_" << r << ".extend([Path.LINETO])" + "\n" "" + "\n" "count_" << r << " = " << count + << "\n" "count_all_" << r << " = " << count_all + << "\n" "c = cycol()" + "\n" "xs_" << r << ", ys_" << r << " = zip(*verts_" << r << ")" + "\n" "ax.plot(xs_" << r << ",ys_" << r << ", '-', lw=1, color=c )" + //"\n" "for i in range( len(verts_0) ):" + //"\n" " plt.text( xs_0[i], ys_0[i], str(i) )" + "\n" ""; + } + if ( mpi_rank == mpi_size - 1 ) { + f << "\n" "ax.set_xlim( " << xmin << "-5, " << xmax << "+5)" + "\n" "ax.set_ylim(-90-5, 90+5)" + "\n" "ax.set_xticks([0,45,90,135,180,225,270,315,360])" + "\n" "ax.set_yticks([-90,-45,0,45,90])" + "\n" "plt.grid()" + "\n" "plt.show()"; + } + // clang-format on + } + comm.barrier(); + } +} + +void outputTriplets( const eckit::PathName& filepath, const std::vector& triplets, idx_t rows, + idx_t cols ) { + const eckit::mpi::Comm& comm = atlas::mpi::comm(); + int mpi_rank = int( comm.rank() ); + int mpi_size = int( comm.size() ); + + for ( int r = 0; r < mpi_size; ++r ) { + if ( mpi_rank == r ) { + std::ofstream f( filepath.asString().c_str(), mpi_rank == 0 ? std::ios::trunc : std::ios::app ); + + if ( mpi_rank == 0 ) { + // clang-format off + f << "\n" + "\n" "import matplotlib.pyplot as plt" + "\n" "from matplotlib.path import Path" + "\n" "import matplotlib.patches as patches" + "\n" "import scipy.sparse as sparse" + "\n" "" + "\n" "from itertools import cycle" + "\n" "import matplotlib.cm as cm" + "\n" "import numpy as np" + "\n" "cycol = cycle([cm.Paired(i) for i in np.linspace(0,1,12,endpoint=True)]).next" + "\n" "" + "\n" "fig = plt.figure()" + "\n" "ax = fig.add_subplot(111,aspect='equal')" + "\n" ""; + } + + if ( mpi_rank == 0 ) { // replace "r" with rank you wish to plot only + f << "\n" "row_"<< r << " = np.array(["; + for ( const auto& triplet : triplets ) { + f << "\n " << triplet.row() << ", "; + } + f << "\n" "])" + "\n" "" + "\n" "col_"<< r << " = np.array(["; + for ( const auto& triplet : triplets ) { + f << "\n " << triplet.col() << ", "; + } + f << "\n" "])" + "\n" "data_"<< r << " = np.array(["; + for ( const auto& triplet : triplets ) { + f << "\n " << triplet.value() << ", "; + } + f << "\n" "])" + "\n" "" + "\n" "matrix_" << r << " = sparse.csr_matrix((data_"<(), array::make_shape( mesh.cells().size(), 2 ) ); + auto nodes_xy = array::make_view( mesh.nodes().xy() ); + for ( idx_t t = 0; t < mesh.cells().nb_types(); ++t ) { + auto& cells = mesh.cells().elements( t ); + auto xy = cells.view( cell_centres ); + + // Compute cell-centres + { + const auto& node_connectivity = cells.node_connectivity(); + const idx_t nb_nodes = cells.nb_nodes(); + const double nb_nodes_double = nb_nodes; + for ( idx_t e = 0; e < cells.size(); ++e ) { + double x{0}; + double y{0}; + for ( idx_t c = 0; c < nb_nodes; ++c ) { + idx_t n = node_connectivity( e, c ); + x += nodes_xy( n, XX ); + y += nodes_xy( n, YY ); + } + xy( e, XX ) = x / nb_nodes_double; + xy( e, YY ) = y / nb_nodes_double; + } + } + } + return cell_centres; +} + +Field create_edge_centres( Mesh& mesh ) { + auto edge_centres = + Field( "edge_centres", array::make_datatype(), array::make_shape( mesh.edges().size(), 2 ) ); + auto nodes_xy = array::make_view( mesh.nodes().xy() ); + auto& edges = mesh.edges(); + auto xy = array::make_view( edge_centres ); + + // Compute edge-centres + { + const auto& node_connectivity = edges.node_connectivity(); + for ( idx_t e = 0; e < edges.size(); ++e ) { + double x{0}; + double y{0}; + for ( idx_t c = 0; c < 2; ++c ) { + idx_t n = node_connectivity( e, c ); + x += nodes_xy( n, XX ); + y += nodes_xy( n, YY ); + } + xy( e, XX ) = 0.5 * x; + xy( e, YY ) = 0.5 * y; + } + } + return edge_centres; +} + +Grid grid() { + return Grid{eckit::Resource( "--grid", "O16" )}; +} + +void test_reordering( const util::Config& reorder_config, const std::vector& expected = {} ) { + std::string type = reorder_config.getString( "type" ); + + auto meshgenerator = StructuredMeshGenerator( util::Config( "patch_pole", false )( "triangulate", true ) ); + Mesh mesh = meshgenerator( grid() ); + auto reorder = mesh::actions::Reorder{reorder_config}; + + if ( expected.size() ) { + const auto node_order = reorder.get()->computeNodesOrder( mesh ); + EXPECT( node_order == expected ); + } + + if ( false ) { // output node_order to feed in to expected + const auto _node_order = reorder.get()->computeNodesOrder( mesh ); + Log::info() << "{"; + for ( idx_t i = 0; i < _node_order.size(); ++i ) { + Log::info() << _node_order[i] << ","; + } + Log::info() << "}"; + } + + reorder( mesh ); + + mesh::actions::build_edges( mesh ); + + auto xy = array::make_view( mesh.nodes().xy() ); + outputConnectedCoordinates( type + "_nodes.py", xy ); + output::Gmsh gmsh{type + ".msh", util::Config( "coordinates", "xy" )}; + gmsh.write( mesh ); + + Field cell_centres = create_cell_centres( mesh ); + auto cell_centres_xy = array::make_view( cell_centres ); + outputConnectedCoordinates( type + "_elements.py", cell_centres_xy ); + + Field edge_centres = create_edge_centres( mesh ); + auto edge_centres_xy = array::make_view( edge_centres ); + outputConnectedCoordinates( type + "_edges.py", edge_centres_xy ); + + std::vector n2n_triplets; + std::vector e2n_triplets; + const auto& node_connectivity = mesh.edges().node_connectivity(); + const idx_t nb_edges = mesh.edges().size(); + n2n_triplets.reserve( 2 * nb_edges ); + for ( idx_t e = 0; e < nb_edges; ++e ) { + n2n_triplets.emplace_back( node_connectivity( e, 0 ), node_connectivity( e, 1 ), 1. ); + n2n_triplets.emplace_back( node_connectivity( e, 1 ), node_connectivity( e, 0 ), 1. ); + e2n_triplets.emplace_back( e, node_connectivity( e, 0 ), 1. ); + e2n_triplets.emplace_back( e, node_connectivity( e, 1 ), 1. ); + } + std::sort( n2n_triplets.begin(), n2n_triplets.end() ); + outputTriplets( type + "_n2n_triplets.py", n2n_triplets, mesh.nodes().size(), mesh.nodes().size() ); + outputTriplets( type + "_e2n_triplets.py", e2n_triplets, mesh.edges().size(), mesh.nodes().size() ); +} + +CASE( "test_hilbert_reordering" ) { + auto reorder_config = option::type( "hilbert" ) | util::Config( "recursion", 30 ); + + std::vector expected; + if ( grid().name() == "O16" && mpi::comm().size() == 1 ) { + // clang-format off + expected = {0,20,1,21,45,74,73,44,72,104,105,141,140,180,224,225,181,182,226,227,142,106,107,143,183,228,184,229,230,185,145,144,108,76,47,75,46,22,2,23,3,77,48,78,49,24,4,25,5,26,51,80,79,50,111,148,112,113,150,149,190,191,236,235,234,189,233,232,187,188,147,110,146,109,186,231,279,280,332,331,388,448,449,450,389,390,451,452,391,334,333,281,282,335,336,283,284,285,338,337,394,395,455,456,454,453,393,392,517,586,587,518,519,520,521,589,590,588,661,662,663,739,740,738,737,660,659,735,736,734,733,656,657,658,585,516,515,584,583,514,513,581,582,654,655,732,731,730,729,653,652,728,727,725,726,649,650,651,578,577,509,510,511,579,580,512,447,446,386,387,278,277,330,329,276,328,384,385,445,444,443,442,382,383,327,275,274,326,325,273,272,324,380,381,441,440,504,505,572,573,574,506,507,508,576,575,647,648,724,723,722,646,645,644,720,721,800,801,802,882,881,880,957,956,1028,1029,1030,958,959,1031,1032,960,884,883,803,804,805,806,885,886,887,807,808,810,809,889,888,963,965,964,1036,1035,1034,962,961,1033,1101,1100,1164,1165,1102,1103,1104,1167,1166,1226,1227,1282,1281,1225,1224,1280,1279,1278,1222,1223,1163,1099,1098,1162,1161,1097,1096,1160,1220,1221,1277,1276,1328,1376,1377,1329,1330,1331,1378,1422,1462,1461,1421,1420,1460,1496,1497,1528,1556,1580,1557,1581,1529,1498,1499,1530,1558,1582,1559,1531,1500,1464,1425,1424,1463,1423,1380,1379,1332,1333,1381,1334,1382,1335,1336,1337,1384,1383,1427,1466,1426,1465,1501,1532,1583,1560,1533,1502,1503,1534,1584,1561,1585,1562,1535,1504,1469,1430,1429,1468,1467,1428,1385,1338,1339,1386,1387,1340,1289,1288,1233,1234,1175,1112,1111,1110,1174,1173,1109,1108,1172,1231,1232,1287,1286,1285,1230,1229,1284,1283,1228,1169,1168,1105,1106,1107,1170,1171,1040,969,968,1039,1038,1037,966,967,891,890,811,812,813,892,893,894,815,814,816,817,896,895,970,1041,1042,971,972,1043,1044,1045,974,973,899,898,897,818,819,820,821,822,901,900,975,1046,1047,976,977,1048,1049,978,903,902,823,824,825,826,904,905,906,827,828,830,829,908,907,981,983,982,1053,1052,1051,980,979,1050,1117,1116,1179,1180,1118,1119,1120,1182,1181,1240,1241,1295,1294,1239,1238,1293,1292,1291,1236,1237,1178,1115,1114,1177,1176,1113,1235,1290,1388,1341,1342,1343,1389,1432,1471,1470,1431,1505,1563,1586,1536,1506,1507,1537,1564,1587,1565,1538,1508,1473,1435,1434,1472,1433,1391,1390,1344,1345,1392,1346,1393,1347,1348,1349,1395,1394,1437,1475,1436,1474,1509,1539,1588,1566,1540,1510,1511,1541,1589,1567,1439,1477,1476,1438,1396,1350,1351,1397,1301,1247,1127,1126,1189,1188,1125,1124,1187,1245,1246,1300,1299,1298,1244,1243,1297,1296,1242,1184,1183,1121,1122,1123,1185,1186,1057,987,986,1056,1055,1054,984,985,910,909,831,832,833,911,912,913,835,834,836,837,915,914,988,1058,1059,989,990,1060,1061,991,917,916,838,839,759,681,680,758,757,755,756,678,679,605,604,534,535,536,606,607,537,471,470,409,297,351,350,296,295,349,407,408,469,468,467,466,405,406,348,294,347,346,293,292,345,403,404,465,464,530,599,600,601,531,532,533,603,602,675,676,677,754,753,752,674,673,750,751,749,748,671,672,598,529,528,597,596,527,526,595,668,669,670,747,746,745,744,667,666,743,742,741,664,665,592,591,522,523,524,593,594,525,459,460,398,397,458,457,396,339,286,287,340,341,288,289,343,342,399,461,462,400,401,463,402,344,290,291,242,241,196,155,117,154,116,153,194,195,240,239,193,238,237,192,151,114,152,115,82,52,81,27,6,28,53,83,54,84,29,7,8,30,56,86,85,55,118,156,197,243,198,244,245,199,157,119,120,158,246,200,201,247,159,121,87,57,31,9,10,32,11,33,59,90,89,58,88,122,123,161,160,202,248,249,203,204,250,251,162,124,125,163,205,252,206,253,254,207,165,164,126,92,61,91,60,34,12,35,13,93,62,94,63,36,14,37,15,38,65,96,95,64,129,168,130,131,170,169,212,213,260,259,258,211,257,256,209,210,167,128,166,127,208,255,305,306,360,359,418,480,481,482,419,420,483,484,421,362,361,307,308,363,364,309,310,311,366,365,424,425,487,488,486,485,423,422,551,622,623,552,553,554,555,625,626,624,699,700,701,779,780,778,777,698,697,775,776,774,773,694,695,696,621,550,549,620,619,548,547,617,618,692,693,772,771,770,769,691,690,768,767,765,766,687,688,689,614,613,543,544,545,615,616,546,479,478,416,417,304,303,358,357,302,356,414,415,477,476,475,474,412,413,355,301,300,354,353,299,298,352,410,411,473,472,538,539,608,609,610,540,541,542,612,611,685,686,764,763,762,684,683,682,760,761,840,841,842,920,919,918,993,992,1062,1063,1064,994,995,1065,1066,996,922,921,843,844,845,846,923,924,925,847,848,850,849,927,926,999,1001,1000,1070,1069,1068,998,997,1067,1133,1132,1194,1195,1134,1135,1136,1197,1196,1254,1255,1308,1307,1253,1252,1306,1305,1304,1250,1251,1193,1131,1130,1192,1191,1129,1128,1190,1248,1249,1303,1302,1352,1398,1399,1353,1354,1355,1400,1442,1480,1479,1441,1440,1478,1512,1513,1542,1568,1590,1569,1591,1543,1514,1515,1544,1570,1592,1571,1545,1516,1482,1445,1444,1481,1443,1402,1401,1356,1357,1403,1358,1404,1359,1360,1361,1406,1405,1447,1484,1446,1483,1517,1546,1593,1572,1547,1518,1519,1548,1594,1573,1595,1574,1549,1520,1487,1450,1449,1486,1485,1448,1407,1362,1363,1408,1409,1364,1315,1314,1261,1262,1205,1144,1143,1142,1204,1203,1141,1140,1202,1259,1260,1313,1312,1311,1258,1257,1310,1309,1256,1199,1198,1137,1138,1139,1200,1201,1074,1005,1004,1073,1072,1071,1002,1003,929,928,851,852,853,930,931,932,855,854,856,857,934,933,1006,1075,1076,1007,1008,1077,1078,1079,1010,1009,937,936,935,858,859,860,861,862,939,938,1011,1080,1081,1012,1013,1082,1083,1014,941,940,863,864,865,866,942,943,944,867,868,870,869,946,945,1017,1019,1018,1087,1086,1085,1016,1015,1084,1149,1148,1209,1210,1150,1151,1152,1212,1211,1268,1269,1321,1320,1267,1266,1319,1318,1317,1264,1265,1208,1147,1146,1207,1206,1145,1263,1316,1410,1365,1366,1367,1411,1452,1489,1488,1451,1521,1575,1596,1550,1522,1523,1551,1576,1597,1577,1552,1524,1491,1455,1454,1490,1453,1413,1412,1368,1369,1414,1370,1415,1371,1372,1373,1417,1416,1457,1493,1456,1492,1525,1553,1598,1578,1554,1526,1527,1555,1599,1579,1459,1495,1494,1458,1418,1374,1375,1419,1327,1275,1159,1158,1219,1218,1157,1156,1217,1273,1274,1326,1325,1324,1272,1271,1323,1322,1270,1214,1213,1153,1154,1155,1215,1216,1091,1023,1022,1090,1089,1088,1020,1021,948,947,871,872,873,949,950,951,875,874,876,877,953,952,1024,1092,1093,1025,1026,1094,1095,1027,955,954,878,879,799,719,718,798,797,795,796,716,717,641,640,568,569,570,642,643,571,503,502,439,323,379,378,322,321,377,437,438,501,500,499,498,435,436,376,320,375,374,319,318,373,433,434,497,496,564,635,636,637,565,566,567,639,638,713,714,715,794,793,792,712,711,790,791,789,788,709,710,634,563,562,633,632,561,560,631,706,707,708,787,786,785,784,705,704,783,782,781,702,703,628,627,556,557,558,629,630,559,491,492,428,427,490,489,426,367,312,313,368,369,314,315,371,370,429,493,494,430,431,495,432,372,316,317,266,265,218,175,135,174,134,173,216,217,264,263,215,262,261,214,171,132,172,133,98,66,97,39,16,40,67,99,68,100,41,17,18,42,70,102,101,69,136,176,219,267,220,268,269,221,177,137,138,178,270,222,223,271,179,139,103,71,43,19,1600,1601,1602,1603,1604,1605,1606,1607,1608,1609,1610,1611,1612,1613,1614,1615,1616,1617,1618,1619,1620,1621,1622,1623,1624,1625,1626,1627,1628,1629,1630,1631}; + // clang-format on + } else { + Log::warning() << "No ordering will be tested" << std::endl; + } + test_reordering( reorder_config, expected ); +} + +CASE( "test_reverse_cuthill_mckee_reordering" ) { + auto reorder_config = option::type( "reverse_cuthill_mckee" ); + + std::vector expected; + if ( grid().name() == "O16" && mpi::comm().size() == 1 ) { + // clang-format off + expected = {1579,1599,1555,1554,1578,1598,1527,1526,1525,1553,1577,1597,1495,1494,1493,1492,1524,1552,1576,1596,1459,1458,1457,1456,1455,1491,1523,1551,1575,1595,1419,1418,1417,1416,1415,1414,1454,1490,1522,1550,1574,1573,1594,1375,1374,1373,1372,1371,1370,1369,1413,1453,1489,1521,1549,1548,1547,1572,1593,1327,1326,1325,1324,1323,1322,1321,1320,1368,1412,1452,1488,1520,1519,1518,1517,1546,1571,1592,1275,1274,1273,1272,1271,1270,1269,1268,1267,1319,1367,1411,1451,1487,1486,1485,1484,1483,1516,1545,1570,1591,1219,1218,1217,1216,1215,1214,1213,1212,1211,1210,1266,1318,1366,1410,1450,1449,1448,1447,1446,1445,1482,1515,1544,1569,1590,1159,1095,1158,1157,1156,1155,1154,1153,1152,1151,1150,1149,1209,1148,1265,1317,1365,1409,1408,1407,1406,1405,1404,1589,1403,1444,1481,1514,1543,1568,1567,1027,955,799,879,1094,1026,1093,1092,1091,1090,1089,1088,1087,1086,1085,1084,1083,1208,1147,1264,1316,1364,1363,1362,1361,1360,1359,1358,1566,1588,1357,1402,1443,1480,1513,1542,1541,1540,878,954,719,798,1082,1025,953,1024,1023,1022,1021,1020,1019,1018,1017,1016,1015,1014,1013,1207,1146,1263,1315,1314,1313,1312,1311,1310,1309,1308,1539,1565,1587,1307,1356,1401,1442,1479,1512,1511,1510,1509,877,797,643,718,1081,1012,876,952,951,950,949,948,947,946,945,944,943,942,941,940,939,1206,1145,1262,1261,1260,1259,1258,1257,1256,1255,1254,1508,1538,1564,1586,1253,1306,1355,1400,1441,1478,1477,1476,1475,1474,796,717,571,642,1080,1011,938,874,875,872,873,870,871,868,869,866,867,864,865,862,863,861,1205,1144,1204,1203,1202,1201,1200,1199,1198,1197,1196,1473,1507,1537,1563,1585,1584,1583,1582,1580,1581,1195,1252,1305,1354,1399,1440,1439,1438,1437,1436,1435,795,716,641,503,570,1079,1010,860,937,794,793,792,791,790,789,788,787,786,785,784,783,782,780,781,1143,1078,1142,1141,1140,1139,1138,1137,1136,1135,1134,1133,1434,1472,1506,1536,1562,1561,1560,1559,1558,1556,1557,1194,1132,1251,1304,1353,1398,1397,1396,1395,1394,1393,1392,715,640,569,439,502,1009,936,859,779,714,713,712,711,710,709,708,707,706,705,704,703,702,701,1077,1008,1076,1075,1074,1073,1072,1071,1070,1069,1068,1067,1066,1391,1433,1471,1505,1535,1534,1533,1532,1531,1530,1528,1529,1193,1131,1250,1303,1352,1351,1350,1349,1348,1347,1346,1345,639,568,501,379,438,858,935,778,700,638,637,636,635,634,633,632,631,630,629,628,627,626,1065,1007,934,1006,1005,1004,1003,1002,1001,1000,999,998,997,996,995,1344,1390,1432,1470,1504,1503,1502,1501,1500,1499,1498,1496,1497,1192,1130,1249,1302,1301,1300,1299,1298,1297,1296,1295,1294,567,500,437,323,378,857,777,699,625,566,565,564,563,562,561,560,559,558,557,556,555,1064,994,856,933,932,931,930,929,928,927,926,925,924,923,922,921,920,1293,1343,1389,1431,1469,1468,1467,1466,1465,1464,1463,1462,1460,1461,1191,1129,1248,1247,1246,1245,1244,1243,1242,1241,1240,1239,499,436,377,271,322,776,698,624,554,498,497,496,495,494,493,492,491,490,489,488,1063,993,919,854,855,852,853,850,851,848,849,846,847,844,845,842,843,841,1238,1292,1342,1388,1430,1429,1428,1427,1426,1425,1424,1423,1422,1420,1421,1190,1128,1189,1188,1187,1186,1185,1184,1183,1182,1181,1180,435,376,321,223,270,775,697,623,553,487,434,433,432,431,430,429,428,427,426,425,1062,992,840,918,774,773,772,771,770,769,768,767,766,765,764,763,762,760,761,1179,1237,1291,1341,1387,1386,1385,1384,1383,1382,1381,1380,1379,1378,1376,1377,1127,1061,1126,1125,1124,1123,1122,1121,1120,1119,1118,1117,1116,375,320,269,179,222,696,622,552,486,424,374,373,372,371,370,369,368,367,366,991,917,839,759,695,694,693,692,691,690,689,688,687,686,685,684,683,682,1178,1236,1290,1340,1339,1338,1337,1336,1335,1334,1333,1332,1331,1330,1328,1329,1115,1060,990,1059,1058,1057,1056,1055,1054,1053,1052,1051,1050,1049,319,268,221,139,178,621,551,485,423,365,318,317,316,315,314,313,312,311,838,916,758,681,620,619,618,617,616,615,614,613,612,611,610,609,608,1177,1235,1289,1288,1287,1286,1285,1284,1283,1282,1281,1280,1279,1278,1276,1277,1048,1114,989,915,988,987,986,985,984,983,982,981,980,979,978,977,267,220,177,103,138,550,484,422,364,310,266,265,264,263,262,261,260,837,757,680,607,549,548,547,546,545,544,543,542,541,540,539,538,1176,1234,1233,1232,1231,1230,1229,1228,1227,1226,1225,1224,1223,1222,1220,1221,1047,976,1113,836,914,913,912,911,910,909,908,907,906,905,904,903,902,901,219,176,137,71,102,483,421,363,309,259,218,217,216,215,214,213,756,679,606,537,482,481,480,479,478,477,476,475,474,473,472,1175,1174,1173,1172,1171,1170,1169,1168,1167,1166,1165,1164,1163,1162,1160,1161,1046,975,1112,900,834,835,832,833,830,831,828,829,826,827,824,825,822,823,821,175,136,101,43,70,420,362,308,258,212,174,173,172,171,170,755,678,605,536,471,419,418,417,416,415,414,413,412,411,410,1111,1110,1109,1108,1107,1106,1105,1104,1103,1102,1101,1100,1099,1098,1096,1097,1045,974,1044,820,899,754,753,752,751,750,749,748,747,746,745,744,743,742,740,741,19,135,100,69,42,361,307,257,211,169,134,133,132,131,677,604,535,470,409,360,359,358,357,356,355,354,353,352,1043,1042,1041,1040,1039,1038,1037,1036,1035,1034,1033,1032,1031,1030,1028,1029,973,898,972,819,739,676,675,674,673,672,671,670,669,668,667,666,665,664,663,18,99,68,41,306,256,210,168,130,98,97,96,603,534,469,408,351,305,304,303,302,301,300,299,298,971,970,969,968,967,966,965,964,963,962,961,960,956,959,958,957,818,897,896,738,662,602,601,600,599,598,597,596,595,594,593,592,591,590,17,67,40,255,209,167,129,95,66,65,533,468,407,350,297,254,253,252,251,250,249,248,895,894,893,892,891,890,889,888,887,886,885,884,880,881,883,882,816,817,737,661,589,532,531,530,529,528,527,526,525,524,523,522,521,800,16,39,208,166,128,94,64,38,467,406,349,296,247,207,206,205,204,203,202,814,815,812,813,810,811,808,809,806,807,804,805,801,802,803,736,660,588,520,466,465,464,463,462,461,460,459,458,457,456,720,15,165,127,93,63,37,405,348,295,246,201,164,163,162,161,160,735,734,733,732,731,730,729,728,727,726,725,724,723,721,722,659,587,519,455,404,403,402,401,400,399,398,397,396,395,644,14,126,92,62,36,347,294,245,200,159,125,124,123,122,658,657,656,655,654,653,652,651,650,649,648,647,646,645,586,518,454,394,346,345,344,343,342,341,340,339,338,572,13,91,61,35,293,244,199,158,121,90,89,88,585,584,583,582,581,580,579,578,577,576,575,574,573,517,453,393,337,292,291,290,289,288,287,286,285,504,12,60,34,243,198,157,120,87,59,58,516,515,514,513,512,511,510,509,508,507,506,505,452,392,336,284,242,241,240,239,238,237,236,440,11,33,197,156,119,86,57,32,451,450,449,448,447,446,445,444,443,442,441,391,335,283,235,196,195,194,193,192,191,380,10,155,118,85,56,31,390,389,388,387,386,385,384,383,382,381,334,282,234,190,154,153,152,151,150,324,9,117,84,55,30,333,332,331,330,329,328,327,326,325,281,233,189,149,116,115,114,113,272,8,83,54,29,280,279,278,277,276,275,274,273,232,188,148,112,82,81,80,224,7,53,28,231,230,229,228,227,226,225,187,147,111,79,52,51,180,6,27,186,185,184,183,182,181,146,110,78,50,26,140,5,145,144,143,142,141,109,77,49,25,104,4,108,107,106,105,76,48,24,72,3,75,74,73,47,23,44,2,46,45,22,20,1,21,0,1631,1630,1629,1628,1627,1626,1625,1624,1623,1622,1621,1620,1619,1618,1617,1616,1615,1614,1613,1612,1611,1610,1609,1608,1607,1606,1605,1604,1603,1602,1601,1600}; + // clang-format on + } + else { + Log::warning() << "No ordering will be tested" << std::endl; + } + test_reordering( reorder_config, expected ); +} + +CASE( "test_none_reordering" ) { + auto reorder_config = option::type( "none" ); + test_reordering( reorder_config ); +} + + +//----------------------------------------------------------------------------- + +} // namespace test +} // namespace atlas + +int main( int argc, char** argv ) { + return atlas::test::run( argc, argv ); +} From 0195936ab02ce7e1f0c27c540b55719884a4d69f Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 16 Sep 2019 11:06:13 +0100 Subject: [PATCH 15/40] ATLAS-245 Allow edges to be sorted by lowest node index upon creation --- src/apps/atlas-benchmark.cc | 7 ++++++- src/atlas/mesh/actions/BuildEdges.cc | 5 ++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/apps/atlas-benchmark.cc b/src/apps/atlas-benchmark.cc index d0b5a1028..27d1336c2 100644 --- a/src/apps/atlas-benchmark.cc +++ b/src/apps/atlas-benchmark.cc @@ -140,6 +140,7 @@ class AtlasBenchmark : public AtlasTool { add_option( new SimpleOption( "exclude", "Exclude number of iterations in statistics (default=1)" ) ); add_option( new SimpleOption( "details", "Show detailed timers (default=false)" ) ); add_option( new SimpleOption( "reorder", "Reorder mesh (default=none)" ) ); + add_option( new SimpleOption( "sort_edges", "Sort edges by lowest node local index" ) ); } void setup(); @@ -172,6 +173,7 @@ class AtlasBenchmark : public AtlasTool { double dz; std::string gridname; std::string reorder{"none"}; + bool sort_edges{false}; TimerStats iteration_timer; TimerStats haloexchange_timer; @@ -200,6 +202,7 @@ int AtlasBenchmark::execute( const Args& args ) { output = false; args.get( "output", output ); args.get( "reorder", reorder ); + args.get( "sort_edges", sort_edges ); bool help( false ); args.get( "help", help ); @@ -315,7 +318,9 @@ void AtlasBenchmark::setup() { mesh::actions::Reorder{option::type( reorder )}( mesh ); ATLAS_TRACE_SCOPE( "Create node_fs" ) { nodes_fs = functionspace::NodeColumns( mesh, option::halo( halo ) ); } - ATLAS_TRACE_SCOPE( "Create edges_fs" ) { edges_fs = functionspace::EdgeColumns( mesh, option::halo( halo ) ); } + ATLAS_TRACE_SCOPE( "Create edges_fs" ) { + edges_fs = functionspace::EdgeColumns( mesh, option::halo( halo ) | util::Config( "sort_edges", sort_edges ) ); + } // mesh.polygon(0).outputPythonScript("plot_polygon.py"); // atlas::output::Output gmsh = atlas::output::Gmsh( "edges.msh", diff --git a/src/atlas/mesh/actions/BuildEdges.cc b/src/atlas/mesh/actions/BuildEdges.cc index ea2313bf6..265c84a31 100644 --- a/src/atlas/mesh/actions/BuildEdges.cc +++ b/src/atlas/mesh/actions/BuildEdges.cc @@ -424,6 +424,9 @@ void build_edges( Mesh& mesh, const eckit::Configuration& config ) { } config.get( "pole_edges", pole_edges ); + bool sort_edges{false}; + config.get( "sort_edges", sort_edges ); + mesh::Nodes& nodes = mesh.nodes(); auto node_part = array::make_view( nodes.partition() ); @@ -458,7 +461,7 @@ void build_edges( Mesh& mesh, const eckit::Configuration& config ) { edge_start = edge_end; edge_end += ( edge_halo_offsets[halo + 1] - edge_halo_offsets[halo] ); - if ( /*sort edges based on lowest node local index =*/false ) { + if ( /*sort edges based on lowest node local index = */ sort_edges ) { if ( sorted_edge_nodes_data.empty() ) { sorted_edge_nodes_data.resize( edge_nodes_data.size() ); } From 363b9ce37dd54e2297fd5743551258f197345978 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 16 Sep 2019 11:22:08 +0100 Subject: [PATCH 16/40] clang-tidy: modernize-use-equals-default --- src/atlas/field/State.cc | 5 +---- src/atlas/grid/detail/spacing/SpacingFactory.cc | 7 +------ src/atlas/mesh/Connectivity.cc | 4 +--- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/atlas/field/State.cc b/src/atlas/field/State.cc index 0fb48e395..4a167fc75 100644 --- a/src/atlas/field/State.cc +++ b/src/atlas/field/State.cc @@ -44,10 +44,7 @@ void load_builder() { } struct force_link { - force_link() { - // load_builder< A DERIVED TYPE >(); - // ... - } + force_link() = default; }; } // namespace diff --git a/src/atlas/grid/detail/spacing/SpacingFactory.cc b/src/atlas/grid/detail/spacing/SpacingFactory.cc index a50a0d2c3..a849f47cc 100644 --- a/src/atlas/grid/detail/spacing/SpacingFactory.cc +++ b/src/atlas/grid/detail/spacing/SpacingFactory.cc @@ -19,12 +19,7 @@ namespace spacing { //---------------------------------------------------------------------------------------------------------------------- void force_link() { - static struct Link { - Link() { - //SpacingBuilder(); - //SpacingBuilder(); - } - } link; + static struct Link { Link() = default; } link; } //---------------------------------------------------------------------------------------------------------------------- diff --git a/src/atlas/mesh/Connectivity.cc b/src/atlas/mesh/Connectivity.cc index fbdc8d3e5..7514e1069 100644 --- a/src/atlas/mesh/Connectivity.cc +++ b/src/atlas/mesh/Connectivity.cc @@ -710,9 +710,7 @@ BlockConnectivityImpl::BlockConnectivityImpl( idx_t rows, idx_t cols, idx_t valu //------------------------------------------------------------------------------------------------------ -BlockConnectivityImpl::~BlockConnectivityImpl() { - //TODO owns_ not used ? -} +BlockConnectivityImpl::~BlockConnectivityImpl() = default; //------------------------------------------------------------------------------------------------------ From 4eea96ecc1847fd333e2fe503b458abb9c7b0cfc Mon Sep 17 00:00:00 2001 From: Pedro Maciel Date: Tue, 27 Aug 2019 17:54:07 +0100 Subject: [PATCH 17/40] ATLAS-246: Proj-based projections --- CMakeLists.txt | 7 + src/atlas/CMakeLists.txt | 12 ++ src/atlas/projection/detail/ProjProjection.cc | 152 +++++++++++++++++ src/atlas/projection/detail/ProjProjection.h | 161 ++++++++++++++++++ src/atlas/projection/detail/ProjectionImpl.cc | 6 + src/atlas/projection/detail/ProjectionImpl.h | 1 + 6 files changed, 339 insertions(+) create mode 100644 src/atlas/projection/detail/ProjProjection.cc create mode 100644 src/atlas/projection/detail/ProjProjection.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f9851bbff..61f542ad7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -200,6 +200,13 @@ ecbuild_add_option( FEATURE EIGEN DESCRIPTION "Use Eigen linear algebra library" REQUIRED_PACKAGES Eigen3 ) +### Proj + +ecbuild_add_option( FEATURE PROJ + DESCRIPTION "PROJ-based projections" + DEFAULT OFF + REQUIRED_PACKAGES PROJ4 ) + ### Type for Global indices and unique point ids set( ATLAS_BITS_GLOBAL 64 ) diff --git a/src/atlas/CMakeLists.txt b/src/atlas/CMakeLists.txt index 7f1b758d7..7c5b20832 100644 --- a/src/atlas/CMakeLists.txt +++ b/src/atlas/CMakeLists.txt @@ -229,6 +229,13 @@ list( APPEND atlas_grid_srcs grid/detail/partitioner/TransPartitioner.cc ) endif() +if( ATLAS_HAVE_PROJ ) +list( APPEND atlas_grid_srcs + projection/detail/ProjProjection.cc + projection/detail/ProjProjection.h +) +endif() + # Append CGAL_COMPLE_FLAGS only to this file ( see ATLAS-193 ) if( CGAL_COMPILE_FLAGS ) @@ -677,6 +684,11 @@ if( ATLAS_HAVE_TRANS ) endif() endif() +if( ATLAS_HAVE_PROJ ) + target_link_libraries( atlas PRIVATE ${PROJ4_LIBRARIES} ) + target_include_directories( atlas PRIVATE ${PROJ4_INCLUDE_DIRS} ) +endif() + if( eckit_VERSION ) set( eckit_link_public PUBLIC ) else() diff --git a/src/atlas/projection/detail/ProjProjection.cc b/src/atlas/projection/detail/ProjProjection.cc new file mode 100644 index 000000000..b00c3829b --- /dev/null +++ b/src/atlas/projection/detail/ProjProjection.cc @@ -0,0 +1,152 @@ +/* + * (C) Copyright 1996- 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 "ProjProjection.h" + +#include + +#include "eckit/types/FloatCompare.h" +#include "eckit/utils/Hash.h" + +#include "atlas/projection/detail/ProjectionFactory.h" +#include "atlas/runtime/Exception.h" +#include "atlas/util/CoordinateEnums.h" +#include "atlas/util/Point.h" + + +namespace atlas { +namespace projection { +namespace detail { + + +ProjProjection::ProjProjection( const eckit::Parametrisation& param ) : + sourceToTarget_( nullptr ), + sourceToGeocentric_( nullptr ), + context_( PJ_DEFAULT_CTX ) { + + ATLAS_ASSERT( param.get( "proj", proj_ ) && !proj_.empty() ); + param.get( "proj_source", source_ = "EPSG:4326" ); // WGS 84 (lat, lon) + param.get( "proj_geocentric", geocentric_ = "EPSG:4978" ); // WGS 84 (x, y, z) + + + // set x/y transformations to/from lon/lat and to/from geocentric coordinates + pj_t p1( proj_create_crs_to_crs( context_, source_.c_str(), proj_.c_str(), nullptr ) ); + ATLAS_ASSERT( p1 ); + sourceToTarget_.reset( proj_normalize_for_visualization( context_, p1 ) ); + ATLAS_ASSERT( sourceToTarget_ ); + + if ( !geocentric_.empty() ) { + pj_t p2( proj_create_crs_to_crs( context_, source_.c_str(), geocentric_.c_str(), nullptr ) ); + ATLAS_ASSERT( p2 ); + sourceToGeocentric_.reset( proj_normalize_for_visualization( context_, p2 ) ); + ATLAS_ASSERT( sourceToGeocentric_ ); + } + + + // set semi-major/minor axis + pj_t source( proj_get_source_crs( context_, sourceToTarget_ ) ); + ATLAS_ASSERT( source ); + + pj_t ellps( proj_get_ellipsoid( context_, source ) ); + if ( ellps ) { + double a; + double b; + ATLAS_ASSERT( proj_ellipsoid_get_parameters( context_, ellps, &a, &b, nullptr, nullptr ) ); + ATLAS_ASSERT( 0 < b && b <= a ); + + eckit::types::is_approximately_equal( a, b ) + ? extraSpec_.set( "radius", a ) + : extraSpec_.set( "semi_major_axis", a ).set( "semi_minor_axis", b ); + } + + + // set units + pj_t target( proj_get_target_crs( context_, sourceToTarget_ ) ); + ATLAS_ASSERT( target ); + + pj_t coord( proj_crs_get_coordinate_system( context_, target ) ); + ATLAS_ASSERT( coord ); + ATLAS_ASSERT( proj_cs_get_axis_count( context_, coord ) > 0 ); + + const char* units_c_str; + if ( proj_cs_get_axis_info( nullptr, coord, 0, nullptr, nullptr, nullptr, nullptr, &units_c_str, nullptr, + nullptr ) ) { + std::string units( units_c_str ); + if ( !units.empty() ) { + extraSpec_.set( "units", units ); + } + } +} + + +void ProjProjection::xy2lonlat( double crd[] ) const { + PJ_COORD P = proj_coord( crd[XX], crd[YY], 0, 0 ); + P = proj_trans( sourceToTarget_, PJ_INV, P ); + + // std::memcpy(crd, &P, 2 * sizeof(double)); + crd[LON] = P.enu.e; + crd[LAT] = P.enu.n; +} + + +void ProjProjection::lonlat2xy( double crd[] ) const { + PJ_COORD P = proj_coord( crd[LON], crd[LAT], 0, 0 ); + P = proj_trans( sourceToTarget_, PJ_FWD, P ); + + // std::memcpy(crd, &P, 2 * sizeof(double)); + crd[XX] = P.xy.x; + crd[YY] = P.xy.y; +} + + +PointXYZ ProjProjection::xyz( const PointLonLat& lonlat ) const { + if ( sourceToGeocentric_ ) { + PJ_COORD P = proj_coord( lonlat.lon(), lonlat.lat(), 0, 0 ); + P = proj_trans( sourceToGeocentric_, PJ_FWD, P ); + return {P.xyz.x, P.xyz.y, P.xyz.z}; + } + return ProjectionImpl::xyz( lonlat ); +} + + +RectangularLonLatDomain ProjProjection::lonlatBoundingBox( const Domain& domain ) const { + return ProjectionImpl::lonlatBoundingBox( domain ); +} + + +ProjProjection::Spec ProjProjection::spec() const { + Spec spec; + spec.set( "type", type() ).set( "proj", proj_ ).set( "proj_source", source_ ).set( "proj_geocentric", geocentric_ ); + return spec | extraSpec_; +} + + +std::string ProjProjection::units() const { + return extraSpec_.getString( "units", "" ); +} + + +void ProjProjection::hash( eckit::Hash& h ) const { + h.add( type() ); + h.add( proj_ ); + h.add( source_ ); + h.add( geocentric_ ); +} + + +namespace { +static ProjectionBuilder register_1( ProjProjection::static_type() ); +} + + +} // namespace detail +} // namespace projection +} // namespace atlas diff --git a/src/atlas/projection/detail/ProjProjection.h b/src/atlas/projection/detail/ProjProjection.h new file mode 100644 index 000000000..ed6f10e60 --- /dev/null +++ b/src/atlas/projection/detail/ProjProjection.h @@ -0,0 +1,161 @@ +/* + * (C) Copyright 1996- 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/projection/detail/ProjectionImpl.h" +#include "atlas/util/Config.h" + + +extern "C" { +using PJ = struct PJconsts; +PJ* proj_destroy( PJ* ); +using PJ_CONTEXT = struct projCtx_t; +PJ_CONTEXT* proj_context_destroy( PJ_CONTEXT* ); +} + + +namespace atlas { +namespace projection { +namespace detail { + + +class ProjProjection final : public ProjectionImpl { +public: + // -- Types + // None + + // -- Exceptions + // None + + // Constructors + + ProjProjection( const eckit::Parametrisation& ); + + // Destructor + // None + + // -- Methods + + static std::string static_type() { return "proj"; } + + // -- Overridden methods + + std::string type() const override { return static_type(); } + + void xy2lonlat( double[] ) const override; + void lonlat2xy( double[] ) const override; + PointXYZ xyz( const PointLonLat& ) const override; + + bool strictlyRegional() const override { return false; } + RectangularLonLatDomain lonlatBoundingBox( const Domain& ) const override; + + Spec spec() const override; + std::string units() const override; + void hash( eckit::Hash& ) const override; + + // -- Class members + // None + + // -- Class methods + // None + + // -- Members + // None + + // -- Methods + // None + + // -- Overridden methods + // None + + // -- Class members + // None + + // -- Class methods + // None + + // -- Friends + // None + +private: + // -- Types + + struct pj_t : std::unique_ptr { + using t = std::unique_ptr; + explicit pj_t( PJ* ptr ) : t( ptr, &proj_destroy ) {} + operator PJ*() const { return t::get(); } + }; + + struct ctx_t : std::unique_ptr { + using t = std::unique_ptr; + explicit ctx_t( PJ_CONTEXT* ptr ) : t( ptr, &proj_context_destroy ) {} + operator PJ_CONTEXT*() const { return t::get(); } + }; + + // -- Contructors + // None + + // -- Destructor + // None + + // -- Convertors + // None + + // -- Operators + // None + + // -- Methods + // None + + // -- Overridden methods + // None + + // -- Class members + // None + + // -- Class methods + // None + + // -- Members + + std::string proj_; + std::string source_; + std::string geocentric_; + + pj_t sourceToTarget_; + pj_t sourceToGeocentric_; + ctx_t context_; + + Spec extraSpec_; + + // -- Methods + // None + + // -- Overridden methods + // None + + // -- Class members + // None + + // -- Class methods + // None + + // -- Friends + // None (how sad) +}; + + +} // namespace detail +} // namespace projection +} // namespace atlas diff --git a/src/atlas/projection/detail/ProjectionImpl.cc b/src/atlas/projection/detail/ProjectionImpl.cc index e987457d7..b82332f3d 100644 --- a/src/atlas/projection/detail/ProjectionImpl.cc +++ b/src/atlas/projection/detail/ProjectionImpl.cc @@ -160,6 +160,12 @@ const ProjectionImpl* ProjectionImpl::create( const eckit::Parametrisation& p ) throw_Exception( "type missing in Params", Here() ); } +PointXYZ ProjectionImpl::xyz( const PointLonLat& lonlat ) const { + atlas::PointXYZ xyz; + atlas::util::Earth::convertSphericalToCartesian( lonlat, xyz ); + return xyz; +} + RectangularLonLatDomain ProjectionImpl::lonlatBoundingBox( const Domain& domain ) const { using eckit::types::is_strictly_greater; diff --git a/src/atlas/projection/detail/ProjectionImpl.h b/src/atlas/projection/detail/ProjectionImpl.h index d967a0f58..745c69d95 100644 --- a/src/atlas/projection/detail/ProjectionImpl.h +++ b/src/atlas/projection/detail/ProjectionImpl.h @@ -51,6 +51,7 @@ class ProjectionImpl : public util::Object { PointLonLat lonlat( const PointXY& ) const; PointXY xy( const PointLonLat& ) const; + virtual PointXYZ xyz(const PointLonLat&) const; virtual bool strictlyRegional() const = 0; virtual RectangularLonLatDomain lonlatBoundingBox( const Domain& ) const = 0; From dd907035dd8583c623383fe2849e1dd189363814 Mon Sep 17 00:00:00 2001 From: Pedro Maciel Date: Tue, 17 Sep 2019 13:58:33 +0100 Subject: [PATCH 18/40] ATLAS-246: Proj-based projections unit test (from Proj examples) --- src/tests/projection/CMakeLists.txt | 5 ++ src/tests/projection/test_proj.cc | 79 +++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 src/tests/projection/test_proj.cc diff --git a/src/tests/projection/CMakeLists.txt b/src/tests/projection/CMakeLists.txt index bca0bf4a7..2d04f94af 100644 --- a/src/tests/projection/CMakeLists.txt +++ b/src/tests/projection/CMakeLists.txt @@ -14,3 +14,8 @@ foreach(test ecbuild_add_test( TARGET atlas_${test} SOURCES ${test}.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endforeach() + +if(ATLAS_HAVE_PROJ) + ecbuild_add_test( TARGET atlas_test_proj SOURCES test_proj.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) +endif() + diff --git a/src/tests/projection/test_proj.cc b/src/tests/projection/test_proj.cc new file mode 100644 index 000000000..3e019b8f4 --- /dev/null +++ b/src/tests/projection/test_proj.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/projection.h" +#include "atlas/util/Config.h" +#include "atlas/util/Point.h" + +#include "tests/AtlasTestEnvironment.h" + + +namespace atlas { +namespace test { + +//----------------------------------------------------------------------------- + +auto check_xy = []( PointXY xy, PointXY ref ) { + double tolerance_micrometers = 1.e-6; + + auto old = Log::info().precision( 16 ); + Log::info() << "Check " << xy << " = (reference) " << ref << std::endl; + Log::info().precision( old ); + + EXPECT( is_approximately_equal( xy.x(), ref.x(), tolerance_micrometers ) ); + EXPECT( is_approximately_equal( xy.y(), ref.y(), tolerance_micrometers ) ); +}; + +auto check_ll = []( PointLonLat ll, PointLonLat ref ) { + double tolerance_microdegrees = 1.e-6; + + auto old = Log::info().precision( 16 ); + Log::info() << "Check " << ll << " = (reference) " << ref << std::endl; + Log::info().precision( old ); + + EXPECT( is_approximately_equal( ll.lon(), ref.lon(), tolerance_microdegrees ) ); + EXPECT( is_approximately_equal( ll.lat(), ref.lat(), tolerance_microdegrees ) ); +}; + +//----------------------------------------------------------------------------- + +CASE( "test_proj_example_string" ) { + Projection projection( util::Config( "type", "proj" ).set( "proj", "+proj=utm +zone=32 +datum=WGS84" ) ); + + PointLonLat a{12, 55}; + check_ll( a, {12, 55} ); + + PointXY b = projection.xy( a ); + check_xy( b, {691875.632137542, 6098907.825129169} ); + check_ll( projection.lonlat( b ), {12, 55} ); +} + +//----------------------------------------------------------------------------- + +CASE( "test_proj_example_EPSG:32632" ) { + Projection projection( util::Config( "type", "proj" ).set( "proj", "EPSG:32632" ) ); + + PointLonLat a{12, 55}; + check_ll( a, {12, 55} ); + + PointXY b = projection.xy( a ); + check_xy( b, {691875.632137542, 6098907.825129169} ); + check_ll( projection.lonlat( b ), {12, 55} ); +} + +//----------------------------------------------------------------------------- + +} // namespace test +} // namespace atlas + +int main( int argc, char** argv ) { + return atlas::test::run( argc, argv ); +} From 7279bf8cc9bc9ecc099a79b6c0c734164ba895bd Mon Sep 17 00:00:00 2001 From: Pedro Maciel Date: Tue, 17 Sep 2019 15:00:55 +0100 Subject: [PATCH 19/40] ATLAS-246: Proj-based projections unit test (from Proj examples) --- src/tests/projection/test_proj.cc | 51 +++++++++---------------------- 1 file changed, 15 insertions(+), 36 deletions(-) diff --git a/src/tests/projection/test_proj.cc b/src/tests/projection/test_proj.cc index 3e019b8f4..77430ed00 100644 --- a/src/tests/projection/test_proj.cc +++ b/src/tests/projection/test_proj.cc @@ -21,52 +21,31 @@ namespace test { //----------------------------------------------------------------------------- -auto check_xy = []( PointXY xy, PointXY ref ) { - double tolerance_micrometers = 1.e-6; +template +void check( const POINT& a, const POINT& b ) { + static constexpr double eps = 1.e-6; auto old = Log::info().precision( 16 ); - Log::info() << "Check " << xy << " = (reference) " << ref << std::endl; + Log::info() << "Check " << a << " = " << b << std::endl; Log::info().precision( old ); - EXPECT( is_approximately_equal( xy.x(), ref.x(), tolerance_micrometers ) ); - EXPECT( is_approximately_equal( xy.y(), ref.y(), tolerance_micrometers ) ); -}; - -auto check_ll = []( PointLonLat ll, PointLonLat ref ) { - double tolerance_microdegrees = 1.e-6; - - auto old = Log::info().precision( 16 ); - Log::info() << "Check " << ll << " = (reference) " << ref << std::endl; - Log::info().precision( old ); - - EXPECT( is_approximately_equal( ll.lon(), ref.lon(), tolerance_microdegrees ) ); - EXPECT( is_approximately_equal( ll.lat(), ref.lat(), tolerance_microdegrees ) ); -}; - -//----------------------------------------------------------------------------- - -CASE( "test_proj_example_string" ) { - Projection projection( util::Config( "type", "proj" ).set( "proj", "+proj=utm +zone=32 +datum=WGS84" ) ); - - PointLonLat a{12, 55}; - check_ll( a, {12, 55} ); - - PointXY b = projection.xy( a ); - check_xy( b, {691875.632137542, 6098907.825129169} ); - check_ll( projection.lonlat( b ), {12, 55} ); + EXPECT( is_approximately_equal( a[0], b[0], eps ) ); + EXPECT( is_approximately_equal( a[1], b[1], eps ) ); } //----------------------------------------------------------------------------- -CASE( "test_proj_example_EPSG:32632" ) { - Projection projection( util::Config( "type", "proj" ).set( "proj", "EPSG:32632" ) ); - +CASE( "test_proj" ) { PointLonLat a{12, 55}; - check_ll( a, {12, 55} ); + check( a, {12, 55} ); + + for ( auto proj : {"+proj=utm +zone=32 +datum=WGS84", "EPSG:32632"} ) { + Projection projection( util::Config( "type", "proj" ).set( "proj", proj ) ); - PointXY b = projection.xy( a ); - check_xy( b, {691875.632137542, 6098907.825129169} ); - check_ll( projection.lonlat( b ), {12, 55} ); + PointXY b = projection.xy( a ); + check( b, {691875.632137542, 6098907.825129169} ); + check( projection.lonlat( b ), {12, 55} ); + } } //----------------------------------------------------------------------------- From afd3076b7e77f5f3fd7d7e5888db88475b694910 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 17 Sep 2019 15:16:19 +0100 Subject: [PATCH 20/40] Remove unused and bugged code in IndexView --- src/atlas/array/gridtools/GridToolsIndexView.h | 10 ---------- src/atlas/array/native/NativeIndexView.h | 8 -------- 2 files changed, 18 deletions(-) diff --git a/src/atlas/array/gridtools/GridToolsIndexView.h b/src/atlas/array/gridtools/GridToolsIndexView.h index d3b398eff..f153bae5c 100644 --- a/src/atlas/array/gridtools/GridToolsIndexView.h +++ b/src/atlas/array/gridtools/GridToolsIndexView.h @@ -47,16 +47,6 @@ class FortranIndex { return *this; } ATLAS_HOST_DEVICE - FortranIndex& operator+( const Value& value ) { - *( idx_ ) += value; - return *this; - } - ATLAS_HOST_DEVICE - FortranIndex& operator-( const Value& value ) { - *( idx_ ) -= value; - return *this; - } - ATLAS_HOST_DEVICE FortranIndex& operator--() { --( *( idx_ ) ); return *this; diff --git a/src/atlas/array/native/NativeIndexView.h b/src/atlas/array/native/NativeIndexView.h index 2b1a68110..aaed2fd6f 100644 --- a/src/atlas/array/native/NativeIndexView.h +++ b/src/atlas/array/native/NativeIndexView.h @@ -70,14 +70,6 @@ class FortranIndex { set( other.get() ); return *this; } - FortranIndex& operator+( const Value& value ) { - *( idx_ ) += value; - return *this; - } - FortranIndex& operator-( const Value& value ) { - *( idx_ ) -= value; - return *this; - } FortranIndex& operator--() { --( *( idx_ ) ); return *this; From 54eb90786d54ac9e411e74d875dd21d2a7f24e90 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 17 Sep 2019 15:17:23 +0100 Subject: [PATCH 21/40] LocalView::shape() function added --- src/atlas/array/LocalView.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/atlas/array/LocalView.h b/src/atlas/array/LocalView.h index abe29e459..d71cef883 100644 --- a/src/atlas/array/LocalView.h +++ b/src/atlas/array/LocalView.h @@ -150,6 +150,8 @@ class LocalView { return strides_[idx]; } + const idx_t* shape() const { return shape_; } + value_type const* data() const { return data_; } return_type* data() { return data_; } From cf44eeeec37c5425d4d2b66faea02c1e1c1dce69 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 17 Sep 2019 15:18:49 +0100 Subject: [PATCH 22/40] Fix EdgeColumns::haloExchange for fields with rank!=2 --- src/atlas/functionspace/EdgeColumns.cc | 53 +++++++++++++++++++------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/src/atlas/functionspace/EdgeColumns.cc b/src/atlas/functionspace/EdgeColumns.cc index d806f4ade..379c0c96e 100644 --- a/src/atlas/functionspace/EdgeColumns.cc +++ b/src/atlas/functionspace/EdgeColumns.cc @@ -318,23 +318,47 @@ Field EdgeColumns::createField( const Field& other, const eckit::Configuration& option::variables( other.variables() ) | config ); } +namespace { + +template +void dispatch_haloExchange( Field& field, const parallel::HaloExchange& halo_exchange, bool on_device ) { + if ( field.datatype() == array::DataType::kind() ) { + halo_exchange.template execute( field.array(), on_device ); + } + else if ( field.datatype() == array::DataType::kind() ) { + halo_exchange.template execute( field.array(), on_device ); + } + else if ( field.datatype() == array::DataType::kind() ) { + halo_exchange.template execute( field.array(), on_device ); + } + else if ( field.datatype() == array::DataType::kind() ) { + halo_exchange.template execute( field.array(), on_device ); + } + else { + throw_Exception( "datatype not supported", Here() ); + } + field.set_dirty( false ); +} +} // namespace + void EdgeColumns::haloExchange( const FieldSet& fieldset, bool on_device ) const { for ( idx_t f = 0; f < fieldset.size(); ++f ) { Field& field = const_cast( fieldset )[f]; - if ( field.datatype() == array::DataType::kind() ) { - halo_exchange().execute( field.array(), on_device ); - } - else if ( field.datatype() == array::DataType::kind() ) { - halo_exchange().execute( field.array(), on_device ); - } - else if ( field.datatype() == array::DataType::kind() ) { - halo_exchange().execute( field.array(), on_device ); - } - else if ( field.datatype() == array::DataType::kind() ) { - halo_exchange().execute( field.array(), on_device ); - } - else { - throw_Exception( "datatype not supported", Here() ); + switch ( field.rank() ) { + case 1: + dispatch_haloExchange<1>( field, halo_exchange(), on_device ); + break; + case 2: + dispatch_haloExchange<2>( field, halo_exchange(), on_device ); + break; + case 3: + dispatch_haloExchange<3>( field, halo_exchange(), on_device ); + break; + case 4: + dispatch_haloExchange<4>( field, halo_exchange(), on_device ); + break; + default: + throw_Exception( "Rank not supported", Here() ); } field.set_dirty( false ); } @@ -344,6 +368,7 @@ void EdgeColumns::haloExchange( const Field& field, bool on_device ) const { fieldset.add( field ); haloExchange( fieldset, on_device ); } + const parallel::HaloExchange& EdgeColumns::halo_exchange() const { if ( halo_exchange_ ) { return *halo_exchange_; From e3e92b7626e2e7cf92ca6533f18908f107d4c951 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 17 Sep 2019 15:26:34 +0100 Subject: [PATCH 23/40] mesh::Elements::indexview() function added --- src/atlas/mesh/Elements.cc | 15 +++++++++++++++ src/atlas/mesh/Elements.h | 7 +++++++ 2 files changed, 22 insertions(+) diff --git a/src/atlas/mesh/Elements.cc b/src/atlas/mesh/Elements.cc index 7306f0fd9..57e1f8aad 100644 --- a/src/atlas/mesh/Elements.cc +++ b/src/atlas/mesh/Elements.cc @@ -164,6 +164,21 @@ idx_t Elements::add( const idx_t nb_elements ) { return position; } + +template <> +array::IndexView Elements::indexview( Field& field ) const { + auto local_view = array::make_host_view( field ).slice( + array::Range{begin(), begin() + size()}, array::Range::all() ); + return array::IndexView( local_view.data(), local_view.shape() ); +} + +template <> +array::IndexView Elements::indexview( const Field& field ) const { + auto local_view = array::make_host_view( field ).slice( + array::Range{begin(), begin() + size()}, array::Range::all() ); + return array::IndexView( local_view.data(), local_view.shape() ); +} + //----------------------------------------------------------------------------- extern "C" { diff --git a/src/atlas/mesh/Elements.h b/src/atlas/mesh/Elements.h index a212039dc..53cf7464c 100644 --- a/src/atlas/mesh/Elements.h +++ b/src/atlas/mesh/Elements.h @@ -17,6 +17,7 @@ #pragma once #include "atlas/array/ArrayView.h" +#include "atlas/array/IndexView.h" #include "atlas/mesh/Connectivity.h" #include "atlas/mesh/HybridElements.h" #include "atlas/util/Object.h" @@ -122,6 +123,12 @@ class Elements : public util::Object { template array::LocalView view( Field& ) const; + template + array::IndexView indexview( const Field& ) const; + + template + array::IndexView indexview( Field& ) const; + idx_t add( const idx_t nb_elements ); private: From 16466b5ee43844c827b5f664263bf47bcd07a17f Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 17 Sep 2019 15:29:35 +0100 Subject: [PATCH 24/40] ATLAS-247 Preliminary support for CellColumns functionspace and Gmsh output --- src/atlas/CMakeLists.txt | 2 + src/atlas/functionspace.h | 1 + src/atlas/functionspace/CellColumns.cc | 840 ++++++++++++++++++ src/atlas/functionspace/CellColumns.h | 187 ++++ src/atlas/mesh/actions/BuildHalo.cc | 31 +- src/atlas/mesh/actions/BuildParallelFields.cc | 99 +++ src/atlas/mesh/actions/BuildParallelFields.h | 2 + src/atlas/output/detail/GmshIO.cc | 94 ++ src/atlas/output/detail/GmshIO.h | 7 + src/tests/functionspace/CMakeLists.txt | 6 + src/tests/functionspace/test_cellcolumns.cc | 117 +++ 11 files changed, 1381 insertions(+), 5 deletions(-) create mode 100644 src/atlas/functionspace/CellColumns.cc create mode 100644 src/atlas/functionspace/CellColumns.h create mode 100644 src/tests/functionspace/test_cellcolumns.cc diff --git a/src/atlas/CMakeLists.txt b/src/atlas/CMakeLists.txt index 7f1b758d7..e11ec2656 100644 --- a/src/atlas/CMakeLists.txt +++ b/src/atlas/CMakeLists.txt @@ -340,6 +340,8 @@ field/detail/FieldInterface.cc list( APPEND atlas_functionspace_srcs functionspace.h +functionspace/CellColumns.h +functionspace/CellColumns.cc functionspace/EdgeColumns.h functionspace/EdgeColumns.cc functionspace/FunctionSpace.h diff --git a/src/atlas/functionspace.h b/src/atlas/functionspace.h index db178dd2b..01de76bc5 100644 --- a/src/atlas/functionspace.h +++ b/src/atlas/functionspace.h @@ -12,6 +12,7 @@ #pragma once +#include "atlas/functionspace/CellColumns.h" #include "atlas/functionspace/EdgeColumns.h" #include "atlas/functionspace/FunctionSpace.h" #include "atlas/functionspace/NodeColumns.h" diff --git a/src/atlas/functionspace/CellColumns.cc b/src/atlas/functionspace/CellColumns.cc new file mode 100644 index 000000000..0e11a0f1f --- /dev/null +++ b/src/atlas/functionspace/CellColumns.cc @@ -0,0 +1,840 @@ +/* + * (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/utils/MD5.h" + +#include "atlas/array/MakeView.h" +#include "atlas/functionspace/CellColumns.h" +#include "atlas/library/config.h" +#include "atlas/mesh/HybridElements.h" +#include "atlas/mesh/IsGhostNode.h" +#include "atlas/mesh/Mesh.h" +#include "atlas/mesh/actions/BuildHalo.h" +#include "atlas/mesh/actions/BuildParallelFields.h" +#include "atlas/mesh/actions/BuildPeriodicBoundaries.h" +#include "atlas/parallel/Checksum.h" +#include "atlas/parallel/GatherScatter.h" +#include "atlas/parallel/HaloExchange.h" +#include "atlas/parallel/omp/omp.h" +#include "atlas/runtime/Exception.h" +#include "atlas/runtime/Log.h" +#include "atlas/runtime/Trace.h" +#include "atlas/util/detail/Cache.h" + +#include "atlas/field/detail/FieldImpl.h" + +#if ATLAS_HAVE_FORTRAN +#define REMOTE_IDX_BASE 1 +#else +#define REMOTE_IDX_BASE 0 +#endif + +namespace atlas { +namespace functionspace { +namespace detail { + +namespace { +template +array::LocalView make_leveled_view( const Field& field ) { + using namespace array; + if ( field.levels() ) { + if ( field.variables() ) { + return make_view( field ).slice( Range::all(), Range::all(), Range::all() ); + } + else { + return make_view( field ).slice( Range::all(), Range::all(), Range::dummy() ); + } + } + else { + if ( field.variables() ) { + return make_view( field ).slice( Range::all(), Range::dummy(), Range::all() ); + } + else { + return make_view( field ).slice( Range::all(), Range::dummy(), Range::dummy() ); + } + } +} +} // namespace + +class CellColumnsHaloExchangeCache : public util::Cache, + public mesh::detail::MeshObserver { +private: + using Base = util::Cache; + CellColumnsHaloExchangeCache() : Base( "CellColumnsHaloExchangeCache" ) {} + +public: + static CellColumnsHaloExchangeCache& instance() { + static CellColumnsHaloExchangeCache inst; + return inst; + } + util::ObjectHandle get_or_create( const Mesh& mesh ) { + creator_type creator = std::bind( &CellColumnsHaloExchangeCache::create, mesh ); + return Base::get_or_create( key( *mesh.get() ), creator ); + } + void onMeshDestruction( mesh::detail::MeshImpl& mesh ) override { remove( key( mesh ) ); } + +private: + static Base::key_type key( const mesh::detail::MeshImpl& mesh ) { + std::ostringstream key; + key << "mesh[address=" << &mesh << "]"; + return key.str(); + } + + static value_type* create( const Mesh& mesh ) { + mesh.get()->attachObserver( instance() ); + value_type* value = new value_type(); + value->setup( array::make_view( mesh.cells().partition() ).data(), + array::make_view( mesh.cells().remote_index() ).data(), REMOTE_IDX_BASE, + mesh.cells().size() ); + return value; + } +}; + +class CellColumnsGatherScatterCache : public util::Cache, + public mesh::detail::MeshObserver { +private: + using Base = util::Cache; + CellColumnsGatherScatterCache() : Base( "CellColumnsGatherScatterCache" ) {} + +public: + static CellColumnsGatherScatterCache& instance() { + static CellColumnsGatherScatterCache inst; + return inst; + } + util::ObjectHandle get_or_create( const Mesh& mesh ) { + creator_type creator = std::bind( &CellColumnsGatherScatterCache::create, mesh ); + return Base::get_or_create( key( *mesh.get() ), creator ); + } + void onMeshDestruction( mesh::detail::MeshImpl& mesh ) override { remove( key( mesh ) ); } + +private: + static Base::key_type key( const mesh::detail::MeshImpl& mesh ) { + std::ostringstream key; + key << "mesh[address=" << &mesh << "]"; + return key.str(); + } + + static value_type* create( const Mesh& mesh ) { + mesh.get()->attachObserver( instance() ); + value_type* value = new value_type(); + value->setup( array::make_view( mesh.cells().partition() ).data(), + array::make_view( mesh.cells().remote_index() ).data(), REMOTE_IDX_BASE, + array::make_view( mesh.cells().global_index() ).data(), mesh.cells().size() ); + return value; + } +}; + +class CellColumnsChecksumCache : public util::Cache, + public mesh::detail::MeshObserver { +private: + using Base = util::Cache; + CellColumnsChecksumCache() : Base( "CellColumnsChecksumCache" ) {} + +public: + static CellColumnsChecksumCache& instance() { + static CellColumnsChecksumCache inst; + return inst; + } + util::ObjectHandle get_or_create( const Mesh& mesh ) { + creator_type creator = std::bind( &CellColumnsChecksumCache::create, mesh ); + return Base::get_or_create( key( *mesh.get() ), creator ); + } + void onMeshDestruction( mesh::detail::MeshImpl& mesh ) override { remove( key( mesh ) ); } + +private: + static Base::key_type key( const mesh::detail::MeshImpl& mesh ) { + std::ostringstream key; + key << "mesh[address=" << &mesh << "]"; + return key.str(); + } + + static value_type* create( const Mesh& mesh ) { + mesh.get()->attachObserver( instance() ); + value_type* value = new value_type(); + util::ObjectHandle gather( + CellColumnsGatherScatterCache::instance().get_or_create( mesh ) ); + value->setup( gather ); + return value; + } +}; + +void CellColumns::set_field_metadata( const eckit::Configuration& config, Field& field ) const { + field.set_functionspace( this ); + + bool global( false ); + if ( config.get( "global", global ) ) { + if ( global ) { + idx_t owner( 0 ); + config.get( "owner", owner ); + field.metadata().set( "owner", owner ); + } + } + field.metadata().set( "global", global ); + + idx_t levels( nb_levels_ ); + config.get( "levels", levels ); + field.set_levels( levels ); + + idx_t variables( 0 ); + config.get( "variables", variables ); + field.set_variables( variables ); +} + +idx_t CellColumns::config_size( const eckit::Configuration& config ) const { + const idx_t rank = static_cast( mpi::comm().rank() ); + idx_t size = nb_cells(); + bool global( false ); + if ( config.get( "global", global ) ) { + if ( global ) { + idx_t owner( 0 ); + config.get( "owner", owner ); + idx_t _nb_cells_global( nb_cells_global() ); + size = ( rank == owner ? _nb_cells_global : 0 ); + } + } + return size; +} + +array::DataType CellColumns::config_datatype( const eckit::Configuration& config ) const { + array::DataType::kind_t kind; + if ( !config.get( "datatype", kind ) ) { + throw_Exception( "datatype missing", Here() ); + } + return array::DataType( kind ); +} + +std::string CellColumns::config_name( const eckit::Configuration& config ) const { + std::string name; + config.get( "name", name ); + return name; +} + +idx_t CellColumns::config_levels( const eckit::Configuration& config ) const { + idx_t levels( nb_levels_ ); + config.get( "levels", levels ); + return levels; +} + +array::ArrayShape CellColumns::config_shape( const eckit::Configuration& config ) const { + array::ArrayShape shape; + + shape.push_back( config_size( config ) ); + + idx_t levels( nb_levels_ ); + config.get( "levels", levels ); + if ( levels > 0 ) { + shape.push_back( levels ); + } + + idx_t variables( 0 ); + config.get( "variables", variables ); + if ( variables > 0 ) { + shape.push_back( variables ); + } + + return shape; +} + +CellColumns::CellColumns( const Mesh& mesh, const eckit::Configuration& config ) : + mesh_( mesh ), + cells_( mesh_.cells() ), + nb_levels_( config.getInt( "levels", 0 ) ), + nb_cells_( 0 ) { + ATLAS_TRACE(); + if ( config.has( "halo" ) ) { + halo_ = mesh::Halo( config.getInt( "halo" ) ); + } + else { + halo_ = mesh::Halo( mesh_ ); + } + + auto get_nb_cells_from_metadata = [&]() { + idx_t nb_cells{0}; + for ( idx_t t = 0; t < cells_.nb_types(); ++t ) { + std::stringstream ss; + ss << "nb_cells_including_halo[" << t << "][" << halo_.size() << "]"; + idx_t nb_cells_for_this_type{0}; + mesh_.metadata().get( ss.str(), nb_cells_for_this_type ); + nb_cells += nb_cells_for_this_type; + } + return nb_cells; + }; + + mesh::actions::build_nodes_parallel_fields( mesh_.nodes() ); + mesh::actions::build_cells_parallel_fields( mesh_ ); + mesh::actions::build_periodic_boundaries( mesh_ ); + + if ( halo_.size() > 0 ) { + mesh::actions::build_halo( mesh_, halo_.size() ); + nb_cells_ = get_nb_cells_from_metadata(); + } + if ( !nb_cells_ ) { + nb_cells_ = mesh.cells().size(); + } + ATLAS_ASSERT( nb_cells_ ); +} + +CellColumns::~CellColumns() = default; + +size_t CellColumns::footprint() const { + size_t size = sizeof( *this ); + // TODO + return size; +} + +std::string CellColumns::distribution() const { + return mesh().metadata().getString( "distribution" ); +} + +idx_t CellColumns::nb_cells() const { + return nb_cells_; +} + +idx_t CellColumns::nb_cells_global() const { + if ( nb_cells_global_ >= 0 ) { + return nb_cells_global_; + } + nb_cells_global_ = gather().glb_dof(); + return nb_cells_global_; +} + +Field CellColumns::createField( const eckit::Configuration& options ) const { + Field field( config_name( options ), config_datatype( options ), config_shape( options ) ); + set_field_metadata( options, field ); + return field; +} + +Field CellColumns::createField( const Field& other, const eckit::Configuration& config ) const { + return createField( option::datatype( other.datatype() ) | option::levels( other.levels() ) | + option::variables( other.variables() ) | config ); +} + + +namespace { + +template +void dispatch_haloExchange( Field& field, const parallel::HaloExchange& halo_exchange, bool on_device ) { + if ( field.datatype() == array::DataType::kind() ) { + halo_exchange.template execute( field.array(), on_device ); + } + else if ( field.datatype() == array::DataType::kind() ) { + halo_exchange.template execute( field.array(), on_device ); + } + else if ( field.datatype() == array::DataType::kind() ) { + halo_exchange.template execute( field.array(), on_device ); + } + else if ( field.datatype() == array::DataType::kind() ) { + halo_exchange.template execute( field.array(), on_device ); + } + else { + throw_Exception( "datatype not supported", Here() ); + } + field.set_dirty( false ); +} +} // namespace + +void CellColumns::haloExchange( const FieldSet& fieldset, bool on_device ) const { + for ( idx_t f = 0; f < fieldset.size(); ++f ) { + Field& field = const_cast( fieldset )[f]; + switch ( field.rank() ) { + case 1: + dispatch_haloExchange<1>( field, halo_exchange(), on_device ); + break; + case 2: + dispatch_haloExchange<2>( field, halo_exchange(), on_device ); + break; + case 3: + dispatch_haloExchange<3>( field, halo_exchange(), on_device ); + break; + case 4: + dispatch_haloExchange<4>( field, halo_exchange(), on_device ); + break; + default: + throw_Exception( "Rank not supported", Here() ); + } + field.set_dirty( false ); + } +} +void CellColumns::haloExchange( const Field& field, bool on_device ) const { + FieldSet fieldset; + fieldset.add( field ); + haloExchange( fieldset, on_device ); +} +const parallel::HaloExchange& CellColumns::halo_exchange() const { + if ( halo_exchange_ ) { + return *halo_exchange_; + } + halo_exchange_ = CellColumnsHaloExchangeCache::instance().get_or_create( mesh_ ); + return *halo_exchange_; +} + +void CellColumns::gather( const FieldSet& local_fieldset, FieldSet& global_fieldset ) const { + ATLAS_ASSERT( local_fieldset.size() == global_fieldset.size() ); + + for ( idx_t f = 0; f < local_fieldset.size(); ++f ) { + const Field& loc = local_fieldset[f]; + Field& glb = global_fieldset[f]; + const idx_t nb_fields = 1; + idx_t root( 0 ); + glb.metadata().get( "owner", root ); + if ( loc.datatype() == array::DataType::kind() ) { + parallel::Field loc_field( make_leveled_view( loc ) ); + parallel::Field glb_field( make_leveled_view( glb ) ); + gather().gather( &loc_field, &glb_field, nb_fields, root ); + } + else if ( loc.datatype() == array::DataType::kind() ) { + parallel::Field loc_field( make_leveled_view( loc ) ); + parallel::Field glb_field( make_leveled_view( glb ) ); + gather().gather( &loc_field, &glb_field, nb_fields, root ); + } + else if ( loc.datatype() == array::DataType::kind() ) { + parallel::Field loc_field( make_leveled_view( loc ) ); + parallel::Field glb_field( make_leveled_view( glb ) ); + gather().gather( &loc_field, &glb_field, nb_fields, root ); + } + else if ( loc.datatype() == array::DataType::kind() ) { + parallel::Field loc_field( make_leveled_view( loc ) ); + parallel::Field glb_field( make_leveled_view( glb ) ); + gather().gather( &loc_field, &glb_field, nb_fields, root ); + } + else { + throw_Exception( "datatype not supported", Here() ); + } + } +} + +void CellColumns::gather( const Field& local, Field& global ) const { + FieldSet local_fields; + FieldSet global_fields; + local_fields.add( local ); + global_fields.add( global ); + gather( local_fields, global_fields ); +} +const parallel::GatherScatter& CellColumns::gather() const { + if ( gather_scatter_ ) { + return *gather_scatter_; + } + gather_scatter_ = CellColumnsGatherScatterCache::instance().get_or_create( mesh_ ); + return *gather_scatter_; +} +const parallel::GatherScatter& CellColumns::scatter() const { + if ( gather_scatter_ ) { + return *gather_scatter_; + } + gather_scatter_ = CellColumnsGatherScatterCache::instance().get_or_create( mesh_ ); + return *gather_scatter_; +} + +void CellColumns::scatter( const FieldSet& global_fieldset, FieldSet& local_fieldset ) const { + ATLAS_ASSERT( local_fieldset.size() == global_fieldset.size() ); + + for ( idx_t f = 0; f < local_fieldset.size(); ++f ) { + const Field& glb = global_fieldset[f]; + Field& loc = local_fieldset[f]; + const idx_t nb_fields = 1; + idx_t root( 0 ); + glb.metadata().get( "owner", root ); + + if ( loc.datatype() == array::DataType::kind() ) { + parallel::Field glb_field( make_leveled_view( glb ) ); + parallel::Field loc_field( make_leveled_view( loc ) ); + scatter().scatter( &glb_field, &loc_field, nb_fields, root ); + } + else if ( loc.datatype() == array::DataType::kind() ) { + parallel::Field glb_field( make_leveled_view( glb ) ); + parallel::Field loc_field( make_leveled_view( loc ) ); + scatter().scatter( &glb_field, &loc_field, nb_fields, root ); + } + else if ( loc.datatype() == array::DataType::kind() ) { + parallel::Field glb_field( make_leveled_view( glb ) ); + parallel::Field loc_field( make_leveled_view( loc ) ); + scatter().scatter( &glb_field, &loc_field, nb_fields, root ); + } + else if ( loc.datatype() == array::DataType::kind() ) { + parallel::Field glb_field( make_leveled_view( glb ) ); + parallel::Field loc_field( make_leveled_view( loc ) ); + scatter().scatter( &glb_field, &loc_field, nb_fields, root ); + } + else { + throw_Exception( "datatype not supported", Here() ); + } + + glb.metadata().broadcast( loc.metadata(), root ); + loc.metadata().set( "global", false ); + } +} +void CellColumns::scatter( const Field& global, Field& local ) const { + FieldSet global_fields; + FieldSet local_fields; + global_fields.add( global ); + local_fields.add( local ); + scatter( global_fields, local_fields ); +} + +namespace { +template +std::string checksum_3d_field( const parallel::Checksum& checksum, const Field& field ) { + array::ArrayView values = array::make_view( field ); + array::ArrayT surface_field( field.shape( 0 ), field.shape( 2 ) ); + array::ArrayView surface = array::make_view( surface_field ); + for ( idx_t n = 0; n < values.shape( 0 ); ++n ) { + for ( idx_t j = 0; j < surface.shape( 1 ); ++j ) { + surface( n, j ) = 0.; + for ( idx_t l = 0; l < values.shape( 1 ); ++l ) { + surface( n, j ) += values( n, l, j ); + } + } + } + return checksum.execute( surface.data(), surface_field.stride( 0 ) ); +} +template +std::string checksum_2d_field( const parallel::Checksum& checksum, const Field& field ) { + array::ArrayView values = array::make_view( field ); + return checksum.execute( values.data(), field.stride( 0 ) ); +} + +} // namespace + +std::string CellColumns::checksum( const FieldSet& fieldset ) const { + eckit::MD5 md5; + for ( idx_t f = 0; f < fieldset.size(); ++f ) { + const Field& field = fieldset[f]; + if ( field.datatype() == array::DataType::kind() ) { + if ( field.levels() ) { + md5 << checksum_3d_field( checksum(), field ); + } + else { + md5 << checksum_2d_field( checksum(), field ); + } + } + else if ( field.datatype() == array::DataType::kind() ) { + if ( field.levels() ) { + md5 << checksum_3d_field( checksum(), field ); + } + else { + md5 << checksum_2d_field( checksum(), field ); + } + } + else if ( field.datatype() == array::DataType::kind() ) { + if ( field.levels() ) { + md5 << checksum_3d_field( checksum(), field ); + } + else { + md5 << checksum_2d_field( checksum(), field ); + } + } + else if ( field.datatype() == array::DataType::kind() ) { + if ( field.levels() ) { + md5 << checksum_3d_field( checksum(), field ); + } + else { + md5 << checksum_2d_field( checksum(), field ); + } + } + else { + throw_Exception( "datatype not supported", Here() ); + } + } + return md5; +} +std::string CellColumns::checksum( const Field& field ) const { + FieldSet fieldset; + fieldset.add( field ); + return checksum( fieldset ); +} + +const parallel::Checksum& CellColumns::checksum() const { + if ( checksum_ ) { + return *checksum_; + } + checksum_ = CellColumnsChecksumCache::instance().get_or_create( mesh_ ); + return *checksum_; +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +extern "C" { +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +CellColumns* atlas__fs__CellColumns__new( Mesh::Implementation* mesh, const eckit::Configuration* config ) { + ATLAS_ASSERT( mesh != nullptr ); + Mesh m( mesh ); + return new CellColumns( m, *config ); +} + +//------------------------------------------------------------------------------ + +void atlas__fs__CellColumns__delete( CellColumns* This ) { + ATLAS_ASSERT( This != nullptr ); + delete ( This ); +} + +//------------------------------------------------------------------------------ + +int atlas__fs__CellColumns__nb_cells( const CellColumns* This ) { + ATLAS_ASSERT( This != nullptr ); + return This->nb_cells(); +} + +//------------------------------------------------------------------------------ + +Mesh::Implementation* atlas__fs__CellColumns__mesh( CellColumns* This ) { + ATLAS_ASSERT( This != nullptr ); + return This->mesh().get(); +} + +//------------------------------------------------------------------------------ + +mesh::Cells* atlas__fs__CellColumns__cells( CellColumns* This ) { + ATLAS_ASSERT( This != nullptr ); + return &This->cells(); +} + +//------------------------------------------------------------------------------ + +using field::FieldImpl; +using field::FieldSetImpl; + +field::FieldImpl* atlas__fs__CellColumns__create_field( const CellColumns* This, const eckit::Configuration* options ) { + ATLAS_ASSERT( This ); + ATLAS_ASSERT( options ); + FieldImpl* field; + { + Field f = This->createField( *options ); + field = f.get(); + field->attach(); + } + field->detach(); + return field; +} + +//------------------------------------------------------------------------------ + +field::FieldImpl* atlas__fs__CellColumns__create_field_template( const CellColumns* This, + const field::FieldImpl* field_template, + const eckit::Configuration* options ) { + ATLAS_ASSERT( This ); + ATLAS_ASSERT( options ); + FieldImpl* field; + { + Field f = This->createField( Field( field_template ), *options ); + field = f.get(); + field->attach(); + } + field->detach(); + return field; +} + +// ----------------------------------------------------------------------------------- + +void atlas__fs__CellColumns__halo_exchange_fieldset( const CellColumns* This, field::FieldSetImpl* fieldset ) { + ATLAS_ASSERT( This != nullptr ); + ATLAS_ASSERT( fieldset != nullptr ); + FieldSet f( fieldset ); + This->haloExchange( f ); +} + +// ----------------------------------------------------------------------------------- + +void atlas__fs__CellColumns__halo_exchange_field( const CellColumns* This, field::FieldImpl* field ) { + ATLAS_ASSERT( This != nullptr ); + ATLAS_ASSERT( field != nullptr ); + Field f( field ); + This->haloExchange( f ); +} + +// ----------------------------------------------------------------------------------- + +const parallel::HaloExchange* atlas__fs__CellColumns__get_halo_exchange( const CellColumns* This ) { + ATLAS_ASSERT( This != nullptr ); + return &This->halo_exchange(); +} + +// ----------------------------------------------------------------------------------- + +void atlas__fs__CellColumns__gather_fieldset( const CellColumns* This, const field::FieldSetImpl* local, + field::FieldSetImpl* global ) { + ATLAS_ASSERT( This ); + ATLAS_ASSERT( local ); + ATLAS_ASSERT( global ); + const FieldSet l( local ); + FieldSet g( global ); + This->gather( l, g ); +} + +// ----------------------------------------------------------------------------------- + +void atlas__fs__CellColumns__gather_field( const CellColumns* This, const field::FieldImpl* local, + field::FieldImpl* global ) { + ATLAS_ASSERT( This ); + ATLAS_ASSERT( local ); + ATLAS_ASSERT( global ); + const Field l( local ); + Field g( global ); + This->gather( l, g ); +} + +// ----------------------------------------------------------------------------------- + +const parallel::GatherScatter* atlas__fs__CellColumns__get_gather( const CellColumns* This ) { + ATLAS_ASSERT( This ); + return &This->gather(); +} + +// ----------------------------------------------------------------------------------- + +const parallel::GatherScatter* atlas__fs__CellColumns__get_scatter( const CellColumns* This ) { + ATLAS_ASSERT( This ); + return &This->scatter(); +} + +// ----------------------------------------------------------------------------------- + +void atlas__fs__CellColumns__scatter_fieldset( const CellColumns* This, const field::FieldSetImpl* global, + field::FieldSetImpl* local ) { + ATLAS_ASSERT( This ); + ATLAS_ASSERT( local ); + ATLAS_ASSERT( global ); + const FieldSet g( global ); + FieldSet l( local ); + This->scatter( g, l ); +} + +// ----------------------------------------------------------------------------------- + +void atlas__fs__CellColumns__scatter_field( const CellColumns* This, const field::FieldImpl* global, + field::FieldImpl* local ) { + ATLAS_ASSERT( This ); + ATLAS_ASSERT( global ); + ATLAS_ASSERT( local ); + const Field g( global ); + Field l( local ); + This->scatter( g, l ); +} + +// ----------------------------------------------------------------------------------- + +const parallel::Checksum* atlas__fs__CellColumns__get_checksum( const CellColumns* This ) { + ATLAS_ASSERT( This ); + return &This->checksum(); +} + +// ----------------------------------------------------------------------------------- + +void atlas__fs__CellColumns__checksum_fieldset( const CellColumns* This, const field::FieldSetImpl* fieldset, + char*& checksum, int& size, int& allocated ) { + ATLAS_ASSERT( This ); + ATLAS_ASSERT( fieldset ); + std::string checksum_str( This->checksum( fieldset ) ); + size = checksum_str.size(); + checksum = new char[size + 1]; + allocated = true; + strcpy( checksum, checksum_str.c_str() ); +} + +// ----------------------------------------------------------------------------------- + +void atlas__fs__CellColumns__checksum_field( const CellColumns* This, const field::FieldImpl* field, char*& checksum, + int& size, int& allocated ) { + ATLAS_ASSERT( This ); + ATLAS_ASSERT( field ); + std::string checksum_str( This->checksum( field ) ); + size = checksum_str.size(); + checksum = new char[size + 1]; + allocated = true; + strcpy( checksum, checksum_str.c_str() ); +} +} + +// ----------------------------------------------------------------------------------- + +} // namespace detail + +// ----------------------------------------------------------------------------------- + +CellColumns::CellColumns() : FunctionSpace(), functionspace_( nullptr ) {} + +CellColumns::CellColumns( const FunctionSpace& functionspace ) : + FunctionSpace( functionspace ), + functionspace_( dynamic_cast( get() ) ) {} + +CellColumns::CellColumns( const Mesh& mesh, const eckit::Configuration& config ) : + FunctionSpace( new detail::CellColumns( mesh, config ) ), + functionspace_( dynamic_cast( get() ) ) {} + +CellColumns::CellColumns( const Mesh& mesh ) : + FunctionSpace( new detail::CellColumns( mesh ) ), + functionspace_( dynamic_cast( get() ) ) {} + +idx_t CellColumns::nb_cells() const { + return functionspace_->nb_cells(); +} + +idx_t CellColumns::nb_cells_global() const { // Only on MPI rank 0, will this be different from 0 + return functionspace_->nb_cells_global(); +} + +const Mesh& CellColumns::mesh() const { + return functionspace_->mesh(); +} + +const mesh::HybridElements& CellColumns::cells() const { + return functionspace_->cells(); +} + +const parallel::HaloExchange& CellColumns::halo_exchange() const { + return functionspace_->halo_exchange(); +} + +void CellColumns::gather( const FieldSet& local, FieldSet& global ) const { + functionspace_->gather( local, global ); +} + +void CellColumns::gather( const Field& local, Field& global ) const { + functionspace_->gather( local, global ); +} + +const parallel::GatherScatter& CellColumns::gather() const { + return functionspace_->gather(); +} + +void CellColumns::scatter( const FieldSet& global, FieldSet& local ) const { + functionspace_->scatter( global, local ); +} + +void CellColumns::scatter( const Field& global, Field& local ) const { + functionspace_->scatter( global, local ); +} + +const parallel::GatherScatter& CellColumns::scatter() const { + return functionspace_->scatter(); +} + +std::string CellColumns::checksum( const FieldSet& fieldset ) const { + return functionspace_->checksum( fieldset ); +} + +std::string CellColumns::checksum( const Field& field ) const { + return functionspace_->checksum( field ); +} + +const parallel::Checksum& CellColumns::checksum() const { + return functionspace_->checksum(); +} + +} // namespace functionspace +} // namespace atlas diff --git a/src/atlas/functionspace/CellColumns.h b/src/atlas/functionspace/CellColumns.h new file mode 100644 index 000000000..203375f6a --- /dev/null +++ b/src/atlas/functionspace/CellColumns.h @@ -0,0 +1,187 @@ +/* + * (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/field/FieldSet.h" +#include "atlas/functionspace/FunctionSpace.h" +#include "atlas/functionspace/detail/FunctionSpaceImpl.h" +#include "atlas/mesh/Halo.h" +#include "atlas/mesh/Mesh.h" +#include "atlas/option.h" +#include "atlas/util/Config.h" + +// ---------------------------------------------------------------------------- +// Forward declarations + +namespace atlas { +class FieldSet; +} + +namespace atlas { +namespace parallel { +class HaloExchange; +class GatherScatter; +class Checksum; +} // namespace parallel +} // namespace atlas + +namespace atlas { +namespace functionspace { +namespace detail { + +// ---------------------------------------------------------------------------- + +class CellColumns : public FunctionSpaceImpl { +public: + CellColumns( const Mesh&, const eckit::Configuration& = util::NoConfig() ); + + virtual ~CellColumns() override; + + virtual std::string type() const override { return "Cells"; } + + virtual std::string distribution() const override; + + idx_t nb_cells() const; + idx_t nb_cells_global() const; // Only on MPI rank 0, will this be different from 0 + std::vector nb_cells_global_foreach_rank() const; + + const Mesh& mesh() const { return mesh_; } + Mesh& mesh() { return mesh_; } + + const mesh::HybridElements& cells() const { return cells_; } + mesh::HybridElements& cells() { return cells_; } + + // -- Field creation methods + + virtual Field createField( const eckit::Configuration& ) const override; + + virtual Field createField( const Field&, const eckit::Configuration& ) const override; + + // -- Parallelisation aware methods + + virtual void haloExchange( const FieldSet&, bool on_device = false ) const override; + virtual void haloExchange( const Field&, bool on_device = false ) const override; + const parallel::HaloExchange& halo_exchange() const; + + void gather( const FieldSet&, FieldSet& ) const; + void gather( const Field&, Field& ) const; + const parallel::GatherScatter& gather() const; + + void scatter( const FieldSet&, FieldSet& ) const; + void scatter( const Field&, Field& ) const; + const parallel::GatherScatter& scatter() const; + + std::string checksum( const FieldSet& ) const; + std::string checksum( const Field& ) const; + const parallel::Checksum& checksum() const; + + virtual idx_t size() const override { return nb_cells_; } + +private: // methods + idx_t config_size( const eckit::Configuration& config ) const; + array::DataType config_datatype( const eckit::Configuration& ) const; + std::string config_name( const eckit::Configuration& ) const; + idx_t config_levels( const eckit::Configuration& ) const; + array::ArrayShape config_shape( const eckit::Configuration& ) const; + void set_field_metadata( const eckit::Configuration&, Field& ) const; + virtual size_t footprint() const override; + +private: // data + Mesh mesh_; // non-const because functionspace may modify mesh + mesh::HybridElements& cells_; // non-const because functionspace may modify mesh + idx_t nb_levels_; + mesh::Halo halo_; + idx_t nb_cells_; + mutable long nb_cells_global_{-1}; + mutable util::ObjectHandle gather_scatter_; // without ghost + mutable util::ObjectHandle halo_exchange_; + mutable util::ObjectHandle checksum_; +}; + +// ------------------------------------------------------------------- + +extern "C" { + +CellColumns* atlas__fs__CellColumns__new( Mesh::Implementation* mesh, const eckit::Configuration* config ); +void atlas__fs__CellColumns__delete( CellColumns* This ); +int atlas__fs__CellColumns__nb_cells( const CellColumns* This ); +Mesh::Implementation* atlas__fs__CellColumns__mesh( CellColumns* This ); +mesh::Cells* atlas__fs__CellColumns__cells( CellColumns* This ); +field::FieldImpl* atlas__fs__CellColumns__create_field( const CellColumns* This, const eckit::Configuration* options ); +field::FieldImpl* atlas__fs__CellColumns__create_field_template( const CellColumns* This, + const field::FieldImpl* field_template, + const eckit::Configuration* options ); + +void atlas__fs__CellColumns__halo_exchange_fieldset( const CellColumns* This, field::FieldSetImpl* fieldset ); +void atlas__fs__CellColumns__halo_exchange_field( const CellColumns* This, field::FieldImpl* field ); +const parallel::HaloExchange* atlas__fs__CellColumns__get_halo_exchange( const CellColumns* This ); + +void atlas__fs__CellColumns__gather_fieldset( const CellColumns* This, const field::FieldSetImpl* local, + field::FieldSetImpl* global ); +void atlas__fs__CellColumns__gather_field( const CellColumns* This, const field::FieldImpl* local, + field::FieldImpl* global ); +const parallel::GatherScatter* atlas__fs__CellColumns__get_gather( const CellColumns* This ); + +void atlas__fs__CellColumns__scatter_fieldset( const CellColumns* This, const field::FieldSetImpl* global, + field::FieldSetImpl* local ); +void atlas__fs__CellColumns__scatter_field( const CellColumns* This, const field::FieldImpl* global, + field::FieldImpl* local ); +const parallel::GatherScatter* atlas__fs__CellColumns__get_scatter( const CellColumns* This ); + +void atlas__fs__CellColumns__checksum_fieldset( const CellColumns* This, const field::FieldSetImpl* fieldset, + char*& checksum, int& size, int& allocated ); +void atlas__fs__CellColumns__checksum_field( const CellColumns* This, const field::FieldImpl* field, char*& checksum, + int& size, int& allocated ); +const parallel::Checksum* atlas__fs__CellColumns__get_checksum( const CellColumns* This ); +} + +} // namespace detail + +// ------------------------------------------------------------------- + +class CellColumns : public FunctionSpace { +public: + CellColumns(); + CellColumns( const FunctionSpace& ); + CellColumns( const Mesh&, const eckit::Configuration& ); + CellColumns( const Mesh& mesh ); + + operator bool() const { return valid(); } + bool valid() const { return functionspace_; } + + idx_t nb_cells() const; + idx_t nb_cells_global() const; // Only on MPI rank 0, will this be different from 0 + + const Mesh& mesh() const; + + const mesh::HybridElements& cells() const; + + // -- Parallelisation aware methods + const parallel::HaloExchange& halo_exchange() const; + + void gather( const FieldSet&, FieldSet& ) const; + void gather( const Field&, Field& ) const; + const parallel::GatherScatter& gather() const; + + void scatter( const FieldSet&, FieldSet& ) const; + void scatter( const Field&, Field& ) const; + const parallel::GatherScatter& scatter() const; + + std::string checksum( const FieldSet& ) const; + std::string checksum( const Field& ) const; + const parallel::Checksum& checksum() const; + +private: + const detail::CellColumns* functionspace_; +}; + +} // namespace functionspace +} // namespace atlas diff --git a/src/atlas/mesh/actions/BuildHalo.cc b/src/atlas/mesh/actions/BuildHalo.cc index ef621d962..5b605a396 100644 --- a/src/atlas/mesh/actions/BuildHalo.cc +++ b/src/atlas/mesh/actions/BuildHalo.cc @@ -529,6 +529,8 @@ class BuildHaloHelper { std::vector> elem_part; + std::vector> elem_ridx; + std::vector> elem_flags; std::vector> elem_type; @@ -545,6 +547,7 @@ class BuildHaloHelper { elem_nodes_id.resize( mpi_size ); elem_nodes_displs.resize( mpi_size ); elem_part.resize( mpi_size ); + elem_ridx.resize( mpi_size ); elem_flags.resize( mpi_size ); elem_type.resize( mpi_size ); } @@ -591,6 +594,7 @@ class BuildHaloHelper { comm.allToAll( send.elem_glb_idx, recv.elem_glb_idx ); comm.allToAll( send.elem_nodes_id, recv.elem_nodes_id ); comm.allToAll( send.elem_part, recv.elem_part ); + comm.allToAll( send.elem_ridx, recv.elem_ridx ); comm.allToAll( send.elem_type, recv.elem_type ); comm.allToAll( send.elem_flags, recv.elem_flags ); comm.allToAll( send.elem_nodes_displs, recv.elem_nodes_displs ); @@ -615,6 +619,7 @@ class BuildHaloHelper { array::ArrayView ghost; mesh::HybridElements::Connectivity* elem_nodes; array::ArrayView elem_part; + array::IndexView elem_ridx; array::ArrayView elem_flags; array::ArrayView elem_glb_idx; @@ -638,6 +643,7 @@ class BuildHaloHelper { ghost( array::make_view( mesh.nodes().ghost() ) ), elem_nodes( &mesh.cells().node_connectivity() ), elem_part( array::make_view( mesh.cells().partition() ) ), + elem_ridx( array::make_indexview( mesh.cells().remote_index() ) ), elem_flags( array::make_view( mesh.cells().flags() ) ), elem_glb_idx( array::make_view( mesh.cells().global_index() ) ), compute_uid( mesh ) { @@ -660,6 +666,7 @@ class BuildHaloHelper { elem_nodes = &mesh.cells().node_connectivity(); elem_part = array::make_view( mesh.cells().partition() ); + elem_ridx = array::make_indexview( mesh.cells().remote_index() ); elem_flags = array::make_view( mesh.cells().flags() ); elem_glb_idx = array::make_view( mesh.cells().global_index() ); } @@ -708,6 +715,7 @@ class BuildHaloHelper { buf.elem_glb_idx[p].resize( nb_elems ); buf.elem_part[p].resize( nb_elems ); + buf.elem_ridx[p].resize( nb_elems ); buf.elem_flags[p].resize( nb_elems, Topology::NONE ); buf.elem_type[p].resize( nb_elems ); buf.elem_nodes_id[p].resize( nb_elem_nodes ); @@ -719,6 +727,7 @@ class BuildHaloHelper { buf.elem_glb_idx[p][jelem] = elem_glb_idx( ielem ); buf.elem_part[p][jelem] = elem_part( ielem ); + buf.elem_ridx[p][jelem] = elem_ridx( ielem ); Topology::set( buf.elem_flags[p][jelem], elem_flags( ielem ) ); buf.elem_type[p][jelem] = mesh.cells().type_idx( ielem ); for ( idx_t jnode = 0; jnode < elem_nodes->cols( ielem ); ++jnode ) { @@ -774,6 +783,7 @@ class BuildHaloHelper { buf.elem_glb_idx[p].resize( nb_elems ); buf.elem_part[p].resize( nb_elems ); + buf.elem_ridx[p].resize( nb_elems ); buf.elem_flags[p].resize( nb_elems, Topology::NONE ); buf.elem_type[p].resize( nb_elems ); buf.elem_nodes_id[p].resize( nb_elem_nodes ); @@ -783,6 +793,7 @@ class BuildHaloHelper { buf.elem_nodes_displs[p][jelem] = jelemnode; idx_t ielem = elems[jelem]; buf.elem_part[p][jelem] = elem_part( ielem ); + buf.elem_ridx[p][jelem] = elem_ridx( ielem ); Topology::set( buf.elem_flags[p][jelem], elem_flags( ielem ) | newflags ); buf.elem_type[p][jelem] = mesh.cells().type_idx( ielem ); std::vector crds( elem_nodes->cols( ielem ) * 2 ); @@ -950,7 +961,7 @@ class BuildHaloHelper { const idx_t nb_elems_from_part = static_cast( buf.elem_glb_idx[jpart].size() ); for ( idx_t e = 0; e < nb_elems_from_part; ++e ) { if ( element_already_exists( buf.elem_glb_idx[jpart][e] ) == false ) { - received_new_elems[jpart].push_back( e ); + received_new_elems[jpart].emplace_back( e ); } } nb_new_elems += received_new_elems[jpart].size(); @@ -962,7 +973,7 @@ class BuildHaloHelper { for ( idx_t jpart = 0; jpart < mpi_size; ++jpart ) { for ( const idx_t ielem : received_new_elems[jpart] ) { - elements_of_type[buf.elem_type[jpart][ielem]][jpart].push_back( ielem ); + elements_of_type[buf.elem_type[jpart][ielem]][jpart].emplace_back( ielem ); ++nb_elements_of_type[buf.elem_type[jpart][ielem]]; } } @@ -982,6 +993,7 @@ class BuildHaloHelper { auto elem_type_glb_idx = elements.view( mesh.cells().global_index() ); auto elem_type_part = elements.view( mesh.cells().partition() ); + auto elem_type_ridx = elements.indexview( mesh.cells().remote_index() ); auto elem_type_halo = elements.view( mesh.cells().halo() ); auto elem_type_flags = elements.view( mesh.cells().flags() ); @@ -992,6 +1004,7 @@ class BuildHaloHelper { int loc_idx = new_elems_pos + new_elem; elem_type_glb_idx( loc_idx ) = std::abs( buf.elem_glb_idx[jpart][jelem] ); elem_type_part( loc_idx ) = buf.elem_part[jpart][jelem]; + elem_type_ridx( loc_idx ) = buf.elem_ridx[jpart][jelem]; elem_type_halo( loc_idx ) = halosize + 1; elem_type_flags( loc_idx ) = buf.elem_flags[jpart][jelem]; for ( idx_t n = 0; n < node_connectivity.cols(); ++n ) { @@ -1324,9 +1337,17 @@ void BuildHalo::operator()( int nb_elems ) { } } - std::stringstream ss; - ss << "nb_nodes_including_halo[" << jhalo + 1 << "]"; - mesh_.metadata().set( ss.str(), mesh_.nodes().size() ); + { + std::stringstream ss; + ss << "nb_nodes_including_halo[" << jhalo + 1 << "]"; + mesh_.metadata().set( ss.str(), mesh_.nodes().size() ); + } + + for ( int t = 0; t < mesh_.cells().nb_types(); ++t ) { + std::stringstream ss; + ss << "nb_cells_including_halo[" << t << "][" << jhalo + 1 << "]"; + mesh_.metadata().set( ss.str(), mesh_.cells().elements( t ).size() ); + } mesh_.metadata().set( "halo", jhalo + 1 ); mesh_.nodes().global_index().metadata().set( "human_readable", false ); mesh_.cells().global_index().metadata().set( "human_readable", false ); diff --git a/src/atlas/mesh/actions/BuildParallelFields.cc b/src/atlas/mesh/actions/BuildParallelFields.cc index ac535c9c2..b2d3fd6ae 100644 --- a/src/atlas/mesh/actions/BuildParallelFields.cc +++ b/src/atlas/mesh/actions/BuildParallelFields.cc @@ -983,6 +983,105 @@ Field& build_edges_global_idx( Mesh& mesh ) { return edges.global_index(); } +//---------------------------------------------------------------------------------------------------------------------- + +Field& build_cells_remote_idx( mesh::Cells& cells, const mesh::Nodes& nodes ) { + ATLAS_TRACE(); + idx_t mypart = static_cast( mpi::comm().rank() ); + idx_t nparts = static_cast( mpi::comm().size() ); + + UniqueLonLat compute_uid( nodes ); + + // This piece should be somewhere central ... could be NPROMA ? + // ----------> + std::vector proc( nparts ); + for ( idx_t jpart = 0; jpart < nparts; ++jpart ) { + proc[jpart] = jpart; + } + // <--------- + + auto ridx = array::make_indexview( cells.remote_index() ); + auto part = array::make_view( cells.partition() ); + const auto& element_nodes = cells.node_connectivity(); + idx_t nb_cells = cells.size(); + + idx_t varsize = 2; + + std::vector> send_needed( mpi::comm().size() ); + std::vector> recv_needed( mpi::comm().size() ); + int sendcnt = 0; + std::map lookup; + for ( idx_t jcell = 0; jcell < nb_cells; ++jcell ) { + uid_t uid = compute_uid( element_nodes.row( jcell ) ); + + if ( idx_t( part( jcell ) ) == mypart ) { + lookup[uid] = jcell; + ridx( jcell ) = jcell; + } + else { + ATLAS_ASSERT( jcell < part.shape( 0 ) ); + if ( part( jcell ) >= static_cast( proc.size() ) ) { + std::stringstream msg; + msg << "Assertion [part(" << jcell << ") < proc.size()] failed\n" + << "part(" << jcell << ") = " << part( jcell ) << "\n" + << "proc.size() = " << proc.size(); + throw_AssertionFailed( msg.str(), Here() ); + } + ATLAS_ASSERT( part( jcell ) < (idx_t)proc.size() ); + ATLAS_ASSERT( (size_t)proc[part( jcell )] < send_needed.size() ); + send_needed[proc[part( jcell )]].push_back( uid ); + send_needed[proc[part( jcell )]].push_back( jcell ); + sendcnt++; + } + } + + ATLAS_TRACE_MPI( ALLTOALL ) { mpi::comm().allToAll( send_needed, recv_needed ); } + + std::vector> send_found( mpi::comm().size() ); + std::vector> recv_found( mpi::comm().size() ); + + for ( idx_t jpart = 0; jpart < nparts; ++jpart ) { + const std::vector& recv_cell = recv_needed[proc[jpart]]; + const idx_t nb_recv_cells = idx_t( recv_cell.size() ) / varsize; + // array::ArrayView recv_node( make_view( Array::wrap(shape, + // recv_needed[ proc[jpart] ].data()) ), + // array::make_shape(recv_needed[ proc[jpart] ].size()/varsize,varsize) + // ); + for ( idx_t jcell = 0; jcell < nb_recv_cells; ++jcell ) { + uid_t uid = recv_cell[jcell * varsize + 0]; + int icell = recv_cell[jcell * varsize + 1]; + if ( lookup.count( uid ) ) { + send_found[proc[jpart]].push_back( icell ); + send_found[proc[jpart]].push_back( lookup[uid] ); + } + else { + std::stringstream msg; + msg << "[" << mpi::comm().rank() << "] " + << "Node requested by rank [" << jpart << "] with uid [" << uid + << "] that should be owned is not found"; + throw_Exception( msg.str(), Here() ); + } + } + } + + ATLAS_TRACE_MPI( ALLTOALL ) { mpi::comm().allToAll( send_found, recv_found ); } + + for ( idx_t jpart = 0; jpart < nparts; ++jpart ) { + const std::vector& recv_cell = recv_found[proc[jpart]]; + const idx_t nb_recv_cells = recv_cell.size() / 2; + // array::ArrayView recv_node( recv_found[ proc[jpart] ].data(), + // array::make_shape(recv_found[ proc[jpart] ].size()/2,2) ); + for ( idx_t jcell = 0; jcell < nb_recv_cells; ++jcell ) { + ridx( recv_cell[jcell * 2 + 0] ) = recv_cell[jcell * 2 + 1]; + } + } + return cells.remote_index(); +} + +void build_cells_parallel_fields( Mesh& mesh ) { + build_cells_remote_idx( mesh.cells(), mesh.nodes() ); +} + //---------------------------------------------------------------------------------------------------------------------- // C wrapper interfaces to C++ routines diff --git a/src/atlas/mesh/actions/BuildParallelFields.h b/src/atlas/mesh/actions/BuildParallelFields.h index a01c9cb1a..5c4cda3f0 100644 --- a/src/atlas/mesh/actions/BuildParallelFields.h +++ b/src/atlas/mesh/actions/BuildParallelFields.h @@ -54,6 +54,8 @@ void build_nodes_parallel_fields( mesh::Nodes& nodes ); */ void build_edges_parallel_fields( Mesh& mesh ); +void build_cells_parallel_fields( Mesh& mesh ); + void renumber_nodes_glb_idx( mesh::Nodes& nodes ); // ------------------------------------------------------------------ diff --git a/src/atlas/output/detail/GmshIO.cc b/src/atlas/output/detail/GmshIO.cc index 576297681..b682080eb 100644 --- a/src/atlas/output/detail/GmshIO.cc +++ b/src/atlas/output/detail/GmshIO.cc @@ -355,6 +355,54 @@ void write_field_nodes( const Metadata& gmsh_options, const functionspace::Struc } // ---------------------------------------------------------------------------- +template +void write_field_elems( const Metadata& gmsh_options, const functionspace::CellColumns& function_space, + const Field& field, std::ostream& out ) { +#if 1 + Log::debug() << "writing CellColumns field " << field.name() << "..." << std::endl; + + bool gather( gmsh_options.get( "gather" ) && atlas::mpi::comm().size() > 1 ); + // unused: bool binary( !gmsh_options.get( "ascii" ) ); + idx_t nlev = std::max( 1, field.levels() ); + idx_t ndata = std::min( function_space.nb_cells(), field.shape( 0 ) ); + idx_t nvars = std::max( 1, field.variables() ); + array::ArrayView gidx = array::make_view( function_space.cells().global_index() ); + Field gidx_glb; + Field field_glb; + if ( gather ) { + gidx_glb = function_space.createField( option::name( "gidx_glb" ) | option::levels( false ) | + option::global() ); + function_space.gather( function_space.cells().global_index(), gidx_glb ); + gidx = array::make_view( gidx_glb ); + + field_glb = function_space.createField( field, option::global() ); + function_space.gather( field, field_glb ); + ndata = std::min( function_space.nb_cells_global(), field_glb.shape( 0 ) ); + } + + std::vector lev = get_levels( nlev, gmsh_options ); + for ( size_t ilev = 0; ilev < lev.size(); ++ilev ) { + int jlev = lev[ilev]; + if ( ( gather && atlas::mpi::comm().rank() == 0 ) || !gather ) { + out << "$ElementData\n"; + out << "1\n"; + out << "\"" << field.name() << field_lev( field, jlev ) << "\"\n"; + out << "1\n"; + out << field_time( field ) << "\n"; + out << "4\n"; + out << field_step( field ) << "\n"; + out << field_vars( nvars ) << "\n"; + out << ndata << "\n"; + out << atlas::mpi::comm().rank() << "\n"; + auto data = gather ? make_level_view( field_glb, ndata, jlev ) + : make_level_view( field, ndata, jlev ); + write_level( out, gidx, data ); + out << "$EndElementData\n"; + } + } +#endif +} + // ---------------------------------------------------------------------------- #if 0 template< typename DATA_TYPE > @@ -999,6 +1047,11 @@ void GmshIO::write( const Field& field, const PathName& file_path, openmode mode fieldset.add( field ); write( fieldset, field.functionspace(), file_path, mode ); } + else if ( functionspace::CellColumns( field.functionspace() ) ) { + FieldSet fieldset; + fieldset.add( field ); + write( fieldset, field.functionspace(), file_path, mode ); + } else { std::stringstream msg; msg << "Field [" << field.name() << "] has functionspace [" << field.functionspace().type() @@ -1066,6 +1119,44 @@ void GmshIO::write_delegate( const FieldSet& fieldset, const functionspace::Node } file.close(); } + +void GmshIO::write_delegate( const FieldSet& fieldset, const functionspace::CellColumns& functionspace, + const eckit::PathName& file_path, GmshIO::openmode mode ) const { + bool is_new_file = ( mode != std::ios_base::app || !file_path.exists() ); + bool binary( !options.get( "ascii" ) ); + if ( binary ) { + mode |= std::ios_base::binary; + } + bool gather = options.has( "gather" ) ? options.get( "gather" ) : false; + GmshFile file( file_path, mode, gather ? -1 : int( atlas::mpi::comm().rank() ) ); + + // Header + if ( is_new_file ) { + write_header_ascii( file ); + } + + // field::Fields + for ( idx_t field_idx = 0; field_idx < fieldset.size(); ++field_idx ) { + const Field& field = fieldset[field_idx]; + Log::debug() << "writing field " << field.name() << " to gmsh file " << file_path << std::endl; + + if ( field.datatype() == array::DataType::int32() ) { + write_field_elems( options, functionspace, field, file ); + } + else if ( field.datatype() == array::DataType::int64() ) { + write_field_elems( options, functionspace, field, file ); + } + else if ( field.datatype() == array::DataType::real32() ) { + write_field_elems( options, functionspace, field, file ); + } + else if ( field.datatype() == array::DataType::real64() ) { + write_field_elems( options, functionspace, field, file ); + } + + file << std::flush; + } + file.close(); +} // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- @@ -1121,6 +1212,9 @@ void GmshIO::write( const FieldSet& fieldset, const FunctionSpace& funcspace, co else if ( functionspace::StructuredColumns( funcspace ) ) { write_delegate( fieldset, functionspace::StructuredColumns( funcspace ), file_path, mode ); } + else if ( functionspace::CellColumns( funcspace ) ) { + write_delegate( fieldset, functionspace::CellColumns( funcspace ), file_path, mode ); + } else { ATLAS_NOTIMPLEMENTED; } diff --git a/src/atlas/output/detail/GmshIO.h b/src/atlas/output/detail/GmshIO.h index ccf1d1db5..67982c801 100644 --- a/src/atlas/output/detail/GmshIO.h +++ b/src/atlas/output/detail/GmshIO.h @@ -14,6 +14,7 @@ #include #include +#include "atlas/functionspace/CellColumns.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/util/Metadata.h" @@ -103,6 +104,12 @@ class GmshIO { void write_delegate( const FieldSet& fieldset, const functionspace::NodeColumns&, const eckit::PathName& file_path, openmode mode = std::ios::out ) const; + /// Write fieldset to file using Cells functionspace + /// Depending on argument "mode", the fields will be appended, + /// or existing file will be overwritten + void write_delegate( const FieldSet& fieldset, const functionspace::CellColumns&, const eckit::PathName& file_path, + openmode mode = std::ios::out ) const; + /// Write fieldset to file using StructuredColumns functionspace /// Depending on argument "mode", the fields will be appended, /// or existing file will be overwritten diff --git a/src/tests/functionspace/CMakeLists.txt b/src/tests/functionspace/CMakeLists.txt index b6aa21cc4..b4e6dfbf1 100644 --- a/src/tests/functionspace/CMakeLists.txt +++ b/src/tests/functionspace/CMakeLists.txt @@ -29,6 +29,12 @@ ecbuild_add_test( TARGET atlas_test_functionspace ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) +ecbuild_add_test( TARGET atlas_test_cellcolumns + SOURCES test_cellcolumns.cc + LIBS atlas + ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} +) + ecbuild_add_test( TARGET atlas_test_structuredcolumns SOURCES test_structuredcolumns.cc LIBS atlas diff --git a/src/tests/functionspace/test_cellcolumns.cc b/src/tests/functionspace/test_cellcolumns.cc new file mode 100644 index 000000000..bc9455660 --- /dev/null +++ b/src/tests/functionspace/test_cellcolumns.cc @@ -0,0 +1,117 @@ +/* + * (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/types/Types.h" + +#include "atlas/array/ArrayView.h" +#include "atlas/array/MakeView.h" +#include "atlas/field/Field.h" +#include "atlas/field/FieldSet.h" +#include "atlas/functionspace/CellColumns.h" +#include "atlas/grid/Grid.h" +#include "atlas/library/Library.h" +#include "atlas/mesh.h" +#include "atlas/meshgenerator.h" +#include "atlas/output/Gmsh.h" +#include "atlas/parallel/HaloExchange.h" +#include "atlas/parallel/mpi/mpi.h" + +#include "tests/AtlasTestEnvironment.h" + +using namespace eckit; +using namespace atlas::functionspace; +using namespace atlas::util; + +namespace atlas { +namespace test { + +Mesh generate_mesh() { + auto grid = Grid{"O16"}; + auto meshgenerator = StructuredMeshGenerator{util::Config( "patch_pole", false )( "triangulate", true )}; + return meshgenerator.generate( grid ); +} + +//----------------------------------------------------------------------------- + +CASE( "test_functionspace_CellColumns_no_halo" ) { + Mesh mesh = generate_mesh(); + CellColumns fs( mesh ); + Field field( fs.createField() ); + EXPECT( field.shape( 0 ) == mesh.cells().size() ); + EXPECT( field.shape( 0 ) == fs.nb_cells() ); + + array::ArrayView value = array::make_view( field ); + array::ArrayView halo = array::make_view( mesh.cells().halo() ); + array::ArrayView partition = array::make_view( mesh.cells().partition() ); + array::IndexView ridx = array::make_indexview( mesh.cells().remote_index() ); + array::ArrayView gidx = array::make_view( mesh.cells().global_index() ); + + const size_t nb_cells = mesh.cells().size(); + for ( size_t j = 0; j < nb_cells; ++j ) { + if ( halo( j ) ) { + value( j ) = -1; + EXPECT( false ); + } + else { + value( j ) = partition( j ); + } + } + + fs.haloExchange( field ); + + for ( size_t j = 0; j < nb_cells; ++j ) { + EXPECT( value( j ) == partition( j ) ); + } +} + +CASE( "test_functionspace_CellColumns_halo_1" ) { + Mesh mesh = generate_mesh(); + CellColumns fs( mesh, option::halo( 1 ) ); + + Field field( fs.createField() ); + EXPECT( field.shape( 0 ) == mesh.cells().size() ); + EXPECT( field.shape( 0 ) == fs.nb_cells() ); + + auto value = array::make_view( field ); + auto halo = array::make_view( mesh.cells().halo() ); + auto partition = array::make_view( mesh.cells().partition() ); + auto ridx = array::make_indexview( mesh.cells().remote_index() ); + auto gidx = array::make_view( mesh.cells().global_index() ); + + + const size_t nb_cells = mesh.cells().size(); + for ( size_t j = 0; j < nb_cells; ++j ) { + if ( halo( j ) ) { + value( j ) = -1.; + } + else { + value( j ) = partition( j ); + } + } + fs.haloExchange( field ); + for ( size_t j = 0; j < nb_cells; ++j ) { + Log::info() << j << "\t" << gidx( j ) << "\t" << ridx( j ) << "\t" << gidx( ridx( j ) ) << " --- " << value( j ) + << std::endl; + EXPECT( value( j ) == partition( j ) ); + } + + output::Gmsh output( "cellcolumns_halo1.msh" ); + output.write( mesh, util::Config( "ghost", true ) ); + output.write( field ); +} + +//----------------------------------------------------------------------------- + +} // namespace test +} // namespace atlas + +int main( int argc, char** argv ) { + return atlas::test::run( argc, argv ); +} From 6bfc8710c3fdd778dbd41fd86e99ac32ab59b4b8 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 17 Sep 2019 17:03:40 +0100 Subject: [PATCH 25/40] ATLAS-247 cleanup --- src/atlas/meshgenerator/MeshGenerator.cc | 14 +++- src/atlas/meshgenerator/MeshGenerator.h | 1 + src/tests/functionspace/test_cellcolumns.cc | 72 +++++++++------------ 3 files changed, 45 insertions(+), 42 deletions(-) diff --git a/src/atlas/meshgenerator/MeshGenerator.cc b/src/atlas/meshgenerator/MeshGenerator.cc index f7f722dad..6288b086c 100644 --- a/src/atlas/meshgenerator/MeshGenerator.cc +++ b/src/atlas/meshgenerator/MeshGenerator.cc @@ -14,6 +14,7 @@ #include "atlas/grid/Grid.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator/MeshGenerator.h" +#include "atlas/runtime/Exception.h" #include "atlas/meshgenerator/detail/MeshGeneratorFactory.h" #include "atlas/meshgenerator/detail/MeshGeneratorImpl.h" @@ -24,8 +25,17 @@ namespace atlas { //---------------------------------------------------------------------------------------------------------------------- -MeshGenerator::MeshGenerator( const std::string& key, const eckit::Parametrisation& params ) : - Handle( meshgenerator::MeshGeneratorFactory::build( key, params ) ) {} +MeshGenerator::MeshGenerator( const std::string& key, const eckit::Parametrisation& config ) : + Handle( meshgenerator::MeshGeneratorFactory::build( key, config ) ) {} + +MeshGenerator::MeshGenerator( const eckit::Parametrisation& config ) : + Handle( meshgenerator::MeshGeneratorFactory::build( + [&config]() { + std::string key; + ATLAS_ASSERT( config.get( "type", key ), "type must be specified in MeshGenerator configuration" ); + return key; + }(), + config ) ) {} void MeshGenerator::hash( eckit::Hash& h ) const { return get()->hash( h ); diff --git a/src/atlas/meshgenerator/MeshGenerator.h b/src/atlas/meshgenerator/MeshGenerator.h index 79a5ac282..6b8fc4e63 100644 --- a/src/atlas/meshgenerator/MeshGenerator.h +++ b/src/atlas/meshgenerator/MeshGenerator.h @@ -46,6 +46,7 @@ class MeshGenerator : public util::ObjectHandle( field ); + auto partition = array::make_view( mesh.cells().partition() ); + auto halo = array::make_view( mesh.cells().halo() ); -CASE( "test_functionspace_CellColumns_no_halo" ) { - Mesh mesh = generate_mesh(); - CellColumns fs( mesh ); - Field field( fs.createField() ); EXPECT( field.shape( 0 ) == mesh.cells().size() ); - EXPECT( field.shape( 0 ) == fs.nb_cells() ); - - array::ArrayView value = array::make_view( field ); - array::ArrayView halo = array::make_view( mesh.cells().halo() ); - array::ArrayView partition = array::make_view( mesh.cells().partition() ); - array::IndexView ridx = array::make_indexview( mesh.cells().remote_index() ); - array::ArrayView gidx = array::make_view( mesh.cells().global_index() ); const size_t nb_cells = mesh.cells().size(); for ( size_t j = 0; j < nb_cells; ++j ) { if ( halo( j ) ) { value( j ) = -1; - EXPECT( false ); } else { value( j ) = partition( j ); } } +} - fs.haloExchange( field ); - +void check_field_values( const Mesh& mesh, Field& field ) { + auto value = array::make_view( field ); + auto partition = array::make_view( mesh.cells().partition() ); + auto halo = array::make_view( mesh.cells().halo() ); + const size_t nb_cells = mesh.cells().size(); for ( size_t j = 0; j < nb_cells; ++j ) { EXPECT( value( j ) == partition( j ) ); } } +//----------------------------------------------------------------------------- + +CASE( "test_functionspace_CellColumns_no_halo" ) { + Mesh mesh = generate_mesh(); + CellColumns fs( mesh ); + + Field field( fs.createField() ); + + set_field_values( mesh, field ); + + fs.haloExchange( field ); + + check_field_values( mesh, field ); +} + CASE( "test_functionspace_CellColumns_halo_1" ) { Mesh mesh = generate_mesh(); CellColumns fs( mesh, option::halo( 1 ) ); - Field field( fs.createField() ); - EXPECT( field.shape( 0 ) == mesh.cells().size() ); - EXPECT( field.shape( 0 ) == fs.nb_cells() ); - - auto value = array::make_view( field ); - auto halo = array::make_view( mesh.cells().halo() ); - auto partition = array::make_view( mesh.cells().partition() ); - auto ridx = array::make_indexview( mesh.cells().remote_index() ); - auto gidx = array::make_view( mesh.cells().global_index() ); + Field field( fs.createField() ); + set_field_values( mesh, field ); - const size_t nb_cells = mesh.cells().size(); - for ( size_t j = 0; j < nb_cells; ++j ) { - if ( halo( j ) ) { - value( j ) = -1.; - } - else { - value( j ) = partition( j ); - } - } fs.haloExchange( field ); - for ( size_t j = 0; j < nb_cells; ++j ) { - Log::info() << j << "\t" << gidx( j ) << "\t" << ridx( j ) << "\t" << gidx( ridx( j ) ) << " --- " << value( j ) - << std::endl; - EXPECT( value( j ) == partition( j ) ); - } + + check_field_values( mesh, field ); output::Gmsh output( "cellcolumns_halo1.msh" ); output.write( mesh, util::Config( "ghost", true ) ); From 97b2f35b85450d45a806893c4ad518c662e8f398 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 17 Sep 2019 17:09:01 +0100 Subject: [PATCH 26/40] ATLAS-247 Comment regarding status --- src/tests/functionspace/test_cellcolumns.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/tests/functionspace/test_cellcolumns.cc b/src/tests/functionspace/test_cellcolumns.cc index ad296aec2..5efc89dbf 100644 --- a/src/tests/functionspace/test_cellcolumns.cc +++ b/src/tests/functionspace/test_cellcolumns.cc @@ -8,6 +8,15 @@ * nor does it submit to any jurisdiction. */ + +/// Linked JIRA issue: ATLAS-247 +/// For now the CellColumns functionspace only works when the mesh has elements of only one type. +/// For this reason we triangulate the mesh always, and don't patch the pole +/// The problem is in the computation of mesh.cells().remote_index() during BuildHalo called +/// within the CellColumns constructor. Meshes without any parallel halo will also succeed as +/// the BuildHalo routine is not called. + + #include "eckit/types/Types.h" #include "atlas/array/ArrayView.h" From 8e4bcbc02b3e5f02e9dab9aa0e350840841133cc Mon Sep 17 00:00:00 2001 From: Pedro Maciel Date: Tue, 17 Sep 2019 17:24:43 +0100 Subject: [PATCH 27/40] ATLAS-246: Proj-based projections unit test (from Proj examples) --- src/tests/projection/test_proj.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/projection/test_proj.cc b/src/tests/projection/test_proj.cc index 77430ed00..dafe688b2 100644 --- a/src/tests/projection/test_proj.cc +++ b/src/tests/projection/test_proj.cc @@ -44,7 +44,7 @@ CASE( "test_proj" ) { PointXY b = projection.xy( a ); check( b, {691875.632137542, 6098907.825129169} ); - check( projection.lonlat( b ), {12, 55} ); + check( projection.lonlat( b ), a ); } } From 96d873402572bb983600c0885ff92f3cfffba64e Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 17 Sep 2019 18:26:48 +0100 Subject: [PATCH 28/40] Compatibility with eckit master branch [1.3.2] (was working already as of eckit 0a9743cb) --- src/atlas/mesh/actions/ReorderHilbert.cc | 61 +++++++++++++++++++----- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/src/atlas/mesh/actions/ReorderHilbert.cc b/src/atlas/mesh/actions/ReorderHilbert.cc index a57ea0872..1d7eda7b4 100644 --- a/src/atlas/mesh/actions/ReorderHilbert.cc +++ b/src/atlas/mesh/actions/ReorderHilbert.cc @@ -134,9 +134,30 @@ gidx_t Hilbert::recursive_algorithm( const PointXY& p, const box_t& box, idx_t l double min_distance = std::numeric_limits::max(); + auto compute_distance2 = []( const PointXY& p1, const PointXY& p2 ) { + // workaround because of eckit 1.3.2 issue with constness in KPoint + double d = 0; + for ( size_t i = 0; i < 2; i++ ) { + double dx = p1[i] - p2[i]; + d += dx * dx; + } + return d; + }; + + auto compute_average = []( const PointXY& p1, const PointXY& p2 ) { + // workaround because of eckit 1.3.2 issue with constness in KPoint + PointXY avg; + avg.x() = p1.x() + p2.x(); + avg.x() *= 0.5; + avg.y() = p1.y() + p2.y(); + avg.y() *= 0.5; + return avg; + }; + idx_t quadrant; for ( idx_t idx = 0; idx < 4; ++idx ) { - double distance = box[idx].distance2( p ); + // double distance = box[idx].distance2( p ); // does not compile with eckit 1.3.2 + double distance = compute_distance2( p, box[idx] ); // workaround if ( distance < min_distance ) { quadrant = idx; min_distance = distance; @@ -147,27 +168,41 @@ gidx_t Hilbert::recursive_algorithm( const PointXY& p, const box_t& box, idx_t l switch ( quadrant ) { case A: box_quadrant[A] = box[A]; - box_quadrant[B] = ( box[A] + box[D] ) * 0.5; - box_quadrant[C] = ( box[A] + box[C] ) * 0.5; - box_quadrant[D] = ( box[A] + box[B] ) * 0.5; + // box_quadrant[B] = ( box[A] + box[D] ) * 0.5; // does not compile with eckit 1.3.2 + // box_quadrant[C] = ( box[A] + box[C] ) * 0.5; // does not compile with eckit 1.3.2 + // box_quadrant[D] = ( box[A] + box[B] ) * 0.5; // does not compile with eckit 1.3.2 + box_quadrant[B] = compute_average( box[A], box[D] ); // workaround + box_quadrant[C] = compute_average( box[A], box[C] ); // workaround + box_quadrant[D] = compute_average( box[A], box[B] ); // workaround break; case B: - box_quadrant[A] = ( box[B] + box[A] ) * 0.5; + // box_quadrant[A] = ( box[B] + box[A] ) * 0.5; // does not compile with eckit 1.3.2 box_quadrant[B] = box[B]; - box_quadrant[C] = ( box[B] + box[C] ) * 0.5; - box_quadrant[D] = ( box[B] + box[D] ) * 0.5; + // box_quadrant[C] = ( box[B] + box[C] ) * 0.5; // does not compile with eckit 1.3.2 + // box_quadrant[D] = ( box[B] + box[D] ) * 0.5; // does not compile with eckit 1.3.2 + box_quadrant[A] = compute_average( box[B], box[A] ); // workaround + box_quadrant[C] = compute_average( box[B], box[C] ); // workaround + box_quadrant[D] = compute_average( box[B], box[D] ); // workaround break; case C: - box_quadrant[A] = ( box[C] + box[A] ) * 0.5; - box_quadrant[B] = ( box[C] + box[B] ) * 0.5; + // box_quadrant[A] = ( box[C] + box[A] ) * 0.5; // does not compile with eckit 1.3.2 + // box_quadrant[B] = ( box[C] + box[B] ) * 0.5; // does not compile with eckit 1.3.2 box_quadrant[C] = box[C]; - box_quadrant[D] = ( box[C] + box[D] ) * 0.5; + // box_quadrant[D] = ( box[C] + box[D] ) * 0.5; // does not compile with eckit 1.3.2 + box_quadrant[A] = compute_average( box[C], box[A] ); // workaround + box_quadrant[B] = compute_average( box[C], box[B] ); // workaround + box_quadrant[D] = compute_average( box[C], box[D] ); // workaround + break; case D: - box_quadrant[A] = ( box[D] + box[C] ) * 0.5; - box_quadrant[B] = ( box[D] + box[B] ) * 0.5; - box_quadrant[C] = ( box[D] + box[A] ) * 0.5; + // box_quadrant[A] = ( box[D] + box[C] ) * 0.5; // does not compile with eckit 1.3.2 + // box_quadrant[B] = ( box[D] + box[B] ) * 0.5; // does not compile with eckit 1.3.2 + // box_quadrant[C] = ( box[D] + box[A] ) * 0.5; // does not compile with eckit 1.3.2 box_quadrant[D] = box[D]; + box_quadrant[A] = compute_average( box[D], box[C] ); // workaround + box_quadrant[B] = compute_average( box[D], box[B] ); // workaround + box_quadrant[C] = compute_average( box[D], box[A] ); // workaround + break; } From 19db6b34797cd972b5f22999b161a0f6585b4396 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Wed, 18 Sep 2019 12:13:53 +0100 Subject: [PATCH 29/40] Fix gridtools_storage backend --- src/atlas/array/LocalView.h | 2 + .../array/gridtools/GridToolsIndexView.cc | 17 +++ .../array/gridtools/GridToolsIndexView.h | 105 ++++++++++++++++++ src/atlas/array/native/NativeIndexView.cc | 7 ++ src/atlas/array/native/NativeIndexView.h | 15 +++ src/atlas/mesh/Elements.cc | 16 +-- src/atlas/mesh/Elements.h | 4 +- 7 files changed, 156 insertions(+), 10 deletions(-) diff --git a/src/atlas/array/LocalView.h b/src/atlas/array/LocalView.h index d71cef883..9cd249dd2 100644 --- a/src/atlas/array/LocalView.h +++ b/src/atlas/array/LocalView.h @@ -152,6 +152,8 @@ class LocalView { const idx_t* shape() const { return shape_; } + const idx_t* strides() const { return strides_; } + value_type const* data() const { return data_; } return_type* data() { return data_; } diff --git a/src/atlas/array/gridtools/GridToolsIndexView.cc b/src/atlas/array/gridtools/GridToolsIndexView.cc index 1406cf9c2..fa6644ccf 100644 --- a/src/atlas/array/gridtools/GridToolsIndexView.cc +++ b/src/atlas/array/gridtools/GridToolsIndexView.cc @@ -29,6 +29,20 @@ void IndexView::dump( std::ostream& os ) const { os << "]" << std::endl; } +template +LocalIndexView::LocalIndexView( Value* data, const idx_t shape[1] ) : data_( const_cast( data ) ) { + strides_[0] = 1; + shape_[0] = shape[0]; +} + +template +LocalIndexView::LocalIndexView( Value* data, const idx_t shape[1], const idx_t strides[1] ) : + data_( const_cast( data ) ) { + strides_[0] = strides[0]; + shape_[0] = shape[0]; +} + + //------------------------------------------------------------------------------------------------------ } // namespace array @@ -41,5 +55,8 @@ namespace array { template class IndexView; template class IndexView; +template class LocalIndexView; +template class LocalIndexView; + } // namespace array } // namespace atlas diff --git a/src/atlas/array/gridtools/GridToolsIndexView.h b/src/atlas/array/gridtools/GridToolsIndexView.h index f153bae5c..332916ab2 100644 --- a/src/atlas/array/gridtools/GridToolsIndexView.h +++ b/src/atlas/array/gridtools/GridToolsIndexView.h @@ -118,6 +118,111 @@ class IndexView { #undef FROM_FORTRAN }; + +#if ATLAS_HAVE_FORTRAN +#define INDEX_REF Index +#define FROM_FORTRAN -1 +#define TO_FORTRAN +1 +#else +#define INDEX_REF * +#define FROM_FORTRAN +#define TO_FORTRAN +#endif + + +template +class LocalIndexView { +public: + using value_type = typename remove_const::type; + +#if ATLAS_HAVE_FORTRAN + typedef detail::FortranIndex Index; +#else + typedef Value& Index; +#endif + +public: + LocalIndexView( Value* data, const idx_t shape[Rank] ); + + LocalIndexView( Value* data, const idx_t shape[Rank], const idx_t strides[Rank] ); + + // -- Access methods + + template + Index operator()( Idx... idx ) { + check_bounds( idx... ); + return INDEX_REF( &data_[index( idx... )] ); + } + + template + const value_type operator()( Ints... idx ) const { + return data_[index( idx... )] FROM_FORTRAN; + } + +private: + // -- Private methods + + template + constexpr idx_t index_part( Int idx, Ints... next_idx ) const { + return idx * strides_[Dim] + index_part( next_idx... ); + } + + template + constexpr idx_t index_part( Int last_idx ) const { + return last_idx * strides_[Dim]; + } + + template + constexpr idx_t index( Ints... idx ) const { + return index_part<0>( idx... ); + } + +#if ATLAS_INDEXVIEW_BOUNDS_CHECKING + template + void check_bounds( Ints... idx ) const { + static_assert( sizeof...( idx ) == Rank, "Expected number of indices is different from rank of array" ); + return check_bounds_part<0>( idx... ); + } +#else + template + void check_bounds( Ints... ) const {} +#endif + + template + void check_bounds_force( Ints... idx ) const { + static_assert( sizeof...( idx ) == Rank, "Expected number of indices is different from rank of array" ); + return check_bounds_part<0>( idx... ); + } + + template + void check_bounds_part( Int idx, Ints... next_idx ) const { + if ( idx_t( idx ) >= shape_[Dim] ) { + throw_OutOfRange( "IndexView", array_dim(), idx, shape_[Dim] ); + } + check_bounds_part( next_idx... ); + } + + template + void check_bounds_part( Int last_idx ) const { + if ( idx_t( last_idx ) >= shape_[Dim] ) { + throw_OutOfRange( "IndexView", array_dim(), last_idx, shape_[Dim] ); + } + } + + idx_t size() const { return shape_[0]; } + + void dump( std::ostream& os ) const; + +private: + Value* data_; + idx_t strides_[Rank]; + idx_t shape_[Rank]; +}; + +#undef INDEX_REF +#undef FROM_FORTRAN +#undef TO_FORTRAN + //------------------------------------------------------------------------------------------------------ } // namespace array diff --git a/src/atlas/array/native/NativeIndexView.cc b/src/atlas/array/native/NativeIndexView.cc index 485901b84..8800984e2 100644 --- a/src/atlas/array/native/NativeIndexView.cc +++ b/src/atlas/array/native/NativeIndexView.cc @@ -25,6 +25,13 @@ IndexView::IndexView( Value* data, const idx_t shape[1] ) : data_( shape_[0] = shape[0]; } +template +IndexView::IndexView( Value* data, const idx_t shape[1], const idx_t strides[1] ) : + data_( const_cast( data ) ) { + strides_[0] = strides[0]; + shape_[0] = shape[0]; +} + template void IndexView::dump( std::ostream& os ) const { os << "size: " << size() << " , values: "; diff --git a/src/atlas/array/native/NativeIndexView.h b/src/atlas/array/native/NativeIndexView.h index aaed2fd6f..6388d330b 100644 --- a/src/atlas/array/native/NativeIndexView.h +++ b/src/atlas/array/native/NativeIndexView.h @@ -114,6 +114,8 @@ class IndexView { public: IndexView( Value* data, const idx_t shape[Rank] ); + IndexView( Value* data, const idx_t shape[Rank], const idx_t strides[Rank] ); + // -- Access methods template @@ -187,6 +189,19 @@ class IndexView { idx_t shape_[Rank]; }; +template +class LocalIndexView : public IndexView { + using Base = IndexView; + +public: + using Base::Base; +}; + +#undef INDEX_REF +#undef FROM_FORTRAN +#undef TO_FORTRAN + + //------------------------------------------------------------------------------------------------------ } // namespace array diff --git a/src/atlas/mesh/Elements.cc b/src/atlas/mesh/Elements.cc index 57e1f8aad..a69db9f0f 100644 --- a/src/atlas/mesh/Elements.cc +++ b/src/atlas/mesh/Elements.cc @@ -166,17 +166,17 @@ idx_t Elements::add( const idx_t nb_elements ) { template <> -array::IndexView Elements::indexview( Field& field ) const { - auto local_view = array::make_host_view( field ).slice( - array::Range{begin(), begin() + size()}, array::Range::all() ); - return array::IndexView( local_view.data(), local_view.shape() ); +array::LocalIndexView Elements::indexview( Field& field ) const { + auto local_view = array::make_host_view( field ).slice( + array::Range{begin(), begin() + size()} ); + return array::LocalIndexView( local_view.data(), local_view.shape(), local_view.strides() ); } template <> -array::IndexView Elements::indexview( const Field& field ) const { - auto local_view = array::make_host_view( field ).slice( - array::Range{begin(), begin() + size()}, array::Range::all() ); - return array::IndexView( local_view.data(), local_view.shape() ); +array::LocalIndexView Elements::indexview( const Field& field ) const { + auto local_view = array::make_host_view( field ).slice( + array::Range{begin(), begin() + size()} ); + return array::LocalIndexView( local_view.data(), local_view.shape(), local_view.strides() ); } //----------------------------------------------------------------------------- diff --git a/src/atlas/mesh/Elements.h b/src/atlas/mesh/Elements.h index 53cf7464c..b4f99b59a 100644 --- a/src/atlas/mesh/Elements.h +++ b/src/atlas/mesh/Elements.h @@ -124,10 +124,10 @@ class Elements : public util::Object { array::LocalView view( Field& ) const; template - array::IndexView indexview( const Field& ) const; + array::LocalIndexView indexview( const Field& ) const; template - array::IndexView indexview( Field& ) const; + array::LocalIndexView indexview( Field& ) const; idx_t add( const idx_t nb_elements ); From 78254847515681c2c4a5602067ad6bfac35ec57b Mon Sep 17 00:00:00 2001 From: Tiago Quintino Date: Fri, 20 Sep 2019 14:42:59 +0100 Subject: [PATCH 30/40] Update to eckit 1.4 --- CMakeLists.txt | 2 +- src/apps/atlas-grids.cc | 2 +- src/tests/grid/test_state.cc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 61f542ad7..3ba0d6699 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,7 @@ ecbuild_declare_project() ### eckit -ecbuild_use_package( PROJECT eckit VERSION 0.21.0 REQUIRED ) +ecbuild_use_package( PROJECT eckit VERSION 1.4 REQUIRED ) ecbuild_debug( " ECKIT_FEATURES : [${ECKIT_FEATURES}]" ) # options & dependencies diff --git a/src/apps/atlas-grids.cc b/src/apps/atlas-grids.cc index 65d976fcb..bd95721a4 100644 --- a/src/apps/atlas-grids.cc +++ b/src/apps/atlas-grids.cc @@ -19,7 +19,7 @@ #include "eckit/filesystem/PathName.h" #include "eckit/log/Bytes.h" #include "eckit/log/Log.h" -#include "eckit/parser/JSON.h" +#include "eckit/log/JSON.h" #include "eckit/types/FloatCompare.h" #include "atlas/grid.h" diff --git a/src/tests/grid/test_state.cc b/src/tests/grid/test_state.cc index 47a44b36e..7413f0cd7 100644 --- a/src/tests/grid/test_state.cc +++ b/src/tests/grid/test_state.cc @@ -11,7 +11,7 @@ #include #include -#include "eckit/parser/JSON.h" +#include "eckit/log/JSON.h" #include "eckit/parser/JSONParser.h" #include "atlas/array/ArrayView.h" From 049dcb44860d16b153e103500aa792e99b91e639 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 20 Sep 2019 15:29:33 +0100 Subject: [PATCH 31/40] Stay backwards compatibile with eckit 1.0.2 for now --- CMakeLists.txt | 2 +- src/apps/atlas-grids.cc | 8 +++++++- src/atlas/trans/ifs/TransIFS.cc | 5 +++++ src/atlas/trans/local/TransLocal.cc | 4 ++++ src/atlas/util/Config.cc | 9 ++++++++- src/atlas/util/Metadata.cc | 6 ++++++ src/tests/grid/test_state.cc | 6 ++++++ 7 files changed, 37 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ba0d6699..0f89a7f0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,7 @@ ecbuild_declare_project() ### eckit -ecbuild_use_package( PROJECT eckit VERSION 1.4 REQUIRED ) +ecbuild_use_package( PROJECT eckit VERSION 1.0.2 REQUIRED ) ecbuild_debug( " ECKIT_FEATURES : [${ECKIT_FEATURES}]" ) # options & dependencies diff --git a/src/apps/atlas-grids.cc b/src/apps/atlas-grids.cc index bd95721a4..968a7fe42 100644 --- a/src/apps/atlas-grids.cc +++ b/src/apps/atlas-grids.cc @@ -15,11 +15,17 @@ #include #include +#include "eckit/eckit_version.h" +#if 10000 * ECKIT_MAJOR_VERSION + 100 * ECKIT_MINOR_VERSION < 10400 +#include "eckit/parser/JSON.h" +#else +#include "eckit/log/JSON.h" +#endif + #include "eckit/config/Resource.h" #include "eckit/filesystem/PathName.h" #include "eckit/log/Bytes.h" #include "eckit/log/Log.h" -#include "eckit/log/JSON.h" #include "eckit/types/FloatCompare.h" #include "atlas/grid.h" diff --git a/src/atlas/trans/ifs/TransIFS.cc b/src/atlas/trans/ifs/TransIFS.cc index e1783dc5b..36812866d 100644 --- a/src/atlas/trans/ifs/TransIFS.cc +++ b/src/atlas/trans/ifs/TransIFS.cc @@ -8,7 +8,12 @@ * nor does it submit to any jurisdiction. */ +#include "eckit/eckit_version.h" +#if 10000 * ECKIT_MAJOR_VERSION + 100 * ECKIT_MINOR_VERSION < 10400 #include "eckit/parser/JSON.h" +#else +#include "eckit/log/JSON.h" +#endif #include "transi/trans.h" diff --git a/src/atlas/trans/local/TransLocal.cc b/src/atlas/trans/local/TransLocal.cc index 87e9ef100..d112249f3 100644 --- a/src/atlas/trans/local/TransLocal.cc +++ b/src/atlas/trans/local/TransLocal.cc @@ -20,7 +20,11 @@ #include "eckit/linalg/LinearAlgebra.h" #include "eckit/linalg/Matrix.h" #include "eckit/log/Bytes.h" +#if 10000 * ECKIT_MAJOR_VERSION + 100 * ECKIT_MINOR_VERSION < 10400 #include "eckit/parser/JSON.h" +#else +#include "eckit/log/JSON.h" +#endif #include "eckit/types/FloatCompare.h" #include "atlas/array.h" diff --git a/src/atlas/util/Config.cc b/src/atlas/util/Config.cc index d0d6b74e9..174124d86 100644 --- a/src/atlas/util/Config.cc +++ b/src/atlas/util/Config.cc @@ -15,8 +15,15 @@ #include #include -#include "eckit/filesystem/PathName.h" + +#include "eckit/eckit_version.h" +#if 10000 * ECKIT_MAJOR_VERSION + 100 * ECKIT_MINOR_VERSION < 10400 #include "eckit/parser/JSON.h" +#else +#include "eckit/log/JSON.h" +#endif + +#include "eckit/filesystem/PathName.h" #include "eckit/parser/YAMLParser.h" #include "atlas/grid/Grid.h" diff --git a/src/atlas/util/Metadata.cc b/src/atlas/util/Metadata.cc index 055935555..0385146d8 100644 --- a/src/atlas/util/Metadata.cc +++ b/src/atlas/util/Metadata.cc @@ -14,7 +14,13 @@ #include #include +#include "eckit/eckit_version.h" +#if 10000 * ECKIT_MAJOR_VERSION + 100 * ECKIT_MINOR_VERSION < 10400 #include "eckit/parser/JSON.h" +#else +#include "eckit/log/JSON.h" +#endif + #include "eckit/parser/JSONParser.h" #include "eckit/utils/Hash.h" diff --git a/src/tests/grid/test_state.cc b/src/tests/grid/test_state.cc index 7413f0cd7..b50f6a37f 100644 --- a/src/tests/grid/test_state.cc +++ b/src/tests/grid/test_state.cc @@ -11,7 +11,13 @@ #include #include +#include "eckit/eckit_version.h" +#if 10000 * ECKIT_MAJOR_VERSION + 100 * ECKIT_MINOR_VERSION < 10400 +#include "eckit/parser/JSON.h" +#else #include "eckit/log/JSON.h" +#endif + #include "eckit/parser/JSONParser.h" #include "atlas/array/ArrayView.h" From d32fec36469f86ba17cdba116e433185934af4f2 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Fri, 20 Sep 2019 16:06:29 +0100 Subject: [PATCH 32/40] ATLAS-245 Test reordering for unstructured meshes --- src/atlas/mesh/Mesh.h | 2 +- src/atlas/mesh/detail/MeshImpl.cc | 4 +- src/atlas/mesh/detail/MeshImpl.h | 6 +- src/atlas/output/detail/GmshIO.cc | 53 +- src/tests/mesh/CMakeLists.txt | 46 +- src/tests/mesh/test_mesh_reorder.cc | 79 +- .../mesh/test_mesh_reorder_unstructured.geo | 11 + .../mesh/test_mesh_reorder_unstructured.msh | 746 ++++++++++++++++++ 8 files changed, 893 insertions(+), 54 deletions(-) create mode 100644 src/tests/mesh/test_mesh_reorder_unstructured.geo create mode 100644 src/tests/mesh/test_mesh_reorder_unstructured.msh diff --git a/src/atlas/mesh/Mesh.h b/src/atlas/mesh/Mesh.h index 0f5f2f233..960db76cb 100644 --- a/src/atlas/mesh/Mesh.h +++ b/src/atlas/mesh/Mesh.h @@ -117,7 +117,7 @@ class Mesh : public util::ObjectHandle { const Polygon& polygon( idx_t halo = 0 ) const { return get()->polygon( halo ); } - const Grid& grid() const { return get()->grid(); } + const Grid grid() const { return get()->grid(); } private: // methods friend std::ostream& operator<<( std::ostream& s, const Mesh& p ) { diff --git a/src/atlas/mesh/detail/MeshImpl.cc b/src/atlas/mesh/detail/MeshImpl.cc index de0ff08db..285402ebc 100644 --- a/src/atlas/mesh/detail/MeshImpl.cc +++ b/src/atlas/mesh/detail/MeshImpl.cc @@ -108,9 +108,9 @@ void MeshImpl::setProjection( const Projection& projection ) { } void MeshImpl::setGrid( const Grid& grid ) { - grid_.reset( new Grid( grid ) ); + grid_ = grid; if ( not projection_ ) { - projection_ = grid_->projection(); + projection_ = grid_.projection(); } } diff --git a/src/atlas/mesh/detail/MeshImpl.h b/src/atlas/mesh/detail/MeshImpl.h index f4dd3468c..5552bba9c 100644 --- a/src/atlas/mesh/detail/MeshImpl.h +++ b/src/atlas/mesh/detail/MeshImpl.h @@ -13,6 +13,7 @@ #include #include +#include "atlas/grid/Grid.h" #include "atlas/mesh/PartitionPolygon.h" #include "atlas/mesh/detail/PartitionGraph.h" #include "atlas/projection/Projection.h" @@ -27,7 +28,6 @@ class Stream; } namespace atlas { -class Grid; class Mesh; namespace mesh { class PartitionPolygon; @@ -108,7 +108,7 @@ class MeshImpl : public util::Object { const PartitionPolygon& polygon( idx_t halo = 0 ) const; - const Grid& grid() const { return *grid_; } + const Grid grid() const { return grid_; } void attachObserver( MeshObserver& ) const; void detachObserver( MeshObserver& ) const; @@ -143,7 +143,7 @@ class MeshImpl : public util::Object { Projection projection_; - std::unique_ptr grid_; + Grid grid_; mutable util::ObjectHandle partition_graph_; diff --git a/src/atlas/output/detail/GmshIO.cc b/src/atlas/output/detail/GmshIO.cc index b682080eb..cdd022e84 100644 --- a/src/atlas/output/detail/GmshIO.cc +++ b/src/atlas/output/detail/GmshIO.cc @@ -617,11 +617,14 @@ void GmshIO::read( const PathName& file_path, Mesh& mesh ) const { mesh::Nodes& nodes = mesh.nodes(); - nodes.add( Field( "xyz", array::make_datatype(), array::make_shape( nb_nodes, 3 ) ) ); + //nodes.add( Field( "xyz", array::make_datatype(), array::make_shape( nb_nodes, 3 ) ) ); - array::ArrayView coords = array::make_view( nodes.field( "xyz" ) ); + // array::ArrayView coords = array::make_view( nodes.field( "xyz" ) ); + array::ArrayView xy = array::make_view( nodes.xy() ); + array::ArrayView lonlat = array::make_view( nodes.lonlat() ); array::ArrayView glb_idx = array::make_view( nodes.global_index() ); array::ArrayView part = array::make_view( nodes.partition() ); + array::ArrayView ghost = array::make_view( nodes.ghost() ); std::map glb_to_loc; int g; @@ -644,21 +647,17 @@ void GmshIO::read( const PathName& file_path, Mesh& mesh ) const { else { file >> g >> x >> y >> z; } - glb_idx( n ) = g; - coords( n, XX ) = x; - coords( n, YY ) = y; - coords( n, ZZ ) = z; - glb_to_loc[g] = n; - part( n ) = 0; - max_glb_idx = std::max( max_glb_idx, static_cast( g ) ); - xmax = std::max( x, xmax ); - zmax = std::max( z, zmax ); - } - if ( xmax < 4 * M_PI && zmax == 0. ) { - for ( idx_t n = 0; n < nb_nodes; ++n ) { - coords( n, XX ) *= rad2deg; - coords( n, YY ) *= rad2deg; - } + glb_idx( n ) = g; + xy( n, XX ) = x; + xy( n, YY ) = y; + lonlat( n, LON ) = x; + lonlat( n, LAT ) = y; + glb_to_loc[g] = n; + part( n ) = 0; + ghost( n ) = 0; + max_glb_idx = std::max( max_glb_idx, static_cast( g ) ); + xmax = std::max( x, xmax ); + zmax = std::max( z, zmax ); } for ( int i = 0; i < 3; ++i ) { std::getline( file, line ); @@ -735,11 +734,11 @@ void GmshIO::read( const PathName& file_path, Mesh& mesh ) const { mesh::Elements& quads = mesh.cells().elements( mesh.cells().add( make_element_type( QUAD ), nb_quads ) ); mesh::Elements& triags = mesh.cells().elements( mesh.cells().add( make_element_type( TRIAG ), nb_triags ) ); - mesh::Elements& edges = mesh.edges().elements( mesh.edges().add( make_element_type( LINE ), nb_edges ) ); + // mesh::Elements& edges = mesh.edges().elements( mesh.edges().add( make_element_type( LINE ), nb_edges ) ); mesh::BlockConnectivity& quad_nodes = quads.node_connectivity(); mesh::BlockConnectivity& triag_nodes = triags.node_connectivity(); - mesh::BlockConnectivity& edge_nodes = edges.node_connectivity(); + // mesh::BlockConnectivity& edge_nodes = edges.node_connectivity(); array::ArrayView quad_glb_idx = array::make_view( quads.global_index() ); array::ArrayView quad_part = array::make_view( quads.partition() ); @@ -747,8 +746,8 @@ void GmshIO::read( const PathName& file_path, Mesh& mesh ) const { array::ArrayView triag_glb_idx = array::make_view( triags.global_index() ); array::ArrayView triag_part = array::make_view( triags.partition() ); - array::ArrayView edge_glb_idx = array::make_view( edges.global_index() ); - array::ArrayView edge_part = array::make_view( edges.partition() ); + // array::ArrayView edge_glb_idx = array::make_view( edges.global_index() ); + // array::ArrayView edge_part = array::make_view( edges.partition() ); // Now read all elements file.seekg( position, std::ios::beg ); @@ -792,12 +791,12 @@ void GmshIO::read( const PathName& file_path, Mesh& mesh ) const { break; case ( LINE ): file >> gn0 >> gn1; - edge_glb_idx( edge ) = g; - edge_part( edge ) = part; - enodes[0] = glb_to_loc[gn0]; - enodes[1] = glb_to_loc[gn1]; - edge_nodes.set( edge, enodes ); - ++edge; + // edge_glb_idx( edge ) = g; + // edge_part( edge ) = part; + // enodes[0] = glb_to_loc[gn0]; + // enodes[1] = glb_to_loc[gn1]; + // edge_nodes.set( edge, enodes ); + // ++edge; break; case ( POINT ): file >> gn0; diff --git a/src/tests/mesh/CMakeLists.txt b/src/tests/mesh/CMakeLists.txt index de904dfbb..13dff96e4 100644 --- a/src/tests/mesh/CMakeLists.txt +++ b/src/tests/mesh/CMakeLists.txt @@ -88,7 +88,7 @@ ecbuild_add_test( TARGET atlas_test_mesh_node2cell ) -foreach( test connectivity stream_connectivity elements ll meshgen3d rgg mesh_reorder ) +foreach( test connectivity stream_connectivity elements ll meshgen3d rgg ) ecbuild_add_test( TARGET atlas_test_${test} SOURCES test_${test}.cc LIBS atlas @@ -96,6 +96,50 @@ foreach( test connectivity stream_connectivity elements ll meshgen3d rgg mesh_re ) endforeach() + +ecbuild_add_executable( TARGET atlas_test_mesh_reorder + SOURCES test_mesh_reorder.cc + LIBS atlas + NOINSTALL +) + +if( ECBUILD-432-fixed ) +# When ECBUILD-432 is fixed (already fixed in develop on top of ecbuild 3.0.0), we don't need TARGET_FILE +ecbuild_add_test( TARGET atlas_test_mesh_reorder_O16 + COMMAND atlas_test_mesh_reorder ARGS --grid O16 + ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} +) + +ecbuild_add_test( TARGET atlas_test_mesh_reorder_unstructured + COMMAND atlas_test_mesh_reorder ARGS --mesh ${CMAKE_CURRENT_SOURCE_DIR}/test_mesh_reorder_unstructured.msh --grid "unstructured" + ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} +) + +else() + # TO BE REMOVED! + + set( exe_atlas_test_mesh_reorder $ ) + if( CMAKE_CROSSCOMPILING_EMULATOR ) + set( exe_atlas_test_mesh_reorder ${CMAKE_CROSSCOMPILING_EMULATOR} ) + set( arg_atlas_test_mesh_reorder $ ) + endif() + + ecbuild_add_test( TARGET atlas_test_mesh_reorder_O16 + COMMAND ${exe_atlas_test_mesh_reorder} ARGS ${arg_atlas_test_mesh_reorder} --grid O16 + ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} + ) + + ecbuild_add_test( TARGET atlas_test_mesh_reorder_unstructured + COMMAND ${exe_atlas_test_mesh_reorder} ARGS ${arg_atlas_test_mesh_reorder} --mesh ${CMAKE_CURRENT_SOURCE_DIR}/test_mesh_reorder_unstructured.msh --grid "unstructured" + ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} + ) + +endif() + + + + + atlas_add_cuda_test( TARGET atlas_test_connectivity_kernel SOURCES test_connectivity_kernel.cu diff --git a/src/tests/mesh/test_mesh_reorder.cc b/src/tests/mesh/test_mesh_reorder.cc index eb53a8491..6912526e1 100644 --- a/src/tests/mesh/test_mesh_reorder.cc +++ b/src/tests/mesh/test_mesh_reorder.cc @@ -31,6 +31,9 @@ #include "tests/AtlasTestEnvironment.h" + +#include "atlas/output/detail/GmshIO.h" + //----------------------------------------------------------------- namespace atlas { @@ -38,7 +41,8 @@ namespace test { //----------------------------------------------------------------------------- -void outputConnectedCoordinates( const eckit::PathName& filepath, const array::ArrayView& xy ) { +void outputConnectedCoordinates( const eckit::PathName& filepath, const array::ArrayView& xy, + const std::string& title ) { const eckit::mpi::Comm& comm = atlas::mpi::comm(); int mpi_rank = int( comm.rank() ); int mpi_size = int( comm.size() ); @@ -104,6 +108,7 @@ void outputConnectedCoordinates( const eckit::PathName& filepath, const array::A "\n" "ax.set_xticks([0,45,90,135,180,225,270,315,360])" "\n" "ax.set_yticks([-90,-45,0,45,90])" "\n" "plt.grid()" + "\n" "plt.title('" << title << "')" "\n" "plt.show()"; } // clang-format on @@ -113,7 +118,7 @@ void outputConnectedCoordinates( const eckit::PathName& filepath, const array::A } void outputTriplets( const eckit::PathName& filepath, const std::vector& triplets, idx_t rows, - idx_t cols ) { + idx_t cols, const std::string& title, const std::string& xlabel, const std::string& ylabel ) { const eckit::mpi::Comm& comm = atlas::mpi::comm(); int mpi_rank = int( comm.rank() ); int mpi_size = int( comm.size() ); @@ -136,7 +141,7 @@ void outputTriplets( const eckit::PathName& filepath, const std::vector( "--grid", "O16" )}; +std::string grid_name() { + return eckit::Resource( "--grid", "O16" ); +} +Mesh get_mesh() { + if ( grid_name() != "unstructured" ) { + auto generate_mesh = StructuredMeshGenerator( util::Config( "patch_pole", false )( "triangulate", true ) ); + return generate_mesh( Grid{grid_name()} ); + } + else { + output::detail::GmshIO gmsh_reader; + return gmsh_reader.read( std::string{eckit::Resource( "--mesh", "" )} ); + } } void test_reordering( const util::Config& reorder_config, const std::vector& expected = {} ) { std::string type = reorder_config.getString( "type" ); + // clang-format off + std::string title = type == "none" ? "Default order" : + type == "hilbert" ? "Hilbert space filling curve" : + type == "reverse_cuthill_mckee" ? "Reverse Cuthill Mckee order": + type; + type = grid_name() + "_" + type; + // clang-format on + auto mesh = get_mesh(); - auto meshgenerator = StructuredMeshGenerator( util::Config( "patch_pole", false )( "triangulate", true ) ); - Mesh mesh = meshgenerator( grid() ); - auto reorder = mesh::actions::Reorder{reorder_config}; + auto reorder = mesh::actions::Reorder{reorder_config}; if ( expected.size() ) { const auto node_order = reorder.get()->computeNodesOrder( mesh ); @@ -254,20 +279,22 @@ void test_reordering( const util::Config& reorder_config, const std::vector reorder( mesh ); - mesh::actions::build_edges( mesh ); + bool sort_edges = false; + // bool sort_edges = false; + mesh::actions::build_edges( mesh, util::Config( "sort_edges", sort_edges ) ); auto xy = array::make_view( mesh.nodes().xy() ); - outputConnectedCoordinates( type + "_nodes.py", xy ); + outputConnectedCoordinates( type + "_nodes.py", xy, title ); output::Gmsh gmsh{type + ".msh", util::Config( "coordinates", "xy" )}; gmsh.write( mesh ); Field cell_centres = create_cell_centres( mesh ); auto cell_centres_xy = array::make_view( cell_centres ); - outputConnectedCoordinates( type + "_elements.py", cell_centres_xy ); + outputConnectedCoordinates( type + "_elements.py", cell_centres_xy, title ); Field edge_centres = create_edge_centres( mesh ); auto edge_centres_xy = array::make_view( edge_centres ); - outputConnectedCoordinates( type + "_edges.py", edge_centres_xy ); + outputConnectedCoordinates( type + "_edges.py", edge_centres_xy, title ); std::vector n2n_triplets; std::vector e2n_triplets; @@ -281,19 +308,27 @@ void test_reordering( const util::Config& reorder_config, const std::vector e2n_triplets.emplace_back( e, node_connectivity( e, 1 ), 1. ); } std::sort( n2n_triplets.begin(), n2n_triplets.end() ); - outputTriplets( type + "_n2n_triplets.py", n2n_triplets, mesh.nodes().size(), mesh.nodes().size() ); - outputTriplets( type + "_e2n_triplets.py", e2n_triplets, mesh.edges().size(), mesh.nodes().size() ); + outputTriplets( type + "_n2n_triplets.py", n2n_triplets, mesh.nodes().size(), mesh.nodes().size(), title, "nodes", + "nodes" ); + outputTriplets( type + "_e2n_triplets.py", e2n_triplets, mesh.edges().size(), mesh.nodes().size(), title, + "connected nodes", "edges" ); } CASE( "test_hilbert_reordering" ) { auto reorder_config = option::type( "hilbert" ) | util::Config( "recursion", 30 ); std::vector expected; - if ( grid().name() == "O16" && mpi::comm().size() == 1 ) { + if ( grid_name() == "O16" && mpi::comm().size() == 1 ) { // clang-format off expected = {0,20,1,21,45,74,73,44,72,104,105,141,140,180,224,225,181,182,226,227,142,106,107,143,183,228,184,229,230,185,145,144,108,76,47,75,46,22,2,23,3,77,48,78,49,24,4,25,5,26,51,80,79,50,111,148,112,113,150,149,190,191,236,235,234,189,233,232,187,188,147,110,146,109,186,231,279,280,332,331,388,448,449,450,389,390,451,452,391,334,333,281,282,335,336,283,284,285,338,337,394,395,455,456,454,453,393,392,517,586,587,518,519,520,521,589,590,588,661,662,663,739,740,738,737,660,659,735,736,734,733,656,657,658,585,516,515,584,583,514,513,581,582,654,655,732,731,730,729,653,652,728,727,725,726,649,650,651,578,577,509,510,511,579,580,512,447,446,386,387,278,277,330,329,276,328,384,385,445,444,443,442,382,383,327,275,274,326,325,273,272,324,380,381,441,440,504,505,572,573,574,506,507,508,576,575,647,648,724,723,722,646,645,644,720,721,800,801,802,882,881,880,957,956,1028,1029,1030,958,959,1031,1032,960,884,883,803,804,805,806,885,886,887,807,808,810,809,889,888,963,965,964,1036,1035,1034,962,961,1033,1101,1100,1164,1165,1102,1103,1104,1167,1166,1226,1227,1282,1281,1225,1224,1280,1279,1278,1222,1223,1163,1099,1098,1162,1161,1097,1096,1160,1220,1221,1277,1276,1328,1376,1377,1329,1330,1331,1378,1422,1462,1461,1421,1420,1460,1496,1497,1528,1556,1580,1557,1581,1529,1498,1499,1530,1558,1582,1559,1531,1500,1464,1425,1424,1463,1423,1380,1379,1332,1333,1381,1334,1382,1335,1336,1337,1384,1383,1427,1466,1426,1465,1501,1532,1583,1560,1533,1502,1503,1534,1584,1561,1585,1562,1535,1504,1469,1430,1429,1468,1467,1428,1385,1338,1339,1386,1387,1340,1289,1288,1233,1234,1175,1112,1111,1110,1174,1173,1109,1108,1172,1231,1232,1287,1286,1285,1230,1229,1284,1283,1228,1169,1168,1105,1106,1107,1170,1171,1040,969,968,1039,1038,1037,966,967,891,890,811,812,813,892,893,894,815,814,816,817,896,895,970,1041,1042,971,972,1043,1044,1045,974,973,899,898,897,818,819,820,821,822,901,900,975,1046,1047,976,977,1048,1049,978,903,902,823,824,825,826,904,905,906,827,828,830,829,908,907,981,983,982,1053,1052,1051,980,979,1050,1117,1116,1179,1180,1118,1119,1120,1182,1181,1240,1241,1295,1294,1239,1238,1293,1292,1291,1236,1237,1178,1115,1114,1177,1176,1113,1235,1290,1388,1341,1342,1343,1389,1432,1471,1470,1431,1505,1563,1586,1536,1506,1507,1537,1564,1587,1565,1538,1508,1473,1435,1434,1472,1433,1391,1390,1344,1345,1392,1346,1393,1347,1348,1349,1395,1394,1437,1475,1436,1474,1509,1539,1588,1566,1540,1510,1511,1541,1589,1567,1439,1477,1476,1438,1396,1350,1351,1397,1301,1247,1127,1126,1189,1188,1125,1124,1187,1245,1246,1300,1299,1298,1244,1243,1297,1296,1242,1184,1183,1121,1122,1123,1185,1186,1057,987,986,1056,1055,1054,984,985,910,909,831,832,833,911,912,913,835,834,836,837,915,914,988,1058,1059,989,990,1060,1061,991,917,916,838,839,759,681,680,758,757,755,756,678,679,605,604,534,535,536,606,607,537,471,470,409,297,351,350,296,295,349,407,408,469,468,467,466,405,406,348,294,347,346,293,292,345,403,404,465,464,530,599,600,601,531,532,533,603,602,675,676,677,754,753,752,674,673,750,751,749,748,671,672,598,529,528,597,596,527,526,595,668,669,670,747,746,745,744,667,666,743,742,741,664,665,592,591,522,523,524,593,594,525,459,460,398,397,458,457,396,339,286,287,340,341,288,289,343,342,399,461,462,400,401,463,402,344,290,291,242,241,196,155,117,154,116,153,194,195,240,239,193,238,237,192,151,114,152,115,82,52,81,27,6,28,53,83,54,84,29,7,8,30,56,86,85,55,118,156,197,243,198,244,245,199,157,119,120,158,246,200,201,247,159,121,87,57,31,9,10,32,11,33,59,90,89,58,88,122,123,161,160,202,248,249,203,204,250,251,162,124,125,163,205,252,206,253,254,207,165,164,126,92,61,91,60,34,12,35,13,93,62,94,63,36,14,37,15,38,65,96,95,64,129,168,130,131,170,169,212,213,260,259,258,211,257,256,209,210,167,128,166,127,208,255,305,306,360,359,418,480,481,482,419,420,483,484,421,362,361,307,308,363,364,309,310,311,366,365,424,425,487,488,486,485,423,422,551,622,623,552,553,554,555,625,626,624,699,700,701,779,780,778,777,698,697,775,776,774,773,694,695,696,621,550,549,620,619,548,547,617,618,692,693,772,771,770,769,691,690,768,767,765,766,687,688,689,614,613,543,544,545,615,616,546,479,478,416,417,304,303,358,357,302,356,414,415,477,476,475,474,412,413,355,301,300,354,353,299,298,352,410,411,473,472,538,539,608,609,610,540,541,542,612,611,685,686,764,763,762,684,683,682,760,761,840,841,842,920,919,918,993,992,1062,1063,1064,994,995,1065,1066,996,922,921,843,844,845,846,923,924,925,847,848,850,849,927,926,999,1001,1000,1070,1069,1068,998,997,1067,1133,1132,1194,1195,1134,1135,1136,1197,1196,1254,1255,1308,1307,1253,1252,1306,1305,1304,1250,1251,1193,1131,1130,1192,1191,1129,1128,1190,1248,1249,1303,1302,1352,1398,1399,1353,1354,1355,1400,1442,1480,1479,1441,1440,1478,1512,1513,1542,1568,1590,1569,1591,1543,1514,1515,1544,1570,1592,1571,1545,1516,1482,1445,1444,1481,1443,1402,1401,1356,1357,1403,1358,1404,1359,1360,1361,1406,1405,1447,1484,1446,1483,1517,1546,1593,1572,1547,1518,1519,1548,1594,1573,1595,1574,1549,1520,1487,1450,1449,1486,1485,1448,1407,1362,1363,1408,1409,1364,1315,1314,1261,1262,1205,1144,1143,1142,1204,1203,1141,1140,1202,1259,1260,1313,1312,1311,1258,1257,1310,1309,1256,1199,1198,1137,1138,1139,1200,1201,1074,1005,1004,1073,1072,1071,1002,1003,929,928,851,852,853,930,931,932,855,854,856,857,934,933,1006,1075,1076,1007,1008,1077,1078,1079,1010,1009,937,936,935,858,859,860,861,862,939,938,1011,1080,1081,1012,1013,1082,1083,1014,941,940,863,864,865,866,942,943,944,867,868,870,869,946,945,1017,1019,1018,1087,1086,1085,1016,1015,1084,1149,1148,1209,1210,1150,1151,1152,1212,1211,1268,1269,1321,1320,1267,1266,1319,1318,1317,1264,1265,1208,1147,1146,1207,1206,1145,1263,1316,1410,1365,1366,1367,1411,1452,1489,1488,1451,1521,1575,1596,1550,1522,1523,1551,1576,1597,1577,1552,1524,1491,1455,1454,1490,1453,1413,1412,1368,1369,1414,1370,1415,1371,1372,1373,1417,1416,1457,1493,1456,1492,1525,1553,1598,1578,1554,1526,1527,1555,1599,1579,1459,1495,1494,1458,1418,1374,1375,1419,1327,1275,1159,1158,1219,1218,1157,1156,1217,1273,1274,1326,1325,1324,1272,1271,1323,1322,1270,1214,1213,1153,1154,1155,1215,1216,1091,1023,1022,1090,1089,1088,1020,1021,948,947,871,872,873,949,950,951,875,874,876,877,953,952,1024,1092,1093,1025,1026,1094,1095,1027,955,954,878,879,799,719,718,798,797,795,796,716,717,641,640,568,569,570,642,643,571,503,502,439,323,379,378,322,321,377,437,438,501,500,499,498,435,436,376,320,375,374,319,318,373,433,434,497,496,564,635,636,637,565,566,567,639,638,713,714,715,794,793,792,712,711,790,791,789,788,709,710,634,563,562,633,632,561,560,631,706,707,708,787,786,785,784,705,704,783,782,781,702,703,628,627,556,557,558,629,630,559,491,492,428,427,490,489,426,367,312,313,368,369,314,315,371,370,429,493,494,430,431,495,432,372,316,317,266,265,218,175,135,174,134,173,216,217,264,263,215,262,261,214,171,132,172,133,98,66,97,39,16,40,67,99,68,100,41,17,18,42,70,102,101,69,136,176,219,267,220,268,269,221,177,137,138,178,270,222,223,271,179,139,103,71,43,19,1600,1601,1602,1603,1604,1605,1606,1607,1608,1609,1610,1611,1612,1613,1614,1615,1616,1617,1618,1619,1620,1621,1622,1623,1624,1625,1626,1627,1628,1629,1630,1631}; - // clang-format on - } else { + // clang-format on + } + else if ( grid_name() == "unstructured" && mpi::comm().size() == 1 ) { + // clang-format off + expected = {0,241,53,128,4,5,234,74,177,52,111,51,50,141,173,83,224,147,57,114,158,79,116,70,228,178,197,93,143,6,185,137,7,8,183,205,95,193,145,9,10,236,11,1,243,12,131,13,214,76,150,226,119,14,190,15,112,87,211,123,59,127,155,171,96,220,54,196,103,63,201,67,239,16,174,17,217,98,139,223,210,122,18,208,19,20,134,88,200,105,238,163,120,71,133,132,81,121,62,180,138,100,187,222,66,161,91,213,84,194,109,49,48,203,47,188,189,102,181,80,159,46,45,44,207,168,108,216,85,186,43,167,42,41,142,110,204,65,195,89,218,237,164,125,153,151,152,148,61,192,165,115,56,230,73,135,106,64,154,72,21,162,231,22,176,101,157,209,140,99,23,175,24,240,68,202,104,221,55,97,219,156,78,117,58,124,212,86,113,25,191,26,118,225,149,75,215,27,129,28,242,2,29,233,30,31,144,199,92,227,179,69,136,184,32,33,182,34,146,94,206,198,229,126,166,90,170,60,160,107,232,82,169,40,39,38,172,77,235,35,36,130,37,244,3}; + // clang-format on + } + else { Log::warning() << "No ordering will be tested" << std::endl; } test_reordering( reorder_config, expected ); @@ -303,11 +338,16 @@ CASE( "test_reverse_cuthill_mckee_reordering" ) { auto reorder_config = option::type( "reverse_cuthill_mckee" ); std::vector expected; - if ( grid().name() == "O16" && mpi::comm().size() == 1 ) { + if ( grid_name() == "O16" && mpi::comm().size() == 1 ) { // clang-format off expected = {1579,1599,1555,1554,1578,1598,1527,1526,1525,1553,1577,1597,1495,1494,1493,1492,1524,1552,1576,1596,1459,1458,1457,1456,1455,1491,1523,1551,1575,1595,1419,1418,1417,1416,1415,1414,1454,1490,1522,1550,1574,1573,1594,1375,1374,1373,1372,1371,1370,1369,1413,1453,1489,1521,1549,1548,1547,1572,1593,1327,1326,1325,1324,1323,1322,1321,1320,1368,1412,1452,1488,1520,1519,1518,1517,1546,1571,1592,1275,1274,1273,1272,1271,1270,1269,1268,1267,1319,1367,1411,1451,1487,1486,1485,1484,1483,1516,1545,1570,1591,1219,1218,1217,1216,1215,1214,1213,1212,1211,1210,1266,1318,1366,1410,1450,1449,1448,1447,1446,1445,1482,1515,1544,1569,1590,1159,1095,1158,1157,1156,1155,1154,1153,1152,1151,1150,1149,1209,1148,1265,1317,1365,1409,1408,1407,1406,1405,1404,1589,1403,1444,1481,1514,1543,1568,1567,1027,955,799,879,1094,1026,1093,1092,1091,1090,1089,1088,1087,1086,1085,1084,1083,1208,1147,1264,1316,1364,1363,1362,1361,1360,1359,1358,1566,1588,1357,1402,1443,1480,1513,1542,1541,1540,878,954,719,798,1082,1025,953,1024,1023,1022,1021,1020,1019,1018,1017,1016,1015,1014,1013,1207,1146,1263,1315,1314,1313,1312,1311,1310,1309,1308,1539,1565,1587,1307,1356,1401,1442,1479,1512,1511,1510,1509,877,797,643,718,1081,1012,876,952,951,950,949,948,947,946,945,944,943,942,941,940,939,1206,1145,1262,1261,1260,1259,1258,1257,1256,1255,1254,1508,1538,1564,1586,1253,1306,1355,1400,1441,1478,1477,1476,1475,1474,796,717,571,642,1080,1011,938,874,875,872,873,870,871,868,869,866,867,864,865,862,863,861,1205,1144,1204,1203,1202,1201,1200,1199,1198,1197,1196,1473,1507,1537,1563,1585,1584,1583,1582,1580,1581,1195,1252,1305,1354,1399,1440,1439,1438,1437,1436,1435,795,716,641,503,570,1079,1010,860,937,794,793,792,791,790,789,788,787,786,785,784,783,782,780,781,1143,1078,1142,1141,1140,1139,1138,1137,1136,1135,1134,1133,1434,1472,1506,1536,1562,1561,1560,1559,1558,1556,1557,1194,1132,1251,1304,1353,1398,1397,1396,1395,1394,1393,1392,715,640,569,439,502,1009,936,859,779,714,713,712,711,710,709,708,707,706,705,704,703,702,701,1077,1008,1076,1075,1074,1073,1072,1071,1070,1069,1068,1067,1066,1391,1433,1471,1505,1535,1534,1533,1532,1531,1530,1528,1529,1193,1131,1250,1303,1352,1351,1350,1349,1348,1347,1346,1345,639,568,501,379,438,858,935,778,700,638,637,636,635,634,633,632,631,630,629,628,627,626,1065,1007,934,1006,1005,1004,1003,1002,1001,1000,999,998,997,996,995,1344,1390,1432,1470,1504,1503,1502,1501,1500,1499,1498,1496,1497,1192,1130,1249,1302,1301,1300,1299,1298,1297,1296,1295,1294,567,500,437,323,378,857,777,699,625,566,565,564,563,562,561,560,559,558,557,556,555,1064,994,856,933,932,931,930,929,928,927,926,925,924,923,922,921,920,1293,1343,1389,1431,1469,1468,1467,1466,1465,1464,1463,1462,1460,1461,1191,1129,1248,1247,1246,1245,1244,1243,1242,1241,1240,1239,499,436,377,271,322,776,698,624,554,498,497,496,495,494,493,492,491,490,489,488,1063,993,919,854,855,852,853,850,851,848,849,846,847,844,845,842,843,841,1238,1292,1342,1388,1430,1429,1428,1427,1426,1425,1424,1423,1422,1420,1421,1190,1128,1189,1188,1187,1186,1185,1184,1183,1182,1181,1180,435,376,321,223,270,775,697,623,553,487,434,433,432,431,430,429,428,427,426,425,1062,992,840,918,774,773,772,771,770,769,768,767,766,765,764,763,762,760,761,1179,1237,1291,1341,1387,1386,1385,1384,1383,1382,1381,1380,1379,1378,1376,1377,1127,1061,1126,1125,1124,1123,1122,1121,1120,1119,1118,1117,1116,375,320,269,179,222,696,622,552,486,424,374,373,372,371,370,369,368,367,366,991,917,839,759,695,694,693,692,691,690,689,688,687,686,685,684,683,682,1178,1236,1290,1340,1339,1338,1337,1336,1335,1334,1333,1332,1331,1330,1328,1329,1115,1060,990,1059,1058,1057,1056,1055,1054,1053,1052,1051,1050,1049,319,268,221,139,178,621,551,485,423,365,318,317,316,315,314,313,312,311,838,916,758,681,620,619,618,617,616,615,614,613,612,611,610,609,608,1177,1235,1289,1288,1287,1286,1285,1284,1283,1282,1281,1280,1279,1278,1276,1277,1048,1114,989,915,988,987,986,985,984,983,982,981,980,979,978,977,267,220,177,103,138,550,484,422,364,310,266,265,264,263,262,261,260,837,757,680,607,549,548,547,546,545,544,543,542,541,540,539,538,1176,1234,1233,1232,1231,1230,1229,1228,1227,1226,1225,1224,1223,1222,1220,1221,1047,976,1113,836,914,913,912,911,910,909,908,907,906,905,904,903,902,901,219,176,137,71,102,483,421,363,309,259,218,217,216,215,214,213,756,679,606,537,482,481,480,479,478,477,476,475,474,473,472,1175,1174,1173,1172,1171,1170,1169,1168,1167,1166,1165,1164,1163,1162,1160,1161,1046,975,1112,900,834,835,832,833,830,831,828,829,826,827,824,825,822,823,821,175,136,101,43,70,420,362,308,258,212,174,173,172,171,170,755,678,605,536,471,419,418,417,416,415,414,413,412,411,410,1111,1110,1109,1108,1107,1106,1105,1104,1103,1102,1101,1100,1099,1098,1096,1097,1045,974,1044,820,899,754,753,752,751,750,749,748,747,746,745,744,743,742,740,741,19,135,100,69,42,361,307,257,211,169,134,133,132,131,677,604,535,470,409,360,359,358,357,356,355,354,353,352,1043,1042,1041,1040,1039,1038,1037,1036,1035,1034,1033,1032,1031,1030,1028,1029,973,898,972,819,739,676,675,674,673,672,671,670,669,668,667,666,665,664,663,18,99,68,41,306,256,210,168,130,98,97,96,603,534,469,408,351,305,304,303,302,301,300,299,298,971,970,969,968,967,966,965,964,963,962,961,960,956,959,958,957,818,897,896,738,662,602,601,600,599,598,597,596,595,594,593,592,591,590,17,67,40,255,209,167,129,95,66,65,533,468,407,350,297,254,253,252,251,250,249,248,895,894,893,892,891,890,889,888,887,886,885,884,880,881,883,882,816,817,737,661,589,532,531,530,529,528,527,526,525,524,523,522,521,800,16,39,208,166,128,94,64,38,467,406,349,296,247,207,206,205,204,203,202,814,815,812,813,810,811,808,809,806,807,804,805,801,802,803,736,660,588,520,466,465,464,463,462,461,460,459,458,457,456,720,15,165,127,93,63,37,405,348,295,246,201,164,163,162,161,160,735,734,733,732,731,730,729,728,727,726,725,724,723,721,722,659,587,519,455,404,403,402,401,400,399,398,397,396,395,644,14,126,92,62,36,347,294,245,200,159,125,124,123,122,658,657,656,655,654,653,652,651,650,649,648,647,646,645,586,518,454,394,346,345,344,343,342,341,340,339,338,572,13,91,61,35,293,244,199,158,121,90,89,88,585,584,583,582,581,580,579,578,577,576,575,574,573,517,453,393,337,292,291,290,289,288,287,286,285,504,12,60,34,243,198,157,120,87,59,58,516,515,514,513,512,511,510,509,508,507,506,505,452,392,336,284,242,241,240,239,238,237,236,440,11,33,197,156,119,86,57,32,451,450,449,448,447,446,445,444,443,442,441,391,335,283,235,196,195,194,193,192,191,380,10,155,118,85,56,31,390,389,388,387,386,385,384,383,382,381,334,282,234,190,154,153,152,151,150,324,9,117,84,55,30,333,332,331,330,329,328,327,326,325,281,233,189,149,116,115,114,113,272,8,83,54,29,280,279,278,277,276,275,274,273,232,188,148,112,82,81,80,224,7,53,28,231,230,229,228,227,226,225,187,147,111,79,52,51,180,6,27,186,185,184,183,182,181,146,110,78,50,26,140,5,145,144,143,142,141,109,77,49,25,104,4,108,107,106,105,76,48,24,72,3,75,74,73,47,23,44,2,46,45,22,20,1,21,0,1631,1630,1629,1628,1627,1626,1625,1624,1623,1622,1621,1620,1619,1618,1617,1616,1615,1614,1613,1612,1611,1610,1609,1608,1607,1606,1605,1604,1603,1602,1601,1600}; // clang-format on } + else if ( grid_name() == "unstructured" && mpi::comm().size() == 1 ) { + // clang-format off + expected = {2,242,29,28,33,32,31,30,184,129,233,34,35,27,215,182,136,144,227,92,75,3,36,244,94,146,235,26,118,229,69,199,179,149,37,130,198,206,77,160,25,86,191,24,117,126,58,225,38,172,60,107,232,212,113,22,175,23,166,156,78,124,39,170,169,82,21,231,68,240,176,99,90,153,97,219,202,40,164,142,204,237,20,19,162,101,140,209,125,55,104,41,65,110,218,89,208,18,134,72,157,64,151,221,135,17,42,195,167,148,152,88,217,122,200,154,106,73,230,16,174,43,85,186,216,165,61,210,98,223,105,56,115,238,81,15,239,112,44,108,168,192,207,63,139,163,133,120,132,121,14,190,13,12,87,67,211,45,80,159,201,103,71,196,180,62,181,1,243,119,214,131,150,226,123,46,102,96,220,54,138,91,213,189,11,76,236,193,59,155,47,188,171,187,222,161,84,10,95,145,205,127,79,48,203,66,100,109,9,183,158,70,116,49,194,114,141,8,137,228,57,178,50,83,224,173,7,93,185,147,197,51,111,6,143,74,52,177,5,234,128,53,4,241,0}; + // clang-format on + } else { Log::warning() << "No ordering will be tested" << std::endl; } @@ -319,7 +359,6 @@ CASE( "test_none_reordering" ) { test_reordering( reorder_config ); } - //----------------------------------------------------------------------------- } // namespace test diff --git a/src/tests/mesh/test_mesh_reorder_unstructured.geo b/src/tests/mesh/test_mesh_reorder_unstructured.geo new file mode 100644 index 000000000..c509fad80 --- /dev/null +++ b/src/tests/mesh/test_mesh_reorder_unstructured.geo @@ -0,0 +1,11 @@ +cl__1 = 20; +Point(1) = {0, 90, 0, 20}; +Point(2) = {0, -90, 0, 20}; +Point(3) = {360, -90, 0, 20}; +Point(4) = {360, 90, 0, 20}; +Line(1) = {1, 2}; +Line(2) = {2, 3}; +Line(3) = {3, 4}; +Line(4) = {4, 1}; +Line Loop(6) = {1, 2, 3, 4}; +Plane Surface(6) = {6}; diff --git a/src/tests/mesh/test_mesh_reorder_unstructured.msh b/src/tests/mesh/test_mesh_reorder_unstructured.msh new file mode 100644 index 000000000..4d003841d --- /dev/null +++ b/src/tests/mesh/test_mesh_reorder_unstructured.msh @@ -0,0 +1,746 @@ +$MeshFormat +2.2 0 8 +$EndMeshFormat +$Nodes +245 +1 0 90 0 +2 0 -90 0 +3 360 -90 0 +4 360 90 0 +5 0 70.00000000003392 0 +6 0 50.00000000006785 0 +7 0 30.00000000010177 0 +8 0 10.0000000001357 0 +9 0 -9.999999999909321 0 +10 0 -30.00000000003331 0 +11 0 -50.00000000006784 0 +12 0 -70.00000000003394 0 +13 19.99999999999056 -90 0 +14 39.99999999998938 -90 0 +15 59.99999999996113 -90 0 +16 79.99999999992301 -90 0 +17 99.99999999988489 -90 0 +18 119.9999999998468 -90 0 +19 139.9999999998087 -90 0 +20 159.9999999997705 -90 0 +21 179.999999999738 -90 0 +22 199.9999999998522 -90 0 +23 219.999999999972 -90 0 +24 240.0000000000917 -90 0 +25 260.0000000001905 -90 0 +26 280.0000000001525 -90 0 +27 300.0000000001144 -90 0 +28 320.0000000000762 -90 0 +29 340.0000000000381 -90 0 +30 360 -70.00000000003392 0 +31 360 -50.00000000006785 0 +32 360 -30.00000000010177 0 +33 360 -10.0000000001357 0 +34 360 9.999999999909321 0 +35 360 30.00000000003331 0 +36 360 50.00000000006784 0 +37 360 70.00000000003394 0 +38 340.0000000000339 90 0 +39 320.0000000000679 90 0 +40 300.0000000001017 90 0 +41 280.0000000001357 90 0 +42 260.0000000001696 90 0 +43 240.0000000002036 90 0 +44 220.0000000002375 90 0 +45 200.0000000002714 90 0 +46 180.0000000002998 90 0 +47 160.0000000001814 90 0 +48 140.0000000000574 90 0 +49 119.9999999999334 90 0 +50 99.99999999983038 90 0 +51 79.99999999986431 90 0 +52 59.99999999989825 90 0 +53 39.99999999993213 90 0 +54 19.99999999996601 90 0 +55 112.4953125547577 -1.034528668769918 0 +56 248.5589743008294 -1.620697276940565 0 +57 180.4071501967631 -18.22910670794318 0 +58 59.11053170181781 31.77798046940436 0 +59 300.8256596134945 -30.96440128614297 0 +60 58.39694233183459 -30.25305092595709 0 +61 302.9087117746154 33.50632748915397 0 +62 203.9829670430084 34.10462326104238 0 +63 150.6244480790695 35.84860845211919 0 +64 134.8021586205883 -38.10097623206762 0 +65 223.5527920684021 -38.24222456969687 0 +66 250.8574744145136 47.13333177013499 0 +67 100.2017140109403 42.82011254711175 0 +68 95.63293464557739 -49.58398509784539 0 +69 264.3670653544635 -49.58398509785408 0 +70 322.3601731870527 -1.754247205894154 0 +71 36.54558067551533 0.1430926795337751 0 +72 144.723460724785 -0.9346546885142281 0 +73 189.075003416131 -54.84230713964594 0 +74 213.1958112377294 -1.687421962623934 0 +75 33.37872916233778 57.37234908530847 0 +76 327.6287099694065 -58.04307780246888 0 +77 33.44181733019612 -56.7962169956472 0 +78 326.0790765456983 56.79405815048837 0 +79 283.1431773651676 -2.878918449256942 0 +80 74.81146146846677 0.8979657382190673 0 +81 178.4569546862494 56.02496411916671 0 +82 177.6401525696667 16.57897066064375 0 +83 277.8888269430748 57.16614962785665 0 +84 74.836062242521 59.25685705398419 0 +85 129.1852690897412 59.62399297546347 0 +86 219.6200624280308 60.0103818673746 0 +87 291.6812123047919 -60.76279650108297 0 +88 68.31878769524958 -60.76279650106879 0 +89 159.3195623213304 -60.87484158694937 0 +90 231.5418574355527 22.77777777777593 0 +91 270.837701251436 26.76840244617329 0 +92 123.8279693600856 25.69367415857216 0 +93 330.5638679018998 -27.15091279865151 0 +94 29.50092545112746 27.13698624158539 0 +95 330.793275881726 27.58245844475076 0 +96 28.73519075051649 -27.6044653209308 0 +97 90.84170513081088 -21.73538371157863 0 +98 267.3289738432465 -21.51739650451633 0 +99 119.9955450739089 -62.6329425933069 0 +100 240.0044549261331 -62.63294259331543 0 +101 91.85727181804643 20.53822201813352 0 +102 208.5502545774449 -63.5256325249602 0 +103 156.9444055171627 65.39516222589877 0 +104 119.7814224601012 -25.96188764517574 0 +105 245.3961222216334 -22.04558297697257 0 +106 163.8524070159093 -34.01678123927347 0 +107 202.6543992858681 -27.18804077782921 0 +108 303.5189734657069 65.94436035098065 0 +109 200.1215839756683 65.59875277128859 0 +110 107.2449209075832 62.63576947907363 0 +111 250.0000000001865 68.9027777777726 0 +112 54.40137577960949 67.51244564377897 0 +113 88.93060654976014 -69.46828122644406 0 +114 271.0693934503005 -69.46828122644862 0 +115 79.94344062348884 37.81517547423627 0 +116 190.8403793318681 0.2258181404442989 0 +117 56.04882305877766 9.468169341151807 0 +118 303.0934679388384 -10.21553080163002 0 +119 309.7902720427637 -71.11629968188154 0 +120 50.2877547283794 -71.02542061431706 0 +121 138.738502832544 -19.90497521535342 0 +122 169.4026025581064 35.09113256695249 0 +123 140.138641995089 -68.90381589231369 0 +124 81.33792382901635 -36.10799403302993 0 +125 279.0495254210482 -36.49012597064144 0 +126 251.0536153550176 19.8137135444985 0 +127 306.4915328743106 10.47427715965349 0 +128 55.78341652883249 -9.571404018333382 0 +129 19.87751692386441 70.15958456566064 0 +130 339.760524254926 -69.76052425492593 0 +131 340.169225738845 70.4174939095748 0 +132 19.77597919895636 -70.23639645160497 0 +133 158.1677043378212 17.24211445756736 0 +134 168.0931478065708 -0.4301236895996373 0 +135 172.9467963247845 -70.57092584485422 0 +136 222.1234458911247 -19.27659862402142 0 +137 341.7637681464647 -0.250606743750371 0 +138 16.16565261545641 2.15684331777131 0 +139 127.2623898451052 7.328766011455413 0 +140 115.9568293129504 -46.53402715089823 0 +141 244.3691320028803 -46.13414826436184 0 +142 88.25403816534376 72.25054012474965 0 +143 269.1871564819572 72.72673275533703 0 +144 18.59028539552749 41.05660166363843 0 +145 341.5521839156876 -41.13356983297784 0 +146 18.93301108720803 -40.54719515958027 0 +147 341.5132688672502 41.07074557752374 0 +148 50.67523949685767 48.4068021154452 0 +149 222.105100880319 39.55822986622463 0 +150 307.4319041748456 -49.8762581379036 0 +151 52.97255850309197 -49.28082839732331 0 +152 231.7600396402623 5.695407325906425 0 +153 215.3944301745059 18.93765157519121 0 +154 267.4559908261958 8.541334973237582 0 +155 185.5085685452679 -38.21311835685254 0 +156 75.64041257429463 -16.75844089960991 0 +157 286.688160836359 -20.41327460243754 0 +158 205.4491653991082 -45.77555781608999 0 +159 72.35430573411951 20.099502608229 0 +160 171.8362619047945 74.07986370024847 0 +161 309.9369913790455 50.0671916712403 0 +162 117.5170936266794 43.13738584570446 0 +163 194.5550064476807 -73.08332460917617 0 +164 156.780039678521 -17.46388399979583 0 +165 268.1431815804891 44.05492268531513 0 +166 198.1188383727465 16.91989489016715 0 +167 286.5886706489387 18.93529139451515 0 +168 231.0337998078781 73.42632242587138 0 +169 195.0018300583509 48.93108667558433 0 +170 287.483598843843 72.95939455317549 0 +171 283.5848920372209 39.07635270283171 0 +172 93.92012799913442 -2.706513016358965 0 +173 320.0378752563915 72.30048144254125 0 +174 70.54602596109341 75.77935236088011 0 +175 106.9268880916626 -74.07275150503335 0 +176 253.0384394182354 -74.35905904206791 0 +177 224.0120977414695 -73.68254643980583 0 +178 36.96764244098916 73.19440790748057 0 +179 41.66636861361176 20.18615944367271 0 +180 318.0624641019973 -20.45757546799682 0 +181 139.6158178146958 19.5634892956278 0 +182 159.9059449441177 48.90590921468275 0 +183 344.4350673573613 17.68337578488568 0 +184 17.72280155012392 -17.08169608962611 0 +185 344.5585630636551 -16.79770633848989 0 +186 15.63705410110073 20.52473307499136 0 +187 214.1550892424172 75.80709141290691 0 +188 109.3481977644668 15.95419308635139 0 +189 140.5393174410529 72.9176935305973 0 +190 145.2951531006595 53.16029453328358 0 +191 69.99999999994206 -76.72654523898782 0 +192 290.0000000001335 -76.72654523900091 0 +193 187.1005575480214 34.60844536225946 0 +194 39.92645964714256 -36.17767476320478 0 +195 90.09603518997541 54.95569093583109 0 +196 235.1762916805387 54.31598162236129 0 +197 125.1449901506737 -9.37174895699291 0 +198 38.93650178346836 38.26660770330993 0 +199 317.6816495125936 24.64065913272499 0 +200 321.0746180263671 -39.36136242517178 0 +201 171.7206286658653 -50.88377086030629 0 +202 102.175535936996 -33.70849569162483 0 +203 257.627263789806 -34.1600925452014 0 +204 121.8369598234261 74.17448652263782 0 +205 263.2153278840442 57.99678292328328 0 +206 39.19587765176713 -17.25476449649182 0 +207 322.0359350844356 40.06418979160251 0 +208 186.7551659940281 75.69961963381287 0 +209 154.5172582733223 -75.2643224488023 0 +210 225.0824198901038 -56.00386439039661 0 +211 150.0824389072419 -48.5346723626546 0 +212 81.26253163981029 -52.62283143755231 0 +213 278.7374683602207 -52.62283143756996 0 +214 134.4092180416289 38.58052466464602 0 +215 34.07918571080313 -75.55939672550636 0 +216 325.4358496709368 -75.54102324337946 0 +217 208.7786157351577 49.47947938673756 0 +218 126.7836450131481 -76.31536666348605 0 +219 240.3262368195954 36.39286410435612 0 +220 263.5004325035516 -6.86408328448151 0 +221 106.4663722916371 -14.92098246875227 0 +222 234.3007758331624 -10.03300846119329 0 +223 108.5504493160437 29.62871753117465 0 +224 132.4911896713029 -53.70212904792614 0 +225 65.49063995501001 47.3006060405383 0 +226 293.1150516236178 -45.56197243247426 0 +227 67.29436123130699 -45.62536472382415 0 +228 331.4617672802139 -13.28220971095655 0 +229 27.90311629136234 14.02956295151091 0 +230 327.2542444932515 13.06265276206173 0 +231 201.493366443536 -10.8257960266146 0 +232 209.4234717532839 -78.05830071478844 0 +233 294.220332407251 53.11996273253979 0 +234 347.4891985538459 -57.4312444506883 0 +235 12.51080144615368 57.43124445068716 0 +236 347.489198553845 57.4312444506873 0 +237 13.01042662241275 -57.4532970513487 0 +238 256.7874297439013 34.48771734472582 0 +239 148.195374272812 -31.48378190732536 0 +240 105.4885607347719 -60.45839751470558 0 +241 254.5696970304026 -60.43568324480958 0 +242 9.969379230957603 80.03989614142364 0 +243 349.7633246244749 -79.76332462447277 0 +244 9.943994799736732 -80.05909911290973 0 +245 350.0423064347197 80.10437347740219 0 +$EndNodes +$Elements +492 +1 15 2 0 1 1 +2 15 2 0 2 2 +3 15 2 0 3 3 +4 15 2 0 4 4 +5 1 2 0 1 1 5 +6 1 2 0 1 5 6 +7 1 2 0 1 6 7 +8 1 2 0 1 7 8 +9 1 2 0 1 8 9 +10 1 2 0 1 9 10 +11 1 2 0 1 10 11 +12 1 2 0 1 11 12 +13 1 2 0 1 12 2 +14 1 2 0 2 2 13 +15 1 2 0 2 13 14 +16 1 2 0 2 14 15 +17 1 2 0 2 15 16 +18 1 2 0 2 16 17 +19 1 2 0 2 17 18 +20 1 2 0 2 18 19 +21 1 2 0 2 19 20 +22 1 2 0 2 20 21 +23 1 2 0 2 21 22 +24 1 2 0 2 22 23 +25 1 2 0 2 23 24 +26 1 2 0 2 24 25 +27 1 2 0 2 25 26 +28 1 2 0 2 26 27 +29 1 2 0 2 27 28 +30 1 2 0 2 28 29 +31 1 2 0 2 29 3 +32 1 2 0 3 3 30 +33 1 2 0 3 30 31 +34 1 2 0 3 31 32 +35 1 2 0 3 32 33 +36 1 2 0 3 33 34 +37 1 2 0 3 34 35 +38 1 2 0 3 35 36 +39 1 2 0 3 36 37 +40 1 2 0 3 37 4 +41 1 2 0 4 4 38 +42 1 2 0 4 38 39 +43 1 2 0 4 39 40 +44 1 2 0 4 40 41 +45 1 2 0 4 41 42 +46 1 2 0 4 42 43 +47 1 2 0 4 43 44 +48 1 2 0 4 44 45 +49 1 2 0 4 45 46 +50 1 2 0 4 46 47 +51 1 2 0 4 47 48 +52 1 2 0 4 48 49 +53 1 2 0 4 49 50 +54 1 2 0 4 50 51 +55 1 2 0 4 51 52 +56 1 2 0 4 52 53 +57 1 2 0 4 53 54 +58 1 2 0 4 54 1 +59 2 2 0 6 10 146 184 +60 2 2 0 6 35 147 183 +61 2 2 0 6 7 186 144 +62 2 2 0 6 32 185 145 +63 2 2 0 6 67 115 101 +64 2 2 0 6 67 162 110 +65 2 2 0 6 85 110 162 +66 2 2 0 6 96 184 146 +67 2 2 0 6 95 183 147 +68 2 2 0 6 94 144 186 +69 2 2 0 6 93 145 185 +70 2 2 0 6 153 166 74 +71 2 2 0 6 56 105 220 +72 2 2 0 6 98 220 105 +73 2 2 0 6 166 116 74 +74 2 2 0 6 98 157 79 +75 2 2 0 6 220 98 79 +76 2 2 0 6 107 155 158 +77 2 2 0 6 57 155 107 +78 2 2 0 6 40 108 173 +79 2 2 0 6 177 24 100 +80 2 2 0 6 100 24 176 +81 2 2 0 6 47 189 103 +82 2 2 0 6 71 184 206 +83 2 2 0 6 71 138 184 +84 2 2 0 6 225 112 148 +85 2 2 0 6 49 50 204 +86 2 2 0 6 80 172 101 +87 2 2 0 6 204 50 110 +88 2 2 0 6 77 194 146 +89 2 2 0 6 101 172 188 +90 2 2 0 6 38 173 131 +91 2 2 0 6 56 126 152 +92 2 2 0 6 90 152 126 +93 2 2 0 6 73 102 158 +94 2 2 0 6 39 40 173 +95 2 2 0 6 55 188 172 +96 2 2 0 6 38 39 173 +97 2 2 0 6 52 53 112 +98 2 2 0 6 84 112 225 +99 2 2 0 6 67 101 223 +100 2 2 0 6 80 101 159 +101 2 2 0 6 64 121 104 +102 2 2 0 6 96 146 194 +103 2 2 0 6 57 164 106 +104 2 2 0 6 20 21 135 +105 2 2 0 6 73 163 102 +106 2 2 0 6 64 104 140 +107 2 2 0 6 65 141 105 +108 2 2 0 6 65 105 136 +109 2 2 0 6 20 135 209 +110 2 2 0 6 47 48 189 +111 2 2 0 6 57 116 134 +112 2 2 0 6 82 134 116 +113 2 2 0 6 65 136 107 +114 2 2 0 6 42 43 111 +115 2 2 0 6 47 103 160 +116 2 2 0 6 53 178 112 +117 2 2 0 6 56 222 105 +118 2 2 0 6 81 160 103 +119 2 2 0 6 80 117 128 +120 2 2 0 6 79 118 127 +121 2 2 0 6 81 103 182 +122 2 2 0 6 57 106 155 +123 2 2 0 6 98 105 203 +124 2 2 0 6 73 158 155 +125 2 2 0 6 60 128 206 +126 2 2 0 6 60 206 194 +127 2 2 0 6 57 107 231 +128 2 2 0 6 82 122 133 +129 2 2 0 6 63 133 122 +130 2 2 0 6 65 107 158 +131 2 2 0 6 80 128 156 +132 2 2 0 6 50 142 110 +133 2 2 0 6 90 149 153 +134 2 2 0 6 62 153 149 +135 2 2 0 6 102 210 158 +136 2 2 0 6 16 17 113 +137 2 2 0 6 25 26 114 +138 2 2 0 6 40 170 108 +139 2 2 0 6 89 211 123 +140 2 2 0 6 81 122 193 +141 2 2 0 6 88 191 113 +142 2 2 0 6 87 114 192 +143 2 2 0 6 123 211 224 +144 2 2 0 6 78 108 161 +145 2 2 0 6 70 230 127 +146 2 2 0 6 127 230 199 +147 2 2 0 6 10 11 146 +148 2 2 0 6 35 36 147 +149 2 2 0 6 6 7 144 +150 2 2 0 6 31 32 145 +151 2 2 0 6 85 214 190 +152 2 2 0 6 85 162 214 +153 2 2 0 6 45 109 187 +154 2 2 0 6 14 15 120 +155 2 2 0 6 27 28 119 +156 2 2 0 6 65 158 210 +157 2 2 0 6 88 113 212 +158 2 2 0 6 87 213 114 +159 2 2 0 6 70 118 180 +160 2 2 0 6 71 117 179 +161 2 2 0 6 42 111 143 +162 2 2 0 6 81 109 208 +163 2 2 0 6 78 173 108 +164 2 2 0 6 81 193 169 +165 2 2 0 6 76 150 119 +166 2 2 0 6 87 119 150 +167 2 2 0 6 88 151 120 +168 2 2 0 6 77 120 151 +169 2 2 0 6 54 129 178 +170 2 2 0 6 82 133 134 +171 2 2 0 6 72 134 133 +172 2 2 0 6 79 127 167 +173 2 2 0 6 21 163 135 +174 2 2 0 6 21 22 163 +175 2 2 0 6 53 54 178 +176 2 2 0 6 48 49 204 +177 2 2 0 6 97 172 156 +178 2 2 0 6 80 156 172 +179 2 2 0 6 48 204 189 +180 2 2 0 6 67 195 115 +181 2 2 0 6 57 134 164 +182 2 2 0 6 65 210 141 +183 2 2 0 6 67 110 195 +184 2 2 0 6 72 197 121 +185 2 2 0 6 70 127 118 +186 2 2 0 6 71 128 117 +187 2 2 0 6 97 124 202 +188 2 2 0 6 68 202 124 +189 2 2 0 6 98 203 125 +190 2 2 0 6 69 125 203 +191 2 2 0 6 61 233 171 +192 2 2 0 6 61 161 233 +193 2 2 0 6 75 148 112 +194 2 2 0 6 149 219 196 +195 2 2 0 6 86 109 217 +196 2 2 0 6 81 169 109 +197 2 2 0 6 86 149 196 +198 2 2 0 6 45 208 109 +199 2 2 0 6 43 168 111 +200 2 2 0 6 72 139 197 +201 2 2 0 6 86 187 109 +202 2 2 0 6 96 206 184 +203 2 2 0 6 91 154 167 +204 2 2 0 6 79 167 154 +205 2 2 0 6 74 136 222 +206 2 2 0 6 85 204 110 +207 2 2 0 6 15 191 120 +208 2 2 0 6 27 119 192 +209 2 2 0 6 100 141 210 +210 2 2 0 6 58 159 115 +211 2 2 0 6 101 115 159 +212 2 2 0 6 60 156 128 +213 2 2 0 6 50 51 142 +214 2 2 0 6 70 180 228 +215 2 2 0 6 71 179 229 +216 2 2 0 6 74 222 152 +217 2 2 0 6 16 113 191 +218 2 2 0 6 26 192 114 +219 2 2 0 6 57 231 116 +220 2 2 0 6 59 118 157 +221 2 2 0 6 79 157 118 +222 2 2 0 6 74 152 153 +223 2 2 0 6 90 153 152 +224 2 2 0 6 61 207 161 +225 2 2 0 6 58 117 159 +226 2 2 0 6 80 159 117 +227 2 2 0 6 66 205 111 +228 2 2 0 6 66 111 196 +229 2 2 0 6 89 209 135 +230 2 2 0 6 61 199 207 +231 2 2 0 6 88 120 191 +232 2 2 0 6 87 192 119 +233 2 2 0 6 74 231 136 +234 2 2 0 6 107 136 231 +235 2 2 0 6 91 167 171 +236 2 2 0 6 84 115 195 +237 2 2 0 6 84 174 112 +238 2 2 0 6 52 112 174 +239 2 2 0 6 64 140 224 +240 2 2 0 6 82 193 122 +241 2 2 0 6 41 42 143 +242 2 2 0 6 75 112 178 +243 2 2 0 6 55 139 188 +244 2 2 0 6 92 188 139 +245 2 2 0 6 56 154 126 +246 2 2 0 6 91 126 154 +247 2 2 0 6 77 151 194 +248 2 2 0 6 59 200 180 +249 2 2 0 6 33 34 137 +250 2 2 0 6 8 9 138 +251 2 2 0 6 72 121 164 +252 2 2 0 6 59 150 200 +253 2 2 0 6 90 126 219 +254 2 2 0 6 61 171 167 +255 2 2 0 6 126 238 219 +256 2 2 0 6 44 45 187 +257 2 2 0 6 46 47 160 +258 2 2 0 6 68 212 113 +259 2 2 0 6 69 114 213 +260 2 2 0 6 58 148 198 +261 2 2 0 6 59 157 125 +262 2 2 0 6 98 125 157 +263 2 2 0 6 97 156 124 +264 2 2 0 6 60 124 156 +265 2 2 0 6 43 44 168 +266 2 2 0 6 75 129 235 +267 2 2 0 6 76 130 234 +268 2 2 0 6 77 237 132 +269 2 2 0 6 78 236 131 +270 2 2 0 6 72 133 181 +271 2 2 0 6 58 198 179 +272 2 2 0 6 40 41 170 +273 2 2 0 6 17 175 113 +274 2 2 0 6 25 114 176 +275 2 2 0 6 82 116 166 +276 2 2 0 6 79 154 220 +277 2 2 0 6 19 209 123 +278 2 2 0 6 72 181 139 +279 2 2 0 6 75 235 144 +280 2 2 0 6 77 146 237 +281 2 2 0 6 76 234 145 +282 2 2 0 6 78 147 236 +283 2 2 0 6 24 25 176 +284 2 2 0 6 17 18 175 +285 2 2 0 6 23 24 177 +286 2 2 0 6 68 113 240 +287 2 2 0 6 69 241 114 +288 2 2 0 6 73 135 163 +289 2 2 0 6 78 131 173 +290 2 2 0 6 61 167 127 +291 2 2 0 6 76 119 216 +292 2 2 0 6 77 215 120 +293 2 2 0 6 81 208 160 +294 2 2 0 6 72 164 134 +295 2 2 0 6 99 224 140 +296 2 2 0 6 106 211 201 +297 2 2 0 6 63 181 133 +298 2 2 0 6 75 144 198 +299 2 2 0 6 94 198 144 +300 2 2 0 6 34 35 183 +301 2 2 0 6 9 10 184 +302 2 2 0 6 32 33 185 +303 2 2 0 6 7 8 186 +304 2 2 0 6 110 142 195 +305 2 2 0 6 81 182 122 +306 2 2 0 6 19 20 209 +307 2 2 0 6 29 130 216 +308 2 2 0 6 13 215 132 +309 2 2 0 6 76 145 200 +310 2 2 0 6 93 200 145 +311 2 2 0 6 82 166 193 +312 2 2 0 6 58 179 117 +313 2 2 0 6 59 180 118 +314 2 2 0 6 28 29 216 +315 2 2 0 6 13 14 215 +316 2 2 0 6 18 218 175 +317 2 2 0 6 84 225 115 +318 2 2 0 6 58 115 225 +319 2 2 0 6 41 143 170 +320 2 2 0 6 19 123 218 +321 2 2 0 6 76 216 130 +322 2 2 0 6 77 132 215 +323 2 2 0 6 103 189 190 +324 2 2 0 6 85 190 189 +325 2 2 0 6 89 135 201 +326 2 2 0 6 73 201 135 +327 2 2 0 6 63 122 182 +328 2 2 0 6 62 166 153 +329 2 2 0 6 44 187 168 +330 2 2 0 6 89 201 211 +331 2 2 0 6 74 116 231 +332 2 2 0 6 104 121 197 +333 2 2 0 6 83 170 143 +334 2 2 0 6 84 142 174 +335 2 2 0 6 51 174 142 +336 2 2 0 6 75 178 129 +337 2 2 0 6 78 207 147 +338 2 2 0 6 95 147 207 +339 2 2 0 6 108 170 233 +340 2 2 0 6 83 233 170 +341 2 2 0 6 45 46 208 +342 2 2 0 6 28 216 119 +343 2 2 0 6 14 120 215 +344 2 2 0 6 64 239 121 +345 2 2 0 6 91 171 165 +346 2 2 0 6 51 52 174 +347 2 2 0 6 18 19 218 +348 2 2 0 6 55 221 197 +349 2 2 0 6 55 197 139 +350 2 2 0 6 89 123 209 +351 2 2 0 6 101 188 223 +352 2 2 0 6 83 143 205 +353 2 2 0 6 99 218 123 +354 2 2 0 6 109 169 217 +355 2 2 0 6 106 201 155 +356 2 2 0 6 99 123 224 +357 2 2 0 6 104 202 140 +358 2 2 0 6 105 141 203 +359 2 2 0 6 59 125 226 +360 2 2 0 6 60 227 124 +361 2 2 0 6 63 214 181 +362 2 2 0 6 66 219 238 +363 2 2 0 6 104 221 202 +364 2 2 0 6 61 127 199 +365 2 2 0 6 68 124 212 +366 2 2 0 6 69 213 125 +367 2 2 0 6 92 139 181 +368 2 2 0 6 15 16 191 +369 2 2 0 6 26 27 192 +370 2 2 0 6 33 137 185 +371 2 2 0 6 34 183 137 +372 2 2 0 6 8 138 186 +373 2 2 0 6 9 184 138 +374 2 2 0 6 5 235 129 +375 2 2 0 6 30 234 130 +376 2 2 0 6 37 131 236 +377 2 2 0 6 12 132 237 +378 2 2 0 6 71 206 128 +379 2 2 0 6 66 196 219 +380 2 2 0 6 83 165 171 +381 2 2 0 6 99 140 240 +382 2 2 0 6 68 240 140 +383 2 2 0 6 69 141 241 +384 2 2 0 6 100 241 141 +385 2 2 0 6 99 175 218 +386 2 2 0 6 91 238 126 +387 2 2 0 6 30 31 234 +388 2 2 0 6 5 6 235 +389 2 2 0 6 36 37 236 +390 2 2 0 6 11 12 237 +391 2 2 0 6 104 197 221 +392 2 2 0 6 70 137 230 +393 2 2 0 6 70 228 137 +394 2 2 0 6 71 229 138 +395 2 2 0 6 83 205 165 +396 2 2 0 6 84 195 142 +397 2 2 0 6 56 152 222 +398 2 2 0 6 96 194 206 +399 2 2 0 6 86 168 187 +400 2 2 0 6 60 194 151 +401 2 2 0 6 75 198 148 +402 2 2 0 6 64 224 211 +403 2 2 0 6 54 242 129 +404 2 2 0 6 5 129 242 +405 2 2 0 6 29 243 130 +406 2 2 0 6 30 130 243 +407 2 2 0 6 38 131 245 +408 2 2 0 6 37 245 131 +409 2 2 0 6 12 244 132 +410 2 2 0 6 13 132 244 +411 2 2 0 6 68 140 202 +412 2 2 0 6 69 203 141 +413 2 2 0 6 97 202 221 +414 2 2 0 6 111 205 143 +415 2 2 0 6 76 200 150 +416 2 2 0 6 93 180 200 +417 2 2 0 6 62 149 217 +418 2 2 0 6 94 179 198 +419 2 2 0 6 58 225 148 +420 2 2 0 6 22 23 232 +421 2 2 0 6 66 165 205 +422 2 2 0 6 2 13 244 +423 2 2 0 6 3 30 243 +424 2 2 0 6 1 5 242 +425 2 2 0 6 1 242 54 +426 2 2 0 6 4 38 245 +427 2 2 0 6 4 245 37 +428 2 2 0 6 3 243 29 +429 2 2 0 6 2 244 12 +430 2 2 0 6 111 168 196 +431 2 2 0 6 86 196 168 +432 2 2 0 6 105 222 136 +433 2 2 0 6 62 169 193 +434 2 2 0 6 73 155 201 +435 2 2 0 6 113 175 240 +436 2 2 0 6 114 241 176 +437 2 2 0 6 59 226 150 +438 2 2 0 6 60 151 227 +439 2 2 0 6 62 193 166 +440 2 2 0 6 86 217 149 +441 2 2 0 6 63 190 214 +442 2 2 0 6 85 189 204 +443 2 2 0 6 90 219 149 +444 2 2 0 6 46 160 208 +445 2 2 0 6 78 161 207 +446 2 2 0 6 6 144 235 +447 2 2 0 6 31 145 234 +448 2 2 0 6 11 237 146 +449 2 2 0 6 36 236 147 +450 2 2 0 6 63 182 190 +451 2 2 0 6 103 190 182 +452 2 2 0 6 100 210 177 +453 2 2 0 6 102 177 210 +454 2 2 0 6 56 220 154 +455 2 2 0 6 67 223 162 +456 2 2 0 6 92 162 223 +457 2 2 0 6 55 172 221 +458 2 2 0 6 97 221 172 +459 2 2 0 6 95 199 230 +460 2 2 0 6 87 150 226 +461 2 2 0 6 88 227 151 +462 2 2 0 6 62 217 169 +463 2 2 0 6 92 181 214 +464 2 2 0 6 92 214 162 +465 2 2 0 6 102 163 232 +466 2 2 0 6 22 232 163 +467 2 2 0 6 102 232 177 +468 2 2 0 6 23 177 232 +469 2 2 0 6 83 171 233 +470 2 2 0 6 106 164 239 +471 2 2 0 6 121 239 164 +472 2 2 0 6 137 183 230 +473 2 2 0 6 137 228 185 +474 2 2 0 6 138 229 186 +475 2 2 0 6 94 229 179 +476 2 2 0 6 93 228 180 +477 2 2 0 6 95 207 199 +478 2 2 0 6 108 233 161 +479 2 2 0 6 91 165 238 +480 2 2 0 6 66 238 165 +481 2 2 0 6 92 223 188 +482 2 2 0 6 99 240 175 +483 2 2 0 6 100 176 241 +484 2 2 0 6 95 230 183 +485 2 2 0 6 93 185 228 +486 2 2 0 6 94 186 229 +487 2 2 0 6 88 212 227 +488 2 2 0 6 124 227 212 +489 2 2 0 6 87 226 213 +490 2 2 0 6 125 213 226 +491 2 2 0 6 64 211 239 +492 2 2 0 6 106 239 211 +$EndElements From a2161f2789c0a5d9187355d153c6bd23572b23c8 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 23 Sep 2019 11:32:32 +0100 Subject: [PATCH 33/40] Fix testing of grids --- .../cropped_shifted_lonlat_1.yml | 2 +- doc/example-grids/regular_lonlat_2.yml | 1 + doc/example-grids/regular_lonlat_3.yml | 1 + doc/example-grids/update_uid.py | 46 ++++++++++--------- src/apps/atlas-grids.cc | 46 +++++++++++++++++-- 5 files changed, 69 insertions(+), 27 deletions(-) diff --git a/doc/example-grids/cropped_shifted_lonlat_1.yml b/doc/example-grids/cropped_shifted_lonlat_1.yml index 6d8f5ecdc..8e237b03b 100644 --- a/doc/example-grids/cropped_shifted_lonlat_1.yml +++ b/doc/example-grids/cropped_shifted_lonlat_1.yml @@ -11,5 +11,5 @@ check : size : 4140 lonlat(first) : [1,89] lonlat(last) : [359,45] - uid : a1426cb32c8694d07d997cd224805947 + uid : 18b1a156853d2cca30b7d13ef8400510 bounding_box(n,w,s,e) : [90,0,45,360] diff --git a/doc/example-grids/regular_lonlat_2.yml b/doc/example-grids/regular_lonlat_2.yml index 17793caf8..809a80aa5 100644 --- a/doc/example-grids/regular_lonlat_2.yml +++ b/doc/example-grids/regular_lonlat_2.yml @@ -18,4 +18,5 @@ check: bounding_box(n,w,s,e) : [60.001, -164.730, 39.999, -151.270] lonlat(first) : [-158., 40.] lonlat(last) : [-164.729, 59.547] + uid : 5ba0c02a9508d662d751ba34fdb4279e diff --git a/doc/example-grids/regular_lonlat_3.yml b/doc/example-grids/regular_lonlat_3.yml index ca002e2fb..026b72739 100644 --- a/doc/example-grids/regular_lonlat_3.yml +++ b/doc/example-grids/regular_lonlat_3.yml @@ -18,4 +18,5 @@ check: bounding_box(n,w,s,e) : [-19.999, 18.370, -40.001, 25.630] lonlat(first) : [18.371, -20.243] lonlat(last) : [22., -40.] + uid : 8c3901b64831fa8418485aba519874df diff --git a/doc/example-grids/update_uid.py b/doc/example-grids/update_uid.py index 53923cb2b..125839888 100755 --- a/doc/example-grids/update_uid.py +++ b/doc/example-grids/update_uid.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # This script checks all example-grids and updates the uid to a newly calculated uid @@ -25,29 +25,33 @@ def run_program_get_error(file): def process_file( file, newfile ): - import string - print( "File: " , file ) + import string + print( "File: " , file ) - f = open(file,"r+") - text = f.read() - f.close() + f = open(file,"r+") + text = f.read() + f.close() - line = run_program_get_error( file ) - if line: - print( line ) - import re - replace = re.match( r"Check failed: grid uid (.*) expected to be (.*)$", line ).group(1) - uid = re.findall( r"^ uid : (.*)$", text, re.MULTILINE)[0] - print("Search/Replace ",uid, " to ", replace ) - newtext = text.replace( uid, replace ) - else: - print( "Nothing to replace, copy-only" ) - newtext = text + newtext = text - print( "New file: ", newfile) - f = open(newfile,"w") - f.write(newtext) - f.close() + lines = run_program_get_error( file ) + if lines: + for line in lines.splitlines(): + import re + matched = re.match( r"Check failed: grid uid (.*) expected to be (.*)$", line ) + if matched : + print( line ) + replace = matched.group(1) + uid = re.findall( r"^ uid : (.*)$", text, re.MULTILINE)[0] + print("Search/Replace ",uid, " to ", replace ) + newtext = text.replace( uid, replace ) + else: + print( "Nothing to replace, copy-only" ) + + print( "New file: ", newfile) + f = open(newfile,"w") + f.write(newtext) + f.close() import sys diff --git a/src/apps/atlas-grids.cc b/src/apps/atlas-grids.cc index 968a7fe42..c9062d3a2 100644 --- a/src/apps/atlas-grids.cc +++ b/src/apps/atlas-grids.cc @@ -319,19 +319,24 @@ int AtlasGrids::execute( const Args& args ) { std::vector bbox; if ( config_check.get( "bounding_box(n,w,s,e)", bbox ) && bbox.size() == 4 ) { auto bb = grid.lonlatBoundingBox(); - if ( ( check_failed = !bb ) ) { + if ( !bb ) { + check_failed = true; out << "Check failed: cannot calculate bounding box for " << grid.spec() << std::endl; } - else if ( ( check_failed = !equal( bb.north(), bbox[0] ) ) ) { + 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 ( ( check_failed = !equal( bb.west(), bbox[1] ) ) ) { + 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 ( ( check_failed = !equal( bb.south(), bbox[2] ) ) ) { + 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 ( ( check_failed = !equal( bb.east(), bbox[3] ) ) ) { + else if ( !equal( bb.east(), bbox[3] ) ) { + check_failed = true; out << "Check failed: e=" << bb.east() << " expected to be " << bbox[3] << std::endl; } } @@ -344,6 +349,37 @@ int AtlasGrids::execute( const Args& args ) { Log::warning() << "Check for bounding_box(n,w,s,e) skipped" << std::endl; } + auto rel_equal = []( double a, double b ) { return std::abs( ( a - b ) / a ) < 1.e-6; }; + + double xmin; + if ( config_check.get( "xmin", xmin ) ) { + if ( !rel_equal( RectangularDomain( grid.domain() ).xmin(), xmin ) ) { + auto precision = out.precision( 2 ); + out << "Check failed: grid xmin " << std::fixed << RectangularDomain( grid.domain() ).xmin() + << " expected to be " << std::fixed << xmin << std::endl; + out.precision( precision ); + check_failed = true; + } + } + else { + Log::warning() << "Check for xmin skipped" << std::endl; + } + + double ymin; + if ( config_check.get( "ymin", ymin ) ) { + if ( !rel_equal( RectangularDomain( grid.domain() ).ymin(), ymin ) ) { + auto precision = out.precision( 2 ); + out << "Check failed: grid ymin " << std::fixed << RectangularDomain( grid.domain() ).ymin() + << " expected to be " << std::fixed << ymin << std::endl; + out.precision( precision ); + check_failed = true; + } + } + else { + Log::warning() << "Check for ymin skipped" << std::endl; + } + + if ( check_failed ) { return failed(); } From 234a3556c8eb4f25364c5302e42e75537b32adcc Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 23 Sep 2019 11:33:44 +0100 Subject: [PATCH 34/40] Add false_easting and false_northing to LambertAzimuthalEqualAreaProjection --- .../regional_lambert_azimuthal_equal_area_2.yml | 11 +++++++++-- .../detail/LambertAzimuthalEqualAreaProjection.cc | 11 +++++++++-- .../detail/LambertAzimuthalEqualAreaProjection.h | 2 ++ 3 files changed, 20 insertions(+), 4 deletions(-) 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 48a72cb50..b06b9681d 100644 --- a/doc/example-grids/regional_lambert_azimuthal_equal_area_2.yml +++ b/doc/example-grids/regional_lambert_azimuthal_equal_area_2.yml @@ -1,4 +1,7 @@ -# EFAS grid +# Close resemblance to EFAS grid +# Here the sphere is used instead of "GRS80" ellipsoid +# Actual EFAS grid uses "+proj=laea +lat_0=52 +lon_0=10 +x_0=4321000 +y_0=3210000 +ellps=GRS80 +units=m +no_defs" +# For real EFAS grid, check "regional_lambert_azimuthal_equal_area_4.yml" type : "regional" nx : 1000 @@ -10,6 +13,8 @@ projection : type : "lambert_azimuthal_equal_area" standard_parallel : 52 central_longitude : 10 + false_easting : 4321000 + false_northing : 3210000 y_numbering : -1 @@ -18,4 +23,6 @@ check : 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 : a87b2f233e26156d01c05a9ef2466721 + uid : 2ef4f1bb005cf8e24ecc9464f8b7e089 + xmin : 2510375.79 + ymin : 748404.45 diff --git a/src/atlas/projection/detail/LambertAzimuthalEqualAreaProjection.cc b/src/atlas/projection/detail/LambertAzimuthalEqualAreaProjection.cc index 7430171c6..526921497 100644 --- a/src/atlas/projection/detail/LambertAzimuthalEqualAreaProjection.cc +++ b/src/atlas/projection/detail/LambertAzimuthalEqualAreaProjection.cc @@ -34,6 +34,9 @@ LambertAzimuthalEqualAreaProjection::LambertAzimuthalEqualAreaProjection( const ATLAS_ASSERT( params.get( "central_longitude", reference_[LON] ) ); ATLAS_ASSERT( params.get( "standard_parallel", reference_[LAT] ) ); params.get( "radius", radius_ = util::Earth::radius() ); + params.get( "false_northing", false_northing_ ); + params.get( "false_easting", false_easting_ ); + lambda0_ = util::Constants::degreesToRadians() * reference_[LON]; phi1_ = util::Constants::degreesToRadians() * reference_[LAT]; @@ -55,12 +58,14 @@ void LambertAzimuthalEqualAreaProjection::lonlat2xy( double crd[] ) const { crd[XX] = kp * cos_phi * sin_dlambda; crd[YY] = kp * ( cos_phi1_ * sin_phi - sin_phi1_ * cos_phi * cos_dlambda ); + crd[XX] += false_easting_; + crd[YY] += false_northing_; } void LambertAzimuthalEqualAreaProjection::xy2lonlat( double crd[] ) const { - const double& x = crd[XX]; - const double& y = crd[YY]; + const double x = crd[XX] - false_easting_; + const double y = crd[YY] - false_northing_; const double rho = std::sqrt( x * x + y * y ); if ( std::abs( rho ) < 1.e-12 ) { @@ -87,6 +92,8 @@ LambertAzimuthalEqualAreaProjection::Spec LambertAzimuthalEqualAreaProjection::s proj.set( "central_longitude", reference_[LON] ); proj.set( "standard_parallel", reference_[LAT] ); proj.set( "radius", radius_ ); + proj.set( "false_easting", false_easting_ ); + proj.set( "false_northing", false_northing_ ); return proj; } diff --git a/src/atlas/projection/detail/LambertAzimuthalEqualAreaProjection.h b/src/atlas/projection/detail/LambertAzimuthalEqualAreaProjection.h index 095ffc7af..b195d3d0e 100644 --- a/src/atlas/projection/detail/LambertAzimuthalEqualAreaProjection.h +++ b/src/atlas/projection/detail/LambertAzimuthalEqualAreaProjection.h @@ -50,6 +50,8 @@ class LambertAzimuthalEqualAreaProjection final : public ProjectionImpl { double phi1_; ///< standard parallel [rad] double sin_phi1_; double cos_phi1_; + double false_northing_{0}; + double false_easting_{0}; }; } // namespace detail From b984f54f13fa5d784d948ea4d03a6e554dc0efdd Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Mon, 23 Sep 2019 12:01:52 +0100 Subject: [PATCH 35/40] Test Proj projections using yml --- ...egional_lambert_azimuthal_equal_area_3.yml | 26 +++++++++++++++++++ ...egional_lambert_azimuthal_equal_area_4.yml | 26 +++++++++++++++++++ src/tests/grid/CMakeLists.txt | 5 ++++ 3 files changed, 57 insertions(+) create mode 100644 doc/example-grids/regional_lambert_azimuthal_equal_area_3.yml create mode 100644 doc/example-grids/regional_lambert_azimuthal_equal_area_4.yml diff --git a/doc/example-grids/regional_lambert_azimuthal_equal_area_3.yml b/doc/example-grids/regional_lambert_azimuthal_equal_area_3.yml new file mode 100644 index 000000000..1751c5355 --- /dev/null +++ b/doc/example-grids/regional_lambert_azimuthal_equal_area_3.yml @@ -0,0 +1,26 @@ +# !!! Requires proj external package (aka proj4) --> ENABLE_PROJ=ON + +# Close resemblance to EFAS grid ( equivalent to regional_lambert_azimuthal_equal_area_2.yml ) +# Here the sphere is used instead of ellipsoid +# Actual EFAS grid uses "+proj=laea +lat_0=52 +lon_0=10 +x_0=4321000 +y_0=3210000 +ellps=GRS80 +units=m +no_defs" + +type : "regional" +nx : 1000 +ny : 950 +dx : 5000 +dy : 5000 +lonlat(xmin,ymax) : [-35.034,66.9821] +projection : + type : "proj" + proj : "+proj=laea +lat_0=52 +lon_0=10 +x_0=4321000 +y_0=3210000 +R=6371229.0" +y_numbering : -1 + + +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 : dbbfc11ac72a5aa4377bbd516548e2c4 + 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 new file mode 100644 index 000000000..551b487df --- /dev/null +++ b/doc/example-grids/regional_lambert_azimuthal_equal_area_4.yml @@ -0,0 +1,26 @@ +# !!! Requires proj external package (aka proj4) --> ENABLE_PROJ=ON + +# EFAS grid + +type : "regional" +nx : 1000 +ny : 950 +dx : 5000 +dy : 5000 +lonlat(xmin,ymax) : [-35.034,66.9821] +projection : + type : "proj" + proj : "+proj=laea +lat_0=52 +lon_0=10 +x_0=4321000 +y_0=3210000 +ellps=GRS80 +units=m +no_defs" +# Equivalent proj string, but leads to different uid (a71208b89dc4bc8d20494514e562c6b7) +# proj : "EPSG:3035" +y_numbering : -1 + + +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 : fe4ea53ff38ccefeae02e53ee307ab42 + xmin : 2502497.60 + ymin : 752495.56 diff --git a/src/tests/grid/CMakeLists.txt b/src/tests/grid/CMakeLists.txt index 31ed56be0..c7c413088 100644 --- a/src/tests/grid/CMakeLists.txt +++ b/src/tests/grid/CMakeLists.txt @@ -32,6 +32,11 @@ foreach(test endforeach() file( GLOB grids ${PROJECT_SOURCE_DIR}/doc/example-grids/*.yml ) +if( NOT HAVE_PROJ ) + ecbuild_list_exclude_pattern( + LIST grids + REGEX regional_lambert_azimuthal_equal_area_[3|4].yml ) +endif() foreach( grid ${grids} ) get_filename_component( grid_name ${grid} NAME_WE ) From dc015b712ed66fd207343d77a551d3e0aa96e2f9 Mon Sep 17 00:00:00 2001 From: Pedro Maciel Date: Thu, 4 Jul 2019 09:01:37 +0100 Subject: [PATCH 36/40] MIR-77, MIR-385, MIR-386: preliminary support for interpolation from Lambert Conformal projected grids --- src/atlas/CMakeLists.txt | 2 + .../detail/LambertConformalProjection.cc | 124 ++++++++++++++++++ .../detail/LambertConformalProjection.h | 60 +++++++++ 3 files changed, 186 insertions(+) create mode 100644 src/atlas/projection/detail/LambertConformalProjection.cc create mode 100644 src/atlas/projection/detail/LambertConformalProjection.h diff --git a/src/atlas/CMakeLists.txt b/src/atlas/CMakeLists.txt index 7ec508f3a..4a2a35b09 100644 --- a/src/atlas/CMakeLists.txt +++ b/src/atlas/CMakeLists.txt @@ -66,6 +66,8 @@ projection/Projection.cc projection/Projection.h projection/detail/LambertAzimuthalEqualAreaProjection.cc projection/detail/LambertAzimuthalEqualAreaProjection.h +projection/detail/LambertConformalProjection.cc +projection/detail/LambertConformalProjection.h projection/detail/LambertProjection.cc projection/detail/LambertProjection.h projection/detail/LonLatProjection.cc diff --git a/src/atlas/projection/detail/LambertConformalProjection.cc b/src/atlas/projection/detail/LambertConformalProjection.cc new file mode 100644 index 000000000..634133ace --- /dev/null +++ b/src/atlas/projection/detail/LambertConformalProjection.cc @@ -0,0 +1,124 @@ +/* + * (C) Copyright 1996- 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 "LambertConformalProjection.h" + +#include + +#include "eckit/config/Parametrisation.h" +#include "eckit/types/FloatCompare.h" +#include "eckit/utils/Hash.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" + + +namespace atlas { +namespace projection { +namespace detail { + +namespace { + +inline double normalise( double a, double minimum, double globe ) { + while ( a >= minimum + globe ) { + a -= globe; + } + while ( a < minimum ) { + a += globe; + } + return a; +} + +inline double tan_d( double lat_d ) { + return std::tan( util::Constants::degreesToRadians() * ( 45. + lat_d * 0.5 ) ); +} + +inline double sin_d( double theta_d ) { + return std::sin( util::Constants::degreesToRadians() * theta_d ); +} + +inline double cos_d( double theta_d ) { + return std::cos( util::Constants::degreesToRadians() * theta_d ); +} + +static ProjectionBuilder register_1( LambertConformalProjection::static_type() ); + +} // namespace + +LambertConformalProjection::LambertConformalProjection( const eckit::Parametrisation& params ) { + ATLAS_ASSERT( params.get( "latitude1", lat1_ = 0 ) ); + ATLAS_ASSERT( params.get( "latitude2", lat2_ = 0 ) ); + ATLAS_ASSERT( params.get( "latitudeD", latD_ = 0 ) ); + ATLAS_ASSERT( params.get( "longitude0", lon0_ = 0 ) ); + params.get( "radius", radius_ = util::Earth::radius() ); + + n_ = eckit::types::is_approximately_equal( lat1_, lat2_ ) + ? sin_d( lat2_ ) + : std::log( cos_d( lat1_ ) / cos_d( lat2_ ) ) / std::log( tan_d( lat2_ ) / tan_d( lat1_ ) ); + inv_n_ = 1. / n_; + sign_ = n_ < 0. ? -1. : 1.; // n < 0: adjustment for southern hemisphere + + F_ = ( cos_d( lat1_ ) * std::pow( tan_d( lat1_ ), n_ ) ) / n_; + rho0_ = sign_ * radius_ * F_ * std::pow( tan_d( latD_ ), -n_ ); +} + +void LambertConformalProjection::lonlat2xy( double crd[] ) const { + double rho = radius_ * F_ * std::pow( tan_d( crd[1] ), -n_ ); + double theta = n_ * normalise( crd[0] - lon0_, -180, 360 ); + + crd[XX] = rho * sin_d( theta ); + crd[YY] = rho0_ - rho * cos_d( theta ); +} + +void LambertConformalProjection::xy2lonlat( double crd[] ) const { + double x = sign_ * crd[XX]; + double y = rho0_ - sign_ * crd[YY]; + + double rho = sign_ * std::sqrt( x * x + y * y ); + double theta = std::atan2( x, y ) * inv_n_; + + crd[LON] = util::Constants::radiansToDegrees() * theta + lon0_; + crd[LAT] = + eckit::types::is_approximately_equal( rho, 0. ) + ? 90 * sign_ + : util::Constants::radiansToDegrees() * 2. * std::atan( std::pow( radius_ * F_ / rho, inv_n_ ) ) - 90.; +} + +LambertConformalProjection::Spec LambertConformalProjection::spec() const { + Spec spec; + spec.set( "type", static_type() ); + spec.set( "latitude1", lat1_ ); + spec.set( "latitude2", lat2_ ); + spec.set( "latitudeD", latD_ ); + spec.set( "longitude0", lon0_ ); + if ( !eckit::types::is_approximately_equal( radius_, util::Earth::radius() ) ) { + spec.set( "radius", radius_ ); + } + + return spec; +} + +void LambertConformalProjection::hash( eckit::Hash& h ) const { + h.add( static_type() ); + h.add( lat1_ ); + h.add( lat2_ ); + h.add( latD_ ); + h.add( lon0_ ); + h.add( radius_ ); +} + +} // namespace detail +} // namespace projection +} // namespace atlas diff --git a/src/atlas/projection/detail/LambertConformalProjection.h b/src/atlas/projection/detail/LambertConformalProjection.h new file mode 100644 index 000000000..ccc4a5a1e --- /dev/null +++ b/src/atlas/projection/detail/LambertConformalProjection.h @@ -0,0 +1,60 @@ +/* + * (C) Copyright 1996- 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/domain.h" +#include "atlas/projection/detail/ProjectionImpl.h" + +namespace atlas { +namespace projection { +namespace detail { + +class LambertConformalProjection final : public ProjectionImpl { +public: + // constructor + LambertConformalProjection( const eckit::Parametrisation& ); + + // projection name + static std::string static_type() { return "lambert_conformal"; } + std::string type() const override { return static_type(); } + + // projection and inverse projection + void xy2lonlat( double crd[] ) const override; + void lonlat2xy( double crd[] ) const override; + + bool strictlyRegional() const override { return true; } + Domain boundingBox( const Domain& domain ) const override { return ProjectionImpl::boundingBox( domain ); } + + // specification + Spec spec() const override; + + std::string units() const override { return "meters"; } + + void hash( eckit::Hash& ) const override; + +private: +// PointLonLat reference_; ///< central longitude/standard parallel [degree]/[degree] + double radius_; ///< sphere radius + double lat1_; ///< first latitude from the pole at which the secant cone cuts the sphere + double lat2_; ///< second latitude from the pole at which the secant cone cuts the sphere + double latD_; ///< latitude where Dx and Dy are specified + double lon0_; ///< longitude of meridian parallel to y-axis along which latitude increases as the y-coordinate increases + + double F_; + double n_; + double inv_n_; + double rho0_; + double sign_; +}; + +} // namespace detail +} // namespace projection +} // namespace atlas From 050f9f954220dde046e64ec36adb1bb06602a9ee Mon Sep 17 00:00:00 2001 From: Pedro Maciel Date: Mon, 23 Sep 2019 18:19:59 +0100 Subject: [PATCH 37/40] MIR-77, MIR-385, MIR-386: preliminary support for interpolation from Lambert Conformal projected grids --- src/atlas/projection/detail/LambertConformalProjection.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/atlas/projection/detail/LambertConformalProjection.h b/src/atlas/projection/detail/LambertConformalProjection.h index ccc4a5a1e..5b8ee8e63 100644 --- a/src/atlas/projection/detail/LambertConformalProjection.h +++ b/src/atlas/projection/detail/LambertConformalProjection.h @@ -31,7 +31,9 @@ class LambertConformalProjection final : public ProjectionImpl { void lonlat2xy( double crd[] ) const override; bool strictlyRegional() const override { return true; } - Domain boundingBox( const Domain& domain ) const override { return ProjectionImpl::boundingBox( domain ); } + RectangularLonLatDomain lonlatBoundingBox( const Domain& domain ) const override { + return ProjectionImpl::lonlatBoundingBox( domain ); + } // specification Spec spec() const override; From e9c76a663e0de70c898c9ac602ebe8a9b313907c Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 24 Sep 2019 14:00:35 +0100 Subject: [PATCH 38/40] MIR-77, MIR-385, MIR-386 Replace native Lambert projection with LambertConformalConic projection --- ...egional_lambert_azimuthal_equal_area_3.yml | 2 +- ...=> regional_lambert_conformal_conic_1.yml} | 11 +- ...=> regional_lambert_conformal_conic_2.yml} | 6 +- src/atlas/CMakeLists.txt | 6 +- ....cc => LambertConformalConicProjection.cc} | 33 +++-- ...on.h => LambertConformalConicProjection.h} | 13 +- .../projection/detail/LambertProjection.cc | 136 ------------------ .../projection/detail/LambertProjection.h | 59 -------- .../projection/detail/ProjectionFactory.cc | 4 +- src/atlas/projection/detail/ProjectionImpl.h | 2 +- .../test_interpolation_structured2D.cc | 4 +- 11 files changed, 44 insertions(+), 232 deletions(-) rename doc/example-grids/{regional_lambert_1.yml => regional_lambert_conformal_conic_1.yml} (71%) rename doc/example-grids/{regional_lambert_2.yml => regional_lambert_conformal_conic_2.yml} (73%) rename src/atlas/projection/detail/{LambertConformalProjection.cc => LambertConformalConicProjection.cc} (78%) rename src/atlas/projection/detail/{LambertConformalProjection.h => LambertConformalConicProjection.h} (79%) delete mode 100644 src/atlas/projection/detail/LambertProjection.cc delete mode 100644 src/atlas/projection/detail/LambertProjection.h 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 1751c5355..464195fbe 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.647, -35.035, 23.895, 74.144] - uid : dbbfc11ac72a5aa4377bbd516548e2c4 + uid : da2de78744323533eff5069272924741 xmin : 2510375.79 ymin : 748404.45 diff --git a/doc/example-grids/regional_lambert_1.yml b/doc/example-grids/regional_lambert_conformal_conic_1.yml similarity index 71% rename from doc/example-grids/regional_lambert_1.yml rename to doc/example-grids/regional_lambert_conformal_conic_1.yml index 53a680403..4d07c0e7f 100644 --- a/doc/example-grids/regional_lambert_1.yml +++ b/doc/example-grids/regional_lambert_conformal_conic_1.yml @@ -4,16 +4,19 @@ ny : 40 dx : 50000 dy : 50000 lonlat(centre) : [4,50] + projection : - type : "lambert" - latitude1 : 50 + type : "lambert_conformal_conic" longitude0 : 4 -y_numbering : +1 + latitude1 : 50 +y_numbering : +1 check : size : 2000 lonlat(first) : [-10.3173,40.22] lonlat(last) : [24.4368,57.2335] - uid : 1cb0b7c8f91617c3b6065d72b85c65c4 + uid : 2e01d66aa6df870b0ad346da4e80c4ff bounding_box(n,w,s,e) : [58.7333,-16.4378,40.219,24.4378] + xmin : -1225000.00 + ymin : -975000.00 diff --git a/doc/example-grids/regional_lambert_2.yml b/doc/example-grids/regional_lambert_conformal_conic_2.yml similarity index 73% rename from doc/example-grids/regional_lambert_2.yml rename to doc/example-grids/regional_lambert_conformal_conic_2.yml index c82c5fda5..4f521e113 100644 --- a/doc/example-grids/regional_lambert_2.yml +++ b/doc/example-grids/regional_lambert_conformal_conic_2.yml @@ -5,7 +5,7 @@ dx : 13545.0 dy : 13545.0 lonlat(xmin,ymin): [-126.138,16.281] projection : - type : "lambert" + type : "lambert_conformal_conic" latitude1 : 25.0 longitude0 : -95.0 y_numbering : +1 @@ -15,5 +15,7 @@ check : size : 151987 lonlat(first) : [-126.138,16.281] lonlat(last) : [-57.3811,55.4813] - uid : ec98b263e57286710aeb4131a614b5f9 + uid : 5c9a0f242e68ff6dd7711ba3ab6f5c7b bounding_box(n,w,s,e) : [58.3664,-139.857,16.28,-57.3801] + xmin : -3332155.29 + ymin : -588892.76 diff --git a/src/atlas/CMakeLists.txt b/src/atlas/CMakeLists.txt index 4a2a35b09..01e57ed24 100644 --- a/src/atlas/CMakeLists.txt +++ b/src/atlas/CMakeLists.txt @@ -66,10 +66,8 @@ projection/Projection.cc projection/Projection.h projection/detail/LambertAzimuthalEqualAreaProjection.cc projection/detail/LambertAzimuthalEqualAreaProjection.h -projection/detail/LambertConformalProjection.cc -projection/detail/LambertConformalProjection.h -projection/detail/LambertProjection.cc -projection/detail/LambertProjection.h +projection/detail/LambertConformalConicProjection.cc +projection/detail/LambertConformalConicProjection.h projection/detail/LonLatProjection.cc projection/detail/LonLatProjection.h projection/detail/MercatorProjection.cc diff --git a/src/atlas/projection/detail/LambertConformalProjection.cc b/src/atlas/projection/detail/LambertConformalConicProjection.cc similarity index 78% rename from src/atlas/projection/detail/LambertConformalProjection.cc rename to src/atlas/projection/detail/LambertConformalConicProjection.cc index 634133ace..63023710c 100644 --- a/src/atlas/projection/detail/LambertConformalProjection.cc +++ b/src/atlas/projection/detail/LambertConformalConicProjection.cc @@ -9,7 +9,7 @@ */ -#include "LambertConformalProjection.h" +#include "LambertConformalConicProjection.h" #include @@ -53,15 +53,19 @@ inline double cos_d( double theta_d ) { return std::cos( util::Constants::degreesToRadians() * theta_d ); } -static ProjectionBuilder register_1( LambertConformalProjection::static_type() ); +static ProjectionBuilder register_1( LambertConformalConicProjection::static_type() ); } // namespace -LambertConformalProjection::LambertConformalProjection( const eckit::Parametrisation& params ) { - ATLAS_ASSERT( params.get( "latitude1", lat1_ = 0 ) ); - ATLAS_ASSERT( params.get( "latitude2", lat2_ = 0 ) ); - ATLAS_ASSERT( params.get( "latitudeD", latD_ = 0 ) ); +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_; + } + if ( not params.get( "latitude2", lat2_ ) ) { + lat2_ = lat1_; + } params.get( "radius", radius_ = util::Earth::radius() ); n_ = eckit::types::is_approximately_equal( lat1_, lat2_ ) @@ -71,10 +75,10 @@ LambertConformalProjection::LambertConformalProjection( const eckit::Parametrisa sign_ = n_ < 0. ? -1. : 1.; // n < 0: adjustment for southern hemisphere F_ = ( cos_d( lat1_ ) * std::pow( tan_d( lat1_ ), n_ ) ) / n_; - rho0_ = sign_ * radius_ * F_ * std::pow( tan_d( latD_ ), -n_ ); + rho0_ = sign_ * radius_ * F_ * std::pow( tan_d( lat0_ ), -n_ ); } -void LambertConformalProjection::lonlat2xy( double crd[] ) const { +void LambertConformalConicProjection::lonlat2xy( double crd[] ) const { double rho = radius_ * F_ * std::pow( tan_d( crd[1] ), -n_ ); double theta = n_ * normalise( crd[0] - lon0_, -180, 360 ); @@ -82,7 +86,7 @@ void LambertConformalProjection::lonlat2xy( double crd[] ) const { crd[YY] = rho0_ - rho * cos_d( theta ); } -void LambertConformalProjection::xy2lonlat( double crd[] ) const { +void LambertConformalConicProjection::xy2lonlat( double crd[] ) const { double x = sign_ * crd[XX]; double y = rho0_ - sign_ * crd[YY]; @@ -96,25 +100,24 @@ void LambertConformalProjection::xy2lonlat( double crd[] ) const { : util::Constants::radiansToDegrees() * 2. * std::atan( std::pow( radius_ * F_ / rho, inv_n_ ) ) - 90.; } -LambertConformalProjection::Spec LambertConformalProjection::spec() const { +LambertConformalConicProjection::Spec LambertConformalConicProjection::spec() const { Spec spec; spec.set( "type", static_type() ); + spec.set( "longitude0", lon0_ ); + spec.set( "latitude0", lat0_ ); spec.set( "latitude1", lat1_ ); spec.set( "latitude2", lat2_ ); - spec.set( "latitudeD", latD_ ); - spec.set( "longitude0", lon0_ ); if ( !eckit::types::is_approximately_equal( radius_, util::Earth::radius() ) ) { spec.set( "radius", radius_ ); } - return spec; } -void LambertConformalProjection::hash( eckit::Hash& h ) const { +void LambertConformalConicProjection::hash( eckit::Hash& h ) const { h.add( static_type() ); h.add( lat1_ ); h.add( lat2_ ); - h.add( latD_ ); + h.add( lat0_ ); h.add( lon0_ ); h.add( radius_ ); } diff --git a/src/atlas/projection/detail/LambertConformalProjection.h b/src/atlas/projection/detail/LambertConformalConicProjection.h similarity index 79% rename from src/atlas/projection/detail/LambertConformalProjection.h rename to src/atlas/projection/detail/LambertConformalConicProjection.h index 5b8ee8e63..083a9ef42 100644 --- a/src/atlas/projection/detail/LambertConformalProjection.h +++ b/src/atlas/projection/detail/LambertConformalConicProjection.h @@ -17,13 +17,13 @@ namespace atlas { namespace projection { namespace detail { -class LambertConformalProjection final : public ProjectionImpl { +class LambertConformalConicProjection final : public ProjectionImpl { public: // constructor - LambertConformalProjection( const eckit::Parametrisation& ); + LambertConformalConicProjection( const eckit::Parametrisation& ); // projection name - static std::string static_type() { return "lambert_conformal"; } + static std::string static_type() { return "lambert_conformal_conic"; } std::string type() const override { return static_type(); } // projection and inverse projection @@ -31,6 +31,7 @@ class LambertConformalProjection final : public ProjectionImpl { void lonlat2xy( double crd[] ) const override; bool strictlyRegional() const override { return true; } + RectangularLonLatDomain lonlatBoundingBox( const Domain& domain ) const override { return ProjectionImpl::lonlatBoundingBox( domain ); } @@ -43,12 +44,12 @@ class LambertConformalProjection final : public ProjectionImpl { void hash( eckit::Hash& ) const override; private: -// PointLonLat reference_; ///< central longitude/standard parallel [degree]/[degree] double radius_; ///< sphere radius double lat1_; ///< first latitude from the pole at which the secant cone cuts the sphere double lat2_; ///< second latitude from the pole at which the secant cone cuts the sphere - double latD_; ///< latitude where Dx and Dy are specified - double lon0_; ///< longitude of meridian parallel to y-axis along which latitude increases as the y-coordinate increases + double lat0_; ///< latitude of origin, where Dx and Dy are specified + double lon0_; ///< longitude of origin, meridian parallel to y-axis along which latitude increases + /// as the y-coordinate increases double F_; double n_; diff --git a/src/atlas/projection/detail/LambertProjection.cc b/src/atlas/projection/detail/LambertProjection.cc deleted file mode 100644 index 9f892eda5..000000000 --- a/src/atlas/projection/detail/LambertProjection.cc +++ /dev/null @@ -1,136 +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 "eckit/utils/Hash.h" - -#include "atlas/projection/detail/LambertProjection.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/Earth.h" - -/* -Projection formula's for Lambert projection from "Map Projections: A Working -Manual" - -The origin of the xy-system is at (lon0,0) - -*/ - -namespace { -static double D2R( const double x ) { - return atlas::util::Constants::degreesToRadians() * x; -} -static double R2D( const double x ) { - return atlas::util::Constants::radiansToDegrees() * x; -} -} // namespace - -namespace atlas { -namespace projection { -namespace detail { - -// constructors -LambertProjection::LambertProjection( const eckit::Parametrisation& params ) { - // check presence of radius - if ( !params.get( "radius", radius_ ) ) { - radius_ = util::Earth::radius(); - } - // check presence of lat1 and lat2 - if ( !params.get( "latitude1", lat1_ ) ) { - throw_Exception( "latitude1 missing in Params", Here() ); - } - if ( !params.get( "latitude2", lat2_ ) ) { - lat2_ = lat1_; - } - // check presence of lon0 - if ( !params.get( "longitude0", lon0_ ) ) { - throw_Exception( "longitude0 missing in Params", Here() ); - } - - setup(); -} - -void LambertProjection::setup() { - // setup (derived) constants - is_tangent_ = std::equal_to()( lat1_, lat2_ ); - if ( is_tangent_ ) { - n_ = std::sin( D2R( lat1_ ) ); - } - else { - n_ = std::log( std::cos( D2R( lat1_ ) ) / std::cos( D2R( lat2_ ) ) ) / - std::log( std::tan( D2R( 45 + lat2_ * 0.5 ) ) / std::tan( D2R( 45. + lat1_ * 0.5 ) ) ); - } - F_ = std::cos( D2R( lat1_ ) ) * std::pow( std::tan( D2R( 45. + lat1_ * 0.5 ) ), n_ ) / n_; - rho0_ = radius_ * F_; - inv_n_ = 1. / n_; - sign_ = ( n_ < 0. ? -1. : 1. ); -} - -void LambertProjection::lonlat2xy( double crd[] ) const { - const double rho = radius_ * F_ / std::pow( std::tan( D2R( 45 + crd[1] * 0.5 ) ), n_ ); - const double theta = D2R( n_ * ( crd[0] - lon0_ ) ); - // eckit::geometry::reduceTo2Pi(theta); // bracket between 0 and 360 - // theta*=n_; - crd[0] = rho * std::sin( theta ); - crd[1] = rho0_ - rho * std::cos( theta ); -} - -// inverse projection -void LambertProjection::xy2lonlat( double crd[] ) const { - // auxiliaries - const double y0 = rho0_ - crd[1]; - const double rho = sign_ * std::sqrt( crd[0] * crd[0] + y0 * y0 ); - const double theta = R2D( std::atan2( sign_ * crd[0], sign_ * y0 ) ); - - // longitude - crd[0] = theta * inv_n_ + lon0_; - - // latitude - if ( rho == 0. ) { - crd[1] = sign_ * 90.; - } - else { - crd[1] = 2. * R2D( std::atan( std::pow( radius_ * F_ / rho, inv_n_ ) ) ) - 90.; - } -} - -// specification -LambertProjection::Spec LambertProjection::spec() const { - Spec proj_spec; - proj_spec.set( "type", static_type() ); - proj_spec.set( "latitude1", lat1_ ); - proj_spec.set( "latitude2", lat2_ ); - proj_spec.set( "longitude0", lon0_ ); - if ( std::not_equal_to()( radius_, util::Earth::radius() ) ) { - proj_spec.set( "radius", radius_ ); - } - return proj_spec; -} - -void LambertProjection::hash( eckit::Hash& hsh ) const { - hsh.add( static_type() ); - hsh.add( lat1_ ); - hsh.add( lat2_ ); - hsh.add( lon0_ ); - hsh.add( radius_ ); -} - -namespace { -static ProjectionBuilder register_projection( LambertProjection::static_type() ); -} // namespace - -} // namespace detail -} // namespace projection -} // namespace atlas diff --git a/src/atlas/projection/detail/LambertProjection.h b/src/atlas/projection/detail/LambertProjection.h deleted file mode 100644 index e24b9d7bf..000000000 --- a/src/atlas/projection/detail/LambertProjection.h +++ /dev/null @@ -1,59 +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. - */ - -#pragma once - -#include "atlas/domain.h" -#include "atlas/projection/detail/ProjectionImpl.h" - -namespace atlas { -namespace projection { -namespace detail { - -class LambertProjection final : public ProjectionImpl { -public: - // constructor - LambertProjection( const eckit::Parametrisation& ); - - // projection name - static std::string static_type() { return "lambert"; } - std::string type() const override { return static_type(); } - - // projection and inverse projection - void xy2lonlat( double crd[] ) const override; - void lonlat2xy( double crd[] ) const override; - - bool strictlyRegional() const override { return true; } - RectangularLonLatDomain lonlatBoundingBox( const Domain& domain ) const override { - return ProjectionImpl::lonlatBoundingBox( domain ); - } - - // specification - Spec spec() const override; - - std::string units() const override { return "meters"; } - - void hash( eckit::Hash& ) const override; - -private: - double lat1_, lat2_; // First and second latitude at which the secant cone - // cuts the sphere - bool is_tangent_; // If the first and second latitude are equal, then the - // projection is on a tangent cone - double lon0_; // central longitude - double radius_; // sphere radius - double n_, inv_n_, F_, rho0_, sign_; // projection constants - - void setup(); -}; - -} // namespace detail -} // namespace projection -} // namespace atlas diff --git a/src/atlas/projection/detail/ProjectionFactory.cc b/src/atlas/projection/detail/ProjectionFactory.cc index 726faa8bd..e2db82387 100644 --- a/src/atlas/projection/detail/ProjectionFactory.cc +++ b/src/atlas/projection/detail/ProjectionFactory.cc @@ -15,7 +15,7 @@ #include "atlas/util/Config.h" #include "atlas/projection/detail/LambertAzimuthalEqualAreaProjection.h" -#include "atlas/projection/detail/LambertProjection.h" +#include "atlas/projection/detail/LambertConformalConicProjection.h" #include "atlas/projection/detail/LonLatProjection.h" #include "atlas/projection/detail/MercatorProjection.h" #include "atlas/projection/detail/SchmidtProjection.h" @@ -34,7 +34,7 @@ void force_link() { ProjectionBuilder(); ProjectionBuilder(); ProjectionBuilder(); - ProjectionBuilder(); + ProjectionBuilder(); ProjectionBuilder(); } } link; diff --git a/src/atlas/projection/detail/ProjectionImpl.h b/src/atlas/projection/detail/ProjectionImpl.h index 745c69d95..b97db72c0 100644 --- a/src/atlas/projection/detail/ProjectionImpl.h +++ b/src/atlas/projection/detail/ProjectionImpl.h @@ -51,7 +51,7 @@ class ProjectionImpl : public util::Object { PointLonLat lonlat( const PointXY& ) const; PointXY xy( const PointLonLat& ) const; - virtual PointXYZ xyz(const PointLonLat&) const; + virtual PointXYZ xyz( const PointLonLat& ) const; virtual bool strictlyRegional() const = 0; virtual RectangularLonLatDomain lonlatBoundingBox( const Domain& ) const = 0; diff --git a/src/tests/interpolation/test_interpolation_structured2D.cc b/src/tests/interpolation/test_interpolation_structured2D.cc index 177418385..205e6462e 100644 --- a/src/tests/interpolation/test_interpolation_structured2D.cc +++ b/src/tests/interpolation/test_interpolation_structured2D.cc @@ -89,9 +89,9 @@ Grid lambert() { gridspec.set( "lonlat(centre)", std::vector{4., 50} ); gridspec.set( "projection", [] { Config projection; - projection.set( "type", "lambert" ); - projection.set( "latitude1", 50. ); + projection.set( "type", "lambert_conformal_conic" ); projection.set( "longitude0", 4. ); + projection.set( "latitude1", 50. ); return projection; }() ); return Grid{gridspec}; From 353de12998f4a814029a677bf345c9c9665ccc9a Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 24 Sep 2019 15:06:49 +0100 Subject: [PATCH 39/40] Fix compilation of test_mesh_reorder.cc for Cray/8.5 --- src/tests/mesh/test_mesh_reorder.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tests/mesh/test_mesh_reorder.cc b/src/tests/mesh/test_mesh_reorder.cc index 6912526e1..fe183405c 100644 --- a/src/tests/mesh/test_mesh_reorder.cc +++ b/src/tests/mesh/test_mesh_reorder.cc @@ -246,7 +246,8 @@ Mesh get_mesh() { } else { output::detail::GmshIO gmsh_reader; - return gmsh_reader.read( std::string{eckit::Resource( "--mesh", "" )} ); + std::string file = eckit::Resource( "--mesh", "" ); + return gmsh_reader.read( file ); } } From e68f021e4b8d34182a8cbad23c5e5f1113aef5c2 Mon Sep 17 00:00:00 2001 From: Willem Deconinck Date: Tue, 24 Sep 2019 17:10:32 +0100 Subject: [PATCH 40/40] Version 0.19.0 --- CHANGELOG.md | 16 ++++++++++++++++ VERSION.cmake | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7387b3914..a8553fdca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,26 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html ## [Unreleased] + +## [0.19.0] - 2019-10-01 +### Fixed +- Lambert ( conformal conic ) projection xy coordinates are now corrected + +### Changed +- LambertProjection renamed to LambertConformalConic + +### Added +- Reordering of nodes strategies (Hilbert curve, ReverseCuthillMckee) +- Preliminary CellColumns functionspace with Gmsh IO; halos are not yet fully supported + + ## [0.18.1] - 2019-08-10 ### Fixed - Match vertical structured interpolation to IFS - Fix in creating vertical dimension in StructuredColumns using interval - Fix in caching StructuredColumnsHaloExchange + ## [0.18.0] - 2019-07-15 ### Changed - Make grid hashes crossplatform @@ -28,6 +42,7 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html - StructuredColumns bug with iSend - Memory corruption in Spectral functionspace with GT CUDA backend + ## [0.17.2] - 2019-06-04 ### Fixed - Compilation with PGI 19.4 @@ -134,6 +149,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.19.0]: https://github.com/ecmwf/atlas/compare/0.18.1...0.19.0 [0.18.1]: https://github.com/ecmwf/atlas/compare/0.18.0...0.18.1 [0.18.0]: https://github.com/ecmwf/atlas/compare/0.17.2...0.18.0 [0.17.2]: https://github.com/ecmwf/atlas/compare/0.17.1...0.17.2 diff --git a/VERSION.cmake b/VERSION.cmake index fbc190781..412631bad 100644 --- a/VERSION.cmake +++ b/VERSION.cmake @@ -6,5 +6,5 @@ # granted to it by virtue of its status as an intergovernmental organisation nor # does it submit to any jurisdiction. -set ( ${PROJECT_NAME}_VERSION_STR "0.18.1" ) +set ( ${PROJECT_NAME}_VERSION_STR "0.19.0" )