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/CMakeLists.txt b/CMakeLists.txt index f9851bbff..0f89a7f0a 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.0.2 REQUIRED ) ecbuild_debug( " ECKIT_FEATURES : [${ECKIT_FEATURES}]" ) # options & dependencies @@ -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/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" ) diff --git a/cmake/FeatureClangTidy.cmake b/cmake/FeatureClangTidy.cmake index 8a1c1c98c..6292e4915 100644 --- a/cmake/FeatureClangTidy.cmake +++ b/cmake/FeatureClangTidy.cmake @@ -21,6 +21,22 @@ ecbuild_add_option( FEATURE CLANG_TIDY CONDITION CLANG_TIDY_EXE ) if (HAVE_CLANG_TIDY) - 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}/*'") + + # 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 "-*" ) + foreach( _clang_tidy_check + readability-braces-around-statements + redundant-string-init + modernize-use-nullptr + 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() + set( CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXE};-checks=${CLANG_TIDY_CHECKS};-header-filter='${CMAKE_SOURCE_DIR}/*'${CLANG_TIDY_FIXIT}" ) endif() 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/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/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..464195fbe --- /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 : da2de78744323533eff5069272924741 + 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/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/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-benchmark.cc b/src/apps/atlas-benchmark.cc index 6a80de61b..7c868177b 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" @@ -126,7 +127,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 ) { @@ -138,6 +139,8 @@ 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)" ) ); + add_option( new SimpleOption( "sort_edges", "Sort edges by lowest node local index" ) ); } void setup(); @@ -169,6 +172,8 @@ class AtlasBenchmark : public AtlasTool { long omp_threads; double dz; std::string gridname; + std::string reorder{"none"}; + bool sort_edges{false}; TimerStats iteration_timer; TimerStats haloexchange_timer; @@ -196,6 +201,8 @@ int AtlasBenchmark::execute( const Args& args ) { args.get( "exclude", exclude ); output = false; args.get( "output", output ); + args.get( "reorder", reorder ); + args.get( "sort_edges", sort_edges ); bool help( false ); args.get( "help", help ); @@ -308,9 +315,12 @@ 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 ) ); } + 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/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..c9062d3a2 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/parser/JSON.h" #include "eckit/types/FloatCompare.h" #include "atlas/grid.h" @@ -29,11 +35,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" @@ -313,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; } } @@ -338,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(); } 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..077e61a67 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 = default; - virtual void run(); + void run() override; }; void Version::run() { diff --git a/src/atlas/CMakeLists.txt b/src/atlas/CMakeLists.txt index 21fda484d..01e57ed24 100644 --- a/src/atlas/CMakeLists.txt +++ b/src/atlas/CMakeLists.txt @@ -66,8 +66,8 @@ projection/Projection.cc projection/Projection.h projection/detail/LambertAzimuthalEqualAreaProjection.cc projection/detail/LambertAzimuthalEqualAreaProjection.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 @@ -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 ) @@ -287,6 +294,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 @@ -334,6 +347,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 @@ -671,6 +686,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/array/LocalView.h b/src/atlas/array/LocalView.h index abe29e459..9cd249dd2 100644 --- a/src/atlas/array/LocalView.h +++ b/src/atlas/array/LocalView.h @@ -150,6 +150,10 @@ class LocalView { return strides_[idx]; } + 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 d3b398eff..332916ab2 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; @@ -128,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/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/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 2b1a68110..6388d330b 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; @@ -122,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 @@ -195,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/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 e5cb95090..5eeebbcad 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() { @@ -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 15257fac6..4a167fc75 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() { @@ -44,10 +44,7 @@ void load_builder() { } struct force_link { - force_link() { - // load_builder< A DERIVED TYPE >(); - // ... - } + force_link() = default; }; } // namespace @@ -58,7 +55,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 +144,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.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/functionspace/EdgeColumns.cc b/src/atlas/functionspace/EdgeColumns.cc index ff8abcae8..379c0c96e 100644 --- a/src/atlas/functionspace/EdgeColumns.cc +++ b/src/atlas/functionspace/EdgeColumns.cc @@ -83,7 +83,7 @@ class EdgeColumnsHaloExchangeCache : public util::Cache +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_; diff --git a/src/atlas/functionspace/NodeColumns.cc b/src/atlas/functionspace/NodeColumns.cc index cdd2e5f2a..7be190e19 100644 --- a/src/atlas/functionspace/NodeColumns.cc +++ b/src/atlas/functionspace/NodeColumns.cc @@ -83,7 +83,7 @@ class NodeColumnsHaloExchangeCache : public util::Cache -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/functionspace/Spectral.cc b/src/atlas/functionspace/Spectral.cc index c176bca21..d46ea0ce4 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 ) ); } @@ -189,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(); @@ -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/functionspace/StructuredColumns.cc b/src/atlas/functionspace/StructuredColumns.cc index 54f5cd281..74f8e3375 100644 --- a/src/atlas/functionspace/StructuredColumns.cc +++ b/src/atlas/functionspace/StructuredColumns.cc @@ -100,7 +100,7 @@ class StructuredColumnsHaloExchangeCache : public util::CachesizeHalo() ); return value; } - virtual ~StructuredColumnsHaloExchangeCache() {} + ~StructuredColumnsHaloExchangeCache() override = default; }; 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 = default; }; class StructuredColumnsChecksumCache : public util::Cache, @@ -180,7 +180,7 @@ class StructuredColumnsChecksumCache : public util::Cachesetup( gather ); return value; } - virtual ~StructuredColumnsChecksumCache() {} + ~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/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/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; 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..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; @@ -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..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 ) : @@ -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..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() ) { @@ -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/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 40bbc65c4..4820747f9 100644 --- a/src/atlas/grid/detail/partitioner/TransPartitioner.cc +++ b/src/atlas/grid/detail/partitioner/TransPartitioner.cc @@ -42,14 +42,15 @@ 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" ); 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/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/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/Connectivity.cc b/src/atlas/mesh/Connectivity.cc index b342eeda3..7514e1069 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; //------------------------------------------------------------------------------------------------------ @@ -710,9 +710,7 @@ BlockConnectivityImpl::BlockConnectivityImpl( idx_t rows, idx_t cols, idx_t valu //------------------------------------------------------------------------------------------------------ -BlockConnectivityImpl::~BlockConnectivityImpl() { - //TODO owns_ not used ? -} +BlockConnectivityImpl::~BlockConnectivityImpl() = default; //------------------------------------------------------------------------------------------------------ @@ -775,8 +773,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/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/Elements.cc b/src/atlas/mesh/Elements.cc index 7306f0fd9..a69db9f0f 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::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::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() ); +} + //----------------------------------------------------------------------------- extern "C" { diff --git a/src/atlas/mesh/Elements.h b/src/atlas/mesh/Elements.h index 912ca88b5..b4f99b59a 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" @@ -34,8 +35,6 @@ namespace mesh { /// @brief Describe elements of a single type class Elements : public util::Object { -public: - // typedef atlas::mesh::BlockConnectivity Connectivity; public: //-- Constructors @@ -124,6 +123,12 @@ class Elements : public util::Object { template array::LocalView view( Field& ) const; + template + array::LocalIndexView indexview( const Field& ) const; + + template + array::LocalIndexView indexview( Field& ) const; + idx_t add( const idx_t nb_elements ); private: 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/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/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/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..9fc1741e5 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 @@ -49,7 +50,7 @@ namespace actions { namespace { // anonymous struct Sort { - Sort() {} + Sort() = default; Sort( gidx_t gid, idx_t idx ) { g = gid; i = idx; @@ -423,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() ); @@ -450,10 +454,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 = */ sort_edges ) { + 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 ce58bdcd7..5b605a396 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() ); } @@ -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(); @@ -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; @@ -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/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/actions/BuildParallelFields.cc b/src/atlas/mesh/actions/BuildParallelFields.cc index 1f6a814b5..b2d3fd6ae 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 { @@ -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() ); } @@ -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/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/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 new file mode 100644 index 000000000..1d7eda7b4 --- /dev/null +++ b/src/atlas/mesh/actions/ReorderHilbert.cc @@ -0,0 +1,395 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include +#include +#include +#include + +#include "atlas/array.h" +#include "atlas/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/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 { +namespace actions { + +// ------------------------------------------------------------------------------------- + +/// @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 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 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 +/// 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(); + + 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 ); // 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; + } + } + + box_t box_quadrant; + switch ( quadrant ) { + case A: + box_quadrant[A] = box[A]; + // 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; // does not compile with eckit 1.3.2 + box_quadrant[B] = box[B]; + // 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; // 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; // 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; // 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; + } + + // 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( const eckit::Parametrisation& config ) { + config.get( "recursion", recursion_ ); + config.get( "ghost_at_end", ghost_at_end_ ); +} + + +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(); + 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() ); + return RectangularDomain( {xmin, xmax}, {ymin, ymax} ); +} + +std::vector ReorderHilbert::computeNodesOrder( Mesh& mesh ) { + using hilbert_reordering_t = std::vector>; + + 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_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.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 ); + } +} +#endif + +namespace { +static ReorderBuilder __ReorderHilbert( "hilbert" ); +} // namespace + + +} // 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..27bf03a43 --- /dev/null +++ b/src/atlas/mesh/actions/ReorderHilbert.h @@ -0,0 +1,50 @@ +/* + * (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 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::Parametrisation& config = util::NoConfig() ); + + std::vector computeNodesOrder( Mesh& ) override; + +private: + idx_t recursion_{30}; + bool ghost_at_end_{true}; +}; + +// ------------------------------------------------------------------ + +} // namespace actions +} // namespace mesh +} // namespace atlas 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/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/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/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::ObjectHandlemesh().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..cdd022e84 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 > @@ -517,7 +565,7 @@ GmshIO::GmshIO() { options.set>( "levels", std::vector() ); } -GmshIO::~GmshIO() {} +GmshIO::~GmshIO() = default; Mesh GmshIO::read( const PathName& file_path ) const { Mesh mesh; @@ -569,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; @@ -596,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 ); @@ -687,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() ); @@ -699,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 ); @@ -744,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; @@ -999,6 +1046,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 +1118,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 +1211,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/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/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 diff --git a/src/atlas/projection/detail/LambertConformalConicProjection.cc b/src/atlas/projection/detail/LambertConformalConicProjection.cc new file mode 100644 index 000000000..63023710c --- /dev/null +++ b/src/atlas/projection/detail/LambertConformalConicProjection.cc @@ -0,0 +1,127 @@ +/* + * (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 "LambertConformalConicProjection.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( LambertConformalConicProjection::static_type() ); + +} // namespace + +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_ ) + ? 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( lat0_ ), -n_ ); +} + +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 ); + + crd[XX] = rho * sin_d( theta ); + crd[YY] = rho0_ - rho * cos_d( theta ); +} + +void LambertConformalConicProjection::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.; +} + +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_ ); + if ( !eckit::types::is_approximately_equal( radius_, util::Earth::radius() ) ) { + spec.set( "radius", radius_ ); + } + return spec; +} + +void LambertConformalConicProjection::hash( eckit::Hash& h ) const { + h.add( static_type() ); + h.add( lat1_ ); + h.add( lat2_ ); + h.add( lat0_ ); + h.add( lon0_ ); + h.add( radius_ ); +} + +} // namespace detail +} // namespace projection +} // namespace atlas diff --git a/src/atlas/projection/detail/LambertProjection.h b/src/atlas/projection/detail/LambertConformalConicProjection.h similarity index 60% rename from src/atlas/projection/detail/LambertProjection.h rename to src/atlas/projection/detail/LambertConformalConicProjection.h index e24b9d7bf..083a9ef42 100644 --- a/src/atlas/projection/detail/LambertProjection.h +++ b/src/atlas/projection/detail/LambertConformalConicProjection.h @@ -1,5 +1,5 @@ /* - * (C) Copyright 2013 ECMWF. + * (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. @@ -17,13 +17,13 @@ namespace atlas { namespace projection { namespace detail { -class LambertProjection final : public ProjectionImpl { +class LambertConformalConicProjection final : public ProjectionImpl { public: // constructor - LambertProjection( const eckit::Parametrisation& ); + LambertConformalConicProjection( const eckit::Parametrisation& ); // projection name - static std::string static_type() { return "lambert"; } + 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 LambertProjection 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,15 +44,18 @@ class LambertProjection final : public ProjectionImpl { 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(); + 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 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_; + double inv_n_; + double rho0_; + double sign_; }; } // namespace detail 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/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/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.cc b/src/atlas/projection/detail/ProjectionImpl.cc index 60b5cb5d5..b82332f3d 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 ); } }; @@ -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..b97db72c0 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; 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 28b1be16d..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" @@ -49,7 +54,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 ); } @@ -90,8 +95,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 +375,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 +534,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 +746,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 ) ); } @@ -867,7 +891,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 ); @@ -910,13 +934,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 +971,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 +1031,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 +1048,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 +1126,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 +1144,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 +1169,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 +1187,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 +1263,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 +1276,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 +1293,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 +1378,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 +1396,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 +1410,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 +1429,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 +1478,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 +1499,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/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..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" @@ -59,7 +63,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/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/atlas/util/Polygon.cc b/src/atlas/util/Polygon.cc index 9daa14c14..dbbdaedb3 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(); @@ -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() ); @@ -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/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 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..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() ); } @@ -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/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..5efc89dbf --- /dev/null +++ b/src/tests/functionspace/test_cellcolumns.cc @@ -0,0 +1,118 @@ +/* + * (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. + */ + + +/// 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" +#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 = + MeshGenerator{util::Config( "type", "structured" )( "patch_pole", false )( "triangulate", true )}; + return meshgenerator.generate( grid ); +} + +void set_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() ); + + EXPECT( field.shape( 0 ) == mesh.cells().size() ); + + 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 ); + } + } +} + +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() ); + + set_field_values( mesh, field ); + + fs.haloExchange( field ); + + check_field_values( mesh, field ); + + 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 ); +} 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/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 ) diff --git a/src/tests/grid/test_state.cc b/src/tests/grid/test_state.cc index fa91a235b..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" @@ -41,8 +47,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 = default; + void generate( State& state, const eckit::Parametrisation& p = util::Config() ) const override; }; // --- Implementation (in .cc file) 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]; 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/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}; diff --git a/src/tests/mesh/CMakeLists.txt b/src/tests/mesh/CMakeLists.txt index 87a3a7778..13dff96e4 100644 --- a/src/tests/mesh/CMakeLists.txt +++ b/src/tests/mesh/CMakeLists.txt @@ -96,6 +96,50 @@ foreach( test connectivity stream_connectivity elements ll meshgen3d rgg ) ) 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_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/mesh/test_mesh_reorder.cc b/src/tests/mesh/test_mesh_reorder.cc new file mode 100644 index 000000000..fe183405c --- /dev/null +++ b/src/tests/mesh/test_mesh_reorder.cc @@ -0,0 +1,370 @@ +/* + * (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" + + +#include "atlas/output/detail/GmshIO.h" + +//----------------------------------------------------------------- + +namespace atlas { +namespace test { + +//----------------------------------------------------------------------------- + +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() ); + + 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.title('" << title << "')" + "\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 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() ); + + 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; +} + +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; + std::string file = eckit::Resource( "--mesh", "" ); + return gmsh_reader.read( file ); + } +} + +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 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 ); + + 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, 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, title ); + + Field edge_centres = create_edge_centres( mesh ); + auto edge_centres_xy = array::make_view( edge_centres ); + outputConnectedCoordinates( type + "_edges.py", edge_centres_xy, title ); + + 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(), 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 ) { + // 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 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 ); +} + +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 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; + } + 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 ); +} 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 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 { 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..dafe688b2 --- /dev/null +++ b/src/tests/projection/test_proj.cc @@ -0,0 +1,58 @@ +/* + * (C) Copyright 2013 ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + + +#include "atlas/projection.h" +#include "atlas/util/Config.h" +#include "atlas/util/Point.h" + +#include "tests/AtlasTestEnvironment.h" + + +namespace atlas { +namespace test { + +//----------------------------------------------------------------------------- + +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 " << a << " = " << b << std::endl; + Log::info().precision( old ); + + EXPECT( is_approximately_equal( a[0], b[0], eps ) ); + EXPECT( is_approximately_equal( a[1], b[1], eps ) ); +} + +//----------------------------------------------------------------------------- + +CASE( "test_proj" ) { + PointLonLat 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( b, {691875.632137542, 6098907.825129169} ); + check( projection.lonlat( b ), a ); + } +} + +//----------------------------------------------------------------------------- + +} // namespace test +} // namespace atlas + +int main( int argc, char** argv ) { + return atlas::test::run( argc, argv ); +} diff --git a/src/tests/trans/test_trans.cc b/src/tests/trans/test_trans.cc index 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(); } 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